Skip to main content

rango_orm_core/
constraints.rs

1use crate::quote_ident;
2
3/// A table-level CHECK constraint — raw SQL expression.
4#[derive(Debug, Clone)]
5pub struct CheckConstraint {
6    pub sql: String,
7    pub name: String,
8}
9
10impl CheckConstraint {
11    pub fn new(sql: impl Into<String>) -> Self {
12        Self {
13            sql: sql.into(),
14            name: String::new(),
15        }
16    }
17
18    pub fn name(mut self, name: impl Into<String>) -> Self {
19        self.name = name.into();
20        self
21    }
22}
23
24/// A table-level UNIQUE constraint — single or multi-column, optional partial condition.
25#[derive(Debug, Clone)]
26pub struct UniqueConstraint {
27    pub fields: Vec<String>,
28    pub condition: Option<String>,
29    pub name: String,
30}
31
32impl UniqueConstraint {
33    pub fn on(fields: &[&str]) -> Self {
34        Self {
35            fields: fields.iter().map(|s| s.to_string()).collect(),
36            condition: None,
37            name: String::new(),
38        }
39    }
40
41    /// Add a WHERE condition (partial unique index).
42    pub fn condition(mut self, cond: impl Into<String>) -> Self {
43        self.condition = Some(cond.into());
44        self
45    }
46
47    pub fn name(mut self, name: impl Into<String>) -> Self {
48        self.name = name.into();
49        self
50    }
51}
52
53/// A table-level constraint — either CHECK or UNIQUE.
54#[derive(Debug, Clone)]
55pub enum Constraint {
56    Check(CheckConstraint),
57    Unique(UniqueConstraint),
58}
59
60/// Shared constraint naming so generated SQL and snapshot diffs agree.
61pub fn check_constraint_name(table: &str, constraint: &CheckConstraint, index: usize) -> String {
62    if constraint.name.is_empty() {
63        format!("{}_check_{}", table, index)
64    } else {
65        constraint.name.clone()
66    }
67}
68
69/// Shared constraint naming so generated SQL and snapshot diffs agree.
70pub fn unique_constraint_name(table: &str, constraint: &UniqueConstraint) -> String {
71    if constraint.name.is_empty() {
72        format!("{}_{}_unique", table, constraint.fields.join("_"))
73    } else {
74        constraint.name.clone()
75    }
76}
77
78/// Shared constraint naming so generated SQL and snapshot diffs agree.
79pub fn constraint_name(table: &str, constraint: &Constraint, index: usize) -> String {
80    match constraint {
81        Constraint::Check(c) => check_constraint_name(table, c, index),
82        Constraint::Unique(u) => unique_constraint_name(table, u),
83    }
84}
85
86/// Normalized constraint definition stored in snapshots.
87pub fn constraint_definition(constraint: &Constraint) -> String {
88    match constraint {
89        Constraint::Check(c) => format!("CHECK ({})", c.sql),
90        Constraint::Unique(u) => {
91            let cols = u
92                .fields
93                .iter()
94                .map(|f| quote_ident(f))
95                .collect::<Vec<_>>()
96                .join(", ");
97            match &u.condition {
98                Some(cond) => format!("UNIQUE ({}) WHERE {}", cols, cond),
99                None => format!("UNIQUE ({})", cols),
100            }
101        }
102    }
103}
104
105impl From<CheckConstraint> for Constraint {
106    fn from(c: CheckConstraint) -> Self {
107        Constraint::Check(c)
108    }
109}
110
111impl From<UniqueConstraint> for Constraint {
112    fn from(c: UniqueConstraint) -> Self {
113        Constraint::Unique(c)
114    }
115}
116
117/// Sort direction.
118#[derive(Debug, Clone, PartialEq, Eq)]
119pub enum OrderDir {
120    Asc,
121    Desc,
122}
123
124/// An ordering clause: column + direction.
125#[derive(Debug, Clone)]
126pub struct OrderBy {
127    pub column: String,
128    pub dir: OrderDir,
129}
130
131/// Ascending order helper.
132pub fn asc(col: impl Into<String>) -> OrderBy {
133    OrderBy {
134        column: col.into(),
135        dir: OrderDir::Asc,
136    }
137}
138
139/// Descending order helper.
140pub fn desc(col: impl Into<String>) -> OrderBy {
141    OrderBy {
142        column: col.into(),
143        dir: OrderDir::Desc,
144    }
145}
146
147/// An index definition.
148#[derive(Debug, Clone)]
149pub struct IndexDef {
150    pub fields: Vec<String>,
151    pub name: String,
152    pub unique: bool,
153}