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.
- 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
Add this to your Cargo.toml:
[dependencies]
model-views = "0.1"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: String,
}
// Reading a user
let user = UserGet {
id: 1,
name: "Alice".to_string(),
email: Some("alice@example.com".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
};Control field visibility in each view mode:
get = "required"- Field is always present (default)get = "optional"- Field is wrapped inOption<T>get = "forbidden"- Field is excluded
create = "required"- Field must be provided (default)create = "optional"- Field is wrapped inOption<T>create = "forbidden"- Field is excluded
patch = "required"- Field is wrapped inPatch<T>(default)patch = "optional"- Field is wrapped inPatch<Option<T>>patch = "forbidden"- Field is excluded
#[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<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();The following cargo features are available:
derive(default) - Enables the#[derive(Views)]macroserde- AddsSerialize/Deserializesupport forPatch<T>uuid- ImplementsViewforuuid::Uuidchrono- ImplementsViewforchrono::DateTime<Utc>
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
Licensed under the European Union Public Licence (EUPL), Version 1.2.