Derive Macros

Ferro provides two derive macros that eliminate boilerplate for database models and validation. FerroModel scaffolds CRUD operations on SeaORM entities; ValidateRules co-locates validation rules with struct field definitions.

FerroModel

FerroModel generates create, update, delete, and query methods from a SeaORM entity model struct. Place it alongside DeriveEntityModel in the derive list.

Entity Definition

#![allow(unused)]
fn main() {
use ferro::FerroModel;
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize, FerroModel)]
#[sea_orm(table_name = "posts")]
pub struct Model {
    #[sea_orm(primary_key)]
    pub id: i32,
    pub title: String,
    pub body: String,
    pub published: bool,
    pub author_id: i32,
    pub slug: Option<String>,
    pub created_at: String,
    pub updated_at: String,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
}

Generated API

Create — returns a builder with a setter for each non-primary-key field:

#![allow(unused)]
fn main() {
let post = Post::create()
    .set_title("Hello")
    .set_body("World")
    .set_published(false)
    .set_author_id(1)
    .insert()
    .await?;
}

Update — selectively updates fields on an existing model instance:

#![allow(unused)]
fn main() {
let post = post.update()
    .set_title("Updated Title")
    .save()
    .await?;
}

Clear optional fieldsclear_<field>() sets a nullable field to None:

#![allow(unused)]
fn main() {
let post = post.update()
    .clear_slug()
    .save()
    .await?;
}

Delete — removes the row from the database:

#![allow(unused)]
fn main() {
post.delete().await?;
}

Query — returns a SeaORM Select<Entity> for building queries:

#![allow(unused)]
fn main() {
let published_posts = Post::query()
    .filter(Column::Published.eq(true))
    .all(&db)
    .await?;
}

Generated Methods

MethodReturnsDescription
Entity::create()CreateBuilderBuilder for inserting a new row
instance.update()UpdateBuilderBuilder for selectively updating fields
instance.delete()ResultDeletes the row from the database
Entity::query()Select<Entity>SeaORM Select for building queries

The primary key field is excluded from CreateBuilder. Option<T> fields gain both set_<field>() and clear_<field>() on UpdateBuilder.

ValidateRules

ValidateRules generates a .validate() method from #[rule(...)] attributes on struct fields. It uses Ferro's built-in rule functions — the same functions available in the fluent Validator::new() API (documented in Validation).

Note: #[rule(...)] is Ferro's own attribute syntax. It is not the same as the #[validate(...)] attribute from the validator crate.

Struct Definition

#![allow(unused)]
fn main() {
use ferro::ValidateRules;

#[derive(ValidateRules)]
struct RegistrationRequest {
    #[rule(required, email)]
    email: String,

    #[rule(required, min(8))]
    password: String,

    #[rule(required, string, min(2), max(50))]
    name: String,

    #[rule(required, integer, min(18), max(120))]
    age: i32,

    #[rule(nullable, url)]
    website: Option<String>,
}
}

Usage

#![allow(unused)]
fn main() {
let req = RegistrationRequest { /* ... */ };
req.validate()?;  // Returns Result<(), ValidationErrors>
}

The .validate() method returns Ok(()) when all rules pass, or Err(ValidationErrors) with per-field error messages when any rule fails.

Available Rules

RuleDescriptionExample
requiredField must be present and non-empty#[rule(required)]
emailMust be a valid email address#[rule(required, email)]
stringMust be a string value#[rule(string)]
integerMust be an integer value#[rule(integer)]
min(n)Minimum value or minimum string length#[rule(min(8))]
max(n)Maximum value or maximum string length#[rule(max(100))]
between(a, b)Value between a and b inclusive#[rule(between(1, 10))]
urlMust be a valid URL#[rule(url)]
nullableField may be None; skips other rules when absent#[rule(nullable, url)]

Rules are evaluated in declaration order. For Option<T> fields, use nullable first to skip subsequent rules when the value is None.

See Also

  • Database — SeaORM entity setup and query patterns
  • Validation — fluent Validator::new() API

MCP Tools

Use explain_model to inspect the generated CRUD API for a FerroModel-derived entity.

explain_model

Returns the full structure of a SeaORM entity, including which fields are included in the CreateBuilder and UpdateBuilder, optional fields that gain clear_*() methods, and any RouteBinding implementation. This is the same tool documented in Database — it works on any entity, whether or not it uses #[derive(FerroModel)].