cartel_sqlite/engine/
catalogs.rs1use std::marker;
2
3#[derive(Clone, Copy)]
5pub struct Entry<'sql> {
6 sql: &'sql str,
7 parameters: usize,
8 columns: usize,
9}
10
11impl<'sql> Entry<'sql> {
12 #[doc(hidden)]
13 pub const fn of<Q: TypedQuery>() -> Self {
14 Self {
15 sql: Q::SQL,
16 parameters: Q::PARAMETERS,
17 columns: Q::COLUMNS,
18 }
19 }
20
21 pub const fn sql(self) -> &'sql str {
22 self.sql
23 }
24
25 pub const fn parameters(self) -> usize {
26 self.parameters
27 }
28
29 pub const fn columns(self) -> usize {
30 self.columns
31 }
32}
33
34pub trait Catalog {
36 const STATEMENTS: &[Entry<'_>];
37}
38
39#[derive(Clone, Copy, PartialEq, Eq)]
41pub struct Shape {
42 pub statements: usize,
43 pub max_sql_bytes: usize,
44}
45
46impl Shape {
47 pub const fn of<I: Catalog>() -> Self {
48 let entries = I::STATEMENTS;
49 let mut stmt: usize = 0;
50 let mut max_sql: usize = 0;
51 while stmt < entries.len() {
52 let bytes = entries[stmt].sql().len();
53 if max_sql < bytes {
54 max_sql = bytes;
55 }
56 stmt += 1;
57 }
58 Self {
59 statements: entries.len(),
60 max_sql_bytes: max_sql,
61 }
62 }
63}
64
65pub(crate) fn validate<I: Catalog>() -> Result<(), crate::Error> {
66 for (stmt, entry) in I::STATEMENTS.iter().enumerate() {
67 let sql = entry.sql();
68 if sql.trim().is_empty() || sql.as_bytes().contains(&0) {
69 return Err(crate::Error::CatalogStatementSql { statement: stmt });
70 }
71 }
72 Ok(())
73}
74
75pub struct Empty;
77
78impl Catalog for Empty {
79 const STATEMENTS: &[Entry<'_>] = &[];
80}
81
82#[doc(hidden)]
83pub trait TypedQuery {
84 const SQL: &str;
85 const PARAMETERS: usize;
86 const COLUMNS: usize;
87}
88
89#[doc(hidden)]
90pub trait HasQuery<Q: TypedQuery>: Catalog {
91 const SLOT: usize;
92}
93
94#[doc(hidden)]
96pub struct Statement<I: Catalog, Q: TypedQuery> {
97 marker: marker::PhantomData<(I, Q)>,
98}
99
100enum Mismatch {
101 Slot,
102 Sql,
103 Parameters,
104 Columns,
105}
106
107impl<I: HasQuery<Q>, Q: TypedQuery> Statement<I, Q> {
108 #[doc(hidden)]
110 pub const fn of() -> Self {
111 const {
112 match Self::try_new() {
113 Ok(stmt) => stmt,
114 Err(_) => panic!("statement"),
115 }
116 }
117 }
118
119 const fn try_new() -> Result<Self, Mismatch> {
120 let slot = <I as HasQuery<Q>>::SLOT;
121 if slot >= I::STATEMENTS.len() {
122 return Err(Mismatch::Slot);
123 }
124 let entry = I::STATEMENTS[slot];
125 if !crate::sql::raw::Parts::new(&[entry.sql]).matches(Q::SQL) {
126 return Err(Mismatch::Sql);
127 }
128 if entry.parameters != Q::PARAMETERS {
129 return Err(Mismatch::Parameters);
130 }
131 if entry.columns != Q::COLUMNS {
132 return Err(Mismatch::Columns);
133 }
134 Ok(Self {
135 marker: marker::PhantomData,
136 })
137 }
138
139 pub(crate) const fn slot(self) -> usize {
140 <I as HasQuery<Q>>::SLOT
141 }
142}
143
144impl<I: Catalog, Q: TypedQuery> Copy for Statement<I, Q> {}
145
146impl<I: Catalog, Q: TypedQuery> Clone for Statement<I, Q> {
147 fn clone(&self) -> Self {
148 *self
149 }
150}