Skip to main content

sqlx_gen/typemap/
sqlite.rs

1use super::RustType;
2use crate::cli::TimeCrate;
3
4pub fn map_type(declared_type: &str, time_crate: TimeCrate) -> RustType {
5    let upper = declared_type.to_uppercase();
6
7    if upper.contains("INT") {
8        return RustType::simple("i64");
9    }
10    if upper.contains("CHAR") || upper.contains("TEXT") || upper.contains("CLOB") {
11        return RustType::simple("String");
12    }
13    if upper.contains("BLOB") || upper.is_empty() {
14        return RustType::simple("Vec<u8>");
15    }
16    if upper.contains("REAL") || upper.contains("FLOAT") || upper.contains("DOUBLE") {
17        return RustType::simple("f64");
18    }
19    if upper.contains("BOOL") {
20        return RustType::simple("bool");
21    }
22    if upper.contains("TIMESTAMP") || upper.contains("DATETIME") {
23        return match time_crate {
24            TimeCrate::Chrono => {
25                RustType::with_import("NaiveDateTime", "use chrono::NaiveDateTime;")
26            }
27            TimeCrate::Time => {
28                RustType::with_import("PrimitiveDateTime", "use time::PrimitiveDateTime;")
29            }
30        };
31    }
32    if upper.contains("DATE") {
33        return match time_crate {
34            TimeCrate::Chrono => RustType::with_import("NaiveDate", "use chrono::NaiveDate;"),
35            TimeCrate::Time => RustType::with_import("Date", "use time::Date;"),
36        };
37    }
38    if upper.contains("TIME") {
39        return match time_crate {
40            TimeCrate::Chrono => RustType::with_import("NaiveTime", "use chrono::NaiveTime;"),
41            TimeCrate::Time => RustType::with_import("Time", "use time::Time;"),
42        };
43    }
44    if upper.contains("NUMERIC") || upper.contains("DECIMAL") {
45        // f64 would silently lose precision for currency-style values. sqlx exposes
46        // the same Decimal type for sqlite as for postgres/mysql.
47        return RustType::with_import("Decimal", "use rust_decimal::Decimal;");
48    }
49
50    // Default: SQLite is loosely typed
51    RustType::simple("String")
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57    use crate::cli::TimeCrate;
58
59    #[test]
60    fn test_integer() {
61        assert_eq!(map_type("INTEGER", TimeCrate::Chrono).path, "i64");
62    }
63
64    #[test]
65    fn test_int() {
66        assert_eq!(map_type("INT", TimeCrate::Chrono).path, "i64");
67    }
68
69    #[test]
70    fn test_bigint() {
71        assert_eq!(map_type("BIGINT", TimeCrate::Chrono).path, "i64");
72    }
73
74    #[test]
75    fn test_smallint() {
76        assert_eq!(map_type("SMALLINT", TimeCrate::Chrono).path, "i64");
77    }
78
79    #[test]
80    fn test_tinyint() {
81        assert_eq!(map_type("TINYINT", TimeCrate::Chrono).path, "i64");
82    }
83
84    #[test]
85    fn test_mediumint() {
86        assert_eq!(map_type("MEDIUMINT", TimeCrate::Chrono).path, "i64");
87    }
88
89    #[test]
90    fn test_text() {
91        assert_eq!(map_type("TEXT", TimeCrate::Chrono).path, "String");
92    }
93
94    #[test]
95    fn test_varchar() {
96        assert_eq!(map_type("VARCHAR(255)", TimeCrate::Chrono).path, "String");
97    }
98
99    #[test]
100    fn test_character() {
101        assert_eq!(map_type("CHARACTER(20)", TimeCrate::Chrono).path, "String");
102    }
103
104    #[test]
105    fn test_clob() {
106        assert_eq!(map_type("CLOB", TimeCrate::Chrono).path, "String");
107    }
108
109    #[test]
110    fn test_blob() {
111        assert_eq!(map_type("BLOB", TimeCrate::Chrono).path, "Vec<u8>");
112    }
113
114    #[test]
115    fn test_empty_type() {
116        assert_eq!(map_type("", TimeCrate::Chrono).path, "Vec<u8>");
117    }
118
119    #[test]
120    fn test_real() {
121        assert_eq!(map_type("REAL", TimeCrate::Chrono).path, "f64");
122    }
123
124    #[test]
125    fn test_float() {
126        assert_eq!(map_type("FLOAT", TimeCrate::Chrono).path, "f64");
127    }
128
129    #[test]
130    fn test_double() {
131        assert_eq!(map_type("DOUBLE", TimeCrate::Chrono).path, "f64");
132    }
133
134    #[test]
135    fn test_double_precision() {
136        assert_eq!(map_type("DOUBLE PRECISION", TimeCrate::Chrono).path, "f64");
137    }
138
139    #[test]
140    fn test_boolean() {
141        assert_eq!(map_type("BOOLEAN", TimeCrate::Chrono).path, "bool");
142    }
143
144    #[test]
145    fn test_timestamp() {
146        let rt = map_type("TIMESTAMP", TimeCrate::Chrono);
147        assert_eq!(rt.path, "NaiveDateTime");
148        assert!(rt.needs_import.as_ref().unwrap().contains("chrono"));
149    }
150
151    #[test]
152    fn test_datetime() {
153        let rt = map_type("DATETIME", TimeCrate::Chrono);
154        assert_eq!(rt.path, "NaiveDateTime");
155        assert!(rt.needs_import.is_some());
156    }
157
158    #[test]
159    fn test_date() {
160        let rt = map_type("DATE", TimeCrate::Chrono);
161        assert_eq!(rt.path, "NaiveDate");
162        assert!(rt.needs_import.as_ref().unwrap().contains("chrono"));
163    }
164
165    #[test]
166    fn test_time() {
167        let rt = map_type("TIME", TimeCrate::Chrono);
168        assert_eq!(rt.path, "NaiveTime");
169        assert!(rt.needs_import.as_ref().unwrap().contains("chrono"));
170    }
171
172    #[test]
173    fn test_numeric_uses_decimal() {
174        let rt = map_type("NUMERIC", TimeCrate::Chrono);
175        assert_eq!(rt.path, "Decimal");
176        assert!(rt.needs_import.as_ref().unwrap().contains("rust_decimal"));
177    }
178
179    #[test]
180    fn test_decimal_uses_decimal() {
181        let rt = map_type("DECIMAL", TimeCrate::Chrono);
182        assert_eq!(rt.path, "Decimal");
183    }
184
185    #[test]
186    fn test_case_insensitive() {
187        assert_eq!(map_type("integer", TimeCrate::Chrono).path, "i64");
188    }
189
190    #[test]
191    fn test_fallback_unknown_type() {
192        assert_eq!(map_type("JSON", TimeCrate::Chrono).path, "String");
193    }
194
195    // --- time crate ---
196
197    #[test]
198    fn test_timestamp_time_crate() {
199        let rt = map_type("TIMESTAMP", TimeCrate::Time);
200        assert_eq!(rt.path, "PrimitiveDateTime");
201        assert!(rt
202            .needs_import
203            .as_ref()
204            .unwrap()
205            .contains("time::PrimitiveDateTime"));
206    }
207
208    #[test]
209    fn test_datetime_time_crate() {
210        let rt = map_type("DATETIME", TimeCrate::Time);
211        assert_eq!(rt.path, "PrimitiveDateTime");
212        assert!(rt
213            .needs_import
214            .as_ref()
215            .unwrap()
216            .contains("time::PrimitiveDateTime"));
217    }
218
219    #[test]
220    fn test_date_time_crate() {
221        let rt = map_type("DATE", TimeCrate::Time);
222        assert_eq!(rt.path, "Date");
223        assert!(rt.needs_import.as_ref().unwrap().contains("time::Date"));
224    }
225
226    #[test]
227    fn test_time_time_crate() {
228        let rt = map_type("TIME", TimeCrate::Time);
229        assert_eq!(rt.path, "Time");
230        assert!(rt.needs_import.as_ref().unwrap().contains("time::Time"));
231    }
232}