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 #[allow(dead_code)]
33 Numeric,
34
35 Bool,
37 Int64,
38 Date,
39 Time,
40 Datetime,
41}
42
43#[derive(Debug, Clone, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
45pub struct SqliteTypeInfo(pub(crate) DataType);
46
47impl SqliteTypeInfo {
48 pub fn null() -> Self {
49 SqliteTypeInfo(DataType::Null)
50 }
51}
52
53impl Display for SqliteTypeInfo {
54 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
55 f.pad(self.name())
56 }
57}
58
59impl SqliteTypeInfo {
60 pub fn is_null(&self) -> bool {
61 matches!(self.0, DataType::Null)
62 }
63
64 pub fn name(&self) -> &str {
65 match self.0 {
66 DataType::Null => "NULL",
67 DataType::Text => "TEXT",
68 DataType::Float => "REAL",
69 DataType::Blob => "BLOB",
70 DataType::Int | DataType::Int64 => "INTEGER",
71 DataType::Numeric => "NUMERIC",
72
73 DataType::Bool => "BOOLEAN",
75 DataType::Date => "DATE",
76 DataType::Time => "TIME",
77 DataType::Datetime => "DATETIME",
78 }
79 }
80}
81
82impl DataType {
83 pub(crate) fn from_code(code: c_int) -> Self {
84 match code {
85 SQLITE_INTEGER => DataType::Int,
86 SQLITE_FLOAT => DataType::Float,
87 SQLITE_BLOB => DataType::Blob,
88 SQLITE_NULL => DataType::Null,
89 SQLITE_TEXT => DataType::Text,
90
91 _ => panic!("unknown data type code {}", code),
93 }
94 }
95}
96
97impl FromStr for DataType {
101 type Err = Error;
102
103 fn from_str(s: &str) -> Result<Self, Self::Err> {
104 let s = s.to_ascii_lowercase();
105 Ok(match &*s {
106 "int4" => DataType::Int,
107 "int8" => DataType::Int64,
108 "boolean" | "bool" => DataType::Bool,
109
110 "date" => DataType::Date,
111 "time" => DataType::Time,
112 "datetime" | "timestamp" => DataType::Datetime,
113
114 _ if s.contains("int") => DataType::Int64,
115
116 _ if s.contains("char") || s.contains("clob") || s.contains("text") => DataType::Text,
117
118 _ if s.contains("blob") => DataType::Blob,
119
120 _ if s.contains("real") || s.contains("floa") || s.contains("doub") => DataType::Float,
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::Bool, "BOOLEAN".parse()?);
183 assert_eq!(DataType::Bool, "BOOL".parse()?);
184
185 assert_eq!(DataType::Datetime, "DATETIME".parse()?);
186 assert_eq!(DataType::Time, "TIME".parse()?);
187 assert_eq!(DataType::Date, "DATE".parse()?);
188
189 Ok(())
190}