Coder Social home page Coder Social logo

rweb's Introduction

rweb

Build Status

Yet another web server framework for rust.

Installation:

[dependencies]
rweb = "0.6"
tokio = "1"

Installation (with automatic openapi generation):

[dependencies]
rweb = { version = "0.6", features = ["openapi"] }
serde = "1"
tokio = "1"

Features

  • Safe & Correct

Since rweb is based on warp, which features safety and correctness, rweb has same property.

  • Easy to read code
use rweb::*;
use serde::{Serialize, Deserialize};

#[get("/output")]
fn output() -> String {
    String::from("this returns 200 with text/plain mime type")
}

#[derive(Debug, Serialize, Deserialize, Schema)]
struct Product {
    id: String,
    title: String,
}

#[get("/products")]
fn products() -> Json<Vec<Product>> {
    // ...
    // This returns 200 with application/json
}

#[get("/products/{id}")]
fn product(id: String) -> Json<Product> {
    // ...
    // This returns 200 with application/json
}

#[get("/product")]
fn new_product(_product: Json<Product>) -> Json<Product> {
    // ...
    // This returns 200 with application/json
}

#[derive(Debug, Serialize, Deserialize, Schema)]
struct SearchOption {
    query: String,
    limit: usize,
    page_token: String,
}

#[get("/search")]
fn search(_product: Query<SearchOption>) -> Json<Vec<Product>> {
    // ...
    // This returns 200 with application/json
}

#[tokio::main]
async fn main() {
    serve(output().or(product()).or(products()).or(search())).run(([127, 0, 0, 1], 3030)).await;
}
  • Websocket

If you want to use websocket, just declare a parameter typed Ws. It's all.

use rweb::*;

#[get("/ws")]
fn example(ws: ws::Ws) -> String {
    String::new("use ws.on_upgrade or extra")
}
  • Automatic openapi spec generation

rweb supports automatically generating openapi specification file based on your code.

See: documentation for usage.

  • API UX interaction for openapi
// Build openapi for your API
let (spec, filter) = openapi::spec().build(move || {
    // Your API's filters
    math::math()
        .or(products::products())
        .or(generic::body())
        .or(generic::optional())
        .or(generic::search())
        .or(response::response())
});

println!("go to http://localhost:3030/docs to interact with your openapi!");
serve(filter.or(openapi_docs(spec)))
    .run(([127, 0, 0, 1], 3030))
    .await;

Comparison

Name rweb actix-web gotham iron nickel rocket rouille Thruster Tide tower-web warp
License license license license license license license license license license license license
Version version version version version version version version version version version version
Recent downloads recent downloads recent downloads recent downloads recent downloads recent downloads recent downloads recent downloads recent downloads recent downloads recent downloads recent downloads
Github stars github stars github stars github stars github stars github stars github stars github stars github stars github stars github stars github stars
Contributors contributors contributors contributors contributors contributors contributors contributors contributors contributors contributors contributors
Activity activity activity activity activity activity activity activity activity activity activity activity
Base framework hyper / warp tokio hyper hyper hyper hyper tiny-http tokio hyper hyper hyper
https Y Y Y ? ? ? ? ? ? ? Y
http 2 Y Y ? ? ? ? ? ? ? ? Y
async Y Y Y Y Y Y Y (via different method)
stable rust Y Y Y Y Y Y Y Y Y Y
openapi support Y

rweb's People

Contributors

0x003e avatar celeo avatar dbanty avatar ddboline avatar dwarn-axiallon avatar e-gy avatar imp avatar kdy1 avatar muqito avatar richardanaya avatar szagi3891 avatar vifino avatar vn971 avatar xabufr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

rweb's Issues

Open APi spec file upload

I have a multipart upload working just fine using a #[filter = "function"] on a request. Is there a way to add the open api spec for a file upload? id and version are documented just fine. The code looks roughly like this...

 #[post("/v1/resource/{id}/version/{version}")]
 #[openapi(
    id = "resource.upload",
    description = "Uploads a resource",
    summary = "Uploads a resource"
)]

