1use chrono::{NaiveDate, NaiveDateTime, NaiveTime, TimeZone};
5
6use super::{Encrypted, Json, Ulid};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13#[non_exhaustive]
14pub enum ColumnKind {
15 BigInt,
17 Int,
19 SmallInt,
21 Double,
23 Real,
25 Text,
27 Blob,
29 Bool,
31 DateTime,
33 NaiveDateTime,
35 Date,
37 Time,
39 Json,
41 Uuid,
43 Unknown,
45}
46
47impl ColumnKind {
48 pub fn as_str(&self) -> &'static str {
50 match self {
51 Self::BigInt => "bigint",
52 Self::Int => "int",
53 Self::SmallInt => "smallint",
54 Self::Double => "double",
55 Self::Real => "real",
56 Self::Text => "text",
57 Self::Blob => "blob",
58 Self::Bool => "bool",
59 Self::DateTime => "datetime",
60 Self::NaiveDateTime => "naive datetime",
61 Self::Date => "date",
62 Self::Time => "time",
63 Self::Json => "json",
64 Self::Uuid => "uuid",
65 Self::Unknown => "unknown",
66 }
67 }
68}
69
70pub trait ColumnType {
74 const KIND: ColumnKind;
76}
77
78macro_rules! kinds {
79 ($($kind:ident => $($t:ty),*;)*) => {$($(
80 impl ColumnType for $t {
81 const KIND: ColumnKind = ColumnKind::$kind;
82 }
83 )*)*};
84}
85
86kinds! {
87 BigInt => i64, u32;
88 Int => i32, u16;
89 SmallInt => i8, i16, u8;
90 Double => f64;
91 Real => f32;
92 Text => String, Ulid;
93 Blob => Vec<u8>;
94 Bool => bool;
95 NaiveDateTime => NaiveDateTime;
96 Date => NaiveDate;
97 Time => NaiveTime;
98 Json => serde_json::Value;
99}
100
101#[cfg(feature = "uuid")]
102impl ColumnType for uuid::Uuid {
103 const KIND: ColumnKind = ColumnKind::Uuid;
104}
105
106impl<Tz: TimeZone> ColumnType for chrono::DateTime<Tz> {
107 const KIND: ColumnKind = ColumnKind::DateTime;
108}
109
110impl<T: ColumnType> ColumnType for Option<T> {
111 const KIND: ColumnKind = T::KIND;
112}
113
114impl<T> ColumnType for Json<T> {
115 const KIND: ColumnKind = ColumnKind::Json;
116}
117
118impl<T> ColumnType for Encrypted<T> {
119 const KIND: ColumnKind = ColumnKind::Text;
120}
121
122#[derive(Debug, Clone)]
124#[non_exhaustive]
125pub struct ModelColumn {
126 pub name: &'static str,
128 pub rust_type: &'static str,
130 pub kind: ColumnKind,
132 pub nullable: bool,
134 pub default: Option<&'static str>,
136 pub references: Option<&'static str>,
138}
139
140impl ModelColumn {
141 pub const fn new(
143 name: &'static str,
144 rust_type: &'static str,
145 kind: ColumnKind,
146 nullable: bool,
147 ) -> Self {
148 Self {
149 name,
150 rust_type,
151 kind,
152 nullable,
153 default: None,
154 references: None,
155 }
156 }
157
158 pub const fn default_sql(mut self, sql: &'static str) -> Self {
160 self.default = Some(sql);
161 self
162 }
163
164 pub const fn references(mut self, table: &'static str) -> Self {
166 self.references = Some(table);
167 self
168 }
169}
170
171#[derive(Debug, Clone)]
174#[non_exhaustive]
175pub struct ModelIndex {
176 pub columns: &'static [&'static str],
178 pub unique: bool,
180}
181
182impl ModelIndex {
183 pub const fn new(columns: &'static [&'static str], unique: bool) -> Self {
185 Self { columns, unique }
186 }
187}
188
189#[doc(hidden)]
190pub struct ColumnProbe<T>(std::marker::PhantomData<fn() -> T>);
191impl<T> ColumnProbe<T> {
192 pub const fn new() -> Self {
193 Self(std::marker::PhantomData)
194 }
195}
196impl<T> Default for ColumnProbe<T> {
197 fn default() -> Self {
198 Self::new()
199 }
200}
201#[doc(hidden)]
202pub trait KnownColumn {
203 fn kind(&self) -> ColumnKind;
204}
205impl<T: ColumnType> KnownColumn for ColumnProbe<T> {
206 fn kind(&self) -> ColumnKind {
207 T::KIND
208 }
209}
210#[doc(hidden)]
211pub trait UnknownColumn {
212 fn kind(&self) -> ColumnKind;
213}
214impl<T> UnknownColumn for &ColumnProbe<T> {
215 fn kind(&self) -> ColumnKind {
216 ColumnKind::Unknown
217 }
218}
219
220#[derive(Clone)]
222#[allow(dead_code)] pub(crate) struct ModelInfo {
224 pub(crate) type_id: std::any::TypeId,
225 pub(crate) name: &'static str,
226 pub(crate) table: &'static str,
227 pub(crate) columns: fn() -> Vec<ModelColumn>,
228 pub(crate) indexes: fn() -> Vec<super::ModelIndex>,
229}
230
231pub(crate) fn model_info<M: super::Model>() -> ModelInfo {
233 ModelInfo {
234 type_id: std::any::TypeId::of::<M>(),
235 name: std::any::type_name::<M>(),
236 table: M::TABLE,
237 columns: M::column_info,
238 indexes: M::indexes,
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245
246 struct Custom;
247
248 #[test]
249 fn kinds_of_known_types() {
250 assert_eq!(<i64 as ColumnType>::KIND, ColumnKind::BigInt);
251 assert_eq!(<Option<String> as ColumnType>::KIND, ColumnKind::Text);
252 assert_eq!(<Json<Vec<i64>> as ColumnType>::KIND, ColumnKind::Json);
253 assert_eq!(ColumnKind::BigInt.as_str(), "bigint");
254 }
255
256 #[test]
257 #[allow(clippy::needless_borrow)]
258 fn probe_tells_known_from_unknown() {
259 #[allow(unused_imports)]
260 use super::{KnownColumn as _, UnknownColumn as _};
261 assert_eq!((&ColumnProbe::<i64>::new()).kind(), ColumnKind::BigInt);
262 assert_eq!((&ColumnProbe::<Custom>::new()).kind(), ColumnKind::Unknown);
263 assert_eq!(
264 (&ColumnProbe::<Option<Custom>>::new()).kind(),
265 ColumnKind::Unknown
266 );
267 }
268}