A list is now called a sequence; works the same

This commit is contained in:
Alex 2022-12-15 13:32:38 +01:00
parent a43b51211a
commit 08c221558f
Signed by: lx
GPG key ID: 0E496D15096376BE
9 changed files with 163 additions and 165 deletions

View file

@ -2,7 +2,7 @@
name = "nettext" name = "nettext"
description = "A text-based data format for cryptographic network protocols" description = "A text-based data format for cryptographic network protocols"
authors = ["Alex Auvolat <alex@adnab.me>"] authors = ["Alex Auvolat <alex@adnab.me>"]
version = "0.2.3" version = "0.3.0"
edition = "2021" edition = "2021"
license = "AGPL-3.0" license = "AGPL-3.0"
readme = "README.md" readme = "README.md"

View file

@ -15,9 +15,9 @@ A term can be of any of the following kinds:
- a string, which may contain only ASCII alphanumeric terms and `.-_*?` - a string, which may contain only ASCII alphanumeric terms and `.-_*?`
- a dict, which maps strings (as defined above) to any term type - a dict, which maps strings (as defined above) to any term type
- a list, which is a consecutive sequence of at least 2 strings or dicts (can be mixed), simply separated by whitespace - a sequence, consistuted of at least two of the above (can be mixed), simply separated by whitespace; sequences cannot be nested
Nested lists can be represented using a special dictionnary with a single key, `.`, 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`. for instance `TEST a { . = 0 4 2 1 9 7 0 } c`.
Dicts are represented as follows: Dicts are represented as follows:
@ -29,7 +29,7 @@ Dicts are represented as follows:
} }
``` ```
Lists are represented as follows: Sequences are represented as follows:
``` ```
term1 term2 term3 term1 term2 term3
@ -44,15 +44,15 @@ SENDTO alex {
} }
``` ```
The raw representation of a parsed dict or list is retained for hashing purposes. The raw representation of a parsed dict or sequence is retained for hashing purposes.
It in the sequence of bytes, in the encoded string, trimmed from whitespace at extremities, It in the sequence of bytes, in the encoded string, trimmed from whitespace at extremities,
that represents the encoded dict or list in that string. that represents the encoded dict or sequence in that string.
In the complex stance example above, here are the lists and dicts and their raw representation: In the complex stance example above, here are the sequence and dicts and their raw representation:
- the toplevel term is a list, whose raw representation is the entire encoded string (assuming no whitespace at beginning or end) - 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 list is a dict, whose raw representation starts at `{` and ends at `}` - 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 list, whose raw representation is exactly `blah blah`. - the second 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. Since strings cannot contain whitespace, they are always equivalent to their raw representation.
@ -61,12 +61,12 @@ Since strings cannot contain whitespace, they are always equivalent to their raw
Terms can be interpreted in a number of different ways, depending on the context: Terms can be interpreted in a number of different ways, depending on the context:
- RAW: the term is interpreted as its raw encoding (see above) - RAW: the term is interpreted as its raw encoding (see above)
- STRING: if the term is a string or a list composed exclusively of strings, the term is interpreted as its raw encoding - STRING: if the term is a string or a sequence composed exclusively of strings, the term is interpreted as its raw encoding
- VARIANT: if the term is a list whose first item is a string, it is interpreted as a variant with the following properties: - VARIANT: if the term is a sequence whose first item is a string, it is interpreted as a variant with the following properties:
- a discriminator (the first item) - a discriminator (the first item)
- a value, which is either the second item in case there are only two items, or the list composed of all items starting from the second if there are more than two - 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 - DICT: if the term is a dict, interpret it as such
- LIST: if the term is a string or a dict, interpret it as a list composed of that single term. Otherwise, the term is a list, interpret it as a list of terms. - 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 - NESTED: if the term is a dict with a single key `.`, interpret it as the term associated to that key
## Data mappings ## Data mappings

View file

@ -8,7 +8,7 @@ use nom::{
IResult, InputLength, IResult, InputLength,
}; };
use crate::dec::{AnyTerm, DecodeError, NonListTerm, Term}; 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};
// ---- // ----
@ -25,21 +25,21 @@ pub fn decode(input: &[u8]) -> std::result::Result<Term<'_, '_>, DecodeError<'_>
fn decode_term(input: &[u8]) -> IResult<&'_ [u8], AnyTerm<'_, '_>> { fn decode_term(input: &[u8]) -> IResult<&'_ [u8], AnyTerm<'_, '_>> {
let (start, _) = take_while(is_whitespace)(input)?; let (start, _) = take_while(is_whitespace)(input)?;
let (rest, list) = separated_list1(take_while1(is_whitespace), decode_nonlist_term)(start)?; let (rest, seq) = separated_list1(take_while1(is_whitespace), decode_nonseq_term)(start)?;
if list.len() == 1 { if seq.len() == 1 {
Ok((rest, list.into_iter().next().unwrap().into())) Ok((rest, seq.into_iter().next().unwrap().into()))
} else { } else {
let raw_len = start.input_len() - rest.input_len(); let raw_len = start.input_len() - rest.input_len();
let list_raw = &start[..raw_len]; let seq_raw = &start[..raw_len];
Ok((rest, AnyTerm::List(list_raw, list))) Ok((rest, AnyTerm::Seq(seq_raw, seq)))
} }
} }
fn decode_nonlist_term(input: &[u8]) -> IResult<&'_ [u8], NonListTerm<'_, '_>> { fn decode_nonseq_term(input: &[u8]) -> IResult<&'_ [u8], NonSeqTerm<'_, '_>> {
let (rest, term) = alt(( let (rest, term) = alt((
map(decode_str, NonListTerm::Str), map(decode_str, NonSeqTerm::Str),
map(decode_dict, |(raw, d)| NonListTerm::Dict(raw, d)), map(decode_dict, |(raw, d)| NonSeqTerm::Dict(raw, d)),
))(input)?; ))(input)?;
Ok((rest, term)) Ok((rest, term))
} }
@ -94,17 +94,17 @@ mod tests {
} }
#[test] #[test]
fn list_of_str_str() { fn seq_of_str_str() {
let bytes = b" plop plap plip ploup "; let bytes = b" plop plap plip ploup ";
assert_eq!( assert_eq!(
decode(bytes), decode(bytes),
Ok(AnyTerm::List( Ok(AnyTerm::Seq(
b"plop plap plip ploup", b"plop plap plip ploup",
vec![ vec![
NonListTerm::Str(b"plop"), NonSeqTerm::Str(b"plop"),
NonListTerm::Str(b"plap"), NonSeqTerm::Str(b"plap"),
NonListTerm::Str(b"plip"), NonSeqTerm::Str(b"plip"),
NonListTerm::Str(b"ploup"), NonSeqTerm::Str(b"ploup"),
] ]
) )
.into()) .into())
@ -122,9 +122,9 @@ mod tests {
(&b"aze"[..], AnyTerm::Str(b"hello")), (&b"aze"[..], AnyTerm::Str(b"hello")),
( (
&b"by"[..], &b"by"[..],
AnyTerm::List( AnyTerm::Seq(
b"bojzkz pipo", b"bojzkz pipo",
vec![NonListTerm::Str(b"bojzkz"), NonListTerm::Str(b"pipo")] vec![NonSeqTerm::Str(b"bojzkz"), NonSeqTerm::Str(b"pipo")]
) )
), ),
(&b"ccde"[..], AnyTerm::Str(b"ke")), (&b"ccde"[..], AnyTerm::Str(b"ke")),
@ -147,9 +147,9 @@ mod tests {
(&b"aze"[..], AnyTerm::Str(b"hello")), (&b"aze"[..], AnyTerm::Str(b"hello")),
( (
&b"by"[..], &b"by"[..],
AnyTerm::List( AnyTerm::Seq(
b"bojzkz pipo", b"bojzkz pipo",
vec![NonListTerm::Str(b"bojzkz"), NonListTerm::Str(b"pipo")] vec![NonSeqTerm::Str(b"bojzkz"), NonSeqTerm::Str(b"pipo")]
) )
), ),
(&b"ccde"[..], AnyTerm::Str(b"ke")), (&b"ccde"[..], AnyTerm::Str(b"ke")),
@ -166,9 +166,9 @@ mod tests {
let bytes = b"HEAD alexpubkey"; let bytes = b"HEAD alexpubkey";
assert_eq!( assert_eq!(
decode(bytes), decode(bytes),
Ok(AnyTerm::List( Ok(AnyTerm::Seq(
b"HEAD alexpubkey", b"HEAD alexpubkey",
vec![NonListTerm::Str(b"HEAD"), NonListTerm::Str(b"alexpubkey")] vec![NonSeqTerm::Str(b"HEAD"), NonSeqTerm::Str(b"alexpubkey")]
) )
.into()), .into()),
); );
@ -179,22 +179,22 @@ mod tests {
let bytes = b"STANCE sthash stsign { author = alexpubkey, height = 12, parent = parenthash, data = MESSAGE { text = hello } }"; let bytes = b"STANCE sthash stsign { author = alexpubkey, height = 12, parent = parenthash, data = MESSAGE { text = hello } }";
assert_eq!( assert_eq!(
decode(bytes), decode(bytes),
Ok(AnyTerm::List( Ok(AnyTerm::Seq(
&bytes[..], &bytes[..],
vec![ vec![
NonListTerm::Str(b"STANCE"), NonSeqTerm::Str(b"STANCE"),
NonListTerm::Str(b"sthash"), NonSeqTerm::Str(b"sthash"),
NonListTerm::Str(b"stsign"), NonSeqTerm::Str(b"stsign"),
NonListTerm::Dict(b"{ author = alexpubkey, height = 12, parent = parenthash, data = MESSAGE { text = hello } }", NonSeqTerm::Dict(b"{ author = alexpubkey, height = 12, parent = parenthash, data = MESSAGE { text = hello } }",
[ [
(&b"author"[..], AnyTerm::Str(b"alexpubkey")), (&b"author"[..], AnyTerm::Str(b"alexpubkey")),
(&b"height"[..], AnyTerm::Str(b"12")), (&b"height"[..], AnyTerm::Str(b"12")),
(&b"parent"[..], AnyTerm::Str(b"parenthash")), (&b"parent"[..], AnyTerm::Str(b"parenthash")),
(&b"data"[..], AnyTerm::List( (&b"data"[..], AnyTerm::Seq(
b"MESSAGE { text = hello }", b"MESSAGE { text = hello }",
vec![ vec![
NonListTerm::Str(b"MESSAGE"), NonSeqTerm::Str(b"MESSAGE"),
NonListTerm::Dict( NonSeqTerm::Dict(
b"{ text = hello }", b"{ text = hello }",
[ [
(&b"text"[..], AnyTerm::Str(b"hello")), (&b"text"[..], AnyTerm::Str(b"hello")),

View file

@ -26,34 +26,34 @@ pub(crate) enum AnyTerm<'a, 'b> {
Str(&'a [u8]), Str(&'a [u8]),
Dict(&'a [u8], HashMap<&'a [u8], AnyTerm<'a, 'b>>), Dict(&'a [u8], HashMap<&'a [u8], AnyTerm<'a, 'b>>),
DictRef(&'a [u8], &'b HashMap<&'a [u8], AnyTerm<'a, 'b>>), DictRef(&'a [u8], &'b HashMap<&'a [u8], AnyTerm<'a, 'b>>),
List(&'a [u8], Vec<NonListTerm<'a, 'b>>), Seq(&'a [u8], Vec<NonSeqTerm<'a, 'b>>),
ListRef(&'a [u8], &'b [NonListTerm<'a, 'b>]), SeqRef(&'a [u8], &'b [NonSeqTerm<'a, 'b>]),
} }
#[derive(Eq, PartialEq, Clone)] #[derive(Eq, PartialEq, Clone)]
pub(crate) enum NonListTerm<'a, 'b> { pub(crate) enum NonSeqTerm<'a, 'b> {
Str(&'a [u8]), Str(&'a [u8]),
Dict(&'a [u8], HashMap<&'a [u8], AnyTerm<'a, 'b>>), Dict(&'a [u8], HashMap<&'a [u8], AnyTerm<'a, 'b>>),
DictRef(&'a [u8], &'b HashMap<&'a [u8], AnyTerm<'a, 'b>>), DictRef(&'a [u8], &'b HashMap<&'a [u8], AnyTerm<'a, 'b>>),
} }
impl<'a, 'b> From<NonListTerm<'a, 'b>> for AnyTerm<'a, 'b> { impl<'a, 'b> From<NonSeqTerm<'a, 'b>> for AnyTerm<'a, 'b> {
fn from(x: NonListTerm<'a, 'b>) -> AnyTerm<'a, 'b> { fn from(x: NonSeqTerm<'a, 'b>) -> AnyTerm<'a, 'b> {
match x { match x {
NonListTerm::Str(s) => AnyTerm::Str(s), NonSeqTerm::Str(s) => AnyTerm::Str(s),
NonListTerm::Dict(raw, d) => AnyTerm::Dict(raw, d), NonSeqTerm::Dict(raw, d) => AnyTerm::Dict(raw, d),
NonListTerm::DictRef(raw, d) => AnyTerm::DictRef(raw, d), NonSeqTerm::DictRef(raw, d) => AnyTerm::DictRef(raw, d),
} }
} }
} }
impl<'a, 'b> TryFrom<AnyTerm<'a, 'b>> for NonListTerm<'a, 'b> { impl<'a, 'b> TryFrom<AnyTerm<'a, 'b>> for NonSeqTerm<'a, 'b> {
type Error = (); type Error = ();
fn try_from(x: AnyTerm<'a, 'b>) -> Result<NonListTerm<'a, 'b>, ()> { fn try_from(x: AnyTerm<'a, 'b>) -> Result<NonSeqTerm<'a, 'b>, ()> {
match x { match x {
AnyTerm::Str(s) => Ok(NonListTerm::Str(s)), AnyTerm::Str(s) => Ok(NonSeqTerm::Str(s)),
AnyTerm::Dict(raw, d) => Ok(NonListTerm::Dict(raw, d)), AnyTerm::Dict(raw, d) => Ok(NonSeqTerm::Dict(raw, d)),
AnyTerm::DictRef(raw, d) => Ok(NonListTerm::DictRef(raw, d)), AnyTerm::DictRef(raw, d) => Ok(NonSeqTerm::DictRef(raw, d)),
_ => Err(()), _ => Err(()),
} }
} }
@ -118,7 +118,7 @@ impl<'a, 'b> Term<'a, 'b> {
} }
} }
/// If the term is a single string, or a list containing only strings, /// If the term is a single string, or a sequence containing only strings,
/// get its raw representation /// get its raw representation
/// ///
/// Example: /// Example:
@ -135,16 +135,16 @@ impl<'a, 'b> Term<'a, 'b> {
pub fn string(&self) -> Result<&'a str, TypeError> { pub fn string(&self) -> Result<&'a str, TypeError> {
match &self.0 { match &self.0 {
AnyTerm::Str(s) => Ok(std::str::from_utf8(s)?), AnyTerm::Str(s) => Ok(std::str::from_utf8(s)?),
AnyTerm::List(r, l) if l.iter().all(|x| matches!(x, NonListTerm::Str(_))) => { AnyTerm::Seq(r, l) if l.iter().all(|x| matches!(x, NonSeqTerm::Str(_))) => {
Ok(std::str::from_utf8(r)?) Ok(std::str::from_utf8(r)?)
} }
_ => Err(TypeError::WrongType("STRING")), _ => Err(TypeError::WrongType("STRING")),
} }
} }
/// Return a list of terms made from this term. /// Return a sequence of terms made from this term.
/// If it is a str or a dict, returns a list of a single term. /// If it is a str or a dict, returns a seq of a single term.
/// If it is a list, that's the list of terms we return. /// If it is a sequence, that's the seq of terms we return.
/// ///
/// Example: /// Example:
/// ///
@ -152,26 +152,26 @@ impl<'a, 'b> Term<'a, 'b> {
/// use nettext::dec::decode; /// use nettext::dec::decode;
/// ///
/// let term1 = decode(b"hello").unwrap(); /// let term1 = decode(b"hello").unwrap();
/// let list1 = term1.list(); /// let seq1 = term1.seq();
/// assert_eq!(list1.len(), 1); /// assert_eq!(seq1.len(), 1);
/// assert_eq!(list1[0].str().unwrap(), "hello"); /// assert_eq!(seq1[0].str().unwrap(), "hello");
/// ///
/// let term2 = decode(b"hello world").unwrap(); /// let term2 = decode(b"hello world").unwrap();
/// let list2 = term2.list(); /// let seq2 = term2.seq();
/// assert_eq!(list2.len(), 2); /// assert_eq!(seq2.len(), 2);
/// assert_eq!(list2[0].str().unwrap(), "hello"); /// assert_eq!(seq2[0].str().unwrap(), "hello");
/// assert_eq!(list2[1].str().unwrap(), "world"); /// assert_eq!(seq2[1].str().unwrap(), "world");
/// ``` /// ```
pub fn list(&self) -> Vec<Term<'a, '_>> { pub fn seq(&self) -> Vec<Term<'a, '_>> {
match self.0.mkref() { match self.0.mkref() {
AnyTerm::ListRef(_r, l) => l.iter().map(|x| Term(x.mkref().into())).collect::<Vec<_>>(), AnyTerm::SeqRef(_r, l) => l.iter().map(|x| Term(x.mkref().into())).collect::<Vec<_>>(),
x => vec![Term(x)], x => vec![Term(x)],
} }
} }
/// Same as `.list()`, but deconstructs it in a const length array, /// Same as `.seq()`, but deconstructs it in a const length array,
/// dynamically checking if there are the correct number of items. /// dynamically checking if there are the correct number of items.
/// This allows to directly bind the resulting list into discrete variables. /// This allows to directly bind the resulting seq into discrete variables.
/// ///
/// Example: /// Example:
/// ///
@ -179,24 +179,24 @@ impl<'a, 'b> Term<'a, 'b> {
/// use nettext::dec::decode; /// use nettext::dec::decode;
/// ///
/// let term1 = decode(b"hello").unwrap(); /// let term1 = decode(b"hello").unwrap();
/// let [s1] = term1.list_of().unwrap(); /// let [s1] = term1.seq_of().unwrap();
/// assert_eq!(s1.str().unwrap(), "hello"); /// assert_eq!(s1.str().unwrap(), "hello");
/// ///
/// let term2 = decode(b"hello world").unwrap(); /// let term2 = decode(b"hello world").unwrap();
/// let [s2a, s2b] = term2.list_of().unwrap(); /// let [s2a, s2b] = term2.seq_of().unwrap();
/// assert_eq!(s2a.str().unwrap(), "hello"); /// assert_eq!(s2a.str().unwrap(), "hello");
/// assert_eq!(s2b.str().unwrap(), "world"); /// assert_eq!(s2b.str().unwrap(), "world");
/// ``` /// ```
pub fn list_of<const N: usize>(&self) -> Result<[Term<'a, '_>; N], TypeError> { pub fn seq_of<const N: usize>(&self) -> Result<[Term<'a, '_>; N], TypeError> {
let list = self.list(); let seq = self.seq();
let list_len = list.len(); let seq_len = seq.len();
list.try_into() seq.try_into()
.map_err(|_| TypeError::WrongLength(list_len, N)) .map_err(|_| TypeError::WrongLength(seq_len, N))
} }
/// Same as `.list_of()`, but only binds the first N-1 terms. /// Same as `.seq_of()`, but only binds the first N-1 terms.
/// If there are exactly N terms, the last one is bound to the Nth return variable. /// If there are exactly N terms, the last one is bound to the Nth return variable.
/// If there are more then N terms, the remaining terms are bound to a new list term /// If there are more then N terms, the remaining terms are bound to a new seq term
/// that is returned as the Nth return variable. /// that is returned as the Nth return variable.
/// ///
/// Example: /// Example:
@ -205,21 +205,21 @@ impl<'a, 'b> Term<'a, 'b> {
/// use nettext::dec::decode; /// use nettext::dec::decode;
/// ///
/// let term1 = decode(b"hello world").unwrap(); /// let term1 = decode(b"hello world").unwrap();
/// let [s1a, s1b] = term1.list_of_first().unwrap(); /// let [s1a, s1b] = term1.seq_of_first().unwrap();
/// assert_eq!(s1a.str().unwrap(), "hello"); /// assert_eq!(s1a.str().unwrap(), "hello");
/// assert_eq!(s1b.str().unwrap(), "world"); /// assert_eq!(s1b.str().unwrap(), "world");
/// ///
/// let term2 = decode(b"hello mighty world").unwrap(); /// let term2 = decode(b"hello mighty world").unwrap();
/// let [s2a, s2b] = term2.list_of_first().unwrap(); /// let [s2a, s2b] = term2.seq_of_first().unwrap();
/// assert_eq!(s2a.str().unwrap(), "hello"); /// assert_eq!(s2a.str().unwrap(), "hello");
/// assert_eq!(s2b.list().len(), 2); /// assert_eq!(s2b.seq().len(), 2);
/// assert_eq!(s2b.raw(), b"mighty world"); /// assert_eq!(s2b.raw(), b"mighty world");
/// ``` /// ```
pub fn list_of_first<const N: usize>(&self) -> Result<[Term<'a, '_>; N], TypeError> { pub fn seq_of_first<const N: usize>(&self) -> Result<[Term<'a, '_>; N], TypeError> {
match self.0.mkref() { match self.0.mkref() {
AnyTerm::ListRef(raw, list) => match list.len().cmp(&N) { AnyTerm::SeqRef(raw, seq) => match seq.len().cmp(&N) {
std::cmp::Ordering::Less => Err(TypeError::WrongLength(list.len(), N)), std::cmp::Ordering::Less => Err(TypeError::WrongLength(seq.len(), N)),
std::cmp::Ordering::Equal => Ok(list std::cmp::Ordering::Equal => Ok(seq
.iter() .iter()
.map(|x| Term(x.mkref().into())) .map(|x| Term(x.mkref().into()))
.collect::<Vec<_>>() .collect::<Vec<_>>()
@ -227,15 +227,15 @@ impl<'a, 'b> Term<'a, 'b> {
.unwrap()), .unwrap()),
std::cmp::Ordering::Greater => { std::cmp::Ordering::Greater => {
let mut ret = Vec::with_capacity(N); let mut ret = Vec::with_capacity(N);
for item in list[0..N - 1].iter() { for item in seq[0..N - 1].iter() {
ret.push(Term(item.mkref().into())); ret.push(Term(item.mkref().into()));
} }
let remaining_begin = list[N - 1].raw().as_ptr() as usize; let remaining_begin = seq[N - 1].raw().as_ptr() as usize;
let remaining_offset = remaining_begin - raw.as_ptr() as usize; let remaining_offset = remaining_begin - raw.as_ptr() as usize;
let remaining_raw = &raw[remaining_offset..]; let remaining_raw = &raw[remaining_offset..];
ret.push(Term(AnyTerm::ListRef(remaining_raw, &list[N - 1..]))); ret.push(Term(AnyTerm::SeqRef(remaining_raw, &seq[N - 1..])));
Ok(ret.try_into().unwrap()) Ok(ret.try_into().unwrap())
} }
@ -275,7 +275,7 @@ impl<'a, 'b> Term<'a, 'b> {
} }
/// Checks term is a dictionnary whose keys are exactly those supplied, /// Checks term is a dictionnary whose keys are exactly those supplied,
/// and returns the associated values as a list. /// and returns the associated values as a seq.
/// ///
/// Example: /// Example:
/// ///
@ -317,7 +317,7 @@ impl<'a, 'b> Term<'a, 'b> {
} }
/// Checks term is a dictionnary whose keys are included in those supplied, /// Checks term is a dictionnary whose keys are included in those supplied,
/// and returns the associated values as a list of options. /// and returns the associated values as a seq of options.
/// ///
/// Example: /// Example:
/// ///
@ -419,10 +419,10 @@ impl<'a, 'b> Term<'a, 'b> {
decode(encoded) decode(encoded)
} }
} }
AnyTerm::ListRef(_, list) => { AnyTerm::SeqRef(_, seq) => {
let mut ret = Vec::with_capacity(128); let mut ret = Vec::with_capacity(128);
for term in list.iter() { for term in seq.iter() {
if let NonListTerm::Str(encoded) = term { if let NonSeqTerm::Str(encoded) = term {
ret.extend(decode(encoded)?) ret.extend(decode(encoded)?)
} else { } else {
return Err(TypeError::WrongType("BYTES")); return Err(TypeError::WrongType("BYTES"));
@ -508,8 +508,8 @@ impl<'a, 'b> AnyTerm<'a, 'b> {
AnyTerm::Str(s) => s, AnyTerm::Str(s) => s,
AnyTerm::Dict(r, _) AnyTerm::Dict(r, _)
| AnyTerm::DictRef(r, _) | AnyTerm::DictRef(r, _)
| AnyTerm::List(r, _) | AnyTerm::Seq(r, _)
| AnyTerm::ListRef(r, _) => r, | AnyTerm::SeqRef(r, _) => r,
} }
} }
@ -518,25 +518,25 @@ impl<'a, 'b> AnyTerm<'a, 'b> {
AnyTerm::Str(s) => AnyTerm::Str(s), AnyTerm::Str(s) => AnyTerm::Str(s),
AnyTerm::Dict(r, d) => AnyTerm::DictRef(r, d), AnyTerm::Dict(r, d) => AnyTerm::DictRef(r, d),
AnyTerm::DictRef(r, d) => AnyTerm::DictRef(r, d), AnyTerm::DictRef(r, d) => AnyTerm::DictRef(r, d),
AnyTerm::List(r, l) => AnyTerm::ListRef(r, &l[..]), AnyTerm::Seq(r, l) => AnyTerm::SeqRef(r, &l[..]),
AnyTerm::ListRef(r, l) => AnyTerm::ListRef(r, l), AnyTerm::SeqRef(r, l) => AnyTerm::SeqRef(r, l),
} }
} }
} }
impl<'a, 'b> NonListTerm<'a, 'b> { impl<'a, 'b> NonSeqTerm<'a, 'b> {
fn raw(&self) -> &'a [u8] { fn raw(&self) -> &'a [u8] {
match &self { match &self {
NonListTerm::Str(s) => s, NonSeqTerm::Str(s) => s,
NonListTerm::Dict(r, _) | NonListTerm::DictRef(r, _) => r, NonSeqTerm::Dict(r, _) | NonSeqTerm::DictRef(r, _) => r,
} }
} }
fn mkref(&self) -> NonListTerm<'a, '_> { fn mkref(&self) -> NonSeqTerm<'a, '_> {
match &self { match &self {
NonListTerm::Str(s) => NonListTerm::Str(s), NonSeqTerm::Str(s) => NonSeqTerm::Str(s),
NonListTerm::Dict(r, d) => NonListTerm::DictRef(r, d), NonSeqTerm::Dict(r, d) => NonSeqTerm::DictRef(r, d),
NonListTerm::DictRef(r, d) => NonListTerm::DictRef(r, d), NonSeqTerm::DictRef(r, d) => NonSeqTerm::DictRef(r, d),
} }
} }
} }
@ -576,8 +576,8 @@ impl<'a, 'b> std::fmt::Debug for AnyTerm<'a, 'b> {
} }
write!(f, ">") write!(f, ">")
} }
AnyTerm::ListRef(raw, l) => { AnyTerm::SeqRef(raw, l) => {
write!(f, "List[`{}`", debug(raw))?; write!(f, "Seq[`{}`", debug(raw))?;
for i in l.iter() { for i in l.iter() {
write!(f, "\n {:?}", i)?; write!(f, "\n {:?}", i)?;
} }
@ -588,7 +588,7 @@ impl<'a, 'b> std::fmt::Debug for AnyTerm<'a, 'b> {
} }
} }
impl<'a, 'b> std::fmt::Debug for NonListTerm<'a, 'b> { impl<'a, 'b> std::fmt::Debug for NonSeqTerm<'a, 'b> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
AnyTerm::from(self.mkref()).fmt(f) AnyTerm::from(self.mkref()).fmt(f)
} }