pub async fn upload(
    id: String,
    version: String,
    #[filter = "multipart"] file_info: FileInfo) {
...
}

fn multipart() -> impl Filter<Extract = (FileInfo,), Error = Infallible> + Clone {
    rweb::header::<Mime>("content-type")
        .and(rweb::body::stream())
        .and_then(get_file_from_form)
        .recover(handle_rejection)
        .unify()
}

Document that/why returning a non-Result when handler is async is invalid

TL;DR: It seems like async route handlers must return a Result. The futures example returns a Result<impl rweb::Reply, Infallible>, suggesting this is known and intentional. Why is this, and can it be documented somewhere?


I see in various examples that a route handler can return an existential type impl Reply. One such example is sse_chat.rs. It compiles fine as-is.

If I make one such handler async, as follows:

diff --git a/examples/sse_chat.rs b/examples/sse_chat.rs
index 6dc28d9..d7c1424 100644
--- a/examples/sse_chat.rs
+++ b/examples/sse_chat.rs
@@ -54,7 +54,7 @@ fn recv_chat(#[data] users: Users) -> impl Reply {
 }
 
 #[get("/")]
-fn index() -> impl Reply {
+async fn index() -> impl Reply {
     rweb::http::Response::builder()
         .header("content-type", "text/html; charset=utf-8")
         .body(INDEX_HTML)

I get two errors:

Click to expand
error[E0271]: type mismatch resolving `<impl futures::Future as futures::Future>::Output == Result<_, _>`
  --> examples/sse_chat.rs:56:1
   |
56 | #[get("/")]
   | ^^^^^^^^^^^ expected opaque type, found enum `Result`
57 | async fn index() -> impl Reply {
   |                     ---------- the expected opaque type
   |
   = note: expected opaque type `impl Reply`
                     found enum `Result<_, _>`
   = note: required because of the requirements on the impl of `TryFuture` for `impl futures::Future`
   = note: this error originates in the attribute macro `get` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0271]: type mismatch resolving `<impl futures::Future as futures::Future>::Output == Result<_, _>`
  --> examples/sse_chat.rs:56:1
   |
56 | #[get("/")]
   | ^^^^^^^^^^^ expected enum `Result`, found opaque type
57 | async fn index() -> impl Reply {
   |                     ---------- the found opaque type
   |
   = note:     expected enum `Result<_, _>`
           found opaque type `impl Reply`
   = note: required because of the requirements on the impl of `TryFuture` for `impl futures::Future`

In short, there is now an extra bound applied somewhere that the returned value must be a Result with certain properties.

I've run both versions through cargo-expand, and see the following diff when adding async:

--- non_async.rs        2021-08-12 16:50:31.818319127 -0700
+++ with_async.rs       2021-08-12 16:50:41.918437102 -0700
@@ -78,16 +78,17 @@
         .map(recv_chat)
 }
 fn index(
-) -> impl rweb::Filter<Extract = (impl Reply,), Error = rweb::warp::Rejection> + rweb::rt::Clone {
+) -> impl rweb::Filter<Extract = (impl rweb::Reply,), Error = rweb::warp::Rejection> + rweb::rt::Clone
+{
     use rweb::Filter;
-    fn index() -> impl Reply {
+    async fn index() -> impl Reply {
         rweb::http::Response::builder()
             .header("content-type", "text/html; charset=utf-8")
             .body(INDEX_HTML)
     }
     rweb::filters::method::get()
         .and(rweb::filters::path::end())
-        .map(index)
+        .and_then(index)
 }
 fn main() {
     tokio::runtime::Builder::new_multi_thread()

Indeed, and_then requires a Future returning a Result, from a glance at the docs.

I haven't dug deeply into this. However, I can confirm that the "futures" example returns a Result with Infallible error, so I'm inclined to believe that this is fully intentional/understood.

What's the reason behind this? I guess map doesn't support async filters, and_then it the only alternative, and the route macros don't currently have support for introducing the Result wrapper? Is there somewhere this could be documented?

I figure this issue can stand in as documentation in the short term, which is why I'm opening it with context.

Error compiling macro module

When I try to run rweb I get this compilation error:

MacBook-Pro-Grzegorz:notatki_panel grzegorzszeliga$ ./server_start.sh
    Updating crates.io index
    Updating git repository `https://github.com/vertigo-web/vertigo`
   Compiling proc-macro-nested v0.1.7
   Compiling tokio-util v0.3.1
   Compiling tempfile v3.2.0
   Compiling rweb-macros v0.4.5
   Compiling futures-util v0.3.11
error[E0432]: unresolved import `syn::export`
  --> /Users/grzegorzszeliga/.cargo/registry/src/github.com-1ecc6299db9ec823/rweb-macros-0.4.5/src/openapi/mod.rs:19:5
   |
19 |     export::ToTokens,
   |     ^^^^^^ could not find `export` in `syn`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0432`.
error: could not compile `rweb-macros`

To learn more, run the command again with --verbose.
warning: build failed, waiting for other jobs to finish...
error: build failed
MacBook-Pro-Grzegorz:notatki_panel grzegorzszeliga$ 

Can I ask for help?

use of undeclared crate or module `indexmap`

I used the latest version "9.1" of rweb in the project.

Probably got some bug in the macro after the last fixes:

error[E0433]: failed to resolve: use of undeclared crate or module `indexmap`
   --> src/main.rs:219:1
    |
219 | #[rweb::get("/api/openapi")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared crate or module `indexmap`
    |
    = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0433]: failed to resolve: use of undeclared crate or module `indexmap`
   --> src/main.rs:208:1
    |
208 | #[rweb::get("/api/sport/{id}")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared crate or module `indexmap`
    |
    = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)

Code:

...

#[rweb::get("/api/openapi")]
async fn get_openapi(#[data] schema: String) -> Result<String, Infallible> {
    Ok(schema)
}

#[rweb::get("/api/sport/{id}")]
async fn api_get_sport(#[data] app: App, id: String) -> Result<rweb::Json<ResponseSport>, Infallible> {
    let id = Arc::new(id);
    let result = app.sportMap.get_model(id).await;

    Ok(ResponseSport {
        result
    }.into())
}
...

OpenAPI Request Body generation with #[json]

Hi!

I haven't found any documentation that suggests this shouldn't work, but the OpenAPI doc generation doesn't seem to pick up arguments tagged with #[json] .

The Response field in the OpenAPI docs is generated appropriately. Do fields need to use the explicit request: Json<ActiveRequest> syntax instead?

#[post("/v1/test/active")]
#[openapi(id = "test_active")]
pub async fn test_set_active(
    #[filter = "authtoken"] auth_token: Option<String>,
    #[json] request: ActiveRequest
    #[data] client: reqwest::Client,
) -> Result<Json<ActiveResponse>, Rejection> 

Information on required fields is missing in generated structures

I create simple handler:

#[derive(Debug, Default, Serialize, Schema)]
struct Person0 {
    name: String,
    age: u64,
    city: Option<String>,
}

#[derive(Debug, Default, Serialize, Schema)]
struct Person {
    name: String,
    age: u64,
    city: Option<String>,
    per0: Person0,
}

#[rweb::get("/test")]
async fn aaa(#[data] appAddr: Arc<App>) -> Result<rweb::Json<Person>, Infallible> {

    Ok(Person {
        name: "Baaaa".into(),
        age: 444,
        //city: Some(String::from("dasdas"))
        city: None,
        per0: Person0 {
            name: "dsadas".into(),
            age: 44,
            city: Some(String::from("dsdas")),
        }
    }.into())
}

The following specification was generated from this handler:

openapi: 3.0.1
info:
  title: ''
  version: ''
paths:
  /test:
    get:
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: object
                properties:
                  name:
                    type: string
                  age:
                    type: integer
                  city:
                    type: string
                    nullable: true
                  per0:
                    type: object
                    properties:
                      name:
                        type: string
                      age:
                        type: integer
                      city:
                        type: string
                        nullable: true

Information that all fields are required is missing from the generated specification.

The correct specification should look like this:

openapi: 3.0.1
info:
  title: ''
  version: ''
paths:
  /test:
    get:
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: object
                required:
                  - name
                  - age
                  - per0
                properties:
                  name:
                    type: string
                  age:
                    type: integer
                  city:
                    type: string
                    nullable: true
                  per0:
                    type: object
                    required:
                    - name
                    - age
                    - city
                    properties:
                      name:
                        type: string
                      age:
                        type: integer
                      city:
                        type: string
                        nullable: true

Can I ask to fix this error ?
I have analysed the "Scheme" macro, but am not yet able to make a fix for this error.

Automatic openapi spec generation

Requirements

  • cargo command should generate swagger.yaml
  • generating swagger.yaml should not involve **rebuilding **

So, we can't use cfg.

Ideas

generating spec files

In this way, we can get all of the routes, but we have some problems

  • It's hard to know which exact path of a handler.

    • For handlers mixed with warp path filters in a functional way, it's almost impossible to detect the correct path.
  • We can't know which handler is actually used.

generating spec descriptor

using a different name

We can generate e.g. fn rweb_swagger_index for fn index() and use rweb_swagger_index

  • It's hard to know which exact path of the handler.
    • If the user uses path filters, rweb_swagger_index and index are routed differently.
fn main() {
    let filter = swagger!(schemes(User, Profile), apis(math(), index()));
}

overloading handle and issuing a mock request to collect api details

We can use http's extension. I tried this, and I found that

  • It requires Arc<Mutex<Collector>>
  • It makes the startup slower.
  • Cannot get the exact path
    • path::full() filter is used to get the full path of the request, not matched path.

forking warp

I don't want to do this.

Add more tests

We need lots of tests. I will write a list of operations that should be verified.

OpenAPI spec gen - properly read all `#[serde]` attributes

Currently serde attributes specified parsed in Meta::Lists are ignored. i.e. for the test openapi rename all, this works

#[derive(Debug, Serialize, Deserialize, Schema)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "camelCase")]
struct Data {
    data_msg: String,
}

while this fails

#[derive(Debug, Serialize, Deserialize, Schema)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
struct Data {
    data_msg: String,
}

Both are valid equivalent serde configs.

prod

Can I actually build something that is prod ready wit rweb?

openapi: cannot generate multiple schemas

Given multiple non-inlined schemas, only one will have generated openapi specs. E.g. if I have a code like:

#[derive(Debug, Serialize, Deserialize, Schema)]
#[schema(component = "One")]
pub struct One {
}
#[derive(Debug, Serialize, Deserialize, Schema)]
#[schema(component = "Two")]
pub struct Two {
}

then only one actual schema will be generated in the openapi specification:

 "components": {
    "schemas": {
      "One": {
            ....
        }
    }
}

even though there will be two references in the API definition itself:

              ...
              "schema": {
                "$ref": "#/components/schemas/One"
              }
              ...
              "schema": {
                "$ref": "#/components/schemas/Two"
              }

Just in case: I triple-checked that I use different "component" name in the two structs.

Enum value of a request path parameter

Hi everyone,

Is it possible to implement Enum type in path ? :

image

#[get("/{color}")]
async fn get_color(color: Color) -> Result<impl rweb::Reply, Rejection> {

}

#[derive(Debug, Serialize, Deserialize, Schema)]
pub enum Color {
    BLACK,
    BLUE,
}

impl std::str::FromStr for Color {
    type Err = &'static str;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "black" => Ok(Color::BLACK),
            "blue" => Ok(Color::BLUE),
            _ => Err("ERR"),
        }
    }
}

impl fmt::Display for Color {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}
---
openapi: 3.0.1
info:
  title: ""
  version: ""
paths:
  "/{color}":
    get:
      parameters:
        - in: path
          name: color
          required: true
          schema:
            type: string
            enum:
              - black
              - blue
      responses:
        "200":
          description: ""
          content:
            text/plain:
              schema:
                type: string
        

OpenAPI component definition not included in Spec when struct only ever used in response

Using rweb version 0.5.1 with openapi feature.

Example struct:

#[derive(Debug, Serialize, Deserialize, Schema)]
#[schema(component = "Item")]
struct Item {
    data: i32,
}

The component definition is included in the Spec when the struct is used as part of the request.

This works:

#[post("/item")]
fn post_item(item: Json<Item>) -> String {
    String::new()
}

However, if the struct is not used as an input parameter across the entire spec. For example if you only have one single method like the example below, then the component definition is not included in the Spec:

#[get("/item")]
fn get_item() -> Json<Item> {
    Item { data: 42 }.into()
}

By the way, I really like this web framework. It's pretty amazing!

Invalid fragment specifier

error: invalid fragment specifier `attr`
  --> /home/shmendez/.cargo/registry/src/github.com-1ecc6299db9ec823/rweb-0.6.1/src/routes.rs:42:7
   |
42 |     ( $s:attr ) => {
   |       ^^^^^^^
   |
   = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`

error: could not compile `rweb` due to previous error

After doing tons of research I can't seem to find anything on the attr fragment. I changed it to expr and everything seems to work just fine. What's going on here?

Incorrect OpenAPI spec for Result type

I'm very new to this, sorry if I misunderstood something. Minimal example is below:

#[derive(Serialize, Schema)]
enum MyError {
  CustomError,
}

#[get("/test_result")]
fn test_result() -> Json<Result<String, MyError>> {
  Json::from(Err(MyError::CustomError))
}

This filter returns following OpenAPI spec:

{
   "openapi":"3.0.1",
   "info":{
      "title":"",
      "version":""
   },
   "paths":{
      "/test_result":{
         "get":{
            "responses":{
               "200":{
                  "description":"",
                  "content":{
                     "application/json":{
                        "schema":{
                           "oneOf":[
                              {
                                 "type":"string"
                              },
                              {
                                 "type":"string",
                                 "enum":[
                                    "CustomError"
                                 ]
                              }
                           ]
                        }
                     }
                  }
               }
            }
         }
      }
   }
}

However, running the code gives this output:

{
  "Err": "CustomError"
}

Note that serde-json represents Result type as either {"Ok": <T>} or {"Err": <E>}.

Seems like the correct OpenAPI should be something like that:

...
"schema": {
  "oneOf": [
    {
      "type": "object",
      "properties": {
        "Ok": {
          "type": "string"
        }
      }
    },
    {
      "type": "object",
      "properties": {
        "Err": {
          "type": "string",
          "enum": [
            "CustomError"
          ]
        }
      }
    }
  ]
}
...

openapi: adding "description" removes "example"

The "description" and "example" seem to be mutually exclusive in rweb-generated openapi.

If I have a code like this:

#[derive(Debug, Serialize, Deserialize, Schema)]
pub struct ExampleTest {
    #[schema(example = "10")]
    data: usize,
    #[schema(example = "\"Example for string values must be escaped like this\"")]
    something: String,
}

the generated openapi specification would have:

              "schema": {
                "type": "object",
                "properties": {
                  "data": {
                    "type": "integer",
                    "example": 10
                  },
                  "something": {
                    "type": "string",
                    "example": "Example for string values must be escaped like this"
                  }
                }
              }

as we see, examples are provided for both fields.

But if we then add a rustdoc comment to the code:

#[derive(Debug, Serialize, Deserialize, Schema)]
pub struct ExampleTest {
    /// this is a description
    #[schema(example = "10")]
    data: usize,
    #[schema(example = "\"Example for string values must be escaped like this\"")]
    something: String,
}

then the generated openapi description will have:

              "schema": {
                "type": "object",
                "properties": {
                  "data": {
                    "description": " this is a description",
                    "type": "integer"
                  },
                  "something": {
                    "type": "string",
                    "example": "Example for string values must be escaped like this"
                  }
                }
              }

As we see, the example section for the documented field is gone.

Thoughts?

dependency injection not possible?

Hi. I'm trying to write an rweb API. The way I wanted to do it, is to use dependency injection to provide some parameters for the API layer. Something along the lines of:

pub struct ApiLayer {
  pub name: String,
  pub db: Database,
}

impl ApiLayer {
  #[get("/output")]
  fn output(&self) -> String {
      format!("Hello from api layer named {}", self.name)  // <------- problem here
  }

  pub async fn run_server(&self) {
    serve(...);
  }
}

The problem, however, is that the compiler complains with something like:

^^^^ `self` value is a keyword only available in methods with a `self` parameter
...
 this function doesn't have a `self` parameter

It would seem that the macro has eliminated the self parameter, and it is thus no longer available inside the method. If I comment out the #[get("/output")] line, things start to compile.

Question is: did I correctly interpret what's happening?
And follow-up: is there any work-around?

Thanks!

openapi: c-like enums appear in the generated spec as object instead of string

When generating Schema for c-like enums the macro leaves the schema_type parameter as default, which is Object. However, it might be better to specify the schema_type as string. If not the visual render of the generated openapi spec doesn't show any possible values for that field. If I add "type": "string" annotation to the generated openapi.json spec it is rendered correctly.
Another way of handling this is to allow to manually specify which type to use via #[schema = ....] attribute on the enum.
What do you think?

Spec gen - Serde skip

One can use skip attributes to skip (ser,de or both) a particular field.
Example

#[derive(serde::Serialize, serde::Deserialize, rweb::Schema, PartialEq, Clone, Debug)]
struct Whence {
	always: u64,
	#[serde(skip_deserializing)]
	onlyYeet: u64,
	#[serde(skip_serializing)]
	onlyTake: u64,
	#[serde(skip)]
	nevah: u64,
}

Sadly (rweb0.6) currently generates includes all fields in the specification.
The plain skipped field is a bug and simply should be excluded from spec.
The skip_... are trickier, as de skipped fields should be excluded only from body specs, while ser skipped fields - only from responses. I am not sure how that would work with components*, neither whether the contextual information to make such a decision is passed down in the calls when generating spec as of currently (from quick glance it seems that no).

Also, there's skip_serializing_if and i think the best that can be done here is not marking it required, once #50 is closed (ironically it's the only skip attribute that works correctly currently :D).

* Sidenote: In NodeJS realm, TSOA handles such cases (and parametrized types) by generating a component per distinct type (and then including paramer types names in component's name).

Implement openapi::Entity for chrono types

Crate chrono contains chrono::Date and chrono::DateTime structs that have Serialize and Deserialize implemented to match the OAS formats date and date-time. I wonder if it makes sense to have a default implementation of rweb::openapi::Entity for chrono::Date and chrono::DateTime so it's possible to use #[derive(Schema)] for structs that contain chrono types?

Example of handling errors when using openapi

Could you please provide an example how to handle errors when implement service in a form of
#[get("/{id}")] #[openapi(id = "products.get")] #[openapi(summary = "Get a product")] fn product(id: String) -> Json<Product> { Product { id, title: Default::default(), } .into() }

Let's say that product was not found, how can I return e.g. status code other then 200 to indicate this?
rweb/examples/dyn_reply.rs - doesn't work well as openapi documentation can't be generated out of this method signature.

OpenAPI spec generation - Detect recursion in components

When generating OpenAPI specs sometimes it is useful to be able to specify recursive and mutually recursive components, which I think is allowed if the components are named.

I have included two examples of what I would like to detect as an error and what I would like to allow in a file here.

https://github.com/johnchildren/rweb-openapi-recursion-example/blob/main/src/lib.rs

Currently these examples all stack overflow, but I think this could be resolved by adding a recursion limit to the collector and either returning an error or explicitly panicing when it is reached.

As an extension, by using component names I think it would be possible to detect if a component has been visited before and prevent further recursion that way, but I am not certain about this.

Am willing to spend some time implementing this if it is a desirable feature.

Global route registrar

I'm wondering if we can write some code to simplify this long list of or

#[tokio::main]
async fn main() {
    serve(output().or(product()).or(products()).or(search())).run(([127, 0, 0, 1], 3030)).await;
}

i'm wondering if we can use lazy_static to have some sort of global registrar of routes that's called using the rweb macros get,post, etc. And we could possibly simplify server startup to:

#[tokio::main]
async fn main() {
    rweb::run_server(([127, 0, 0, 1], 3030)).await;
}

We'd have a version for HTTP 2 as well

Does that sound like an idea you'd like?

ย #[router] moves #[data] to the first filter

When I use the #[router] attribute with multiple services that use #[data] arguments, the generated code doesn't clone #[data].

Example code:

#[router(
    "/tata/",
    services(
        svc1,
        svc2,
        svc3
    )
)]
pub fn my_router(
    #[data] config: AppConfig,
    #[data] mongo: MongoClient,
    #[data] vault: Arc<dyn Vault>,
) {}

Errors:

29 |     #[data] config: AppConfig,
   |             ^^^^^^
   |             |
   |             value moved here
   |             value used here after move

Generated code by cargo expand:

    pub fn my_router(
        config: AppConfig,
        mongo: MongoClient,
        vault: Arc<dyn Vault>,
    ) -> impl Clone + rweb::Filter<Extract = (impl rweb::Reply,), Error = rweb::Rejection> {
        use rweb::{rt::StatusCode, Filter};
        rweb::openapi::with(|__collector: Option<&mut rweb::openapi::Collector>| {
            if let Some(__collector) = __collector {
                __collector.with_appended_prefix("/tata/", ::alloc::vec::Vec::new(), || {
                    rweb::filters::path::path("tata").and(
                        svc1(config, mongo, vault)
                            .or(svc2(config, mongo, vault))
                            .or(svc3(config, mongo, vault)),
                    )
                })
            } else {
                rweb::filters::path::path("tata").and(
                    svc1(config, mongo, vault)
                        .or(svc2(config, mongo, vault))
                        .or(svc3(config, mongo, vault)),
                )
            }
        })
    }

Data arguments should be cloned in methods calls.

OpenAPI spec gen - detect serde enum representation

Currently the spec generator for enums always assumes an untagged representation, i.e. #[serde(untagged)], which means the spec is not generated correctly for the other cases (and the serde default one).

Particularities: internally and adjacently tagged representations should be handled using discriminator/propertyName iff all variants are components (as per "When using the discriminator, inline schemas will not be considered." discriminator specification).

References:

Example of utility: internally tagged representation can nicely describe GeoJSON primitives (spec).

Related to #72.

Error compiling example

error[E0432]: unresolved import syn::export
--> /home/skynet/.cargo/registry/src/github.com-1ecc6299db9ec823/rweb-macros-0.3.0-alpha.1/src/openapi.rs:19:5
|
19 | export::ToTokens,
| ^^^^^^ could not find export in syn

Inject dependency into a filter using openapi macro

Hello,

I'm using the openapi feature and trying to pass the database service to the filter, is this possible?
I need to implement a custom filter to compare the header content against the database records.

This is the code I have:

fn authenticate(db_service: DbService,) -> impl Clone + Filter<Extract = (), Error = Rejection> {
    let token = rweb::header::<String>("authorization");

    db_service.verify_token(&token);
    // TODO: Return filter
}
#[get("/products")]
#[openapi(id = "products.get")]
#[openapi(summary = "List products")]
#[openapi(response(code = "200", description = "OK"))]
pub fn list_products(
    #[data] db_service: DbService,
    #[filter = "authenticate(db_service)"] _header: (),
) -> Json<Vec<Product>> {
    Json::from(db_service.list_products().unwrap())
}

When I run this code it returns the following error:

error: custom attribute panicked
  --> src/api/products.rs:33:1
   |
33 | #[get("/products")]
   | ^^^^^^^^^^^^^^^^^^
   |
   = help: message: unexpected token

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.