pub mod de;
mod decimal;
pub mod ser;
mod timestamp;
pub use de::from_ion;
pub use ser::{to_binary, to_pretty, to_string};
#[cfg(test)]
#[cfg(feature = "experimental-serde")]
mod tests {
use crate::serde::{from_ion, to_binary, to_pretty, to_string};
use std::net::IpAddr;
use crate::{Decimal, Element, Timestamp};
use chrono::{DateTime, FixedOffset, Utc};
use rstest::*;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
#[rstest]
#[case::i8(to_binary(&-1_i8).unwrap(), &[0xE0, 0x01, 0x00, 0xEA, 0x31, 0x01])]
#[case::i16(to_binary(&-1_i16).unwrap(), &[0xE0, 0x01, 0x00, 0xEA, 0x31, 0x01])]
#[case::i32(to_binary(&-1_i32).unwrap(), &[0xE0, 0x01, 0x00, 0xEA, 0x31, 0x01])]
#[case::i64(to_binary(&-1_i64).unwrap(), &[0xE0, 0x01, 0x00, 0xEA, 0x31, 0x01])]
#[case::u8(to_binary(&1_u8).unwrap(), &[0xE0, 0x01, 0x00, 0xEA, 0x21, 0x01])]
#[case::u16(to_binary(&1_u16).unwrap(), &[0xE0, 0x01, 0x00, 0xEA, 0x21, 0x01])]
#[case::u32(to_binary(&1_u32).unwrap(), &[0xE0, 0x01, 0x00, 0xEA, 0x21, 0x01])]
#[case::u64(to_binary(&1_u64).unwrap(), &[0xE0, 0x01, 0x00, 0xEA, 0x21, 0x01])]
#[case::f32(to_binary(&1_f32).unwrap(), &[0xE0, 0x01, 0x00, 0xEA, 0x44, 0x3f, 0x80, 0x00, 0x00])]
#[case::f64(to_binary(&1_f64).unwrap(), &[0xE0, 0x01, 0x00, 0xEA, 0x44, 0x3f, 0x80, 0x00, 0x00])]
#[case::char(to_binary(&'a').unwrap(), &[0xE0, 0x01, 0x00, 0xEA, 0x81, 0x61])]
#[case::str(to_binary(&"a").unwrap(), &[0xE0, 0x01, 0x00, 0xEA, 0x81, 0x61])]
#[case::some(to_binary(&Some(1)).unwrap(), &[0xE0, 0x01, 0x00, 0xEA, 0x21, 0x01])]
#[case::unit(to_binary(&()).unwrap(), &[0xE0, 0x01, 0x00, 0xEA, 0x0F])]
fn test_primitives_binary(#[case] ion_data: Vec<u8>, #[case] expected: &[u8]) {
assert_eq!(&ion_data[..], expected);
}
#[rstest]
#[case::i8(to_string(&-1_i8).unwrap(), "-1")]
#[case::i16(to_string(&-1_i16).unwrap(), "-1")]
#[case::i32(to_string(&-1_i32).unwrap(), "-1")]
#[case::i64(to_string(&-1_i64).unwrap(), "-1")]
#[case::u8(to_string(&1_u8).unwrap(), "1")]
#[case::u16(to_string(&1_u16).unwrap(), "1")]
#[case::u32(to_string(&1_u32).unwrap(), "1")]
#[case::u64(to_string(&1_u64).unwrap(), "1")]
#[case::char(to_string(&'a').unwrap(), "\"a\"")]
#[case::str(to_string(&"a").unwrap(), "\"a\"")]
#[case::some(to_string(&Some(1)).unwrap(), "1" )]
#[case::unit(to_string(&()).unwrap(), "null")]
fn test_primitives_text(#[case] ion_data: String, #[case] expected: &str) {
assert_eq!(ion_data.trim(), expected);
}
#[test]
fn test_blob() {
#[derive(Serialize, Deserialize)]
struct Test {
#[serde(with = "serde_bytes")]
binary: Vec<u8>,
}
#[rustfmt::skip]
let expected = &[
0xE0, 0x01, 0x00, 0xEA, 0xEE, 0x8F, 0x81, 0x83, 0xDC, 0x86, 0x71, 0x03, 0x87, 0xB7, 0x86, 0x62, 0x69, 0x6E, 0x61, 0x72, 0x79, 0xD7, 0x8A, 0xA5, 0x68, 0x65, 0x6C, 0x6C, 0x6F, ];
let test = Test {
binary: b"hello".to_vec(),
}; let ion_data = to_binary(&test).unwrap();
assert_eq!(&ion_data[..], expected);
let de: Test = from_ion(ion_data).expect("unable to parse test");
assert_eq!(de.binary, test.binary);
let ion_data_str = to_string(&test).unwrap();
assert_eq!(ion_data_str.trim(), "{binary: {{aGVsbG8=}}, }");
let de: Test = from_ion(ion_data_str).expect("unable to parse test");
assert_eq!(de.binary, test.binary);
}
#[test]
fn test_struct() {
#[serde_as]
#[derive(Serialize, Deserialize)]
struct Test {
int: u32,
float: f64,
#[serde(with = "serde_bytes")]
binary: Vec<u8>,
seq: Vec<String>,
decimal: Decimal,
date: Timestamp,
#[serde_as(as = "crate::Timestamp")]
date0: DateTime<Utc>,
#[serde_as(as = "crate::Timestamp")]
date1: DateTime<FixedOffset>,
nested_struct: NestedTest,
unit_struct: UnitStruct,
newtype_struct: NewTypeStruct,
tuple_struct: TupleStruct,
optional: Option<i64>,
}
#[serde_as]
#[derive(Serialize, Deserialize)]
struct NestedTest {
boolean: bool,
str: String,
}
#[serde_as]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct UnitStruct;
#[serde_as]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct NewTypeStruct(i64);
#[serde_as]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct TupleStruct(i64, i64);
let datetime: DateTime<FixedOffset> = Utc::now().into();
let my_date0 = Utc::now();
let my_date = Timestamp::from(datetime);
let my_decimal = Decimal::new(1225, -2);
let test = Test {
int: 1,
float: 3.46,
binary: b"EDO".to_vec(),
seq: vec!["a".to_string(), "b".to_string()],
decimal: my_decimal.clone(),
date: my_date.clone(),
date0: my_date0,
date1: datetime,
nested_struct: NestedTest {
boolean: true,
str: "hello".to_string(),
},
unit_struct: UnitStruct,
newtype_struct: NewTypeStruct(5),
tuple_struct: TupleStruct(5, 10),
optional: None,
};
let result = to_pretty(&test).expect("failed to serialize");
println!("result: {result}");
let back_result: Test = from_ion(result.as_str()).expect("failed to deserialize");
assert_eq!(back_result.int, 1);
assert_eq!(back_result.float, 3.46);
assert_eq!(back_result.binary, b"EDO");
assert_eq!(back_result.seq.len(), 2);
assert_eq!(back_result.seq[0], "a");
assert_eq!(back_result.seq[1], "b");
assert_eq!(back_result.decimal, my_decimal.clone());
assert_eq!(back_result.date, my_date.clone());
assert_eq!(back_result.date0, my_date0.clone());
assert_eq!(back_result.date1, datetime.clone());
assert!(back_result.nested_struct.boolean);
assert_eq!(&back_result.nested_struct.str, "hello");
assert_eq!(back_result.unit_struct, UnitStruct);
assert_eq!(back_result.newtype_struct, NewTypeStruct(5));
assert_eq!(back_result.tuple_struct, TupleStruct(5, 10));
assert_eq!(back_result.optional, None);
}
#[test]
fn test_enum() {
#[serde_as]
#[derive(Serialize, Deserialize, PartialEq, Debug)]
enum E {
Unit,
Newtype(u32),
Tuple(u32, u32),
Struct { a: u32 },
}
let i = r#"Unit"#;
let expected = E::Unit;
assert_eq!(expected, from_ion(i).unwrap());
assert_eq!(
Element::read_first(i),
Element::read_first(to_string(&expected).unwrap())
);
let i = r#"Newtype::1"#;
let expected = E::Newtype(1);
assert_eq!(expected, from_ion(i).unwrap());
assert_eq!(
Element::read_first(i),
Element::read_first(to_string(&expected).unwrap())
);
let i = r#"Tuple::[1, 2]"#;
let expected = E::Tuple(1, 2);
assert_eq!(expected, from_ion(i).unwrap());
assert_eq!(
Element::read_first(i),
Element::read_first(to_string(&expected).unwrap())
);
let i = r#"Struct::{a: 1}"#;
let expected = E::Struct { a: 1 };
assert_eq!(expected, from_ion(i).unwrap());
assert_eq!(
Element::read_first(i),
Element::read_first(to_string(&expected).unwrap())
);
}
#[test]
fn test_nested_newtype_variant() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
enum Outter {
First(Inner),
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
enum Inner {
Second(u32),
}
#[rustfmt::skip]
let expected_binary = [
0xE0, 0x01, 0x00, 0xEA, 0xEE, 0x96, 0x81, 0x83, 0xDE, 0x92, 0x86, 0x71, 0x03, 0x87, 0xBD, 0x85, 0x46, 0x69, 0x72, 0x73, 0x74, 0x86, 0x53, 0x65, 0x63, 0x6F, 0x6E, 0x64, 0xE5, 0x82, 0x8A, 0x8B, 0x21, 0x03,
];
let i = r#"First::Second::3"#;
let expected = Outter::First(Inner::Second(3));
assert_eq!(expected, from_ion(i).unwrap());
let b = to_binary(&expected).unwrap();
assert_eq!(expected_binary, &b[..]);
}
#[test]
fn test_symbol() {
let i = r#"inches"#;
let expected = String::from("inches");
assert_eq!(expected, from_ion::<String, _>(i).unwrap());
let i = r#"'with space'"#;
let expected = String::from("with space");
assert_eq!(expected, from_ion::<String, _>(i).unwrap());
let i = r#"'\'embedded quotes\''"#;
let expected = String::from("'embedded quotes'");
assert_eq!(expected, from_ion::<String, _>(i).unwrap());
}
#[test]
fn human_readable() {
let ip: IpAddr = "127.0.0.1".parse().unwrap();
let expected_binary = [
224, 1, 0, 234, 235, 129, 131, 216, 134, 113, 3, 135, 179, 130, 86, 52, 233, 129, 138,
182, 33, 127, 32, 32, 33, 1,
];
let expected_s = "\"127.0.0.1\" ";
let binary = to_binary(&ip).unwrap();
let s = to_string(&ip).unwrap();
assert_eq!(&binary[..], &expected_binary[..]);
assert_eq!(s, expected_s);
assert_eq!(&from_ion::<IpAddr, _>(s).unwrap(), &ip);
assert_eq!(&from_ion::<IpAddr, _>(binary).unwrap(), &ip);
}
mod newtype_variant_annotations {
use super::*;
use std::collections::BTreeMap;
#[derive(Serialize, Deserialize, PartialEq, Debug)]
enum Wrapper {
Tag(bool),
Num(f32),
Big(f64),
Items(Vec<u32>),
Dict(BTreeMap<String, u32>),
Record { x: i32, y: i32 },
Price(Decimal),
When(Timestamp),
}
#[test]
fn newtype_variant_bool() {
let val = Wrapper::Tag(true);
let ion = to_string(&val).unwrap();
assert_eq!(Element::read_first("Tag::true"), Element::read_first(&ion));
let roundtrip: Wrapper = from_ion(&ion).unwrap();
assert_eq!(val, roundtrip);
}
#[test]
fn newtype_variant_f32() {
let val = Wrapper::Num(2.5f32);
let ion = to_string(&val).unwrap();
assert_eq!(Element::read_first("Num::2.5e0"), Element::read_first(&ion));
let roundtrip: Wrapper = from_ion(&ion).unwrap();
assert_eq!(val, roundtrip);
}
#[test]
fn newtype_variant_f64() {
let val = Wrapper::Big(1.234f64);
let ion = to_string(&val).unwrap();
assert_eq!(
Element::read_first("Big::1.234e0"),
Element::read_first(&ion)
);
let roundtrip: Wrapper = from_ion(&ion).unwrap();
assert_eq!(val, roundtrip);
}
#[test]
fn newtype_variant_seq() {
let val = Wrapper::Items(vec![1, 2, 3]);
let ion = to_string(&val).unwrap();
assert_eq!(
Element::read_first("Items::[1, 2, 3]"),
Element::read_first(&ion)
);
let roundtrip: Wrapper = from_ion(&ion).unwrap();
assert_eq!(val, roundtrip);
}
#[test]
fn newtype_variant_map() {
let mut map = BTreeMap::new();
map.insert("a".to_string(), 1u32);
let val = Wrapper::Dict(map);
let ion = to_string(&val).unwrap();
assert_eq!(
Element::read_first("Dict::{a: 1}"),
Element::read_first(&ion)
);
let roundtrip: Wrapper = from_ion(&ion).unwrap();
assert_eq!(val, roundtrip);
}
#[test]
fn newtype_variant_struct() {
let val = Wrapper::Record { x: 10, y: 20 };
let ion = to_string(&val).unwrap();
assert_eq!(
Element::read_first("Record::{x: 10, y: 20}"),
Element::read_first(&ion)
);
let roundtrip: Wrapper = from_ion(&ion).unwrap();
assert_eq!(val, roundtrip);
}
#[test]
fn newtype_variant_decimal() {
let val = Wrapper::Price(Decimal::new(199, -2));
let ion = to_string(&val).unwrap();
assert_eq!(
Element::read_first("Price::1.99"),
Element::read_first(&ion)
);
let roundtrip: Wrapper = from_ion(&ion).unwrap();
assert_eq!(val, roundtrip);
}
#[test]
fn newtype_variant_timestamp() {
let ts = Timestamp::with_ymd(2024, 6, 15)
.with_hms(12, 0, 0)
.build()
.unwrap();
let val = Wrapper::When(ts.clone());
let ion = to_string(&val).unwrap();
assert_eq!(
Element::read_first("When::2024-06-15T12:00:00-00:00"),
Element::read_first(&ion)
);
let roundtrip: Wrapper = from_ion(&ion).unwrap();
assert_eq!(val, roundtrip);
}
}
}