View file

@ -12,7 +12,7 @@ pub enum Error {
#[error(display = "Duplicate key: {}", _0)] #[error(display = "Duplicate key: {}", _0)]
DuplicateKey(String), DuplicateKey(String),
#[error( #[error(
display = "Refusing to build nested lists with list(), use either list_flatten() or list_nested()" display = "Refusing to build nested sequencess with seq(), use either seq_flatten() or seq_nested()"
)] )]
ListInList, SeqInSeq,
} }

View file

@ -5,7 +5,7 @@
//! ``` //! ```
//! use nettext::enc::*; //! use nettext::enc::*;
//! //!
//! let nettext_encoding = list([ //! let nettext_encoding = seq([
//! string("CALL").unwrap(), //! string("CALL").unwrap(),
//! string("myfunction").unwrap(), //! string("myfunction").unwrap(),
//! dict([ //! dict([
@ -34,7 +34,7 @@ enum T<'a> {
Str(&'a [u8]), Str(&'a [u8]),
OwnedStr(Vec<u8>), OwnedStr(Vec<u8>),
Dict(HashMap<Cow<'a, [u8]>, T<'a>>), Dict(HashMap<Cow<'a, [u8]>, T<'a>>),
List(Vec<T<'a>>), Seq(Vec<T<'a>>),
} }
/// The result type for trying to encode something as nettext /// The result type for trying to encode something as nettext
@ -95,49 +95,49 @@ pub fn raw(bytes: &[u8]) -> Result<'_> {
Ok(Term(T::Str(bytes))) Ok(Term(T::Str(bytes)))
} }
/// Term corresponding to a list of terms /// Term corresponding to a sequence of terms
/// ///
/// ``` /// ```
/// use nettext::enc::*; /// use nettext::enc::*;
/// ///
/// assert_eq!(list([ /// assert_eq!(seq([
/// string("Hello").unwrap(), /// string("Hello").unwrap(),
/// string("world").unwrap() /// string("world").unwrap()
/// ]).unwrap().encode(), b"Hello world"); /// ]).unwrap().encode(), b"Hello world");
/// ``` /// ```
pub fn list<'a, I: IntoIterator<Item = Term<'a>>>(terms: I) -> Result<'a> { pub fn seq<'a, I: IntoIterator<Item = Term<'a>>>(terms: I) -> Result<'a> {
let mut tmp = Vec::with_capacity(8); let mut tmp = Vec::with_capacity(8);
for t in terms { for t in terms {
match t.0 { match t.0 {
T::List(_) => return Err(Error::ListInList), T::Seq(_) => return Err(Error::SeqInSeq),
x => tmp.push(x), x => tmp.push(x),
} }
} }
Ok(Term(T::List(tmp))) Ok(Term(T::Seq(tmp)))
} }
/// Term corresponding to a list of terms. Sub-lists are flattenned. /// Term corresponding to a sequence of terms. Sub-sequences are flattenned.
pub fn list_flatten<'a, I: IntoIterator<Item = Term<'a>>>(terms: I) -> Result<'a> { pub fn seq_flatten<'a, I: IntoIterator<Item = Term<'a>>>(terms: I) -> Result<'a> {
let mut tmp = Vec::with_capacity(8); let mut tmp = Vec::with_capacity(8);
for t in terms { for t in terms {
match t.0 { match t.0 {
T::List(t) => tmp.extend(t), T::Seq(t) => tmp.extend(t),
x => tmp.push(x), x => tmp.push(x),
} }
} }
Ok(Term(T::List(tmp))) Ok(Term(T::Seq(tmp)))
} }
/// Term corresponding to a list of terms. Sub-lists are represented as NESTED: `{.= sub list items }`. /// Term corresponding to a sequence of terms. Sub-sequences are represented as NESTED: `{.= sub sequence items }`.
pub fn list_nested<'a, I: IntoIterator<Item = Term<'a>>>(terms: I) -> Result<'a> { pub fn seq_nested<'a, I: IntoIterator<Item = Term<'a>>>(terms: I) -> Result<'a> {
let mut tmp = Vec::with_capacity(8); let mut tmp = Vec::with_capacity(8);
for t in terms { for t in terms {
match t.0 { match t.0 {
T::List(t) => tmp.push(Term(T::List(t)).nested().0), T::Seq(t) => tmp.push(Term(T::Seq(t)).nested().0),
x => tmp.push(x), x => tmp.push(x),
} }
} }
Ok(Term(T::List(tmp))) Ok(Term(T::Seq(tmp)))
} }
/// Term corresponding to a dictionnary of items /// Term corresponding to a dictionnary of items
@ -177,7 +177,7 @@ pub fn bytes(bytes: &[u8]) -> Term<'static> {
} }
/// Same as `bytes()`, but splits the byte slice in 48-byte chunks /// Same as `bytes()`, but splits the byte slice in 48-byte chunks
/// and encodes each chunk separately, putting them in a list of terms. /// and encodes each chunk separately, putting them in a sequence of terms.
/// Usefull for long byte slices to have cleaner representations, /// Usefull for long byte slices to have cleaner representations,
/// mainly usefull for dictionnary keys. /// mainly usefull for dictionnary keys.
pub fn bytes_split(bytes: &[u8]) -> Term<'static> { pub fn bytes_split(bytes: &[u8]) -> Term<'static> {
@ -186,7 +186,7 @@ pub fn bytes_split(bytes: &[u8]) -> Term<'static> {
.map(|b| T::OwnedStr(base64::encode_config(b, base64::URL_SAFE_NO_PAD).into_bytes())) .map(|b| T::OwnedStr(base64::encode_config(b, base64::URL_SAFE_NO_PAD).into_bytes()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if chunks.len() > 1 { if chunks.len() > 1 {
Term(T::List(chunks)) Term(T::Seq(chunks))
} else { } else {
Term(chunks.into_iter().next().unwrap_or(T::Str(b"."))) Term(chunks.into_iter().next().unwrap_or(T::Str(b".")))
} }
@ -194,15 +194,15 @@ pub fn bytes_split(bytes: &[u8]) -> Term<'static> {
impl<'a> Term<'a> { impl<'a> Term<'a> {
/// Append a term to an existing term. /// Append a term to an existing term.
/// Transforms the initial term into a list if necessary. /// Transforms the initial term into a seq if necessary.
#[must_use] #[must_use]
pub fn append(self, t: Term<'a>) -> Term<'a> { pub fn append(self, t: Term<'a>) -> Term<'a> {
match self.0 { match self.0 {
T::List(mut v) => { T::Seq(mut v) => {
v.push(t.0); v.push(t.0);
Term(T::List(v)) Term(T::Seq(v))
} }
x => Term(T::List(vec![x, t.0])), x => Term(T::Seq(vec![x, t.0])),
} }
} }
@ -228,7 +228,7 @@ impl<'a> Term<'a> {
/// ``` /// ```
/// use nettext::enc::*; /// use nettext::enc::*;
/// ///
/// assert_eq!(list([string("hello").unwrap(), string("world").unwrap()]).unwrap().nested().encode(), b"{.= hello world }"); /// assert_eq!(seq([string("hello").unwrap(), string("world").unwrap()]).unwrap().nested().encode(), b"{.= hello world }");
/// ``` /// ```
#[must_use] #[must_use]
pub fn nested(self) -> Term<'a> { pub fn nested(self) -> Term<'a> {
@ -271,9 +271,7 @@ impl<'a> Term<'a> {
/// Generate the nettext representation of a term, as a String /// Generate the nettext representation of a term, as a String
pub fn encode_string(self) -> String { pub fn encode_string(self) -> String {
unsafe { unsafe { String::from_utf8_unchecked(self.encode()) }
String::from_utf8_unchecked(self.encode())
}
} }
} }
@ -317,7 +315,7 @@ impl<'a> T<'a> {
buf.push(b'}'); buf.push(b'}');
} }
} }
T::List(l) => { T::Seq(l) => {
let indent2 = indent + 2; let indent2 = indent + 2;
for (i, v) in l.into_iter().enumerate() { for (i, v) in l.into_iter().enumerate() {
if !is_toplevel && buf.iter().rev().take_while(|c| **c != b'\n').count() >= 70 { if !is_toplevel && buf.iter().rev().take_while(|c| **c != b'\n').count() >= 70 {
@ -341,7 +339,7 @@ mod tests {
#[test] #[test]
fn complex1() { fn complex1() {
let input = list([ let input = seq([
string("HELLO").unwrap(), string("HELLO").unwrap(),
string("alexhelloworld").unwrap(), string("alexhelloworld").unwrap(),
dict([ dict([
@ -365,18 +363,18 @@ mod tests {
#[test] #[test]
fn nested() { fn nested() {
assert!(list([ assert!(seq([
string("a").unwrap(), string("a").unwrap(),
string("b").unwrap(), string("b").unwrap(),
list([string("c").unwrap(), string("d").unwrap()]).unwrap() seq([string("c").unwrap(), string("d").unwrap()]).unwrap()
]) ])
.is_err()); .is_err());
assert_eq!( assert_eq!(
list([ seq([
string("a").unwrap(), string("a").unwrap(),
string("b").unwrap(), string("b").unwrap(),
list([string("c").unwrap(), string("d").unwrap()]) seq([string("c").unwrap(), string("d").unwrap()])
.unwrap() .unwrap()
.nested() .nested()
]) ])

View file

@ -9,7 +9,7 @@
//! let keypair = SigningKeyPair::gen_with_defaults(); //! let keypair = SigningKeyPair::gen_with_defaults();
//! //!
//! // Encode a fist object that represents a payload that will be hashed and signed //! // Encode a fist object that represents a payload that will be hashed and signed
//! let signed_payload = list([ //! let signed_payload = seq([
//! string("CALL").unwrap(), //! string("CALL").unwrap(),
//! string("myfunction").unwrap(), //! string("myfunction").unwrap(),
//! dict([ //! dict([
@ -44,7 +44,7 @@
//! //!
//! let object2 = decode(payload.raw()).unwrap(); //! let object2 = decode(payload.raw()).unwrap();
//! //!
//! let [verb, arg1, arg2, pubkey] = object2.list_of().unwrap(); //! let [verb, arg1, arg2, pubkey] = object2.seq_of().unwrap();
//! let pubkey = pubkey.public_key().unwrap(); //! let pubkey = pubkey.public_key().unwrap();
//! assert!(verify_signature(&signature, payload.raw(), &pubkey)); //! assert!(verify_signature(&signature, payload.raw(), &pubkey));
//! //!

View file

@ -228,7 +228,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 'a> {
where where
V: Visitor<'de>, V: Visitor<'de>,
{ {
let [variant, args] = self.0.list_of_first()?; let [variant, args] = self.0.seq_of_first()?;
if variant.string()? == name { if variant.string()? == name {
visitor.visit_newtype_struct(&mut Deserializer(args)) visitor.visit_newtype_struct(&mut Deserializer(args))
} else { } else {
@ -244,14 +244,14 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 'a> {
where where
V: Visitor<'de>, V: Visitor<'de>,
{ {
visitor.visit_seq(&mut Seq(&self.0.list())) visitor.visit_seq(&mut Seq(&self.0.seq()))
} }
fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value> fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value>
where where
V: Visitor<'de>, V: Visitor<'de>,
{ {
visitor.visit_seq(&mut Seq(&self.0.list())) visitor.visit_seq(&mut Seq(&self.0.seq()))
} }
fn deserialize_tuple_struct<V>( fn deserialize_tuple_struct<V>(
@ -263,9 +263,9 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 'a> {
where where
V: Visitor<'de>, V: Visitor<'de>,
{ {
let [variant, args] = self.0.list_of_first()?; let [variant, args] = self.0.seq_of_first()?;
if variant.string()? == name { if variant.string()? == name {
visitor.visit_seq(&mut Seq(&args.list())) visitor.visit_seq(&mut Seq(&args.seq()))
} else { } else {
Err(Error::custom(format!( Err(Error::custom(format!(
"Expected {}, got: `{}`", "Expected {}, got: `{}`",
@ -291,7 +291,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 'a> {
where where
V: Visitor<'de>, V: Visitor<'de>,
{ {
let [variant, data] = self.0.list_of()?; let [variant, data] = self.0.seq_of()?;
if variant.string()? != name { if variant.string()? != name {
return Err(Error::custom(format!( return Err(Error::custom(format!(
"Expected {}, got: `{}`", "Expected {}, got: `{}`",
@ -414,7 +414,7 @@ impl<'de, 'a> EnumAccess<'de> for Enum<'de, 'a> {
where where
V: DeserializeSeed<'de>, V: DeserializeSeed<'de>,
{ {
let variant = &self.0.list()[0]; let variant = &self.0.seq()[0];
let variant = seed.deserialize(&mut Deserializer(Term(variant.0.mkref())))?; let variant = seed.deserialize(&mut Deserializer(Term(variant.0.mkref())))?;
Ok((variant, self)) Ok((variant, self))
} }
@ -424,7 +424,7 @@ impl<'de, 'a> VariantAccess<'de> for Enum<'de, 'a> {
type Error = Error; type Error = Error;
fn unit_variant(self) -> Result<()> { fn unit_variant(self) -> Result<()> {
if self.0.list().len() > 1 { if self.0.seq().len() > 1 {
Err(Error::custom("Spurrious data in unit variant")) Err(Error::custom("Spurrious data in unit variant"))
} else { } else {
Ok(()) Ok(())
@ -435,7 +435,7 @@ impl<'de, 'a> VariantAccess<'de> for Enum<'de, 'a> {
where where
T: DeserializeSeed<'de>, T: DeserializeSeed<'de>,
{ {
let [_, rest] = self.0.list_of_first()?; let [_, rest] = self.0.seq_of_first()?;
seed.deserialize(&mut Deserializer(rest)) seed.deserialize(&mut Deserializer(rest))
} }
@ -443,15 +443,15 @@ impl<'de, 'a> VariantAccess<'de> for Enum<'de, 'a> {
where where
V: Visitor<'de>, V: Visitor<'de>,
{ {
let [_, rest] = self.0.list_of_first()?; let [_, rest] = self.0.seq_of_first()?;
visitor.visit_seq(&mut Seq(&rest.list())) visitor.visit_seq(&mut Seq(&rest.seq()))
} }
fn struct_variant<V>(self, _fields: &'static [&'static str], visitor: V) -> Result<V::Value> fn struct_variant<V>(self, _fields: &'static [&'static str], visitor: V) -> Result<V::Value>
where where
V: Visitor<'de>, V: Visitor<'de>,
{ {
let [_, rest] = self.0.list_of_first()?; let [_, rest] = self.0.seq_of_first()?;
visitor.visit_map(&mut Dict::from_term(&rest)?) visitor.visit_map(&mut Dict::from_term(&rest)?)
} }
} }

View file

@ -128,7 +128,7 @@ impl<'a> ser::Serializer for &'a mut Serializer {
where where
T: ?Sized + Serialize, T: ?Sized + Serialize,
{ {
Ok(list_flatten([string(name)?, value.serialize(self)?])?) Ok(seq_flatten([string(name)?, value.serialize(self)?])?)
} }
fn serialize_newtype_variant<T>( fn serialize_newtype_variant<T>(
@ -141,7 +141,7 @@ impl<'a> ser::Serializer for &'a mut Serializer {
where where
T: ?Sized + Serialize, T: ?Sized + Serialize,
{ {
Ok(list_flatten([ Ok(seq_flatten([
string_owned(format!("{}.{}", name, variant))?, string_owned(format!("{}.{}", name, variant))?,
value.serialize(self)?, value.serialize(self)?,
])?) ])?)
@ -226,7 +226,7 @@ impl ser::SerializeSeq for SeqSerializer {
} }
fn end(self) -> Result<Self::Ok> { fn end(self) -> Result<Self::Ok> {
Ok(list_nested(self.items.into_iter())?) Ok(seq_nested(self.items.into_iter())?)
} }
} }
@ -243,7 +243,7 @@ impl ser::SerializeTuple for SeqSerializer {
} }
fn end(self) -> Result<Self::Ok> { fn end(self) -> Result<Self::Ok> {
Ok(list_nested(self.items.into_iter())?) Ok(seq_nested(self.items.into_iter())?)
} }
} }
@ -260,7 +260,7 @@ impl ser::SerializeTupleStruct for SeqSerializer {
} }
fn end(self) -> Result<Self::Ok> { fn end(self) -> Result<Self::Ok> {
Ok(list_nested(self.items.into_iter())?) Ok(seq_nested(self.items.into_iter())?)
} }
} }
@ -277,7 +277,7 @@ impl ser::SerializeTupleVariant for SeqSerializer {
} }
fn end(self) -> Result<Self::Ok> { fn end(self) -> Result<Self::Ok> {
Ok(list_nested(self.items.into_iter())?) Ok(seq_nested(self.items.into_iter())?)
} }
} }
@ -334,7 +334,7 @@ impl ser::SerializeStruct for StructSerializer {
} }
fn end(self) -> Result<Term<'static>> { fn end(self) -> Result<Term<'static>> {
Ok(list([string(self.name)?, dict(self.fields.into_iter())?])?) Ok(seq([string(self.name)?, dict(self.fields.into_iter())?])?)
} }
} }
@ -357,7 +357,7 @@ impl ser::SerializeStructVariant for StructVariantSerializer {
} }
fn end(self) -> Result<Term<'static>> { fn end(self) -> Result<Term<'static>> {
Ok(list([ Ok(seq([
string_owned(format!("{}.{}", self.name, self.variant))?, string_owned(format!("{}.{}", self.name, self.variant))?,
dict(self.fields.into_iter())?, dict(self.fields.into_iter())?,
])?) ])?)