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"
description = "A text-based data format for cryptographic network protocols"
authors = ["Alex Auvolat <alex@adnab.me>"]
version = "0.2.3"
version = "0.3.0"
edition = "2021"
license = "AGPL-3.0"
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 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`.
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
@ -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,
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 third term of the list 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 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`.
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:
- 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
- VARIANT: if the term is a list whose first item is a string, it is interpreted as a variant with the following properties:
- 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 sequence whose first item is a string, it is interpreted as a variant with the following properties:
- 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
- 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
## Data mappings

View file

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

View file

@ -26,34 +26,34 @@ 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<NonListTerm<'a, 'b>>),
ListRef(&'a [u8], &'b [NonListTerm<'a, 'b>]),
Seq(&'a [u8], Vec<NonSeqTerm<'a, 'b>>),
SeqRef(&'a [u8], &'b [NonSeqTerm<'a, 'b>]),
}
#[derive(Eq, PartialEq, Clone)]
pub(crate) enum NonListTerm<'a, 'b> {
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>>),
}
impl<'a, 'b> From<NonListTerm<'a, 'b>> for AnyTerm<'a, 'b> {
fn from(x: NonListTerm<'a, 'b>) -> AnyTerm<'a, 'b> {
impl<'a, 'b> From<NonSeqTerm<'a, 'b>> for AnyTerm<'a, 'b> {
fn from(x: NonSeqTerm<'a, 'b>) -> AnyTerm<'a, 'b> {
match x {
NonListTerm::Str(s) => AnyTerm::Str(s),
NonListTerm::Dict(raw, d) => AnyTerm::Dict(raw, d),
NonListTerm::DictRef(raw, d) => AnyTerm::DictRef(raw, d),
NonSeqTerm::Str(s) => AnyTerm::Str(s),
NonSeqTerm::Dict(raw, d) => AnyTerm::Dict(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 = ();
fn try_from(x: AnyTerm<'a, 'b>) -> Result<NonListTerm<'a, 'b>, ()> {
fn try_from(x: AnyTerm<'a, 'b>) -> Result<NonSeqTerm<'a, 'b>, ()> {
match x {
AnyTerm::Str(s) => Ok(NonListTerm::Str(s)),
AnyTerm::Dict(raw, d) => Ok(NonListTerm::Dict(raw, d)),
AnyTerm::DictRef(raw, d) => Ok(NonListTerm::DictRef(raw, d)),
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)),
_ => 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
///
/// Example:
@ -135,16 +135,16 @@ impl<'a, 'b> Term<'a, 'b> {
pub fn string(&self) -> Result<&'a str, TypeError> {
match &self.0 {
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)?)
}
_ => Err(TypeError::WrongType("STRING")),
}
}
/// Return a list 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 list, that's the list of terms we return.
/// Return a sequence of terms made from this term.
/// If it is a str or a dict, returns a seq of a single term.
/// If it is a sequence, that's the seq of terms we return.
///
/// Example:
///
@ -152,26 +152,26 @@ impl<'a, 'b> Term<'a, 'b> {
/// use nettext::dec::decode;
///
/// let term1 = decode(b"hello").unwrap();
/// let list1 = term1.list();
/// assert_eq!(list1.len(), 1);
/// assert_eq!(list1[0].str().unwrap(), "hello");
/// let seq1 = term1.seq();
/// assert_eq!(seq1.len(), 1);
/// assert_eq!(seq1[0].str().unwrap(), "hello");
///
/// let term2 = decode(b"hello world").unwrap();
/// let list2 = term2.list();
/// assert_eq!(list2.len(), 2);
/// assert_eq!(list2[0].str().unwrap(), "hello");
/// assert_eq!(list2[1].str().unwrap(), "world");
/// let seq2 = term2.seq();
/// assert_eq!(seq2.len(), 2);
/// assert_eq!(seq2[0].str().unwrap(), "hello");
/// 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() {
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)],
}
}
/// 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.
/// This allows to directly bind the resulting list into discrete variables.
/// This allows to directly bind the resulting seq into discrete variables.
///
/// Example:
///
@ -179,24 +179,24 @@ impl<'a, 'b> Term<'a, 'b> {
/// use nettext::dec::decode;
///
/// let term1 = decode(b"hello").unwrap();
/// let [s1] = term1.list_of().unwrap();
/// let [s1] = term1.seq_of().unwrap();
/// assert_eq!(s1.str().unwrap(), "hello");
///
/// 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!(s2b.str().unwrap(), "world");
/// ```
pub fn list_of<const N: usize>(&self) -> Result<[Term<'a, '_>; N], TypeError> {
let list = self.list();
let list_len = list.len();
list.try_into()
.map_err(|_| TypeError::WrongLength(list_len, N))
pub fn seq_of<const N: usize>(&self) -> Result<[Term<'a, '_>; N], TypeError> {
let seq = self.seq();
let seq_len = seq.len();
seq.try_into()
.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 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.
///
/// Example:
@ -205,21 +205,21 @@ impl<'a, 'b> Term<'a, 'b> {
/// use nettext::dec::decode;
///
/// 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!(s1b.str().unwrap(), "world");
///
/// 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!(s2b.list().len(), 2);
/// assert_eq!(s2b.seq().len(), 2);
/// 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() {
AnyTerm::ListRef(raw, list) => match list.len().cmp(&N) {
std::cmp::Ordering::Less => Err(TypeError::WrongLength(list.len(), N)),
std::cmp::Ordering::Equal => Ok(list
AnyTerm::SeqRef(raw, seq) => match seq.len().cmp(&N) {
std::cmp::Ordering::Less => Err(TypeError::WrongLength(seq.len(), N)),
std::cmp::Ordering::Equal => Ok(seq
.iter()
.map(|x| Term(x.mkref().into()))
.collect::<Vec<_>>()
@ -227,15 +227,15 @@ impl<'a, 'b> Term<'a, 'b> {
.unwrap()),
std::cmp::Ordering::Greater => {
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()));
}
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_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())
}
@ -275,7 +275,7 @@ impl<'a, 'b> Term<'a, 'b> {
}
/// 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:
///
@ -317,7 +317,7 @@ impl<'a, 'b> Term<'a, 'b> {
}
/// 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:
///
@ -419,10 +419,10 @@ impl<'a, 'b> Term<'a, 'b> {
decode(encoded)
}
}
AnyTerm::ListRef(_, list) => {
AnyTerm::SeqRef(_, seq) => {
let mut ret = Vec::with_capacity(128);
for term in list.iter() {
if let NonListTerm::Str(encoded) = term {
for term in seq.iter() {
if let NonSeqTerm::Str(encoded) = term {
ret.extend(decode(encoded)?)
} else {
return Err(TypeError::WrongType("BYTES"));
@ -508,8 +508,8 @@ impl<'a, 'b> AnyTerm<'a, 'b> {
AnyTerm::Str(s) => s,
AnyTerm::Dict(r, _)
| AnyTerm::DictRef(r, _)
| AnyTerm::List(r, _)
| AnyTerm::ListRef(r, _) => r,
| AnyTerm::Seq(r, _)
| AnyTerm::SeqRef(r, _) => r,
}
}
@ -518,25 +518,25 @@ 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),
}
}
}
impl<'a, 'b> NonListTerm<'a, 'b> {
impl<'a, 'b> NonSeqTerm<'a, 'b> {
fn raw(&self) -> &'a [u8] {
match &self {
NonListTerm::Str(s) => s,
NonListTerm::Dict(r, _) | NonListTerm::DictRef(r, _) => r,
NonSeqTerm::Str(s) => s,
NonSeqTerm::Dict(r, _) | NonSeqTerm::DictRef(r, _) => r,
}
}
fn mkref(&self) -> NonListTerm<'a, '_> {
fn mkref(&self) -> NonSeqTerm<'a, '_> {
match &self {
NonListTerm::Str(s) => NonListTerm::Str(s),
NonListTerm::Dict(r, d) => NonListTerm::DictRef(r, d),
NonListTerm::DictRef(r, d) => NonListTerm::DictRef(r, d),
NonSeqTerm::Str(s) => NonSeqTerm::Str(s),
NonSeqTerm::Dict(r, d) => NonSeqTerm::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, ">")
}
AnyTerm::ListRef(raw, l) => {
write!(f, "List[`{}`", debug(raw))?;
AnyTerm::SeqRef(raw, l) => {
write!(f, "Seq[`{}`", debug(raw))?;
for i in l.iter() {
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> {
AnyTerm::from(self.mkref()).fmt(f)
}

View file

@ -12,7 +12,7 @@ pub enum Error {
#[error(display = "Duplicate key: {}", _0)]
DuplicateKey(String),
#[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::*;
//!
//! let nettext_encoding = list([
//! let nettext_encoding = seq([
//! string("CALL").unwrap(),
//! string("myfunction").unwrap(),
//! dict([
@ -34,7 +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>>),
}
/// 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)))
}
/// Term corresponding to a list of terms
/// Term corresponding to a sequence of terms
///
/// ```
/// use nettext::enc::*;
///
/// assert_eq!(list([
/// assert_eq!(seq([
/// string("Hello").unwrap(),
/// string("world").unwrap()
/// ]).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);
for t in terms {
match t.0 {
T::List(_) => return Err(Error::ListInList),
T::Seq(_) => return Err(Error::SeqInSeq),
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.
pub fn list_flatten<'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> {
let mut tmp = Vec::with_capacity(8);
for t in terms {
match t.0 {
T::List(t) => tmp.extend(t),
T::Seq(t) => tmp.extend(t),
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 }`.
pub fn list_nested<'a, I: IntoIterator<Item = Term<'a>>>(terms: I) -> Result<'a> {
/// 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::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),
}
}
Ok(Term(T::List(tmp)))
Ok(Term(T::Seq(tmp)))
}
/// 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
/// 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,
/// mainly usefull for dictionnary keys.
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()))
.collect::<Vec<_>>();
if chunks.len() > 1 {
Term(T::List(chunks))
Term(T::Seq(chunks))
} else {
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> {
/// 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]
pub fn append(self, t: Term<'a>) -> Term<'a> {
match self.0 {
T::List(mut v) => {
T::Seq(mut v) => {
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::*;
///
/// 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]
pub fn nested(self) -> Term<'a> {
@ -271,9 +271,7 @@ impl<'a> Term<'a> {
/// Generate the nettext representation of a term, as a String
pub fn encode_string(self) -> String {
unsafe {
String::from_utf8_unchecked(self.encode())
}
unsafe { String::from_utf8_unchecked(self.encode()) }
}
}
@ -317,7 +315,7 @@ impl<'a> T<'a> {
buf.push(b'}');
}
}
T::List(l) => {
T::Seq(l) => {
let indent2 = indent + 2;
for (i, v) in l.into_iter().enumerate() {
if !is_toplevel && buf.iter().rev().take_while(|c| **c != b'\n').count() >= 70 {
@ -341,7 +339,7 @@ mod tests {
#[test]
fn complex1() {
let input = list([
let input = seq([
string("HELLO").unwrap(),
string("alexhelloworld").unwrap(),
dict([
@ -365,18 +363,18 @@ mod tests {
#[test]
fn nested() {
assert!(list([
assert!(seq([
string("a").unwrap(),
string("b").unwrap(),
list([string("c").unwrap(), string("d").unwrap()]).unwrap()
seq([string("c").unwrap(), string("d").unwrap()]).unwrap()
])
.is_err());
assert_eq!(
list([
seq([
string("a").unwrap(),
string("b").unwrap(),
list([string("c").unwrap(), string("d").unwrap()])
seq([string("c").unwrap(), string("d").unwrap()])
.unwrap()
.nested()
])

View file

@ -9,7 +9,7 @@
//! let keypair = SigningKeyPair::gen_with_defaults();
//!
//! // 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("myfunction").unwrap(),
//! dict([
@ -44,7 +44,7 @@
//!
//! 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();
//! 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
V: Visitor<'de>,
{
let [variant, args] = self.0.list_of_first()?;
let [variant, args] = self.0.seq_of_first()?;
if variant.string()? == name {
visitor.visit_newtype_struct(&mut Deserializer(args))
} else {
@ -244,14 +244,14 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 'a> {
where
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>
where
V: Visitor<'de>,
{
visitor.visit_seq(&mut Seq(&self.0.list()))
visitor.visit_seq(&mut Seq(&self.0.seq()))
}
fn deserialize_tuple_struct<V>(
@ -263,9 +263,9 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 'a> {
where
V: Visitor<'de>,
{
let [variant, args] = self.0.list_of_first()?;
let [variant, args] = self.0.seq_of_first()?;
if variant.string()? == name {
visitor.visit_seq(&mut Seq(&args.list()))
visitor.visit_seq(&mut Seq(&args.seq()))
} else {
Err(Error::custom(format!(
"Expected {}, got: `{}`",
@ -291,7 +291,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 'a> {
where
V: Visitor<'de>,
{
let [variant, data] = self.0.list_of()?;
let [variant, data] = self.0.seq_of()?;
if variant.string()? != name {
return Err(Error::custom(format!(
"Expected {}, got: `{}`",
@ -414,7 +414,7 @@ impl<'de, 'a> EnumAccess<'de> for Enum<'de, 'a> {
where
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())))?;
Ok((variant, self))
}
@ -424,7 +424,7 @@ impl<'de, 'a> VariantAccess<'de> for Enum<'de, 'a> {
type Error = Error;
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"))
} else {
Ok(())
@ -435,7 +435,7 @@ impl<'de, 'a> VariantAccess<'de> for Enum<'de, 'a> {
where
T: DeserializeSeed<'de>,
{
let [_, rest] = self.0.list_of_first()?;
let [_, rest] = self.0.seq_of_first()?;
seed.deserialize(&mut Deserializer(rest))
}
@ -443,15 +443,15 @@ impl<'de, 'a> VariantAccess<'de> for Enum<'de, 'a> {
where
V: Visitor<'de>,
{
let [_, rest] = self.0.list_of_first()?;
visitor.visit_seq(&mut Seq(&rest.list()))
let [_, rest] = self.0.seq_of_first()?;
visitor.visit_seq(&mut Seq(&rest.seq()))
}
fn struct_variant<V>(self, _fields: &'static [&'static str], visitor: V) -> Result<V::Value>
where
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)?)
}
}

View file

@ -128,7 +128,7 @@ impl<'a> ser::Serializer for &'a mut Serializer {
where
T: ?Sized + Serialize,
{
Ok(list_flatten([string(name)?, value.serialize(self)?])?)
Ok(seq_flatten([string(name)?, value.serialize(self)?])?)
}
fn serialize_newtype_variant<T>(
@ -141,7 +141,7 @@ impl<'a> ser::Serializer for &'a mut Serializer {
where
T: ?Sized + Serialize,
{
Ok(list_flatten([
Ok(seq_flatten([
string_owned(format!("{}.{}", name, variant))?,
value.serialize(self)?,
])?)
@ -226,7 +226,7 @@ impl ser::SerializeSeq for SeqSerializer {
}
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> {
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> {
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> {
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>> {
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>> {
Ok(list([
Ok(seq([
string_owned(format!("{}.{}", self.name, self.variant))?,
dict(self.fields.into_iter())?,
])?)