drizzle_types/sqlite/
sql_type.rs

1//! SQLite column type definitions
2//!
3//! Defines the core SQLite storage classes and type affinities.
4
5/// Enum representing supported SQLite column types.
6///
7/// These correspond to the [SQLite storage classes](https://sqlite.org/datatype3.html#storage_classes_and_datatypes).
8/// Each type maps to specific Rust types and has different capabilities for constraints and features.
9///
10/// # Examples
11///
12/// ```
13/// use drizzle_types::sqlite::SQLiteType;
14///
15/// let int_type = SQLiteType::Integer;
16/// assert_eq!(int_type.to_sql_type(), "INTEGER");
17/// assert!(int_type.is_valid_flag("autoincrement"));
18///
19/// let text_type = SQLiteType::Text;
20/// assert!(text_type.is_valid_flag("json"));
21/// assert!(!text_type.is_valid_flag("autoincrement"));
22/// ```
23#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25#[cfg_attr(feature = "serde", serde(rename_all = "UPPERCASE"))]
26pub enum SQLiteType {
27    /// SQLite INTEGER type - stores signed integers up to 8 bytes.
28    ///
29    /// See: <https://sqlite.org/datatype3.html#integer_datatype>
30    ///
31    /// Supports: primary keys, autoincrement, enums (discriminant storage)
32    Integer,
33
34    /// SQLite TEXT type - stores text in UTF-8, UTF-16BE, or UTF-16LE encoding.
35    ///
36    /// See: <https://sqlite.org/datatype3.html#text_datatype>
37    ///
38    /// Supports: enums (variant name storage), JSON serialization
39    Text,
40
41    /// SQLite BLOB type - stores binary data exactly as input.
42    ///
43    /// See: <https://sqlite.org/datatype3.html#blob_datatype>
44    ///
45    /// Supports: JSON serialization, UUID storage
46    Blob,
47
48    /// SQLite REAL type - stores floating point values as 8-byte IEEE floating point numbers.
49    ///
50    /// See: <https://sqlite.org/datatype3.html#real_datatype>
51    Real,
52
53    /// SQLite NUMERIC type - stores values as INTEGER, REAL, or TEXT depending on the value.
54    ///
55    /// See: <https://sqlite.org/datatype3.html#numeric_datatype>
56    Numeric,
57
58    /// SQLite ANY type - no type affinity, can store any type of data.
59    ///
60    /// See: <https://sqlite.org/datatype3.html#type_affinity>
61    #[default]
62    Any,
63}
64
65impl SQLiteType {
66    /// Convert from attribute name to enum variant
67    ///
68    /// Handles common attribute names used in the macro system.
69    #[must_use]
70    pub fn from_attribute_name(name: &str) -> Option<Self> {
71        if name.eq_ignore_ascii_case("integer") {
72            Some(Self::Integer)
73        } else if name.eq_ignore_ascii_case("text") {
74            Some(Self::Text)
75        } else if name.eq_ignore_ascii_case("blob") {
76            Some(Self::Blob)
77        } else if name.eq_ignore_ascii_case("real") {
78            Some(Self::Real)
79        } else if name.eq_ignore_ascii_case("number") || name.eq_ignore_ascii_case("numeric") {
80            Some(Self::Numeric)
81        } else if name.eq_ignore_ascii_case("boolean") {
82            Some(Self::Integer) // Store booleans as integers (0/1)
83        } else if name.eq_ignore_ascii_case("any") {
84            Some(Self::Any)
85        } else {
86            None
87        }
88    }
89
90    /// Get the SQL type string for this type
91    #[must_use]
92    pub const fn to_sql_type(&self) -> &'static str {
93        match self {
94            Self::Integer => "INTEGER",
95            Self::Text => "TEXT",
96            Self::Blob => "BLOB",
97            Self::Real => "REAL",
98            Self::Numeric => "NUMERIC",
99            Self::Any => "ANY",
100        }
101    }
102
103    /// Check if a flag is valid for this column type
104    ///
105    /// # Valid Flags per Type
106    ///
107    /// - `INTEGER`: `primary`, `primary_key`, `unique`, `autoincrement`, `enum`
108    /// - `TEXT`: `primary`, `primary_key`, `unique`, `json`, `enum`
109    /// - `BLOB`: `primary`, `primary_key`, `unique`, `json`
110    /// - `REAL`: `primary`, `primary_key`, `unique`
111    /// - `NUMERIC`: `primary`, `primary_key`, `unique`
112    /// - `ANY`: `primary`, `primary_key`, `unique`
113    #[must_use]
114    pub fn is_valid_flag(&self, flag: &str) -> bool {
115        matches!(flag, "primary" | "primary_key" | "unique")
116            || matches!(
117                (self, flag),
118                (Self::Integer, "autoincrement")
119                    | (Self::Text | Self::Blob, "json")
120                    | (Self::Text | Self::Integer, "enum")
121            )
122    }
123}
124
125impl core::fmt::Display for SQLiteType {
126    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
127        f.write_str(self.to_sql_type())
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    #[test]
136    fn test_from_attribute_name() {
137        assert_eq!(
138            SQLiteType::from_attribute_name("integer"),
139            Some(SQLiteType::Integer)
140        );
141        assert_eq!(
142            SQLiteType::from_attribute_name("INTEGER"),
143            Some(SQLiteType::Integer)
144        );
145        assert_eq!(
146            SQLiteType::from_attribute_name("text"),
147            Some(SQLiteType::Text)
148        );
149        assert_eq!(
150            SQLiteType::from_attribute_name("blob"),
151            Some(SQLiteType::Blob)
152        );
153        assert_eq!(
154            SQLiteType::from_attribute_name("boolean"),
155            Some(SQLiteType::Integer)
156        );
157        assert_eq!(SQLiteType::from_attribute_name("unknown"), None);
158    }
159
160    #[test]
161    fn test_to_sql_type() {
162        assert_eq!(SQLiteType::Integer.to_sql_type(), "INTEGER");
163        assert_eq!(SQLiteType::Text.to_sql_type(), "TEXT");
164        assert_eq!(SQLiteType::Blob.to_sql_type(), "BLOB");
165        assert_eq!(SQLiteType::Real.to_sql_type(), "REAL");
166        assert_eq!(SQLiteType::Numeric.to_sql_type(), "NUMERIC");
167        assert_eq!(SQLiteType::Any.to_sql_type(), "ANY");
168    }
169
170    #[test]
171    fn test_is_valid_flag() {
172        // Autoincrement only valid for INTEGER
173        assert!(SQLiteType::Integer.is_valid_flag("autoincrement"));
174        assert!(!SQLiteType::Text.is_valid_flag("autoincrement"));
175        assert!(!SQLiteType::Blob.is_valid_flag("autoincrement"));
176
177        // JSON valid for TEXT and BLOB
178        assert!(SQLiteType::Text.is_valid_flag("json"));
179        assert!(SQLiteType::Blob.is_valid_flag("json"));
180        assert!(!SQLiteType::Integer.is_valid_flag("json"));
181
182        // Enum valid for TEXT and INTEGER
183        assert!(SQLiteType::Text.is_valid_flag("enum"));
184        assert!(SQLiteType::Integer.is_valid_flag("enum"));
185        assert!(!SQLiteType::Blob.is_valid_flag("enum"));
186
187        // Primary/unique valid for all
188        assert!(SQLiteType::Integer.is_valid_flag("primary"));
189        assert!(SQLiteType::Text.is_valid_flag("unique"));
190        assert!(SQLiteType::Blob.is_valid_flag("primary_key"));
191    }
192}