aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs2
-rw-r--r--src/module.rs16
-rw-r--r--src/router.rs96
3 files changed, 100 insertions, 14 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 7d4a80b..fd31430 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -40,7 +40,7 @@ pub mod utilities;
#[macro_use]
extern crate log;
-pub use module::Module;
+pub use module::{AsyncModule, Module};
pub use response::Response;
pub use router::Router;
pub use tokio::main;
diff --git a/src/module.rs b/src/module.rs
index 0c2a67b..e9ac743 100644
--- a/src/module.rs
+++ b/src/module.rs
@@ -16,15 +16,9 @@
// Copyright (C) 2022-2022 Fuwn <[email protected]>
// SPDX-License-Identifier: GPL-3.0-only
-use crate::{context::HookContext, Router};
+mod asynchronous;
+mod sync;
-pub trait Module {
- /// Called right after the module is attached.
- fn on_attach(&mut self, _: &mut Router) {}
-
- /// Called before a route is mounted.
- fn on_pre_route(&mut self, _: HookContext<'_>) {}
-
- /// Called after a route is mounted.
- fn on_post_route(&mut self, _: HookContext<'_>) {}
-}
+#[allow(clippy::module_name_repetitions)]
+pub use asynchronous::AsyncModule;
+pub use sync::Module;
diff --git a/src/router.rs b/src/router.rs
index 2744d4a..e1785dc 100644
--- a/src/router.rs
+++ b/src/router.rs
@@ -25,7 +25,10 @@ use std::{
};
use openssl::ssl::{self, SslAcceptor, SslMethod};
-use tokio::io::{AsyncReadExt, AsyncWriteExt};
+use tokio::{
+ io::{AsyncReadExt, AsyncWriteExt},
+ sync::Mutex as AsyncMutex,
+};
use url::Url;
use crate::{
@@ -37,7 +40,7 @@ use crate::{
PreRouteHook,
RouteResponse,
},
- module::Module,
+ module::{AsyncModule, Module},
response::Response,
};
@@ -76,6 +79,7 @@ pub struct Router {
character_set: String,
languages: Vec<String>,
port: i32,
+ async_modules: Arc<AsyncMutex<Vec<Box<dyn AsyncModule + Send>>>>,
modules: Arc<Mutex<Vec<Box<dyn Module + Send>>>>,
fix_path: bool,
}
@@ -352,6 +356,17 @@ impl Router {
};
let route = &mut self.routes.at(&fixed_path);
+ for module in &mut *self.async_modules.lock().await {
+ module
+ .on_pre_route(HookContext::new(
+ stream.get_ref(),
+ &url,
+ route.as_ref().map_or(None, |route| Some(&route.params)),
+ &stream.ssl().peer_certificate(),
+ ))
+ .await;
+ }
+
for module in &mut *self.modules.lock().unwrap() {
module.on_pre_route(HookContext::new(
stream.get_ref(),
@@ -420,6 +435,17 @@ impl Router {
))
};
+ for module in &mut *self.async_modules.lock().await {
+ module
+ .on_post_route(HookContext::new(
+ stream.get_ref(),
+ &url,
+ route.as_ref().map_or(None, |route| Some(&route.params)),
+ &stream.ssl().peer_certificate(),
+ ))
+ .await;
+ }
+
for module in &mut *self.modules.lock().unwrap() {
module.on_post_route(HookContext::new(
stream.get_ref(),
@@ -678,6 +704,71 @@ impl Router {
self
}
+ /// Attach a stateful module to a `Router`; with async support
+ ///
+ /// Like a stateless module is an extension or middleware to a `Router`.
+ /// Modules get full access to the `Router` and can be extended by a third
+ /// party, but also, can create hooks will be executed through various parts
+ /// of a routes' lifecycle. Stateful modules also have state, so variables can
+ /// be stored for further access.
+ ///
+ /// # Panics
+ ///
+ /// May panic if the stateful module cannot be attached.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use log::info;
+ /// use windmark::{context::HookContext, Response, Router};
+ ///
+ /// #[derive(Default)]
+ /// struct Clicker {
+ /// clicks: isize,
+ /// }
+ ///
+ /// #[async_trait::async_trait]
+ /// impl windmark::AsyncModule for Clicker {
+ /// async fn on_attach(&mut self, _: &mut Router) {
+ /// info!("clicker has been attached!");
+ /// }
+ ///
+ /// async fn on_pre_route(&mut self, context: HookContext<'_>) {
+ /// self.clicks += 1;
+ ///
+ /// info!(
+ /// "clicker has been called pre-route on {} with {} clicks!",
+ /// context.url.path(),
+ /// self.clicks
+ /// );
+ /// }
+ ///
+ /// async fn on_post_route(&mut self, context: HookContext<'_>) {
+ /// info!(
+ /// "clicker has been called post-route on {} with {} clicks!",
+ /// context.url.path(),
+ /// self.clicks
+ /// );
+ /// }
+ /// }
+ ///
+ /// Router::new().attach_async(Clicker::default());
+ /// ```
+ pub fn attach_async(
+ &mut self,
+ mut module: impl AsyncModule + 'static + Send,
+ ) -> &mut Self {
+ tokio::task::block_in_place(|| {
+ tokio::runtime::Handle::current().block_on(async {
+ module.on_attach(self).await;
+
+ (*self.async_modules.lock().await).push(Box::new(module));
+ });
+ });
+
+ self
+ }
+
/// Attach a stateful module to a `Router`.
///
/// Like a stateless module is an extension or middleware to a `Router`.
@@ -835,6 +926,7 @@ impl Default for Router {
languages: vec!["en".to_string()],
port: 1965,
modules: Arc::new(Mutex::new(vec![])),
+ async_modules: Arc::new(AsyncMutex::new(vec![])),
fix_path: false,
}
}