1use std::fmt::{self, Display, Formatter};
2use std::os::raw::c_int;
3use std::str::FromStr;
4
5use libsqlite3_sys::{SQLITE_BLOB, SQLITE_FLOAT, SQLITE_INTEGER, SQLITE_NULL, SQLITE_TEXT};
6use rbdc::Error;
7use rbs::Value;
8
9pub trait Type {
10 fn type_info(&self) -> SqliteTypeInfo;
11}
12
13impl<T: Type> Type for Option<T> {
15 fn type_info(&self) -> SqliteTypeInfo {
16 match self {
17 None => SqliteTypeInfo(DataType::Null),
18 Some(v) => v.type_info(),
19 }
20 }
21}
22
23#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
24pub(crate) enum DataType {
25 Null,
26 Int,
27 Float,
28 Text,
29 Blob,
30
31 Numeric,
32
33 Bool,
35 Int64,
36 Date,
37 Time,
38 Datetime,
39}
40
41#[derive(Debug, Clone, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
43pub struct SqliteTypeInfo(pub(crate) DataType);
44
45impl SqliteTypeInfo {
46 pub fn null() -> Self {
47 SqliteTypeInfo(DataType::Null)
48 }
49}
50
51impl Display for SqliteTypeInfo {
52 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
53 f.pad(self.name())
54 }
55}
56
57impl SqliteTypeInfo {
58 pub fn is_null(&self) -> bool {
59 matches!(self.0, DataType::Null)
60 }
61
62 pub fn name(&self) -> &str {
63 match self.0 {
64 DataType::Null => "NULL",
65 DataType::Text => "TEXT",
66 DataType::Float => "REAL",
67 DataType::Blob => "BLOB",
68 DataType::Int | DataType::Int64 => "INTEGER",
69 DataType::Numeric => "NUMERIC",
70
71 DataType::Bool => "BOOLEAN",
73 DataType::Date => "DATE",
74 DataType::Time => "TIME",
75 DataType::Datetime => "DATETIME",
76 }
77 }
78}
79
80impl DataType {
81 pub(crate) fn from_code(code: c_int) -> Self {
82 match code {
83 SQLITE_INTEGER => DataType::Int,
84 SQLITE_FLOAT => DataType::Float,
85 SQLITE_BLOB => DataType::Blob,
86 SQLITE_NULL => DataType::Null,
87 SQLITE_TEXT => DataType::Text,
88
89 _ => unreachable!("unknown SQLite data type code {} - this indicates a version mismatch between libsqlite3_sys and the SQLite library", code),
92 }
93 }
94}
95
96impl FromStr for DataType {
100 type Err = Error;
101
102 fn from_str(s: &str) -> Result<Self, Self::Err> {
103 let s = s.to_ascii_lowercase();
104 Ok(match &*s {
105 "int4" => DataType::Int,
106 "int8" => DataType::Int64,
107 "boolean" | "bool" => DataType::Bool,
108
109 "date" => DataType::Date,
110 "time" => DataType::Time,
111 "datetime" | "timestamp" => DataType::Datetime,
112
113 _ if s.contains("int") => DataType::Int64,
114
115 _ if s.contains("char") || s.contains("clob") || s.contains("text") => DataType::Text,
116
117 _ if s.contains("blob") => DataType::Blob,
118
119 _ if s.contains("real") || s.contains("floa") || s.contains("doub") => DataType::Float,
120
121 _ if s.contains("num") || s.contains("dec") => DataType::Numeric,
122
123 _ => {
124 return Err(format!("unknown type: `{}`", s).into());
125 }
126 })
127 }
128}
129
130impl Type for Value {
131 fn type_info(&self) -> SqliteTypeInfo {
132 match self {
133 Value::Null => SqliteTypeInfo::null(),
134 Value::Bool(_) => SqliteTypeInfo(DataType::Bool),
135 Value::I32(_) => SqliteTypeInfo(DataType::Int),
136 Value::I64(_) => SqliteTypeInfo(DataType::Int64),
137 Value::U32(_) => SqliteTypeInfo(DataType::Int),
138 Value::U64(_) => SqliteTypeInfo(DataType::Int64),
139 Value::F32(_) => SqliteTypeInfo(DataType::Float),
140 Value::F64(_) => SqliteTypeInfo(DataType::Float),
141 Value::String(_) => SqliteTypeInfo(DataType::Text),
142 Value::Binary(_) => SqliteTypeInfo(DataType::Blob),
143 Value::Array(_) => SqliteTypeInfo(DataType::Null),
144 Value::Map(_) => SqliteTypeInfo(DataType::Null),
145 Value::Ext(t, _) => match *t {
146 "Date" => SqliteTypeInfo(DataType::Text),
147 "DateTime" => SqliteTypeInfo(DataType::Text),
148 "Time" => SqliteTypeInfo(DataType::Text),
149 "Timestamp" => SqliteTypeInfo(DataType::Int64),
150 "Decimal" => SqliteTypeInfo(DataType::Numeric),
151 "Json" => SqliteTypeInfo(DataType::Blob),
152 "Uuid" => SqliteTypeInfo(DataType::Text),
153 _ => SqliteTypeInfo(DataType::Null),
154 },
155 }
156 }
157}
158
159#[test]
160fn test_data_type_from_str() -> Result<(), Error> {
161 assert_eq!(DataType::Int, "INT4".parse()?);
162
163 assert_eq!(DataType::Int64, "INT".parse()?);
164 assert_eq!(DataType::Int64, "INTEGER".parse()?);
165 assert_eq!(DataType::Int64, "INTBIG".parse()?);
166 assert_eq!(DataType::Int64, "MEDIUMINT".parse()?);
167
168 assert_eq!(DataType::Int64, "BIGINT".parse()?);
169 assert_eq!(DataType::Int64, "UNSIGNED BIG INT".parse()?);
170 assert_eq!(DataType::Int64, "INT8".parse()?);
171
172 assert_eq!(DataType::Text, "CHARACTER(20)".parse()?);
173 assert_eq!(DataType::Text, "NCHAR(55)".parse()?);
174 assert_eq!(DataType::Text, "TEXT".parse()?);
175 assert_eq!(DataType::Text, "CLOB".parse()?);
176
177 assert_eq!(DataType::Blob, "BLOB".parse()?);
178
179 assert_eq!(DataType::Float, "REAL".parse()?);
180 assert_eq!(DataType::Float, "FLOAT".parse()?);
181 assert_eq!(DataType::Float, "DOUBLE PRECISION".parse()?);
182
183 assert_eq!(DataType::Numeric, "NUMERIC".parse()?);
184 assert_eq!(DataType::Numeric, "DECIMAL".parse()?);
185 assert_eq!(DataType::Numeric, "DECIMAL(10,2)".parse()?);
186
187 assert_eq!(DataType::Bool, "BOOLEAN".parse()?);
188 assert_eq!(DataType::Bool, "BOOL".parse()?);
189
190 assert_eq!(DataType::Datetime, "DATETIME".parse()?);
191 assert_eq!(DataType::Time, "TIME".parse()?);
192 assert_eq!(DataType::Date, "DATE".parse()?);
193
194 Ok(())
195}