| Crates.io | layered |
| lib.rs | layered |
| version | 0.3.0 |
| created_at | 2025-10-17 08:05:55.020328+00 |
| updated_at | 2026-01-22 20:28:57.171772+00 |
| description | Build composable async services with layered middleware. |
| homepage | https://github.com/microsoft/oxidizer |
| repository | https://github.com/microsoft/oxidizer |
| max_upload_size | |
| id | 1887276 |
| size | 105,092 |
Build composable async services with layered middleware.
This crate provides the Service trait and a layer system for adding cross-cutting
concerns like timeouts, retries, and logging.
Tower predates async fn in traits, requiring manual Future types
or boxing and poll_ready back-pressure semantics. Tower’s &mut self also requires cloning
for concurrent requests. This crate uses async fn with &self, enabling simpler middleware
and natural concurrency. Tower interop is available via the tower-service feature.
A Service transforms an input into an output asynchronously:
use layered::Service;
struct Greeter;
impl Service<String> for Greeter {
type Out = String;
async fn execute(&self, name: String) -> Self::Out {
format!("Hello, {name}!")
}
}
Use Execute to turn any async function into a service:
use layered::{Execute, Service};
let greeter = Execute::new(|name: String| async move {
format!("Hello, {name}!")
});
assert_eq!(greeter.execute("World".into()).await, "Hello, World!");
Service trait that transforms inputs into outputs
asynchronously. Think of it as async fn(&self, In) -> Out.Layer trait that constructs middleware around a
service. Layers are composable and can be stacked using tuples like (layer1, layer2, service).A Layer wraps a service with additional behavior. In this example, we create a logging
middleware that prints inputs before passing them to the inner service:
use layered::{Execute, Layer, Service, Stack};
// A simple logging layer
struct LogLayer;
impl<S> Layer<S> for LogLayer {
type Service = LogService<S>;
fn layer(&self, inner: S) -> Self::Service {
LogService(inner)
}
}
struct LogService<S>(S);
impl<S, In: Send + std::fmt::Display> Service<In> for LogService<S>
where
S: Service<In>,
{
type Out = S::Out;
async fn execute(&self, input: In) -> Self::Out {
println!("Input: {input}");
self.0.execute(input).await
}
}
// Stack layers with the service (layers apply outer to inner)
let service = (
LogLayer,
Execute::new(|x: i32| async move { x * 2 }),
).into_service();
let result = service.execute(21).await;
All services must implement Send and Sync, and returned futures must be Send.
This ensures compatibility with multi-threaded async runtimes like Tokio.
intercept: Enables Intercept middleware
dynamic-service: Enables DynamicService for type erasure
tower-service: Enables Tower interoperability via the tower module