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, 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::>(); 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>); 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()) ); } }