use crate::edn::{Edn, Error};
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::str::FromStr;
pub(crate) mod parse;
#[allow(clippy::missing_errors_doc)]
pub trait Deserialize: Sized {
fn deserialize(edn: &Edn) -> Result<Self, Error>;
}
fn build_deserialize_error(edn: &Edn, type_: &str) -> Error {
Error::Deserialize(format!("couldn't convert `{}` into `{}`", edn, type_))
}
impl Deserialize for () {
fn deserialize(edn: &Edn) -> Result<Self, Error> {
match edn {
Edn::Nil => Ok(()),
_ => Err(build_deserialize_error(edn, "unit")),
}
}
}
macro_rules! impl_deserialize_float {
( $( $name:ty ),+ ) => {
$(
impl Deserialize for $name
{
fn deserialize(edn: &Edn) -> Result<Self, Error> {
edn
.to_float()
.ok_or_else(|| build_deserialize_error(&edn, "float"))
.map(|u| u as $name)
}
}
)+
};
}
impl_deserialize_float!(f32, f64);
impl Deserialize for crate::Double {
fn deserialize(edn: &Edn) -> Result<Self, Error> {
edn.to_float()
.ok_or_else(|| build_deserialize_error(edn, "edn_rs::Double"))
.map(std::convert::Into::into)
}
}
macro_rules! impl_deserialize_int {
( $( $name:ty ),+ ) => {
$(
impl Deserialize for $name
{
fn deserialize(edn: &Edn) -> Result<Self, Error> {
edn
.to_int()
.ok_or_else(|| build_deserialize_error(&edn, "int"))
.map(|u| u as $name)
}
}
)+
};
}
impl_deserialize_int!(isize, i8, i16, i32, i64);
macro_rules! impl_deserialize_uint {
( $( $name:ty ),+ ) => {
$(
impl Deserialize for $name
{
fn deserialize(edn: &Edn) -> Result<Self, Error> {
edn
.to_uint()
.ok_or_else(|| build_deserialize_error(&edn, "uint"))
.map(|u| u as $name)
}
}
)+
};
}
impl_deserialize_uint!(usize, u8, u16, u32, u64);
impl Deserialize for bool {
fn deserialize(edn: &Edn) -> Result<Self, Error> {
edn.to_bool()
.ok_or_else(|| build_deserialize_error(edn, "bool"))
}
}
impl Deserialize for String {
fn deserialize(edn: &Edn) -> Result<Self, Error> {
match edn {
Edn::Str(s) => {
if s.starts_with('\"') {
Ok(s.replace('\"', ""))
} else {
Ok(s.clone())
}
}
e => Ok(e.to_string()),
}
}
}
impl Deserialize for char {
fn deserialize(edn: &Edn) -> Result<Self, Error> {
edn.to_char()
.ok_or_else(|| build_deserialize_error(edn, "char"))
}
}
impl<T> Deserialize for Vec<T>
where
T: Deserialize,
{
fn deserialize(edn: &Edn) -> Result<Self, Error> {
match edn {
Edn::Vector(_) => Ok(edn
.iter_some()
.ok_or_else(|| Error::Iter(format!("Could not create iter from {:?}", edn)))?
.map(|e| Deserialize::deserialize(e))
.collect::<Result<Vec<T>, Error>>()?),
Edn::List(_) => Ok(edn
.iter_some()
.ok_or_else(|| Error::Iter(format!("Could not create iter from {:?}", edn)))?
.map(|e| Deserialize::deserialize(e))
.collect::<Result<Vec<T>, Error>>()?),
Edn::Set(_) => Ok(edn
.iter_some()
.ok_or_else(|| Error::Iter(format!("Could not create iter from {:?}", edn)))?
.map(|e| Deserialize::deserialize(e))
.collect::<Result<Vec<T>, Error>>()?),
_ => Err(build_deserialize_error(
edn,
std::any::type_name::<Vec<T>>(),
)),
}
}
}
#[allow(clippy::implicit_hasher)]
impl<T> Deserialize for HashMap<String, T>
where
T: Deserialize,
{
fn deserialize(edn: &Edn) -> Result<Self, Error> {
match edn {
Edn::Map(_) => edn
.map_iter()
.ok_or_else(|| Error::Iter(format!("Could not create iter from {:?}", edn)))?
.map(|(key, e)| {
Ok((
key.to_string(),
Deserialize::deserialize(e).map_err(|_| {
Error::Deserialize(format!(
"Cannot safely deserialize {:?} to {}",
edn, "HashMap"
))
})?,
))
})
.collect::<Result<HashMap<String, T>, Error>>(),
Edn::NamespacedMap(ns, _) => edn
.map_iter()
.ok_or_else(|| Error::Iter(format!("Could not create iter from {:?}", edn)))?
.map(|(key, e)| {
let deser_element = Deserialize::deserialize(e).map_err(|_| {
Error::Deserialize(format!(
"Cannot safely deserialize {:?} to {}",
edn, "HashMap"
))
});
if ns.starts_with(':') {
Ok((ns.to_string() + "/" + key, deser_element?))
} else {
Ok((String::from(':') + ns + "/" + key, deser_element?))
}
})
.collect::<Result<HashMap<String, T>, Error>>(),
_ => Err(build_deserialize_error(
edn,
std::any::type_name::<HashMap<String, T>>(),
)),
}
}
}
impl<T> Deserialize for BTreeMap<String, T>
where
T: Deserialize,
{
fn deserialize(edn: &Edn) -> Result<Self, Error> {
match edn {
Edn::Map(_) => edn
.map_iter()
.ok_or_else(|| Error::Iter(format!("Could not create iter from {:?}", edn)))?
.map(|(key, e)| {
Ok((
key.to_string(),
Deserialize::deserialize(e).map_err(|_| {
Error::Deserialize(format!(
"Cannot safely deserialize {:?} to {}",
edn, "BTreeMap"
))
})?,
))
})
.collect::<Result<BTreeMap<String, T>, Error>>(),
Edn::NamespacedMap(ns, _) => edn
.map_iter()
.ok_or_else(|| Error::Iter(format!("Could not create iter from {:?}", edn)))?
.map(|(key, e)| {
let deser_element = Deserialize::deserialize(e).map_err(|_| {
Error::Deserialize(format!(
"Cannot safely deserialize {:?} to {}",
edn, "BTreeMap"
))
});
if ns.starts_with(':') {
Ok((ns.to_string() + "/" + key, deser_element?))
} else {
Ok((String::from(':') + ns + "/" + key, deser_element?))
}
})
.collect::<Result<BTreeMap<String, T>, Error>>(),
_ => Err(build_deserialize_error(
edn,
std::any::type_name::<BTreeMap<String, T>>(),
)),
}
}
}
#[allow(clippy::implicit_hasher)]
impl<T: std::cmp::Eq + std::hash::Hash> Deserialize for HashSet<T>
where
T: Deserialize,
{
fn deserialize(edn: &Edn) -> Result<Self, Error> {
match edn {
Edn::Set(_) => edn
.set_iter()
.ok_or_else(|| Error::Iter(format!("Could not create iter from {:?}", edn)))?
.map(|e| {
Deserialize::deserialize(e).map_err(|_| {
Error::Deserialize(format!(
"Cannot safely deserialize {:?} to {}",
edn, "HashSet"
))
})
})
.collect::<Result<HashSet<T>, Error>>(),
_ => Err(build_deserialize_error(
edn,
std::any::type_name::<HashSet<T>>(),
)),
}
}
}
impl<T: std::cmp::Eq + std::hash::Hash + std::cmp::Ord> Deserialize for BTreeSet<T>
where
T: Deserialize,
{
fn deserialize(edn: &Edn) -> Result<Self, Error> {
match edn {
Edn::Set(_) => edn
.set_iter()
.ok_or_else(|| Error::Iter(format!("Could not create iter from {:?}", edn)))?
.map(|e| {
Deserialize::deserialize(e).map_err(|_| {
Error::Deserialize(format!(
"Cannot safely deserialize {:?} to {}",
edn, "BTreeSet"
))
})
})
.collect::<Result<BTreeSet<T>, Error>>(),
_ => Err(build_deserialize_error(
edn,
std::any::type_name::<BTreeSet<T>>(),
)),
}
}
}
impl<T> Deserialize for Option<T>
where
T: Deserialize,
{
fn deserialize(edn: &Edn) -> Result<Self, Error> {
match edn {
Edn::Nil => Ok(None),
_ => Ok(Some(from_edn(edn)?)),
}
}
}
pub fn from_str<T: Deserialize>(s: &str) -> Result<T, Error> {
let edn = Edn::from_str(s)?;
from_edn(&edn)
}
pub fn from_edn<T: Deserialize>(edn: &Edn) -> Result<T, Error> {
T::deserialize(edn)
}
#[cfg(test)]
mod test {
use super::*;
use crate::edn::{List, Map, Set, Vector};
use crate::{hmap, hset, map, set};
#[test]
fn unit() {
let nil = "nil";
let unit: () = from_str(nil).unwrap();
assert_eq!(unit, ())
}
#[test]
fn deser_btreeset_with_error() {
let edn = "#{\"a\", 5, \"b\"}";
let err: Result<BTreeSet<usize>, Error> = from_str(edn);
assert_eq!(
err,
Err(Error::Deserialize(
"Cannot safely deserialize Set(Set({Str(\"a\"), Str(\"b\"), UInt(5)})) to BTreeSet"
.to_string()
))
)
}
#[test]
fn from_str_simple_vec() {
let edn = "[1 \"2\" 3.3 :b true \\c]";
assert_eq!(
Edn::from_str(edn),
Ok(Edn::Vector(Vector::new(vec![
Edn::UInt(1),
Edn::Str("2".to_string()),
Edn::Double(3.3.into()),
Edn::Key(":b".to_string()),
Edn::Bool(true),
Edn::Char('c')
])))
);
}
#[test]
fn from_str_list_with_vec() {
let edn = "(1 \"2\" 3.3 :b [true \\c])";
assert_eq!(
Edn::from_str(edn),
Ok(Edn::List(List::new(vec![
Edn::UInt(1),
Edn::Str("2".to_string()),
Edn::Double(3.3.into()),
Edn::Key(":b".to_string()),
Edn::Vector(Vector::new(vec![Edn::Bool(true), Edn::Char('c')]))
])))
);
}
#[test]
fn from_str_list_with_set() {
let edn = "(1 -10 \"2\" 3.3 :b #{true \\c})";
assert_eq!(
Edn::from_str(edn),
Ok(Edn::List(List::new(vec![
Edn::UInt(1),
Edn::Int(-10),
Edn::Str("2".to_string()),
Edn::Double(3.3.into()),
Edn::Key(":b".to_string()),
Edn::Set(Set::new(set![Edn::Bool(true), Edn::Char('c')]))
])))
);
}
#[test]
fn from_str_simple_map() {
let edn = "{:a \"2\" :b true :c nil}";
assert_eq!(
Edn::from_str(edn),
Ok(Edn::Map(Map::new(
map! {":a".to_string() => Edn::Str("2".to_string()),
":b".to_string() => Edn::Bool(true), ":c".to_string() => Edn::Nil}
)))
);
}
#[test]
fn from_str_complex_map() {
let edn = "{:a \"2\" :b [true false] :c #{:A {:a :b} nil}}";
assert_eq!(
Edn::from_str(edn),
Ok(Edn::Map(Map::new(map! {
":a".to_string() =>Edn::Str("2".to_string()),
":b".to_string() => Edn::Vector(Vector::new(vec![Edn::Bool(true), Edn::Bool(false)])),
":c".to_string() => Edn::Set(Set::new(
set!{
Edn::Map(Map::new(map!{":a".to_string() => Edn::Key(":b".to_string())})),
Edn::Key(":A".to_string()),
Edn::Nil}))})))
);
}
#[test]
fn from_str_wordy_str() {
let edn = "[\"hello brave new world\"]";
assert_eq!(
Edn::from_str(edn).unwrap(),
Edn::Vector(Vector::new(vec![Edn::Str(
"hello brave new world".to_string()
)]))
)
}
#[test]
fn namespaced_maps() {
let edn = ":abc{ 0 :val 1 :value}";
assert_eq!(
Edn::from_str(edn).unwrap(),
Edn::NamespacedMap(
"abc".to_string(),
Map::new(map! {
"0".to_string() => Edn::Key(":val".to_string()),
"1".to_string() => Edn::Key(":value".to_string())
})
)
);
}
#[test]
fn uuid() {
let uuid = "#uuid \"af6d8699-f442-4dfd-8b26-37d80543186b\"";
let edn: Edn = Edn::from_str(uuid).unwrap();
assert_eq!(
edn,
Edn::Uuid("af6d8699-f442-4dfd-8b26-37d80543186b".to_string())
)
}
#[test]
fn deserialize_struct_with_vec() {
#[derive(PartialEq, Debug)]
struct Foo {
bar: Vec<Option<usize>>,
}
impl Deserialize for Foo {
fn deserialize(edn: &Edn) -> Result<Self, Error> {
Ok(Foo {
bar: from_edn(&edn[":bar"])?,
})
}
}
let edn_foo = "{:bar [1 nil 3]}";
let foo: Foo = from_str(edn_foo).unwrap();
assert_eq!(
foo,
Foo {
bar: vec![Some(1), None, Some(3)],
}
);
}
#[test]
fn test_sym() {
let edn: Edn = Edn::from_str("(a b c your-hair!-is+_parsed?)").unwrap();
let expected = Edn::List(List::new(vec![
Edn::Symbol("a".to_string()),
Edn::Symbol("b".to_string()),
Edn::Symbol("c".to_string()),
Edn::Symbol("your-hair!-is+_parsed?".to_string()),
]));
assert_eq!(edn, expected);
}
#[test]
fn test_nft() {
let t: Edn = Edn::from_str("tTEST").unwrap();
let f: Edn = Edn::from_str("fTEST").unwrap();
let n: Edn = Edn::from_str("nTEST").unwrap();
let err: Edn = Edn::from_str("fTE").unwrap();
assert_eq!(n, Edn::Symbol("nTEST".to_string()));
assert_eq!(f, Edn::Symbol("fTEST".to_string()));
assert_eq!(t, Edn::Symbol("tTEST".to_string()));
assert_eq!(err, Edn::Symbol("fTE".to_string()));
}
#[test]
fn test_more_sym() {
let edn: Edn = Edn::from_str("(a \\b \"c\" 5 #{hello world})").unwrap();
let expected = Edn::List(List::new(vec![
Edn::Symbol("a".to_string()),
Edn::Char('b'),
Edn::Str("c".to_string()),
Edn::UInt(5usize),
Edn::Set(Set::new(
set! { Edn::Symbol("hello".to_string()), Edn::Symbol("world".to_string()) },
)),
]));
assert_eq!(edn, expected);
}
#[test]
fn namespaced_maps_navigation() {
let edn_str = ":abc{ 0 :val 1 :value}";
let edn = Edn::from_str(edn_str).unwrap();
assert_eq!(edn[0], Edn::Key(":val".to_string()));
assert_eq!(edn["0"], Edn::Key(":val".to_string()));
assert_eq!(edn[1], Edn::Key(":value".to_string()));
assert_eq!(edn["1"], Edn::Key(":value".to_string()));
}
#[test]
fn deser_namespaced_btreemap() {
let ns_map = Edn::NamespacedMap(
"abc".to_string(),
Map::new(map! {
"0".to_string() => Edn::Key(":val".to_string()),
"1".to_string() => Edn::Key(":value".to_string())
}),
);
let expected = map! {
":abc/0".to_string() => ":val".to_string(),
":abc/1".to_string() => ":value".to_string()
};
let map: std::collections::BTreeMap<String, String> = from_edn(&ns_map).unwrap();
assert_eq!(map, expected);
}
#[test]
fn deser_namespaced_hashmap() {
let ns_map = Edn::NamespacedMap(
"abc".to_string(),
Map::new(map! {
"0".to_string() => Edn::Key(":val".to_string()),
"1".to_string() => Edn::Key(":value".to_string())
}),
);
let expected = hmap! {
":abc/0".to_string() => ":val".to_string(),
":abc/1".to_string() => ":value".to_string()
};
let map: std::collections::HashMap<String, String> = from_edn(&ns_map).unwrap();
assert_eq!(map, expected);
}
#[test]
fn deser_btreemap() {
let ns_map = Edn::Map(Map::new(map! {
":a".to_string() => Edn::Vector(Vector::new(vec![Edn::Key(":val".to_string())])),
":b".to_string() => Edn::Vector(Vector::new(vec![Edn::Key(":value".to_string())]))
}));
let expected = map! {
":a".to_string() => vec![":val".to_string()],
":b".to_string() => vec![":value".to_string()]
};
let map: std::collections::BTreeMap<String, Vec<String>> = from_edn(&ns_map).unwrap();
assert_eq!(map, expected);
}
#[test]
fn deser_hashmap() {
let ns_map = Edn::Map(Map::new(map! {
":a".to_string() => Edn::Bool(true),
":b".to_string() => Edn::Bool(false)
}));
let expected = hmap! {
":a".to_string() => true,
":b".to_string() => false
};
let map: std::collections::HashMap<String, bool> = from_edn(&ns_map).unwrap();
assert_eq!(map, expected);
}
#[test]
fn deser_btreeset() {
let set = Edn::Set(Set::new(set! {
Edn::UInt(4),
Edn::UInt(5),
Edn::UInt(6)
}));
let expected = set! {
4,
5,
6,
};
let deser_set: std::collections::BTreeSet<usize> = from_edn(&set).unwrap();
assert_eq!(deser_set, expected);
}
#[test]
fn deser_hashset() {
let set = Edn::Set(Set::new(set! {
Edn::Double(4.6.into()),
Edn::Double(5.6.into()),
Edn::Double(6.6.into())
}));
let expected = hset! {
crate::Double::from(4.6),
crate::Double::from(5.6),
crate::Double::from(6.6),
};
let deser_set: std::collections::HashSet<crate::Double> = from_edn(&set).unwrap();
assert_eq!(deser_set, expected);
}
#[test]
fn weird_input() {
let edn = "{:a]";
assert_eq!(
Edn::from_str(edn),
Err(Error::ParseEdn(
"Could not identify symbol index".to_string()
))
);
}
#[test]
fn string_with_empty_set() {
assert_eq!("\"#{}\"", format!("{}", Edn::from_str("\"#{}\"").unwrap()));
}
}