use std::collections::{BTreeMap, HashMap};
use chrono::{SecondsFormat, Utc};
use serde::{Deserialize, Serialize};
use crate::expr::Expr;
pub type ParamValue = PropertyValue;
pub type ParamObject = BTreeMap<String, PropertyValue>;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PropertyValue {
Null,
Bool(bool),
I64(i64),
DateTime(i64),
F64(f64),
F32(f32),
String(String),
Bytes(Vec<u8>),
I64Array(Vec<i64>),
F64Array(Vec<f64>),
F32Array(Vec<f32>),
StringArray(Vec<String>),
Array(Vec<PropertyValue>),
Object(BTreeMap<String, PropertyValue>),
}
impl PropertyValue {
pub fn array<V>(values: impl IntoIterator<Item = V>) -> Self
where
V: Into<PropertyValue>,
{
Self::Array(values.into_iter().map(Into::into).collect())
}
pub fn object<K, V>(values: impl IntoIterator<Item = (K, V)>) -> Self
where
K: Into<String>,
V: Into<PropertyValue>,
{
Self::Object(
values
.into_iter()
.map(|(key, value)| (key.into(), value.into()))
.collect(),
)
}
pub fn as_str(&self) -> Option<&str> {
match self {
Self::String(value) => Some(value),
_ => None,
}
}
pub fn as_i64(&self) -> Option<i64> {
match self {
Self::I64(value) => Some(*value),
_ => None,
}
}
pub fn datetime_millis(millis: i64) -> Self {
Self::DateTime(millis)
}
pub fn as_datetime_millis(&self) -> Option<i64> {
match self {
Self::DateTime(value) => Some(*value),
_ => None,
}
}
pub fn as_f64(&self) -> Option<f64> {
match self {
Self::F64(value) => Some(*value),
Self::F32(value) => Some(*value as f64),
_ => None,
}
}
pub fn as_bool(&self) -> Option<bool> {
match self {
Self::Bool(value) => Some(*value),
_ => None,
}
}
pub fn as_array(&self) -> Option<&[PropertyValue]> {
match self {
Self::Array(values) => Some(values),
_ => None,
}
}
pub fn as_object(&self) -> Option<&BTreeMap<String, PropertyValue>> {
match self {
Self::Object(values) => Some(values),
_ => None,
}
}
}
impl From<&str> for PropertyValue {
fn from(value: &str) -> Self {
Self::String(value.to_string())
}
}
impl From<String> for PropertyValue {
fn from(value: String) -> Self {
Self::String(value)
}
}
impl From<i64> for PropertyValue {
fn from(value: i64) -> Self {
Self::I64(value)
}
}
impl From<i32> for PropertyValue {
fn from(value: i32) -> Self {
Self::I64(value as i64)
}
}
impl From<f64> for PropertyValue {
fn from(value: f64) -> Self {
Self::F64(value)
}
}
impl From<f32> for PropertyValue {
fn from(value: f32) -> Self {
Self::F32(value)
}
}
impl From<bool> for PropertyValue {
fn from(value: bool) -> Self {
Self::Bool(value)
}
}
impl From<Vec<u8>> for PropertyValue {
fn from(value: Vec<u8>) -> Self {
Self::Bytes(value)
}
}
impl From<Vec<i64>> for PropertyValue {
fn from(value: Vec<i64>) -> Self {
Self::I64Array(value)
}
}
impl From<Vec<f64>> for PropertyValue {
fn from(value: Vec<f64>) -> Self {
Self::F64Array(value)
}
}
impl From<Vec<f32>> for PropertyValue {
fn from(value: Vec<f32>) -> Self {
Self::F32Array(value)
}
}
impl From<Vec<String>> for PropertyValue {
fn from(value: Vec<String>) -> Self {
Self::StringArray(value)
}
}
impl From<Vec<PropertyValue>> for PropertyValue {
fn from(value: Vec<PropertyValue>) -> Self {
Self::Array(value)
}
}
impl From<BTreeMap<String, PropertyValue>> for PropertyValue {
fn from(value: BTreeMap<String, PropertyValue>) -> Self {
Self::Object(value)
}
}
impl From<HashMap<String, PropertyValue>> for PropertyValue {
fn from(value: HashMap<String, PropertyValue>) -> Self {
Self::Object(value.into_iter().collect())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DateTime(i64);
impl DateTime {
pub fn from_millis(millis: i64) -> Self {
Self(millis)
}
pub fn parse_rfc3339(input: &str) -> Result<Self, chrono::ParseError> {
Ok(Self(
chrono::DateTime::parse_from_rfc3339(input)?
.with_timezone(&Utc)
.timestamp_millis(),
))
}
pub fn millis(self) -> i64 {
self.0
}
pub fn to_rfc3339(self) -> Option<String> {
chrono::DateTime::<Utc>::from_timestamp_millis(self.0)
.map(|dt| dt.to_rfc3339_opts(SecondsFormat::Millis, true))
}
}
impl From<DateTime> for PropertyValue {
fn from(value: DateTime) -> Self {
Self::DateTime(value.millis())
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PropertyInput {
Value(PropertyValue),
Expr(Expr),
}
impl PropertyInput {
pub fn param(name: impl Into<String>) -> Self {
Self::Expr(Expr::param(name))
}
pub fn into_expr(self) -> Expr {
match self {
Self::Value(value) => Expr::Constant(value),
Self::Expr(expr) => expr,
}
}
}
impl<T> From<T> for PropertyInput
where
PropertyValue: From<T>,
{
fn from(value: T) -> Self {
Self::Value(value.into())
}
}
impl From<Expr> for PropertyInput {
fn from(value: Expr) -> Self {
Self::Expr(value)
}
}
pub type PropertyMap = HashMap<String, PropertyValue>;