#[derive(Model)]
{
// Attributes available to this derive:
#[table]
#[check]
#[bind]
#[extr]
#[column]
}
Expand description
Derive macro for the Model
trait.
Requirements
Model
can only be derived for non-generic struct
s with suitable named fields.
Type | Example | Supported? |
---|---|---|
Standard struct | struct Person { name: String } | ✔ |
Generic struct | struct Person<'a, T, const N: usize> { ... } | ✘ |
Tuple struct | struct Point(i64, i64) | ✘ |
Unit/ZST struct | struct Unit; or struct Unit {} | ✘ |
enum s | enum Direction { Up, Down } | ✘ |
union s | union Number { i: i32, f: f32 } | ✘ |
(Note, however, that any non-supported type can be used in a Model
assuming it meets the below requirements.)
All fields in a Model
derivee must either:
- Implement
ToSql
andFromSql
. Most common types will meet this requirement. - Have
#[bind]
and#[extr]
attributes on fields that do not meet the first requirement.- This escape hatch is designed to enable compatibility with certain
std
types likePathBuf
and third-party crate types.
- This escape hatch is designed to enable compatibility with certain
Usage
Most of the time, deriving Model
is easy. The only thing you need to specify is the table name:
#[derive(Model)]
#[table("people")] // <-- Required
pub struct Person {
pub name: String,
pub age: u16,
}
For more complicated types and schemas, you may need to make use of some of the attributes recognized by the macro:
#[derive(Model)]
#[table("users")]
#[check("schema.sql")]
struct User {
username: String,
#[bind(bind_path)]
#[extr(extr_path)]
home_dir: PathBuf,
#[column("pwd")]
password: Vec<u8>,
}
Attributes
The Model
derive macro recognizes several attributes.
#[check]
Usage:
#[check("path_to_schema")]
pub struct T { ... }
The check
attribute automatically generates a test that checks the derived Model
implementation against a provided schema.
More specifically, the generated test verifies that:
- The specified table exists.
- The columns in the schema match up with those specified by the model type. If the model has fields not present in the schema (or vice versa) the test will fail.
It does not verify the validity of column types, nor does it test actual insertion/retrieval.
#[bind]
/#[extr]
Usage:
#[bind(path::to::fn)]
#[extr(path::to::fn)]
field: T,
The bind
and extr
attributes specify functions used to convert the annotated field to and from an SQL-friendly representation.
This is primarily intended as an escape hatch for when you can’t implement ToSql
and FromSql
yourself.
Both attributes take as their argument a path to a free function used to do the conversion.
- For
bind
, the signature should befn(&T) -> BindResult
. - For
extr
, the signature should befn(&ValueRef) -> ExtrResult<T>
.
In both cases T
is the type of the field being annotated. For some types (e.g. PathBuf
) you may also be able to use a type it derefs to, like Path
.
Example implementations for PathBuf
:
pub fn bind_path(value: &Path) -> BindResult {
use rusqlite::types::Value;
use rusqlite::types::ToSqlOutput;
// Depending on your program, it may make more sense
// to error if a lossless conversion isn't possible.
let str = value.to_string_lossy().into_owned();
Ok(ToSqlOutput::Owned(
Value::Text(str)
))
}
pub fn extr_path(value: &ValueRef) -> ExtrResult<PathBuf> {
let path = value.as_str()?;
let path = PathBuf::from(path);
Ok(path)
}
#[column]
Usage:
#[column("name")]
field: T,
The column
attribute overrides the column name Exemplar maps the annotated field to. By default, the field name is assumed to directly map to the underlying schema - #[column]
is how you alter this behavior.
Notes
Any type that derives Model
also has an implementation of TryFrom<Row>
derived, making models usable in some generic contexts.