Skip to main content

toolu_orm_core/
column.rs

1//! Column types, definitions, and dialect-aware DDL generation.
2
3use serde::{Deserialize, Serialize};
4
5use crate::dialect::Dialect;
6
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8pub enum ColumnType {
9  // Core SQLite
10  Text,
11  Integer,
12  Real,
13  Blob,
14  // Turso STRICT types
15  Uuid,
16  Boolean,
17  Timestamp,
18  Date,
19  Time,
20  Json,
21  BigInt,
22  SmallInt,
23  Varchar(u32),
24  // Postgres-oriented types (usable on both dialects)
25  Serial,
26  BigSerial,
27  Jsonb,
28  Numeric,
29  Char(u32),
30  Array(Box<ColumnType>),
31}
32
33impl ColumnType {
34  /// SQL type name for Turso STRICT tables (extension types).
35  pub fn as_sql(&self) -> String {
36    match self {
37      Self::Text | Self::Jsonb | Self::Char(_) | Self::Array(_) => "TEXT".to_owned(),
38      Self::Integer | Self::Serial | Self::BigSerial => "INTEGER".to_owned(),
39      Self::Real | Self::Numeric => "REAL".to_owned(),
40      Self::Blob => "BLOB".to_owned(),
41      Self::Uuid => "uuid".to_owned(),
42      Self::Boolean => "boolean".to_owned(),
43      Self::Timestamp => "timestamp".to_owned(),
44      Self::Date => "date".to_owned(),
45      Self::Time => "time".to_owned(),
46      Self::Json => "json".to_owned(),
47      Self::BigInt => "bigint".to_owned(),
48      Self::SmallInt => "smallint".to_owned(),
49      Self::Varchar(n) => format!("varchar({n})"),
50    }
51  }
52
53  /// SQL type for non-STRICT tables (standard SQLite compatibility).
54  pub fn as_compat_sql(&self) -> String {
55    match self {
56      Self::Text
57      | Self::Uuid
58      | Self::Date
59      | Self::Time
60      | Self::Json
61      | Self::Jsonb
62      | Self::Varchar(_)
63      | Self::Char(_)
64      | Self::Array(_) => "TEXT".to_owned(),
65      Self::Integer
66      | Self::BigInt
67      | Self::SmallInt
68      | Self::Timestamp
69      | Self::Boolean
70      | Self::Serial
71      | Self::BigSerial => "INTEGER".to_owned(),
72      Self::Real | Self::Numeric => "REAL".to_owned(),
73      Self::Blob => "BLOB".to_owned(),
74    }
75  }
76
77  /// DDL type name for the given dialect. Used by the SQL generator (`sql.rs`)
78  /// to produce CREATE TABLE / ALTER TABLE statements.
79  ///
80  /// This is separate from [`Self::as_sql()`] which returns Turso STRICT type names
81  /// (e.g. `"uuid"`, `"boolean"`) for backward compatibility.
82  pub fn as_ddl_sql(&self, dialect: Dialect) -> String {
83    match dialect {
84      Dialect::Sqlite => match self {
85        Self::Text
86        | Self::Uuid
87        | Self::Date
88        | Self::Time
89        | Self::Json
90        | Self::Jsonb
91        | Self::Varchar(_)
92        | Self::Char(_)
93        | Self::Array(_) => "TEXT".to_owned(),
94        Self::Integer
95        | Self::Boolean
96        | Self::Timestamp
97        | Self::BigInt
98        | Self::SmallInt
99        | Self::Serial
100        | Self::BigSerial => "INTEGER".to_owned(),
101        Self::Real | Self::Numeric => "REAL".to_owned(),
102        Self::Blob => "BLOB".to_owned(),
103      },
104      Dialect::Postgres => match self {
105        Self::Text => "TEXT".to_owned(),
106        Self::Integer => "INTEGER".to_owned(),
107        Self::Real => "DOUBLE PRECISION".to_owned(),
108        Self::Blob => "BYTEA".to_owned(),
109        Self::Uuid => "UUID".to_owned(),
110        Self::Boolean => "BOOLEAN".to_owned(),
111        Self::Timestamp => "TIMESTAMPTZ".to_owned(),
112        Self::Date => "DATE".to_owned(),
113        Self::Time => "TIME".to_owned(),
114        Self::Json => "JSON".to_owned(),
115        Self::Jsonb => "JSONB".to_owned(),
116        Self::BigInt => "BIGINT".to_owned(),
117        Self::SmallInt => "SMALLINT".to_owned(),
118        Self::Serial => "SERIAL".to_owned(),
119        Self::BigSerial => "BIGSERIAL".to_owned(),
120        Self::Numeric => "NUMERIC".to_owned(),
121        Self::Varchar(n) => format!("VARCHAR({n})"),
122        Self::Char(n) => format!("CHAR({n})"),
123        Self::Array(inner) => format!("{}[]", inner.as_ddl_sql(Dialect::Postgres)),
124      },
125    }
126  }
127}
128
129#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
130pub enum ForeignKeyAction {
131  Cascade,
132  SetNull,
133  SetDefault,
134  Restrict,
135  NoAction,
136}
137
138impl ForeignKeyAction {
139  pub fn as_sql(self) -> &'static str {
140    match self {
141      Self::Cascade => "CASCADE",
142      Self::SetNull => "SET NULL",
143      Self::SetDefault => "SET DEFAULT",
144      Self::Restrict => "RESTRICT",
145      Self::NoAction => "NO ACTION",
146    }
147  }
148}
149
150/// Trait for Rust enums that map to TEXT columns with CHECK constraints.
151pub trait EnumSchema {
152  fn variants() -> &'static [&'static str];
153}
154
155#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
156pub struct ColumnDef {
157  pub name: String,
158  pub column_type: ColumnType,
159  pub primary_key: bool,
160  pub not_null: bool,
161  #[serde(default, skip_serializing_if = "Option::is_none")]
162  pub default: Option<String>,
163  pub unique: bool,
164  #[serde(default, skip_serializing_if = "Option::is_none")]
165  pub references: Option<String>,
166  #[serde(default, skip_serializing_if = "Option::is_none")]
167  pub on_delete: Option<ForeignKeyAction>,
168  #[serde(default, skip_serializing_if = "Option::is_none")]
169  pub on_update: Option<ForeignKeyAction>,
170  #[serde(default, skip_serializing_if = "Option::is_none")]
171  pub check: Option<String>,
172  /// FTS5 `UNINDEXED`: the column is stored but not searchable. Ignored on
173  /// ordinary tables, and defaulted so older snapshots still deserialize.
174  #[serde(default, skip_serializing_if = "std::ops::Not::not")]
175  pub unindexed: bool,
176}
177
178// Marker types for schema definition — used by the #[table] proc macro.
179pub struct Text;
180pub struct Integer;
181pub struct Real;
182pub struct Blob;
183pub struct Uuid;
184pub struct Boolean;
185pub struct Timestamp;
186pub struct Date;
187pub struct Time;
188pub struct Json;
189pub struct BigInt;
190pub struct SmallInt;
191pub struct Varchar<const N: u32>;
192pub struct Serial;
193pub struct BigSerial;
194pub struct Jsonb;
195pub struct Numeric;
196pub struct Char<const N: u32>;