#[cfg(feature = "serde")]
mod test {
extern crate alloc;
use alloc::borrow::ToOwned;
use alloc::collections::BTreeMap;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::fmt;
use clojure_reader::de::from_str;
use serde::de::{self, Visitor};
use serde_derive::Deserialize;
#[test]
fn super_simple_types() {
assert_eq!(42, from_str::<u8>("42").unwrap());
assert_eq!(42, from_str::<i64>("42").unwrap());
assert_eq!(424242, from_str::<i64>("424242").unwrap());
let res = from_str::<u8>("424242");
let Err(res) = res else { panic!() };
let expected = "EdnError { code: Serde(\"can't convert Err(TryFromIntError(())) into u8\"), line: None, column: None, ptr: None }";
assert_eq!(format!("{res}"), expected);
assert_eq!("lol cats", from_str::<String>(r#""lol cats""#).unwrap());
assert_eq!("lol 猫s", from_str::<&str>(r#""lol 猫s""#).unwrap());
assert_eq!(false, from_str("false").unwrap());
}
#[test]
fn maybe() {
#[derive(Deserialize, PartialEq, Debug)]
#[serde(rename_all = "kebab-case")]
struct Test {
#[serde(alias = "maybe_int")]
maybe_int: Option<u32>,
maybe_str: Option<String>,
}
let res = from_str::<Test>(r#"{:maybe-int 42, 42 "neko", :maybe-str "gato"}"#).unwrap();
assert_eq!(res, Test { maybe_int: Some(42), maybe_str: Some("gato".to_string()) });
}
#[test]
fn rejects_trailing_input() {
assert_eq!(42, from_str::<u8>("42 , \n").unwrap());
assert_eq!(42, from_str::<u8>("42 ; comment").unwrap());
assert_eq!(42, from_str::<u8>("42 ; comment\n").unwrap());
assert!(from_str::<u8>("42 43").is_err());
assert!(from_str::<u8>("42 ; comment\n43").is_err());
}
#[test]
fn ignored_struct_fields_accept_any_edn_value() {
#[derive(Deserialize, PartialEq, Debug)]
struct Cat {
known: u8,
}
assert_eq!(Cat { known: 3 }, from_str::<Cat>(r#"{1 #foo/bar 2, :known 3}"#).unwrap());
}
#[test]
fn map_deserialization_handles_empty_and_numeric_keys() {
assert_eq!(from_str::<BTreeMap<String, String>>("{}").unwrap(), BTreeMap::new());
assert_eq!(
from_str::<BTreeMap<i64, String>>(r#"{1 "a" 2 "b"}"#).unwrap(),
BTreeMap::from([(1, "a".to_owned()), (2, "b".to_owned())])
);
}
#[test]
fn large_unsigned_values() {
#[cfg(feature = "arbitrary-nums")]
assert_eq!(from_str::<u64>("18446744073709551615N").unwrap(), u64::MAX);
#[cfg(not(feature = "arbitrary-nums"))]
assert!(from_str::<u64>("18446744073709551615").is_err());
}
#[test]
fn errors() {
let edn_str = r"cat in your nums";
let res = from_str::<u8>(edn_str);
let expected = r#"Err(EdnError { code: Serde("cannot convert Symbol(\"cat\") to i64"), line: None, column: None, ptr: None })"#;
assert!(res.is_err());
assert_eq!(format!("{res:?}"), expected);
let res = from_str::<f32>(edn_str);
assert!(res.is_err());
}
#[test]
fn integer_overflow_errors() {
assert_eq!(
format!("{:?}", from_str::<i8>("128")),
"Err(EdnError { code: Serde(\"can't convert Err(TryFromIntError(())) into i8\"), line: None, column: None, ptr: None })"
);
assert_eq!(
format!("{:?}", from_str::<i16>("32768")),
"Err(EdnError { code: Serde(\"can't convert Err(TryFromIntError(())) into i16\"), line: None, column: None, ptr: None })"
);
assert_eq!(
format!("{:?}", from_str::<i32>("2147483648")),
"Err(EdnError { code: Serde(\"can't convert Err(TryFromIntError(())) into i32\"), line: None, column: None, ptr: None })"
);
assert_eq!(
format!("{:?}", from_str::<u16>("65536")),
"Err(EdnError { code: Serde(\"can't convert Err(TryFromIntError(())) into u16\"), line: None, column: None, ptr: None })"
);
assert_eq!(
format!("{:?}", from_str::<u32>("4294967296")),
"Err(EdnError { code: Serde(\"can't convert Err(TryFromIntError(())) into u32\"), line: None, column: None, ptr: None })"
);
assert_eq!(
format!("{:?}", from_str::<u64>("-1")),
"Err(EdnError { code: Serde(\"can't convert Err(TryFromIntError(())) into u64\"), line: None, column: None, ptr: None })"
);
}
#[test]
fn byte_buf() {
#[derive(Debug, PartialEq)]
struct Bytes(Vec<u8>);
impl<'de> serde::Deserialize<'de> for Bytes {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct BytesVisitor;
impl<'de> Visitor<'de> for BytesVisitor {
type Value = Bytes;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("bytes")
}
fn visit_bytes<E>(self, _v: &[u8]) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Bytes(_v.to_vec()))
}
fn visit_byte_buf<E>(self, _v: Vec<u8>) -> Result<Self::Value, E>
where
E: de::Error,
{
Err(E::custom("expected transient bytes"))
}
}
deserializer.deserialize_bytes(BytesVisitor)
}
}
#[derive(Debug)]
struct ByteBuf;
impl<'de> serde::Deserialize<'de> for ByteBuf {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct ByteBufVisitor;
impl<'de> Visitor<'de> for ByteBufVisitor {
type Value = ByteBuf;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a byte buffer")
}
fn visit_byte_buf<E>(self, _v: Vec<u8>) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(ByteBuf)
}
}
deserializer.deserialize_byte_buf(ByteBufVisitor)
}
}
assert_eq!(from_str::<Bytes>("[1 2 3]").unwrap(), Bytes(vec![1, 2, 3]));
assert_eq!(from_str::<Bytes>("(1 2 3)").unwrap(), Bytes(vec![1, 2, 3]));
assert!(matches!(from_str::<ByteBuf>("[1 2 3]"), Ok(ByteBuf)));
assert_eq!(from_str::<Vec<u8>>("[1 2 3]").unwrap(), vec![1, 2, 3]);
assert_eq!(from_str::<Vec<u8>>("(1 2 3)").unwrap(), vec![1, 2, 3]);
assert_eq!(
format!("{:?}", from_str::<Vec<u8>>("[256]")),
"Err(EdnError { code: Serde(\"can't convert Err(TryFromIntError(())) into u8\"), line: None, column: None, ptr: None })"
);
}
#[test]
fn borrowed_bytes_are_unsupported() {
#[derive(Deserialize, PartialEq, Debug)]
struct SomeBytes<'a> {
data: &'a [u8],
}
assert_eq!(
format!("{:?}", from_str::<SomeBytes<'_>>(r#"{:data [1 2 3]}"#)),
"Err(EdnError { code: Serde(\"invalid type: byte array, expected a borrowed byte array\"), line: None, column: None, ptr: None })"
);
assert_eq!(
format!("{:?}", from_str::<SomeBytes<'_>>(r#"{:data (1 2 3)}"#)),
"Err(EdnError { code: Serde(\"invalid type: byte array, expected a borrowed byte array\"), line: None, column: None, ptr: None })"
);
}
#[test]
fn seq() {
let expected: [i64; 4] = [1, 4, 42, 3];
let res = from_str::<[i64; 4]>("[1 4 42 3]");
assert_eq!(expected, res.unwrap());
let expected: Vec<u16> = vec![1, 4, 42, 3];
let res = from_str::<Vec<u16>>("[1 4 42 3]");
assert_eq!(expected, res.unwrap());
let expected: Vec<u16> = vec![1, 3, 4, 42];
let res = from_str::<Vec<u16>>("#{1 4 42 3}");
assert_eq!(expected, res.unwrap());
}
#[test]
fn unit_struct() {
#[derive(Deserialize, PartialEq, Debug)]
struct Unit;
assert_eq!(Unit {}, from_str::<Unit>("").unwrap());
assert_eq!(Unit {}, from_str::<Unit>("{}").unwrap());
#[derive(Deserialize, PartialEq, Debug)]
struct UnitNone {
unit: Unit,
nothing: Option<()>,
}
assert_eq!(
UnitNone { unit: Unit, nothing: None },
from_str::<UnitNone>(r#"{"unit" {} "nothing" nil}"#).unwrap()
);
}
#[test]
fn empty_map_uses_struct_defaults() {
#[derive(Deserialize, PartialEq, Debug)]
#[serde(default, rename_all = "kebab-case")]
struct CatDefaults {
nap_spots: u8,
favorite_treat: String,
}
impl Default for CatDefaults {
fn default() -> Self {
Self { nap_spots: 9, favorite_treat: "tuna".to_owned() }
}
}
assert_eq!(CatDefaults::default(), from_str::<CatDefaults>("{}").unwrap());
assert_eq!(
CatDefaults { nap_spots: 2, favorite_treat: "tuna".to_owned() },
from_str::<CatDefaults>(r#"{:nap-spots 2}"#).unwrap()
);
}
#[test]
fn new_type() {
#[derive(Deserialize, PartialEq, Debug)]
struct Meters(i64);
assert_eq!(Meters(420), from_str(r#"420"#).unwrap());
}
#[test]
fn tuple_struct() {
#[derive(Deserialize, PartialEq, Debug)]
struct Test {
int: (u32, i64),
}
assert_eq!(Test { int: (42, -420) }, from_str(r#"{"int" (42 -420)}"#).unwrap());
}
#[test]
fn test_simple_struct() {
#[derive(Deserialize, PartialEq, Debug)]
struct Test {
int: u32,
c: char,
}
assert_eq!(Test { int: 42, c: 'c' }, from_str(r#"{"int" 42 :c \c}"#).unwrap());
assert_eq!(Test { int: 42, c: 'n' }, from_str(r#"{:int 42 "c" \n}"#).unwrap());
}
#[test]
fn test_struct() {
#[derive(Deserialize, PartialEq, Debug)]
struct Test {
int: u32,
seq: Vec<String>,
}
#[derive(Deserialize, PartialEq, Debug)]
struct Tests {
tests: Vec<Test>,
}
let edn_str = r#"{"int" 1, "seq" ["a","b"]}"#;
let expected = Test { int: 1, seq: vec!["a".to_owned(), "b".to_owned()] };
assert_eq!(expected, from_str(edn_str).unwrap());
let edn_str = r#"{:int 1, "seq" ["a","b"]}"#;
assert_eq!(expected, from_str(edn_str).unwrap());
let edn_str = r#"{:tests [{:int 1, "seq" ["a","b"]} {:int 2, "seq" ["a","b"]}]}"#;
let expected = Tests {
tests: vec![
Test { int: 1, seq: vec!["a".to_owned(), "b".to_owned()] },
Test { int: 2, seq: vec!["a".to_owned(), "b".to_owned()] },
],
};
assert_eq!(expected, from_str(edn_str).unwrap());
}
#[test]
#[cfg(feature = "floats")]
fn complex_struct() {
#[derive(Deserialize, PartialEq, Debug)]
struct Nums {
a: i8,
b: i16,
cat: i32,
double: f64,
trunk: f32,
}
#[derive(Deserialize, PartialEq, Debug)]
struct Test {
int: u64,
nums: Nums,
}
let edn_str = r#"{:int 1, :nums {:a 4, :b 2, :cat 42, :double 42.42, :trunk 42.0}}"#;
let expected = Test { int: 1, nums: Nums { a: 4, b: 2, cat: 42, double: 42.42, trunk: 42.0 } };
assert_eq!(expected, from_str(edn_str).unwrap());
}
#[test]
fn test_enum() {
#[derive(Deserialize, PartialEq, Debug)]
enum E {
Unit,
AnotherUnit,
Newtype(u32),
Tuple(u32, u32),
Struct { a: u32, b: usize },
}
#[derive(Deserialize, PartialEq, Debug)]
struct Test {
e: E,
}
assert_eq!(Test { e: E::Unit }, from_str::<Test>(r#"{:e #E/Unit nil}"#).unwrap());
assert_eq!(E::Unit, from_str::<E>(r#"#E/Unit nil"#).unwrap());
assert_eq!(E::Unit, from_str::<E>(r#"#E/Unit :Unit"#).unwrap());
assert_eq!(E::Unit, from_str::<E>(r#"#E/Unit sillycat"#).unwrap());
assert_eq!(E::AnotherUnit, from_str::<E>(r#"#E/AnotherUnit nil"#).unwrap());
assert_eq!(E::Newtype(1), from_str::<E>(r#"#E/Newtype 1"#).unwrap());
assert_eq!(E::Tuple(1, 2), from_str::<E>(r#"#E/Tuple [1 2]"#).unwrap());
assert_eq!(E::Struct { a: 1, b: 42 }, from_str::<E>(r#"#E/Struct {:a 1, :b 42}"#,).unwrap());
assert_eq!(
format!("{:?}", from_str::<E>(r#"#B/Unit sillycat"#)),
"Err(EdnError { code: Serde(\"namespace in B/Unit can't be matched to E\"), line: None, column: None, ptr: None })"
);
assert_eq!(
format!("{:?}", from_str::<E>(r#""#)),
"Err(EdnError { code: Serde(\"can't convert Nil into Tagged for enum\"), line: None, column: None, ptr: None })"
);
assert_eq!(
format!("{:?}", from_str::<E>(r#"#BUnit sillycat"#)),
"Err(EdnError { code: Serde(\"Expected namespace in BUnit for Tagged for enum\"), line: None, column: None, ptr: None })"
);
}
#[test]
fn internally_tagged_enum() {
#[derive(Deserialize, PartialEq, Debug)]
#[serde(tag = "cafe", rename_all = "kebab-case", rename_all_fields = "kebab-case")]
enum CatCafe {
BeforeOpening,
HerdingKittens { count: u8 },
ServingTreats { bowls: u8, favorite_flavor: String },
NapTime { sunny_spots: u8 },
}
assert_eq!(CatCafe::BeforeOpening, from_str::<CatCafe>(r#"{:cafe "before-opening"}"#).unwrap());
assert_eq!(
CatCafe::HerdingKittens { count: 7 },
from_str::<CatCafe>(r#"{:cafe "herding-kittens", :count 7}"#).unwrap()
);
assert_eq!(
CatCafe::ServingTreats { bowls: 3, favorite_flavor: "salmon".to_string() },
from_str::<CatCafe>(r#"{:cafe "serving-treats", :bowls 3, :favorite-flavor "salmon"}"#)
.unwrap()
);
assert_eq!(
CatCafe::NapTime { sunny_spots: 2 },
from_str::<CatCafe>(r#"{:cafe "nap-time", :sunny-spots 2}"#).unwrap()
);
}
#[test]
fn serde_errors() {
assert_eq!(
format!("{:?}", from_str::<String>(r#"#E/Tuple [4/2]"#)),
"Err(EdnError { code: Serde(\"Don't know how to convert Tagged(\\\"E/Tuple\\\", Vector([Rational((4, 2))])) into any\"), line: None, column: None, ptr: None })"
);
#[derive(Deserialize, PartialEq, Debug)]
struct SomeBytes<'a> {
data: &'a [u8],
}
assert_eq!(
format!("{:?}", from_str::<SomeBytes<'_>>(r#"[4/2]"#)),
"Err(EdnError { code: Serde(\"can't convert Rational((4, 2)) into bytes\"), line: None, column: None, ptr: None })"
);
}
}