use std::{
fmt::{self, Display},
str::FromStr,
};
use serde::de::Visitor;
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, serde_repr::Serialize_repr)]
#[repr(u8)]
#[non_exhaustive]
#[derive(Default)]
pub enum Ty {
#[doc(hidden)]
#[default]
Null = 0,
Bool = 1,
TinyInt = 2,
SmallInt = 3,
Int = 4,
BigInt = 5, UTinyInt = 11, USmallInt = 12, UInt = 13, UBigInt = 14, Float = 6, Double = 7, Timestamp = 9, VarChar = 8,
NChar = 10, Json = 15,
VarBinary = 16, #[doc(hidden)]
Decimal, #[doc(hidden)]
Blob, #[doc(hidden)]
MediumBlob,
Geometry, }
impl<'de> serde::Deserialize<'de> for Ty {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct TyVisitor;
impl<'de> Visitor<'de> for TyVisitor {
type Value = Ty;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("invalid TDengine type")
}
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Ty::from_u8(v as u8))
}
fn visit_u8<E>(self, v: u8) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Ty::from_u8(v))
}
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Ty::from_u8(v as u8))
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ty::from_str(v).map_err(<E as serde::de::Error>::custom)
}
fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Ty::Null)
}
fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_any(self)
}
fn visit_unit<E>(self) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Ty::Null)
}
}
deserializer.deserialize_any(TyVisitor)
}
}
impl FromStr for Ty {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"timestamp" => Ok(Ty::Timestamp),
"bool" => Ok(Ty::Bool),
"tinyint" => Ok(Ty::TinyInt),
"smallint" => Ok(Ty::SmallInt),
"int" => Ok(Ty::Int),
"bigint" => Ok(Ty::BigInt),
"tinyint unsigned" => Ok(Ty::UTinyInt),
"smallint unsigned" => Ok(Ty::USmallInt),
"int unsigned" => Ok(Ty::UInt),
"bigint unsigned" => Ok(Ty::UBigInt),
"float" => Ok(Ty::Float),
"double" => Ok(Ty::Double),
"binary" | "varchar" => Ok(Ty::VarChar),
"nchar" => Ok(Ty::NChar),
"json" => Ok(Ty::Json),
"varbinary" => Ok(Ty::VarBinary),
"decimal" => Ok(Ty::Decimal),
"blob" => Ok(Ty::Blob),
"mediumblob" => Ok(Ty::MediumBlob),
"geometry" => Ok(Ty::Geometry),
_ => Err("not a valid data type string"),
}
}
}
impl Ty {
pub const fn is_null(&self) -> bool {
matches!(self, Ty::Null)
}
pub const fn is_var_type(&self) -> bool {
use Ty::*;
matches!(self, VarChar | VarBinary | NChar | Geometry)
}
pub const fn is_json(&self) -> bool {
matches!(self, Ty::Json)
}
pub const fn is_primitive(&self) -> bool {
use Ty::*;
matches!(
self,
Bool | TinyInt
| SmallInt
| Int
| BigInt
| UTinyInt
| USmallInt
| UInt
| UBigInt
| Float
| Double
| Timestamp
| Decimal
)
}
pub const fn fixed_length(&self) -> usize {
use Ty::*;
match self {
Bool => 1,
TinyInt => 1,
SmallInt => 2,
Int => 4,
BigInt => 8,
Float => 4,
Double => 8,
Timestamp => 8,
UTinyInt => 1,
USmallInt => 2,
UInt => 4,
UBigInt => 8,
Decimal => 16,
_ => 0,
}
}
pub const fn name(&self) -> &'static str {
use Ty::*;
match self {
Null => "NULL",
Bool => "BOOL",
TinyInt => "TINYINT",
SmallInt => "SMALLINT",
Int => "INT",
BigInt => "BIGINT",
Float => "FLOAT",
Double => "DOUBLE",
VarChar => "BINARY",
Timestamp => "TIMESTAMP",
NChar => "NCHAR",
UTinyInt => "TINYINT UNSIGNED",
USmallInt => "SMALLINT UNSIGNED",
UInt => "INT UNSIGNED",
UBigInt => "BIGINT UNSIGNED",
Json => "JSON",
VarBinary => "VARBINARY",
Decimal => "DECIMAL",
Blob => "BLOB",
MediumBlob => "MEDIUMBLOB",
Geometry => "GEOMETRY",
}
}
pub const fn lowercase_name(&self) -> &'static str {
use Ty::*;
match self {
Null => "null",
Bool => "bool",
TinyInt => "tinyint",
SmallInt => "smallint",
Int => "int",
BigInt => "bigint",
Float => "float",
Double => "double",
VarChar => "binary",
Timestamp => "timestamp",
NChar => "nchar",
UTinyInt => "tinyint unsigned",
USmallInt => "smallint unsigned",
UInt => "int unsigned",
UBigInt => "bigint unsigned",
Json => "json",
VarBinary => "varbinary",
Decimal => "decimal",
Blob => "blob",
MediumBlob => "mediumblob",
Geometry => "geometry",
}
}
#[inline]
pub(crate) const fn as_variant_str(&self) -> &'static str {
use Ty::*;
macro_rules! _var_str {
($($v:ident) *) => {
match self {
$($v => stringify!($v),) *
}
}
}
_var_str!(
Null Bool TinyInt SmallInt Int BigInt UTinyInt USmallInt UInt UBigInt
Float Double VarChar NChar Timestamp Json VarBinary Decimal Blob MediumBlob Geometry
)
}
#[inline]
const fn from_u8(v: u8) -> Self {
use Ty::*;
match v {
0 => Null,
1 => Bool,
2 => TinyInt,
3 => SmallInt,
4 => Int,
5 => BigInt,
6 => Float,
7 => Double,
8 => VarChar,
9 => Timestamp,
10 => NChar,
11 => UTinyInt,
12 => USmallInt,
13 => UInt,
14 => UBigInt,
15 => Json,
16 => VarBinary,
17 => Decimal,
18 => Blob,
19 => MediumBlob,
20 => Geometry,
_ => panic!("unknown data type"),
}
}
}
impl From<u8> for Ty {
#[inline]
fn from(v: u8) -> Self {
unsafe { std::mem::transmute(v) }
}
}
impl Display for Ty {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name())
}
}
macro_rules! _impl_from_primitive {
($($ty:ty) *) => {
$(
impl From<$ty> for Ty {
#[inline]
fn from(v: $ty) -> Self {
Self::from_u8(v as _)
}
}
)*
}
}
_impl_from_primitive!(i8 i16 i32 i64 u16 u32 u64);