1use std::{collections::BTreeMap, marker::PhantomData};
6
7use sea_query::{IndexCreateStatement, TableCreateStatement};
8
9use crate::value::{EqTyp, MyTyp};
10
11#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
12pub enum ColumnType {
13 Integer = 0,
14 Float = 1,
15 String = 2,
16 Blob = 3,
17}
18
19impl ColumnType {
20 pub fn sea_type(&self) -> sea_query::ColumnType {
21 use sea_query::ColumnType as T;
22 match self {
23 ColumnType::Integer => T::Integer,
24 ColumnType::Float => T::custom("REAL"),
25 ColumnType::String => T::Text,
26 ColumnType::Blob => T::Blob,
27 }
28 }
29}
30
31#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
32pub struct Column {
33 pub typ: ColumnType,
34 pub nullable: bool,
35 pub fk: Option<(String, String)>,
36}
37
38#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
39pub struct Index {
40 pub columns: Vec<String>,
42 pub unique: bool,
43}
44
45impl Index {
46 fn normalize(&mut self) -> bool {
47 self.columns.sort();
49 self.unique
51 }
52}
53
54#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
55pub struct Table {
56 pub columns: BTreeMap<String, Column>,
57 pub indices: Vec<Index>,
58}
59
60impl Table {
61 pub(crate) fn new<T: crate::Table>() -> Self {
62 let mut f = crate::hash::TypBuilder::default();
63 T::typs(&mut f);
64 f.ast
65 }
66
67 fn normalize(&mut self) {
68 self.indices.retain_mut(Index::normalize);
69 self.indices.sort();
70 }
71}
72
73impl Table {
74 pub fn create(&self, extra_indices: &mut Vec<IndexCreateStatement>) -> TableCreateStatement {
75 use sea_query::*;
76 let mut create = Table::create();
77 for (name, col) in &self.columns {
78 let name = Alias::new(name);
79 let mut def = ColumnDef::new_with_type(name.clone(), col.typ.sea_type());
80 if col.nullable {
81 def.null();
82 } else {
83 def.not_null();
84 }
85 create.col(&mut def);
86 if let Some((table, fk)) = &col.fk {
87 create.foreign_key(
88 ForeignKey::create()
89 .to(Alias::new(table), Alias::new(fk))
90 .from_col(name),
91 );
92 }
93 }
94 for index_spec in &*self.indices {
95 let mut index = sea_query::Index::create();
96 if index_spec.unique {
97 index.unique();
98 }
99 for col in &index_spec.columns {
102 index.col(Alias::new(col));
103 }
104
105 if index.is_unique_key() {
106 create.index(&mut index);
108 } else {
109 extra_indices.push(index);
110 }
111 }
112 create
113 }
114}
115
116#[derive(Debug, Hash, Default, PartialEq, Eq)]
117pub struct Schema {
118 pub tables: BTreeMap<String, Table>,
119}
120
121impl Schema {
122 pub(crate) fn new<S: crate::migrate::Schema>() -> Self {
123 let mut b = crate::migrate::TableTypBuilder::default();
124 S::typs(&mut b);
125 b.ast
126 }
127
128 pub(crate) fn normalize(mut self) -> Self {
129 self.tables.values_mut().for_each(Table::normalize);
130 self
131 }
132}
133
134#[cfg(feature = "dev")]
135pub mod dev {
136 use std::{
137 hash::{Hash, Hasher},
138 io::{Read, Write},
139 };
140
141 use k12::{
142 KangarooTwelve, KangarooTwelveCore,
143 digest::{ExtendableOutput, core_api::CoreWrapper},
144 };
145
146 pub struct KangarooHasher {
147 inner: CoreWrapper<KangarooTwelveCore<'static>>,
148 }
149
150 impl Default for KangarooHasher {
151 fn default() -> Self {
152 let core = KangarooTwelveCore::new(&[]);
153 let hasher = KangarooTwelve::from_core(core);
154 Self { inner: hasher }
155 }
156 }
157
158 impl Hasher for KangarooHasher {
159 fn finish(&self) -> u64 {
160 let mut xof = self.inner.clone().finalize_xof();
161 let mut buf = [0; 8];
162 xof.read_exact(&mut buf).unwrap();
163 u64::from_le_bytes(buf)
164 }
165
166 fn write(&mut self, bytes: &[u8]) {
167 self.inner.write_all(bytes).unwrap();
168 }
169 }
170
171 pub fn hash_schema<S: crate::migrate::Schema>() -> String {
175 let mut hasher = KangarooHasher::default();
176 super::Schema::new::<S>().normalize().hash(&mut hasher);
177 format!("{:x}", hasher.finish())
178 }
179}
180
181pub struct TypBuilder<S> {
182 pub(crate) ast: Table,
183 _p: PhantomData<S>,
184}
185
186impl<S> Default for TypBuilder<S> {
187 fn default() -> Self {
188 Self {
189 ast: Default::default(),
190 _p: Default::default(),
191 }
192 }
193}
194
195impl<S> TypBuilder<S> {
196 pub fn col<T: SchemaType<S>>(&mut self, name: &'static str) {
197 let mut item = Column {
198 typ: T::TYP,
199 nullable: T::NULLABLE,
200 fk: None,
201 };
202 if let Some((table, fk)) = T::FK {
203 item.fk = Some((table.to_owned(), fk.to_owned()))
204 }
205 let old = self.ast.columns.insert(name.to_owned(), item);
206 debug_assert!(old.is_none());
207 }
208
209 pub fn index(&mut self, cols: &[&'static str], unique: bool) {
210 let mut index = Index {
211 columns: Vec::default(),
212 unique,
213 };
214 for &col in cols {
215 index.columns.push(col.to_owned());
216 }
217 self.ast.indices.push(index);
218 }
219
220 pub fn check_unique_compatible<T: EqTyp>(&mut self) {}
221}
222
223struct Null;
224struct NotNull;
225
226#[diagnostic::on_unimplemented(
229 message = "Can not use `{Self}` as a column type in schema `{S}`",
230 note = "Table names can be used as schema column types as long as they are not #[no_reference]"
231)]
232trait SchemaType<S>: MyTyp {
233 type N;
234}
235
236impl<S> SchemaType<S> for String {
237 type N = NotNull;
238}
239impl<S> SchemaType<S> for Vec<u8> {
240 type N = NotNull;
241}
242impl<S> SchemaType<S> for i64 {
243 type N = NotNull;
244}
245impl<S> SchemaType<S> for f64 {
246 type N = NotNull;
247}
248impl<S, T: SchemaType<S, N = NotNull>> SchemaType<S> for Option<T> {
249 type N = Null;
250}
251#[diagnostic::do_not_recommend]
253impl<T: crate::Table<Referer = ()>> SchemaType<T::Schema> for T {
254 type N = NotNull;
255}