sqlmo/schema/
type.rs

1use crate::to_sql::{Dialect, ToSql};
2use anyhow::Result;
3use std::str::FromStr;
4
5use crate::util::SqlExtension;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub enum Type {
10    Boolean,
11    // integer types
12    I16,
13    I32,
14    I64,
15    // float types
16    F32,
17    F64,
18    // arbitrary precision types
19    Decimal,
20    Numeric(u8, u8),
21    // byte types
22    Bytes,
23    // date types
24    Time,
25    Date,
26    DateTime,
27    NaiveDateTime,
28    Duration,
29    // json types
30    Json,
31    Jsonb,
32    // extension types
33    Uuid,
34    // string types
35    Text,
36    // Array types
37    Array(Box<Type>),
38    Other(String),
39}
40
41impl Type {
42    pub fn lossy_eq(&self, other: &Type) -> bool {
43        use Type::*;
44        match (self, other) {
45            (Other(_), _) => true,
46            (a, b) => a == b,
47        }
48    }
49}
50
51impl FromStr for Type {
52    type Err = anyhow::Error;
53
54    fn from_str(s: &str) -> Result<Self> {
55        use Type::*;
56        let s = match s {
57            "numeric" => Decimal,
58            "bigint" => I64,
59            "int8" => I64,
60            "double precision" => F64,
61            "real" => F32,
62            "bool" => Boolean,
63            "boolean" => Boolean,
64            "date" => Date,
65            "bytea" => Bytes,
66            "timestamp with time zone" => DateTime,
67            "timestamp without time zone" => NaiveDateTime,
68            "interval" => Duration,
69            "json" => Json,
70            "jsonb" => Jsonb,
71            "uuid" => Uuid,
72            "smallint" => I16,
73            "text" => Text,
74            "character varying" => Text,
75            "varchar" => Text,
76            "integer" => I32,
77            "ARRAY" => panic!("Encountered `ARRAY` type when reading data schema from database. ARRAY must be handled separately."),
78            s => Other(s.to_string()),
79        };
80        Ok(s)
81    }
82}
83
84impl ToSql for Type {
85    fn write_sql(&self, buf: &mut String, dialect: Dialect) {
86        use self::Type::*;
87        let s = match self {
88            Boolean => "boolean",
89            I16 => "smallint",
90            I32 => "integer",
91            I64 => "bigint",
92            Bytes => "bytea",
93            Time => "time without time zone",
94            Date => "date",
95            DateTime => "timestamptz",
96            NaiveDateTime => "timestamp without time zone",
97            Duration => "interval",
98            Json => "json",
99            Jsonb => "jsonb",
100            F32 => "real",
101            F64 => "double precision",
102            Decimal => "numeric",
103            Numeric(p, s) => {
104                return buf.push_str(&format!("numeric({}, {})", p, s));
105            }
106            Uuid => "uuid",
107            Text => "character varying",
108            Array(inner) => {
109                buf.push_sql(inner.as_ref(), dialect);
110                if dialect == Dialect::Postgres {
111                    buf.push_str("[]");
112                } else {
113                    buf.push_str(" ARRAY")
114                }
115                return;
116            }
117            Other(z) => {
118                #[cfg(feature = "tracing")]
119                tracing::warn!(z, "Unknown type. SQL may not be valid.");
120                buf.push_str("/* Unknown type: ");
121                buf.push_str(z);
122                buf.push_str(" */");
123                return;
124            }
125        };
126        buf.push_str(s);
127    }
128}
129
130#[cfg(test)]
131mod test {
132    use super::*;
133
134    #[test]
135    fn test_numeric() {
136        let s = "numeric";
137        let t = Type::from_str(s).unwrap();
138        assert_eq!(t, Type::Decimal);
139    }
140}