1use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
12pub struct Schema {
13 pub tables: Vec<TableDef>,
14 pub enums: Vec<EnumDef>,
15}
16
17impl Schema {
18 pub fn table(&self, name: &str) -> Option<&TableDef> {
19 self.tables.iter().find(|t| t.name == name)
20 }
21}
22
23#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
25pub struct EnumDef {
26 pub rust_name: String,
28 pub values: Vec<String>,
30}
31
32#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
33pub struct TableDef {
34 pub name: String,
36 pub struct_name: String,
38 pub columns: Vec<ColumnDef>,
39 #[serde(default, skip_serializing_if = "Vec::is_empty")]
44 pub relations: Vec<RelationDef>,
45}
46
47impl TableDef {
48 pub fn column(&self, name: &str) -> Option<&ColumnDef> {
49 self.columns.iter().find(|c| c.name == name)
50 }
51
52 pub fn relation(&self, field: &str) -> Option<&RelationDef> {
53 self.relations.iter().find(|r| r.field == field)
54 }
55
56 pub fn primary_key(&self) -> Vec<&ColumnDef> {
57 self.columns.iter().filter(|c| c.primary_key).collect()
58 }
59
60 pub fn auto_id(&self) -> bool {
63 let pk = self.primary_key();
64 pk.len() == 1 && pk[0].name == "id" && pk[0].ty == SqlType::Integer
65 }
66}
67
68#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
73pub struct RelationDef {
74 pub field: String,
76 pub target_struct: String,
78 pub target_table: String,
81 pub local_column: String,
83 pub nullable: bool,
85}
86
87#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
88pub struct ColumnDef {
89 pub name: String,
90 pub rust_type: String,
92 pub ty: SqlType,
93 pub nullable: bool,
94 pub primary_key: bool,
95 pub unique: bool,
96 pub json: bool,
98 pub is_enum: bool,
102 #[serde(default)]
104 pub index: bool,
105 pub default: Option<DefaultValue>,
106 pub references: Option<ForeignKey>,
107 pub check_in: Option<Vec<String>>,
109 #[serde(default, skip_serializing_if = "Option::is_none")]
112 pub renamed_from: Option<String>,
113}
114
115impl ColumnDef {
116 pub fn signature(&self) -> ColumnDef {
120 ColumnDef {
121 renamed_from: None,
122 index: false,
123 ..self.clone()
124 }
125 }
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
129pub enum SqlType {
130 Integer,
131 Real,
132 Text,
133 Blob,
134 Boolean,
135 Timestamp,
136}
137
138impl SqlType {
139 pub fn sql(self) -> &'static str {
140 match self {
141 SqlType::Integer => "INTEGER",
142 SqlType::Real => "REAL",
143 SqlType::Text => "TEXT",
144 SqlType::Blob => "BLOB",
145 SqlType::Boolean => "BOOLEAN",
146 SqlType::Timestamp => "TIMESTAMP",
147 }
148 }
149}
150
151#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
152pub enum DefaultValue {
153 Now,
155 Int(i64),
156 Float(f64),
157 Text(String),
158 Bool(bool),
159}
160
161impl DefaultValue {
162 pub fn sql(&self) -> String {
163 match self {
164 DefaultValue::Now => "CURRENT_TIMESTAMP".into(),
165 DefaultValue::Int(i) => i.to_string(),
166 DefaultValue::Float(f) => f.to_string(),
167 DefaultValue::Text(s) => format!("'{}'", s.replace('\'', "''")),
168 DefaultValue::Bool(b) => if *b { "1" } else { "0" }.into(),
169 }
170 }
171}
172
173#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
174pub struct ForeignKey {
175 pub table: String,
179 pub column: String,
180 pub on_delete: Option<OnDelete>,
181}
182
183#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
184pub enum OnDelete {
185 Cascade,
186 SetNull,
187 Restrict,
188}
189
190impl OnDelete {
191 pub fn sql(self) -> &'static str {
192 match self {
193 OnDelete::Cascade => "CASCADE",
194 OnDelete::SetNull => "SET NULL",
195 OnDelete::Restrict => "RESTRICT",
196 }
197 }
198}