Crate model_views

Crate model_views 

Source
Expand description

Type-safe view types for different access modes on data models.

This crate provides a trait-based system for generating specialized view types for different operations on data models (Get/Read, Create, Patch/Update). Each view type only includes the fields relevant to that operation, enforcing API contracts at compile time.

§Core Concepts

§View Modes

The crate defines three access modes for models:

  • ViewModeGet: For read operations, retrieving existing data
  • ViewModeCreate: For create operations, accepting input to create new entities
  • ViewModePatch: For update operations, allowing partial modifications

§The View Trait

The View trait associates a type with its representation in a specific mode:

trait View<M: ViewMode> {
    type Type;
}

This allows the same model to have different representations depending on the operation.

§Patch Type

The Patch<T> enum is central to update operations, providing an explicit way to distinguish between “don’t update this field” and “update this field to a value”:

  • Patch::Ignore: Leave the field unchanged
  • Patch::Update(value): Update the field to the given value

This is clearer than using Option<T> for updates, especially when dealing with optional fields.

§Usage

§Basic Example

use model_views::{Views, Patch};

#[derive(Views)]
#[views(serde)]
struct User {
    // ID is returned when reading, but can't be set during create/update
    #[views(get = "required", create = "forbidden", patch = "forbidden")]
    id: u64,
     
    // Name is always required for all operations
    #[views(get = "required", create = "required", patch = "required")]
    name: String,
     
    // Email is optional everywhere
    #[views(get = "optional", create = "optional", patch = "optional")]
    email: String,
}

// This generates three types:

// UserGet - for reading user data
let user_get = UserGet {
    id: 1,
    name: "Alice".to_string(),
    email: Some("alice@example.com".to_string()),
};

// UserCreate - for creating new users
let user_create = UserCreate {
    name: "Bob".to_string(),
    email: Some("bob@example.com".to_string()),
};

// UserPatch - for updating users
let user_patch = UserPatch {
    name: Patch::Update("Charlie".to_string()), // Update the name
    email: Patch::Ignore,                       // Don't change email
};

§Nested Models

Views work seamlessly with nested structures:

#[derive(Views)]
struct Post {
    #[views(get = "required", create = "forbidden", patch = "forbidden")]
    id: u64,
     
    #[views(get = "required")]
    title: String,
     
    // Nested models are automatically handled
    #[views(get = "required", create = "forbidden", patch = "optional")]
    author: User,
}

// PostPatch will have: author: Patch<Option<UserPatch>>
let post_patch = PostPatch {
    title: Patch::Update("New Title".to_string()),
    author: Patch::Update(Some(UserPatch {
        name: Patch::Update("New Author Name".to_string()),
        email: Patch::Ignore,
    })),
};

§Field Policies

Each field can be configured independently for each view mode:

  • get = "required": Field is always present in Get view

  • get = "optional": Field is Option<T> in Get view

  • get = "forbidden": Field is excluded from Get view

  • create = "required": Field must be provided when creating

  • create = "optional": Field is Option<T> in Create view

  • create = "forbidden": Field cannot be set during creation

  • patch = "patch": Field is Patch<T> in Patch view

  • patch = "optional": Field is Patch<Option<T>> in Patch view

  • patch = "forbidden": Field cannot be modified via patches

§Features

  • derive (default): Enables the #[derive(Views)] procedural macro
  • serde: Adds Serialize/Deserialize support for Patch<T>
  • uuid: Implements View for uuid::Uuid
  • chrono: Implements View for chrono::DateTime<Utc>

§Benefits

  • Type Safety: Different operations use different types, catching errors at compile time
  • API Clarity: View types clearly document which fields are required/optional for each operation
  • Reduced Boilerplate: Automatically generates DTOs (Data Transfer Objects) from models
  • Explicit Updates: Patch<T> makes update intent clear, avoiding ambiguity with Option<T>

Structs§

ViewModeCreate
Create access for a model.
ViewModeGet
Read access for a model.
ViewModePatch
Update/Write access for a model.

Enums§

Patch
Represents a potential update to a value, either providing a new value or explicitly indicating that the value should be ignored/unchanged.

Traits§

View
ViewMode
Access mode for a model.

Derive Macros§

Views
Derives view types for different access modes from a model struct.