🏗️ Elegant & Highly Performant Async Gemini Server Framework for the Modern Age
async framework gemini-protocol protocol gemini rust
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(module): async module support

Fuwn a6a61d5b bda843b8

+109 -20
+3 -1
Cargo.toml
··· 24 24 openssl = "0.10.38" 25 25 tokio-openssl = "0.6.3" 26 26 27 - tokio = { version = "1.26.0", features = ["full"] } # Non-blocking I/O 27 + # Non-blocking I/O 28 + tokio = { version = "1.26.0", features = ["full"] } 29 + async-trait = "0.1.68" 28 30 29 31 # Logging 30 32 pretty_env_logger = { version = "0.4.0", optional = true }
+6 -5
examples/windmark.rs
··· 33 33 clicks: isize, 34 34 } 35 35 36 - impl windmark::Module for Clicker { 37 - fn on_attach(&mut self, _: &mut Router) { 36 + #[async_trait::async_trait] 37 + impl windmark::AsyncModule for Clicker { 38 + async fn on_attach(&mut self, _: &mut Router) { 38 39 println!("clicker has been attached!"); 39 40 } 40 41 41 - fn on_pre_route(&mut self, context: HookContext<'_>) { 42 + async fn on_pre_route(&mut self, context: HookContext<'_>) { 42 43 self.clicks += 1; 43 44 44 45 info!( ··· 48 49 ); 49 50 } 50 51 51 - fn on_post_route(&mut self, context: HookContext<'_>) { 52 + async fn on_post_route(&mut self, context: HookContext<'_>) { 52 53 info!( 53 54 "clicker has been called post-route on {} with {} clicks!", 54 55 context.url.path(), ··· 78 79 router.attach_stateless(|r| { 79 80 r.mount("/module", success!("This is a module!")); 80 81 }); 81 - router.attach(Clicker::default()); 82 + router.attach_async(Clicker::default()); 82 83 router.set_pre_route_callback(|context| { 83 84 info!( 84 85 "accepted connection from {} to {}",
+1 -1
src/lib.rs
··· 40 40 #[macro_use] 41 41 extern crate log; 42 42 43 - pub use module::Module; 43 + pub use module::{AsyncModule, Module}; 44 44 pub use response::Response; 45 45 pub use router::Router; 46 46 pub use tokio::main;
+5 -11
src/module.rs
··· 16 16 // Copyright (C) 2022-2022 Fuwn <contact@fuwn.me> 17 17 // SPDX-License-Identifier: GPL-3.0-only 18 18 19 - use crate::{context::HookContext, Router}; 20 - 21 - pub trait Module { 22 - /// Called right after the module is attached. 23 - fn on_attach(&mut self, _: &mut Router) {} 24 - 25 - /// Called before a route is mounted. 26 - fn on_pre_route(&mut self, _: HookContext<'_>) {} 19 + mod asynchronous; 20 + mod sync; 27 21 28 - /// Called after a route is mounted. 29 - fn on_post_route(&mut self, _: HookContext<'_>) {} 30 - } 22 + #[allow(clippy::module_name_repetitions)] 23 + pub use asynchronous::AsyncModule; 24 + pub use sync::Module;
+94 -2
src/router.rs
··· 25 25 }; 26 26 27 27 use openssl::ssl::{self, SslAcceptor, SslMethod}; 28 - use tokio::io::{AsyncReadExt, AsyncWriteExt}; 28 + use tokio::{ 29 + io::{AsyncReadExt, AsyncWriteExt}, 30 + sync::Mutex as AsyncMutex, 31 + }; 29 32 use url::Url; 30 33 31 34 use crate::{ ··· 37 40 PreRouteHook, 38 41 RouteResponse, 39 42 }, 40 - module::Module, 43 + module::{AsyncModule, Module}, 41 44 response::Response, 42 45 }; 43 46 ··· 76 79 character_set: String, 77 80 languages: Vec<String>, 78 81 port: i32, 82 + async_modules: Arc<AsyncMutex<Vec<Box<dyn AsyncModule + Send>>>>, 79 83 modules: Arc<Mutex<Vec<Box<dyn Module + Send>>>>, 80 84 fix_path: bool, 81 85 } ··· 352 356 }; 353 357 let route = &mut self.routes.at(&fixed_path); 354 358 359 + for module in &mut *self.async_modules.lock().await { 360 + module 361 + .on_pre_route(HookContext::new( 362 + stream.get_ref(), 363 + &url, 364 + route.as_ref().map_or(None, |route| Some(&route.params)), 365 + &stream.ssl().peer_certificate(), 366 + )) 367 + .await; 368 + } 369 + 355 370 for module in &mut *self.modules.lock().unwrap() { 356 371 module.on_pre_route(HookContext::new( 357 372 stream.get_ref(), ··· 420 435 )) 421 436 }; 422 437 438 + for module in &mut *self.async_modules.lock().await { 439 + module 440 + .on_post_route(HookContext::new( 441 + stream.get_ref(), 442 + &url, 443 + route.as_ref().map_or(None, |route| Some(&route.params)), 444 + &stream.ssl().peer_certificate(), 445 + )) 446 + .await; 447 + } 448 + 423 449 for module in &mut *self.modules.lock().unwrap() { 424 450 module.on_post_route(HookContext::new( 425 451 stream.get_ref(), ··· 678 704 self 679 705 } 680 706 707 + /// Attach a stateful module to a `Router`; with async support 708 + /// 709 + /// Like a stateless module is an extension or middleware to a `Router`. 710 + /// Modules get full access to the `Router` and can be extended by a third 711 + /// party, but also, can create hooks will be executed through various parts 712 + /// of a routes' lifecycle. Stateful modules also have state, so variables can 713 + /// be stored for further access. 714 + /// 715 + /// # Panics 716 + /// 717 + /// May panic if the stateful module cannot be attached. 718 + /// 719 + /// # Examples 720 + /// 721 + /// ```rust 722 + /// use log::info; 723 + /// use windmark::{context::HookContext, Response, Router}; 724 + /// 725 + /// #[derive(Default)] 726 + /// struct Clicker { 727 + /// clicks: isize, 728 + /// } 729 + /// 730 + /// #[async_trait::async_trait] 731 + /// impl windmark::AsyncModule for Clicker { 732 + /// async fn on_attach(&mut self, _: &mut Router) { 733 + /// info!("clicker has been attached!"); 734 + /// } 735 + /// 736 + /// async fn on_pre_route(&mut self, context: HookContext<'_>) { 737 + /// self.clicks += 1; 738 + /// 739 + /// info!( 740 + /// "clicker has been called pre-route on {} with {} clicks!", 741 + /// context.url.path(), 742 + /// self.clicks 743 + /// ); 744 + /// } 745 + /// 746 + /// async fn on_post_route(&mut self, context: HookContext<'_>) { 747 + /// info!( 748 + /// "clicker has been called post-route on {} with {} clicks!", 749 + /// context.url.path(), 750 + /// self.clicks 751 + /// ); 752 + /// } 753 + /// } 754 + /// 755 + /// Router::new().attach_async(Clicker::default()); 756 + /// ``` 757 + pub fn attach_async( 758 + &mut self, 759 + mut module: impl AsyncModule + 'static + Send, 760 + ) -> &mut Self { 761 + tokio::task::block_in_place(|| { 762 + tokio::runtime::Handle::current().block_on(async { 763 + module.on_attach(self).await; 764 + 765 + (*self.async_modules.lock().await).push(Box::new(module)); 766 + }); 767 + }); 768 + 769 + self 770 + } 771 + 681 772 /// Attach a stateful module to a `Router`. 682 773 /// 683 774 /// Like a stateless module is an extension or middleware to a `Router`. ··· 835 926 languages: vec!["en".to_string()], 836 927 port: 1965, 837 928 modules: Arc::new(Mutex::new(vec![])), 929 + async_modules: Arc::new(AsyncMutex::new(vec![])), 838 930 fix_path: false, 839 931 } 840 932 }