use crate::{ast::parser::ident, Error};
use pom::parser::*;
use serde::{Deserialize, Serialize};
use sqlparser::ast as sql;
use std::fmt;
#[derive(PartialEq, Debug, Clone, Hash, Eq, Serialize, Deserialize)]
pub enum DataType {
Bool,
S8,
S16,
S32,
S64,
F32,
F64,
U8,
U16,
U32,
U64,
I8,
I16,
I32,
I64,
Uuid,
UuidRand,
UuidSlug,
Utc,
Text,
Ident,
Url,
Json,
Bytes,
}
impl DataType {
pub fn all() -> Vec<DataType> {
vec![
DataType::Bool,
DataType::S8,
DataType::S16,
DataType::S32,
DataType::S64,
DataType::F32,
DataType::F64,
DataType::U8,
DataType::U16,
DataType::U32,
DataType::U64,
DataType::I8,
DataType::I16,
DataType::I32,
DataType::I64,
DataType::Uuid,
DataType::UuidRand,
DataType::UuidSlug,
DataType::Utc,
DataType::Text,
DataType::Ident,
DataType::Url,
DataType::Json,
DataType::Bytes,
]
}
fn match_data_type(dt: &str) -> Result<Self, Error> {
match dt {
"bool" => Ok(DataType::Bool),
"s8" => Ok(DataType::S8),
"s16" => Ok(DataType::S16),
"s32" => Ok(DataType::S32),
"s64" => Ok(DataType::S64),
"u8" => Ok(DataType::U8),
"u16" => Ok(DataType::U16),
"u32" => Ok(DataType::U32),
"u64" => Ok(DataType::U64),
"i8" => Ok(DataType::I8),
"i16" => Ok(DataType::I16),
"i32" => Ok(DataType::I32),
"i64" => Ok(DataType::I64),
"f32" => Ok(DataType::F32),
"f64" => Ok(DataType::F64),
"uuid" => Ok(DataType::Uuid),
"uuid_rand" => Ok(DataType::UuidRand),
"uuid_slug" => Ok(DataType::UuidSlug),
"utc" => Ok(DataType::Utc),
"text" => Ok(DataType::Text),
"ident" => Ok(DataType::Ident),
"url" => Ok(DataType::Url),
"json" => Ok(DataType::Json),
"bytes" => Ok(DataType::Bytes),
_ => Err(Error::InvalidDataType(dt.to_string())),
}
}
pub fn is_numeric(&self) -> bool {
match self {
DataType::S8
| DataType::S16
| DataType::S32
| DataType::S64
| DataType::F32
| DataType::F64
| DataType::U8
| DataType::U16
| DataType::U32
| DataType::U64
| DataType::I8
| DataType::I16
| DataType::I32
| DataType::I64 => true,
_ => false,
}
}
pub fn is_autogenerate(&self) -> bool {
match self {
DataType::S8 | DataType::S16 | DataType::S32 | DataType::S64 => {
true
}
DataType::UuidRand | DataType::UuidSlug => true,
_ => false,
}
}
}
impl fmt::Display for DataType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let display = match self {
DataType::Bool => "bool",
DataType::S8 => "s8",
DataType::S16 => "s16",
DataType::S32 => "s32",
DataType::S64 => "s64",
DataType::F32 => "f32",
DataType::F64 => "f64",
DataType::U8 => "u8",
DataType::U16 => "u16",
DataType::U32 => "u32",
DataType::U64 => "u64",
DataType::I8 => "i8",
DataType::I16 => "i16",
DataType::I32 => "i32",
DataType::I64 => "i64",
DataType::Uuid => "uuid",
DataType::UuidRand => "uuid_rand",
DataType::UuidSlug => "uuid_slug",
DataType::Utc => "utc",
DataType::Text => "text",
DataType::Ident => "ident",
DataType::Url => "url",
DataType::Json => "json",
DataType::Bytes => "bytes",
};
write!(f, "{}", display)
}
}
pub fn data_type<'a>() -> Parser<'a, char, DataType> {
ident().convert(|v| DataType::match_data_type(&v))
}
impl Into<sql::DataType> for &DataType {
fn into(self) -> sql::DataType {
match self {
DataType::Bool => sql::DataType::Boolean,
DataType::S8 => sql::DataType::SmallInt(None),
DataType::S16 => sql::DataType::SmallInt(None),
DataType::S32 => sql::DataType::Int(None),
DataType::S64 => sql::DataType::BigInt(None),
DataType::F32 => sql::DataType::Float(None),
DataType::F64 => sql::DataType::Float(None),
DataType::U8 => sql::DataType::SmallInt(None),
DataType::U16 => sql::DataType::SmallInt(None),
DataType::U32 => sql::DataType::Int(None),
DataType::U64 => sql::DataType::BigInt(None),
DataType::I8 => sql::DataType::SmallInt(None),
DataType::I16 => sql::DataType::SmallInt(None),
DataType::I32 => sql::DataType::Int(None),
DataType::I64 => sql::DataType::BigInt(None),
DataType::Uuid => sql::DataType::Uuid,
DataType::UuidRand => sql::DataType::Uuid,
DataType::UuidSlug => sql::DataType::Text,
DataType::Utc => {
sql::DataType::Timestamp(None, sql::TimezoneInfo::None)
}
DataType::Text => sql::DataType::Text,
DataType::Ident => sql::DataType::Text,
DataType::Url => sql::DataType::Text,
DataType::Json => sql::DataType::JSON,
DataType::Bytes => sql::DataType::Bytea,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::parser::utils::*;
#[test]
fn test_data_type() {
let input = to_chars("s32");
let ret = data_type().parse(&input).expect("must be parsed");
println!("{:#?}", ret);
assert_eq!(ret, DataType::S32);
}
#[test]
fn test_invalid_data_type() {
let input = to_chars("x32");
let ret = data_type().parse(&input);
println!("{:#?}", ret);
assert!(ret.is_err());
}
#[test]
fn test_invalid_more_data_type() {
let input = to_chars("serial32");
let ret = data_type().parse(&input);
println!("{:#?}", ret);
assert!(ret.is_err());
let err = ret.err().unwrap();
println!("{}", err);
assert!(err.to_string().contains(r#"InvalidDataType("serial32")"#))
}
#[test]
fn all_data_types() {
let all = [
"bool",
"s8",
"s16",
"s32",
"s64",
"u8",
"u16",
"u32",
"u64",
"i8",
"i16",
"i32",
"i64",
"f32",
"f64",
"uuid",
"uuid_rand",
"uuid_slug",
"utc",
"text",
"ident",
"url",
];
for d in all.iter() {
println!("trying {}...", d);
let input = to_chars(d);
let ret = data_type().parse(&input).expect("must be parsed");
println!("{} = {:#?}", d, ret);
}
}
}