Remove nested structs and add list syntax

This commit is contained in:
Alex 2022-12-15 14:45:27 +01:00
parent 08c221558f
commit abc27adea4
Signed by: lx
GPG key ID: 0E496D15096376BE
8 changed files with 325 additions and 156 deletions

View file

@ -15,11 +15,9 @@ A term can be of any of the following kinds:
- a string, which may contain only ASCII alphanumeric terms and `.-_*?`
- a dict, which maps strings (as defined above) to any term type
- a list, which may contain any number of any kind of terms (can be mixed)
- a sequence, consistuted of at least two of the above (can be mixed), simply separated by whitespace; sequences cannot be nested
Nested sequences can be represented using a special dictionnary with a single key, `.`,
for instance `TEST a { . = 0 4 2 1 9 7 0 } c`.
Dicts are represented as follows:
```
@ -29,6 +27,12 @@ Dicts are represented as follows:
}
```
Dicts are represented as follows:
```
[ term1, term2 ]
```
Sequences are represented as follows:
```
@ -38,8 +42,12 @@ term1 term2 term3
As a consequence, complex data structures can be defined as follows:
```
SENDTO alex {
SEND MESSAGE {
topic = blah,
to = [
TOPIC hello,
USER john
],
body = blah blah
}
```
@ -52,7 +60,8 @@ In the complex stance example above, here are the sequence and dicts and their r
- the toplevel term is a sequence, whose raw representation is the entire encoded string (assuming no whitespace at beginning or end)
- the third term of the sequence is a dict, whose raw representation starts at `{` and ends at `}`
- the second mapping of the dict is a sequence, whose raw representation is exactly `blah blah`.
- the second mapping of the dict is a list, whose raw representation starts at `[` and ends at `]`
- the third mapping of the dict is a sequence, whose raw representation is exactly `blah blah`.
Since strings cannot contain whitespace, they are always equivalent to their raw representation.
@ -66,8 +75,8 @@ Terms can be interpreted in a number of different ways, depending on the context
- a discriminator (the first item)
- a value, which is either the second item in case there are only two items, or the sequence composed of all items starting from the second if there are more than two
- DICT: if the term is a dict, interpret it as such
- SEQ: if the term is a string or a dict, interpret it as a sequence composed of that single term. Otherwise, the term is a sequence, interpret it as a sequence of terms.
- NESTED: if the term is a dict with a single key `.`, interpret it as the term associated to that key
- LIST: if the term is a list, interpret it as such
- SEQ: if the term is a string, a list, or a dict, interpret it as a sequence composed of that single term. Otherwise, the term is a sequence, interpret it as a sequence of terms.
## Data mappings

View file

@ -9,7 +9,10 @@ use nom::{
};
use crate::dec::{AnyTerm, DecodeError, NonSeqTerm, Term};
use crate::{is_string_char, is_whitespace, DICT_ASSIGN, DICT_CLOSE, DICT_DELIM, DICT_OPEN};
use crate::{
is_string_char, is_whitespace, DICT_ASSIGN, DICT_CLOSE, DICT_DELIM, DICT_OPEN, LIST_CLOSE,
LIST_DELIM, LIST_OPEN,
};
// ----
@ -40,6 +43,7 @@ fn decode_nonseq_term(input: &[u8]) -> IResult<&'_ [u8], NonSeqTerm<'_, '_>> {
let (rest, term) = alt((
map(decode_str, NonSeqTerm::Str),
map(decode_dict, |(raw, d)| NonSeqTerm::Dict(raw, d)),
map(decode_list, |(raw, l)| NonSeqTerm::List(raw, l)),
))(input)?;
Ok((rest, term))
}
@ -81,6 +85,27 @@ fn decode_dict_item(d: &[u8]) -> IResult<&'_ [u8], (&'_ [u8], AnyTerm<'_, '_>)>
Ok((d, (key, value)))
}
type ListType<'a> = (&'a [u8], Vec<AnyTerm<'a, 'a>>);
fn decode_list(list_begin: &[u8]) -> IResult<&'_ [u8], ListType<'_>> {
let (d, _) = tag(&[LIST_OPEN][..])(list_begin)?;
let (d, list) = separated_list0(list_separator, decode_term)(d)?;
let (d, _) = opt(list_separator)(d)?;
let (d, _) = take_while(is_whitespace)(d)?;
let (list_end, _) = tag(&[LIST_CLOSE][..])(d)?;
let raw_len = list_begin.input_len() - list_end.input_len();
let list_raw = &list_begin[..raw_len];
Ok((list_end, (list_raw, list)))
}
fn list_separator(d: &[u8]) -> IResult<&'_ [u8], ()> {
let (d, _) = take_while(is_whitespace)(d)?;
let (d, _) = tag(&[LIST_DELIM][..])(d)?;
Ok((d, ()))
}
// ----
#[cfg(test)]
@ -161,6 +186,48 @@ mod tests {
);
}
#[test]
fn simple_list() {
let bytes = b" [ hello, bojzkz pipo, ke ] ";
assert_eq!(
decode(bytes),
Ok(AnyTerm::List(
b"[ hello, bojzkz pipo, ke ]",
[
AnyTerm::Str(b"hello"),
AnyTerm::Seq(
b"bojzkz pipo",
vec![NonSeqTerm::Str(b"bojzkz"), NonSeqTerm::Str(b"pipo")]
),
AnyTerm::Str(b"ke"),
]
.to_vec()
)
.into())
);
}
#[test]
fn simple_list_2() {
let bytes = b" [ hello, bojzkz pipo , ke , ] ";
assert_eq!(
decode(bytes),
Ok(AnyTerm::List(
b"[ hello, bojzkz pipo , ke , ]",
[
AnyTerm::Str(b"hello"),
AnyTerm::Seq(
b"bojzkz pipo",
vec![NonSeqTerm::Str(b"bojzkz"), NonSeqTerm::Str(b"pipo")]
),
AnyTerm::Str(b"ke"),
]
.to_vec()
)
.into())
);
}
#[test]
fn real_world_1() {
let bytes = b"HEAD alexpubkey";
@ -209,4 +276,51 @@ mod tests {
]).into(),
));
}
#[test]
fn real_world_3() {
let bytes = b"[ USER john, USER luke, GROUP strategy { owner = USER john, members = [ USER john, USER luke ] } ]";
let user_john = AnyTerm::Seq(
b"USER john",
vec![NonSeqTerm::Str(b"USER"), NonSeqTerm::Str(b"john")],
);
let user_luke = AnyTerm::Seq(
b"USER luke",
vec![NonSeqTerm::Str(b"USER"), NonSeqTerm::Str(b"luke")],
);
assert_eq!(
decode(bytes),
Ok(AnyTerm::List(
&bytes[..],
vec![
user_john.clone(),
user_luke.clone(),
AnyTerm::Seq(
b"GROUP strategy { owner = USER john, members = [ USER john, USER luke ] }",
vec![
NonSeqTerm::Str(b"GROUP"),
NonSeqTerm::Str(b"strategy"),
NonSeqTerm::Dict(
b"{ owner = USER john, members = [ USER john, USER luke ] }",
[
(&b"owner"[..], user_john.clone()),
(
&b"members"[..],
AnyTerm::List(
b"[ USER john, USER luke ]",
vec![user_john, user_luke,]
)
)
]
.into_iter()
.collect()
)
]
),
]
)
.into())
);
}
}

View file

@ -8,6 +8,8 @@ use std::collections::HashMap;
#[cfg(any(feature = "dryoc"))]
use crate::crypto;
use crate::debug;
pub use decode::*;
pub use error::*;
@ -26,6 +28,8 @@ pub(crate) enum AnyTerm<'a, 'b> {
Str(&'a [u8]),
Dict(&'a [u8], HashMap<&'a [u8], AnyTerm<'a, 'b>>),
DictRef(&'a [u8], &'b HashMap<&'a [u8], AnyTerm<'a, 'b>>),
List(&'a [u8], Vec<AnyTerm<'a, 'b>>),
ListRef(&'a [u8], &'b [AnyTerm<'a, 'b>]),
Seq(&'a [u8], Vec<NonSeqTerm<'a, 'b>>),
SeqRef(&'a [u8], &'b [NonSeqTerm<'a, 'b>]),
}
@ -35,6 +39,8 @@ pub(crate) enum NonSeqTerm<'a, 'b> {
Str(&'a [u8]),
Dict(&'a [u8], HashMap<&'a [u8], AnyTerm<'a, 'b>>),
DictRef(&'a [u8], &'b HashMap<&'a [u8], AnyTerm<'a, 'b>>),
List(&'a [u8], Vec<AnyTerm<'a, 'b>>),
ListRef(&'a [u8], &'b [AnyTerm<'a, 'b>]),
}
impl<'a, 'b> From<NonSeqTerm<'a, 'b>> for AnyTerm<'a, 'b> {
@ -43,6 +49,8 @@ impl<'a, 'b> From<NonSeqTerm<'a, 'b>> for AnyTerm<'a, 'b> {
NonSeqTerm::Str(s) => AnyTerm::Str(s),
NonSeqTerm::Dict(raw, d) => AnyTerm::Dict(raw, d),
NonSeqTerm::DictRef(raw, d) => AnyTerm::DictRef(raw, d),
NonSeqTerm::List(raw, l) => AnyTerm::List(raw, l),
NonSeqTerm::ListRef(raw, l) => AnyTerm::ListRef(raw, l),
}
}
}
@ -54,6 +62,8 @@ impl<'a, 'b> TryFrom<AnyTerm<'a, 'b>> for NonSeqTerm<'a, 'b> {
AnyTerm::Str(s) => Ok(NonSeqTerm::Str(s)),
AnyTerm::Dict(raw, d) => Ok(NonSeqTerm::Dict(raw, d)),
AnyTerm::DictRef(raw, d) => Ok(NonSeqTerm::DictRef(raw, d)),
AnyTerm::List(raw, l) => Ok(NonSeqTerm::List(raw, l)),
AnyTerm::ListRef(raw, l) => Ok(NonSeqTerm::ListRef(raw, l)),
_ => Err(()),
}
}
@ -353,28 +363,23 @@ impl<'a, 'b> Term<'a, 'b> {
}
}
/// Checks term is a dictionary with a single key `.`,
/// and returns the associated value.
/// Checks if the term is a list, and if so, return its elements in a vec.
///
/// Example:
///
/// ```
/// use nettext::dec::decode;
///
/// let term = decode(b"{ . = a b c d e }").unwrap();
/// assert_eq!(term.nested().unwrap().raw(), b"a b c d e");
/// let term2 = decode(b"[ hello, world ]").unwrap();
/// let seq2 = term2.list().unwrap();
/// assert_eq!(seq2.len(), 2);
/// assert_eq!(seq2[0].str().unwrap(), "hello");
/// assert_eq!(seq2[1].str().unwrap(), "world");
/// ```
pub fn nested(&self) -> Result<Term<'a, '_>, TypeError> {
pub fn list(&self) -> Result<Vec<Term<'a, '_>>, TypeError> {
match self.0.mkref() {
AnyTerm::DictRef(_, d) if d.len() == 1 => {
let (k, v) = d.iter().next().unwrap();
if k != b"." {
Err(TypeError::WrongType("NESTED"))
} else {
Ok(Term(v.mkref()))
}
}
_ => Err(TypeError::WrongType("NESTED")),
AnyTerm::ListRef(_r, l) => Ok(l.iter().map(|x| Term(x.mkref().into())).collect::<Vec<_>>()),
_ => Err(TypeError::WrongType("LIST")),
}
}
@ -508,6 +513,8 @@ impl<'a, 'b> AnyTerm<'a, 'b> {
AnyTerm::Str(s) => s,
AnyTerm::Dict(r, _)
| AnyTerm::DictRef(r, _)
| AnyTerm::List(r, _)
| AnyTerm::ListRef(r, _)
| AnyTerm::Seq(r, _)
| AnyTerm::SeqRef(r, _) => r,
}
@ -518,6 +525,8 @@ impl<'a, 'b> AnyTerm<'a, 'b> {
AnyTerm::Str(s) => AnyTerm::Str(s),
AnyTerm::Dict(r, d) => AnyTerm::DictRef(r, d),
AnyTerm::DictRef(r, d) => AnyTerm::DictRef(r, d),
AnyTerm::List(r, l) => AnyTerm::ListRef(r, l),
AnyTerm::ListRef(r, l) => AnyTerm::ListRef(r, l),
AnyTerm::Seq(r, l) => AnyTerm::SeqRef(r, &l[..]),
AnyTerm::SeqRef(r, l) => AnyTerm::SeqRef(r, l),
}
@ -529,6 +538,7 @@ impl<'a, 'b> NonSeqTerm<'a, 'b> {
match &self {
NonSeqTerm::Str(s) => s,
NonSeqTerm::Dict(r, _) | NonSeqTerm::DictRef(r, _) => r,
NonSeqTerm::List(r, _) | NonSeqTerm::ListRef(r, _) => r,
}
}
@ -537,6 +547,8 @@ impl<'a, 'b> NonSeqTerm<'a, 'b> {
NonSeqTerm::Str(s) => NonSeqTerm::Str(s),
NonSeqTerm::Dict(r, d) => NonSeqTerm::DictRef(r, d),
NonSeqTerm::DictRef(r, d) => NonSeqTerm::DictRef(r, d),
NonSeqTerm::List(r, l) => NonSeqTerm::ListRef(r, l),
NonSeqTerm::ListRef(r, l) => NonSeqTerm::ListRef(r, l),
}
}
}
@ -561,10 +573,6 @@ impl<'a, 'b> std::fmt::Display for Term<'a, 'b> {
// ---- DEBUG REPR ----
pub(crate) fn debug(x: &[u8]) -> &str {
std::str::from_utf8(x).unwrap_or("<invalid ascii>")
}
impl<'a, 'b> std::fmt::Debug for AnyTerm<'a, 'b> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
match self.mkref() {
@ -576,6 +584,13 @@ impl<'a, 'b> std::fmt::Debug for AnyTerm<'a, 'b> {
}
write!(f, ">")
}
AnyTerm::ListRef(raw, l) => {
write!(f, "List[`{}`", debug(raw))?;
for i in l.iter() {
write!(f, "\n {:?}", i)?;
}
write!(f, "]")
}
AnyTerm::SeqRef(raw, l) => {
write!(f, "Seq[`{}`", debug(raw))?;
for i in l.iter() {

View file

@ -34,6 +34,7 @@ enum T<'a> {
Str(&'a [u8]),
OwnedStr(Vec<u8>),
Dict(HashMap<Cow<'a, [u8]>, T<'a>>),
List(Vec<T<'a>>),
Seq(Vec<T<'a>>),
}
@ -95,7 +96,7 @@ pub fn raw(bytes: &[u8]) -> Result<'_> {
Ok(Term(T::Str(bytes)))
}
/// Term corresponding to a sequence of terms
/// Term corresponding to a sequence of terms. Subsequences are banned and will raise an error.
///
/// ```
/// use nettext::enc::*;
@ -117,7 +118,7 @@ pub fn seq<'a, I: IntoIterator<Item = Term<'a>>>(terms: I) -> Result<'a> {
}
/// Term corresponding to a sequence of terms. Sub-sequences are flattenned.
pub fn seq_flatten<'a, I: IntoIterator<Item = Term<'a>>>(terms: I) -> Result<'a> {
pub fn seq_flatten<'a, I: IntoIterator<Item = Term<'a>>>(terms: I) -> Term<'a> {
let mut tmp = Vec::with_capacity(8);
for t in terms {
match t.0 {
@ -125,19 +126,22 @@ pub fn seq_flatten<'a, I: IntoIterator<Item = Term<'a>>>(terms: I) -> Result<'a>
x => tmp.push(x),
}
}
Ok(Term(T::Seq(tmp)))
Term(T::Seq(tmp))
}
/// Term corresponding to a sequence of terms. Sub-sequences are represented as NESTED: `{.= sub sequence items }`.
pub fn seq_nested<'a, I: IntoIterator<Item = Term<'a>>>(terms: I) -> Result<'a> {
let mut tmp = Vec::with_capacity(8);
for t in terms {
match t.0 {
T::Seq(t) => tmp.push(Term(T::Seq(t)).nested().0),
x => tmp.push(x),
}
}
Ok(Term(T::Seq(tmp)))
/// Term corresponding to a list of terms.
///
/// ```
/// use nettext::enc::*;
///
/// assert_eq!(list([
/// string("Hello").unwrap(),
/// string("world").unwrap()
/// ]).encode(), b"[\n Hello,\n world,\n]");
/// ```
pub fn list<'a, I: IntoIterator<Item = Term<'a>>>(terms: I) -> Term<'a> {
let terms = terms.into_iter().map(|x| x.0).collect::<Vec<_>>();
Term(T::List(terms))
}
/// Term corresponding to a dictionnary of items
@ -219,21 +223,6 @@ impl<'a> Term<'a> {
_ => Err(Error::NotADictionnary),
}
}
/// Makes a NESTED term of this term, by putting it in a dict
/// with a single key `.`.
///
/// Example:
///
/// ```
/// use nettext::enc::*;
///
/// assert_eq!(seq([string("hello").unwrap(), string("world").unwrap()]).unwrap().nested().encode(), b"{.= hello world }");
/// ```
#[must_use]
pub fn nested(self) -> Term<'a> {
dict([(".", self)]).unwrap()
}
}
// ---- additional internal functions for serde module ----
@ -285,13 +274,9 @@ impl<'a> T<'a> {
buf.extend_from_slice(b"{}");
} else if d.len() == 1 {
let (k, v) = d.into_iter().next().unwrap();
if k.as_ref() == b"." {
buf.extend_from_slice(b"{.= ");
} else {
buf.extend_from_slice(b"{ ");
buf.extend_from_slice(k.borrow());
buf.extend_from_slice(b" = ");
}
buf.extend_from_slice(b"{ ");
buf.extend_from_slice(k.borrow());
buf.extend_from_slice(b" = ");
v.encode_aux(buf, indent + 2, false);
buf.extend_from_slice(b" }");
} else {
@ -315,6 +300,29 @@ impl<'a> T<'a> {
buf.push(b'}');
}
}
T::List(l) => {
if l.len() == 0 {
buf.extend_from_slice(b"[]");
} else if l.len() == 1 {
buf.extend_from_slice(b"[ ");
l.into_iter().next().unwrap().encode_aux(buf, indent + 2, false);
buf.extend_from_slice(b" ]");
} else {
let indent2 = indent + 2;
buf.extend_from_slice(b"[\n");
for item in l {
for _ in 0..indent2 {
buf.push(b' ');
}
item.encode_aux(buf, indent2, false);
buf.extend_from_slice(b",\n");
}
for _ in 0..indent {
buf.push(b' ');
}
buf.push(b']');
}
}
T::Seq(l) => {
let indent2 = indent + 2;
for (i, v) in l.into_iter().enumerate() {
@ -335,6 +343,7 @@ impl<'a> T<'a> {
#[cfg(test)]
mod tests {
use crate::debug;
use super::*;
#[test]
@ -342,6 +351,10 @@ mod tests {
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()),
@ -350,37 +363,17 @@ mod tests {
.unwrap(),
])
.unwrap();
let expected = b"HELLO alexhelloworld {
let expected = "HELLO alexhelloworld [
dude,
why,
] {
data = { f1 = plop, f2 = kuko },
from = jxx,
subject = hello,
}";
let enc = input.encode();
eprintln!("{}", std::str::from_utf8(&enc).unwrap());
eprintln!("{}", std::str::from_utf8(&expected[..]).unwrap());
assert_eq!(&enc, &expected[..]);
}
#[test]
fn nested() {
assert!(seq([
string("a").unwrap(),
string("b").unwrap(),
seq([string("c").unwrap(), string("d").unwrap()]).unwrap()
])
.is_err());
assert_eq!(
seq([
string("a").unwrap(),
string("b").unwrap(),
seq([string("c").unwrap(), string("d").unwrap()])
.unwrap()
.nested()
])
.unwrap()
.encode(),
b"a b {.= c d }"
);
eprintln!("{}", debug(&enc));
eprintln!("{}", expected);
assert_eq!(debug(&enc), expected);
}
}

