derive_sql_common/derive/
sqltype.rs

1//! Define data type and their conversion to SQL data type
2//!
3
4#[derive(Debug)]
5pub enum SqlType {
6  Integer,
7  Text,
8  OptionText,
9  Boolean,
10  Float,
11  OptionFloat,
12  DateTime,
13  Date,
14  Unsupported,
15}
16
17impl From<&syn::Field> for SqlType {
18  fn from(f: &syn::Field) -> SqlType {
19    (&f.ty).into()
20  }
21}
22
23impl From<&syn::Type> for SqlType {
24  fn from(t: &syn::Type) -> SqlType {
25    SqlType::from_type(t)
26  }
27}
28
29impl SqlType {
30  pub fn from_type(ty: &syn::Type) -> SqlType {
31    match ty {
32      syn::Type::Path(syn::TypePath { path, .. }) if path.is_ident("String") => SqlType::Text,
33      syn::Type::Path(syn::TypePath { path, .. }) if path.is_ident("i8")     => SqlType::Integer,
34      syn::Type::Path(syn::TypePath { path, .. }) if path.is_ident("u8")     => SqlType::Integer,
35      syn::Type::Path(syn::TypePath { path, .. }) if path.is_ident("i32")    => SqlType::Integer,
36      syn::Type::Path(syn::TypePath { path, .. }) if path.is_ident("u32")    => SqlType::Integer,
37      syn::Type::Path(syn::TypePath { path, .. }) if path.is_ident("i64")    => SqlType::Integer,
38      syn::Type::Path(syn::TypePath { path, .. }) if path.is_ident("u64")    => SqlType::Integer,
39      syn::Type::Path(syn::TypePath { path, .. }) if path.is_ident("usize")  => SqlType::Integer,
40      syn::Type::Path(syn::TypePath { path, .. }) if path.is_ident("bool")   => SqlType::Boolean,
41      syn::Type::Path(syn::TypePath { path, .. }) if path.is_ident("f32")    => SqlType::Float,
42      syn::Type::Path(syn::TypePath { path, .. }) if path.is_ident("f64")    => SqlType::Float,
43      syn::Type::Path(syn::TypePath { path: syn::Path { segments, .. } , .. }) => {
44        match segments.last() {
45          Some(syn::PathSegment { ident, ..}) if ident == "DateTime"  => SqlType::DateTime,
46          Some(syn::PathSegment { ident, ..}) if ident == "NaiveDate" => SqlType::Date,
47          Some(syn::PathSegment { ident, ..}) if ident == "NaiveDateTime" => SqlType::DateTime,
48          Some(syn::PathSegment { ident, 
49            arguments: syn::PathArguments::AngleBracketed( syn::AngleBracketedGenericArguments { args, ..  } )
50          }) if ident == "Option" => {
51            match args.last() {
52              Some(syn::GenericArgument::Type(syn::Type::Path(syn::TypePath { path, .. }))) if path.is_ident("String") => SqlType::OptionText,
53              Some(syn::GenericArgument::Type(syn::Type::Path(syn::TypePath { path, .. }))) if path.is_ident("f32")    => SqlType::OptionFloat,
54              Some(syn::GenericArgument::Type(syn::Type::Path(syn::TypePath { path, .. }))) if path.is_ident("f64")    => SqlType::OptionFloat,
55              _ => SqlType::Unsupported,
56            }
57          },
58          _ => SqlType::Unsupported,
59        }
60      },
61      _ => SqlType::Unsupported,
62    }
63  }
64
65  pub fn to_string(&self) -> &str {
66    match self {
67      SqlType::Integer     => "INTEGER",
68      SqlType::Text        => "TEXT",
69      SqlType::OptionText  => "TEXT",
70      SqlType::Boolean     => "BOOL", // "BIT",
71      SqlType::Float       => "FLOAT",
72      SqlType::OptionFloat => "FLOAT",
73      SqlType::DateTime    => "DATETIME",
74      SqlType::Date        => "DATE",
75      SqlType::Unsupported => "", 
76    }
77  }
78}
79
80#[cfg(test)]
81mod test_sql_type {
82  use super::*;
83  
84  #[test]
85  fn works() -> Result<(), Box<dyn std::error::Error>> {
86    let t = syn::parse_str::<syn::Type>("[u32; 6]")?;
87    let t = SqlType::from_type(&t);
88    assert!(matches!(t, SqlType::Unsupported));
89    assert!(t.to_string().eq(""));
90    
91    for k in ["u32", "usize", "i32"] {
92      let t = syn::parse_str::<syn::Type>(k)?;
93      let t = SqlType::from_type(&t);
94      assert!(matches!(t, SqlType::Integer));
95      assert!(t.to_string().eq("INTEGER"));
96    }
97    
98    let t = syn::parse_str::<syn::Type>("String")?;
99    let t = SqlType::from_type(&t);
100    assert!(matches!(t, SqlType::Text));
101    assert!(t.to_string().eq("TEXT"));
102    
103    let t = syn::parse_str::<syn::Type>("Option<String>")?;
104    let t = SqlType::from_type(&t);
105    assert!(matches!(t, SqlType::OptionText));
106    assert!(t.to_string().eq("TEXT"));
107    
108    let t = syn::parse_str::<syn::Type>("std::option::Option<String>")?;
109    let t = SqlType::from_type(&t);
110    assert!(matches!(t, SqlType::OptionText));
111    assert!(t.to_string().eq("TEXT"));
112    
113    let t = syn::parse_str::<syn::Type>("bool")?;
114    let t = SqlType::from_type(&t);
115    assert!(matches!(t, SqlType::Boolean));
116    assert!(t.to_string().eq("BOOL"));
117
118    let t = syn::parse_str::<syn::Type>("f32")?;
119    let t = SqlType::from_type(&t);
120    assert!(matches!(t, SqlType::Float));
121    assert!(t.to_string().eq("FLOAT"));
122    
123    let t = syn::parse_str::<syn::Type>("DateTime")?;
124    let t = SqlType::from_type(&t);
125    assert!(matches!(t, SqlType::DateTime));
126    assert!(t.to_string().eq("DATETIME"));
127
128    let t = syn::parse_str::<syn::Type>("NaiveDate")?;
129    let t = SqlType::from_type(&t);
130    assert!(matches!(t, SqlType::Date));
131    assert!(t.to_string().eq("DATE"));
132
133    let t = syn::parse_str::<syn::Type>("NaiveDateTime")?;
134    let t = SqlType::from_type(&t);
135    assert!(matches!(t, SqlType::DateTime));
136    assert!(t.to_string().eq("DATETIME"));
137
138    Ok(())
139  }
140}