1pub mod canonical;
6mod check_constraint;
7mod diff;
8pub mod from_db;
9pub mod from_macro;
10pub mod read;
11#[cfg(test)]
12mod test;
13
14use sea_query::{Alias, IndexCreateStatement, IntoIden, SqliteQueryBuilder, TableCreateStatement};
15
16use crate::schema::{
17 canonical::ColumnType,
18 from_macro::{Index, Schema, Table},
19};
20
21impl ColumnType {
22 pub fn sea_type(&self) -> sea_query::ColumnType {
23 use sea_query::ColumnType as T;
24 match self {
25 ColumnType::Integer => T::Integer,
26 ColumnType::Real => T::custom("REAL"),
27 ColumnType::Text => T::Text,
28 ColumnType::Blob => T::Blob,
29 ColumnType::Any => T::custom("ANY"),
30 }
31 }
32}
33
34mod normalize {
35 use crate::schema::{
36 canonical, from_db,
37 from_macro::{Schema, Table},
38 };
39
40 impl from_db::Index {
41 pub fn normalize(self) -> Option<canonical::Unique> {
42 self.unique.then_some(canonical::Unique {
43 columns: self.columns.into_iter().collect(),
44 })
45 }
46 }
47
48 impl Table {
49 fn normalize(self) -> canonical::Table {
50 canonical::Table {
51 columns: self.columns.into_iter().map(|(k, v)| (k, v.def)).collect(),
52 indices: self
53 .indices
54 .into_iter()
55 .filter_map(|idx| idx.def.normalize())
56 .collect(),
57 }
58 }
59 }
60
61 impl Schema {
62 pub(crate) fn normalize(self) -> canonical::Schema {
63 canonical::Schema {
64 tables: self
65 .tables
66 .into_iter()
67 .map(|(k, v)| (k.to_owned(), v.normalize()))
68 .collect(),
69 }
70 }
71 }
72}
73
74impl Table {
75 pub(crate) fn new<T: crate::Table>() -> Self {
76 let mut f = crate::schema::from_macro::TypBuilder::default();
77 T::typs(&mut f);
78 f.ast.span = T::SPAN;
79 f.ast
80 }
81}
82
83impl Schema {
84 pub(crate) fn new<S: crate::migrate::Schema>() -> Self {
85 let mut b = crate::migrate::TableTypBuilder::default();
86 S::typs(&mut b);
87 b.ast.span = S::SPAN;
88 b.ast
89 }
90}
91
92impl Table {
93 pub fn create(&self) -> TableCreateStatement {
94 use sea_query::*;
95 let mut create = Table::create();
96 for (name, col) in &self.columns {
97 let col = &col.def;
98 let name = Alias::new(name);
99 let mut def = ColumnDef::new_with_type(name.clone(), col.typ.sea_type());
100 if col.nullable {
101 def.null();
102 } else {
103 def.not_null();
104 }
105 if let Some(check) = &col.check {
106 def.check(sea_query::Expr::cust(check.clone()));
107 }
108 create.col(&mut def);
109 if let Some((table, fk)) = &col.fk {
110 create.foreign_key(
111 ForeignKey::create()
112 .to(Alias::new(table), Alias::new(fk))
113 .from_col(name),
114 );
115 }
116 }
117 for index in &self.indices {
118 let mut index = index.create();
119 if index.is_unique_key() {
123 create.index(&mut index);
124 }
125 }
126 create
127 }
128
129 pub fn delayed_indices(&self, table_name: &'static str) -> impl Iterator<Item = String> {
134 let table_name = table_name.into_iden();
135 self.indices
136 .iter()
137 .map(|x| x.create())
138 .filter(|x| !x.is_unique_key())
139 .enumerate()
140 .map(move |(index_num, mut index)| {
141 index
142 .table(table_name.clone())
143 .name(format!("{table_name}_index_{index_num}"))
144 .to_string(SqliteQueryBuilder)
145 })
146 }
147}
148
149impl Index {
150 pub fn create(&self) -> IndexCreateStatement {
151 let mut index = sea_query::Index::create();
152 if self.def.unique {
153 index.unique();
154 }
155 for col in &self.def.columns {
158 index.col(Alias::new(col));
159 }
160 index
161 }
162}
163
164#[cfg(feature = "dev")]
165pub mod dev {
166 use std::{
167 hash::{Hash, Hasher},
168 io::{Read, Write},
169 };
170
171 use k12::{
172 KangarooTwelve, KangarooTwelveCore,
173 digest::{ExtendableOutput, core_api::CoreWrapper},
174 };
175
176 pub struct KangarooHasher {
177 inner: CoreWrapper<KangarooTwelveCore<'static>>,
178 }
179
180 impl Default for KangarooHasher {
181 fn default() -> Self {
182 let core = KangarooTwelveCore::new(&[]);
183 let hasher = KangarooTwelve::from_core(core);
184 Self { inner: hasher }
185 }
186 }
187
188 impl Hasher for KangarooHasher {
189 fn finish(&self) -> u64 {
190 let mut xof = self.inner.clone().finalize_xof();
191 let mut buf = [0; 8];
192 xof.read_exact(&mut buf).unwrap();
193 u64::from_le_bytes(buf)
194 }
195
196 fn write(&mut self, bytes: &[u8]) {
197 self.inner.write_all(bytes).unwrap();
198 }
199 }
200
201 pub fn hash_schema<S: crate::migrate::Schema>() -> String {
205 let mut hasher = KangarooHasher::default();
206 super::Schema::new::<S>().normalize().hash(&mut hasher);
207 format!("{:x}", hasher.finish())
208 }
209}