use serde::{ de::{Deserialize, DeserializeOwned}, ser::Serialize, }; use std::{collections::BTreeMap, fmt::Debug, fs::File, io::Cursor, path::Path}; use crate::{ stream::{private::Sealed, Event, OwnedEvent, Writer}, Date, Deserializer, Dictionary, Error, Integer, Serializer, Uid, Value, }; struct VecWriter { events: Vec, } impl VecWriter { pub fn new() -> VecWriter { VecWriter { events: Vec::new() } } pub fn into_inner(self) -> Vec { self.events } } impl Writer for VecWriter { fn write_start_array(&mut self, len: Option) -> Result<(), Error> { self.events.push(Event::StartArray(len)); Ok(()) } fn write_start_dictionary(&mut self, len: Option) -> Result<(), Error> { self.events.push(Event::StartDictionary(len)); Ok(()) } fn write_end_collection(&mut self) -> Result<(), Error> { self.events.push(Event::EndCollection); Ok(()) } fn write_boolean(&mut self, value: bool) -> Result<(), Error> { self.events.push(Event::Boolean(value)); Ok(()) } fn write_data(&mut self, value: &[u8]) -> Result<(), Error> { self.events.push(Event::Data(value.to_owned().into())); Ok(()) } fn write_date(&mut self, value: Date) -> Result<(), Error> { self.events.push(Event::Date(value)); Ok(()) } fn write_integer(&mut self, value: Integer) -> Result<(), Error> { self.events.push(Event::Integer(value)); Ok(()) } fn write_real(&mut self, value: f64) -> Result<(), Error> { self.events.push(Event::Real(value)); Ok(()) } fn write_string(&mut self, value: &str) -> Result<(), Error> { self.events.push(Event::String(value.to_owned().into())); Ok(()) } fn write_uid(&mut self, value: Uid) -> Result<(), Error> { self.events.push(Event::Uid(value)); Ok(()) } } impl Sealed for VecWriter {} fn new_serializer() -> Serializer { Serializer::new(VecWriter::new()) } fn new_deserializer(events: Vec) -> Deserializer>> { let result_events = events.into_iter().map(Ok).collect(); Deserializer::new(result_events) } fn assert_roundtrip(obj: T, comparison: Option<&[Event]>) where T: Debug + DeserializeOwned + PartialEq + Serialize, { let mut se = new_serializer(); obj.serialize(&mut se).unwrap(); let events = se.into_inner().into_inner(); if let Some(comparison) = comparison { assert_eq!(&events[..], comparison); } let mut de = new_deserializer(events); let new_obj = T::deserialize(&mut de).unwrap(); assert_eq!(new_obj, obj); } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] enum Animal { Cow, Dog(DogOuter), Frog(Result, Option>), Cat { age: Integer, name: String, firmware: Option>, }, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] struct DogOuter { inner: Vec, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] struct DogInner { a: (), b: usize, c: Vec, d: Option, } #[test] fn cow() { let cow = Animal::Cow; let comparison = &[Event::String("Cow".into())]; assert_roundtrip(cow, Some(comparison)); } #[test] fn dog() { let dog = Animal::Dog(DogOuter { inner: vec![DogInner { a: (), b: 12, c: vec!["a".to_string(), "b".to_string()], d: Some(Uid::new(42)), }], }); let comparison = &[ Event::StartDictionary(Some(1)), Event::String("Dog".into()), Event::StartDictionary(None), Event::String("inner".into()), Event::StartArray(Some(1)), Event::StartDictionary(None), Event::String("a".into()), Event::String("".into()), Event::String("b".into()), Event::Integer(12.into()), Event::String("c".into()), Event::StartArray(Some(2)), Event::String("a".into()), Event::String("b".into()), Event::EndCollection, Event::String("d".into()), Event::Uid(Uid::new(42)), Event::EndCollection, Event::EndCollection, Event::EndCollection, Event::EndCollection, ]; assert_roundtrip(dog, Some(comparison)); } #[test] fn frog() { let frog = Animal::Frog( Ok("hello".to_owned()), Some(vec![1.0, 2.0, 3.14159, 0.000000001, 1.27e31]), ); let comparison = &[ Event::StartDictionary(Some(1)), Event::String("Frog".into()), Event::StartArray(Some(2)), Event::StartDictionary(Some(1)), Event::String("Ok".into()), Event::String("hello".into()), Event::EndCollection, Event::StartDictionary(Some(1)), Event::String("Some".into()), Event::StartArray(Some(5)), Event::Real(1.0), Event::Real(2.0), Event::Real(3.14159), Event::Real(0.000000001), Event::Real(1.27e31), Event::EndCollection, Event::EndCollection, Event::EndCollection, Event::EndCollection, ]; assert_roundtrip(frog, Some(comparison)); } #[test] fn cat_with_firmware() { let cat = Animal::Cat { age: 12.into(), name: "Paws".to_owned(), firmware: Some(vec![0, 1, 2, 3, 4, 5, 6, 7, 8]), }; let comparison = &[ Event::StartDictionary(Some(1)), Event::String("Cat".into()), Event::StartDictionary(None), Event::String("age".into()), Event::Integer(12.into()), Event::String("name".into()), Event::String("Paws".into()), Event::String("firmware".into()), Event::StartArray(Some(9)), Event::Integer(0.into()), Event::Integer(1.into()), Event::Integer(2.into()), Event::Integer(3.into()), Event::Integer(4.into()), Event::Integer(5.into()), Event::Integer(6.into()), Event::Integer(7.into()), Event::Integer(8.into()), Event::EndCollection, Event::EndCollection, Event::EndCollection, ]; assert_roundtrip(cat, Some(comparison)); } #[test] fn cat_without_firmware() { let cat = Animal::Cat { age: Integer::from(-12), name: "Paws".to_owned(), firmware: None, }; let comparison = &[ Event::StartDictionary(Some(1)), Event::String("Cat".into()), Event::StartDictionary(None), Event::String("age".into()), Event::Integer(Integer::from(-12)), Event::String("name".into()), Event::String("Paws".into()), Event::EndCollection, Event::EndCollection, ]; assert_roundtrip(cat, Some(comparison)); } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] struct NewtypeStruct(NewtypeInner); #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] struct NewtypeInner(u8, u8, u8); #[test] fn newtype_struct() { let newtype = NewtypeStruct(NewtypeInner(34, 32, 13)); let comparison = &[ Event::StartArray(Some(3)), Event::Integer(34.into()), Event::Integer(32.into()), Event::Integer(13.into()), Event::EndCollection, ]; assert_roundtrip(newtype, Some(comparison)); } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] struct TypeWithOptions { a: Option, b: Option>, c: Option>, } #[test] fn type_with_options() { let inner = TypeWithOptions { a: None, b: Some(Some(12)), c: None, }; let obj = TypeWithOptions { a: Some("hello".to_owned()), b: Some(None), c: Some(Box::new(inner)), }; let comparison = &[ Event::StartDictionary(None), Event::String("a".into()), Event::String("hello".into()), Event::String("b".into()), Event::StartDictionary(Some(1)), Event::String("None".into()), Event::String("".into()), Event::EndCollection, Event::String("c".into()), Event::StartDictionary(None), Event::String("b".into()), Event::StartDictionary(Some(1)), Event::String("Some".into()), Event::Integer(12.into()), Event::EndCollection, Event::EndCollection, Event::EndCollection, ]; assert_roundtrip(obj, Some(comparison)); } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] struct TypeWithDate { a: Option, b: Option, } #[test] fn type_with_date() { let date = Date::from_rfc3339("1920-01-01T00:10:00Z").unwrap(); let obj = TypeWithDate { a: Some(28), b: Some(date.clone()), }; let comparison = &[ Event::StartDictionary(None), Event::String("a".into()), Event::Integer(28.into()), Event::String("b".into()), Event::Date(date), Event::EndCollection, ]; assert_roundtrip(obj, Some(comparison)); } #[test] fn option_some() { let obj = Some(12); let comparison = &[Event::Integer(12.into())]; assert_roundtrip(obj, Some(comparison)); } #[test] fn option_none() { let obj: Option = None; let comparison = &[]; assert_roundtrip(obj, Some(comparison)); } #[test] fn option_some_some() { let obj = Some(Some(12)); let comparison = &[ Event::StartDictionary(Some(1)), Event::String("Some".into()), Event::Integer(12.into()), Event::EndCollection, ]; assert_roundtrip(obj, Some(comparison)); } #[test] fn option_some_none() { let obj: Option> = Some(None); let comparison = &[ Event::StartDictionary(Some(1)), Event::String("None".into()), Event::String("".into()), Event::EndCollection, ]; assert_roundtrip(obj, Some(comparison)); } #[test] fn option_dictionary_values() { let mut obj = BTreeMap::new(); obj.insert("a".to_owned(), None); obj.insert("b".to_owned(), Some(None)); obj.insert("c".to_owned(), Some(Some(144))); let comparison = &[ Event::StartDictionary(Some(3)), Event::String("a".into()), Event::StartDictionary(Some(1)), Event::String("None".into()), Event::String("".into()), Event::EndCollection, Event::String("b".into()), Event::StartDictionary(Some(1)), Event::String("Some".into()), Event::StartDictionary(Some(1)), Event::String("None".into()), Event::String("".into()), Event::EndCollection, Event::EndCollection, Event::String("c".into()), Event::StartDictionary(Some(1)), Event::String("Some".into()), Event::StartDictionary(Some(1)), Event::String("Some".into()), Event::Integer(144.into()), Event::EndCollection, Event::EndCollection, Event::EndCollection, ]; assert_roundtrip(obj, Some(comparison)); } #[test] fn option_dictionary_keys() { let mut obj = BTreeMap::new(); obj.insert(None, 1); obj.insert(Some(None), 2); obj.insert(Some(Some(144)), 3); let comparison = &[ Event::StartDictionary(Some(3)), Event::StartDictionary(Some(1)), Event::String("None".into()), Event::String("".into()), Event::EndCollection, Event::Integer(1.into()), Event::StartDictionary(Some(1)), Event::String("Some".into()), Event::StartDictionary(Some(1)), Event::String("None".into()), Event::String("".into()), Event::EndCollection, Event::EndCollection, Event::Integer(2.into()), Event::StartDictionary(Some(1)), Event::String("Some".into()), Event::StartDictionary(Some(1)), Event::String("Some".into()), Event::Integer(144.into()), Event::EndCollection, Event::EndCollection, Event::Integer(3.into()), Event::EndCollection, ]; assert_roundtrip(obj, Some(comparison)); } #[test] fn option_array() { let obj = vec![None, Some(None), Some(Some(144))]; let comparison = &[ Event::StartArray(Some(3)), Event::StartDictionary(Some(1)), Event::String("None".into()), Event::String("".into()), Event::EndCollection, Event::StartDictionary(Some(1)), Event::String("Some".into()), Event::StartDictionary(Some(1)), Event::String("None".into()), Event::String("".into()), Event::EndCollection, Event::EndCollection, Event::StartDictionary(Some(1)), Event::String("Some".into()), Event::StartDictionary(Some(1)), Event::String("Some".into()), Event::Integer(144.into()), Event::EndCollection, Event::EndCollection, Event::EndCollection, ]; assert_roundtrip(obj, Some(comparison)); } #[test] fn enum_variant_types() { #[derive(Debug, Deserialize, Eq, PartialEq, Serialize)] enum Foo { Unit, Newtype(u32), Tuple(u32, String), Struct { v: u32, s: String }, } let expected = &[Event::String("Unit".into())]; assert_roundtrip(Foo::Unit, Some(expected)); let expected = &[ Event::StartDictionary(Some(1)), Event::String("Newtype".into()), Event::Integer(42.into()), Event::EndCollection, ]; assert_roundtrip(Foo::Newtype(42), Some(expected)); let expected = &[ Event::StartDictionary(Some(1)), Event::String("Tuple".into()), Event::StartArray(Some(2)), Event::Integer(42.into()), Event::String("bar".into()), Event::EndCollection, Event::EndCollection, ]; assert_roundtrip(Foo::Tuple(42, "bar".into()), Some(expected)); let expected = &[ Event::StartDictionary(Some(1)), Event::String("Struct".into()), Event::StartDictionary(None), Event::String("v".into()), Event::Integer(42.into()), Event::String("s".into()), Event::String("bar".into()), Event::EndCollection, Event::EndCollection, ]; assert_roundtrip( Foo::Struct { v: 42, s: "bar".into(), }, Some(expected), ); } #[test] fn deserialise_old_enum_unit_variant_encoding() { #[derive(Debug, Deserialize, Eq, PartialEq, Serialize)] enum Foo { Bar, Baz, } // `plist` before v1.1 serialised unit enum variants as if they were newtype variants // containing an empty string. let events = &[ Event::StartDictionary(Some(1)), Event::String("Baz".into()), Event::String("".into()), Event::EndCollection, ]; let mut de = new_deserializer(events.to_vec()); let obj = Foo::deserialize(&mut de).unwrap(); assert_eq!(obj, Foo::Baz); } #[test] fn deserialize_dictionary_xml() { let reader = File::open(&Path::new("./tests/data/xml.plist")).unwrap(); let dict: Dictionary = crate::from_reader(reader).unwrap(); check_common_plist(&dict); // xml.plist has this member, but binary.plist does not. assert_eq!( dict.get("HexademicalNumber") // sic .unwrap() .as_unsigned_integer() .unwrap(), 0xDEADBEEF ); } #[test] fn deserialize_dictionary_binary() { let reader = File::open(&Path::new("./tests/data/binary.plist")).unwrap(); let dict: Dictionary = crate::from_reader(reader).unwrap(); check_common_plist(&dict); } // Shared checks used by the tests deserialize_dictionary_xml() and // deserialize_dictionary_binary(), which load files with different formats // but the same data elements. fn check_common_plist(dict: &Dictionary) { // Array elements let lines = dict.get("Lines").unwrap().as_array().unwrap(); assert_eq!(lines.len(), 2); assert_eq!( lines[0].as_string().unwrap(), "It is a tale told by an idiot," ); assert_eq!( lines[1].as_string().unwrap(), "Full of sound and fury, signifying nothing." ); // Dictionary // // There is no embedded dictionary in this plist. See // deserialize_dictionary_binary_nskeyedarchiver() below for an example // of that. // Boolean elements assert_eq!(dict.get("IsTrue").unwrap().as_boolean().unwrap(), true); assert_eq!(dict.get("IsNotFalse").unwrap().as_boolean().unwrap(), false); // Data let data = dict.get("Data").unwrap().as_data().unwrap(); assert_eq!(data.len(), 15); assert_eq!( data, &[0, 0, 0, 0xbe, 0, 0, 0, 0x03, 0, 0, 0, 0x1e, 0, 0, 0] ); // Date assert_eq!( dict.get("Birthdate").unwrap().as_date().unwrap(), Date::from_rfc3339("1981-05-16T11:32:06Z").unwrap() ); // Real assert_eq!(dict.get("Height").unwrap().as_real().unwrap(), 1.6); // Integer assert_eq!( dict.get("BiggestNumber") .unwrap() .as_unsigned_integer() .unwrap(), 18446744073709551615 ); assert_eq!( dict.get("Death").unwrap().as_unsigned_integer().unwrap(), 1564 ); assert_eq!( dict.get("SmallestNumber") .unwrap() .as_signed_integer() .unwrap(), -9223372036854775808 ); // String assert_eq!( dict.get("Author").unwrap().as_string().unwrap(), "William Shakespeare" ); assert_eq!(dict.get("Blank").unwrap().as_string().unwrap(), ""); // Uid // // No checks for Uid value type in this test. See // deserialize_dictionary_binary_nskeyedarchiver() below for an example // of that. } #[test] fn deserialize_dictionary_binary_nskeyedarchiver() { let reader = File::open(&Path::new("./tests/data/binary_NSKeyedArchiver.plist")).unwrap(); let dict: Dictionary = crate::from_reader(reader).unwrap(); assert_eq!( dict.get("$archiver").unwrap().as_string().unwrap(), "NSKeyedArchiver" ); let objects = dict.get("$objects").unwrap().as_array().unwrap(); assert_eq!(objects.len(), 5); assert_eq!(objects[0].as_string().unwrap(), "$null"); let objects_1 = objects[1].as_dictionary().unwrap(); assert_eq!( *objects_1.get("$class").unwrap().as_uid().unwrap(), Uid::new(4) ); assert_eq!( objects_1 .get("NSRangeCount") .unwrap() .as_unsigned_integer() .unwrap(), 42 ); assert_eq!( *objects_1.get("NSRangeData").unwrap().as_uid().unwrap(), Uid::new(2) ); let objects_2 = objects[2].as_dictionary().unwrap(); assert_eq!( *objects_2.get("$class").unwrap().as_uid().unwrap(), Uid::new(3) ); let objects_2_nsdata = objects_2.get("NS.data").unwrap().as_data().unwrap(); assert_eq!(objects_2_nsdata.len(), 103); assert_eq!(objects_2_nsdata[0], 0x03); assert_eq!(objects_2_nsdata[102], 0x01); let objects_3 = objects[3].as_dictionary().unwrap(); let objects_3_classes = objects_3.get("$classes").unwrap().as_array().unwrap(); assert_eq!(objects_3_classes.len(), 3); assert_eq!(objects_3_classes[0].as_string().unwrap(), "NSMutableData"); assert_eq!(objects_3_classes[1].as_string().unwrap(), "NSData"); assert_eq!(objects_3_classes[2].as_string().unwrap(), "NSObject"); assert_eq!( objects_3.get("$classname").unwrap().as_string().unwrap(), "NSMutableData" ); let objects_4 = objects[4].as_dictionary().unwrap(); let objects_4_classes = objects_4.get("$classes").unwrap().as_array().unwrap(); assert_eq!(objects_4_classes.len(), 3); assert_eq!( objects_4_classes[0].as_string().unwrap(), "NSMutableIndexSet" ); assert_eq!(objects_4_classes[1].as_string().unwrap(), "NSIndexSet"); assert_eq!(objects_4_classes[2].as_string().unwrap(), "NSObject"); assert_eq!( objects_4.get("$classname").unwrap().as_string().unwrap(), "NSMutableIndexSet" ); let top = dict.get("$top").unwrap().as_dictionary().unwrap(); assert_eq!( *top.get("foundItems").unwrap().as_uid().unwrap(), Uid::new(1) ); let version = dict.get("$version").unwrap().as_unsigned_integer().unwrap(); assert_eq!(version, 100000); } #[test] fn dictionary_deserialize_dictionary_in_struct() { // Example from #[derive(Deserialize)] struct LayerinfoData { color: Option, lib: Option, } let lib_dict: LayerinfoData = crate::from_bytes(r#" color 1,0.75,0,0.7 lib com.typemytype.robofont.segmentType curve "#.as_bytes()).unwrap(); assert_eq!(lib_dict.color.unwrap(), "1,0.75,0,0.7"); assert_eq!( lib_dict .lib .unwrap() .get("com.typemytype.robofont.segmentType") .unwrap() .as_string() .unwrap(), "curve" ); } #[test] fn dictionary_serialize_xml() { // Dictionary to be embedded in dict, below. let mut inner_dict = Dictionary::new(); inner_dict.insert( "FirstKey".to_owned(), Value::String("FirstValue".to_owned()), ); inner_dict.insert("SecondKey".to_owned(), Value::Data(vec![10, 20, 30, 40])); inner_dict.insert("ThirdKey".to_owned(), Value::Real(1.234)); inner_dict.insert( "FourthKey".to_owned(), Value::Date(Date::from_rfc3339("1981-05-16T11:32:06Z").unwrap()), ); // Top-level dictionary. let mut dict = Dictionary::new(); dict.insert( "AnArray".to_owned(), Value::Array(vec![ Value::String("Hello, world!".to_owned()), Value::Integer(Integer::from(345)), ]), ); dict.insert("ADictionary".to_owned(), Value::Dictionary(inner_dict)); dict.insert("AnInteger".to_owned(), Value::Integer(Integer::from(123))); dict.insert("ATrueBoolean".to_owned(), Value::Boolean(true)); dict.insert("AFalseBoolean".to_owned(), Value::Boolean(false)); // Serialize dictionary as an XML plist. let mut buf = Cursor::new(Vec::new()); crate::to_writer_xml(&mut buf, &dict).unwrap(); let buf = buf.into_inner(); let xml = std::str::from_utf8(&buf).unwrap(); let comparison = " \tAnArray \t \t\tHello, world! \t\t345 \t \tADictionary \t \t\tFirstKey \t\tFirstValue \t\tSecondKey \t\t\n\t\tChQeKA==\n\t\t \t\tThirdKey \t\t1.234 \t\tFourthKey \t\t1981-05-16T11:32:06Z \t \tAnInteger \t123 \tATrueBoolean \t \tAFalseBoolean \t "; assert_eq!(xml, comparison); } #[test] fn serde_yaml_to_value() { let value: Value = serde_yaml::from_str("true").unwrap(); assert_eq!(value, Value::Boolean(true)); }