Skip to content

Commit ac52183

Browse files
authored
feat: introduce layered crate (#189)
Summary Introduces layered, a modern service abstraction crate for building composable async services with middleware. Key features: - Service trait using native async fn with &self - no manual Future types or boxing required - Layer and Stack for composing middleware using ergonomic tuple syntax: (layer1, layer2, service).build() - Execute helper to convert closures into services - Intercept middleware for observing/modifying inputs and outputs (logging, metrics, validation) - DynamicService for type erasure when concrete types become unwieldy - Bidirectional Tower interoperability via the tower module
1 parent bae3cf9 commit ac52183

27 files changed

+2773
-1
lines changed

.spelling

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
297
1+
299
2+
23
0.X.Y
34
100k
45
10k
@@ -64,6 +65,7 @@ C-NUM-FMT
6465
codebase
6566
codebases
6667
composability
68+
composable
6769
config
6870
const
6971
contoso

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Please see each crate's change log below:
1010
- [`fundle`](./crates/fundle/CHANGELOG.md)
1111
- [`fundle_macros`](./crates/fundle_macros/CHANGELOG.md)
1212
- [`fundle_macros_impl`](./crates/fundle_macros_impl/CHANGELOG.md)
13+
- [`layered`](./crates/layered/CHANGELOG.md)
1314
- [`ohno`](./crates/ohno/CHANGELOG.md)
1415
- [`ohno_macros`](./crates/ohno_macros/CHANGELOG.md)
1516
- [`recoverable`](./crates/recoverable/CHANGELOG.md)

Cargo.lock

Lines changed: 85 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ data_privacy_macros_impl = { path = "crates/data_privacy_macros_impl", default-f
3232
fundle = { path = "crates/fundle", default-features = false, version = "0.3.0" }
3333
fundle_macros = { path = "crates/fundle_macros", default-features = false, version = "0.3.0" }
3434
fundle_macros_impl = { path = "crates/fundle_macros_impl", default-features = false, version = "0.3.0" }
35+
layered = { path = "crates/layered", default-features = false, version = "0.1.0" }
3536
ohno = { path = "crates/ohno", default-features = false, version = "0.2.0" }
3637
ohno_macros = { path = "crates/ohno_macros", default-features = false, version = "0.2.0" }
3738
recoverable = { path = "crates/recoverable", default-features = false, version = "0.1.0" }
@@ -50,6 +51,7 @@ chrono-tz = { version = "0.10.4", default-features = false }
5051
criterion = { version = "0.7.0", default-features = false }
5152
derive_more = { version = "2.0.1", default-features = false }
5253
duct = { version = "1.1.1", default-features = false }
54+
dynosaur = { version = "0.3.0", default-features = false }
5355
futures = { version = "0.3.31", default-features = false }
5456
futures-core = { version = "0.3.31", default-features = false }
5557
futures-util = { version = "0.3.31", default-features = false }
@@ -83,6 +85,9 @@ thiserror = { version = "2.0.17", default-features = false }
8385
time = { version = "0.3.37", default-features = false }
8486
time-tz = { version = "2.0.0", default-features = false }
8587
tokio = { version = "1.48.0", default-features = false }
88+
tower = { version = "0.5.2", default-features = false }
89+
tower-layer = { version = "0.3.3", default-features = false }
90+
tower-service = { version = "0.3.3", default-features = false }
8691
tracing = { version = "0.1.41", default-features = false }
8792
tracing-subscriber = { version = "0.3.20", default-features = false }
8893
trait-variant = { version = "0.1.2", default-features = false }

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ These are the crates built out of this repo:
3232
- [`fundle`](./crates/fundle/README.md) - Compile-time safe dependency injection for Rust.
3333
- [`fundle_macros`](crates/fundle_macros/README.md) - Macros for the `fundle` crate.
3434
- [`fundle_macros_impl`](crates/fundle_macros_impl/README.md) - Macros for the `fundle` crate.
35+
- [`layered`](./crates/layered/README.md) - A foundational service abstraction for building composable, middleware-driven systems.
3536
- [`ohno`](./crates/ohno/README.md) - High-quality Rust error handling.
3637
- [`ohno_macros`](./crates/ohno_macros/README.md) - Macros for the `ohno` crate.
3738
- [`recoverable`](./crates/recoverable/README.md) - Recovery information and classification for resilience patterns.

crates/layered/CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Changelog
2+
3+
## [0.1.0] - 2025-12-30
4+
5+
- ✨ Features
6+
7+
- Introduce the layered crate with `Service` trait and layer composition system
8+
- Add `Execute` wrapper for turning async functions into services
9+
- Add `Stack` trait for composing layers with tuples
10+
- Add `Intercept` middleware for observing and modifying inputs/outputs (`intercept` feature)
11+
- Add `DynamicService` for type-erased services (`dynamic-service` feature)
12+
- Add Tower interoperability via `Adapter` (`tower-service` feature)

crates/layered/Cargo.toml

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
[package]
5+
name = "layered"
6+
description = "Build composable async services with layered middleware."
7+
version = "0.1.0"
8+
readme = "README.md"
9+
keywords = ["oxidizer", "service", "middleware", "layer", "compose"]
10+
categories = ["data-structures"]
11+
12+
edition.workspace = true
13+
rust-version.workspace = true
14+
authors.workspace = true
15+
license.workspace = true
16+
homepage.workspace = true
17+
repository.workspace = true
18+
19+
[package.metadata.docs.rs]
20+
all-features = true
21+
22+
[package.metadata.cargo_check_external_types]
23+
allowed_external_types = ["tower_layer::Layer", "tower_service::Service"]
24+
25+
[features]
26+
default = []
27+
intercept = []
28+
dynamic-service = ["dep:dynosaur"]
29+
tower-service = ["dep:tower-service"]
30+
31+
[dependencies]
32+
dynosaur = { workspace = true, optional = true }
33+
tower-layer = { workspace = true }
34+
tower-service = { workspace = true, optional = true }
35+
36+
[dev-dependencies]
37+
alloc_tracker = { workspace = true }
38+
criterion = { workspace = true }
39+
dynosaur = { workspace = true }
40+
futures = { workspace = true, features = ["executor"] }
41+
mutants = { workspace = true }
42+
pin-project-lite = { workspace = true }
43+
static_assertions = { workspace = true }
44+
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
45+
tower = { workspace = true, features = ["limit", "util"] }
46+
tower-service = { workspace = true }
47+
48+
[lints]
49+
workspace = true
50+
51+
[[example]]
52+
name = "custom_layer"
53+
54+
[[example]]
55+
name = "service_basic"
56+
required-features = ["intercept"]
57+
58+
[[example]]
59+
name = "tower_compatibility"
60+
required-features = ["tower-service", "intercept"]
61+
62+
[[bench]]
63+
harness = false
64+
name = "dynamic"
65+
required-features = ["dynamic-service", "intercept"]
66+
67+
[[bench]]
68+
harness = false
69+
name = "intercept"
70+
required-features = ["intercept"]
71+
72+
[[bench]]
73+
harness = false
74+
name = "tower"
75+
required-features = ["intercept", "tower-service"]

0 commit comments

Comments
 (0)