diff --git a/.gitignore b/.gitignore index ea8c4bf..0b42d2d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -/target +/target diff --git a/.rustfmt.toml b/.rustfmt.toml index 0612b80..2edfbb5 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1,11 +1,11 @@ -condense_wildcard_suffixes = true -edition = "2021" -fn_single_line = true -format_code_in_doc_comments = true -format_macro_matchers = true -hard_tabs = true -match_block_trailing_comma = true -imports_granularity = "Crate" -newline_style = "Unix" -group_imports = "StdExternalCrate" -tab_spaces = 4 +condense_wildcard_suffixes = true +edition = "2021" +fn_single_line = true +format_code_in_doc_comments = true +format_macro_matchers = true +hard_tabs = true +match_block_trailing_comma = true +imports_granularity = "Crate" +newline_style = "Unix" +group_imports = "StdExternalCrate" +tab_spaces = 4 diff --git a/Cargo.lock b/Cargo.lock index c794669..d8eeddc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -641,7 +641,7 @@ dependencies = [ [[package]] name = "leptos_reactive_axum" -version = "0.1.0" +version = "1.0.0" dependencies = [ "axum", "axum-test", @@ -652,6 +652,7 @@ dependencies = [ "serde_json", "thiserror", "tokio", + "tower", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 64d0ce5..4b0c5bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = ["macros", "."] [package] name = "leptos_reactive_axum" -version = "0.1.0" +version = "1.0.0" edition = "2021" description = "reactive context for axum handlers" authors = ["davidontop "] @@ -13,15 +13,19 @@ license = "MIT" repository = "https://git.davidon.top/public/leptos_reactive_axum.git" [dependencies] -leptos_reactive_axum_macros = { path = "./macros", version = "0" } +leptos_reactive_axum_macros = { path = "./macros", version = "0", optional = true } leptos_reactive = { version = "0.6", features = ["ssr"] } axum = "0.7" scopeguard = "1.2.0" thiserror = "1.0.61" http = "1.1.0" +tower = { version = "0.4.13", optional = true } [features] +default = ["middleware"] nightly = ["leptos_reactive/nightly"] +macros = ["dep:leptos_reactive_axum_macros"] +middleware = ["dep:tower"] [dev-dependencies] axum-test = "15.1.0" diff --git a/LICENSE b/LICENSE index 9b84d0d..437f690 100644 --- a/LICENSE +++ b/LICENSE @@ -1,22 +1,22 @@ -MIT License - -Copyright (c) 2023 DavidOnTop - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - +MIT License + +Copyright (c) 2023 DavidOnTop + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/README.md b/README.md index c632700..c64d2d2 100644 --- a/README.md +++ b/README.md @@ -1 +1,5 @@ -# ctxt +# whoami +Library providing utilities for using leptos_reactive runtime inside axum handlers. Also automaticly uses provide_context to make the request Parts available inside any function called by the handler. + +# But why? +When using something like the [rstml-component](https://crates.io/crates/rstml-component) crate it is usefull to be able to extract anything from parts inside any component. For example inside nav bar extract the path to be able to highlight active route. Additionally you can create a middleware layer that can add to the context things like IsLoggedIn or other information about the request that is used within more then one function/component called by the handler. diff --git a/macros/Cargo.toml b/macros/Cargo.toml index b76843a..7a2ec90 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "leptos_reactive_axum_macros" -version = "0.1.0" +version = "1.0.0" edition = "2021" description = "reactive context for axum handlers" authors = ["davidontop "] diff --git a/macros/src/lib.rs b/macros/src/lib.rs index a54e9a7..ec11d39 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,6 +1,7 @@ mod reactive; /// macro that when applied on an axum handler will provide leptos_reactive runtime and will allow you to use `leptos_reactive_axum::extract` by providing a context holding request parts +/// WARNING: inserts .unwrap() into handlers to make types the same as in function arguments, use middleware to avoid this #[proc_macro_attribute] pub fn reactive( attr: proc_macro::TokenStream, diff --git a/src/lib.rs b/src/lib.rs index 22cc5cf..f4c7c33 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,12 @@ pub mod error; +#[cfg(feature = "middleware")] +pub mod middleware; use std::fmt::Debug; use axum::{extract::FromRequestParts, http::request::Parts}; use error::ExtractionError; +#[cfg(feature = "macros")] pub use leptos_reactive_axum_macros::reactive; /// used to extract request parts from handlers, should be used in conjunction with `leptos_reactive_axum_macros::reactive macro` diff --git a/src/middleware.rs b/src/middleware.rs new file mode 100644 index 0000000..cac226d --- /dev/null +++ b/src/middleware.rs @@ -0,0 +1,52 @@ +use std::{future::Future, pin::Pin}; + +use axum::{extract::Request, response::Response}; +use tower::{Layer, Service}; + +/// ```rust +/// axum::Router::new().layer(ReactiveLayer); +/// ``` +#[derive(Clone)] +pub struct ReactiveLayer; +impl Layer for ReactiveLayer { + type Service = ReactiveMiddleware; + + fn layer(&self, inner: S) -> Self::Service { + ReactiveMiddleware {inner} + } +} + +#[derive(Clone)] +pub struct ReactiveMiddleware { + inner: S, +} + +impl Service for ReactiveMiddleware +where + S: Service + Send + 'static, + S::Future: Send + 'static, +{ + type Response = S::Response; + type Error = S::Error; + type Future = Pin> + Send + 'static>>; + + fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> std::task::Poll> { + self.inner.poll_ready(cx) + } + + fn call(&mut self, req: Request) -> Self::Future { + let __runtime = leptos_reactive::create_runtime(); + let (parts, body) = req.into_parts(); + leptos_reactive::provide_context(parts.clone()); + let req = Request::from_parts(parts, body); + + let future = self.inner.call(req); + Box::pin(async move { + let response: Response = future.await?; + + __runtime.dispose(); + + Ok(response) + }) + } +} diff --git a/tests/middleware.rs b/tests/middleware.rs new file mode 100644 index 0000000..cbc25e4 --- /dev/null +++ b/tests/middleware.rs @@ -0,0 +1,34 @@ +use axum::{response::IntoResponse, routing::get, Json, Router}; +use axum_test::TestServer; +use http::{HeaderMap, HeaderName, HeaderValue}; +use leptos_reactive_axum::extract; +use serde_json::{json, Value}; + +async fn handler(Json(json): Json) -> impl IntoResponse { + let headers: HeaderMap = extract().await.unwrap(); + + assert_eq!( + headers.get("macrosareawesome"), + Some(&HeaderValue::from_static("YehTheyAre")) + ); + assert_eq!(json.as_str(), Some("hello there")); + + "" +} + +#[tokio::test] +async fn test_handlers() { + let router = Router::new() + .route("/", get(handler)) + .layer(leptos_reactive_axum::middleware::ReactiveLayer); + + let server = TestServer::new(router).unwrap(); + + let _ = server + .get("/") + .add_header( + HeaderName::from_static("macrosareawesome"), + HeaderValue::from_static("YehTheyAre"), + ) + .json(&json!("hello there")); +}