immigrant_schema/
lib.rs

1use std::ops::Deref;
2
3use derivative::Derivative;
4use ids::DbIdent;
5
6use self::{
7	attribute::AttributeList, column::Column, composite::Composite, diagnostics::Report, ids::Ident, index::{Check, Index, PrimaryKey, UniqueConstraint}, names::{ItemKind, TypeIdent}, root::Schema, scalar::{Enum, Scalar, ScalarAnnotation}, sql::Sql, table::{ForeignKey, Table, TableAnnotation}, uid::RenameMap, view::View
8};
9
10pub mod column;
11pub mod composite;
12pub mod index;
13pub mod mixin;
14pub mod process;
15pub mod root;
16pub mod scalar;
17pub mod sql;
18pub mod table;
19pub mod trigger;
20pub mod view;
21
22pub mod ids;
23pub mod names;
24
25pub mod parser;
26
27pub mod attribute;
28
29mod changelist;
30pub mod renamelist;
31
32pub use changelist::{mk_change_list, ChangeList, IsCompatible, IsIsomorph};
33
34mod span;
35pub mod uid;
36pub use uid::HasUid;
37
38pub mod diagnostics;
39
40mod util;
41
42#[macro_export]
43macro_rules! w {
44    ($out:expr_2021, $($tt:tt)*) => {{
45        use std::fmt::Write;
46        write!($out, $($tt)*).unwrap();
47    }};
48}
49#[macro_export]
50macro_rules! wl {
51    ($out:ident, $($tt:tt)*) => {{
52        use std::fmt::Write;
53			writeln!($out, $($tt)*).unwrap();
54    }};
55    ($out:ident) => {{
56        use std::fmt::Write;
57			writeln!($out).unwrap();
58    }};
59}
60#[macro_export]
61macro_rules! newty_enum {
62    (
63        $(#[$($attr:tt)+])*
64        $vis:vis enum $name:ident {$($Variant:ident = $accessor:ident),* $(,)+}
65    ) => {
66        $(#[$($attr)+])*
67        #[derive(derivative::Derivative)]
68        #[derivative(Debug)]
69        $vis enum $name {
70            $(
71                #[derivative(Debug = "transparent")]
72                $Variant($Variant),
73            )*
74        }
75        paste::paste!{impl $name {$(
76            $vis fn [<is_ $accessor>](&self) -> bool {
77                matches!(self, Self::$Variant(_))
78            }
79            $vis fn [<as_ $accessor>](&self) -> Option<&$Variant> {
80                match self {
81                    Self::$Variant(value) => Some(value),
82                    _ => None,
83                }
84            }
85            $vis fn [<as_ $accessor _mut>](&mut self) -> Option<&mut $Variant> {
86                match self {
87                    Self::$Variant(value) => Some(value),
88                    _ => None,
89                }
90            }
91        )*}}
92    };
93}
94
95#[derive(Derivative)]
96#[derivative(Debug)]
97pub struct TableItem<'a, I> {
98	#[derivative(Debug = "ignore")]
99	pub table: SchemaTable<'a>,
100	pub value: &'a I,
101}
102impl<'a, I> TableItem<'a, I> {
103	pub fn unchecked_new(table: SchemaTable<'a>, value: &'a I) -> Self {
104		Self { table, value }
105	}
106}
107impl<I> Deref for TableItem<'_, I> {
108	type Target = I;
109	fn deref(&self) -> &Self::Target {
110		self.value
111	}
112}
113impl<I> Clone for TableItem<'_, I> {
114	fn clone(&self) -> Self {
115		*self
116	}
117}
118impl<I> Copy for TableItem<'_, I> {}
119impl<T> IsCompatible for TableItem<'_, T>
120where
121	T: IsCompatible,
122{
123	fn is_compatible(&self, new: &Self, rn: &RenameMap, report_self: &mut Report, report_new: &mut Report) -> bool {
124		self.value.is_compatible(new.value, rn, report_self, report_new)
125	}
126}
127impl<T> IsIsomorph for TableItem<'_, T>
128where
129	T: IsIsomorph,
130{
131	fn is_isomorph(&self, other: &Self, rn: &RenameMap, report_self: &mut Report, report_other: &mut Report) -> bool {
132		self.value.is_isomorph(other.value, rn, report_self, report_other)
133	}
134}
135impl<T> HasUid for TableItem<'_, T>
136where
137	T: HasUid,
138{
139	fn uid(&self) -> uid::Uid {
140		self.value.uid()
141	}
142}
143impl<T> HasDefaultDbName for TableItem<'_, T>
144where
145	T: HasDefaultDbName,
146{
147	type Kind = T::Kind;
148
149	fn default_db(&self) -> Option<DbIdent<Self::Kind>> {
150		self.value.default_db()
151	}
152}
153impl<T> HasIdent for TableItem<'_, T>
154where
155	T: HasIdent,
156{
157	type Kind = T::Kind;
158
159	fn id(&self) -> Ident<Self::Kind> {
160		self.value.id()
161	}
162}
163
164pub type TableIndex<'a> = TableItem<'a, Index>;
165pub type TableColumn<'a> = TableItem<'a, Column>;
166pub type TableForeignKey<'a> = TableItem<'a, ForeignKey>;
167pub type TableSql<'a> = TableItem<'a, Sql>;
168pub type TablePrimaryKey<'a> = TableItem<'a, PrimaryKey>;
169pub type TableCheck<'a> = TableItem<'a, Check>;
170pub type TableUniqueConstraint<'a> = TableItem<'a, UniqueConstraint>;
171
172pub struct SchemaSql<'a> {
173	pub schema: &'a Schema,
174	pub sql: &'a Sql,
175}
176impl Deref for SchemaSql<'_> {
177	type Target = Sql;
178	fn deref(&self) -> &Self::Target {
179		self.sql
180	}
181}
182
183#[derive(Debug, PartialEq, Clone, Copy)]
184pub struct Diff<I> {
185	pub old: I,
186	pub new: I,
187}
188pub type SchemaDiff<'a> = Diff<&'a Schema>;
189pub type TableDiff<'a> = Diff<SchemaTable<'a>>;
190pub type EnumDiff<'a> = Diff<SchemaEnum<'a>>;
191pub type ColumnDiff<'a> = Diff<TableColumn<'a>>;
192
193#[derive(Clone, Copy, Derivative)]
194#[derivative(Debug)]
195pub struct SchemaEnum<'a> {
196	#[derivative(Debug = "ignore")]
197	pub schema: &'a Schema,
198	pub en: &'a Enum,
199}
200
201impl Deref for SchemaEnum<'_> {
202	type Target = Enum;
203
204	fn deref(&self) -> &Self::Target {
205		self.en
206	}
207}
208
209#[derive(Clone, Copy, Debug)]
210pub enum SchemaItem<'a> {
211	Table(SchemaTable<'a>),
212	Enum(SchemaEnum<'a>),
213	Scalar(SchemaScalar<'a>),
214	Composite(SchemaComposite<'a>),
215	View(SchemaView<'a>),
216}
217derive_is_isomorph_by_id_name!(SchemaItem<'_>);
218impl SchemaItem<'_> {
219	pub fn as_enum(&self) -> Option<SchemaEnum> {
220		match self {
221			Self::Enum(e) => Some(*e),
222			_ => None,
223		}
224	}
225	pub fn as_scalar(&self) -> Option<SchemaScalar> {
226		match self {
227			Self::Scalar(e) => Some(*e),
228			_ => None,
229		}
230	}
231	pub fn as_composite(&self) -> Option<SchemaComposite> {
232		match self {
233			Self::Composite(e) => Some(*e),
234			_ => None,
235		}
236	}
237	pub fn as_table(&self) -> Option<SchemaTable> {
238		match self {
239			Self::Table(e) => Some(*e),
240			_ => None,
241		}
242	}
243	pub fn as_type(&self) -> Option<SchemaType> {
244		Some(match self {
245			SchemaItem::Enum(e) => SchemaType::Enum(*e),
246			SchemaItem::Scalar(s) => SchemaType::Scalar(*s),
247			SchemaItem::Composite(c) => SchemaType::Composite(*c),
248			_ => return None,
249		})
250	}
251	pub fn as_view(&self) -> Option<SchemaView> {
252		match self {
253			Self::View(e) => Some(*e),
254			_ => None,
255		}
256	}
257	pub fn schema(&self) -> &Schema {
258		match self {
259			SchemaItem::Table(t) => t.schema,
260			SchemaItem::Enum(e) => e.schema,
261			SchemaItem::Scalar(s) => s.schema,
262			SchemaItem::Composite(c) => c.schema,
263			SchemaItem::View(v) => v.schema,
264		}
265	}
266	pub fn is_external(&self) -> bool {
267		match self {
268			SchemaItem::Table(t) => t.is_external(),
269			SchemaItem::Enum(_) => false,
270			SchemaItem::Scalar(s) => s.is_external(),
271			SchemaItem::Composite(_) => false,
272			SchemaItem::View(_) => false,
273		}
274	}
275}
276impl HasUid for SchemaItem<'_> {
277	fn uid(&self) -> uid::Uid {
278		match self {
279			SchemaItem::Table(t) => t.uid(),
280			SchemaItem::Enum(e) => e.uid(),
281			SchemaItem::Scalar(s) => s.uid(),
282			SchemaItem::Composite(s) => s.uid(),
283			SchemaItem::View(v) => v.uid(),
284		}
285	}
286}
287impl HasIdent for SchemaItem<'_> {
288	type Kind = ItemKind;
289
290	fn id(&self) -> Ident<Self::Kind> {
291		match self {
292			SchemaItem::Table(t) => Ident::unchecked_cast(t.id()),
293			SchemaItem::Enum(e) => Ident::unchecked_cast(e.id()),
294			SchemaItem::Scalar(s) => Ident::unchecked_cast(s.id()),
295			SchemaItem::Composite(s) => Ident::unchecked_cast(s.id()),
296			SchemaItem::View(v) => Ident::unchecked_cast(v.id()),
297		}
298	}
299}
300impl HasDefaultDbName for SchemaItem<'_> {
301	type Kind = ItemKind;
302
303	fn default_db(&self) -> Option<DbIdent<Self::Kind>> {
304		match self {
305			SchemaItem::Table(t) => t.default_db().map(DbIdent::unchecked_from),
306			SchemaItem::Enum(e) => e.default_db().map(DbIdent::unchecked_from),
307			SchemaItem::Scalar(s) => s.default_db().map(DbIdent::unchecked_from),
308			SchemaItem::Composite(s) => s.default_db().map(DbIdent::unchecked_from),
309			SchemaItem::View(v) => v.default_db().map(DbIdent::unchecked_from),
310		}
311	}
312}
313
314#[derive(Clone, Copy, Derivative)]
315#[derivative(Debug)]
316pub struct SchemaScalar<'a> {
317	#[derivative(Debug = "ignore")]
318	pub schema: &'a Schema,
319	pub scalar: &'a Scalar,
320}
321impl Deref for SchemaScalar<'_> {
322	type Target = Scalar;
323
324	fn deref(&self) -> &Self::Target {
325		self.scalar
326	}
327}
328
329#[derive(Clone, Copy, Derivative)]
330#[derivative(Debug)]
331pub struct SchemaComposite<'a> {
332	#[derivative(Debug = "ignore")]
333	pub schema: &'a Schema,
334	pub composite: &'a Composite,
335}
336impl Deref for SchemaComposite<'_> {
337	type Target = Composite;
338
339	fn deref(&self) -> &Self::Target {
340		self.composite
341	}
342}
343
344#[derive(Clone, Copy, Debug)]
345pub enum SchemaType<'a> {
346	Enum(SchemaEnum<'a>),
347	Scalar(SchemaScalar<'a>),
348	Composite(SchemaComposite<'a>),
349}
350impl<'a> SchemaType<'a> {
351	pub fn ident(&self) -> TypeIdent {
352		match self {
353			SchemaType::Scalar(s) => s.id(),
354			SchemaType::Enum(e) => e.id(),
355			SchemaType::Composite(e) => e.id(),
356		}
357	}
358	pub fn has_default(&self) -> bool {
359		match self {
360			SchemaType::Scalar(s) => s
361				.annotations
362				.iter()
363				.any(|a| matches!(a, ScalarAnnotation::Default(_))),
364			SchemaType::Enum(_) => false,
365			SchemaType::Composite(_) => false,
366		}
367	}
368	pub fn attrlist(&self) -> &AttributeList {
369		match self {
370			SchemaType::Scalar(s) => &s.attrlist,
371			SchemaType::Enum(e) => &e.attrlist,
372			SchemaType::Composite(c) => &c.attrlist,
373		}
374	}
375	pub fn depend_on(&self, out: &mut Vec<TypeIdent>) {
376		match self {
377			SchemaType::Enum(e) => out.push(e.id()),
378			SchemaType::Scalar(s) => s.depend_on(out),
379			SchemaType::Composite(c) => {
380				// Composites can be inlined... How that should be handled?
381				out.push(c.id());
382			}
383		}
384	}
385	pub fn type_dependencies(&self, out: &mut Vec<TypeIdent>) {
386		match self {
387			SchemaType::Enum(_e) => {
388				// Enums are just strings
389			}
390			SchemaType::Scalar(s) => {
391				// Scalars have inline sql, and we need to recurse in case if scalar is inlined
392				s.type_dependencies(out);
393			}
394			SchemaType::Composite(c) => {
395				// Composites depend on types of their fields (composites/scalars)
396				for f in c.fields() {
397					f.ty().depend_on(out);
398				}
399			}
400		}
401	}
402	pub fn as_schema_item(self) -> SchemaItem<'a> {
403		match self {
404			SchemaType::Enum(schema_enum) => SchemaItem::Enum(schema_enum),
405			SchemaType::Scalar(schema_scalar) => SchemaItem::Scalar(schema_scalar),
406			SchemaType::Composite(schema_composite) => SchemaItem::Composite(schema_composite),
407		}
408	}
409}
410
411#[derive(Clone, Copy, Derivative)]
412#[derivative(Debug)]
413pub struct SchemaTable<'a> {
414	#[derivative(Debug = "ignore")]
415	pub schema: &'a Schema,
416	pub table: &'a Table,
417}
418
419impl SchemaTable<'_> {
420	pub fn sql<'a>(&'a self, sql: &'a Sql) -> TableSql<'a> {
421		TableSql {
422			table: *self,
423			value: sql,
424		}
425	}
426	pub fn primary_key(&self) -> Option<TablePrimaryKey<'_>> {
427		let pk = self.table.primary_key()?;
428		Some(TablePrimaryKey {
429			table: *self,
430			value: pk,
431		})
432	}
433	pub fn checks(&self) -> impl Iterator<Item = TableCheck> {
434		self.annotations
435			.iter()
436			.filter_map(TableAnnotation::as_check)
437			.map(|value| TableCheck {
438				table: *self,
439				value,
440			})
441	}
442	pub fn unique_constraints(&self) -> impl Iterator<Item = TableUniqueConstraint> {
443		self.annotations
444			.iter()
445			.filter_map(TableAnnotation::as_unique_constraint)
446			.map(|value| TableUniqueConstraint {
447				table: *self,
448				value,
449			})
450	}
451}
452impl Deref for SchemaTable<'_> {
453	type Target = Table;
454
455	fn deref(&self) -> &Self::Target {
456		self.table
457	}
458}
459
460#[derive(Clone, Copy, Derivative)]
461#[derivative(Debug)]
462pub struct SchemaView<'a> {
463	#[derivative(Debug = "ignore")]
464	pub schema: &'a Schema,
465	pub view: &'a View,
466}
467impl Deref for SchemaView<'_> {
468	type Target = View;
469
470	fn deref(&self) -> &Self::Target {
471		self.view
472	}
473}
474
475#[derive(Clone, Copy, Derivative)]
476#[derivative(Debug)]
477pub enum SchemaTableOrView<'a> {
478	Table(SchemaTable<'a>),
479	View(SchemaView<'a>),
480}
481
482pub trait HasIdent {
483	type Kind;
484	fn id(&self) -> Ident<Self::Kind>;
485}
486impl<T> HasIdent for &T
487where
488	T: HasIdent,
489{
490	type Kind = T::Kind;
491
492	fn id(&self) -> Ident<Self::Kind> {
493		(*self).id()
494	}
495}
496pub trait HasDefaultDbName {
497	type Kind;
498	fn default_db(&self) -> Option<DbIdent<Self::Kind>>;
499}
500impl<T> HasDefaultDbName for &T
501where
502	T: HasDefaultDbName,
503{
504	type Kind = T::Kind;
505
506	fn default_db(&self) -> Option<DbIdent<Self::Kind>> {
507		(*self).default_db()
508	}
509}
510
511#[macro_export]
512macro_rules! def_name_impls {
513	($t:ty, $k:ident) => {
514		impl $crate::uid::HasUid for $t {
515			fn uid(&self) -> $crate::uid::Uid {
516				$crate::uid::HasUid::uid(&self.uid)
517			}
518		}
519		impl $crate::HasIdent for $t {
520			type Kind = $k;
521			fn id(&self) -> $crate::ids::Ident<Self::Kind> {
522				self.name.code.clone()
523			}
524		}
525		impl $crate::HasDefaultDbName for $t {
526			type Kind = $k;
527			fn default_db(&self) -> Option<$crate::ids::DbIdent<Self::Kind>> {
528				self.name.db.clone()
529			}
530		}
531	};
532}
533
534#[macro_export]
535macro_rules! delegate_name_impls {
536	($t:ty, $k:ident) => {
537		impl $crate::uid::HasUid for $t {
538			fn uid(&self) -> $crate::uid::Uid {
539				self.value.uid()
540			}
541		}
542		impl $crate::HasIdent for $t {
543			type Kind = $k;
544			fn id(&self) -> $crate::ids::Ident<Self::Kind> {
545				self.value.id()
546			}
547		}
548		impl $crate::HasDefaultDbName for $t {uiaa matrix
549			type Kind = $k;
550			fn default_db(&self) -> Option<$crate::ids::DbIdent<Self::Kind>> {
551				self.value.default_db()
552			}
553		}
554	};
555}
556
557#[macro_export]
558macro_rules! db_name_impls {
559	($t:ty, $k:ident) => {
560		impl $crate::uid::HasUid for $t {
561			fn uid(&self) -> Uid {
562				$crate::uid::HasUid::uid(&self.uid)
563			}
564		}
565		impl $crate::HasDefaultDbName for $t {
566			type Kind = $k;
567			fn default_db(&self) -> Option<$crate::ids::DbIdent<Self::Kind>> {
568				self.name.clone()
569			}
570		}
571	};
572}
573
574#[macro_export]
575macro_rules! id_impls {
576	($t:ty, $k:ident) => {
577		impl $crate::uid::HasUid for $t {
578			fn uid(&self) -> $crate::uid::Uid {
579				$crate::uid::HasUid::uid(&self.uid)
580			}
581		}
582		impl $crate::HasIdent for $t {
583			type Kind = $k;
584			fn id(&self) -> $crate::ids::Ident<Self::Kind> {
585				self.name.clone()
586			}
587		}
588	};
589}