use std::{
borrow::{Borrow, Cow},
cmp::Ordering,
collections::BTreeMap,
fmt::{Debug, Display},
hash::Hash,
ops::Deref,
};
use serde::{
de::{Deserialize, Deserializer, MapAccess, SeqAccess, VariantAccess, Visitor},
ser::Serialize,
};
type MapImpl<K, V> = BTreeMap<K, V>;
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Number(N);
impl Number {
pub fn as_f64(&self) -> f64 {
match self.0 {
N::PosInt(n) => n as f64,
N::NegInt(n) => n as f64,
N::Float(n) => n,
}
}
pub fn as_i64(&self) -> Option<i64> {
match self.0 {
N::PosInt(n) => {
if n <= i64::MAX as u64 {
Some(n as i64)
} else {
None
}
}
N::NegInt(n) => Some(n),
N::Float(_) => None,
}
}
pub fn as_u64(&self) -> Option<u64> {
match self.0 {
N::PosInt(n) => Some(n),
N::NegInt(_) | N::Float(_) => None,
}
}
}
impl Display for Number {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0 {
N::PosInt(n) => f.write_str(itoa::Buffer::new().format(n)),
N::NegInt(n) => f.write_str(itoa::Buffer::new().format(n)),
N::Float(n) => f.write_str(ryu::Buffer::new().format_finite(n)),
}
}
}
impl Debug for Number {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Number({})", self)
}
}
#[derive(Clone, Copy)]
enum N {
PosInt(u64),
NegInt(i64),
Float(f64),
}
impl Hash for N {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match *self {
N::NegInt(n) => n.hash(state),
N::PosInt(n) => n.hash(state),
N::Float(n) => {
if 0.0_f64 == n {
0.0_f64.to_bits().hash(state)
} else {
n.to_bits().hash(state)
}
}
}
}
}
impl PartialEq for N {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::PosInt(l), Self::PosInt(r)) => l == r,
(Self::NegInt(l), Self::NegInt(r)) => l == r,
(Self::Float(l), Self::Float(r)) => l == r,
_ => false,
}
}
}
impl Eq for N {}
impl PartialOrd for N {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for N {
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(N::PosInt(l), N::PosInt(r)) => l.cmp(r),
(N::PosInt(_), N::NegInt(_)) => Ordering::Greater,
(N::PosInt(l), N::Float(r)) => (*l as f64).partial_cmp(r).unwrap(),
(N::NegInt(_), N::PosInt(_)) => Ordering::Less,
(N::NegInt(l), N::NegInt(r)) => l.partial_cmp(r).unwrap(),
(N::NegInt(l), N::Float(r)) => (*l as f64).partial_cmp(r).unwrap(),
(N::Float(l), N::PosInt(r)) => l.partial_cmp(&(*r as f64)).unwrap(),
(N::Float(l), N::NegInt(r)) => l.partial_cmp(&(*r as f64)).unwrap(),
(N::Float(l), N::Float(r)) => l.partial_cmp(r).unwrap(),
}
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
pub struct Identifier(String);
impl Identifier {
fn from_validated(ident: String) -> Self {
Self(ident)
}
fn as_str(&self) -> &str {
&self.0
}
}
impl Display for Identifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl Deref for Identifier {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl Borrow<str> for Identifier {
fn borrow(&self) -> &str {
self.as_str()
}
}
impl From<Identifier> for String {
fn from(value: Identifier) -> Self {
value.0
}
}
impl TryFrom<&str> for Identifier {
type Error = Cow<'static, str>;
fn try_from(value: &str) -> Result<Self, Self::Error> {
is_valid_identifier(value).map(|_| Identifier::from_validated(value.to_string()))
}
}
pub struct TryFromStringError {
pub input: String,
pub error: Cow<'static, str>,
}
impl TryFrom<String> for Identifier {
type Error = TryFromStringError;
fn try_from(value: String) -> Result<Self, Self::Error> {
match is_valid_identifier(&value) {
Ok(_) => Ok(Identifier::from_validated(value)),
Err(e) => Err(TryFromStringError {
input: value,
error: e,
}),
}
}
}
fn is_valid_identifier(value: &str) -> Result<(), Cow<'static, str>> {
let mut chars = value.char_indices();
match chars.next() {
None => Err("Not an identifier; empty input".into()),
Some((_, 'a'..='z' | 'A'..='Z' | '_')) => {
for (i, c) in chars {
if !c.is_ascii_alphanumeric() && c != '_' {
return Err(format!(
"Not an identifier; invalid character '{c}' at pos {}",
i + 1
)
.into());
}
}
match value {
"null" | "true" | "false" => Err("Not an identifier, but a keyword".into()),
_ => Ok(()),
}
}
Some((_, c)) => Err(format!("Not an identifier; invalid start character '{c}'").into()),
}
}
impl Serialize for Identifier {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.0)
}
}
impl<'de> Deserialize<'de> for Identifier {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct IdentifierVisitor;
impl<'de> Visitor<'de> for IdentifierVisitor {
type Value = Identifier;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("an identifier")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Identifier::try_from(v).map_err(|e| serde::de::Error::custom(format!("{v:?}: {e}")))
}
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Identifier::try_from(v).map_err(|TryFromStringError { input, error }| {
serde::de::Error::custom(format!("{input:?}: {error}"))
})
}
}
deserializer.deserialize_str(IdentifierVisitor)
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum Properties {
List(Vec<Value>),
Map(MapImpl<Identifier, Value>),
}
impl Display for Properties {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::List(v) => v.fmt(f),
Self::Map(v) => v.fmt(f),
}
}
}
impl Hash for Properties {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
core::mem::discriminant(self).hash(state);
match self {
Properties::List(v) => v.hash(state),
Properties::Map(v) => v.hash(state),
}
}
}
impl Ord for Properties {
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(Properties::List(l), Properties::List(r)) => l.cmp(r),
(Properties::List(_), Properties::Map(_)) => Ordering::Less,
(Properties::Map(_), Properties::List(_)) => Ordering::Greater,
(Properties::Map(l), Properties::Map(r)) => l.cmp(r),
}
}
}
impl PartialOrd for Properties {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum Value {
Null,
Bool(bool),
Number(Number),
String(String),
List(Vec<Value>),
Map(MapImpl<Value, Value>),
Object(Identifier, Properties),
}
impl Value {
fn value_type(&self) -> &'static str {
match self {
Value::Null => "null",
Value::Bool(_) => "bool",
Value::Number(_) => "number",
Value::String(_) => "string",
Value::List(_) => "list",
Value::Map(_) => "map",
Value::Object(_, _) => "object",
}
}
#[inline]
pub fn as_null(&self) -> Option<()> {
match self {
Value::Null => Some(()),
_ => None,
}
}
#[inline]
pub fn as_bool(&self) -> Option<bool> {
match self {
Value::Bool(v) => Some(*v),
_ => None,
}
}
#[inline]
pub fn as_f64(&self) -> Option<f64> {
match self {
Value::Number(n) => Some(n.as_f64()),
_ => None,
}
}
#[inline]
pub fn as_i64(&self) -> Option<i64> {
match self {
Value::Number(n) => n.as_i64(),
_ => None,
}
}
#[inline]
pub fn as_u64(&self) -> Option<u64> {
match self {
Value::Number(n) => n.as_u64(),
_ => None,
}
}
#[inline]
pub fn as_str(&self) -> Option<&str> {
match self {
Value::String(s) => Some(s.as_str()),
_ => None,
}
}
#[inline]
pub fn as_list(&self) -> Option<&Vec<Value>> {
match self {
Value::List(xs) => Some(xs),
_ => None,
}
}
#[inline]
pub fn as_list_mut(&mut self) -> Option<&mut Vec<Value>> {
match self {
Value::List(xs) => Some(xs),
_ => None,
}
}
#[inline]
pub fn as_map(&self) -> Option<&MapImpl<Value, Value>> {
match self {
Value::Map(xs) => Some(xs),
_ => None,
}
}
#[inline]
pub fn as_map_mut(&mut self) -> Option<&mut MapImpl<Value, Value>> {
match self {
Value::Map(xs) => Some(xs),
_ => None,
}
}
#[inline]
pub fn as_object(&self) -> Option<(&Identifier, &Properties)> {
match self {
Value::Object(name, props) => Some((name, props)),
_ => None,
}
}
#[inline]
pub fn as_object_mut(&mut self) -> Option<(&mut Identifier, &mut Properties)> {
match self {
Value::Object(name, props) => Some((name, props)),
_ => None,
}
}
}
impl From<()> for Value {
fn from(_value: ()) -> Self {
Value::Null
}
}
impl<T> From<&T> for Value
where
T: Into<Value> + Copy,
{
fn from(value: &T) -> Self {
(*value).into()
}
}
impl From<bool> for Value {
fn from(value: bool) -> Self {
Value::Bool(value)
}
}
macro_rules! impl_from_int {
($utype:ty, $itype:ty) => {
impl From<$utype> for Value {
fn from(value: $utype) -> Self {
Value::Number(Number(N::PosInt(value as u64)))
}
}
impl From<$itype> for Value {
fn from(value: $itype) -> Self {
if value < 0 {
Value::Number(Number(N::NegInt(value as i64)))
} else {
Value::Number(Number(N::PosInt(value as u64)))
}
}
}
};
}
macro_rules! impl_from_float {
($type:ty) => {
impl From<$type> for Value {
fn from(value: $type) -> Self {
Value::Number(Number(N::Float(value as f64)))
}
}
};
}
impl_from_int!(u8, i8);
impl_from_int!(u16, i16);
impl_from_int!(u32, i32);
impl_from_int!(u64, i64);
impl_from_float!(f32);
impl_from_float!(f64);
impl From<&str> for Value {
fn from(value: &str) -> Self {
value.to_string().into()
}
}
impl From<String> for Value {
fn from(value: String) -> Self {
Value::String(value)
}
}
impl From<char> for Value {
fn from(value: char) -> Self {
Value::String(value.to_string())
}
}
impl<T> From<&[T]> for Value
where
T: Into<Value> + Copy,
{
fn from(value: &[T]) -> Self {
value.iter().map(Into::into).collect::<Vec<_>>().into()
}
}
impl<T, const N: usize> From<[T; N]> for Value
where
T: Into<Value>,
{
fn from(value: [T; N]) -> Self {
value.into_iter().map(Into::into).collect::<Vec<_>>().into()
}
}
impl From<Vec<Value>> for Value {
fn from(value: Vec<Value>) -> Self {
Value::List(value)
}
}
impl<K, V> From<&[(K, V)]> for Value
where
K: Into<Value> + Copy,
V: Into<Value> + Copy,
{
fn from(value: &[(K, V)]) -> Self {
let mut m = MapImpl::new();
for (k, v) in value {
m.insert(k.into(), v.into());
}
Value::Map(m)
}
}
impl<K, V, const N: usize> From<[(K, V); N]> for Value
where
K: Into<Value>,
V: Into<Value>,
{
fn from(value: [(K, V); N]) -> Self {
let mut m = MapImpl::new();
for (k, v) in value {
m.insert(k.into(), v.into());
}
Value::Map(m)
}
}
impl Hash for Value {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
core::mem::discriminant(self).hash(state);
match self {
Value::Null => {}
Value::Bool(v) => v.hash(state),
Value::Number(v) => v.hash(state),
Value::String(v) => v.hash(state),
Value::List(v) => v.hash(state),
Value::Map(v) => v.hash(state),
Value::Object(name, props) => {
name.hash(state);
props.hash(state);
}
}
}
}
impl Ord for Value {
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(Value::Null, Value::Null) => Ordering::Equal,
(Value::Null, _) => Ordering::Less,
(_, Value::Null) => Ordering::Greater,
(Value::Bool(l), Value::Bool(r)) => l.cmp(r),
(Value::Bool(_), _) => Ordering::Less,
(_, Value::Bool(_)) => Ordering::Greater,
(Value::Number(l), Value::Number(r)) => l.cmp(r),
(Value::Number(_), _) => Ordering::Less,
(_, Value::Number(_)) => Ordering::Greater,
(Value::String(l), Value::String(r)) => l.cmp(r),
(Value::String(_), _) => Ordering::Less,
(_, Value::String(_)) => Ordering::Greater,
(Value::List(l), Value::List(r)) => l.cmp(r),
(Value::List(_), _) => Ordering::Less,
(_, Value::List(_)) => Ordering::Greater,
(Value::Map(l), Value::Map(r)) => l.cmp(r),
(Value::Map(_), _) => Ordering::Less,
(_, Value::Map(_)) => Ordering::Greater,
(Value::Object(lname, lprops), Value::Object(rname, rprops)) => {
lname.cmp(rname).then_with(|| lprops.cmp(rprops))
}
}
}
}
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
struct PropertiesVisitor;
impl<'de> Visitor<'de> for PropertiesVisitor {
type Value = Properties;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a list or map")
}
fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
match ValueVisitor.visit_seq(seq) {
Err(e) => Err(e),
Ok(Value::List(list)) => Ok(Properties::List(list)),
Ok(value) => panic!("not a list: {}", value.value_type()),
}
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut elems = MapImpl::new();
while let Some((k, v)) = map.next_entry()? {
elems.insert(k, v);
}
Ok(Properties::Map(elems))
}
}
impl<'de> Deserialize<'de> for Properties {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(PropertiesVisitor)
}
}
struct NonValidatedPropertiesVisitor;
impl<'de> Visitor<'de> for NonValidatedPropertiesVisitor {
type Value = NonValidatedProperties;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
PropertiesVisitor.expecting(formatter) }
fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
PropertiesVisitor.visit_seq(seq).map(NonValidatedProperties)
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut elems = MapImpl::new();
while let Some((k, v)) = map.next_entry()? {
elems.insert(Identifier::from_validated(k), v);
}
Ok(NonValidatedProperties(Properties::Map(elems)))
}
}
struct NonValidatedProperties(Properties);
impl<'de> Deserialize<'de> for NonValidatedProperties {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(NonValidatedPropertiesVisitor)
}
}
struct ValueVisitor;
impl<'de> Visitor<'de> for ValueVisitor {
type Value = Value;
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str("a null, a bool, a number, a string, a list, a map, or a named object")
}
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Value::Bool(v))
}
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Value::Number(Number(if v < 0 {
N::NegInt(v)
} else {
N::PosInt(v as u64)
})))
}
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Value::Number(Number(N::PosInt(v))))
}
fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Value::Number(Number(N::Float(v))))
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Value::String(v.to_string()))
}
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Value::String(v))
}
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Value::List(
v.iter()
.map(|n| Value::Number(Number(N::PosInt((*n).into()))))
.collect(),
))
}
fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Value::Null)
}
fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(self)
}
fn visit_unit<E>(self) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Value::Null)
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut elems = Vec::with_capacity(cautious(seq.size_hint()));
while let Some(elem) = seq.next_element()? {
elems.push(elem);
}
Ok(Value::List(elems))
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut elems = MapImpl::new();
while let Some((k, v)) = map.next_entry()? {
elems.insert(k, v);
}
Ok(Value::Map(elems))
}
fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
where
A: serde::de::EnumAccess<'de>,
{
data.variant::<String>().and_then(|(name, vaccess)| {
vaccess
.newtype_variant()
.map(|NonValidatedProperties(props)| {
Value::Object(Identifier::from_validated(name), props)
})
})
}
}
impl<'de> Deserialize<'de> for Value {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(ValueVisitor)
}
}
fn cautious(size_hint: Option<usize>) -> usize {
size_hint.unwrap_or(0).min(4096)
}
impl Serialize for Value {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
Value::Null => serializer.serialize_none(),
Value::Bool(v) => serializer.serialize_bool(*v),
Value::Number(Number(N::PosInt(v))) => serializer.serialize_u64(*v),
Value::Number(Number(N::NegInt(v))) => serializer.serialize_i64(*v),
Value::Number(Number(N::Float(v))) => serializer.serialize_f64(*v),
Value::String(v) => serializer.serialize_str(v.as_str()),
Value::List(vs) => vs.serialize(serializer),
Value::Map(vs) => vs.serialize(serializer),
Value::Object(name, properties) => serialize_object(name, properties, serializer),
}
}
}
#[cfg(all(
feature = "lossless-serialize-value-leaking",
feature = "lossless-serialize-value-unsafe"
))]
compile_error!("only one `lossless-serialize-value-*` feature can be enabled");
#[cfg(all(
not(feature = "lossless-serialize-value-leaking"),
not(feature = "lossless-serialize-value-unsafe")
))]
fn serialize_object<S: serde::Serializer>(
_name: &str,
properties: &Properties,
serializer: S,
) -> Result<S::Ok, S::Error> {
match properties {
Properties::List(vs) => vs.serialize(serializer),
Properties::Map(vs) => vs.serialize(serializer),
}
}
#[cfg(any(
feature = "lossless-serialize-value-leaking",
feature = "lossless-serialize-value-unsafe"
))]
fn serialize_object<S: serde::Serializer>(
name: &str,
properties: &Properties,
serializer: S,
) -> Result<S::Ok, S::Error> {
use serde::ser::{SerializeStructVariant, SerializeTupleVariant};
match properties {
Properties::List(vs) => {
let mut ser = serializer.serialize_tuple_variant("", 0, intern(name), vs.len())?;
for v in vs {
ser.serialize_field(v)?;
}
ser.end()
}
Properties::Map(vs) => {
let mut ser = serializer.serialize_struct_variant("", 0, intern(name), vs.len())?;
for (k, v) in vs {
ser.serialize_field(intern(k.as_str()), v)?;
}
ser.end()
}
}
}
#[cfg(feature = "lossless-serialize-value-leaking")]
fn intern(s: &str) -> &'static str {
use hashbrown::HashSet;
use std::cell::RefCell;
thread_local! {
static STRINGS: RefCell<HashSet<&'static str>> = RefCell::new(HashSet::new());
}
STRINGS.with_borrow_mut(|cache| *cache.get_or_insert_with(s, |s| Box::leak(s.into())))
}
#[cfg(feature = "lossless-serialize-value-unsafe")]
fn intern(s: &str) -> &'static str {
unsafe { std::mem::transmute(s) }
}
#[cfg(test)]
mod tests {
use super::*;
use serde::Deserialize;
fn from_str<'de, T: Deserialize<'de>>(s: &'de str) -> Result<T, String> {
let mut de = crate::de::Deserializer::from_str(s);
T::deserialize(&mut de).map_err(|e| format!("{e}"))
}
fn from_str_<'de, T: Deserialize<'de>>(s: &'de str) -> Result<T, String> {
let mut de = crate::de::Deserializer::from_str(s).with_value_objects(false);
T::deserialize(&mut de).map_err(|e| format!("{e}"))
}
#[test]
fn test_number() {
assert_eq!(Ok(Value::Number(Number(N::PosInt(123)))), from_str("123"));
assert_eq!(Ok(Value::Number(Number(N::NegInt(-123)))), from_str("-123"));
assert_eq!(
Ok(Value::Number(Number(N::PosInt(u64::MAX)))),
from_str("18446744073709551615")
);
assert_eq!(
Ok(Value::Number(Number(N::NegInt(i64::MIN)))),
from_str("-9223372036854775808")
);
assert_eq!(
Ok(Value::Number(Number(N::Float(-123.0)))),
from_str("-123.0")
);
assert_eq!(
Ok(Value::Number(Number(N::Float(0.0000000009)))),
from_str("0.0000000009")
);
assert_eq!(
Ok(Value::Number(Number(N::Float(-123.000000000123)))),
from_str("-123.000000000123")
);
}
#[test]
fn test_bool() {
assert_eq!(Ok(Value::Bool(true)), from_str("true"));
assert_eq!(Ok(Value::Bool(false)), from_str("false"));
}
#[test]
fn test_null() {
assert_eq!(Ok(Value::Null), from_str("null"));
}
#[test]
fn test_string() {
assert_eq!(
Ok(Value::String("hello, world".to_string())),
from_str(r#""hello, world""#)
);
}
#[test]
fn test_list() {
assert_eq!(Ok(Value::from([1u8, 2, 3, 4])), from_str("[1, 2, 3, 4]"));
assert_eq!(
Ok(Value::List(vec![
"a".into(),
"b".into(),
"c".into(),
"d".into()
])),
from_str(r#"["a", "b", "c", "d"]"#)
);
}
#[test]
fn test_map() {
assert_eq!(Ok(Value::Map(MapImpl::new())), from_str("{}"));
assert_eq!(
Ok(Value::from([(1, "foo"), (2, "bar"), (3, "quux")])),
from_str(r#"{1: "foo", 2: "bar", 3: "quux"}"#)
);
}
#[test]
fn test_map_nested() {
assert_eq!(
Ok(Value::from([
("abc", [(1, 10), (2, 20)]),
("def", [(3, 30), (4, 40)]),
])),
from_str(
r#"
{
"abc": {1: 10, 2: 20},
"def": {3: 30, 4: 40},
}"#
)
);
}
fn identifier(s: &str) -> Identifier {
Identifier::try_from(s).unwrap()
}
#[test]
fn test_object() {
assert_eq!(
Ok(Value::Object(
"Foo".try_into().unwrap(),
Properties::List([Value::from(1), Value::from(true), Value::from(3.45f64)].into())
)),
from_str(r#"Foo(1, true, 3.45)"#)
);
assert_eq!(
Ok(Value::Object(
"Bar".try_into().unwrap(),
Properties::Map(
[
(identifier("x"), Value::from(1)),
(identifier("agile"), Value::from(true))
]
.into()
)
)),
from_str(r#"Bar { x: 1, agile: true }"#)
);
assert_eq!(
Ok(Value::Object(
identifier("Quux"),
Properties::List([].into())
)),
from_str(r#"Quux()"#)
);
assert_eq!(
Ok(Value::Object(
identifier("Quux"),
Properties::Map([].into())
)),
from_str(r#"Quux{}"#)
);
}
fn from_json<'de, T: Deserialize<'de>>(s: &'de str) -> Result<T, String> {
serde_json::from_str::<T>(s).map_err(|e| format!("{e}"))
}
#[test]
fn parse_json_into_sono_value_0() {
assert_eq!(Ok("foo".into()), from_json::<Value>(r#""foo""#));
assert_eq!(Ok(u32::MAX.into()), from_json::<Value>(r#"4294967295"#));
assert_eq!(Ok(4.29.into()), from_json::<Value>(r#"4.29"#));
assert_eq!(Ok(false.into()), from_json::<Value>(r#"false"#));
assert_eq!(Ok(true.into()), from_json::<Value>(r#"true"#));
assert_eq!(Ok(().into()), from_json::<Value>(r#"null"#));
assert_eq!(
Ok(["a", "b", "c"].into()),
from_json::<Value>(r#"["a", "b", "c"]"#)
);
assert_eq!(
Ok([("a", 1), ("b", 2), ("c", 3)].into()),
from_json::<Value>(r#"{"a": 1, "b": 2, "c": 3}"#)
);
}
#[test]
fn parse_json_into_sono_value_1() {
#[derive(Deserialize, Debug, PartialEq, Eq)]
struct Foo {
name: &'static str,
age: usize,
more: Value,
}
assert_eq!(
Ok(Foo {
name: "dana",
age: 42,
more: [("abc", Value::from(true)), ("def", Value::from(22))].into()
}),
from_json::<Foo>(r#"{ "age": 42, "name": "dana", "more": {"abc": true, "def": 22} }"#)
);
}
#[test]
fn parse_sono_into_json_value_0() {
use serde_json::{json, Value};
assert_eq!(Ok(json!("foo")), from_str_(r#""foo""#));
assert_eq!(Ok(json!(u32::MAX)), from_str_(r#"4294967295"#));
assert_eq!(Ok(json!(4.29)), from_str_(r#"4.29"#));
assert_eq!(Ok(json!(false)), from_str_(r#"false"#));
assert_eq!(Ok(json!(true)), from_str_(r#"true"#));
assert_eq!(Ok(Value::Null), from_str_(r#"null"#));
assert_eq!(Ok(json!(["a", "b", "c"])), from_str_(r#"["a", "b", "c"]"#));
assert_eq!(
Ok(json!({"a": 1, "b": 2, "c": 3})),
from_str_(r#"{"a": 1, "b": 2, "c": 3}"#)
);
}
#[test]
fn parse_sono_into_json_value_1() {
use serde_json::{json, Value};
#[derive(Deserialize, Debug, PartialEq, Eq)]
struct Foo {
name: &'static str,
age: usize,
more: Value,
}
assert_eq!(
Ok(Foo {
name: "dana",
age: 42,
more: json!({"abc": true, "def": 22}),
}),
from_str_::<Foo>(r#"Foo {age: 42, name: "dana", more: {"abc": true, "def": 22} }"#)
);
}
#[test]
fn parse_sono_into_json_value_2() {
use serde_json::{json, Value};
#[derive(Deserialize, Debug, PartialEq, Eq)]
struct Foo {
more: Value,
}
assert_eq!(
Ok(Foo {
more: json!({"abc": true, "def": 22}),
}),
from_str_::<Foo>(r#"Foo {more: Quux {"abc": true, "def": 22} }"#)
);
}
#[cfg(any(
feature = "lossless-serialize-value-leaking",
feature = "lossless-serialize-value-unsafe"
))]
mod lossless {
use std::collections::HashMap;
use super::identifier;
use crate::{value::Identifier, Properties, Value};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
struct LosslessSono {
one: usize,
two: char,
three: String,
four: Vec<String>,
five: HashMap<usize, bool>,
more: Value,
}
impl LosslessSono {
fn example_value() -> Self {
LosslessSono {
one: 32,
two: 'x',
three: "hello".into(),
four: vec!["a".into(), "b".into(), "d".into()],
five: {
let mut m = HashMap::new();
m.insert(11, true);
m.insert(22, false);
m.insert(33, true);
m
},
more: Value::List(vec![
Value::Object(
identifier("hoho"),
Properties::List([1.into(), 2.into(), 3.into()].into()),
),
Value::Object(
identifier("hihi"),
Properties::Map(
[
(identifier("foo"), 'x'.into()),
(identifier("bar"), 'y'.into()),
]
.into(),
),
),
]),
}
}
}
#[derive(Deserialize, Debug, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
#[allow(dead_code)]
enum More {
Hoho(usize, usize, usize),
Hihi { foo: char, bar: char },
}
impl TryFrom<&Value> for More {
type Error = String;
fn try_from(value: &Value) -> Result<Self, Self::Error> {
match value.as_object().map(|(id, props)| (&**id, props)) {
Some(("hoho", Properties::List(props))) => match &props[..] {
[x, y, z] => Ok(More::Hoho(
x.as_u64().unwrap_or(0) as usize,
y.as_u64().unwrap_or(0) as usize,
z.as_u64().unwrap_or(0) as usize,
)),
_ => Err("not exactly three positional properties".to_string()),
},
Some(("hihi", Properties::Map(props))) => {
match (props.get("foo"), props.get("bar")) {
(Some(Value::String(foo)), Some(Value::String(bar))) => {
Ok(More::Hihi {
foo: foo.chars().next().unwrap(),
bar: bar.chars().next().unwrap(),
})
}
_ => Err("missing properties or invalid values".to_string()),
}
}
_ => Err("unexpected value".to_string()),
}
}
}
#[derive(Deserialize, Debug)]
#[allow(dead_code)]
struct LosslessJson {
one: usize,
two: char,
three: String,
four: Vec<String>,
five: HashMap<usize, bool>,
more: Vec<More>,
}
#[test]
fn test_sono_to_str_and_back_lossless() {
let f = LosslessSono::example_value();
let sono = crate::to_string(&f).unwrap();
let mut de = crate::de::Deserializer::from_str(&sono);
let g = LosslessSono::deserialize(&mut de).unwrap();
assert_eq!(f, g);
}
#[test]
fn test_value_object_as_externally_tagged_json() {
let f = LosslessSono::example_value();
let json = serde_json::to_string_pretty(&f).unwrap();
let g = serde_json::from_str::<LosslessJson>(&json).unwrap();
assert_eq!(f.one, g.one);
assert_eq!(f.two, g.two);
assert_eq!(f.three, g.three);
assert_eq!(f.four, g.four);
assert_eq!(f.five, g.five);
assert_eq!(
f.more
.as_list()
.unwrap()
.iter()
.map(|v| More::try_from(v).unwrap())
.collect::<Vec<_>>(),
g.more
);
}
#[test]
fn test_object_with_invalid_identifier() {
for name in ["foo-barr", "null", "true", "false"] {
let ident: Result<Identifier, _> = name.try_into();
assert!(ident.is_err(), "for input: {name:?}");
let err = ident.unwrap_err();
assert!(
err.starts_with("Not an identifier"),
"for input: {name:?}; actuall error: {err}",
);
}
}
}
}