diff --git a/Cargo.toml b/Cargo.toml index f9f7f5e..f1d74c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "nettext" description = "A text-based data format for cryptographic network protocols" authors = ["Alex Auvolat "] -version = "0.3.0" +version = "0.3.1" edition = "2021" license = "AGPL-3.0" readme = "README.md" diff --git a/src/enc/mod.rs b/src/enc/mod.rs index 37ac4c5..37791f4 100644 --- a/src/enc/mod.rs +++ b/src/enc/mod.rs @@ -262,6 +262,13 @@ impl<'a> Term<'a> { pub fn encode_string(self) -> String { unsafe { String::from_utf8_unchecked(self.encode()) } } + + /// Generate the concise nettext representation of a term + pub fn encode_concise(self) -> Vec { + let mut buf = Vec::with_capacity(128); + self.0.encode_concise_aux(&mut buf); + buf + } } impl<'a> T<'a> { @@ -339,6 +346,46 @@ impl<'a> T<'a> { } } } + + fn encode_concise_aux(self, buf: &mut Vec) { + match self { + T::Str(s) => buf.extend_from_slice(s), + T::OwnedStr(s) => buf.extend_from_slice(&s), + T::Dict(mut d) => { + buf.push(b'{'); + let mut keys = d.keys().cloned().collect::>(); + keys.sort(); + for (i, k) in keys.into_iter().enumerate() { + if i > 0 { + buf.push(b','); + } + let v = d.remove(&k).unwrap(); + buf.extend_from_slice(k.borrow()); + buf.push(b'='); + v.encode_concise_aux(buf); + } + buf.push(b'}'); + } + T::List(l) => { + buf.push(b'['); + for (i,item) in l.into_iter().enumerate() { + if i > 0 { + buf.push(b','); + } + item.encode_concise_aux(buf); + } + buf.push(b']'); + } + T::Seq(l) => { + for (i, v) in l.into_iter().enumerate() { + if i > 0 { + buf.push(b' '); + } + v.encode_concise_aux(buf); + } + } + } + } } #[cfg(test)] @@ -363,6 +410,7 @@ mod tests { .unwrap(), ]) .unwrap(); + let expected = "HELLO alexhelloworld [ dude, why, @@ -371,9 +419,27 @@ mod tests { from = jxx, subject = hello, }"; - let enc = input.encode(); - eprintln!("{}", debug(&enc)); - eprintln!("{}", expected); - assert_eq!(debug(&enc), expected); + assert_eq!(debug(&input.encode()), expected); + } + + #[test] + fn complex1_concise() { + let input = seq([ + string("HELLO").unwrap(), + string("alexhelloworld").unwrap(), + list([ + string("dude").unwrap(), + string("why").unwrap(), + ]), + dict([ + ("from", string("jxx").unwrap()), + ("subject", string("hello").unwrap()), + ("data", raw(b"{ f1 = plop, f2 = kuko }").unwrap()), + ]) + .unwrap(), + ]) + .unwrap(); + let expected_concise = "HELLO alexhelloworld [dude,why] {data={ f1 = plop, f2 = kuko },from=jxx,subject=hello}"; + assert_eq!(debug(&input.encode_concise()), expected_concise); } } diff --git a/src/serde/mod.rs b/src/serde/mod.rs index 3a292c6..809cdf9 100644 --- a/src/serde/mod.rs +++ b/src/serde/mod.rs @@ -17,14 +17,20 @@ mod tests { fn test_bidir Deserialize<'de> + PartialEq + std::fmt::Debug>( input: T, - expected: &str, + expected_concise: &str, ) { - eprintln!("Expecting: {}", expected); + eprintln!("Expecting: {}", expected_concise); + let ser = to_bytes(&input).unwrap(); let ser = debug(&ser); eprintln!("Serialized: {}", ser); - assert_eq!(ser, expected); assert_eq!(from_bytes::(ser.as_bytes()).unwrap(), input); + + let ser_concise = to_term(&input).unwrap().encode_concise(); + let ser_concise = debug(&ser_concise); + eprintln!("Serialized (concise): {}", ser_concise); + assert_eq!(ser_concise, expected_concise); + assert_eq!(from_bytes::(ser_concise.as_bytes()).unwrap(), input); } #[test] @@ -39,13 +45,7 @@ mod tests { int: 1, seq: vec!["a".to_string(), "b".to_string()], }; - let expected = r#"Test { - int = 1, - seq = [ - YQ, - Yg, - ], - }"#; + let expected = r#"Test {int=1,seq=[YQ,Yg]}"#; test_bidir(input, expected); let input = vec![ @@ -58,22 +58,7 @@ mod tests { seq: vec!["c".to_string(), "d".to_string()], }, ]; - let expected = r#"[ - Test { - int = 1, - seq = [ - YQ, - Yg, - ], - }, - Test { - int = 2, - seq = [ - Yw, - ZA, - ], - }, -]"#; + let expected = r#"[Test {int=1,seq=[YQ,Yg]},Test {int=2,seq=[Yw,ZA]}]"#; test_bidir(input, expected); } @@ -100,7 +85,7 @@ mod tests { test_bidir(input, expected); let input = E::Struct { a: 1 }; - let expected = r#"E.Struct { a = 1 }"#; + let expected = r#"E.Struct {a=1}"#; test_bidir(input, expected); let input = vec![ @@ -112,14 +97,7 @@ mod tests { E::Tuple(3, 2), ]; let expected = - r#"[ - E.Unit, - E.Unit, - E.Newtype 1, - E.Tuple 1 2, - E.Struct { a = 1 }, - E.Tuple 3 2, -]"#; + r#"[E.Unit,E.Unit,E.Newtype 1,E.Tuple 1 2,E.Struct {a=1},E.Tuple 3 2]"#; test_bidir(input, expected); } @@ -133,24 +111,14 @@ mod tests { #[test] fn test_list() { let input = vec![1, 2, 3, 4]; - let expected = r#"[ - 1, - 2, - 3, - 4, -]"#; + let expected = r#"[1,2,3,4]"#; test_bidir(input, expected); } #[test] fn test_seqlist() { let input = vec![(1, 2), (2, 3), (3, 4), (5, 6)]; - let expected = r#"[ - 1 2, - 2 3, - 3 4, - 5 6, -]"#; + let expected = r#"[1 2,2 3,3 4,5 6]"#; test_bidir(input, expected); } @@ -159,26 +127,13 @@ mod tests { let mut input = HashMap::new(); input.insert("hello".to_string(), "world".to_string()); input.insert("dont".to_string(), "panic".to_string()); - let expected = r#"{ - ZG9udA = cGFuaWM, - aGVsbG8 = d29ybGQ, -}"#; + let expected = r#"{ZG9udA=cGFuaWM,aGVsbG8=d29ybGQ}"#; test_bidir(input, expected); let mut input = HashMap::new(); input.insert(12, vec![42, 125]); input.insert(33, vec![19, 22, 21]); - let expected = r#"{ - 12 = [ - 42, - 125, - ], - 33 = [ - 19, - 22, - 21, - ], -}"#; + let expected = r#"{12=[42,125],33=[19,22,21]}"#; test_bidir(input, expected); } }