diff options
| -rw-r--r-- | Cargo.toml | 6 | ||||
| -rw-r--r-- | Makefile.toml | 3 | ||||
| -rw-r--r-- | examples/struct_router.rs | 49 | ||||
| -rw-r--r-- | rossweisse/Cargo.toml | 16 | ||||
| -rw-r--r-- | rossweisse/src/implementations.rs | 22 | ||||
| -rw-r--r-- | rossweisse/src/implementations/route.rs | 22 | ||||
| -rw-r--r-- | rossweisse/src/implementations/router.rs | 23 | ||||
| -rw-r--r-- | rossweisse/src/implementations/router/fields.rs | 86 | ||||
| -rw-r--r-- | rossweisse/src/implementations/router/methods.rs | 66 | ||||
| -rw-r--r-- | rossweisse/src/implementations/router/parser.rs | 21 | ||||
| -rw-r--r-- | rossweisse/src/implementations/router/parser/field_initializer.rs | 39 | ||||
| -rw-r--r-- | rossweisse/src/implementations/router/parser/field_initializers.rs | 28 | ||||
| -rw-r--r-- | rossweisse/src/lib.rs | 91 |
13 files changed, 472 insertions, 0 deletions
@@ -1,5 +1,8 @@ # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[workspace] +members = ["rossweisse"] + [package] name = "windmark" version = "0.3.6" @@ -45,3 +48,6 @@ matchit = "0.6.0" tree_magic = { version = "0.2.3", optional = true } # MIME paste = "1.0.12" # Token Pasting + +[dev-dependencies] +rossweisse = { version = "*", path = "./rossweisse" } diff --git a/Makefile.toml b/Makefile.toml index 543059e..f921fc6 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -1,3 +1,6 @@ +[config] +default_to_workspace = false + [tasks.fmt] args = ["fmt"] command = "cargo" diff --git a/examples/struct_router.rs b/examples/struct_router.rs new file mode 100644 index 0000000..bf559ab --- /dev/null +++ b/examples/struct_router.rs @@ -0,0 +1,49 @@ +// This file is part of Windmark <https://github.com/gemrest/windmark>. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// Copyright (C) 2022-2023 Fuwn <[email protected]> +// SPDX-License-Identifier: GPL-3.0-only + +//! `cargo run --example struct_router` + +use rossweisse::route; + +#[rossweisse::router] +struct Router { + _phantom: (), +} + +#[rossweisse::router] +impl Router { + #[route] + pub fn index( + _context: windmark::context::RouteContext, + ) -> windmark::Response { + windmark::Response::success("Hello, World!") + } +} + +#[windmark::main] +async fn main() -> Result<(), Box<dyn std::error::Error>> { + { + let mut router = Router::new(); + + router.router().set_private_key_file("windmark_private.pem"); + router.router().set_certificate_file("windmark_public.pem"); + + router + } + .run() + .await +} diff --git a/rossweisse/Cargo.toml b/rossweisse/Cargo.toml new file mode 100644 index 0000000..bba907c --- /dev/null +++ b/rossweisse/Cargo.toml @@ -0,0 +1,16 @@ +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[package] +name = "rossweisse" +version = "0.0.0" +authors = ["Fuwn <[email protected]>"] +edition = "2021" +license = "GPL-3.0-only" + +[lib] +proc-macro = true + +[dependencies] +quote = "1.0.26" # Quasi-quoting +syn = "2.0.15" # Source Code Parsing +proc-macro2 = "1.0.56" # `proc-macro` Wrapper diff --git a/rossweisse/src/implementations.rs b/rossweisse/src/implementations.rs new file mode 100644 index 0000000..cdf6f7f --- /dev/null +++ b/rossweisse/src/implementations.rs @@ -0,0 +1,22 @@ +// This file is part of Windmark <https://github.com/gemrest/windmark>. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// Copyright (C) 2022-2023 Fuwn <[email protected]> +// SPDX-License-Identifier: GPL-3.0-only + +mod route; +mod router; + +pub use route::route; +pub use router::{fields, methods}; diff --git a/rossweisse/src/implementations/route.rs b/rossweisse/src/implementations/route.rs new file mode 100644 index 0000000..d6e2356 --- /dev/null +++ b/rossweisse/src/implementations/route.rs @@ -0,0 +1,22 @@ +// This file is part of Windmark <https://github.com/gemrest/windmark>. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// Copyright (C) 2022-2023 Fuwn <[email protected]> +// SPDX-License-Identifier: GPL-3.0-only + +use proc_macro::TokenStream; + +pub fn route(_arguments: TokenStream, item: syn::ItemFn) -> TokenStream { + quote::quote! { #item }.into() +} diff --git a/rossweisse/src/implementations/router.rs b/rossweisse/src/implementations/router.rs new file mode 100644 index 0000000..184e7a0 --- /dev/null +++ b/rossweisse/src/implementations/router.rs @@ -0,0 +1,23 @@ +// This file is part of Windmark <https://github.com/gemrest/windmark>. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// Copyright (C) 2022-2023 Fuwn <[email protected]> +// SPDX-License-Identifier: GPL-3.0-only + +mod fields; +mod methods; +mod parser; + +pub use fields::fields; +pub use methods::methods; diff --git a/rossweisse/src/implementations/router/fields.rs b/rossweisse/src/implementations/router/fields.rs new file mode 100644 index 0000000..e1a92b9 --- /dev/null +++ b/rossweisse/src/implementations/router/fields.rs @@ -0,0 +1,86 @@ +// This file is part of Windmark <https://github.com/gemrest/windmark>. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// Copyright (C) 2022-2023 Fuwn <[email protected]> +// SPDX-License-Identifier: GPL-3.0-only + +use proc_macro::TokenStream; +use quote::quote; +use syn::parse_macro_input; + +pub fn fields(arguments: TokenStream, item: syn::ItemStruct) -> TokenStream { + let field_initializers = + parse_macro_input!(arguments as super::parser::FieldInitializers); + let router_identifier = item.ident; + let named_fields = match item.fields { + syn::Fields::Named(fields) => fields, + _ => + panic!( + "`#[rossweisse::router]` can only be used on `struct`s with named \ + fields" + ), + }; + let mut default_expressions = vec![]; + let new_method_fields = named_fields.named.iter().map(|field| { + let name = &field.ident; + let initialiser = field_initializers + .0 + .iter() + .find(|initialiser| initialiser.ident == name.clone().unwrap()) + .map(|initialiser| &initialiser.expr) + .unwrap_or_else(|| { + default_expressions.push({ + let default_expression: syn::Expr = + syn::parse_quote! { ::std::default::Default::default() }; + + default_expression + }); + + default_expressions.last().unwrap() + }); + + quote! { + #name: #initialiser, + } + }); + let new_methods = quote! { + fn _new() -> Self { + Self { + #(#new_method_fields)* + router: ::windmark::Router::new(), + } + } + + pub async fn run(&mut self) -> Result<(), Box<dyn std::error::Error>> { + self.router.run().await + } + + pub fn router(&mut self) -> &mut ::windmark::Router { + &mut self.router + } + }; + let output_fields = named_fields.named; + let output = quote! { + struct #router_identifier { + #output_fields + router: ::windmark::Router, + } + + impl #router_identifier { + #new_methods + } + }; + + output.into() +} diff --git a/rossweisse/src/implementations/router/methods.rs b/rossweisse/src/implementations/router/methods.rs new file mode 100644 index 0000000..4f9bbdf --- /dev/null +++ b/rossweisse/src/implementations/router/methods.rs @@ -0,0 +1,66 @@ +// This file is part of Windmark <https://github.com/gemrest/windmark>. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// Copyright (C) 2022-2023 Fuwn <[email protected]> +// SPDX-License-Identifier: GPL-3.0-only + +use proc_macro::TokenStream; + +pub fn methods(_arguments: TokenStream, item: syn::ItemImpl) -> TokenStream { + let routes = item + .items + .iter() + .filter_map(|item| { + if let syn::ImplItem::Fn(method) = item { + if method + .attrs + .iter() + .any(|attribute| attribute.path().is_ident("route")) + { + Some(method.sig.ident.clone()) + } else { + None + } + } else { + None + } + }) + .collect::<Vec<_>>(); + let (implementation_generics, type_generics, where_clause) = + item.generics.split_for_impl(); + let name = &item.self_ty; + let route_paths = routes + .iter() + .map(|route| format!("/{}", route)) + .collect::<Vec<_>>(); + + quote::quote! { + #item + + impl #implementation_generics #name #type_generics #where_clause { + pub fn new() -> Self { + let mut router = Self::_new(); + + #( + router.router.mount(#route_paths, |context| { + Self::#routes(context) + }); + )* + + router + } + } + } + .into() +} diff --git a/rossweisse/src/implementations/router/parser.rs b/rossweisse/src/implementations/router/parser.rs new file mode 100644 index 0000000..1ef66fa --- /dev/null +++ b/rossweisse/src/implementations/router/parser.rs @@ -0,0 +1,21 @@ +// This file is part of Windmark <https://github.com/gemrest/windmark>. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// Copyright (C) 2022-2023 Fuwn <[email protected]> +// SPDX-License-Identifier: GPL-3.0-only + +mod field_initializer; +mod field_initializers; + +pub use field_initializers::FieldInitializers; diff --git a/rossweisse/src/implementations/router/parser/field_initializer.rs b/rossweisse/src/implementations/router/parser/field_initializer.rs new file mode 100644 index 0000000..212f88a --- /dev/null +++ b/rossweisse/src/implementations/router/parser/field_initializer.rs @@ -0,0 +1,39 @@ +// This file is part of Windmark <https://github.com/gemrest/windmark>. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// Copyright (C) 2022-2023 Fuwn <[email protected]> +// SPDX-License-Identifier: GPL-3.0-only + +use syn::parse; + +pub struct FieldInitializer { + pub ident: syn::Ident, + #[allow(unused)] + eq_token: syn::Token![=], + pub expr: syn::Expr, +} + +impl parse::Parse for FieldInitializer { + fn parse(input: parse::ParseStream<'_>) -> syn::Result<Self> { + let ident = input.parse()?; + let eq_token = input.parse()?; + let expr = input.parse()?; + + Ok(Self { + ident, + eq_token, + expr, + }) + } +} diff --git a/rossweisse/src/implementations/router/parser/field_initializers.rs b/rossweisse/src/implementations/router/parser/field_initializers.rs new file mode 100644 index 0000000..649b0d7 --- /dev/null +++ b/rossweisse/src/implementations/router/parser/field_initializers.rs @@ -0,0 +1,28 @@ +// This file is part of Windmark <https://github.com/gemrest/windmark>. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// Copyright (C) 2022-2023 Fuwn <[email protected]> +// SPDX-License-Identifier: GPL-3.0-only + +use syn::parse; + +use super::field_initializer::FieldInitializer; + +pub struct FieldInitializers(pub Vec<FieldInitializer>); + +impl parse::Parse for FieldInitializers { + fn parse(input: parse::ParseStream<'_>) -> syn::Result<Self> { + Ok(Self(syn::punctuated::Punctuated::<FieldInitializer, syn::Token![,]>::parse_terminated(input)?.into_iter().collect())) + } +} diff --git a/rossweisse/src/lib.rs b/rossweisse/src/lib.rs new file mode 100644 index 0000000..315a0de --- /dev/null +++ b/rossweisse/src/lib.rs @@ -0,0 +1,91 @@ +// This file is part of Windmark <https://github.com/gemrest/windmark>. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// Copyright (C) 2022-2023 Fuwn <[email protected]> +// SPDX-License-Identifier: GPL-3.0-only + +#![deny( + clippy::all, + clippy::nursery, + clippy::pedantic, + future_incompatible, + nonstandard_style, + rust_2018_idioms, + unsafe_code, + unused, + warnings +)] +#![recursion_limit = "128"] + +mod implementations; + +use proc_macro::TokenStream; +use syn::Item; + +/// Marks a `struct` as a router or marks an `impl` block as a router +/// implementation +/// +/// # Examples +/// +/// ```rust +/// #[rossweisse::router] +/// struct Router { +/// _phantom: (), +/// } +/// +/// #[rossweisse::router] +/// impl Router { +/// #[route] +/// pub fn index( +/// _context: windmark::context::RouteContext, +/// ) -> windmark::Response { +/// windmark::Response::success("Hello, World!") +/// } +/// } +/// ``` +#[proc_macro_attribute] +pub fn router(arguments: TokenStream, item: TokenStream) -> TokenStream { + let output = match syn::parse::<Item>(item.clone()) { + Ok(Item::Struct(item)) => implementations::fields(arguments, item), + Ok(Item::Impl(item)) => implementations::methods(arguments, item), + _ => panic!("`#[rossweisse::router]` can only be used on `struct`s"), + }; + + output.into() +} + +/// Marks a method of a router implementation as a route to mount +/// +/// # Examples +/// +/// ```rust +/// #[rossweisse::router] +/// impl Router { +/// #[route] +/// pub fn index( +/// _context: windmark::context::RouteContext, +/// ) -> windmark::Response { +/// windmark::Response::success("Hello, World!") +/// } +/// } +/// ``` +#[proc_macro_attribute] +pub fn route(arguments: TokenStream, item: TokenStream) -> TokenStream { + let output = match syn::parse::<Item>(item.clone()) { + Ok(Item::Fn(item)) => implementations::route(arguments, item), + _ => panic!("`#[rossweisse::route]` can only be used on `fn`s"), + }; + + output.into() +} |