# typed_use_cases
**Formalize use cases at the type level. Zero runtime overhead.**
A Rust library that brings UML use cases into your type system, providing compile-time awareness when your code structure changes in ways that affect declared use cases.
> ⚠️ **Experimental**: This is a proof-of-concept library. It has not been validated in production environments and should be considered an exploratory tool for representing use cases in type systems.
---
## The Problem
In traditional development, use cases live outside the code:
- **Maintained manually**: Use case diagrams and specifications are separate documents
- **Silent drift**: When code changes, no one remembers to update the use cases
- **Becomes stale**: Documentation diverges from implementation over time
- **Poorly defined**: Use cases often end up ambiguous, generic, or unmeasurable
- **Abandoned**: Teams stop using use cases because they're too hard to maintain
The fundamental issue: **use cases and code evolve independently with no connection between them**.
---
## What This Library Does
`typed_use_cases` brings use cases into your type system with **zero runtime cost**:
- **Formalizes use cases** as named traits in Rust
- **Compile-time awareness**: The compiler knows which use cases exist
- **Breaking changes are visible**: If you change a method signature or remove an implementation, the compiler breaks at the use case level
- **No runtime overhead**: Uses a zero-sized System type (0 bytes)
- **Verification in tests only**: Uses `#[cfg(test)]` to verify implementations
### What This Library Is NOT
This library is **not**:
- ❌ **NOT a verification tool** - It does not prove your program satisfies a use case
- ❌ **NOT a testing framework** - It does not test that your system fulfills use case requirements (this is not feasible to automate)
- ❌ **NOT a formal methods tool** - It does not prove program properties
- ❌ **NOT for runtime enforcement** - All verification happens at compile time, in test builds only
### What This Library IS
This library **is**:
- ✅ **A type-level representation** - Makes your type system aware of declared use cases
- ✅ **A compile-time alarm** - Alerts you when changes might violate use case contracts
- ✅ **A documentation tool** - Use cases live in code, not separate documents
- ✅ **A proof of concept** - An experiment to see if type-level use cases provide value
You can think of it as: **"The compiler knows which use cases should exist, and will complain if you break their structure."**
It does **not** guarantee that your implementation is correct or complete, only that the structure exists.
---
## Possible Use Cases for This Library
### 1. LLM Context Enhancement
This library may be useful for Large Language Models (LLMs) to:
- Parse use case declarations directly from code
- Use use cases as a source of truth when generating or modifying code
- Better understand system intent and behavior contracts
### 2. Documentation Generation
Use case metadata (NAME, DESCRIPTION) is accessible at compile time and can be:
- Extracted for automatic documentation
- Used to generate diagrams
- Displayed in developer tools
### 3. Architectural Awareness
During refactoring or feature development:
- The type system alerts you when changes affect use cases
- Use case traits act as stable interfaces
- Easier to see which parts of the system implement which business logic
---
## The Solution
`typed_use_cases` makes use cases **first-class citizens in your type system**:
```rust
use typed_use_cases::{Actor, Entity, UseCase};
// 1. Declare actors and entities
#[derive(Actor)]
struct Authenticated { user_id: u64 }
#[derive(Entity)]
struct Cart {
owner: Authenticated,
items: Vec<u64>,
}
// 2. Declare use cases as named traits
trait AddItemToCart: UseCase<
Authenticated,
Cart,
Input = Product,
Output = Result<Cart, String>,
Dependencies = (Box<dyn InventoryService>, Box<dyn CartRepository>),
> {}
// 3. Define your System type (application-specific, not part of the library)
struct System; // Zero-sized type
// 4. Implement use cases on your System
impl UseCase<Authenticated, Cart> for System {
const NAME: &'static str = "Add item to cart";
const DESCRIPTION: &'static str = "An authenticated user can add a product to their cart";
type Input = Product;
type Output = Result<Cart, String>;
type Dependencies = (Box<dyn InventoryService>, Box<dyn CartRepository>);
fn satisfy(
actor: Authenticated,
cart: Cart,
product: Product,
deps: Self::Dependencies,
) -> Self::Output {
let (inventory, cart_repo) = deps;
// Implementation here...
Ok(cart)
}
}
impl AddItemToCart for System {}
// 5. Verify at compile time (in tests) that all use cases are implemented
typed_use_cases::implement_all_use_cases!(System: [
AddItemToCart,
// The compiler will complain if you declare a use case but forget to implement it
]);
```
**Result**: The type system is aware of your use cases. If you change a signature or remove an implementation, compilation fails in tests.
**Important**: The `System` type is **defined by you**, not by this library. It represents your application's use case implementations.
---
## How It Works
### Zero-Sized Type (ZST)
`System` is a **zero-sized type** — it occupies **0 bytes** at runtime:
```rust
assert_eq!(std::mem::size_of::<System>(), 0);
```
The `System` type exists purely for the compiler. It acts as a **witness** that your use cases are implemented.
### Compile-Time Verification
The `implement_all_use_cases!` macro expands only in test builds:
```rust
#[cfg(test)]
mod __use_cases_verification {
static_assertions::assert_impl_all!(System: AddItemToCart);
static_assertions::assert_impl_all!(System: Checkout);
// ... one assertion per use case
}
```
This generates **zero runtime code**. The assertions run at compile time during `cargo test`.
### No Controller Changes
Your HTTP handlers and controllers remain unchanged. The library is purely a **compile-time concern**:
```rust
// Your controller code - unchanged
async fn add_to_cart_handler(req: Request) -> Response {
let actor = authenticate(&req)?;
let cart = load_cart(actor.user_id)?;
let product = parse_product(&req)?;
// Call the use case execution logic
let updated_cart = add_item_to_cart_logic(actor, cart, product);
save_cart(&updated_cart)?;
Response::ok()
}
```
The `System` type and trait implementations serve as **documentation and verification**, not as runtime infrastructure.
---
## Install
Using cargo (recommended):
```bash
cargo add typed_use_cases
```
Or add to your `Cargo.toml`:
```toml
[dependencies]
typed_use_cases = "0.1"
```
That's it! No additional dependencies needed.
Note: `static_assertions` is only needed for the compile-time verification macro and does not appear in release builds.
---
## Concepts
### Actor
An **actor** is the initiator of a use case. Actors exist independently of any entity or action.
```rust
#[derive(Actor)]
struct Anonymous;
#[derive(Actor)]
struct Authenticated { user_id: u64 }
```
### Entity
An **entity** is what a use case operates on — the subject of the action.
```rust
#[derive(Entity)]
struct Catalog {
products: Vec<Product>,
}
#[derive(Entity)]
struct Cart {
owner: Authenticated,
items: Vec<u64>,
}
```
### DependentEntity
A **dependent entity** is an entity whose existence is tied to a specific actor (e.g., a `Cart` belongs to an `Authenticated` user).
The `#[derive(Entity)]` macro automatically implements `DependentEntity<A>` if:
- A field named `owner` exists
- Its type is a single-segment path (e.g., `Authenticated`)
```rust
#[derive(Entity)]
struct Cart {
owner: Authenticated, // Automatically generates DependentEntity<Authenticated>
items: Vec<u64>,
}
```
Manual implementation:
```rust
impl DependentEntity<Authenticated> for Cart {
fn owner(&self) -> &Authenticated {
&self.owner
}
}
```
### UseCase
A **use case** IS the action. It's declared as a named trait that extends `UseCase<Actor, Entity>` and fixes all type parameters:
```rust
trait AddItemToCart: UseCase<
Authenticated, // Who initiates
Cart, // What it operates on
Input = Product, // Additional input data
Output = Cart, // What it produces
Dependencies = (), // External services needed
> {}
```
Each use case has compile-time metadata:
```rust
impl UseCase<Authenticated, Cart> for System {
const NAME: &'static str = "Add item to cart";
const DESCRIPTION: &'static str = "An authenticated user can add a product to their cart";
// ... implementation
}
```
### System
A **zero-sized type defined by the user** (not by the library) that implements all use cases. It exists only for the compiler and occupies 0 bytes at runtime:
```rust
struct System; // Your application defines this
impl UseCase<Anonymous, Catalog> for System { /* ... */ }
impl UseCase<Authenticated, Cart> for System { /* ... */ }
```
**Important**: The `System` type belongs to **your application**, not to the `typed_use_cases` library. Each application defines its own System type to implement its use cases.
### implement_all_use_cases!
A macro that verifies (at compile time, in test builds only) that `System` implements every declared use case:
```rust
typed_use_cases::implement_all_use_cases!(System: [
BrowseCatalog,
AddItemToCart,
Checkout,
]);
```
If any use case is missing, **compilation fails** during `cargo test`.
---
## Example: E-Commerce
See [`examples/ecommerce.rs`](examples/ecommerce.rs) for a complete working example with:
- 3 actors: `Anonymous`, `Registered`, `Authenticated`
- 4 entities: `Catalog`, `Product`, `Cart`, `Order`
- 3 use cases: `BrowseCatalog`, `AddItemToCart`, `Checkout`
Run it with:
```bash
cargo run --example ecommerce
cargo test --example ecommerce
```
---
## Design Principles
- **Zero runtime overhead**: `System` is a ZST. No allocation, no dynamic dispatch.
- **Compile-time verification**: Use cases are checked at compile time, not runtime.
- **No framework lock-in**: Compatible with any web framework, DI container, or async runtime.
- **No boilerplate in production**: Controllers remain unchanged. This is purely additive.
- **Stable Rust**: No nightly features required.
---
## FAQ
### Does this work with async?
The `UseCase::execute` signature is synchronous by default. For async use cases:
1. Use the trait as a **contract witness** only (for compile-time verification)
2. Implement your actual async logic separately
3. Or wrap the execution in an async block
### Does this impose a dependency injection pattern?
No. `Dependencies` is a free associated type. Use `()` if you have no dependencies, or pass in any type you want (a struct, a trait object, a tuple of services, etc.).
### What about multiple use cases with the same Actor + Entity?
Each `UseCase<A, E>` implementation must be unique. If you have two use cases with the same actor and entity types, use different entity types (e.g., `Cart` vs `Order`) or create wrapper types.
### Does `static_assertions` appear in my release binary?
No. The `implement_all_use_cases!` macro expands only under `#[cfg(test)]`, and `static_assertions` is listed as a `# No additional dependencies needed!`.
---
## License
MIT
---
## Contributing
Contributions welcome! Please open an issue or pull request on GitHub.