Skip to main content

rusticx_core/
model.rs

1use crate::{column::ColumnDef, error::Result, value::{Row, Value}};
2
3/// Core trait that every ORM model must implement.
4///
5/// You should **never implement this by hand**. Apply `#[derive(Model)]` to
6/// your struct and the macro generates all methods automatically from the
7/// field types and `#[rusticx(...)]` attributes.
8///
9/// # Example
10///
11/// ```rust,ignore
12/// #[derive(Debug, Serialize, Deserialize, Model)]
13/// #[rusticx(table = "orders")]
14/// pub struct Order {
15///     #[rusticx(primary_key)]
16///     pub id: uuid::Uuid,
17///     pub user_id: uuid::Uuid,
18///     pub total: f64,
19///     pub placed_at: chrono::DateTime<chrono::Utc>,
20/// }
21/// ```
22pub trait Model: Sized + Send + Sync + 'static {
23    /// Table name (SQL) or collection name (MongoDB).
24    ///
25    /// Defaults to the struct name converted to `snake_case` and pluralised
26    /// (`User` → `users`). Override with `#[rusticx(table = "my_table")]`.
27    fn table_name() -> &'static str;
28
29    /// Full column schema — used by `migrate()` to emit `CREATE TABLE` DDL.
30    fn columns() -> Vec<ColumnDef>;
31
32    /// Primary key column name (default: `"id"`).
33    ///
34    /// Override at struct level: `#[rusticx(primary_key = "uuid")]`.
35    fn primary_key() -> &'static str {
36        "id"
37    }
38
39    /// Serialize all persisted fields into a flat [`Row`] map.
40    ///
41    /// Called by `Repository::insert` and `Repository::save` before
42    /// handing data to the backend adapter.
43    fn to_row(&self) -> Result<Row>;
44
45    /// Deserialize a [`Row`] map (returned by the backend) into `Self`.
46    ///
47    /// Called by every `find*` method after fetching raw rows.
48    fn from_row(row: Row) -> Result<Self>;
49
50    /// Extract the primary key value from this instance.
51    ///
52    /// Used by `Repository::save` to decide insert vs. update and by
53    /// `Repository::delete_by_id`.
54    fn pk_value(&self) -> Result<Value>;
55}
56
57/// Schema descriptor built from `Model::columns()`.
58#[derive(Debug, Clone)]
59pub struct TableSchema {
60    pub table: String,
61    pub columns: Vec<ColumnDef>,
62    pub indexes: Vec<IndexDef>,
63}
64
65#[derive(Debug, Clone)]
66pub struct IndexDef {
67    pub name: String,
68    pub columns: Vec<String>,
69    pub unique: bool,
70}
71
72impl TableSchema {
73    pub fn from_model<M: Model>() -> Self {
74        Self {
75            table: M::table_name().to_owned(),
76            columns: M::columns(),
77            indexes: vec![],
78        }
79    }
80
81    pub fn with_index(mut self, name: impl Into<String>, cols: Vec<impl Into<String>>, unique: bool) -> Self {
82        self.indexes.push(IndexDef {
83            name: name.into(),
84            columns: cols.into_iter().map(|c| c.into()).collect(),
85            unique,
86        });
87        self
88    }
89}