use crate::evaluator::EvaluationError;
pub use num_traits::cast::FromPrimitive;
use pel::runtime::value::Value as PelValue;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Number, Value as JsonValue};
use std::collections::HashMap;
use std::convert::TryFrom;
use std::iter::FromIterator;
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Default)]
#[serde(from = "serde_json::Value", into = "serde_json::Value")]
pub enum Value {
#[default]
Null,
Bool(bool),
Number(f64),
String(String),
Array(Vec<Value>),
Object(HashMap<String, Value>),
}
impl Value {
pub fn is_null(&self) -> bool {
matches!(self, Value::Null)
}
pub fn as_bool(&self) -> Option<bool> {
match self {
Value::Bool(b) => Some(*b),
_ => None,
}
}
pub fn as_str(&self) -> Option<&str> {
match self {
Value::String(s) => Some(s),
_ => None,
}
}
pub fn as_num(&self) -> Option<f64> {
match self {
Value::Number(f) => Some(*f),
_ => None,
}
}
pub fn as_slice(&self) -> Option<&[Value]> {
match self {
Value::Array(a) => Some(a),
_ => None,
}
}
pub fn as_object(&self) -> Option<&HashMap<String, Value>> {
match self {
Value::Object(o) => Some(o),
_ => None,
}
}
}
impl From<JsonValue> for Value {
fn from(value: JsonValue) -> Self {
match value {
JsonValue::Null => Value::Null,
JsonValue::Bool(val) => val.into_value(),
JsonValue::Number(num) => num.as_f64().unwrap_or_default().into_value(),
JsonValue::String(str) => str.into_value(),
JsonValue::Array(vec) => vec.into_value(),
JsonValue::Object(map) => map.into_iter().collect(),
}
}
}
impl From<Value> for JsonValue {
fn from(value: Value) -> Self {
JsonValue::try_from_value(value).unwrap_or_default()
}
}
impl TryFrom<&PelValue> for Value {
type Error = EvaluationError;
fn try_from(value: &PelValue) -> Result<Self, Self::Error> {
if value.is_null() {
return Ok(Value::Null);
};
if let Some(val) = value.as_bool() {
return Ok(val.into_value());
};
if let Some(val) = value.as_f64() {
return Ok(val.into_value());
};
if let Some(val) = value.as_str() {
return Ok(val.into_value());
};
if let Some(array) = value.as_slice() {
let mut vec = Vec::new();
for item in array {
vec.push(Value::try_from(item)?);
}
return Ok(vec.into_value());
};
if let Some(obj) = value.as_object() {
let mut map = HashMap::new();
for (key, val) in obj {
map.insert(key.to_string(), Value::try_from(val)?);
}
return Ok(map.into_value());
};
if let Some(s) = value.as_doc_node().and_then(|d| d.content()) {
return Ok(Value::String(s));
}
Err(EvaluationError::TypeMismatch)
}
}
impl IntoValue for JsonValue {
fn into_value(self) -> Value {
Value::from(self)
}
}
impl Value {
fn array_to_pel(vec: Vec<Value>) -> pel::runtime::value::Array {
vec.into_iter().map(Value::into_pel).collect()
}
fn obj_to_pel(obj: HashMap<String, Value>) -> pel::runtime::value::Object {
obj.into_iter()
.map(|(key, value)| (key, value.into_pel()))
.collect()
}
#[cfg(feature = "experimental_coerced_type")]
pub(crate) fn coerced_pel(self, bytes: &[u8]) -> PelValue {
let origin = std::rc::Rc::new(String::from_utf8_lossy(bytes).to_string());
match self {
Value::Object(obj) => PelValue::coerced_object(Self::obj_to_pel(obj), origin),
Value::Array(vec) => PelValue::coerced_array(Self::array_to_pel(vec), origin),
other => other.into_pel(),
}
}
pub(crate) fn into_pel(self) -> PelValue {
match self {
Value::Null => PelValue::null(),
Value::Bool(val) => PelValue::bool(val),
Value::Number(num) => PelValue::number(num),
Value::String(val) => PelValue::string(val),
Value::Array(vec) => PelValue::array(Self::array_to_pel(vec)),
Value::Object(obj) => PelValue::object(Self::obj_to_pel(obj)),
}
}
}
pub trait IntoValue {
fn into_value(self) -> Value;
}
impl IntoValue for Value {
fn into_value(self) -> Value {
self
}
}
impl IntoValue for &str {
fn into_value(self) -> Value {
Value::String(self.to_string())
}
}
impl IntoValue for String {
fn into_value(self) -> Value {
Value::String(self)
}
}
macro_rules! into_value_num {
[$($num_type:ty), +] => {
$(
impl IntoValue for $num_type {
fn into_value(self) -> Value {
Value::Number(self as f64)
}
}
)*
};
}
into_value_num![i8, i16, i32, i64, u8, u16, u32, u64, f32, f64];
impl IntoValue for bool {
fn into_value(self) -> Value {
Value::Bool(self)
}
}
impl<K: IntoValue> FromIterator<K> for Value {
fn from_iter<T: IntoIterator<Item = K>>(iter: T) -> Self {
Value::Array(iter.into_iter().map(IntoValue::into_value).collect())
}
}
impl<'a, K: IntoValue> FromIterator<(&'a str, K)> for Value {
fn from_iter<T: IntoIterator<Item = (&'a str, K)>>(iter: T) -> Self {
Value::Object(
iter.into_iter()
.map(|(key, val)| (key.to_string(), val.into_value()))
.collect(),
)
}
}
impl<K: IntoValue> FromIterator<(String, K)> for Value {
fn from_iter<T: IntoIterator<Item = (String, K)>>(iter: T) -> Self {
Value::Object(
iter.into_iter()
.map(|(key, val)| (key, val.into_value()))
.collect(),
)
}
}
impl<K: IntoValue> IntoValue for Vec<K> {
fn into_value(self) -> Value {
self.into_iter().collect()
}
}
impl<K: IntoValue> IntoValue for HashMap<String, K> {
fn into_value(self) -> Value {
self.into_iter().collect()
}
}
impl<K: IntoValue> IntoValue for HashMap<&str, K> {
fn into_value(self) -> Value {
self.into_iter().collect()
}
}
impl<K: IntoValue> IntoValue for Option<K> {
fn into_value(self) -> Value {
match self {
None => Value::Null,
Some(k) => k.into_value(),
}
}
}
pub trait TryFromValue: Sized {
fn try_from_value(value: Value) -> Result<Self, EvaluationError>;
}
impl TryFromValue for Value {
fn try_from_value(value: Value) -> Result<Self, EvaluationError> {
Ok(value)
}
}
impl TryFromValue for bool {
fn try_from_value(value: Value) -> Result<Self, EvaluationError> {
match value {
Value::Bool(val) => Ok(val),
_ => Err(EvaluationError::TypeMismatch),
}
}
}
macro_rules! try_from_value_num {
[$($num_type:ty), +] => {
$(
impl TryFromValue for $num_type {
fn try_from_value(value: Value) -> Result<Self, EvaluationError> {
match value {
Value::Number(num) => {
<$num_type>::from_f64(num)
.ok_or(EvaluationError::TypeMismatch)
}
_ => Err(EvaluationError::TypeMismatch)
}
}
}
)*
};
}
try_from_value_num![i8, i16, i32, i64, u8, u16, u32, u64, f32, f64];
impl TryFromValue for String {
fn try_from_value(value: Value) -> Result<Self, EvaluationError> {
match value {
Value::String(val) => Ok(val),
_ => Err(EvaluationError::TypeMismatch),
}
}
}
impl<K: TryFromValue> TryFromValue for Vec<K> {
fn try_from_value(value: Value) -> Result<Self, EvaluationError> {
match value {
Value::Array(array) => array.into_iter().map(K::try_from_value).collect(),
_ => Err(EvaluationError::TypeMismatch),
}
}
}
impl<K: TryFromValue> TryFromValue for HashMap<String, K> {
fn try_from_value(value: Value) -> Result<Self, EvaluationError> {
match value {
Value::Object(obj) => obj
.into_iter()
.map(|(key, val)| Ok((key, K::try_from_value(val)?)))
.collect(),
_ => Err(EvaluationError::TypeMismatch),
}
}
}
impl TryFromValue for Map<String, JsonValue> {
fn try_from_value(value: Value) -> Result<Self, EvaluationError> {
match value {
Value::Object(obj) => obj
.into_iter()
.map(|(key, val)| Ok((key, JsonValue::try_from_value(val)?)))
.collect(),
_ => Err(EvaluationError::TypeMismatch),
}
}
}
impl TryFromValue for JsonValue {
fn try_from_value(value: Value) -> Result<Self, EvaluationError> {
match value {
Value::Null => Ok(JsonValue::Null),
Value::Bool(val) => Ok(JsonValue::Bool(val)),
Value::Number(n) => Number::from_f64(n)
.map(JsonValue::Number)
.ok_or(EvaluationError::TypeMismatch),
Value::String(s) => Ok(JsonValue::String(s)),
Value::Array(_) => Vec::<JsonValue>::try_from_value(value).map(JsonValue::Array),
Value::Object(_) => {
Map::<String, JsonValue>::try_from_value(value).map(JsonValue::Object)
}
}
}
}