layered

Crates.iolayered
lib.rslayered
version0.3.0
created_at2025-10-17 08:05:55.020328+00
updated_at2026-01-22 20:28:57.171772+00
descriptionBuild composable async services with layered middleware.
homepagehttps://github.com/microsoft/oxidizer
repositoryhttps://github.com/microsoft/oxidizer
max_upload_size
id1887276
size105,092
(oxidizer-crates-io-bot)

documentation

README

Layered Logo

Layered

crate.io docs.rs MSRV CI Coverage License This crate was developed as part of the Oxidizer project

Layered Services

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.

Why not Tower?

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.

Quick Start

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!");

Key Concepts

  • Service: A type implementing the Service trait that transforms inputs into outputs asynchronously. Think of it as async fn(&self, In) -> Out.
  • Middleware: A service that wraps another service to add cross-cutting behavior such as logging, timeouts, or retries. Middleware receives inputs before the inner service and can process outputs after.
  • Layer: A type implementing the Layer trait that constructs middleware around a service. Layers are composable and can be stacked using tuples like (layer1, layer2, service).

Layers and Middleware

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;

Thread Safety

All services must implement Send and Sync, and returned futures must be Send. This ensures compatibility with multi-threaded async runtimes like Tokio.

Features

  • intercept: Enables Intercept middleware

  • dynamic-service: Enables DynamicService for type erasure

  • tower-service: Enables Tower interoperability via the tower module


This crate was developed as part of The Oxidizer Project. Browse this crate's source code.
Commit count: 14

cargo fmt