use std::borrow::Cow; use std::collections::BTreeMap; #[cfg(feature = "serde_json")] use displaydoc::Display; use thiserror::Error; use crate::Nomo; #[derive(Clone)] pub enum NomoValue { String { value: Cow<'static, str>, }, Array { value: Vec, }, Bool { value: bool, }, Object { value: BTreeMap, }, Integer { value: u64, }, SignedInteger { value: i64, }, Float { value: f64, }, Iterator { value: Box>, }, } impl NomoValue { pub fn as_str(&self) -> Option<&str> { if let Self::String { value } = self { Some(value) } else { None } } pub fn as_array(&self) -> Option<&[NomoValue]> { if let Self::Array { value } = self { Some(value) } else { None } } pub fn as_bool(&self) -> Option { if let Self::Bool { value } = self { Some(*value) } else { None } } pub fn as_object(&self) -> Option<&BTreeMap> { if let Self::Object { value } = self { Some(value) } else { None } } pub fn as_integer(&self) -> Option { if let Self::Integer { value } = self { Some(*value) } else { None } } pub fn as_float(&self) -> Option { if let Self::Float { value } = self { Some(*value) } else { None } } pub fn as_iterator(&self) -> Option<&dyn CloneIterator> { if let Self::Iterator { value } = self { Some(value) } else { None } } pub fn as_iterator_mut(&mut self) -> Option<&mut dyn CloneIterator> { if let Self::Iterator { value } = self { Some(value) } else { None } } pub(crate) fn try_add(&self, right_value: &NomoValue) -> Option { match (self, right_value) { (NomoValue::Integer { value: lval }, NomoValue::Integer { value: rval }) => { Some(NomoValue::Integer { value: *lval + *rval, }) } ( NomoValue::SignedInteger { value: lval }, NomoValue::SignedInteger { value: rval }, ) => Some(NomoValue::SignedInteger { value: *lval + *rval, }), (NomoValue::Integer { value: val }, NomoValue::SignedInteger { value: sval }) | (NomoValue::SignedInteger { value: sval }, NomoValue::Integer { value: val }) => { Some(NomoValue::SignedInteger { value: (i64::try_from(*val).ok()?) + *sval, }) } (NomoValue::String { value: rstr }, NomoValue::String { value: lstr }) => { Some(NomoValue::String { value: Cow::Owned(format!("{rstr}{lstr}")), }) } (NomoValue::Array { value: lval }, NomoValue::Array { value: rval }) => { Some(NomoValue::Array { value: Vec::from_iter(lval.iter().chain(rval.iter()).cloned()), }) } _ => None, } } pub(crate) fn try_sub(&self, right_value: &NomoValue) -> Option { macro_rules! op { ($left:expr, $right:expr) => {{ ($left) - ($right) }}; } match (self, right_value) { (NomoValue::Integer { value: lval }, NomoValue::Integer { value: rval }) => { Some(NomoValue::Integer { value: op!(*lval, *rval), }) } ( NomoValue::SignedInteger { value: lval }, NomoValue::SignedInteger { value: rval }, ) => Some(NomoValue::SignedInteger { value: op!(*lval, *rval), }), (NomoValue::Integer { value: val }, NomoValue::SignedInteger { value: sval }) | (NomoValue::SignedInteger { value: sval }, NomoValue::Integer { value: val }) => { Some(NomoValue::SignedInteger { value: op!(i64::try_from(*val).ok()?, *sval), }) } _ => None, } } pub(crate) fn try_mul(&self, right_value: &NomoValue) -> Option { macro_rules! op { ($left:expr, $right:expr) => {{ ($left) * ($right) }}; } match (self, right_value) { (NomoValue::Integer { value: lval }, NomoValue::Integer { value: rval }) => { Some(NomoValue::Integer { value: op!(*lval, *rval), }) } ( NomoValue::SignedInteger { value: lval }, NomoValue::SignedInteger { value: rval }, ) => Some(NomoValue::SignedInteger { value: op!(*lval, *rval), }), (NomoValue::Integer { value: val }, NomoValue::SignedInteger { value: sval }) | (NomoValue::SignedInteger { value: sval }, NomoValue::Integer { value: val }) => { Some(NomoValue::SignedInteger { value: op!(i64::try_from(*val).ok()?, *sval), }) } _ => None, } } pub(crate) fn try_div(&self, right_value: &NomoValue) -> Option { macro_rules! op { ($left:expr, $right:expr) => {{ ($left) / ($right) }}; } match (self, right_value) { (NomoValue::Integer { value: lval }, NomoValue::Integer { value: rval }) => { Some(NomoValue::Integer { value: op!(*lval, *rval), }) } ( NomoValue::SignedInteger { value: lval }, NomoValue::SignedInteger { value: rval }, ) => Some(NomoValue::SignedInteger { value: op!(*lval, *rval), }), (NomoValue::Integer { value: val }, NomoValue::SignedInteger { value: sval }) | (NomoValue::SignedInteger { value: sval }, NomoValue::Integer { value: val }) => { Some(NomoValue::SignedInteger { value: op!(i64::try_from(*val).ok()?, *sval), }) } _ => None, } } pub(crate) fn try_and(&self, right_value: &NomoValue) -> Option { match (self, right_value) { (NomoValue::Bool { value: lval }, NomoValue::Bool { value: rval }) => { Some(NomoValue::Bool { value: *lval && *rval, }) } _ => None, } } pub(crate) fn try_or(&self, right_value: &NomoValue) -> Option { match (self, right_value) { (NomoValue::Bool { value: lval }, NomoValue::Bool { value: rval }) => { Some(NomoValue::Bool { value: *lval || *rval, }) } _ => None, } } pub(crate) fn try_to_string(&self) -> Option { match self { NomoValue::String { value } => Some(value.to_string()), NomoValue::Array { .. } => None, NomoValue::Bool { value } => Some(value.to_string()), NomoValue::Object { .. } => None, NomoValue::Integer { value } => Some(value.to_string()), NomoValue::SignedInteger { value } => Some(value.to_string()), NomoValue::Float { value } => Some(value.to_string()), NomoValue::Iterator { .. } => None, } } } pub trait CloneIterator: Iterator { fn clone_box(&self) -> Box>; } impl CloneIterator for I where I: Iterator + Clone + 'static, { fn clone_box(&self) -> Box> { Box::new(Clone::clone(self)) } } impl Clone for Box { fn clone(&self) -> Self { self.clone_box() } } impl std::fmt::Debug for NomoValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::String { value } => f.debug_struct("String").field("value", value).finish(), Self::Array { value } => f.debug_struct("Array").field("value", value).finish(), Self::Bool { value } => f.debug_struct("Bool").field("value", value).finish(), Self::Object { value } => f.debug_struct("Object").field("value", value).finish(), Self::Integer { value } => f.debug_struct("Integer").field("value", value).finish(), Self::SignedInteger { value } => f .debug_struct("SignedInteger") .field("value", value) .finish(), Self::Float { value } => f.debug_struct("Float").field("value", value).finish(), Self::Iterator { value: _ } => f .debug_struct("Iterator") .field("value", &"Iterator") .finish(), } } } impl From<&R> for NomoValue where R: Into + Clone, { fn from(value: &R) -> Self { value.clone().into() } } impl From for NomoValue { fn from(val: String) -> Self { NomoValue::String { value: Cow::Owned(val), } } } impl From<&'static str> for NomoValue { fn from(val: &'static str) -> Self { NomoValue::String { value: Cow::Borrowed(val), } } } impl From> for NomoValue where V: Into, { fn from(val: Vec) -> Self { NomoValue::Array { value: val.into_iter().map(Into::into).collect(), } } } impl From> for NomoValue where V: Into, { fn from(val: std::collections::VecDeque) -> Self { NomoValue::Array { value: val.into_iter().map(Into::into).collect(), } } } impl From<&[V]> for NomoValue where V: Into + Clone, { fn from(value: &[V]) -> Self { NomoValue::Array { value: value.iter().cloned().map(Into::into).collect(), } } } impl From> for NomoValue where K: Into, V: Into, { fn from(val: std::collections::HashMap) -> Self { NomoValue::Object { value: val.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), } } } #[cfg(feature = "serde_json")] #[derive(Debug, Error, Display)] /// Could not transform value to [`NomoValue`] pub struct NomoValueError; #[cfg(feature = "serde_json")] impl TryFrom for NomoValue { type Error = NomoValueError; fn try_from(value: serde_json::Value) -> Result { match value { serde_json::Value::Null => todo!(), serde_json::Value::Bool(value) => Ok(NomoValue::Bool { value }), serde_json::Value::Number(number) => { if let Some(value) = number.as_u64() { return Ok(NomoValue::Integer { value }); } if let Some(value) = number.as_f64() { return Ok(NomoValue::Float { value }); } if let Some(value) = number.as_i64() { return Ok(NomoValue::SignedInteger { value }); } Err(NomoValueError) } serde_json::Value::String(str) => Ok(NomoValue::String { value: Cow::Owned(str), }), serde_json::Value::Array(values) => Ok(NomoValue::Array { value: values .into_iter() .map(TryInto::try_into) .collect::>()?, }), serde_json::Value::Object(_map) => todo!(), } } }