nomo/src/value.rs
Marcel Müller b2a97c56db Add array concatenation
Signed-off-by: Marcel Müller <neikos@neikos.email>
2026-03-13 07:42:29 +01:00

399 lines
12 KiB
Rust

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<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<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(),
}
}
}
#[cfg(feature = "serde_json")]
#[derive(Debug, Error, Display)]
/// Could not transform value to [`NomoValue`]
pub struct 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!(),
}
}
}