1#[cfg(feature = "std")]
8use std::borrow::Cow;
9
10#[cfg(all(feature = "alloc", not(feature = "std")))]
11use alloc::borrow::Cow;
12
13#[cfg(feature = "serde")]
14use crate::serde_helpers::{cow_from_string, cow_option_from_string};
15
16#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
24pub enum GeneratedType {
25 #[default]
27 Stored,
28 Virtual,
30}
31
32#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
34pub struct GeneratedDef {
35 pub expression: &'static str,
37 pub gen_type: GeneratedType,
39}
40
41impl GeneratedDef {
42 #[must_use]
44 pub const fn stored(expression: &'static str) -> Self {
45 Self {
46 expression,
47 gen_type: GeneratedType::Stored,
48 }
49 }
50
51 #[must_use]
53 pub const fn virtual_col(expression: &'static str) -> Self {
54 Self {
55 expression,
56 gen_type: GeneratedType::Virtual,
57 }
58 }
59
60 #[must_use]
62 pub const fn into_generated(self) -> Generated {
63 Generated {
64 expression: Cow::Borrowed(self.expression),
65 gen_type: self.gen_type,
66 }
67 }
68}
69
70#[derive(Clone, Debug, PartialEq, Eq)]
72#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
73#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
74pub struct Generated {
75 #[cfg_attr(
77 feature = "serde",
78 serde(rename = "as", deserialize_with = "cow_from_string")
79 )]
80 pub expression: Cow<'static, str>,
81 #[cfg_attr(feature = "serde", serde(rename = "type"))]
83 pub gen_type: GeneratedType,
84}
85
86#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
108pub struct ColumnDef {
109 pub table: &'static str,
111 pub name: &'static str,
113 pub sql_type: &'static str,
115 pub not_null: bool,
117 pub autoincrement: bool,
119 pub primary_key: bool,
121 pub unique: bool,
123 pub default: Option<&'static str>,
125 pub generated: Option<GeneratedDef>,
127}
128
129impl ColumnDef {
130 #[must_use]
132 pub const fn new(table: &'static str, name: &'static str, sql_type: &'static str) -> Self {
133 Self {
134 table,
135 name,
136 sql_type,
137 not_null: false,
138 autoincrement: false,
139 primary_key: false,
140 unique: false,
141 default: None,
142 generated: None,
143 }
144 }
145
146 #[must_use]
148 pub const fn not_null(self) -> Self {
149 Self {
150 not_null: true,
151 ..self
152 }
153 }
154
155 #[must_use]
157 pub const fn autoincrement(self) -> Self {
158 Self {
159 autoincrement: true,
160 ..self
161 }
162 }
163
164 #[must_use]
166 pub const fn primary_key(self) -> Self {
167 Self {
168 primary_key: true,
169 not_null: true,
170 ..self
171 }
172 }
173
174 #[must_use]
176 pub const fn primary(self) -> Self {
177 self.primary_key()
178 }
179
180 #[must_use]
182 pub const fn unique(self) -> Self {
183 Self {
184 unique: true,
185 ..self
186 }
187 }
188
189 #[must_use]
191 pub const fn default_value(self, value: &'static str) -> Self {
192 Self {
193 default: Some(value),
194 ..self
195 }
196 }
197
198 #[must_use]
200 pub const fn generated_stored(self, expression: &'static str) -> Self {
201 Self {
202 generated: Some(GeneratedDef::stored(expression)),
203 ..self
204 }
205 }
206
207 #[must_use]
209 pub const fn generated_virtual(self, expression: &'static str) -> Self {
210 Self {
211 generated: Some(GeneratedDef::virtual_col(expression)),
212 ..self
213 }
214 }
215
216 #[must_use]
218 pub const fn into_column(self) -> Column {
219 Column {
220 table: Cow::Borrowed(self.table),
221 name: Cow::Borrowed(self.name),
222 sql_type: Cow::Borrowed(self.sql_type),
223 not_null: self.not_null,
224 autoincrement: if self.autoincrement { Some(true) } else { None },
225 primary_key: if self.primary_key { Some(true) } else { None },
226 unique: if self.unique { Some(true) } else { None },
227 default: match self.default {
228 Some(s) => Some(Cow::Borrowed(s)),
229 None => None,
230 },
231 generated: match self.generated {
232 Some(g) => Some(g.into_generated()),
233 None => None,
234 },
235 ordinal_position: None,
236 }
237 }
238}
239
240impl Default for ColumnDef {
241 fn default() -> Self {
242 Self::new("", "", "")
243 }
244}
245
246#[derive(Clone, Debug, PartialEq, Eq)]
252#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
253#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
254pub struct Column {
255 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
257 pub table: Cow<'static, str>,
258
259 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
261 pub name: Cow<'static, str>,
262
263 #[cfg_attr(
265 feature = "serde",
266 serde(rename = "type", deserialize_with = "cow_from_string")
267 )]
268 pub sql_type: Cow<'static, str>,
269
270 #[cfg_attr(feature = "serde", serde(default))]
272 pub not_null: bool,
273
274 #[cfg_attr(
276 feature = "serde",
277 serde(default, skip_serializing_if = "Option::is_none")
278 )]
279 pub autoincrement: Option<bool>,
280
281 #[cfg_attr(
283 feature = "serde",
284 serde(default, skip_serializing_if = "Option::is_none")
285 )]
286 pub primary_key: Option<bool>,
287
288 #[cfg_attr(
290 feature = "serde",
291 serde(default, skip_serializing_if = "Option::is_none")
292 )]
293 pub unique: Option<bool>,
294
295 #[cfg_attr(
297 feature = "serde",
298 serde(
299 default,
300 skip_serializing_if = "Option::is_none",
301 deserialize_with = "cow_option_from_string"
302 )
303 )]
304 pub default: Option<Cow<'static, str>>,
305
306 #[cfg_attr(
308 feature = "serde",
309 serde(default, skip_serializing_if = "Option::is_none")
310 )]
311 pub generated: Option<Generated>,
312
313 #[cfg_attr(
317 feature = "serde",
318 serde(default, skip_serializing_if = "Option::is_none")
319 )]
320 pub ordinal_position: Option<i32>,
321}
322
323impl Column {
324 #[must_use]
326 pub fn new(
327 table: impl Into<Cow<'static, str>>,
328 name: impl Into<Cow<'static, str>>,
329 sql_type: impl Into<Cow<'static, str>>,
330 ) -> Self {
331 Self {
332 table: table.into(),
333 name: name.into(),
334 sql_type: sql_type.into(),
335 not_null: false,
336 autoincrement: None,
337 primary_key: None,
338 unique: None,
339 default: None,
340 generated: None,
341 ordinal_position: None,
342 }
343 }
344
345 #[must_use]
347 pub fn not_null(mut self) -> Self {
348 self.not_null = true;
349 self
350 }
351
352 #[must_use]
354 pub fn autoincrement(mut self) -> Self {
355 self.autoincrement = Some(true);
356 self
357 }
358
359 #[must_use]
361 pub fn default_value(mut self, value: impl Into<Cow<'static, str>>) -> Self {
362 self.default = Some(value.into());
363 self
364 }
365
366 #[inline]
368 #[must_use]
369 pub fn name(&self) -> &str {
370 &self.name
371 }
372
373 #[inline]
375 #[must_use]
376 pub fn table(&self) -> &str {
377 &self.table
378 }
379
380 #[inline]
382 #[must_use]
383 pub fn sql_type(&self) -> &str {
384 &self.sql_type
385 }
386
387 #[inline]
389 #[must_use]
390 pub fn is_primary_key(&self) -> bool {
391 self.primary_key.unwrap_or(false)
392 }
393
394 #[inline]
396 #[must_use]
397 pub fn is_autoincrement(&self) -> bool {
398 self.autoincrement.unwrap_or(false)
399 }
400
401 #[inline]
403 #[must_use]
404 pub fn is_unique(&self) -> bool {
405 self.unique.unwrap_or(false)
406 }
407}
408
409impl Default for Column {
410 fn default() -> Self {
411 Self::new("", "", "")
412 }
413}
414
415impl From<ColumnDef> for Column {
416 fn from(def: ColumnDef) -> Self {
417 let mut col = def.into_column();
418 if let Some(generated_def) = def.generated {
420 col.generated = Some(generated_def.into_generated());
421 }
422 col
423 }
424}
425
426#[cfg(test)]
427mod tests {
428 use super::*;
429
430 #[test]
431 fn test_const_column_def() {
432 const COL_DEF: ColumnDef = ColumnDef::new("users", "id", "INTEGER")
433 .primary_key()
434 .autoincrement();
435
436 assert_eq!(COL_DEF.name, "id");
437 assert_eq!(COL_DEF.table, "users");
438 assert_eq!(COL_DEF.sql_type, "INTEGER");
439 assert!(COL_DEF.not_null);
440 assert!(COL_DEF.primary_key);
441 assert!(COL_DEF.autoincrement);
442
443 const COL: Column = COL_DEF.into_column();
444
445 assert_eq!(COL.name, Cow::Borrowed("id"));
446 assert_eq!(COL.table, Cow::Borrowed("users"));
447 assert_eq!(COL.sql_type, Cow::Borrowed("INTEGER"));
448 assert!(COL.not_null);
449 }
452
453 #[test]
454 fn test_const_columns_array() {
455 const COLUMNS: &[ColumnDef] = &[
456 ColumnDef::new("users", "id", "INTEGER")
457 .primary_key()
458 .autoincrement(),
459 ColumnDef::new("users", "name", "TEXT").not_null(),
460 ColumnDef::new("users", "email", "TEXT"),
461 ];
462
463 assert_eq!(COLUMNS.len(), 3);
464 assert_eq!(COLUMNS[0].name, "id");
465 assert_eq!(COLUMNS[1].name, "name");
466 assert_eq!(COLUMNS[2].name, "email");
467 assert!(COLUMNS[1].not_null);
468 assert!(!COLUMNS[2].not_null);
469 }
470
471 #[test]
472 fn test_generated_column() {
473 const GEN_COL: ColumnDef = ColumnDef::new("users", "full_name", "TEXT")
474 .generated_stored("first_name || ' ' || last_name");
475
476 assert!(GEN_COL.generated.is_some());
477 assert_eq!(GEN_COL.generated.unwrap().gen_type, GeneratedType::Stored);
478 }
479
480 #[cfg(feature = "serde")]
481 #[test]
482 fn test_serde_roundtrip() {
483 let col = Column::new("users", "id", "INTEGER");
484 let json = serde_json::to_string(&col).unwrap();
485 let parsed: Column = serde_json::from_str(&json).unwrap();
486 assert_eq!(parsed.name(), "id");
487 }
488}