nettext/src/dec/decode.rs

327 lines
10 KiB
Rust

use std::collections::HashMap;
use nom::{
branch::alt,
bytes::complete::{tag, take_while, take_while1},
combinator::{map, opt},
multi::{separated_list0, separated_list1},
IResult, InputLength,
};
use crate::dec::{AnyTerm, DecodeError, NonSeqTerm, Term};
use crate::{
is_string_char, is_whitespace, DICT_ASSIGN, DICT_CLOSE, DICT_DELIM, DICT_OPEN, LIST_CLOSE,
LIST_DELIM, LIST_OPEN,
};
// ----
/// Decodes a nettext string into the term it represents.
pub fn decode(input: &[u8]) -> std::result::Result<Term<'_, '_>, DecodeError<'_>> {
let (rest, term) = decode_term(input)?;
let (end, _) = take_while(is_whitespace)(rest)?;
if !end.is_empty() {
return Err(DecodeError::Garbage(end));
}
Ok(Term(term))
}
fn decode_term(input: &[u8]) -> IResult<&'_ [u8], AnyTerm<'_, '_>> {
let (start, _) = take_while(is_whitespace)(input)?;
let (rest, seq) = separated_list1(take_while1(is_whitespace), decode_nonseq_term)(start)?;
if seq.len() == 1 {
Ok((rest, seq.into_iter().next().unwrap().into()))
} else {
let raw_len = start.input_len() - rest.input_len();
let seq_raw = &start[..raw_len];
Ok((rest, AnyTerm::Seq(seq_raw, seq)))
}
}
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))
}
fn decode_str(input: &[u8]) -> IResult<&'_ [u8], &'_ [u8]> {
let (rest, data) = take_while1(is_string_char)(input)?;
Ok((rest, data))
}
type DictType<'a> = (&'a [u8], HashMap<&'a [u8], AnyTerm<'a, 'a>>);
fn decode_dict(dict_begin: &[u8]) -> IResult<&'_ [u8], DictType<'_>> {
let (d, _) = tag(&[DICT_OPEN][..])(dict_begin)?;
let (d, items) = separated_list0(dict_separator, decode_dict_item)(d)?;
let (d, _) = opt(dict_separator)(d)?;
let (d, _) = take_while(is_whitespace)(d)?;
let (dict_end, _) = tag(&[DICT_CLOSE][..])(d)?;
let dict = items.into_iter().collect::<HashMap<_, _>>();
let raw_len = dict_begin.input_len() - dict_end.input_len();
let dict_raw = &dict_begin[..raw_len];
Ok((dict_end, (dict_raw, dict)))
}
fn dict_separator(d: &[u8]) -> IResult<&'_ [u8], ()> {
let (d, _) = take_while(is_whitespace)(d)?;
let (d, _) = tag(&[DICT_DELIM][..])(d)?;
Ok((d, ()))
}
fn decode_dict_item(d: &[u8]) -> IResult<&'_ [u8], (&'_ [u8], AnyTerm<'_, '_>)> {
let (d, _) = take_while(is_whitespace)(d)?;
let (d, key) = decode_str(d)?;
let (d, _) = take_while(is_whitespace)(d)?;
let (d, _) = tag(&[DICT_ASSIGN][..])(d)?;
let (d, value) = decode_term(d)?;
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)]
mod tests {
use super::*;
#[test]
fn simple_str() {
let bytes = b" plop ";
assert_eq!(decode(bytes), Ok(AnyTerm::Str(b"plop").into()));
}
#[test]
fn seq_of_str_str() {
let bytes = b" plop plap plip ploup ";
assert_eq!(
decode(bytes),
Ok(AnyTerm::Seq(
b"plop plap plip ploup",
vec![
NonSeqTerm::Str(b"plop"),
NonSeqTerm::Str(b"plap"),
NonSeqTerm::Str(b"plip"),
NonSeqTerm::Str(b"ploup"),
]
)
.into())
);
}
#[test]
fn simple_dict() {
let bytes = b" { aze = hello, by = bojzkz pipo, ccde = ke } ";
assert_eq!(
decode(bytes),
Ok(AnyTerm::Dict(
b"{ aze = hello, by = bojzkz pipo, ccde = ke }",
[
(&b"aze"[..], AnyTerm::Str(b"hello")),
(
&b"by"[..],
AnyTerm::Seq(
b"bojzkz pipo",
vec![NonSeqTerm::Str(b"bojzkz"), NonSeqTerm::Str(b"pipo")]
)
),
(&b"ccde"[..], AnyTerm::Str(b"ke")),
]
.into_iter()
.collect()
)
.into())
);
}
#[test]
fn simple_dict_2() {
let bytes = b" { aze = hello, by = bojzkz pipo , ccde = ke , } ";
assert_eq!(
decode(bytes),
Ok(AnyTerm::Dict(
b"{ aze = hello, by = bojzkz pipo , ccde = ke , }",
[
(&b"aze"[..], AnyTerm::Str(b"hello")),
(
&b"by"[..],
AnyTerm::Seq(
b"bojzkz pipo",
vec![NonSeqTerm::Str(b"bojzkz"), NonSeqTerm::Str(b"pipo")]
)
),
(&b"ccde"[..], AnyTerm::Str(b"ke")),
]
.into_iter()
.collect()
)
.into())
);
}
#[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";
assert_eq!(
decode(bytes),
Ok(AnyTerm::Seq(
b"HEAD alexpubkey",
vec![NonSeqTerm::Str(b"HEAD"), NonSeqTerm::Str(b"alexpubkey")]
)
.into()),
);
}
#[test]
fn real_world_2() {
let bytes = b"STANCE sthash stsign { author = alexpubkey, height = 12, parent = parenthash, data = MESSAGE { text = hello } }";
assert_eq!(
decode(bytes),
Ok(AnyTerm::Seq(
&bytes[..],
vec![
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::Seq(
b"MESSAGE { text = hello }",
vec![
NonSeqTerm::Str(b"MESSAGE"),
NonSeqTerm::Dict(
b"{ text = hello }",
[
(&b"text"[..], AnyTerm::Str(b"hello")),
]
.into_iter()
.collect()
)
]
))
].into_iter().collect()
),
]).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())
);
}
}