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, 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, 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 create
118 }
119
120 pub fn create_indices(&self, table_name: &str) -> impl Iterator<Item = String> {
121 let index_table_ref = Alias::new(table_name);
122 self.indices
123 .iter()
124 .enumerate()
125 .map(move |(index_num, index)| {
126 index
127 .create()
128 .table(index_table_ref.clone())
129 .name(format!("{table_name}_index_{index_num}"))
130 .to_string(SqliteQueryBuilder)
131 })
132 }
133}
134
135impl Index {
136 pub fn create(&self) -> IndexCreateStatement {
137 let mut index = sea_query::Index::create();
138 if self.def.unique {
139 index.unique();
140 }
141 for col in &self.def.columns {
144 index.col(Alias::new(col));
145 }
146 index
147 }
148}
149
150#[cfg(feature = "dev")]
151pub mod dev {
152 use std::{
153 hash::{Hash, Hasher},
154 io::{Read, Write},
155 };
156
157 use k12::{
158 KangarooTwelve, KangarooTwelveCore,
159 digest::{ExtendableOutput, core_api::CoreWrapper},
160 };
161
162 pub struct KangarooHasher {
163 inner: CoreWrapper<KangarooTwelveCore<'static>>,
164 }
165
166 impl Default for KangarooHasher {
167 fn default() -> Self {
168 let core = KangarooTwelveCore::new(&[]);
169 let hasher = KangarooTwelve::from_core(core);
170 Self { inner: hasher }
171 }
172 }
173
174 impl Hasher for KangarooHasher {
175 fn finish(&self) -> u64 {
176 let mut xof = self.inner.clone().finalize_xof();
177 let mut buf = [0; 8];
178 xof.read_exact(&mut buf).unwrap();
179 u64::from_le_bytes(buf)
180 }
181
182 fn write(&mut self, bytes: &[u8]) {
183 self.inner.write_all(bytes).unwrap();
184 }
185 }
186
187 pub fn hash_schema<S: crate::migrate::Schema>() -> String {
191 let mut hasher = KangarooHasher::default();
192 super::Schema::new::<S>().normalize().hash(&mut hasher);
193 format!("{:x}", hasher.finish())
194 }
195}