3 releases
| 0.1.2 | May 14, 2025 |
|---|---|
| 0.1.1 | May 12, 2025 |
| 0.1.0 | May 12, 2025 |
#2628 in Procedural macros
94 downloads per month
47KB
341 lines
Rusticx Derive
rusticx-derive is a procedural macro crate that provides the #[derive(Model)] attribute for the rusticx ORM. This macro automatically generates the necessary SQLModel trait implementation for your Rust structs, enabling them to be easily mapped to database tables.
Purpose
Writing database mapping code manually can be repetitive and error-prone. The #[derive(Model)] macro automates this process by inspecting your struct definition and its attributes to generate:
-
- The database table name.
-
- The primary key field name and logic to retrieve its value.
-
- The SQL
CREATE TABLEstatement, including column definitions, constraints, and defaults.
- The SQL
-
- Logic to extract field names and values for SQL queries (like
INSERTorUPDATE).
- Logic to extract field names and values for SQL queries (like
-
- Logic to deserialize database rows (typically represented as a JSON object) back into struct instances.
This allows you to define your database models in a declarative way using standard Rust structs and attributes, reducing boilerplate code and potential errors.
Installation
Add rusticx-derive and the main rusticx crate to your Cargo.toml. Remember to enable the feature flags for the database drivers you intend to use in the rusticx crate (e.g., "postgres", "mysql", "rusqlite").
[dependencies]
rusticx = { version = "x.y.z", features = ["<database-driver>"] } # Replace x.y.z with the actual version and specify features
rusticx-derive = "x.y.z" # Replace x.y.z with the actual version
serde = { version = "1.0", features = ["derive"] } # Required for deserialization
serde_json = "1.0" # Required for row deserialization via JSON
# Add uuid and chrono if you use Uuid or date/time types in your models
# uuid = { version = "1.0", features = ["serde", "v4"] }
# chrono = { version = "0.4", features = ["serde"] }
Usage
Apply the #[derive(Model)] attribute to your struct definition. Your struct must have named fields. You can customize the table name and field mappings using #[model(...)] attributes on the struct and its fields. The struct typically needs to derive serde::Deserialize for the from_row method generated by the macro.
use rusticx_derive::Model;
use serde::{Serialize, Deserialize};
use uuid::Uuid; // Example using uuid crate
use chrono::NaiveDateTime; // Example using chrono crate
#[derive(Model, Debug, Serialize, Deserialize)]
#[model(table = "my_application_users")] // Optional: Customize table name
struct User {
#[model(primary_key, auto_increment)] // Marks 'id' as primary key with auto-increment
id: Option<i32>, // Use Option<i32> for nullable auto-increment PKs
#[model(column = "username")] // Optional: Specify a custom column name
name: String, // Maps to a text/varchar column
#[model(nullable)] // Explicitly marks the 'email' column as nullable
email: Option<String>, // Option<T> fields are automatically treated as nullable
#[model(default = "'pending'")] // Sets a SQL default value (note the single quotes for the string literal)
status: String,
#[model(sql_type = "JSONB")] // Specify a custom SQL data type
metadata: serde_json::Value, // serde_json::Value maps well to JSON/JSONB types
#[model(skip)] // This field will be ignored by the ORM
transient_data: String,
#[model(primary_key, uuid)] // Alternative primary key: UUID with default generation (only one PK per struct!)
// user_uuid: Uuid, // Use Uuid or Option<Uuid> for UUID primary keys
created_at: NaiveDateTime, // Maps to an appropriate DateTime/Timestamp type
}
Attributes
The #[derive(Model)] macro recognizes #[model(...)] attributes on the struct and its fields.
Struct Attributes (#[model(...)] on the struct)
-
#[model(table = "custom_name")]: Specifies the database table name for this model. If omitted, the macro defaults to the struct name converted to lowercase and pluralized (e.g.,User->users).
Field Attributes (#[model(...)] on fields)
-
#[model(primary_key)]: Designates this field as the primary key for the table. Exactly one field should be marked with this attribute.
-
#[model(column = "custom_name")]: Specifies the database column name for this field. If omitted, the macro defaults to the field name converted to lowercase.
-
#[model(default = "SQL_DEFAULT_VALUE")]: Sets a SQLDEFAULTconstraint for the column. The value provided inside the quotes is inserted directly into theCREATE TABLEstatement. Ensure the value is correctly formatted for SQL (e.g., enclose string literals in single quotes:"'active'").
-
#[model(nullable)]: Explicitly marks the column as allowingNULLvalues (NULLin SQL). Fields withOption<T>type are automatically treated as nullable, but this attribute can be used on non-Option types that should still allowNULL.
-
#[model(sql_type = "SQL_TYPE_STRING")]: Specifies a custom SQL data type for the column. This overrides the default type mapping based on the Rust type. The string provided is used directly in theCREATE TABLEstatement.
-
#[model(skip)]: Excludes this field entirely from the generatedSQLModelimplementation. It will not be included inCREATE TABLEstatements,INSERT/UPDATEqueries, or deserialized byfrom_row. Useful for transient or computed fields.
-
#[model(auto_increment)]: Applicable only to fields also marked with#[model(primary_key)]and having an integer type (likei32,i64, etc., oftenOption<i32>). Adds the database-specific syntax for auto-incrementing integer primary keys (SERIALorGENERATED ALWAYS AS IDENTITYfor PostgreSQL,AUTO_INCREMENTfor MySQL,AUTOINCREMENTfor SQLite).
-
#[model(uuid)]: Applicable only to fields also marked with#[model(primary_key)]and having auuid::UuidorOption<uuid::Uuid>type. Adds database-specific default value generation for UUID primary keys (gen_random_uuid()for PostgreSQL,UUID()for MySQL, and a standard UUID generation expression for SQLite).
Automatic SQL Type Mapping
The macro attempts to infer appropriate SQL data types for columns based on the Rust type of the field. This mapping is then translated to the specific syntax for the target database type in the CREATE TABLE statement.
| Rust Type | Mapped SqlType | Typical SQL Type Examples (PostgreSQL, MySQL, SQLite) |
|---|---|---|
i8, i16, i32, u8, u16, u32 |
Integer |
INTEGER, INT |
i64, u64 |
BigInt |
BIGINT |
f32, f64 |
Float |
REAL, DOUBLE PRECISION, FLOAT |
bool |
Boolean |
BOOLEAN, TINYINT(1) |
String, str |
Text |
TEXT, VARCHAR |
Uuid (from uuid crate`) |
Text |
TEXT, VARCHAR(36) (often stored as text) |
NaiveDate (from chrono crate) |
Date |
DATE |
NaiveTime (from chrono crate) |
Time |
TIME |
NaiveDateTime, DateTime (from chrono crate) |
DateTime |
TIMESTAMP, DATETIME |
Vec<u8> |
Blob |
BYTEA, BLOB |
Option<T> |
(Maps T) |
Adds NULL constraint |
serde_json::Value |
Blob |
BLOB (Consider using #[model(sql_type = "JSONB")] or similar for JSON types) |
You can always override this automatic mapping using the #[model(sql_type = "...") attribute on a field.
Requirements & Notes
-
- The derived struct must have named fields (
struct MyStruct { field1: Type, field2: Type, ... }). Tuple structs and unit structs are not supported.
- The derived struct must have named fields (
-
- The struct needs to derive
serde::Deserializefor thefrom_rowmethod to function correctly, as row data is currently processed by converting it to aserde_json::Valueobject before deserialization.
- The struct needs to derive
-
- Ensure that the types used in your struct fields (like
Uuid,NaiveDateTime,serde_json::Value) are included as dependencies in yourCargo.tomlwith necessary features (e.g.,serdefeature for integration).
- Ensure that the types used in your struct fields (like
-
- Only one field should be marked as
#[model(primary_key)]. The macro does not currently enforce this and may produce unexpected results if multiple fields are marked.
- Only one field should be marked as
-
- The
primary_key_valuemethod in the generatedSQLModelcurrently returnsOption<i32>, which is suitable for auto-increment integer primary keys. If you useUuidor other types for your primary key, you might need to interact with the field directly or potentially extend theSQLModeltrait in the mainrusticxcrate to support different primary key return types. Similarly,set_primary_keytakesi32.
- The
License
Dependencies
~150–550KB
~13K SLoC