database_reflection/reflection/
datatypes.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use std::str::FromStr;
4
5#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
6pub enum SqlSigned {
7    #[default]
8    Signed,
9    Unsigned,
10}
11
12#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
13#[serde(rename_all = "snake_case")]
14/// Basic SQL datatypes, defaults to VARCHAR(45)
15pub enum SqlDatatype {
16    Tinyint(u32, SqlSigned),
17    Smallint(u32, SqlSigned),
18    Mediumint(u32, SqlSigned),
19    Int(u32, SqlSigned),
20    Bigint(u32, SqlSigned),
21    Float(u32, u32, SqlSigned),
22    Double(u32, u32, SqlSigned),
23    Decimal(u32, u32, SqlSigned),
24
25    Date,
26    Time,
27    Datetime,
28    Timestamp,
29
30    Char(u32),
31    Varchar(u32),
32    Text(u32),
33
34    Binary(u32),
35    Varbinary(u32),
36
37    Enum(Vec<String>),
38    Set(Vec<String>),
39}
40
41#[derive(Debug, PartialEq, Eq)]
42pub struct ParseDatatypeError;
43
44impl SqlDatatype {
45    /// Get datatype sign
46    pub fn sign(&self) -> Option<SqlSigned> {
47        match self {
48            SqlDatatype::Tinyint(_, sign) => Some(sign.clone()),
49            SqlDatatype::Smallint(_, sign) => Some(sign.clone()),
50            SqlDatatype::Mediumint(_, sign) => Some(sign.clone()),
51            SqlDatatype::Int(_, sign) => Some(sign.clone()),
52            SqlDatatype::Bigint(_, sign) => Some(sign.clone()),
53            SqlDatatype::Float(_, _, sign) => Some(sign.clone()),
54            SqlDatatype::Double(_, _, sign) => Some(sign.clone()),
55            SqlDatatype::Decimal(_, _, sign) => Some(sign.clone()),
56            _ => None,
57        }
58    }
59
60    /// Get datatype length
61    #[allow(clippy::len_without_is_empty)]
62    pub fn len(&self) -> Option<u32> {
63        match self {
64            SqlDatatype::Tinyint(len, _) => Some(*len),
65            SqlDatatype::Smallint(len, _) => Some(*len),
66            SqlDatatype::Mediumint(len, _) => Some(*len),
67            SqlDatatype::Int(len, _) => Some(*len),
68            SqlDatatype::Bigint(len, _) => Some(*len),
69            SqlDatatype::Float(len, _, _) => Some(*len),
70            SqlDatatype::Double(len, _, _) => Some(*len),
71            SqlDatatype::Decimal(len, _, _) => Some(*len),
72            SqlDatatype::Char(len) => Some(*len),
73            SqlDatatype::Varchar(len) => Some(*len),
74            SqlDatatype::Text(len) => Some(*len),
75            SqlDatatype::Binary(len) => Some(*len),
76            SqlDatatype::Varbinary(len) => Some(*len),
77            SqlDatatype::Enum(v) => Some(v.len() as u32),
78            SqlDatatype::Set(v) => Some(v.len() as u32),
79            _ => None,
80        }
81    }
82
83    /// Check if column datatype is one of the character types
84    pub fn is_text(&self) -> bool {
85        matches!(
86            self,
87            SqlDatatype::Text(_) | SqlDatatype::Varchar(_) | SqlDatatype::Char(_)
88        )
89    }
90}
91
92impl Default for SqlDatatype {
93    fn default() -> Self {
94        SqlDatatype::Varchar(45)
95    }
96}
97
98impl FromStr for SqlDatatype {
99    type Err = ParseDatatypeError;
100
101    fn from_str(s: &str) -> Result<Self, Self::Err> {
102        SqlDatatype::try_from(s)
103    }
104}
105
106impl TryFrom<&str> for SqlDatatype {
107    type Error = ParseDatatypeError;
108
109    fn try_from(value: &str) -> Result<Self, Self::Error> {
110        return if value.contains('(') && value.contains(')') {
111            // type with length information
112
113            let is_signed = if value.contains("unsigned") {
114                SqlSigned::Unsigned
115            } else {
116                SqlSigned::Signed
117            };
118
119            let (type_group, type_length) = value
120                .split_once('(')
121                .map(|s| (s.0, s.1.rsplit_once(')').unwrap_or(("", "")).0))
122                .ok_or(ParseDatatypeError)?;
123
124            match type_group {
125                "set" | "enum" => {
126                    let trim_match: &[_] = &['"', '\''];
127                    let options = type_length
128                        .split(',')
129                        .map(|s| s.trim_matches(trim_match).to_string())
130                        .collect();
131
132                    return if type_group == "set" {
133                        Ok(SqlDatatype::Set(options))
134                    } else {
135                        Ok(SqlDatatype::Enum(options))
136                    };
137                }
138                "float" | "double" | "decimal" => {
139                    let (left, right) = {
140                        let v = type_length
141                            .splitn(2, ',')
142                            .map(|s| s.parse::<u32>().unwrap_or_default())
143                            .collect::<Vec<u32>>();
144
145                        (
146                            *v.first().ok_or(ParseDatatypeError)?,
147                            *v.last().ok_or(ParseDatatypeError)?,
148                        )
149                    };
150
151                    if type_group == "float" {
152                        Ok(SqlDatatype::Float(left, right, is_signed))
153                    } else if type_group == "double" {
154                        Ok(SqlDatatype::Double(left, right, is_signed))
155                    } else {
156                        Ok(SqlDatatype::Decimal(left, right, is_signed))
157                    }
158                }
159                _ => {
160                    if let Ok(len_val) = type_length.parse::<u32>() {
161                        match type_group {
162                            "tinyint" => Ok(SqlDatatype::Tinyint(len_val, is_signed)),
163                            "int" => Ok(SqlDatatype::Int(len_val, is_signed)),
164                            "smallint" => Ok(SqlDatatype::Smallint(len_val, is_signed)),
165                            "mediumint" => Ok(SqlDatatype::Mediumint(len_val, is_signed)),
166                            "bigint" => Ok(SqlDatatype::Bigint(len_val, is_signed)),
167
168                            "char" => Ok(SqlDatatype::Char(len_val)),
169                            "varchar" => Ok(SqlDatatype::Varchar(len_val)),
170                            "text" => Ok(SqlDatatype::Text(len_val)), // can be without length
171
172                            "binary" => Ok(SqlDatatype::Binary(len_val)),
173                            "varbinary" => Ok(SqlDatatype::Varbinary(len_val)),
174
175                            _ => Err(ParseDatatypeError),
176                        }
177                    } else {
178                        Err(ParseDatatypeError)
179                    }
180                }
181            }
182        } else {
183            // fixed length type
184
185            match value {
186                "text" => Ok(SqlDatatype::Text(65535)),
187                "date" => Ok(SqlDatatype::Date),
188                "time" => Ok(SqlDatatype::Time),
189                "datetime" => Ok(SqlDatatype::Datetime),
190                "timestamp" => Ok(SqlDatatype::Timestamp),
191                _ => Err(ParseDatatypeError),
192            }
193        };
194    }
195}
196
197#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
198#[serde(rename_all = "snake_case")]
199/// Default value container
200pub enum DefaultValue {
201    #[default]
202    Null,
203    Value(Value),
204}
205
206#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
207#[serde(rename_all = "snake_case")]
208/// JS Number
209pub enum JsonNumber {
210    #[default]
211    Number,
212    BigInt,
213    Int,
214    Float,
215}
216
217#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
218#[serde(rename_all = "snake_case")]
219/// JS String
220pub enum JsonString {
221    #[default]
222    String,
223    Datetime,
224    Date,
225    Time,
226    Json,
227}
228
229#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
230#[serde(rename_all = "snake_case")]
231/// Basic JSON datatypes, defaults to string
232pub enum JsonDatatype {
233    Number(JsonNumber),
234    String(JsonString, Option<u32>),
235    Boolean,
236    Array(Vec<String>),
237    Object(Value),
238}
239
240impl Default for JsonDatatype {
241    fn default() -> Self {
242        JsonDatatype::String(JsonString::String, None)
243    }
244}
245
246impl From<&SqlDatatype> for JsonDatatype {
247    fn from(value: &SqlDatatype) -> Self {
248        match value {
249            SqlDatatype::Tinyint(1, SqlSigned::Unsigned) => JsonDatatype::Boolean,
250            SqlDatatype::Int(_, _)
251            | SqlDatatype::Smallint(_, _)
252            | SqlDatatype::Mediumint(_, _)
253            | SqlDatatype::Tinyint(_, _) => JsonDatatype::Number(JsonNumber::Int),
254            SqlDatatype::Float(_, _, _)
255            | SqlDatatype::Double(_, _, _)
256            | SqlDatatype::Decimal(_, _, _) => JsonDatatype::Number(JsonNumber::Float),
257            SqlDatatype::Bigint(_, _) => JsonDatatype::Number(JsonNumber::BigInt),
258            SqlDatatype::Date => JsonDatatype::String(JsonString::Date, Some(10)),
259            SqlDatatype::Time => JsonDatatype::String(JsonString::Time, Some(8)),
260            SqlDatatype::Datetime | SqlDatatype::Timestamp => {
261                JsonDatatype::String(JsonString::Datetime, Some(20))
262            }
263            SqlDatatype::Char(length)
264            | SqlDatatype::Varchar(length)
265            | SqlDatatype::Text(length) => JsonDatatype::String(JsonString::String, Some(*length)),
266            SqlDatatype::Binary(length) | SqlDatatype::Varbinary(length) => {
267                JsonDatatype::String(JsonString::String, Some(*length))
268            }
269            SqlDatatype::Enum(options) | SqlDatatype::Set(options) => {
270                JsonDatatype::Array(options.clone())
271            }
272        }
273    }
274}
275
276#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
277/// Basic Rust type as string
278pub struct RustDatatype(pub String, pub Option<u32>);
279
280pub const RUST_TYPE_STRING: &str = "String";
281//pub const RUST_TYPE_USIZE: &str = "usize";
282//pub const RUST_TYPE_ISIZE: &str = "isize";
283pub const RUST_TYPE_I32: &str = "i32";
284pub const RUST_TYPE_I64: &str = "i64";
285pub const RUST_TYPE_U32: &str = "u32";
286pub const RUST_TYPE_U64: &str = "u64";
287pub const RUST_TYPE_F32: &str = "f32";
288pub const RUST_TYPE_F64: &str = "f64";
289pub const RUST_TYPE_BOOL: &str = "bool";
290pub const RUST_TYPE_VEC: &str = "Vec<String>";
291
292impl Default for RustDatatype {
293    fn default() -> Self {
294        RustDatatype(RUST_TYPE_STRING.to_string(), None)
295    }
296}
297
298impl From<&SqlDatatype> for RustDatatype {
299    fn from(value: &SqlDatatype) -> Self {
300        match value {
301            SqlDatatype::Tinyint(1, _) => {
302                // SqlSigned::Unsigned, pattern removed to patch database bugs by code...
303                RustDatatype(RUST_TYPE_BOOL.to_string(), None)
304            }
305            SqlDatatype::Int(len, SqlSigned::Unsigned)
306            | SqlDatatype::Smallint(len, SqlSigned::Unsigned)
307            | SqlDatatype::Mediumint(len, SqlSigned::Unsigned)
308            | SqlDatatype::Tinyint(len, SqlSigned::Unsigned) => {
309                RustDatatype(RUST_TYPE_U32.to_string(), Some(*len))
310            }
311            SqlDatatype::Int(len, SqlSigned::Signed)
312            | SqlDatatype::Smallint(len, SqlSigned::Signed)
313            | SqlDatatype::Mediumint(len, SqlSigned::Signed)
314            | SqlDatatype::Tinyint(len, SqlSigned::Signed) => {
315                RustDatatype(RUST_TYPE_I32.to_string(), Some(*len))
316            }
317            SqlDatatype::Float(_, fp, _) => RustDatatype(RUST_TYPE_F32.to_string(), Some(*fp)),
318            SqlDatatype::Double(_, fp, _) | SqlDatatype::Decimal(_, fp, _) => {
319                RustDatatype(RUST_TYPE_F64.to_string(), Some(*fp))
320            }
321            SqlDatatype::Bigint(len, SqlSigned::Unsigned) => {
322                RustDatatype(RUST_TYPE_U64.to_string(), Some(*len))
323            }
324            SqlDatatype::Bigint(len, SqlSigned::Signed) => {
325                RustDatatype(RUST_TYPE_I64.to_string(), Some(*len))
326            }
327            SqlDatatype::Date => RustDatatype(RUST_TYPE_STRING.to_string(), Some(10)),
328            SqlDatatype::Time => RustDatatype(RUST_TYPE_STRING.to_string(), Some(8)),
329            SqlDatatype::Datetime | SqlDatatype::Timestamp => {
330                RustDatatype(RUST_TYPE_STRING.to_string(), Some(20))
331            }
332            SqlDatatype::Char(length)
333            | SqlDatatype::Varchar(length)
334            | SqlDatatype::Text(length) => {
335                RustDatatype(RUST_TYPE_STRING.to_string(), Some(*length))
336            }
337            SqlDatatype::Binary(length) | SqlDatatype::Varbinary(length) => {
338                RustDatatype(RUST_TYPE_STRING.to_string(), Some(*length))
339            }
340            SqlDatatype::Enum(options) => RustDatatype(
341                RUST_TYPE_STRING.to_string(),
342                Some(options.iter().fold(0u32, |ac, c| {
343                    if c.len() as u32 > ac {
344                        c.len() as u32
345                    } else {
346                        ac
347                    }
348                })),
349            ),
350            SqlDatatype::Set(options) => {
351                RustDatatype(RUST_TYPE_VEC.to_string(), Some(options.len() as u32))
352            }
353        }
354    }
355}