use serde::{Deserialize, Serialize};
use crate::value::AttributeValue;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ValueType {
String,
Long,
Double,
Boolean,
Date,
#[serde(rename = "datetime")]
DateTime,
#[serde(rename = "datetime-tz")]
DateTimeTz,
Decimal,
Duration,
}
impl ValueType {
pub const fn as_str(&self) -> &'static str {
match self {
Self::String => "string",
Self::Long => "long",
Self::Double => "double",
Self::Boolean => "boolean",
Self::Date => "date",
Self::DateTime => "datetime",
Self::DateTimeTz => "datetime-tz",
Self::Decimal => "decimal",
Self::Duration => "duration",
}
}
pub fn parse(s: &str) -> Option<Self> {
match s {
"string" => Some(Self::String),
"long" => Some(Self::Long),
"double" => Some(Self::Double),
"boolean" => Some(Self::Boolean),
"date" => Some(Self::Date),
"datetime" => Some(Self::DateTime),
"datetime-tz" => Some(Self::DateTimeTz),
"decimal" => Some(Self::Decimal),
"duration" => Some(Self::Duration),
_ => None,
}
}
}
impl std::fmt::Display for ValueType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
pub trait TypeBridgeAttribute: Clone + Send + Sync + 'static {
const ATTR_NAME: &'static str;
const VALUE_TYPE: &'static str;
const VALUE_TYPE_ENUM: ValueType;
fn to_value(&self) -> AttributeValue;
fn from_value(value: &AttributeValue) -> Option<Self>;
}
#[macro_export]
macro_rules! define_attribute {
($name:ident, $attr_name:expr, "string") => {
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct $name(pub String);
impl $crate::TypeBridgeAttribute for $name {
const ATTR_NAME: &'static str = $attr_name;
const VALUE_TYPE: &'static str = "string";
const VALUE_TYPE_ENUM: $crate::ValueType = $crate::ValueType::String;
fn to_value(&self) -> $crate::AttributeValue {
$crate::AttributeValue::String(self.0.clone())
}
fn from_value(value: &$crate::AttributeValue) -> Option<Self> {
match value {
$crate::AttributeValue::String(s) => Some($name(s.clone())),
_ => None,
}
}
}
};
($name:ident, $attr_name:expr, "long") => {
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct $name(pub i64);
impl $crate::TypeBridgeAttribute for $name {
const ATTR_NAME: &'static str = $attr_name;
const VALUE_TYPE: &'static str = "long";
const VALUE_TYPE_ENUM: $crate::ValueType = $crate::ValueType::Long;
fn to_value(&self) -> $crate::AttributeValue {
$crate::AttributeValue::Long(self.0)
}
fn from_value(value: &$crate::AttributeValue) -> Option<Self> {
match value {
$crate::AttributeValue::Long(n) => Some($name(*n)),
_ => None,
}
}
}
};
($name:ident, $attr_name:expr, "integer") => {
$crate::define_attribute!($name, $attr_name, "long");
};
($name:ident, $attr_name:expr, "int") => {
$crate::define_attribute!($name, $attr_name, "long");
};
($name:ident, $attr_name:expr, "double") => {
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct $name(pub f64);
impl $crate::TypeBridgeAttribute for $name {
const ATTR_NAME: &'static str = $attr_name;
const VALUE_TYPE: &'static str = "double";
const VALUE_TYPE_ENUM: $crate::ValueType = $crate::ValueType::Double;
fn to_value(&self) -> $crate::AttributeValue {
$crate::AttributeValue::Double(self.0)
}
fn from_value(value: &$crate::AttributeValue) -> Option<Self> {
match value {
$crate::AttributeValue::Double(n) => Some($name(*n)),
_ => None,
}
}
}
};
($name:ident, $attr_name:expr, "boolean") => {
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct $name(pub bool);
impl $crate::TypeBridgeAttribute for $name {
const ATTR_NAME: &'static str = $attr_name;
const VALUE_TYPE: &'static str = "boolean";
const VALUE_TYPE_ENUM: $crate::ValueType = $crate::ValueType::Boolean;
fn to_value(&self) -> $crate::AttributeValue {
$crate::AttributeValue::Boolean(self.0)
}
fn from_value(value: &$crate::AttributeValue) -> Option<Self> {
match value {
$crate::AttributeValue::Boolean(b) => Some($name(*b)),
_ => None,
}
}
}
};
($name:ident, $attr_name:expr, "bool") => {
$crate::define_attribute!($name, $attr_name, "boolean");
};
($name:ident, $attr_name:expr, "date") => {
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct $name(pub String);
impl $crate::TypeBridgeAttribute for $name {
const ATTR_NAME: &'static str = $attr_name;
const VALUE_TYPE: &'static str = "date";
const VALUE_TYPE_ENUM: $crate::ValueType = $crate::ValueType::Date;
fn to_value(&self) -> $crate::AttributeValue {
$crate::AttributeValue::Date(self.0.clone())
}
fn from_value(value: &$crate::AttributeValue) -> Option<Self> {
match value {
$crate::AttributeValue::Date(s) => Some($name(s.clone())),
_ => None,
}
}
}
};
($name:ident, $attr_name:expr, "datetime") => {
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct $name(pub String);
impl $crate::TypeBridgeAttribute for $name {
const ATTR_NAME: &'static str = $attr_name;
const VALUE_TYPE: &'static str = "datetime";
const VALUE_TYPE_ENUM: $crate::ValueType = $crate::ValueType::DateTime;
fn to_value(&self) -> $crate::AttributeValue {
$crate::AttributeValue::DateTime(self.0.clone())
}
fn from_value(value: &$crate::AttributeValue) -> Option<Self> {
match value {
$crate::AttributeValue::DateTime(s) => Some($name(s.clone())),
_ => None,
}
}
}
};
($name:ident, $attr_name:expr, "datetime-tz") => {
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct $name(pub String);
impl $crate::TypeBridgeAttribute for $name {
const ATTR_NAME: &'static str = $attr_name;
const VALUE_TYPE: &'static str = "datetime-tz";
const VALUE_TYPE_ENUM: $crate::ValueType = $crate::ValueType::DateTimeTz;
fn to_value(&self) -> $crate::AttributeValue {
$crate::AttributeValue::DateTimeTZ(self.0.clone())
}
fn from_value(value: &$crate::AttributeValue) -> Option<Self> {
match value {
$crate::AttributeValue::DateTimeTZ(s) => Some($name(s.clone())),
_ => None,
}
}
}
};
($name:ident, $attr_name:expr, "decimal") => {
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct $name(pub String);
impl $crate::TypeBridgeAttribute for $name {
const ATTR_NAME: &'static str = $attr_name;
const VALUE_TYPE: &'static str = "decimal";
const VALUE_TYPE_ENUM: $crate::ValueType = $crate::ValueType::Decimal;
fn to_value(&self) -> $crate::AttributeValue {
$crate::AttributeValue::Decimal(self.0.clone())
}
fn from_value(value: &$crate::AttributeValue) -> Option<Self> {
match value {
$crate::AttributeValue::Decimal(s) => Some($name(s.clone())),
_ => None,
}
}
}
};
($name:ident, $attr_name:expr, "duration") => {
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct $name(pub String);
impl $crate::TypeBridgeAttribute for $name {
const ATTR_NAME: &'static str = $attr_name;
const VALUE_TYPE: &'static str = "duration";
const VALUE_TYPE_ENUM: $crate::ValueType = $crate::ValueType::Duration;
fn to_value(&self) -> $crate::AttributeValue {
$crate::AttributeValue::Duration(self.0.clone())
}
fn from_value(value: &$crate::AttributeValue) -> Option<Self> {
match value {
$crate::AttributeValue::Duration(s) => Some($name(s.clone())),
_ => None,
}
}
}
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn value_type_serde_roundtrip() {
for vt in [
ValueType::String,
ValueType::Long,
ValueType::Double,
ValueType::Boolean,
ValueType::Date,
ValueType::DateTime,
ValueType::DateTimeTz,
ValueType::Decimal,
ValueType::Duration,
] {
let json = serde_json::to_string(&vt).unwrap();
let parsed: ValueType = serde_json::from_str(&json).unwrap();
assert_eq!(vt, parsed);
}
}
#[test]
fn value_type_serde_names() {
assert_eq!(
serde_json::to_string(&ValueType::String).unwrap(),
"\"string\""
);
assert_eq!(serde_json::to_string(&ValueType::Long).unwrap(), "\"long\"");
assert_eq!(
serde_json::to_string(&ValueType::DateTime).unwrap(),
"\"datetime\""
);
assert_eq!(
serde_json::to_string(&ValueType::DateTimeTz).unwrap(),
"\"datetime-tz\""
);
}
}