aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFuwn <[email protected]>2023-04-27 05:52:31 +0000
committerFuwn <[email protected]>2023-04-27 05:52:31 +0000
commit2afebbc1f8fa218bb3eae271391b707d14778fc7 (patch)
treece226b86a871110dc86484a2fd9a01db4d501eae
parentdocs(readme): add information about examples (diff)
downloadwindmark-2afebbc1f8fa218bb3eae271391b707d14778fc7.tar.xz
windmark-2afebbc1f8fa218bb3eae271391b707d14778fc7.zip
feat(rossweisse): initial implementation of struct router framework
-rw-r--r--Cargo.toml6
-rw-r--r--Makefile.toml3
-rw-r--r--examples/struct_router.rs49
-rw-r--r--rossweisse/Cargo.toml16
-rw-r--r--rossweisse/src/implementations.rs22
-rw-r--r--rossweisse/src/implementations/route.rs22
-rw-r--r--rossweisse/src/implementations/router.rs23
-rw-r--r--rossweisse/src/implementations/router/fields.rs86
-rw-r--r--rossweisse/src/implementations/router/methods.rs66
-rw-r--r--rossweisse/src/implementations/router/parser.rs21
-rw-r--r--rossweisse/src/implementations/router/parser/field_initializer.rs39
-rw-r--r--rossweisse/src/implementations/router/parser/field_initializers.rs28
-rw-r--r--rossweisse/src/lib.rs91
13 files changed, 472 insertions, 0 deletions
diff --git a/Cargo.toml b/Cargo.toml
index ee4c9c4..0c0cd53 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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()
+}