View file

@ -100,6 +100,9 @@ pub(crate) const DICT_OPEN: u8 = b'{';
pub(crate) const DICT_CLOSE: u8 = b'}';
pub(crate) const DICT_ASSIGN: u8 = b'=';
pub(crate) const DICT_DELIM: u8 = b',';
pub(crate) const LIST_OPEN: u8 = b'[';
pub(crate) const LIST_CLOSE: u8 = b']';
pub(crate) const LIST_DELIM: u8 = b',';
pub(crate) const STR_EXTRA_CHARS: &[u8] = b"._-+*?@:";
pub(crate) fn is_string_char(c: u8) -> bool {
@ -109,3 +112,7 @@ pub(crate) fn is_string_char(c: u8) -> bool {
pub(crate) fn is_whitespace(c: u8) -> bool {
c.is_ascii_whitespace()
}
pub(crate) fn debug(x: &[u8]) -> &str {
std::str::from_utf8(x).unwrap_or("<invalid ascii>")
}

View file

@ -6,7 +6,7 @@ use serde::de::{
};
use serde::Deserialize;
use crate::dec::debug as fmtdebug;
use crate::debug as fmtdebug;
use crate::dec::*;
use crate::serde::error::{Error, Result};
@ -15,11 +15,7 @@ pub struct Deserializer<'de, 'a>(Term<'de, 'a>);
impl<'de, 'a> Deserializer<'de, 'a> {
fn from_term(input: &'a Term<'de, 'a>) -> Deserializer<'de, 'a> {
if let Ok(nested) = input.nested() {
Deserializer(nested)
} else {
Deserializer(Term(input.0.mkref()))
}
Deserializer(Term(input.0.mkref()))
}
}
@ -244,7 +240,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 'a> {
where
V: Visitor<'de>,
{
visitor.visit_seq(&mut Seq(&self.0.seq()))
visitor.visit_seq(&mut Seq(&self.0.list()?))
}
fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value>

View file

@ -13,15 +13,18 @@ mod tests {
use super::*;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::debug;
fn test_bidir<T: Serialize + for<'de> Deserialize<'de> + PartialEq + std::fmt::Debug>(
input: T,
expected: &[u8],
expected: &str,
) {
eprintln!("Expecting: {}", expected);
let ser = to_bytes(&input).unwrap();
eprintln!("Serialized: {}", std::str::from_utf8(&ser).unwrap());
assert_eq!(&ser, expected);
assert_eq!(&from_bytes::<T>(&ser).unwrap(), &input);
let ser = debug(&ser);
eprintln!("Serialized: {}", ser);
assert_eq!(ser, expected);
assert_eq!(from_bytes::<T>(ser.as_bytes()).unwrap(), input);
}
#[test]
@ -36,9 +39,12 @@ mod tests {
int: 1,
seq: vec!["a".to_string(), "b".to_string()],
};
let expected = br#"Test {
let expected = r#"Test {
int = 1,
seq = YQ Yg,
seq = [
YQ,
Yg,
],
}"#;
test_bidir(input, expected);
@ -52,13 +58,22 @@ mod tests {
seq: vec!["c".to_string(), "d".to_string()],
},
];
let expected = br#"{.= 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);
}
@ -73,19 +88,19 @@ mod tests {
}
let input = E::Unit;
let expected = br#"E.Unit"#;
let expected = r#"E.Unit"#;
test_bidir(input, expected);
let input = E::Newtype(1);
let expected = br#"E.Newtype 1"#;
let expected = r#"E.Newtype 1"#;
test_bidir(input, expected);
let input = E::Tuple(1, 2);
let expected = br#"E.Tuple 1 2"#;
let expected = r#"E.Tuple 1 2"#;
test_bidir(input, expected);
let input = E::Struct { a: 1 };
let expected = br#"E.Struct { a = 1 }"#;
let expected = r#"E.Struct { a = 1 }"#;
test_bidir(input, expected);
let input = vec![
@ -97,36 +112,45 @@ mod tests {
E::Tuple(3, 2),
];
let expected =
br#"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);
}
#[test]
fn test_seq1() {
let input = (1, 2, 3, 4);
let expected = br#"1 2 3 4"#;
let expected = r#"1 2 3 4"#;
test_bidir(input, expected);
}
#[test]
fn test_seq2() {
let input = (1, 2, (2, 3, 4), 5, 6);
let expected = br#"1 2 {.= 2 3 4 } 5 6"#;
fn test_list() {
let input = vec![1, 2, 3, 4];
let expected = r#"[
1,
2,
3,
4,
]"#;
test_bidir(input, expected);
}
#[test]
fn test_seq3() {
let input = [1, 2, 3, 4];
let expected = br#"1 2 3 4"#;
test_bidir(input, expected);
}
#[test]
fn test_seq4() {
let input = [[1, 2], [2, 3], [3, 4]];
let expected = br#"{.= 1 2 } {.= 2 3 } {.= 3 4 }"#;
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,
]"#;
test_bidir(input, expected);
}
@ -135,7 +159,7 @@ 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 = br#"{
let expected = r#"{
ZG9udA = cGFuaWM,
aGVsbG8 = d29ybGQ,
}"#;
@ -144,9 +168,16 @@ mod tests {
let mut input = HashMap::new();
input.insert(12, vec![42, 125]);
input.insert(33, vec![19, 22, 21]);
let expected = br#"{
12 = 42 125,
33 = 19 22 21,
let expected = r#"{
12 = [
42,
125,
],
33 = [
19,
22,
21,
],
}"#;
test_bidir(input, expected);
}

