2 releases
Uses new Rust 2024
| 0.1.1 | Nov 14, 2025 |
|---|---|
| 0.1.0 | Nov 14, 2025 |
#644 in Encoding
20KB
186 lines
model-views
Type-safe view types for different access modes on data models.
This library automatically generates specialized view types for Get, Create, and Patch operations from your model structs. Each view type only includes the fields relevant to that operation, enforcing API contracts at compile time and reducing boilerplate.
Key Features
- Type Safety: Different operations use different types, catching errors at compile time
- Reduced Boilerplate: Automatically generates DTOs from model definitions
- Explicit Updates:
Patch<T>type makes update intent clear - Serde Integration: Optional serialization/deserialization support
- Nested Models: Works seamlessly with nested structures
- Generic Support: Handles generic types and where clauses
Usage
Add this to your Cargo.toml:
[dependencies]
model-views = "0.1"
Basic Example
use model_views::{Views, Patch};
#[derive(Views)]
#[views(serde)]
struct User {
// ID is read-only, generated by the system
#[views(get = "required", create = "forbidden", patch = "forbidden")]
id: u64,
// Name is required for all operations
#[views(get = "required", create = "required", patch = "required")]
name: String,
// Email is optional
#[views(get = "optional", create = "optional", patch = "optional")]
email: Option<String>,
}
// Reading a user
let user = UserGet {
id: 1,
name: "Alice".to_string(),
email: Some("[email protected]".to_string()),
};
// Creating a new user (no ID field)
let create = UserCreate {
name: "Bob".to_string(),
email: None,
};
// Updating a user (only specify changed fields)
let patch = UserPatch {
name: Patch::Update("Charlie".to_string()),
email: Patch::Ignore, // Don't change email
};
Field Policies
Control field visibility in each view mode:
Get Mode
get = "required"- Field is always present (default)get = "optional"- Field is wrapped inOption<T>get = "forbidden"- Field is excluded
Create Mode
create = "required"- Field must be provided (default)create = "optional"- Field is wrapped inOption<T>create = "forbidden"- Field is excluded
Patch Mode
patch = "required"- Field is wrapped inPatch<T>(default)patch = "optional"- Field is wrapped inPatch<Option<T>>patch = "forbidden"- Field is excluded
Nested Models
#[derive(Views)]
struct Article {
#[views(get = "required", create = "forbidden", patch = "forbidden")]
id: u64,
#[views(get = "required", create = "required", patch = "required")]
title: String,
// Nested view types are automatically handled
#[views(get = "required", create = "forbidden", patch = "optional")]
author: User,
}
The Patch Type
The Patch<T> enum makes update intent explicit:
use model_views::Patch;
// Explicitly ignore a field
let no_change = Patch::Ignore;
// Update a field to a new value
let update = Patch::Update("new value".to_string());
// Convert to/from Option
let opt: Option<String> = update.into();
let patch: Patch<String> = Some("value".to_string()).into();
Cargo Features
The following cargo features are available:
derive(default) - Enables the#[derive(Views)]macroserde- AddsSerialize/Deserializesupport forPatch<T>uuid- ImplementsViewforuuid::Uuidchrono- ImplementsViewforchrono::DateTime<Utc>
Use Cases
This library is particularly useful for:
- REST API request/response types
- GraphQL input/output types
- Database model transformations
- Form validation and submission
- Any scenario where different operations require different field sets
License
Licensed under the European Union Public Licence (EUPL), Version 1.2.
Dependencies
~0.2–0.9MB
~16K SLoC