Adds support for parsing multiple netencode values from stdin. This is overly complicated for my tastes, but I don’t see a better way of writing this logic that does not read all of stdin before starting to parse the first value. A kingdom for a conduit. Change-Id: Ia4f849d4096c43e887756b756d2a85d7f9cd380a Reviewed-on: https://cl.tvl.fyi/c/depot/+/6631 Autosubmit: Profpatsch <mail@profpatsch.de> Reviewed-by: Profpatsch <mail@profpatsch.de> Tested-by: BuildkiteCI
		
			
				
	
	
		
			969 lines
		
	
	
	
		
			29 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			969 lines
		
	
	
	
		
			29 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| extern crate exec_helpers;
 | ||
| extern crate nom;
 | ||
| 
 | ||
| use std::collections::HashMap;
 | ||
| use std::fmt::{Debug, Display};
 | ||
| use std::io::{Read, Write};
 | ||
| 
 | ||
| #[derive(Debug, PartialEq, Eq, Clone)]
 | ||
| pub enum T {
 | ||
|     // Unit
 | ||
|     Unit,
 | ||
|     // Boolean
 | ||
|     N1(bool),
 | ||
|     // Naturals
 | ||
|     N3(u8),
 | ||
|     N6(u64),
 | ||
|     N7(u128),
 | ||
|     // Integers
 | ||
|     I3(i8),
 | ||
|     I6(i64),
 | ||
|     I7(i128),
 | ||
|     // Text
 | ||
|     // TODO: make into &str
 | ||
|     Text(String),
 | ||
|     // TODO: rename to Bytes
 | ||
|     Binary(Vec<u8>),
 | ||
|     // Tags
 | ||
|     // TODO: make into &str
 | ||
|     // TODO: rename to Tag
 | ||
|     Sum(Tag<String, T>),
 | ||
|     // TODO: make into &str
 | ||
|     Record(HashMap<String, T>),
 | ||
|     List(Vec<T>),
 | ||
| }
 | ||
| 
 | ||
| impl T {
 | ||
|     pub fn to_u<'a>(&'a self) -> U<'a> {
 | ||
