use construe::StrConstrue;
use rusqlite::types::{
FromSql,
ToSql
};
use crate::value::{
Check,
StrChain
};
use crate::types::{
FromSql2,
ToSql2
};
pub trait Column: FromSql + ToSql + FromSql2 + ToSql2 {
const AFFINITY: Affinity;
const NULLABLE: bool = false;
const CHECKS: &'static [Check] = &[];
const DEFINITION: ColumnDef = ColumnDef {
affinity: Self::AFFINITY,
nullable: Self::NULLABLE,
checks: Self::CHECKS
};
}
#[derive(Debug, PartialEq, Eq)]
pub struct ColumnDef {
pub affinity: Affinity,
pub nullable: bool,
pub checks: &'static [Check]
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Affinity {
Integer,
Real,
Text,
Blob,
}
impl Affinity {
pub const fn as_str(self) -> &'static str {
match self {
Affinity::Integer => "INTEGER",
Affinity::Real => "REAL",
Affinity::Text => "TEXT",
Affinity::Blob => "BLOB",
}
}
}
impl ColumnDef {
pub(crate) const fn nullable(&self) -> Self {
Self {nullable: true, ..*self}
}
pub(crate) const fn push_sql<const N: usize>(
&self,
name: &StrChain,
mut sc: StrConstrue<N>)
-> StrConstrue<N>
{
sc = name.join(sc, "_");
sc = sc.push_str(" ");
sc = sc.push_str(self.affinity.as_str());
if !self.nullable {
sc = sc.push_str(" NOT NULL");
}
let mut checks = self.checks;
while let [Check::Sql(check), rest @ ..] = checks {
checks = rest;
sc = sc.push_str(" CHECK ( ");
sc = name.join(sc, "_");
sc = sc.push_str(" ");
sc = sc.push_str(check);
sc = sc.push_str(" ) ");
}
sc
}
}
macro_rules! column {
($t:ty, $col:expr) => {
impl Column for $t {
const AFFINITY: Affinity = $col;
}
};
}
column!(Vec<u8>, Affinity::Blob);
impl<const N: usize> Column for [u8; N] {
const AFFINITY: Affinity = Affinity::Blob;
}
column!(std::rc::Rc<str>, Affinity::Text);
column!(std::sync::Arc<str>, Affinity::Text);
column!(Box<str>, Affinity::Text);
column!(String, Affinity::Text);
column!(f32, Affinity::Real);
column!(f64, Affinity::Real);
column!(i8, Affinity::Integer);
column!(i16, Affinity::Integer);
column!(i32, Affinity::Integer);
column!(i64, Affinity::Integer);
column!(u8, Affinity::Integer);
column!(u16, Affinity::Integer);
column!(u32, Affinity::Integer);
column!(u64, Affinity::Integer);
column!(usize, Affinity::Integer);
impl Column for bool {
const AFFINITY: Affinity = Affinity::Integer;
const NULLABLE: bool = false;
const CHECKS: &'static [Check] = &[
Check::Sql("BETWEEN 0 AND 1")
];
}