Skip to main content

rainier_orm/
entity.rs

1//! The [`Entity`] trait — the contract `#[derive(Entity)]` fulfils.
2//!
3//! Everything generic CRUD needs is here: where the rows live ([`table`],
4//! [`primary_key`]), what the columns are ([`columns`]), how to decode a row
5//! ([`from_row`]), and how to project a value back out for writes
6//! ([`insert_values`], [`update_values`], [`pk_value`]). The repository layer
7//! ([`crate::repo`]) and schema builder ([`crate::schema`]) are written purely
8//! against this trait, so they work for any derived struct with no per-entity
9//! code.
10//!
11//! [`table`]: Entity::table
12//! [`primary_key`]: Entity::primary_key
13//! [`columns`]: Entity::columns
14//! [`from_row`]: Entity::from_row
15//! [`insert_values`]: Entity::insert_values
16//! [`update_values`]: Entity::update_values
17//! [`pk_value`]: Entity::pk_value
18
19use crate::{ColumnSpec, ForeignKeySpec, IndexSpec, Result, Row};
20use sea_query::Value;
21
22/// A struct that maps to one table. Implemented by `#[derive(Entity)]`; hand
23/// impls are possible but the derive is the intended path.
24pub trait Entity: Sized {
25    /// The table name.
26    fn table() -> &'static str;
27
28    /// Column metadata, in declaration order. Drives both DDL generation and
29    /// the column list of generated `INSERT`s.
30    fn columns() -> &'static [ColumnSpec];
31
32    /// The primary-key column name. Used by `find_by_pk` / `update` /
33    /// `delete_by_pk`.
34    fn primary_key() -> &'static str;
35
36    /// Secondary indexes (single-column and composite, unique or not),
37    /// emitted as separate `CREATE INDEX` statements by [`crate::schema`].
38    /// Defaults to none.
39    fn indexes() -> &'static [IndexSpec] {
40        &[]
41    }
42
43    /// Foreign-key constraints, emitted inline in the `CREATE TABLE`. These
44    /// enforce referential integrity *within one database*; they are not ORM
45    /// relationships (no navigation is generated). Defaults to none.
46    fn foreign_keys() -> &'static [ForeignKeySpec] {
47        &[]
48    }
49
50    /// Columns whose values are the **shard key** — a shard-encoded id
51    /// (`#[orm(shard_key)]`), the "user id by proxy". When a query constrains
52    /// one of these, the repo layer routes the operation to that id's shard.
53    ///
54    /// This is the *only* per-entity sharding metadata, and it's unavoidable:
55    /// the key column differs per table (`id` on the owner, `user_id` on its
56    /// rows), and its presence is what marks an entity *sharded* vs *global*.
57    /// Everything else about sharding — the family name, the id codec, whether a
58    /// connector shards at all — lives on the connector, not here. An entity
59    /// with no shard-key column is **global**, and sharding never affects it.
60    fn shard_columns() -> &'static [&'static str] {
61        &[]
62    }
63
64    /// Decode one row into `Self`. Generated to read each field through its
65    /// [`FromColumn`](crate::FromColumn) impl.
66    fn from_row(row: &dyn Row) -> Result<Self>;
67
68    /// `(column, value)` pairs for an `INSERT`. Auto-increment primary keys
69    /// are omitted so the database assigns them.
70    fn insert_values(&self) -> Vec<(&'static str, Value)>;
71
72    /// `(column, value)` pairs for an `UPDATE` — every column except the
73    /// primary key.
74    fn update_values(&self) -> Vec<(&'static str, Value)>;
75
76    /// This instance's primary-key value, for `WHERE pk = ?`.
77    fn pk_value(&self) -> Value;
78
79    /// One column's value by name, or `None` if the entity has no such column.
80    ///
81    /// What relationship loading needs: given a slice of parents, collect the
82    /// key column to look children up by. Defaults to a scan of
83    /// [`update_values`](Self::update_values) plus the primary key, which is
84    /// correct for every entity and costs one allocation of the row's values.
85    fn value_of(&self, column: &str) -> Option<Value> {
86        if column == Self::primary_key() {
87            return Some(self.pk_value());
88        }
89        self.update_values().into_iter().find(|(name, _)| *name == column).map(|(_, value)| value)
90    }
91}