441 lines
13 KiB
Rust
441 lines
13 KiB
Rust
use std::borrow::Cow;
|
|
use std::collections::BTreeMap;
|
|
|
|
#[cfg(feature = "serde_json")]
|
|
use displaydoc::Display;
|
|
use thiserror::Error;
|
|
|
|
#[derive(Clone)]
|
|
pub enum NomoValue {
|
|
String {
|
|
value: Cow<'static, str>,
|
|
},
|
|
Array {
|
|
value: Vec<NomoValue>,
|
|
},
|
|
Bool {
|
|
value: bool,
|
|
},
|
|
Object {
|
|
value: BTreeMap<String, NomoValue>,
|
|
},
|
|
Integer {
|
|
value: u64,
|
|
},
|
|
SignedInteger {
|
|
value: i64,
|
|
},
|
|
Float {
|
|
value: f64,
|
|
},
|
|
Iterator {
|
|
value: Box<dyn CloneIterator<Item = NomoValue>>,
|
|
},
|
|
}
|
|
|
|
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<bool> {
|
|
if let Self::Bool { value } = self {
|
|
Some(*value)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn as_object(&self) -> Option<&BTreeMap<String, NomoValue>> {
|
|
if let Self::Object { value } = self {
|
|
Some(value)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn as_integer(&self) -> Option<u64> {
|
|
if let Self::Integer { value } = self {
|
|
Some(*value)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn as_float(&self) -> Option<f64> {
|
|
if let Self::Float { value } = self {
|
|
Some(*value)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn as_iterator(&self) -> Option<&dyn CloneIterator<Item = NomoValue>> {
|
|
if let Self::Iterator { value } = self {
|
|
Some(value)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn as_iterator_mut(&mut self) -> Option<&mut dyn CloneIterator<Item = NomoValue>> {
|
|
if let Self::Iterator { value } = self {
|
|
Some(value)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub(crate) fn try_add(&self, right_value: &NomoValue) -> Option<NomoValue> {
|
|
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<NomoValue> {
|
|
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<NomoValue> {
|
|
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<NomoValue> {
|
|
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<NomoValue> {
|
|
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<NomoValue> {
|
|
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<String> {
|
|
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<Item = NomoValue> {
|
|
fn clone_box(&self) -> Box<dyn CloneIterator<Item = NomoValue>>;
|
|
}
|
|
|
|
impl<I> CloneIterator for I
|
|
where
|
|
I: Iterator<Item = NomoValue> + Clone + 'static,
|
|
{
|
|
fn clone_box(&self) -> Box<dyn CloneIterator<Item = NomoValue>> {
|
|
Box::new(Clone::clone(self))
|
|
}
|
|
}
|
|
|
|
impl Clone for Box<dyn CloneIterator> {
|
|
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<R> From<&R> for NomoValue
|
|
where
|
|
R: Into<NomoValue> + Clone,
|
|
{
|
|
fn from(value: &R) -> Self {
|
|
value.clone().into()
|
|
}
|
|
}
|
|
|
|
impl From<String> 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<u64> for NomoValue {
|
|
fn from(val: u64) -> Self {
|
|
NomoValue::Integer { value: val }
|
|
}
|
|
}
|
|
|
|
impl From<i64> for NomoValue {
|
|
fn from(val: i64) -> Self {
|
|
NomoValue::SignedInteger { value: val }
|
|
}
|
|
}
|
|
|
|
impl<V> From<Vec<V>> for NomoValue
|
|
where
|
|
V: Into<NomoValue>,
|
|
{
|
|
fn from(val: Vec<V>) -> Self {
|
|
NomoValue::Array {
|
|
value: val.into_iter().map(Into::into).collect(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<V> From<std::collections::VecDeque<V>> for NomoValue
|
|
where
|
|
V: Into<NomoValue>,
|
|
{
|
|
fn from(val: std::collections::VecDeque<V>) -> Self {
|
|
NomoValue::Array {
|
|
value: val.into_iter().map(Into::into).collect(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<V> From<&[V]> for NomoValue
|
|
where
|
|
V: Into<NomoValue> + Clone,
|
|
{
|
|
fn from(value: &[V]) -> Self {
|
|
NomoValue::Array {
|
|
value: value.iter().cloned().map(Into::into).collect(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<K, V> From<std::collections::HashMap<K, V>> for NomoValue
|
|
where
|
|
K: Into<String>,
|
|
V: Into<NomoValue>,
|
|
{
|
|
fn from(val: std::collections::HashMap<K, V>) -> Self {
|
|
NomoValue::Object {
|
|
value: val.into_iter().map(|(k, v)| (k.into(), v.into())).collect(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Error, Display)]
|
|
/// Could not transform value to/from [`NomoValue`]
|
|
pub struct NomoValueError;
|
|
|
|
impl TryFrom<NomoValue> for String {
|
|
type Error = NomoValueError;
|
|
|
|
fn try_from(value: NomoValue) -> Result<Self, Self::Error> {
|
|
match value {
|
|
NomoValue::String { value } => Ok(value.to_string()),
|
|
_ => Err(NomoValueError),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TryFrom<NomoValue> for i64 {
|
|
type Error = NomoValueError;
|
|
|
|
fn try_from(value: NomoValue) -> Result<Self, Self::Error> {
|
|
match value {
|
|
NomoValue::SignedInteger { value } => Ok(value),
|
|
_ => Err(NomoValueError),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TryFrom<NomoValue> for u64 {
|
|
type Error = NomoValueError;
|
|
|
|
fn try_from(value: NomoValue) -> Result<Self, Self::Error> {
|
|
match value {
|
|
NomoValue::Integer { value } => Ok(value),
|
|
_ => Err(NomoValueError),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "serde_json")]
|
|
impl TryFrom<serde_json::Value> for NomoValue {
|
|
type Error = NomoValueError;
|
|
fn try_from(value: serde_json::Value) -> Result<Self, NomoValueError> {
|
|
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::<Result<_, _>>()?,
|
|
}),
|
|
serde_json::Value::Object(_map) => todo!(),
|
|
}
|
|
}
|
|
}
|