View file

@ -28,7 +28,7 @@ impl<'a> ser::Serializer for &'a mut Serializer {
type Error = Error;
type SerializeSeq = SeqSerializer;
type SerializeSeq = ListSerializer;
type SerializeTuple = SeqSerializer;
type SerializeTupleStruct = SeqSerializer;
type SerializeTupleVariant = SeqSerializer;
@ -104,7 +104,7 @@ impl<'a> ser::Serializer for &'a mut Serializer {
where
T: ?Sized + Serialize,
{
Ok(value.serialize(self)?.nested())
Ok(value.serialize(self)?)
}
fn serialize_unit(self) -> Result<Self::Ok> {
@ -128,7 +128,7 @@ impl<'a> ser::Serializer for &'a mut Serializer {
where
T: ?Sized + Serialize,
{
Ok(seq_flatten([string(name)?, value.serialize(self)?])?)
Ok(seq_flatten([string(name)?, value.serialize(self)?]))
}
fn serialize_newtype_variant<T>(
@ -144,11 +144,11 @@ impl<'a> ser::Serializer for &'a mut Serializer {
Ok(seq_flatten([
string_owned(format!("{}.{}", name, variant))?,
value.serialize(self)?,
])?)
]))
}
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
Ok(SeqSerializer { items: vec![] })
Ok(ListSerializer { items: vec![] })
}
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
@ -213,22 +213,6 @@ impl<'a> ser::Serializer for &'a mut Serializer {
pub struct SeqSerializer {
items: Vec<Term<'static>>,
}
impl ser::SerializeSeq for SeqSerializer {
type Ok = Term<'static>;
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
self.items.push(value.serialize(&mut Serializer)?);
Ok(())
}
fn end(self) -> Result<Self::Ok> {
Ok(seq_nested(self.items.into_iter())?)
}
}
impl ser::SerializeTuple for SeqSerializer {
type Ok = Term<'static>;
@ -243,7 +227,7 @@ impl ser::SerializeTuple for SeqSerializer {
}
fn end(self) -> Result<Self::Ok> {
Ok(seq_nested(self.items.into_iter())?)
Ok(seq(self.items.into_iter())?)
}
}
@ -260,7 +244,7 @@ impl ser::SerializeTupleStruct for SeqSerializer {
}
fn end(self) -> Result<Self::Ok> {
Ok(seq_nested(self.items.into_iter())?)
Ok(seq(self.items.into_iter())?)
}
}
@ -277,7 +261,27 @@ impl ser::SerializeTupleVariant for SeqSerializer {
}
fn end(self) -> Result<Self::Ok> {
Ok(seq_nested(self.items.into_iter())?)
Ok(seq(self.items.into_iter())?)
}
}
pub struct ListSerializer {
items: Vec<Term<'static>>,
}
impl ser::SerializeSeq for ListSerializer {
type Ok = Term<'static>;
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
self.items.push(value.serialize(&mut Serializer)?);
Ok(())
}
fn end(self) -> Result<Self::Ok> {
Ok(list(self.items.into_iter()))
}
}