diff --git a/Cargo.toml b/Cargo.toml index 981618db9..59ea08ebf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = ["crates/*"] resolver = "2" [workspace.package] -version = "0.49.1" +version = "0.50.0" authors = ["Chrislearn Young "] edition = "2021" rust-version = "1.67" @@ -80,23 +80,23 @@ rustls = "0.21.1" rustls-pemfile = "1.0" rust-embed = "6" salvo-utils = { version = "0.0.5", default-features = true } -salvo_macros = { version = "0.49.1", path = "crates/macros", default-features = false } -salvo_core = { version = "0.49.1", path = "crates/core", default-features = false } -salvo_extra = { version = "0.49.1", path = "crates/extra", default-features = false } -salvo-compression = { version = "0.49.1", path = "crates/compression", default-features = false } -salvo-cache = { version = "0.49.1", path = "crates/cache", default-features = false } -salvo-cors = { version = "0.49.1", path = "crates/cors", default-features = false } -salvo-csrf = { version = "0.49.1", path = "crates/csrf", default-features = false } -salvo-flash = { version = "0.49.1", path = "crates/flash", default-features = false } +salvo_macros = { version = "0.50.0", path = "crates/macros", default-features = false } +salvo_core = { version = "0.50.0", path = "crates/core", default-features = false } +salvo_extra = { version = "0.50.0", path = "crates/extra", default-features = false } +salvo-compression = { version = "0.50.0", path = "crates/compression", default-features = false } +salvo-cache = { version = "0.50.0", path = "crates/cache", default-features = false } +salvo-cors = { version = "0.50.0", path = "crates/cors", default-features = false } +salvo-csrf = { version = "0.50.0", path = "crates/csrf", default-features = false } +salvo-flash = { version = "0.50.0", path = "crates/flash", default-features = false } salvo-http3 = { version = "0.0.4", default-features = false } -salvo-jwt-auth = { version = "0.49.1", path = "crates/jwt-auth", default-features = false } -salvo-oapi = { version = "0.49.1", path = "./crates/oapi", default-features = false } -salvo-oapi-macros = { version = "0.49.1", path = "crates/oapi-macros", default-features = false } -salvo-otel = { version = "0.49.1", path = "crates/otel", default-features = false } -salvo-proxy = { version = "0.49.1", path = "crates/proxy", default-features = false } -salvo-rate-limiter = { version = "0.49.1", path = "crates/rate-limiter", default-features = false } -salvo-serve-static = { version = "0.49.1", path = "crates/serve-static", default-features = false } -salvo-session = { version = "0.49.1", path = "crates/session", default-features = false } +salvo-jwt-auth = { version = "0.50.0", path = "crates/jwt-auth", default-features = false } +salvo-oapi = { version = "0.50.0", path = "./crates/oapi", default-features = false } +salvo-oapi-macros = { version = "0.50.0", path = "crates/oapi-macros", default-features = false } +salvo-otel = { version = "0.50.0", path = "crates/otel", default-features = false } +salvo-proxy = { version = "0.50.0", path = "crates/proxy", default-features = false } +salvo-rate-limiter = { version = "0.50.0", path = "crates/rate-limiter", default-features = false } +salvo-serve-static = { version = "0.50.0", path = "crates/serve-static", default-features = false } +salvo-session = { version = "0.50.0", path = "crates/session", default-features = false } serde = "1" serde_json = "1" serde-xml-rs = "0.6" diff --git a/crates/core/src/conn/proto.rs b/crates/core/src/conn/proto.rs index bd3d5269c..6ae682d8e 100644 --- a/crates/core/src/conn/proto.rs +++ b/crates/core/src/conn/proto.rs @@ -186,11 +186,9 @@ where if !prefix.is_empty() { self.pre = Some(prefix); } - println!("======poll ready {} remaing: {}", copy_len, buf.remaining()); return Poll::Ready(Ok(())); } } - println!("======inner poll poll_read"); Pin::new(&mut self.inner).poll_read(cx, buf) } } diff --git a/crates/oapi/Cargo.toml b/crates/oapi/Cargo.toml index d0c8b47ae..59b133cac 100644 --- a/crates/oapi/Cargo.toml +++ b/crates/oapi/Cargo.toml @@ -14,8 +14,10 @@ authors = ["Juha Kukkonen ", "Chrislearn Young +//! +use salvo_core::writing::Text; +use salvo_core::{async_trait, Depot, FlowCtrl, Handler, Request, Response, Router}; + +const INDEX_TMPL: &str = r#" + + + + + + + + + + +"#; + +/// Implements [`Handler`] for serving RapiDoc. +#[derive(Clone, Debug)] +pub struct RapiDoc { + spec_url: String, + html: String, +} +impl RapiDoc { + /// Create a new [`RapiDoc`] for given path. + /// + /// Path argument will expose the RapiDoc to the user and should be something that + /// the underlying application framework / library supports. + /// + /// # Examples + /// + /// ```rust + /// # use salvo_oapi::rapidoc::RapiDoc; + /// let doc = RapiDoc::new("/rapidoc/openapi.json"); + /// ``` + pub fn new(spec_url: impl Into) -> Self { + let spec_url = spec_url.into(); + Self { + html: INDEX_TMPL.replace("{{spec_url}}", &spec_url), + spec_url, + } + } + + /// Returns the spec url. + pub fn sepec_url(&self) -> &str { + &self.spec_url + } + + /// Consusmes the [`RapiDoc`] and returns [`Router`] with the [`RapiDoc`] as handler. + pub fn into_router(self, path: impl Into) -> Router { + Router::with_path(path.into()).handle(self) + } +} + +#[async_trait] +impl Handler for RapiDoc { + async fn handle(&self, _req: &mut Request, _depot: &mut Depot, res: &mut Response, _ctrl: &mut FlowCtrl) { + res.render(Text::Html(&self.html)); + } +} \ No newline at end of file diff --git a/crates/oapi/src/redoc/mod.rs b/crates/oapi/src/redoc/mod.rs new file mode 100644 index 000000000..1c1e80c4c --- /dev/null +++ b/crates/oapi/src/redoc/mod.rs @@ -0,0 +1,86 @@ +//! This crate implements necessary boiler plate code to serve ReDoc via web server. It +//! works as a bridge for serving the OpenAPI documentation created with [`salvo`][salvo] library in the +//! ReDoc. +//! +//! [salvo]: +//! +use salvo_core::writing::Text; +use salvo_core::{async_trait, Depot, FlowCtrl, Handler, Request, Response, Router}; + +const INDEX_TMPL: &str = r#" + + + + Redoc + + + + + + + +
+ + + + + +"#; + +/// Implements [`Handler`] for serving ReDoc. +#[derive(Clone, Debug)] +pub struct ReDoc { + spec_url: String, + html: String, +} +impl ReDoc { + /// Create a new [`ReDoc`] for given path. + /// + /// Path argument will expose the ReDoc to the user and should be something that + /// the underlying application framework / library supports. + /// + /// # Examples + /// + /// ```rust + /// # use salvo_oapi::rapidoc::ReDoc; + /// let doc = ReDoc::new("/rapidoc/openapi.json"); + /// ``` + pub fn new(spec_url: impl Into) -> Self { + let spec_url = spec_url.into(); + Self { + html: INDEX_TMPL.replace("{{spec_url}}", &spec_url), + spec_url, + } + } + + /// Returns the spec url. + pub fn sepec_url(&self) -> &str { + &self.spec_url + } + + /// Consusmes the [`ReDoc`] and returns [`Router`] with the [`ReDoc`] as handler. + pub fn into_router(self, path: impl Into) -> Router { + Router::with_path(path.into()).handle(self) + } +} + +#[async_trait] +impl Handler for ReDoc { + async fn handle(&self, _req: &mut Request, _depot: &mut Depot, res: &mut Response, _ctrl: &mut FlowCtrl) { + res.render(Text::Html(&self.html)); + } +} \ No newline at end of file diff --git a/crates/oapi/src/swagger_ui/mod.rs b/crates/oapi/src/swagger_ui/mod.rs index 8915e965b..ff43802e4 100644 --- a/crates/oapi/src/swagger_ui/mod.rs +++ b/crates/oapi/src/swagger_ui/mod.rs @@ -8,7 +8,6 @@ use std::borrow::Cow; mod config; pub mod oauth; -use crate::OpenApi; pub use config::Config; use rust_embed::RustEmbed; use salvo_core::http::uri::{Parts as UriParts, Uri}; @@ -77,9 +76,7 @@ const INDEX_TMPL: &str = r#" /// Implements [`Handler`] for serving Swagger UI. #[derive(Clone, Debug)] pub struct SwaggerUi { - urls: Vec<(Url<'static>, OpenApi)>, config: Config<'static>, - external_urls: Vec<(Url<'static>, serde_json::Value)>, } impl SwaggerUi { /// Create a new [`SwaggerUi`] for given path. @@ -94,18 +91,11 @@ impl SwaggerUi { /// let swagger = SwaggerUi::new("/swagger-ui/{_:.*}"); /// ``` pub fn new(config: impl Into>) -> Self { - Self { - urls: Vec::new(), - config: config.into(), - external_urls: Vec::new(), - } + Self { config: config.into() } } /// Add api doc [`Url`] into [`SwaggerUi`]. /// - /// Method takes two arguments where first one is path which exposes the [`OpenApi`] to the user. - /// Second argument is the actual Rust implementation of the OpenAPI doc which is being exposed. - /// /// Calling this again will add another url to the Swagger UI. /// /// # Examples @@ -115,11 +105,10 @@ impl SwaggerUi { /// # use salvo_oapi::OpenApi; /// /// let swagger = SwaggerUi::new("/api-doc/openapi.json") - /// .url("/api-docs/openapi2.json", OpenApi::new("example api", "0.0.1")); + /// .url("/api-docs/openapi2.json"); /// ``` - pub fn url>>(mut self, url: U, openapi: OpenApi) -> Self { - self.urls.push((url.into(), openapi)); - + pub fn url>>(mut self, url: U) -> Self { + self.config.urls.push(url.into()); self } @@ -140,77 +129,13 @@ impl SwaggerUi { /// let swagger = SwaggerUi::new("/swagger-ui/{_:.*}") /// .urls( /// vec![ - /// (Url::with_primary("api doc 1", "/api-docs/openapi.json", true), OpenApi::new("example api", "0.0.1")), - /// (Url::new("api doc 2", "/api-docs/openapi2.json"), OpenApi::new("example api2", "0.0.1")) + /// (Url::with_primary("api doc 1", "/api-docs/openapi.json", true)), + /// (Url::new("api doc 2", "/api-docs/openapi2.json")) /// ] /// ); /// ``` - pub fn urls(mut self, urls: Vec<(Url<'static>, OpenApi)>) -> Self { - self.urls = urls; - - self - } - - /// Add external API doc to the [`SwaggerUi`]. - /// - /// This operation is unchecked and so it does not check any validity of provided content. - /// Users are required to do their own check if any regarding validity of the external - /// OpenAPI document. - /// - /// Method accepts two arguments, one is [`Url`] the API doc is served at and the second one is - /// the [`serde_json::Value`] of the OpenAPI doc to be served. - /// - /// # Examples - /// - /// Add external API doc to the [`SwaggerUi`]. - ///```rust - /// # use salvo_oapi::swagger_ui::{SwaggerUi, Url}; - /// # use salvo_oapi::OpenApi; - /// # use serde_json::json; - /// let external_openapi = json!({"openapi": "3.0.0"}); - /// - /// let swagger = SwaggerUi::new("/swagger-ui/{_:.*}") - /// .external_url_unchecked("/api-docs/openapi.json", external_openapi); - ///``` - pub fn external_url_unchecked>>(mut self, url: U, openapi: serde_json::Value) -> Self { - self.external_urls.push((url.into(), openapi)); - - self - } - - /// Add external API docs to the [`SwaggerUi`] from iterator. - /// - /// This operation is unchecked and so it does not check any validity of provided content. - /// Users are required to do their own check if any regarding validity of the external - /// OpenAPI documents. - /// - /// Method accepts one argument, an `iter` of [`Url`] and [`serde_json::Value`] tuples. The - /// [`Url`] will point to location the OpenAPI document is served and the [`serde_json::Value`] - /// is the OpenAPI document to be served. - /// - /// # Examples - /// - /// Add external API docs to the [`SwaggerUi`]. - ///```rust - /// # use salvo_oapi::swagger_ui::{SwaggerUi, Url}; - /// # use salvo_oapi::OpenApi; - /// # use serde_json::json; - /// let external_openapi = json!({"openapi": "3.0.0"}); - /// let external_openapi2 = json!({"openapi": "3.0.0"}); - /// - /// let swagger = SwaggerUi::new("/swagger-ui/{_:.*}") - /// .external_urls_from_iter_unchecked([ - /// ("/api-docs/openapi.json", external_openapi), - /// ("/api-docs/openapi2.json", external_openapi2) - /// ]); - ///``` - pub fn external_urls_from_iter_unchecked, U: Into>>( - mut self, - external_urls: I, - ) -> Self { - self.external_urls - .extend(external_urls.into_iter().map(|(url, doc)| (url.into(), doc))); - + pub fn urls(mut self, urls: Vec>) -> Self { + self.config.urls = urls; self } @@ -424,7 +349,7 @@ pub fn serve<'a>(path: &str, config: &Config<'a>) -> Result - - - - - Swagger UI - - - - - - - -
- - - - - diff --git a/crates/salvo/src/lib.rs b/crates/salvo/src/lib.rs index 6ebcde133..abe2fcaa3 100644 --- a/crates/salvo/src/lib.rs +++ b/crates/salvo/src/lib.rs @@ -209,5 +209,7 @@ pub mod prelude { #![feature ="oapi"] pub use crate::oapi::{endpoint, EndpointArgRegister, EndpointOutRegister, OpenApi, ToSchema, ToResponse, ToResponses}; pub use crate::oapi::swagger_ui::SwaggerUi; + pub use crate::oapi::rapidoc::RapiDoc; + pub use crate::oapi::redoc::ReDoc; } } diff --git a/examples/oapi-todos/src/main.rs b/examples/oapi-todos/src/main.rs index a800c71fd..1eb64c813 100644 --- a/examples/oapi-todos/src/main.rs +++ b/examples/oapi-todos/src/main.rs @@ -23,7 +23,9 @@ async fn main() { let router = router .push(doc.into_router("/api-doc/openapi.json")) - .push(SwaggerUi::new("/api-doc/openapi.json").into_router("/")); + .push(SwaggerUi::new("/api-doc/openapi.json").into_router("/swagger-ui")) + .push(RapiDoc::new("/api-doc/openapi.json").into_router("/rapidoc")) + .push(ReDoc::new("/api-doc/openapi.json").into_router("/redoc")); let acceptor = TcpListener::new("127.0.0.1:5800").bind().await; Server::new(acceptor).serve(router).await;