|         match self {
 | ||
|             T::Unit => U::Unit,
 | ||
|             T::N1(b) => U::N1(*b),
 | ||
|             T::N3(u) => U::N3(*u),
 | ||
|             T::N6(u) => U::N6(*u),
 | ||
|             T::N7(u) => U::N7(*u),
 | ||
|             T::I3(i) => U::I3(*i),
 | ||
|             T::I6(i) => U::I6(*i),
 | ||
|             T::I7(i) => U::I7(*i),
 | ||
|             T::Text(t) => U::Text(t.as_str()),
 | ||
|             T::Binary(v) => U::Binary(v),
 | ||
|             T::Sum(Tag { tag, val }) => U::Sum(Tag {
 | ||
|                 tag: tag.as_str(),
 | ||
|                 val: Box::new(val.to_u()),
 | ||
|             }),
 | ||
|             T::Record(map) => U::Record(map.iter().map(|(k, v)| (k.as_str(), v.to_u())).collect()),
 | ||
|             T::List(l) => U::List(l.iter().map(|v| v.to_u()).collect::<Vec<U<'a>>>()),
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     pub fn encode<'a>(&'a self) -> Vec<u8> {
 | ||
|         match self {
 | ||
|             // TODO: don’t go via U, inefficient
 | ||
|             o => o.to_u().encode(),
 | ||
|         }
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| #[derive(Debug, PartialEq, Eq, Clone)]
 | ||
| pub enum U<'a> {
 | ||
|     Unit,
 | ||
|     // Boolean
 | ||
|     N1(bool),
 | ||
|     // Naturals
 | ||
|     N3(u8),
 | ||
|     N6(u64),
 | ||
|     N7(u128),
 | ||
|     // Integers
 | ||
|     I3(i8),
 | ||
|     I6(i64),
 | ||
|     I7(i128),
 | ||
|     // Text
 | ||
|     Text(&'a str),
 | ||
|     Binary(&'a [u8]),
 | ||
|     // TODO: the U-recursion we do here means we can’t be breadth-lazy anymore
 | ||
|     // like we originally planned; maybe we want to go `U<'a>` → `&'a [u8]` again?
 | ||
|     // Tags
 | ||
|     // TODO: rename to Tag
 | ||
|     Sum(Tag<&'a str, U<'a>>),
 | ||
|     Record(HashMap<&'a str, U<'a>>),
 | ||
|     List(Vec<U<'a>>),
 | ||
| }
 | ||
| 
 | ||
| impl<'a> U<'a> {
 | ||
|     pub fn encode(&self) -> Vec<u8> {
 | ||
|         let mut c = std::io::Cursor::new(vec![]);
 | ||
|         encode(&mut c, self);
 | ||
|         c.into_inner()
 | ||
|     }
 | ||
| 
 | ||
|     pub fn to_t(&self) -> T {
 | ||
|         match self {
 | ||
|             U::Unit => T::Unit,
 | ||
|             U::N1(b) => T::N1(*b),
 | ||
|             U::N3(u) => T::N3(*u),
 | ||
|             U::N6(u) => T::N6(*u),
 | ||
|             U::N7(u) => T::N7(*u),
 | ||
|             U::I3(i) => T::I3(*i),
 | ||
|             U::I6(i) => T::I6(*i),
 | ||
|             U::I7(i) => T::I7(*i),
 | ||
|             U::Text(t) => T::Text((*t).to_owned()),
 | ||
|             U::Binary(v) => T::Binary((*v).to_owned()),
 | ||
|             U::Sum(Tag { tag, val }) => T::Sum(Tag {
 | ||
|                 tag: (*tag).to_owned(),
 | ||
|                 val: Box::new(val.to_t()),
 | ||
|             }),
 | ||
|             U::Record(map) => T::Record(
 | ||
|                 map.iter()
 | ||
|                     .map(|(k, v)| ((*k).to_owned(), v.to_t()))
 | ||
|                     .collect::<HashMap<String, T>>(),
 | ||
|             ),
 | ||
|             U::List(l) => T::List(l.iter().map(|v| v.to_t()).collect::<Vec<T>>()),
 | ||
|         }
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| #[derive(Debug, PartialEq, Eq, Clone)]
 | ||
| pub struct Tag<S, A> {
 | ||
|     // TODO: make into &str
 | ||
|     pub tag: S,
 | ||
|     pub val: Box<A>,
 | ||
| }
 | ||
| 
 | ||
| impl<S, A> Tag<S, A> {
 | ||
|     fn map<F, B>(self, f: F) -> Tag<S, B>
 | ||
|     where
 | ||
|         F: Fn(A) -> B,
 | ||
|     {
 | ||
|         Tag {
 | ||
|             tag: self.tag,
 | ||
|             val: Box::new(f(*self.val)),
 | ||
|         }
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| fn encode_tag<W: Write>(w: &mut W, tag: &str, val: &U) -> std::io::Result<()> {
 | ||
|     write!(w, "<{}:{}|", tag.len(), tag)?;
 | ||
|     encode(w, val)?;
 | ||
|     Ok(())
 | ||
| }
 | ||
| 
 | ||
| pub fn encode<W: Write>(w: &mut W, u: &U) -> std::io::Result<()> {
 | ||
|     match u {
 | ||
|         U::Unit => write!(w, "u,"),
 | ||
|         U::N1(b) => {
 | ||
|             if *b {
 | ||
|                 write!(w, "n1:1,")
 | ||
|             } else {
 | ||
|                 write!(w, "n1:0,")
 | ||
|             }
 | ||
|         }
 | ||
|         U::N3(n) => write!(w, "n3:{},", n),
 | ||
|         U::N6(n) => write!(w, "n6:{},", n),
 | ||
|         U::N7(n) => write!(w, "n7:{},", n),
 | ||
|         U::I3(i) => write!(w, "i3:{},", i),
 | ||
|         U::I6(i) => write!(w, "i6:{},", i),
 | ||
|         U::I7(i) => write!(w, "i7:{},", i),
 | ||
|         U::Text(s) => {
 | ||
|             write!(w, "t{}:", s.len());
 | ||
|             w.write_all(s.as_bytes());
 | ||
|             write!(w, ",")
 | ||
|         }
 | ||
|         U::Binary(s) => {
 | ||
|             write!(w, "b{}:", s.len());
 | ||
|             w.write_all(&s);
 | ||
|             write!(w, ",")
 | ||
|         }
 | ||
|         U::Sum(Tag { tag, val }) => encode_tag(w, tag, val),
 | ||
|         U::Record(m) => {
 | ||
|             let mut c = std::io::Cursor::new(vec![]);
 | ||
|             for (k, v) in m {
 | ||
|                 encode_tag(&mut c, k, v)?;
 | ||
|             }
 | ||
|             write!(w, "{{{}:", c.get_ref().len())?;
 | ||
|             w.write_all(c.get_ref())?;
 | ||
|             write!(w, "}}")
 | ||
|         }
 | ||
|         U::List(l) => {
 | ||
|             let mut c = std::io::Cursor::new(vec![]);
 | ||
|             for u in l {
 | ||
|                 encode(&mut c, u)?;
 | ||
|             }
 | ||
|             write!(w, "[{}:", c.get_ref().len())?;
 | ||
|             w.write_all(c.get_ref())?;
 | ||
|             write!(w, "]")
 | ||
|         }
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| pub fn text(s: String) -> T {
 | ||
|     T::Text(s)
 | ||
| }
 | ||
| 
 | ||
| pub fn t_from_stdin_or_die_user_error<'a>(prog_name: &'_ str) -> T {
 | ||
|     match t_from_stdin_or_die_user_error_with_rest(prog_name, &vec![]) {
 | ||
|         None => exec_helpers::die_user_error(prog_name, "stdin was empty"),
 | ||
|         Some((rest, t)) => {
 | ||
|             if rest.is_empty() {
 | ||
|                 t
 | ||
|             } else {
 | ||
|                 exec_helpers::die_user_error(
 | ||
|                     prog_name,
 | ||
|                     format!(
 | ||
|                         "stdin contained some soup after netencode value: {:?}",
 | ||
|                         String::from_utf8_lossy(&rest)
 | ||
|                     ),
 | ||
|                 )
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /// Read a netencode value from stdin incrementally, return bytes that could not be read.
 | ||
| /// Nothing if there was nothing to read from stdin & no initial_bytes were provided.
 | ||
| /// These can be passed back as `initial_bytes` if more values should be read.
 | ||
| pub fn t_from_stdin_or_die_user_error_with_rest<'a>(
 | ||
|     prog_name: &'_ str,
 | ||
|     initial_bytes: &[u8],
 | ||
| ) -> Option<(Vec<u8>, T)> {
 | ||
|     let mut chonker = Chunkyboi::new(std::io::stdin().lock(), 4096);
 | ||
|     // The vec to pass to the parser on each step
 | ||
|     let mut parser_vec: Vec<u8> = initial_bytes.to_vec();
 | ||
|     // whether stdin was already empty
 | ||
|     let mut was_empty: bool = false;
 | ||
|     loop {
 | ||
|         match chonker.next() {
 | ||
|             None => {
 | ||
|                 if parser_vec.is_empty() {
 | ||
|                     return None;
 | ||
|                 } else {
 | ||
|                     was_empty = true
 | ||
|                 }
 | ||
|             }
 | ||
|             Some(Err(err)) => exec_helpers::die_temporary(
 | ||
|                 prog_name,
 | ||
|                 &format!("could not read from stdin: {:?}", err),
 | ||
|             ),
 | ||
|             Some(Ok(mut new_bytes)) => parser_vec.append(&mut new_bytes),
 | ||
|         }
 | ||
| 
 | ||
|         match parse::t_t(&parser_vec) {
 | ||
|             Ok((rest, t)) => return Some((rest.to_owned(), t)),
 | ||
|             Err(nom::Err::Incomplete(Needed)) => {
 | ||
|                 if was_empty {
 | ||
|                     exec_helpers::die_user_error(
 | ||
|                         prog_name,
 | ||
|                         &format!(
 | ||
|                             "unable to parse netencode from stdin, input incomplete: {:?}",
 | ||
|                             parser_vec
 | ||
|                         ),
 | ||
|                     );
 | ||
|                 }
 | ||
|                 // read more from stdin and try parsing again
 | ||
|                 continue;
 | ||
|             }
 | ||
|             Err(err) => exec_helpers::die_user_error(
 | ||
|                 prog_name,
 | ||
|                 &format!("unable to parse netencode from stdin: {:?}", err),
 | ||
|             ),
 | ||
|         }
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| // iter helper
 | ||
| // TODO: put into its own module
 | ||
| struct Chunkyboi<T> {
 | ||
|     inner: T,
 | ||
|     buf: Vec<u8>,
 | ||
| }
 | ||
| 
 | ||
| impl<R: Read> Chunkyboi<R> {
 | ||
|     fn new(inner: R, chunksize: usize) -> Self {
 | ||
|         let buf = vec![0; chunksize];
 | ||
|         Chunkyboi { inner, buf }
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| impl<R: Read> Iterator for Chunkyboi<R> {
 | ||
|     type Item = std::io::Result<Vec<u8>>;
 | ||
| 
 | ||
|     fn next(&mut self) -> Option<std::io::Result<Vec<u8>>> {
 | ||
|         match self.inner.read(&mut self.buf) {
 | ||
|             Ok(0) => None,
 | ||
|             Ok(read) => {
 | ||
|                 // clone a new buffer so we can reuse the internal one
 | ||
|                 Some(Ok(self.buf[..read].to_owned()))
 | ||
|             }
 | ||
|             Err(err) => Some(Err(err)),
 | ||
|         }
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| pub mod parse {
 | ||
|     use super::{Tag, T, U};
 | ||
| 
 | ||
|     use std::collections::HashMap;
 | ||
|     use std::ops::Neg;
 | ||
|     use std::str::FromStr;
 | ||
| 
 | ||
|     use nom::branch::alt;
 | ||
|     use nom::bytes::streaming::{tag, take};
 | ||
|     use nom::character::streaming::{char, digit1};
 | ||
|     use nom::combinator::{flat_map, map, map_parser, map_res, opt};
 | ||
|     use nom::error::{context, ErrorKind, ParseError};
 | ||
|     use nom::sequence::tuple;
 | ||
|     use nom::IResult;
 | ||
| 
 | ||
|     fn unit_t(s: &[u8]) -> IResult<&[u8], ()> {
 | ||
|         let (s, _) = context("unit", tag("u,"))(s)?;
 | ||
|         Ok((s, ()))
 | ||
|     }
 | ||
| 
 | ||
|     fn usize_t(s: &[u8]) -> IResult<&[u8], usize> {
 | ||
|         context(
 | ||
|             "usize",
 | ||
|             map_res(map_res(digit1, |n| std::str::from_utf8(n)), |s| {
 | ||
|                 s.parse::<usize>()
 | ||
|             }),
 | ||
|         )(s)
 | ||
|     }
 | ||
| 
 | ||
|     fn sized(begin: char, end: char) -> impl Fn(&[u8]) -> IResult<&[u8], &[u8]> {
 | ||
|         move |s: &[u8]| {
 | ||
|             // This is the point where we check the descriminator;
 | ||
|             // if the beginning char does not match, we can immediately return.
 | ||
|             let (s, _) = char(begin)(s)?;
 | ||
|             let (s, (len, _)) = tuple((usize_t, char(':')))(s)?;
 | ||
|             let (s, (res, _)) = tuple((take(len), char(end)))(s)?;
 | ||
|             Ok((s, res))
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     fn uint_t<'a, I: FromStr + 'a>(t: &'static str) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], I> {
 | ||
|         move |s: &'a [u8]| {
 | ||
|             let (s, (_, _, int, _)) = tuple((
 | ||
|                 tag(t.as_bytes()),
 | ||
|                 char(':'),
 | ||
|                 map_res(map_res(digit1, |n: &[u8]| std::str::from_utf8(n)), |s| {
 | ||
|                     s.parse::<I>()
 | ||
|                 }),
 | ||
|                 char(','),
 | ||
|             ))(s)?;
 | ||
|             Ok((s, int))
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     fn bool_t<'a>() -> impl Fn(&'a [u8]) -> IResult<&'a [u8], bool> {
 | ||
|         context(
 | ||
|             "bool",
 | ||
|             alt((map(tag("n1:0,"), |_| false), map(tag("n1:1,"), |_| true))),
 | ||
|         )
 | ||
|     }
 | ||
| 
 | ||
|     fn int_t<'a, I: FromStr + Neg<Output = I>>(
 | ||
|         t: &'static str,
 | ||
|     ) -> impl Fn(&'a [u8]) -> IResult<&[u8], I> {
 | ||
|         context(t, move |s: &'a [u8]| {
 | ||
|             let (s, (_, _, neg, int, _)) = tuple((
 | ||
|                 tag(t.as_bytes()),
 | ||
|                 char(':'),
 | ||
|                 opt(char('-')),
 | ||
|                 map_res(map_res(digit1, |n: &[u8]| std::str::from_utf8(n)), |s| {
 | ||
|                     s.parse::<I>()
 | ||
|                 }),
 | ||
|                 char(','),
 | ||
|             ))(s)?;
 | ||
|             let res = match neg {
 | ||
|                 Some(_) => -int,
 | ||
|                 None => int,
 | ||
|             };
 | ||
|             Ok((s, res))
 | ||
|         })
 | ||
|     }
 | ||
| 
 | ||
|     fn tag_t(s: &[u8]) -> IResult<&[u8], Tag<String, T>> {
 | ||
|         // recurses into the main parser
 | ||
|         map(tag_g(t_t), |Tag { tag, val }| Tag {
 | ||
|             tag: tag.to_string(),
 | ||
|             val,
 | ||
|         })(s)
 | ||
|     }
 | ||
| 
 | ||
|     fn tag_g<'a, P, O>(inner: P) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], Tag<&'a str, O>>
 | ||
|     where
 | ||
|         P: Fn(&'a [u8]) -> IResult<&'a [u8], O>,
 | ||
|     {
 | ||
|         move |s: &[u8]| {
 | ||
|             let (s, tag) = sized('<', '|')(s)?;
 | ||
|             let (s, val) = inner(s)?;
 | ||
|             Ok((
 | ||
|                 s,
 | ||
|                 Tag {
 | ||
|                     tag: std::str::from_utf8(tag)
 | ||
|                         .map_err(|_| nom::Err::Failure((s, ErrorKind::Char)))?,
 | ||
|                     val: Box::new(val),
 | ||
|                 },
 | ||
|             ))
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     /// parse text scalar (`t5:hello,`)
 | ||
|     fn text(s: &[u8]) -> IResult<&[u8], T> {
 | ||
|         let (s, res) = text_g(s)?;
 | ||
|         Ok((s, T::Text(res.to_string())))
 | ||
|     }
 | ||
| 
 | ||
|     fn text_g(s: &[u8]) -> IResult<&[u8], &str> {
 | ||
|         let (s, res) = sized('t', ',')(s)?;
 | ||
|         Ok((
 | ||
|             s,
 | ||
|             std::str::from_utf8(res).map_err(|_| nom::Err::Failure((s, ErrorKind::Char)))?,
 | ||
|         ))
 | ||
|     }
 | ||
| 
 | ||
|     fn binary<'a>() -> impl Fn(&'a [u8]) -> IResult<&'a [u8], T> {
 | ||
|         map(binary_g(), |b| T::Binary(b.to_owned()))
 | ||
|     }
 | ||
| 
 | ||
|     fn binary_g() -> impl Fn(&[u8]) -> IResult<&[u8], &[u8]> {
 | ||
|         sized('b', ',')
 | ||
|     }
 | ||
| 
 | ||
|     fn list_t(s: &[u8]) -> IResult<&[u8], Vec<T>> {
 | ||
|         list_g(t_t)(s)
 | ||
|     }
 | ||
| 
 | ||
|     /// Wrap the inner parser of an `many0`/`fold_many0`, so that the parser
 | ||
|     /// is not called when the `s` is empty already, preventing it from
 | ||
|     /// returning `Incomplete` on streaming parsing.
 | ||
|     fn inner_no_empty_string<'a, P, O>(inner: P) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], O>
 | ||
|     where
 | ||
|         O: Clone,
 | ||
|         P: Fn(&'a [u8]) -> IResult<&'a [u8], O>,
 | ||
|     {
 | ||
|         move |s: &'a [u8]| {
 | ||
|             if s.is_empty() {
 | ||
|                 // This is a bit hacky, `many0` considers the inside done
 | ||
|                 // when a parser returns `Err::Error`, ignoring the actual error content
 | ||
|                 Err(nom::Err::Error((s, nom::error::ErrorKind::Many0)))
 | ||
|             } else {
 | ||
|                 inner(s)
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     fn list_g<'a, P, O>(inner: P) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], Vec<O>>
 | ||
|     where
 | ||
|         O: Clone,
 | ||
|         P: Fn(&'a [u8]) -> IResult<&'a [u8], O>,
 | ||
|     {
 | ||
|         map_parser(
 | ||
|             sized('[', ']'),
 | ||
|             nom::multi::many0(inner_no_empty_string(inner)),
 | ||
|         )
 | ||
|     }
 | ||
| 
 | ||
|     fn record_t<'a>(s: &'a [u8]) -> IResult<&'a [u8], HashMap<String, T>> {
 | ||
|         let (s, r) = record_g(t_t)(s)?;
 | ||
|         Ok((
 | ||
|             s,
 | ||
|             r.into_iter()
 | ||
|                 .map(|(k, v)| (k.to_string(), v))
 | ||
|                 .collect::<HashMap<_, _>>(),
 | ||
|         ))
 | ||
|     }
 | ||
| 
 | ||
|     fn record_g<'a, P, O>(inner: P) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], HashMap<&'a str, O>>
 | ||
|     where
 | ||
|         O: Clone,
 | ||
|         P: Fn(&'a [u8]) -> IResult<&'a [u8], O>,
 | ||
|     {
 | ||
|         move |s: &'a [u8]| {
 | ||
|             let (s, map) = map_parser(
 | ||
|                 sized('{', '}'),
 | ||
|                 nom::multi::fold_many0(
 | ||
|                     inner_no_empty_string(tag_g(&inner)),
 | ||
|                     HashMap::new(),
 | ||
|                     |mut acc: HashMap<_, _>, Tag { tag, mut val }| {
 | ||
|                         // ignore earlier tags with the same name
 | ||
|                         // according to netencode spec
 | ||
|                         let _ = acc.insert(tag, *val);
 | ||
|                         acc
 | ||
|                     },
 | ||
|                 ),
 | ||
|             )(s)?;
 | ||
|             if map.is_empty() {
 | ||
|                 // records must not be empty, according to the spec
 | ||
|                 Err(nom::Err::Failure((s, nom::error::ErrorKind::Many1)))
 | ||
|             } else {
 | ||
|                 Ok((s, map))
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     pub fn u_u(s: &[u8]) -> IResult<&[u8], U> {
 | ||
|         alt((
 | ||
|             map(text_g, U::Text),
 | ||
|             map(binary_g(), U::Binary),
 | ||
|             map(unit_t, |()| U::Unit),
 | ||
|             map(tag_g(u_u), |t| U::Sum(t)),
 | ||
|             map(list_g(u_u), U::List),
 | ||
|             map(record_g(u_u), U::Record),
 | ||
|             map(bool_t(), |u| U::N1(u)),
 | ||
|             map(uint_t("n3"), |u| U::N3(u)),
 | ||
|             map(uint_t("n6"), |u| U::N6(u)),
 | ||
|             map(uint_t("n7"), |u| U::N7(u)),
 | ||
|             map(int_t("i3"), |u| U::I3(u)),
 | ||
|             map(int_t("i6"), |u| U::I6(u)),
 | ||
|             map(int_t("i7"), |u| U::I7(u)),
 | ||
|             // less common
 | ||
|             map(uint_t("n2"), |u| U::N3(u)),
 | ||
|             map(uint_t("n4"), |u| U::N6(u)),
 | ||
|             map(uint_t("n5"), |u| U::N6(u)),
 | ||
|             map(int_t("i1"), |u| U::I3(u)),
 | ||
|             map(int_t("i2"), |u| U::I3(u)),
 | ||
|             map(int_t("i4"), |u| U::I6(u)),
 | ||
|             map(int_t("i5"), |u| U::I6(u)),
 | ||
|             // TODO: 8, 9 not supported
 | ||
|         ))(s)
 | ||
|     }
 | ||
| 
 | ||
|     pub fn t_t(s: &[u8]) -> IResult<&[u8], T> {
 | ||
|         alt((
 | ||
|             text,
 | ||
|             binary(),
 | ||
|             map(unit_t, |_| T::Unit),
 | ||
|             map(tag_t, |t| T::Sum(t)),
 | ||
|             map(list_t, |l| T::List(l)),
 | ||
|             map(record_t, |p| T::Record(p)),
 | ||
|             map(bool_t(), |u| T::N1(u)),
 | ||
|             // 8, 64 and 128 bit
 | ||
|             map(uint_t("n3"), |u| T::N3(u)),
 | ||
|             map(uint_t("n6"), |u| T::N6(u)),
 | ||
|             map(uint_t("n7"), |u| T::N7(u)),
 | ||
|             map(int_t("i3"), |u| T::I3(u)),
 | ||
|             map(int_t("i6"), |u| T::I6(u)),
 | ||
|             map(int_t("i7"), |u| T::I7(u)),
 | ||
|             // less common
 | ||
|             map(uint_t("n2"), |u| T::N3(u)),
 | ||
|             map(uint_t("n4"), |u| T::N6(u)),
 | ||
|             map(uint_t("n5"), |u| T::N6(u)),
 | ||
|             map(int_t("i1"), |u| T::I3(u)),
 | ||
|             map(int_t("i2"), |u| T::I3(u)),
 | ||
|             map(int_t("i4"), |u| T::I6(u)),
 | ||
|             map(int_t("i5"), |u| T::I6(u)),
 | ||
|             // TODO: 8, 9 not supported
 | ||
|         ))(s)
 | ||
|     }
 | ||
| 
 | ||
|     #[cfg(test)]
 | ||
|     mod tests {
 | ||
|         use super::*;
 | ||
| 
 | ||
|         #[test]
 | ||
|         fn test_parse_unit_t() {
 | ||
|             assert_eq!(unit_t("u,".as_bytes()), Ok(("".as_bytes(), ())));
 | ||
|         }
 | ||
| 
 | ||
|         #[test]
 | ||
|         fn test_parse_bool_t() {
 | ||
|             assert_eq!(bool_t()("n1:0,".as_bytes()), Ok(("".as_bytes(), false)));
 | ||
|             assert_eq!(bool_t()("n1:1,".as_bytes()), Ok(("".as_bytes(), true)));
 | ||
|         }
 | ||
| 
 | ||
|         #[test]
 | ||
|         fn test_parse_usize_t() {
 | ||
|             assert_eq!(usize_t("32foo".as_bytes()), Ok(("foo".as_bytes(), 32)));
 | ||
|         }
 | ||
| 
 | ||
|         #[test]
 | ||
|         fn test_parse_int_t() {
 | ||
|             assert_eq!(
 | ||
|                 uint_t::<u8>("n3")("n3:42,abc".as_bytes()),
 | ||
|                 Ok(("abc".as_bytes(), 42))
 | ||
|             );
 | ||
|             assert_eq!(
 | ||
|                 uint_t::<u8>("n3")("n3:1024,abc".as_bytes()),
 | ||
|                 Err(nom::Err::Error((
 | ||
|                     "1024,abc".as_bytes(),
 | ||
|                     nom::error::ErrorKind::MapRes
 | ||
|                 )))
 | ||
|             );
 | ||
|             assert_eq!(
 | ||
|                 int_t::<i64>("i6")("i6:-23,abc".as_bytes()),
 | ||
|                 Ok(("abc".as_bytes(), -23))
 | ||
|             );
 | ||
|             assert_eq!(
 | ||
|                 int_t::<i128>("i3")("i3:0,:abc".as_bytes()),
 | ||
|                 Ok((":abc".as_bytes(), 0))
 | ||
|             );
 | ||
|             assert_eq!(
 | ||
|                 uint_t::<u8>("n7")("n7:09,".as_bytes()),
 | ||
|                 Ok(("".as_bytes(), 9))
 | ||
|             );
 | ||
|             // assert_eq!(
 | ||
|             //     length("c"),
 | ||
|             //     Err(nom::Err::Error(("c", nom::error::ErrorKind::Digit)))
 | ||
|             // );
 | ||
|             // assert_eq!(
 | ||
|             //     length(":"),
 | ||
|             //     Err(nom::Err::Error((":", nom::error::ErrorKind::Digit)))
 | ||
|             // );
 | ||
|         }
 | ||
| 
 | ||
|         #[test]
 | ||
|         fn test_parse_text() {
 | ||
|             assert_eq!(
 | ||
|                 text("t5:hello,".as_bytes()),
 | ||
|                 Ok(("".as_bytes(), T::Text("hello".to_owned()))),
 | ||
|                 "{}",
 | ||
|                 r"t5:hello,"
 | ||
|             );
 | ||
|             assert_eq!(
 | ||
|                 text("t4:fo".as_bytes()),
 | ||
|                 // The content of the text should be 4 long
 | ||
|                 Err(nom::Err::Incomplete(nom::Needed::Size(4))),
 | ||
|                 "{}",
 | ||
|                 r"t4:fo,"
 | ||
|             );
 | ||
|             assert_eq!(
 | ||
|                 text("t9:今日は,".as_bytes()),
 | ||
|                 Ok(("".as_bytes(), T::Text("今日は".to_owned()))),
 | ||
|                 "{}",
 | ||
|                 r"t9:今日は,"
 | ||
|             );
 | ||
|         }
 | ||
| 
 | ||
|         #[test]
 | ||
|         fn test_parse_binary() {
 | ||
|             assert_eq!(
 | ||
|                 binary()("b5:hello,".as_bytes()),
 | ||
|                 Ok(("".as_bytes(), T::Binary(Vec::from("hello".to_owned())))),
 | ||
|                 "{}",
 | ||
|                 r"b5:hello,"
 | ||
|             );
 | ||
|             assert_eq!(
 | ||
|                 binary()("b4:fo".as_bytes()),
 | ||
|                 // The content of the byte should be 4 long
 | ||
|                 Err(nom::Err::Incomplete(nom::Needed::Size(4))),
 | ||
|                 "{}",
 | ||
|                 r"b4:fo,"
 | ||
|             );
 | ||
|             assert_eq!(
 | ||
|                 binary()("b4:foob".as_bytes()),
 | ||
|                 // The content is 4 bytes now, but the finishing , is missing
 | ||
|                 Err(nom::Err::Incomplete(nom::Needed::Size(1))),
 | ||
|                 "{}",
 | ||
|                 r"b4:fo,"
 | ||
|             );
 | ||
|             assert_eq!(
 | ||
|                 binary()("b9:今日は,".as_bytes()),
 | ||
|                 Ok(("".as_bytes(), T::Binary(Vec::from("今日は".as_bytes())))),
 | ||
|                 "{}",
 | ||
|                 r"b9:今日は,"
 | ||
|             );
 | ||
|         }
 | ||
| 
 | ||
|         #[test]
 | ||
|         fn test_list() {
 | ||
|             assert_eq!(
 | ||
|                 list_t("[0:]".as_bytes()),
 | ||
|                 Ok(("".as_bytes(), vec![])),
 | ||
|                 "{}",
 | ||
|                 r"[0:]"
 | ||
|             );
 | ||
|             assert_eq!(
 | ||
|                 list_t("[6:u,u,u,]".as_bytes()),
 | ||
|                 Ok(("".as_bytes(), vec![T::Unit, T::Unit, T::Unit,])),
 | ||
|                 "{}",
 | ||
|                 r"[6:u,u,u,]"
 | ||
|             );
 | ||
|             assert_eq!(
 | ||
|                 list_t("[15:u,[7:t3:foo,]u,]".as_bytes()),
 | ||
|                 Ok((
 | ||
|                     "".as_bytes(),
 | ||
|                     vec![T::Unit, T::List(vec![T::Text("foo".to_owned())]), T::Unit,]
 | ||
|                 )),
 | ||
|                 "{}",
 | ||
|                 r"[15:u,[7:t3:foo,]u,]"
 | ||
|             );
 | ||
|         }
 | ||
| 
 | ||
|         #[test]
 | ||
|         fn test_record() {
 | ||
|             assert_eq!(
 | ||
|                 record_t("{21:<1:a|u,<1:b|u,<1:c|u,}".as_bytes()),
 | ||
|                 Ok((
 | ||
|                     "".as_bytes(),
 | ||
|                     vec![
 | ||
|                         ("a".to_owned(), T::Unit),
 | ||
|                         ("b".to_owned(), T::Unit),
 | ||
|                         ("c".to_owned(), T::Unit),
 | ||
|                     ]
 | ||
|                     .into_iter()
 | ||
|                     .collect::<HashMap<String, T>>()
 | ||
|                 )),
 | ||
|                 "{}",
 | ||
|                 r"{21:<1:a|u,<1:b|u,<1:c|u,}"
 | ||
|             );
 | ||
|             // duplicated keys are ignored (first is taken)
 | ||
|             assert_eq!(
 | ||
|                 record_t("{25:<1:a|u,<1:b|u,<1:a|i1:-1,}".as_bytes()),
 | ||
|                 Ok((
 | ||
|                     "".as_bytes(),
 | ||
|                     vec![("a".to_owned(), T::I3(-1)), ("b".to_owned(), T::Unit),]
 | ||
|                         .into_iter()
 | ||
|                         .collect::<HashMap<_, _>>()
 | ||
|                 )),
 | ||
|                 "{}",
 | ||
|                 r"{25:<1:a|u,<1:b|u,<1:a|i1:-1,}"
 | ||
|             );
 | ||
|             // empty records are not allowed
 | ||
|             assert_eq!(
 | ||
|                 record_t("{0:}".as_bytes()),
 | ||
|                 Err(nom::Err::Failure((
 | ||
|                     "".as_bytes(),
 | ||
|                     nom::error::ErrorKind::Many1
 | ||
|                 ))),
 | ||
|                 "{}",
 | ||
|                 r"{0:}"
 | ||
|             );
 | ||
|         }
 | ||
| 
 | ||
|         #[test]
 | ||
|         fn test_parse() {
 | ||
|             assert_eq!(
 | ||
|                 t_t("n3:255,".as_bytes()),
 | ||
|                 Ok(("".as_bytes(), T::N3(255))),
 | ||
|                 "{}",
 | ||
|                 r"n3:255,"
 | ||
|             );
 | ||
|             assert_eq!(
 | ||
|                 t_t("t6:halloo,".as_bytes()),
 | ||
|                 Ok(("".as_bytes(), T::Text("halloo".to_owned()))),
 | ||
|                 "{}",
 | ||
|                 r"t6:halloo,"
 | ||
|             );
 | ||
|             assert_eq!(
 | ||
|                 t_t("<3:foo|t6:halloo,".as_bytes()),
 | ||
|                 Ok((
 | ||
|                     "".as_bytes(),
 | ||
|                     T::Sum(Tag {
 | ||
|                         tag: "foo".to_owned(),
 | ||
|                         val: Box::new(T::Text("halloo".to_owned()))
 | ||
|                     })
 | ||
|                 )),
 | ||
|                 "{}",
 | ||
|                 r"<3:foo|t6:halloo,"
 | ||
|             );
 | ||
|             // { a: Unit
 | ||
|             // , foo: List <A: Unit | B: List i3> }
 | ||
|             assert_eq!(
 | ||
|                 t_t("{52:<1:a|u,<3:foo|[33:<1:A|u,<1:A|n1:1,<1:B|[7:i3:127,]]}".as_bytes()),
 | ||
|                 Ok((
 | ||
|                     "".as_bytes(),
 | ||
|                     T::Record(
 | ||
|                         vec![
 | ||
|                             ("a".to_owned(), T::Unit),
 | ||
|                             (
 | ||
|                                 "foo".to_owned(),
 | ||
|                                 T::List(vec![
 | ||
|                                     T::Sum(Tag {
 | ||
|                                         tag: "A".to_owned(),
 | ||
|                                         val: Box::new(T::Unit)
 | ||
|                                     }),
 | ||
|                                     T::Sum(Tag {
 | ||
|                                         tag: "A".to_owned(),
 | ||
|                                         val: Box::new(T::N1(true))
 | ||
|                                     }),
 | ||
|                                     T::Sum(Tag {
 | ||
|                                         tag: "B".to_owned(),
 | ||
|                                         val: Box::new(T::List(vec![T::I3(127)]))
 | ||
|                                     }),
 | ||
|                                 ])
 | ||
|                             )
 | ||
|                         ]
 | ||
|                         .into_iter()
 | ||
|                         .collect::<HashMap<String, T>>()
 | ||
|                     )
 | ||
|                 )),
 | ||
|                 "{}",
 | ||
|                 r"{52:<1:a|u,<3:foo|[33:<1:A|u,<1:A|n1:1,<1:B|[7:i3:127,]]}"
 | ||
|             );
 | ||
|         }
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| pub mod dec {
 | ||
|     use super::*;
 | ||
|     use std::collections::HashMap;
 | ||
| 
 | ||
|     pub struct DecodeError(pub String);
 | ||
| 
 | ||
|     pub trait Decoder<'a> {
 | ||
|         type A;
 | ||
|         fn dec(&self, u: U<'a>) -> Result<Self::A, DecodeError>;
 | ||
|     }
 | ||
| 
 | ||
|     /// Any netencode, as `T`.
 | ||
|     #[derive(Clone, Copy)]
 | ||
|     pub struct AnyT;
 | ||
|     /// Any netencode, as `U`.
 | ||
|     #[derive(Clone, Copy)]
 | ||
|     pub struct AnyU;
 | ||
| 
 | ||
|     impl<'a> Decoder<'a> for AnyT {
 | ||
|         type A = T;
 | ||
|         fn dec(&self, u: U<'a>) -> Result<Self::A, DecodeError> {
 | ||
|             Ok(u.to_t())
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     impl<'a> Decoder<'a> for AnyU {
 | ||
|         type A = U<'a>;
 | ||
|         fn dec(&self, u: U<'a>) -> Result<Self::A, DecodeError> {
 | ||
|             Ok(u)
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     /// A text
 | ||
|     #[derive(Clone, Copy)]
 | ||
|     pub struct Text;
 | ||
| 
 | ||
|     /// A bytestring
 | ||
|     // TODO: rename to Bytes
 | ||
|     #[derive(Clone, Copy)]
 | ||
|     pub struct Binary;
 | ||
| 
 | ||
|     impl<'a> Decoder<'a> for Text {
 | ||
|         type A = &'a str;
 | ||
|         fn dec(&self, u: U<'a>) -> Result<Self::A, DecodeError> {
 | ||
|             match u {
 | ||
|                 U::Text(t) => Ok(t),
 | ||
|                 other => Err(DecodeError(format!("Cannot decode {:?} into Text", other))),
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     impl<'a> Decoder<'a> for Binary {
 | ||
|         type A = &'a [u8];
 | ||
|         fn dec(&self, u: U<'a>) -> Result<Self::A, DecodeError> {
 | ||
|             match u {
 | ||
|                 U::Binary(b) => Ok(b),
 | ||
|                 other => Err(DecodeError(format!(
 | ||
|                     "Cannot decode {:?} into Binary",
 | ||
|                     other
 | ||
|                 ))),
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     /// Any scalar, converted to bytes.
 | ||
|     #[derive(Clone, Copy)]
 | ||
|     pub struct ScalarAsBytes;
 | ||
| 
 | ||
|     impl<'a> Decoder<'a> for ScalarAsBytes {
 | ||
|         type A = Vec<u8>;
 | ||
|         fn dec(&self, u: U<'a>) -> Result<Self::A, DecodeError> {
 | ||
|             match u {
 | ||
|                 U::N3(u) => Ok(format!("{}", u).into_bytes()),
 | ||
|                 U::N6(u) => Ok(format!("{}", u).into_bytes()),
 | ||
|                 U::N7(u) => Ok(format!("{}", u).into_bytes()),
 | ||
|                 U::I3(i) => Ok(format!("{}", i).into_bytes()),
 | ||
|                 U::I6(i) => Ok(format!("{}", i).into_bytes()),
 | ||
|                 U::I7(i) => Ok(format!("{}", i).into_bytes()),
 | ||
|                 U::Text(t) => Ok(t.as_bytes().to_owned()),
 | ||
|                 U::Binary(b) => Ok(b.to_owned()),
 | ||
|                 o => Err(DecodeError(format!("Cannot decode {:?} into scalar", o))),
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     /// A map of Ts (TODO: rename to map)
 | ||
|     #[derive(Clone, Copy)]
 | ||
|     pub struct Record<T>(pub T);
 | ||
| 
 | ||
|     impl<'a, Inner> Decoder<'a> for Record<Inner>
 | ||
|     where
 | ||
|         Inner: Decoder<'a>,
 | ||
|     {
 | ||
|         type A = HashMap<&'a str, Inner::A>;
 | ||
|         fn dec(&self, u: U<'a>) -> Result<Self::A, DecodeError> {
 | ||
|             match u {
 | ||
|                 U::Record(map) => map
 | ||
|                     .into_iter()
 | ||
|                     .map(|(k, v)| self.0.dec(v).map(|v2| (k, v2)))
 | ||
|                     .collect::<Result<Self::A, _>>(),
 | ||
|                 o => Err(DecodeError(format!("Cannot decode {:?} into record", o))),
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     /// Assume a record and project out the field with the given name and type.
 | ||
|     #[derive(Clone, Copy)]
 | ||
|     pub struct RecordDot<'a, T> {
 | ||
|         pub field: &'a str,
 | ||
|         pub inner: T,
 | ||
|     }
 | ||
| 
 | ||
|     impl<'a, Inner> Decoder<'a> for RecordDot<'_, Inner>
 | ||
|     where
 | ||
|         Inner: Decoder<'a> + Clone,
 | ||
|     {
 | ||
|         type A = Inner::A;
 | ||
|         fn dec(&self, u: U<'a>) -> Result<Self::A, DecodeError> {
 | ||
|             match Record(self.inner.clone()).dec(u) {
 | ||
|                 Ok(mut map) => match map.remove(self.field) {
 | ||
|                     Some(inner) => Ok(inner),
 | ||
|                     None => Err(DecodeError(format!(
 | ||
|                         "Cannot find `{}` in record map",
 | ||
|                         self.field
 | ||
|                     ))),
 | ||
|                 },
 | ||
|                 Err(err) => Err(err),
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     /// Equals one of the listed `A`s exactly, after decoding.
 | ||
|     #[derive(Clone)]
 | ||
|     pub struct OneOf<T, A> {
 | ||
|         pub inner: T,
 | ||
|         pub list: Vec<A>,
 | ||
|     }
 | ||
| 
 | ||
|     impl<'a, Inner> Decoder<'a> for OneOf<Inner, Inner::A>
 | ||
|     where
 | ||
|         Inner: Decoder<'a>,
 | ||
|         Inner::A: Display + Debug + PartialEq,
 | ||
|     {
 | ||
|         type A = Inner::A;
 | ||
|         fn dec(&self, u: U<'a>) -> Result<Self::A, DecodeError> {
 | ||
|             match self.inner.dec(u) {
 | ||
|                 Ok(inner) => match self.list.iter().any(|x| x.eq(&inner)) {
 | ||
|                     true => Ok(inner),
 | ||
|                     false => Err(DecodeError(format!(
 | ||
|                         "{} is not one of {:?}",
 | ||
|                         inner, self.list
 | ||
|                     ))),
 | ||
|                 },
 | ||
|                 Err(err) => Err(err),
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     /// Try decoding as `T`.
 | ||
|     #[derive(Clone)]
 | ||
|     pub struct Try<T>(pub T);
 | ||
| 
 | ||
|     impl<'a, Inner> Decoder<'a> for Try<Inner>
 | ||
|     where
 | ||
|         Inner: Decoder<'a>,
 | ||
|     {
 | ||
|         type A = Option<Inner::A>;
 | ||
|         fn dec(&self, u: U<'a>) -> Result<Self::A, DecodeError> {
 | ||
|             match self.0.dec(u) {
 | ||
|                 Ok(inner) => Ok(Some(inner)),
 | ||
|                 Err(err) => Ok(None),
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
| }
 |