1use 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 pub(crate) const fn nullable(&self) -> Self {
59 Self {nullable: true, ..*self}
60 }
61 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
91macro_rules! column {
96 ($t:ty, $col:expr) => {
97 impl Column for $t {
98 const AFFINITY: Affinity = $col;
99 }
100 };
101}
102
103column!(Vec<u8>, Affinity::Blob);
105impl<const N: usize> Column for [u8; N] {
106 const AFFINITY: Affinity = Affinity::Blob;
107}
108
109column!(std::rc::Rc<str>, Affinity::Text);
111column!(std::sync::Arc<str>, Affinity::Text);
112column!(Box<str>, Affinity::Text);
113column!(String, Affinity::Text);
114
115column!(f32, Affinity::Real);
117column!(f64, Affinity::Real);
118
119column!(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
132impl 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