liter/
column.rs

1//! Data primitives -- a [`Column`] defined by [`Affinity`] & [`Check`]s
2
3use construe::StrConstrue;
4use rusqlite::types::{
5	FromSql,
6	ToSql
7};
8
9use crate::value::{
10	Check,
11	StrChain
12};
13use crate::types::{
14	FromSql2,
15	ToSql2
16};
17
18pub trait Column: FromSql + ToSql + FromSql2 + ToSql2 {
19	const AFFINITY: Affinity;
20	const NULLABLE: bool = false;
21	const CHECKS: &'static [Check] = &[];
22
23	const DEFINITION: ColumnDef = ColumnDef {
24		affinity: Self::AFFINITY,
25		nullable: Self::NULLABLE,
26		checks: Self::CHECKS
27	};
28}
29
30#[derive(Debug, PartialEq, Eq)]
31pub struct ColumnDef {
32	pub affinity: Affinity,
33	pub nullable: bool,
34	pub checks: &'static [Check]
35}
36
37#[derive(Clone, Copy, Debug, PartialEq, Eq)]
38pub enum Affinity {
39	Integer,
40	Real,
41	Text,
42	Blob,
43}
44
45impl Affinity {
46	pub const fn as_str(self) -> &'static str {
47		match self {
48			Affinity::Integer => "INTEGER",
49			Affinity::Real => "REAL",
50			Affinity::Text => "TEXT",
51			Affinity::Blob => "BLOB",
52		}
53	}
54}
55
56impl ColumnDef {
57	/// Override the `nullable` field as `true`
58	pub(crate) const fn nullable(&self) -> Self {
59		Self {nullable: true, ..*self}
60	}
61	/// Write out the [`Column`] SQL defintion
62	///
63	/// ```sql
64	/// column_name INTEGER NOT NULL
65	/// ```
66	pub(crate) const fn push_sql<const N: usize>(
67		&self,
68		name: &StrChain,
69		mut sc: StrConstrue<N>)
70		-> StrConstrue<N>
71	{
72		sc = name.join(sc, "_");
73		sc = sc.push_str(" ");
74		sc = sc.push_str(self.affinity.as_str());
75		if !self.nullable {
76			sc = sc.push_str(" NOT NULL");
77		}
78		let mut checks = self.checks;
79		while let [Check::Sql(check), rest @ ..] = checks {
80			checks = rest;
81			sc = sc.push_str(" CHECK ( ");
82			sc = name.join(sc, "_");
83			sc = sc.push_str(" ");
84			sc = sc.push_str(check);
85			sc = sc.push_str(" ) ");
86		}
87		sc
88	}
89}
90
91/*
92 *	COLUMNS
93 */
94
95macro_rules! column {
96	($t:ty, $col:expr) => {
97		impl Column for $t {
98			const AFFINITY: Affinity = $col;
99		}
100	};
101}
102
103/* BLOB */
104column!(Vec<u8>, Affinity::Blob);
105impl<const N: usize> Column for [u8; N] {
106	const AFFINITY: Affinity = Affinity::Blob;
107}
108
109/* TEXT */
110column!(std::rc::Rc<str>, Affinity::Text);
111column!(std::sync::Arc<str>, Affinity::Text);
112column!(Box<str>, Affinity::Text);
113column!(String, Affinity::Text);
114
115/* REAL */
116column!(f32, Affinity::Real);
117column!(f64, Affinity::Real);
118
119/* INTEGER */
120column!(i8, Affinity::Integer);
121column!(i16, Affinity::Integer);
122column!(i32, Affinity::Integer);
123column!(i64, Affinity::Integer);
124
125column!(u8, Affinity::Integer);
126column!(u16, Affinity::Integer);
127column!(u32, Affinity::Integer);
128column!(u64, Affinity::Integer);
129
130column!(usize, Affinity::Integer);
131
132/* BOOL */
133impl Column for bool {
134	const AFFINITY: Affinity = Affinity::Integer;
135	const NULLABLE: bool = false;
136	const CHECKS: &'static [Check] = &[
137		Check::Sql("BETWEEN 0 AND 1")
138	];
139}
140