diff --git a/Cargo.toml b/Cargo.toml index b6ee48c2..bf493d27 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,11 +8,12 @@ members = [ exclude = [ "benches", "benches_rt/glommio", + "benches_rt/nio", "benches_rt/smol", "benches_rt/tokio", "benches_rt/vs_actix-web", ] [workspace.dependencies] -byte_reader = { version = "3.1", features = ["text"] } -serde = { version = "1.0", features = ["derive"] } +byte_reader = { version = "3.1", features = ["text"] } +serde = { version = "1.0", features = ["derive"] } diff --git a/README.md b/README.md index e4d85174..d77715f0 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@
- *macro-less and type-safe* APIs for intuitive and declarative code -- *multiple runtimes* are supported:`tokio`, `async-std`, `smol`, `glommio`, `worker` (Cloudflare Workers) +- *multiple runtimes* are supported:`tokio`, `async-std`, `smol`, `nio`, `glommio`, `worker` (Cloudflare Workers)
License @@ -78,11 +78,12 @@ Hello, your_name! ## Feature flags -### `"rt_tokio"`, `"rt_async-std"`, `"rt_smol"`, `"rt_glommio"`:native async runtime +### `"rt_tokio"`, `"rt_async-std"`, `"rt_smol"`, `"rt_nio"`, `"rt_glommio"`:native async runtime - [tokio](https://github.com/tokio-rs/tokio) - [async-std](https://github.com/async-rs/async-std) - [smol](https://github.com/smol-rs/smol) +- [nio](https://github.com/nurmohammed840/nio) - [glommio](https://github.com/DataDog/glommio) ### `"rt_worker"`:Cloudflare Workers diff --git a/Taskfile.yaml b/Taskfile.yaml index 76907ddb..9f3f9319 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -15,14 +15,14 @@ tasks: - task: test:examples - task: test:doc - task: test:no_rt - - for: [tokio, async-std, smol, glommio, worker] + - for: [tokio, async-std, smol, nio, glommio, worker] task: test:rt vars: { rt: '{{.ITEM}}' } check: deps: - task: check:no_rt - - for: [tokio, async-std, smol, glommio] + - for: [tokio, async-std, smol, nio, glommio] task: check:native_rt vars: { native_rt: '{{.ITEM}}' } - task: check:rt_worker @@ -32,10 +32,9 @@ tasks: - (! cargo version | grep -q 'nightly') cmds: - cd benches && cargo bench --features DEBUG --no-run - - cd benches_rt/glommio && cargo check - - cd benches_rt/smol && cargo check - - cd benches_rt/tokio && cargo check - cd benches_rt/vs_actix-web && cargo check + - for: [tokio, smol, nio, glommio] + cmd: cd benches_rt/{{.ITEM}} && cargo check bench: status: diff --git a/benches_rt/nio/Cargo.toml b/benches_rt/nio/Cargo.toml new file mode 100644 index 00000000..f762d4d0 --- /dev/null +++ b/benches_rt/nio/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "ohkami_benches-with-nio" +version = "0.0.0" +edition = "2021" +authors = ["kanarus "] + +[dependencies] +# set `default-features = false` to assure "DEBUG" feature be off even when DEBUGing `../ohkami` +ohkami = { path = "../../ohkami", default-features = false, features = ["rt_nio"] } +nio = { version = "0.0" } + +[profile.release] +opt-level = 3 +debug = false +debug-assertions = false +lto = true +panic = "abort" +incremental = false +codegen-units = 1 +rpath = false +strip = false + +[features] +DEBUG = ["ohkami/DEBUG"] \ No newline at end of file diff --git a/benches_rt/nio/src/bin/param.rs b/benches_rt/nio/src/bin/param.rs new file mode 100644 index 00000000..ddedad5a --- /dev/null +++ b/benches_rt/nio/src/bin/param.rs @@ -0,0 +1,9 @@ +use ohkami::prelude::*; + +#[nio::main] +async fn main() { + Ohkami::new(( + "/user/:id" + .GET(|id: String| async {id}), + )).howl("0.0.0.0:3000").await +} diff --git a/ohkami/Cargo.toml b/ohkami/Cargo.toml index 86da0358..a1b2ca9a 100644 --- a/ohkami/Cargo.toml +++ b/ohkami/Cargo.toml @@ -24,6 +24,7 @@ ohkami_macros = { version = "=0.21.0", path = "../ohkami_macros" } tokio = { version = "1", optional = true, features = ["rt", "net", "time"] } async-std = { version = "1", optional = true } smol = { version = "2", optional = true } +nio = { version = "0.0", optional = true } glommio = { version = "0.9", optional = true } worker = { version = "0.4", optional = true } @@ -39,16 +40,17 @@ sha2 = { version = "0.10", default-features = false } ctrlc = { version = "3.4", optional = true } num_cpus = { version = "1.16", optional = true } futures-util = { version = "0.3", optional = true, default-features = false, features = ["io", "async-await-macro"] } -mews = { version = "0.1", optional = true } +mews = { version = "0.2", optional = true } [features] default = ["testing"] -rt_tokio = ["__rt__", "__rt_native__", "dep:tokio", "tokio/io-util", "tokio/macros", "mews?/tokio"] -rt_async-std = ["__rt__", "__rt_native__", "dep:async-std", "dep:futures-util", "mews?/async-std"] -rt_smol = ["__rt__", "__rt_native__", "dep:smol", "dep:futures-util", "mews?/smol"] -rt_glommio = ["__rt__", "__rt_native__", "dep:glommio", "dep:futures-util", "dep:num_cpus", "mews?/glommio"] +rt_tokio = ["__rt__", "__rt_native__", "dep:tokio", "tokio/io-util", "tokio/macros", "mews?/rt_tokio" ] +rt_async-std = ["__rt__", "__rt_native__", "dep:async-std", "dep:futures-util", "mews?/rt_async-std"] +rt_smol = ["__rt__", "__rt_native__", "dep:smol", "dep:futures-util", "mews?/rt_smol" ] +rt_nio = ["__rt__", "__rt_native__", "dep:nio", "dep:tokio", "tokio/io-util", "mews?/rt_nio" ] +rt_glommio = ["__rt__", "__rt_native__", "dep:glommio", "dep:futures-util", "dep:num_cpus", "mews?/rt_glommio" ] rt_worker = ["__rt__", "dep:worker", "ohkami_macros/worker"] nightly = [] @@ -70,6 +72,7 @@ DEBUG = ["tokio?/rt-multi-thread"] # "rt_tokio", # #"rt_async-std", # #"rt_smol", +# #"rt_nio", # #"rt_glommio", # #"rt_worker", # #"DEBUG", diff --git a/ohkami/src/lib.rs b/ohkami/src/lib.rs index d008dd23..cdff0f56 100644 --- a/ohkami/src/lib.rs +++ b/ohkami/src/lib.rs @@ -11,7 +11,7 @@ //!
//! //! - *macro-less and type-safe* APIs for intuitive and declarative code -//! - *multi runtimes* are supported:`tokio`, `async-std`, `worker` (Cloudflare Workers) +//! - *multi runtimes* are supported:`tokio`, `async-std`, `smol`, `nio`, `glommio`, `worker` (Cloudflare Workers) //! //!
//! License @@ -28,16 +28,12 @@ #[cfg(any( - all(feature="rt_tokio", feature="rt_async-std"), - all(feature="rt_tokio", feature="rt_smol"), - all(feature="rt_tokio", feature="rt_glommio"), - all(feature="rt_tokio", feature="rt_worker"), - all(feature="rt_async-std", feature="rt_smol"), - all(feature="rt_async-std", feature="rt_glommio"), - all(feature="rt_async-std", feature="rt_worker"), - all(feature="rt_smol", feature="rt_glommio"), - all(feature="rt_smol", feature="rt_worker"), - all(feature="rt_glommio", feature="rt_worker"), + all(feature="rt_tokio", any(feature="rt_async-std", feature="rt_smol", feature="rt_nio", feature="rt_glommio", feature="rt_worker" )), + all(feature="rt_async-std", any(feature="rt_smol", feature="rt_nio", feature="rt_glommio", feature="rt_worker", feature="rt_tokio" )), + all(feature="rt_smol", any(feature="rt_nio", feature="rt_glommio", feature="rt_worker", feature="rt_tokio", feature="rt_async-std")), + all(feature="rt_nio", any(feature="rt_glommio", feature="rt_worker", feature="rt_tokio", feature="rt_async-std", feature="rt_smol" )), + all(feature="rt_glommio", any(feature="rt_worker", feature="rt_tokio", feature="rt_async-std", feature="rt_smol", feature="rt_nio" )), + all(feature="rt_worker", any(feature="rt_tokio", feature="rt_async-std", feature="rt_smol", feature="rt_nio", feature="rt_glommio" )), ))] compile_error! {" Can't activate multiple `rt_*` features at once! "} @@ -62,13 +58,15 @@ mod __rt__ { pub(crate) use async_std::net::{TcpListener, TcpStream, ToSocketAddrs}; #[cfg(feature="rt_smol")] pub(crate) use smol::net::{TcpListener, TcpStream, AsyncToSocketAddrs as ToSocketAddrs}; + #[cfg(feature="rt_nio")] + pub(crate) use {nio::net::{TcpListener, TcpStream}, std::net::ToSocketAddrs}; #[cfg(feature="rt_glommio")] pub(crate) use {glommio::net::{TcpListener, TcpStream}, std::net::ToSocketAddrs}; pub(crate) async fn bind(address: impl ToSocketAddrs) -> TcpListener { let binded = TcpListener::bind(address); - #[cfg(any(feature="rt_tokio",feature="rt_async-std",feature="rt_smol"))] + #[cfg(any(feature="rt_tokio", feature="rt_async-std", feature="rt_smol", feature="rt_nio"))] let binded = binded.await; binded.expect("Failed to bind TCP listener") @@ -82,6 +80,8 @@ mod __rt__ { pub(crate) async fn sleep(duration: std::time::Duration) { smol::Timer::after(duration).await; } + #[cfg(feature="rt_nio")] + pub(crate) use nio::time::sleep; #[cfg(feature="rt_glommio")] pub(crate) use glommio::timer::sleep; @@ -91,6 +91,8 @@ mod __rt__ { pub(crate) use async_std::io::ReadExt as AsyncRead; #[cfg(feature="rt_smol")] pub(crate) use futures_util::AsyncReadExt as AsyncRead; + #[cfg(feature="rt_nio")] + pub(crate) use tokio::io::AsyncReadExt as AsyncRead; #[cfg(feature="rt_glommio")] pub(crate) use futures_util::AsyncReadExt as AsyncRead; @@ -100,6 +102,8 @@ mod __rt__ { pub(crate) use async_std::io::WriteExt as AsyncWrite; #[cfg(feature="rt_smol")] pub(crate) use futures_util::AsyncWriteExt as AsyncWrite; + #[cfg(feature="rt_nio")] + pub(crate) use tokio::io::AsyncWriteExt as AsyncWrite; #[cfg(feature="rt_glommio")] pub(crate) use futures_util::AsyncWriteExt as AsyncWrite; @@ -109,10 +113,12 @@ mod __rt__ { pub(crate) use futures_util::select; #[cfg(feature="rt_smol")] pub(crate) use futures_util::select; + #[cfg(feature="rt_nio")] + pub(crate) use tokio::select; #[cfg(feature="rt_glommio")] pub(crate) use futures_util::select; - #[cfg(any(feature="rt_tokio"))] + #[cfg(any(feature="rt_tokio", feature="rt_nio"))] pub(crate) const fn selectable(future: F) -> F { future } @@ -121,7 +127,7 @@ mod __rt__ { ::futures_util::FutureExt::fuse(future) } - #[cfg(any(feature="rt_tokio",feature="rt_async-std",feature="rt_smol"))] + #[cfg(any(feature="rt_tokio", feature="rt_async-std", feature="rt_smol", feature="rt_nio"))] mod task { pub trait Task: std::future::Future + Send + 'static {} impl + Send + 'static> Task for F {} @@ -141,27 +147,45 @@ mod __rt__ { #[cfg(feature="rt_smol")] smol::spawn(task).detach(); + #[cfg(feature="rt_nio")] + nio::spawn(task); + #[cfg(feature="rt_glommio")] glommio::spawn_local(task).detach(); } - #[cfg(feature="testing")] - #[cfg(test)] - pub(crate) fn block_on(future: impl std::future::Future) { - #[cfg(feature="rt_tokio")] - tokio::runtime::Builder::new_current_thread() - .enable_all() - .build().unwrap() - .block_on(future); - - #[cfg(feature="rt_async-std")] - async_std::task::block_on(future); - - #[cfg(feature="rt_smol")] - smol::block_on(future); - - #[cfg(feature="rt_glommio")] - glommio::LocalExecutor::default().run(future); + #[cfg(all(test, feature="testing"))] + pub(crate) mod testing { + pub(crate) fn block_on(future: impl std::future::Future) { + #[cfg(feature="rt_tokio")] + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build().unwrap() + .block_on(future); + + #[cfg(feature="rt_async-std")] + async_std::task::block_on(future); + + #[cfg(feature="rt_smol")] + smol::block_on(future); + + #[cfg(feature="rt_nio")] + nio::runtime::Builder::new_multi_thread() + .enable_all() + .build().unwrap() + .block_on(future); + + #[cfg(feature="rt_glommio")] + glommio::LocalExecutor::default().run(future); + } + + pub(crate) const PORT: u16 = { + #[cfg(feature="rt_tokio") ] {3001} + #[cfg(feature="rt_async-std")] {3002} + #[cfg(feature="rt_smol") ] {3003} + #[cfg(feature="rt_nio") ] {3004} + #[cfg(feature="rt_glommio") ] {3005} + }; } } diff --git a/ohkami/src/ohkami/mod.rs b/ohkami/src/ohkami/mod.rs index e9f2ef4e..6d8abd62 100644 --- a/ohkami/src/ohkami/mod.rs +++ b/ohkami/src/ohkami/mod.rs @@ -14,7 +14,7 @@ use std::sync::Arc; #[cfg(feature="__rt_native__")] use crate::{__rt__, Session}; -/// # Ohkami - a robust wolf who serves your web app +/// # Ohkami - a smart wolf who serves your web app /// ///
/// @@ -105,8 +105,9 @@ use crate::{__rt__, Session}; /// `async ({path_params}?, {FromRequest type}s...) -> {IntoResponse type}` /// /// #### path_params: -/// A tuple of types that implement `FromParam` trait.\ -/// If the path contains only one parameter, then you can omit the tuple.\ +/// A tuple of types that implement `FromParam` trait e.g. `(&str, usize)`.\ +/// If the path contains only one parameter, then you can omit the tuple \ +/// e.g. just `param: &str`.\ /// (In current ohkami, at most *2* path params can be handled.) /// ///
@@ -248,7 +249,7 @@ impl Ohkami { /// - `tokio::net::ToSocketAddrs` if using `tokio` /// - `async_std::net::ToSocketAddrs` if using `async-std` /// - `smol::net::AsyncToSocketAddrs` if using `smol` - /// - `std::net::ToSocketAddrs` if using `glommio` + /// - `std::net::ToSocketAddrs` if using `nio` or `glommio` /// /// *note* : Keep-Alive timeout is 42 seconds and this is not /// configureable by user (it'll be in future version...) @@ -313,7 +314,7 @@ impl Ohkami { __rt__::select! { accept = __rt__::selectable(listener.accept()) => { let (connection, addr) = { - #[cfg(any(feature="rt_tokio", feature="rt_async-std", feature="rt_smol"))] { + #[cfg(any(feature="rt_tokio", feature="rt_async-std", feature="rt_smol", feature="rt_nio"))] { let Ok((connection, addr)) = accept else {continue}; (connection, addr) } @@ -453,12 +454,12 @@ mod sync { use std::task::{Context, Poll, Waker}; use std::pin::Pin; - #[cfg(any(feature="rt_tokio", feature="rt_async-std", feature="rt_smol"))] + #[cfg(any(feature="rt_tokio", feature="rt_async-std", feature="rt_smol", feature="rt_nio"))] use std::{sync::atomic::AtomicPtr, ptr::null_mut}; #[cfg(any(feature="rt_glommio"))] use std::sync::Mutex; - #[cfg(any(feature="rt_tokio", feature="rt_async-std", feature="rt_smol"))] + #[cfg(any(feature="rt_tokio", feature="rt_async-std", feature="rt_smol", feature="rt_nio"))] static WAKER: AtomicPtr = AtomicPtr::new(null_mut()); #[cfg(any(feature="rt_glommio"))] static WAKER: Mutex> = Mutex::new(Vec::new()); @@ -477,7 +478,7 @@ mod sync { crate::DEBUG!("[CtrlC::catch] Ready"); Poll::Ready(()) } else { - #[cfg(any(feature="rt_tokio", feature="rt_async-std", feature="rt_smol"))] { + #[cfg(any(feature="rt_tokio", feature="rt_async-std", feature="rt_smol", feature="rt_nio"))] { let prev_waker = WAKER.swap( Box::into_raw(Box::new(cx.waker().clone())), Ordering::SeqCst @@ -502,7 +503,7 @@ mod sync { } pub fn new() -> Self { - #[cfg(any(feature="rt_tokio", feature="rt_async-std", feature="rt_smol"))] + #[cfg(any(feature="rt_tokio", feature="rt_async-std", feature="rt_smol", feature="rt_nio"))] ::ctrlc::set_handler(|| { CATCH.store(true, Ordering::SeqCst); let waker = WAKER.swap(null_mut(), Ordering::SeqCst); @@ -530,10 +531,10 @@ mod sync { #[cfg(all(feature="testing", feature="__rt_native__"))] #[cfg(test)] #[test] fn can_howl_on_any_native_async_runtime() { - __rt__::block_on(async { + __rt__::testing::block_on(async { crate::util::timeout_in( std::time::Duration::from_secs(3), - Ohkami::new(()).howl("localhost:3000") + Ohkami::new(()).howl(("localhost", __rt__::testing::PORT)) ).await }); }