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