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 _ => panic!("unknown data type code {}", code),
91 }
92 }
93}
94
95impl FromStr for DataType {
99 type Err = Error;
100
101 fn from_str(s: &str) -> Result<Self, Self::Err> {
102 let s = s.to_ascii_lowercase();
103 Ok(match &*s {
104 "int4" => DataType::Int,
105 "int8" => DataType::Int64,
106 "boolean" | "bool" => DataType::Bool,
107
108 "date" => DataType::Date,
109 "time" => DataType::Time,
110 "datetime" | "timestamp" => DataType::Datetime,
111
112 _ if s.contains("int") => DataType::Int64,
113
114 _ if s.contains("char") || s.contains("clob") || s.contains("text") => DataType::Text,
115
116 _ if s.contains("blob") => DataType::Blob,
117
118 _ if s.contains("real") || s.contains("floa") || s.contains("doub") => DataType::Float,
119
120 _ if s.contains("num") || s.contains("dec") => DataType::Numeric,
121
122 _ => {
123 return Err(format!("unknown type: `{}`", s).into());
124 }
125 })
126 }
127}
128
129impl Type for Value {
130 fn type_info(&self) -> SqliteTypeInfo {
131 match self {
132 Value::Null => SqliteTypeInfo::null(),
133 Value::Bool(_) => SqliteTypeInfo(DataType::Bool),
134 Value::I32(_) => SqliteTypeInfo(DataType::Int),
135 Value::I64(_) => SqliteTypeInfo(DataType::Int64),
136 Value::U32(_) => SqliteTypeInfo(DataType::Int),
137 Value::U64(_) => SqliteTypeInfo(DataType::Int64),
138 Value::F32(_) => SqliteTypeInfo(DataType::Float),
139 Value::F64(_) => SqliteTypeInfo(DataType::Float),
140 Value::String(_) => SqliteTypeInfo(DataType::Text),
141 Value::Binary(_) => SqliteTypeInfo(DataType::Blob),
142 Value::Array(_) => SqliteTypeInfo(DataType::Null),
143 Value::Map(_) => SqliteTypeInfo(DataType::Null),
144 Value::Ext(t, _) => match *t {
145 "Date" => SqliteTypeInfo(DataType::Text),
146 "DateTime" => SqliteTypeInfo(DataType::Text),
147 "Time" => SqliteTypeInfo(DataType::Text),
148 "Timestamp" => SqliteTypeInfo(DataType::Int64),
149 "Decimal" => SqliteTypeInfo(DataType::Numeric),
150 "Json" => SqliteTypeInfo(DataType::Blob),
151 "Uuid" => SqliteTypeInfo(DataType::Text),
152 _ => SqliteTypeInfo(DataType::Null),
153 },
154 }
155 }
156}
157
158#[test]
159fn test_data_type_from_str() -> Result<(), Error> {
160 assert_eq!(DataType::Int, "INT4".parse()?);
161
162 assert_eq!(DataType::Int64, "INT".parse()?);
163 assert_eq!(DataType::Int64, "INTEGER".parse()?);
164 assert_eq!(DataType::Int64, "INTBIG".parse()?);
165 assert_eq!(DataType::Int64, "MEDIUMINT".parse()?);
166
167 assert_eq!(DataType::Int64, "BIGINT".parse()?);
168 assert_eq!(DataType::Int64, "UNSIGNED BIG INT".parse()?);
169 assert_eq!(DataType::Int64, "INT8".parse()?);
170
171 assert_eq!(DataType::Text, "CHARACTER(20)".parse()?);
172 assert_eq!(DataType::Text, "NCHAR(55)".parse()?);
173 assert_eq!(DataType::Text, "TEXT".parse()?);
174 assert_eq!(DataType::Text, "CLOB".parse()?);
175
176 assert_eq!(DataType::Blob, "BLOB".parse()?);
177
178 assert_eq!(DataType::Float, "REAL".parse()?);
179 assert_eq!(DataType::Float, "FLOAT".parse()?);
180 assert_eq!(DataType::Float, "DOUBLE PRECISION".parse()?);
181
182 assert_eq!(DataType::Numeric, "NUMERIC".parse()?);
183 assert_eq!(DataType::Numeric, "DECIMAL".parse()?);
184 assert_eq!(DataType::Numeric, "DECIMAL(10,2)".parse()?);
185
186 assert_eq!(DataType::Bool, "BOOLEAN".parse()?);
187 assert_eq!(DataType::Bool, "BOOL".parse()?);
188
189 assert_eq!(DataType::Datetime, "DATETIME".parse()?);
190 assert_eq!(DataType::Time, "TIME".parse()?);
191 assert_eq!(DataType::Date, "DATE".parse()?);
192
193 Ok(())
194}