use std::{
borrow::{Borrow, Cow},
collections::{BTreeMap, HashMap},
fmt,
hash::{BuildHasher, Hash},
iter::FromIterator,
ops::{Range, RangeFrom, RangeTo},
string::{String, ToString},
vec::Vec,
};
use indexmap::IndexMap;
use serde::{de::DeserializeOwned, Serialize};
mod deserializer;
mod error;
mod object;
mod serializer;
pub use error::{Error, Result};
pub use object::Object;
use crate::functions::Function;
pub fn deserialize_from_value<T>(value: Value) -> Result<T>
where
T: DeserializeOwned,
{
T::deserialize(value)
}
pub fn serialize_to_value<T>(value: T) -> Result<Value>
where
T: Serialize,
{
value.serialize(serializer::Serializer)
}
#[derive(Clone)]
pub enum Value {
Null,
Bool(bool),
Number(f64),
String(String),
Array(Vec<Value>),
Object(Object),
Function(Function),
Range(Option<i32>, Option<i32>),
}
impl Value {
pub fn get<K: Hash + Eq + ?Sized>(&self, key: &K) -> Result<Option<&Self>>
where
String: Borrow<K>,
{
if let Self::Object(obj) = self {
Ok(obj.get(key))
} else {
Err(Error::BadValueType {
expected: "Array",
found: self.type_name(),
})
}
}
pub fn get_mut<K: Hash + Eq + ?Sized>(&mut self, key: &K) -> Result<Option<&mut Self>>
where
String: Borrow<K>,
{
if let Self::Object(obj) = self {
Ok(obj.get_mut(key))
} else {
Err(Error::BadValueType {
expected: "Object",
found: self.type_name(),
})
}
}
pub fn index(&self, index: usize) -> Result<Option<&Self>> {
if let Self::Array(arr) = self {
Ok(arr.get(index))
} else {
Err(Error::BadValueType {
expected: "Array",
found: self.type_name(),
})
}
}
pub fn index_mut(&mut self, index: usize) -> Result<Option<&mut Self>> {
if let Self::Array(arr) = self {
Ok(arr.get_mut(index))
} else {
Err(Error::BadValueType {
expected: "Object",
found: self.type_name(),
})
}
}
pub fn as_null(&self) -> Result<()> {
if &Self::Null == self {
Ok(())
} else {
Err(Error::BadValueType {
expected: "Null",
found: self.type_name(),
})
}
}
pub fn as_bool(&self) -> Result<&bool> {
if let Self::Bool(val) = self {
Ok(val)
} else {
Err(Error::BadValueType {
expected: "Bool",
found: self.type_name(),
})
}
}
pub fn as_number(&self) -> Result<&f64> {
if let Self::Number(val) = self {
Ok(val)
} else {
Err(Error::BadValueType {
expected: "Number",
found: self.type_name(),
})
}
}
pub fn as_string(&self) -> Result<&str> {
if let Self::String(val) = self {
Ok(val)
} else {
Err(Error::BadValueType {
expected: "String",
found: self.type_name(),
})
}
}
pub fn as_array(&self) -> Result<&[Self]> {
if let Self::Array(val) = self {
Ok(val)
} else {
Err(Error::BadValueType {
expected: "Array",
found: self.type_name(),
})
}
}
pub fn as_object(&self) -> Result<&Object> {
if let Self::Object(val) = self {
Ok(val)
} else {
Err(Error::BadValueType {
expected: "Object",
found: self.type_name(),
})
}
}
pub fn as_function(&self) -> Result<&Function> {
if let Self::Function(val) = self {
Ok(val)
} else {
Err(Error::BadValueType {
expected: "Function",
found: self.type_name(),
})
}
}
pub fn as_range(&self) -> Result<(&Option<i32>, &Option<i32>)> {
if let Self::Range(start, end) = self {
Ok((start, end))
} else {
Err(Error::BadValueType {
expected: "Range",
found: self.type_name(),
})
}
}
pub fn as_bool_mut(&mut self) -> Result<&mut bool> {
if let Self::Bool(val) = self {
Ok(val)
} else {
Err(Error::BadValueType {
expected: "Bool",
found: self.type_name(),
})
}
}
pub fn as_number_mut(&mut self) -> Result<&mut f64> {
if let Self::Number(val) = self {
Ok(val)
} else {
Err(Error::BadValueType {
expected: "Number",
found: self.type_name(),
})
}
}
pub fn as_string_mut(&mut self) -> Result<&mut str> {
if let Self::String(val) = self {
Ok(val)
} else {
Err(Error::BadValueType {
expected: "String",
found: self.type_name(),
})
}
}
pub fn as_array_mut(&mut self) -> Result<&mut [Self]> {
if let Self::Array(val) = self {
Ok(val)
} else {
Err(Error::BadValueType {
expected: "Array",
found: self.type_name(),
})
}
}
pub fn as_object_mut(&mut self) -> Result<&mut Object> {
if let Self::Object(val) = self {
Ok(val)
} else {
Err(Error::BadValueType {
expected: "Object",
found: self.type_name(),
})
}
}
pub fn as_function_mut(&mut self) -> Result<&mut Function> {
if let Self::Function(val) = self {
Ok(val)
} else {
Err(Error::BadValueType {
expected: "Function",
found: self.type_name(),
})
}
}
pub fn as_range_mut(&mut self) -> Result<(&mut Option<i32>, &mut Option<i32>)> {
if let Self::Range(start, end) = self {
Ok((start, end))
} else {
Err(Error::BadValueType {
expected: "Range",
found: self.type_name(),
})
}
}
pub fn type_name(&self) -> &'static str {
match self {
Self::Null => "Null",
Self::Bool(_) => "Bool",
Self::Number(_) => "Number",
Self::String(_) => "String",
Self::Array(_) => "Array",
Self::Object(_) => "Object",
Self::Function(_) => "Function",
Self::Range(_, _) => "Range",
}
}
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
(Self::Number(l0), Self::Number(r0)) => l0 == r0,
(Self::String(l0), Self::String(r0)) => l0 == r0,
(Self::Array(l0), Self::Array(r0)) => l0 == r0,
(Self::Object(l0), Self::Object(r0)) => l0 == r0,
(Self::Range(l0, l1), Self::Range(r0, r1)) => l0 == r0 && l1 == r1,
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
}
}
}
impl fmt::Debug for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Null => write!(f, "null"),
Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
Self::Number(arg0) => f.debug_tuple("Number").field(arg0).finish(),
Self::String(arg0) => f.debug_tuple("String").field(arg0).finish(),
Self::Array(arg0) => f.debug_tuple("Array").field(arg0).finish(),
Self::Object(arg0) => f.debug_tuple("Object").field(arg0).finish(),
Self::Function(_) => f.debug_tuple("Function").finish(),
Self::Range(arg0, arg1) => f.debug_tuple("Range").field(arg0).field(arg1).finish(),
}
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Null => write!(f, "null"),
Self::String(val) => write!(f, "{val}"),
Self::Number(val) => write!(f, "{val}"),
Self::Bool(val) => write!(f, "{val}"),
Self::Array(array) => {
write!(f, "[")?;
for (i, val) in array.iter().enumerate() {
if i == 0 {
write!(f, "{val}")?;
} else {
write!(f, ", {val}")?;
}
}
write!(f, "]")
}
Self::Object(obj) => {
write!(f, "{{")?;
for (i, (key, val)) in obj.iter().enumerate() {
if i == 0 {
if key.contains([' ', ':']) {
write!(f, "\"{key}\"")?;
} else {
write!(f, "{key}")?;
}
} else if key.contains([' ', ':']) {
write!(f, ", \"{key}\"")?;
} else {
write!(f, ", {key}")?;
}
write!(f, ": {val}")?;
}
write!(f, "}}")
}
Self::Function(_) => Ok(()),
Self::Range(start, end) => {
if let Some(start) = start {
write!(f, "{start}")?;
}
write!(f, "..")?;
if let Some(end) = end {
write!(f, "{end}")?;
}
Ok(())
}
}
}
}
pub trait FromValue
where
Self: Sized,
{
fn from_value(val: Value) -> std::result::Result<Self, Box<dyn std::error::Error>>;
}
pub trait IntoValue {
fn into_value(self) -> Value;
}
macro_rules! from_integer {
($($ty:ident)*) => {
$(
impl FromValue for $ty {
fn from_value(val: Value) -> std::result::Result<Self, Box<dyn std::error::Error>> {
if let Value::Number(val) = val {
#[allow(trivial_numeric_casts)]
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_sign_loss)]
Ok(val as $ty)
} else {
Err(Box::new(crate::Error::MismatchedTypes {
expected: "Number".to_string(),
found: val.type_name().to_string(),
}))
}
}
}
impl IntoValue for $ty {
fn into_value(self) -> Value {
#[allow(trivial_numeric_casts)]
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_sign_loss)]
#[allow(clippy::cast_lossless)]
#[allow(clippy::cast_precision_loss)]
Value::Number(self as f64)
}
}
)*
};
}
from_integer! {
i8 i16 i32 i64 isize
u8 u16 u32 u64 usize
f32 f64
}
impl FromValue for bool {
fn from_value(val: Value) -> std::result::Result<Self, Box<dyn std::error::Error>> {
if let Value::Bool(val) = val {
Ok(val)
} else {
Err(Box::new(crate::Error::MismatchedTypes {
expected: "Bool".to_string(),
found: val.type_name().to_string(),
}))
}
}
}
impl IntoValue for bool {
fn into_value(self) -> Value {
Value::Bool(self)
}
}
impl FromValue for String {
fn from_value(val: Value) -> std::result::Result<Self, Box<dyn std::error::Error>> {
if let Value::String(val) = val {
Ok(val)
} else {
Err(Box::new(crate::Error::MismatchedTypes {
expected: "String".to_string(),
found: val.type_name().to_string(),
}))
}
}
}
impl IntoValue for String {
fn into_value(self) -> Value {
Value::String(self)
}
}
impl IntoValue for &'_ str {
fn into_value(self) -> Value {
Value::String(self.to_string())
}
}
impl IntoValue for Cow<'_, str> {
fn into_value(self) -> Value {
Value::String(self.to_string())
}
}
impl FromValue for Object {
fn from_value(val: Value) -> std::result::Result<Self, Box<dyn std::error::Error>> {
if let Value::Object(val) = val {
Ok(val)
} else {
Err(Box::new(crate::Error::MismatchedTypes {
expected: "Object".to_string(),
found: val.type_name().to_string(),
}))
}
}
}
impl IntoValue for Object {
fn into_value(self) -> Value {
Value::Object(self)
}
}
impl<K: From<String> + Hash + Eq, V: FromValue> FromValue for IndexMap<K, V> {
fn from_value(val: Value) -> std::result::Result<Self, Box<dyn std::error::Error>> {
if let Value::Object(val) = val {
val.into_iter()
.map(|(k, v)| Ok((k.into(), V::from_value(v)?)))
.collect()
} else {
Err(Box::new(crate::Error::MismatchedTypes {
expected: "Object".to_string(),
found: val.type_name().to_string(),
}))
}
}
}
impl<K: Into<String>, V: IntoValue> IntoValue for IndexMap<K, V> {
fn into_value(self) -> Value {
Value::Object(
self.into_iter()
.map(|(k, v)| (k.into(), v.into_value()))
.collect(),
)
}
}
impl<K: From<String> + Hash + Eq, V: FromValue, S: BuildHasher + Default> FromValue
for HashMap<K, V, S>
{
fn from_value(val: Value) -> std::result::Result<Self, Box<dyn std::error::Error>> {
if let Value::Object(val) = val {
val.into_iter()
.map(|(k, v)| Ok((k.into(), V::from_value(v)?)))
.collect()
} else {
Err(Box::new(crate::Error::MismatchedTypes {
expected: "Object".to_string(),
found: val.type_name().to_string(),
}))
}
}
}
impl<K: Into<String>, V: IntoValue, S: BuildHasher> IntoValue for HashMap<K, V, S> {
fn into_value(self) -> Value {
Value::Object(
self.into_iter()
.map(|(k, v)| (k.into(), v.into_value()))
.collect(),
)
}
}
impl<K: From<String> + Ord, V: FromValue> FromValue for BTreeMap<K, V> {
fn from_value(val: Value) -> std::result::Result<Self, Box<dyn std::error::Error>> {
if let Value::Object(val) = val {
val.into_iter()
.map(|(k, v)| Ok((k.into(), V::from_value(v)?)))
.collect()
} else {
Err(Box::new(crate::Error::MismatchedTypes {
expected: "Object".to_string(),
found: val.type_name().to_string(),
}))
}
}
}
impl<K: Into<String>, V: IntoValue> IntoValue for BTreeMap<K, V> {
fn into_value(self) -> Value {
Value::Object(
self.into_iter()
.map(|(k, v)| (k.into(), v.into_value()))
.collect(),
)
}
}
impl<const N: usize, T: IntoValue> IntoValue for [T; N] {
fn into_value(self) -> Value {
Value::Array(self.into_iter().map(IntoValue::into_value).collect())
}
}
impl<T: FromValue> FromValue for Vec<T> {
fn from_value(val: Value) -> std::result::Result<Self, Box<dyn std::error::Error>> {
if let Value::Array(val) = val {
val.into_iter().map(|v| T::from_value(v)).collect()
} else {
Err(Box::new(crate::Error::MismatchedTypes {
expected: "Array".to_string(),
found: val.type_name().to_string(),
}))
}
}
}
impl<T: IntoValue> IntoValue for Vec<T> {
fn into_value(self) -> Value {
Value::Array(self.into_iter().map(IntoValue::into_value).collect())
}
}
impl<T: Into<Self>> FromIterator<T> for Value {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
Self::Array(iter.into_iter().map(Into::into).collect())
}
}
impl FromValue for Function {
fn from_value(val: Value) -> std::result::Result<Self, Box<dyn std::error::Error>> {
if let Value::Function(val) = val {
Ok(val)
} else {
Err(Box::new(crate::Error::MismatchedTypes {
expected: "Function".to_string(),
found: val.type_name().to_string(),
}))
}
}
}
impl IntoValue for Function {
fn into_value(self) -> Value {
Value::Function(self)
}
}
impl<T: From<i32>> FromValue for Range<T> {
fn from_value(val: Value) -> std::result::Result<Self, Box<dyn std::error::Error>> {
if let Value::Range(start, end) = val {
if let (Some(start), Some(end)) = (start, end) {
Ok(start.into()..end.into())
} else {
Err(Box::new(crate::Error::ExpectedBoundedRange))
}
} else {
Err(Box::new(crate::Error::MismatchedTypes {
expected: "Range".to_string(),
found: val.type_name().to_string(),
}))
}
}
}
impl<T: Into<i32>> IntoValue for Range<T> {
fn into_value(self) -> Value {
Value::Range(Some(self.start.into()), Some(self.end.into()))
}
}
impl<T: From<i32>> FromValue for RangeFrom<T> {
fn from_value(val: Value) -> std::result::Result<Self, Box<dyn std::error::Error>> {
if let Value::Range(start, end) = val {
if let (Some(start), None) = (start, end) {
Ok(start.into()..)
} else {
Err(Box::new(crate::Error::ExpectedRangeFrom))
}
} else {
Err(Box::new(crate::Error::MismatchedTypes {
expected: "Range".to_string(),
found: val.type_name().to_string(),
}))
}
}
}
impl<T: Into<i32>> IntoValue for RangeFrom<T> {
fn into_value(self) -> Value {
Value::Range(Some(self.start.into()), None)
}
}
impl<T: From<i32>> FromValue for RangeTo<T> {
fn from_value(val: Value) -> std::result::Result<Self, Box<dyn std::error::Error>> {
if let Value::Range(start, end) = val {
if let (None, Some(end)) = (start, end) {
Ok(..end.into())
} else {
Err(Box::new(crate::Error::ExpectedRangeTo))
}
} else {
Err(Box::new(crate::Error::MismatchedTypes {
expected: "Range".to_string(),
found: val.type_name().to_string(),
}))
}
}
}
impl<T: Into<i32>> IntoValue for RangeTo<T> {
fn into_value(self) -> Value {
Value::Range(None, Some(self.end.into()))
}
}
impl FromValue for () {
fn from_value(val: Value) -> std::result::Result<Self, Box<dyn std::error::Error>> {
if val == Value::Null {
Ok(())
} else {
Err(Box::new(crate::Error::MismatchedTypes {
expected: "Null".to_string(),
found: val.type_name().to_string(),
}))
}
}
}
impl IntoValue for () {
fn into_value(self) -> Value {
Value::Null
}
}
impl<T: FromValue> FromValue for Option<T> {
fn from_value(val: Value) -> std::result::Result<Self, Box<dyn std::error::Error>> {
if val == Value::Null {
Ok(None)
} else {
Ok(Some(T::from_value(val)?))
}
}
}
impl<T: IntoValue> IntoValue for Option<T> {
fn into_value(self) -> Value {
if let Some(val) = self {
val.into_value()
} else {
Value::Null
}
}
}
impl FromValue for Value {
fn from_value(val: Value) -> std::result::Result<Self, Box<dyn std::error::Error>> {
Ok(val)
}
}
impl IntoValue for Value {
fn into_value(self) -> Value {
self
}
}
macro_rules! from_into_tuple {
( $num_args:literal, $(($n:tt, $T:ident)),+ ) => {
impl<$($T: FromValue,)*> FromValue for ($($T,)*) {
fn from_value(val: Value) -> Result<Self, Box<dyn std::error::Error>> {
if let Value::Array(mut val) = val {
if val.len() == $num_args {
Ok(($({ let _ = $n; $T::from_value(val.remove(0))? },)*))
} else {
Err(Box::new(crate::Error::BadTupleLength {
expected: $num_args,
found: val.len(),
}))
}
} else {
Err(Box::new(crate::Error::MismatchedTypes {
expected: "Array".to_string(),
found: val.type_name().to_string(),
}))
}
}
}
impl<$($T: IntoValue,)*> IntoValue for ($($T,)*) {
fn into_value(self) -> Value {
Value::Array(vec![$(self.$n.into_value(),)*])
}
}
}
}
#[rustfmt::skip]
mod impls {
use super::{FromValue, IntoValue, ToString, Value};
from_into_tuple!(1, (0, A));
from_into_tuple!(2, (0, A), (1, B));
from_into_tuple!(3, (0, A), (1, B), (2, C));
from_into_tuple!(4, (0, A), (1, B), (2, C), (3, D));
from_into_tuple!(5, (0, A), (1, B), (2, C), (3, D), (4, E));
from_into_tuple!(6, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F));
from_into_tuple!(7, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G));
from_into_tuple!(8, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H));
from_into_tuple!(9, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I));
from_into_tuple!(10, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J));
from_into_tuple!(11, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J), (10, K));
from_into_tuple!(12, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J), (10, K), (11, L));
from_into_tuple!(13, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J), (10, K), (11, L), (12, M));
from_into_tuple!(14, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J), (10, K), (11, L), (12, M), (13, N));
from_into_tuple!(15, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J), (10, K), (11, L), (12, M), (13, N), (14, O));
from_into_tuple!(16, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J), (10, K), (11, L), (12, M), (13, N), (14, O), (15, P));
}
#[cfg(test)]
mod tests {
use crate::{object, FromValue, IntoValue, Value};
mod pochoir {
pub mod lang {
pub use crate::*;
}
}
fn both_ways<T: FromValue + IntoValue + PartialEq + std::fmt::Debug + Clone>(v: &T) {
assert_eq!(T::from_value(v.clone().into_value()).unwrap(), *v);
}
#[test]
#[allow(clippy::too_many_lines)]
fn custom_structure() {
#[derive(FromValue, IntoValue, Debug, PartialEq, Clone)]
struct DataStructUnit;
#[derive(FromValue, IntoValue, Debug, PartialEq, Clone)]
struct DataStructUnnamed(String, String, usize);
#[derive(FromValue, IntoValue, Debug, PartialEq, Clone)]
struct DataNested {
life: usize,
nature: String,
}
#[derive(FromValue, IntoValue, Debug, PartialEq, Clone)]
struct Data {
some_nested_field: DataNested,
}
#[derive(FromValue, IntoValue, Debug, PartialEq, Clone)]
enum DataEnum {
A,
B(usize, String),
C { field_1: String, field_2: Vec<f64> },
}
#[derive(FromValue, IntoValue, Debug, PartialEq, Clone)]
struct StructWithSomeOptions {
a: usize,
b: Option<String>,
c: String,
}
assert_eq!(DataStructUnit.into_value(), Value::Object(object! {}));
assert_eq!(
DataStructUnnamed("a".to_string(), "b".to_string(), 33).into_value(),
Value::Object(object! {
"__field0" => "a",
"__field1" => "b",
"__field2" => 33,
})
);
assert_eq!(
Data {
some_nested_field: DataNested {
life: 42,
nature: "heal".to_string(),
}
}
.into_value(),
Value::Object(object! {
"some_nested_field" => Value::Object(object! {
"life" => Value::Number(42.0),
"nature" => Value::String("heal".to_string()),
}),
})
);
assert_eq!(
DataEnum::A.into_value(),
Value::Object(object! {
"__enum_variant" => "A",
})
);
assert_eq!(
DataEnum::B(42, "foo".to_string()).into_value(),
Value::Object(object! {
"__enum_variant" => "B",
"__field0" => 42,
"__field1" => "foo",
})
);
assert_eq!(
DataEnum::C {
field_1: "bar".to_string(),
field_2: vec![2.0, 33.0, 12.0]
}
.into_value(),
Value::Object(object! {
"__enum_variant" => "C",
"field_1" => "bar",
"field_2" => vec![2.0, 33.0, 12.0],
})
);
both_ways(&DataStructUnit);
both_ways(&DataStructUnnamed("a".to_string(), "b".to_string(), 33));
both_ways(&Data {
some_nested_field: DataNested {
life: 42,
nature: "heal".to_string(),
},
});
both_ways(&DataEnum::A);
both_ways(&DataEnum::B(42, "foo".to_string()));
both_ways(&DataEnum::C {
field_1: "bar".to_string(),
field_2: vec![2.0, 33.0, 12.0],
});
both_ways(&StructWithSomeOptions {
a: 12,
b: None,
c: "hello".to_string(),
});
both_ways(&StructWithSomeOptions {
a: 12,
b: Some("hello".to_string()),
c: "world".to_string(),
});
assert_eq!(
StructWithSomeOptions::from_value(Value::Object(crate::Object::from_iter([
("a".to_string(), Value::Number(12.0)),
("b".to_string(), Value::Null),
("c".to_string(), Value::String("hello".to_string()))
])))
.unwrap(),
StructWithSomeOptions {
a: 12,
b: None,
c: "hello".to_string(),
}
);
}
}