bencode-rs 0.2.2

bencode format parsing
Documentation
mod parsing;

use parsing::{next_nested, next_outer, Res, Value as ParseValue};
use std::{error::Error, fmt::Display};

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Value {
    Integer(i64),
    ByteString(Vec<u8>),
    List(Vec<Value>),
    Dictionary(Vec<(Value, Value)>),
}

pub fn parse_all(bytes: &[u8]) -> Result<Vec<Value>, ParseError> {
    let (_, out) = parse_many(bytes, next_outer)?;
    Ok(out)
}

fn parse_many(
    mut bytes: &[u8],
    f: fn(&[u8]) -> Res<Option<ParseValue>>,
) -> Result<(&[u8], Vec<Value>), ParseError> {
    let mut out = vec![];
    loop {
        let (i, maybe_value) = parse_one(bytes, f)?;
        bytes = i;
        match maybe_value {
            Some(value) => out.push(value),
            None => break,
        }
    }
    Ok((bytes, out))
}

#[allow(clippy::type_complexity)]
fn parse_dictionary(mut bytes: &[u8]) -> Result<(&[u8], Vec<(Value, Value)>), ParseError> {
    let mut out = vec![];
    loop {
        let (i, k) = parse_one(bytes, next_nested)?;
        bytes = i;
        let Some(k) = k else { break };
        let (i, v) = parse_one(bytes, next_nested)?;
        bytes = i;
        let Some(v) = v else { return Err(ParseError) };
        out.push((k, v));
    }
    Ok((bytes, out))
}

fn parse_one(
    bytes: &[u8],
    f: fn(&[u8]) -> Res<Option<ParseValue>>,
) -> Result<(&[u8], Option<Value>), ParseError> {
    Ok(match f(bytes) {
        Ok((i, None)) => (i, None),
        Ok((i, Some(ParseValue::Integer(int)))) => (i, Some(Value::Integer(int))),
        Ok((i, Some(ParseValue::ByteString(s)))) => (i, Some(Value::ByteString(s.to_vec()))),
        Ok((i, Some(ParseValue::List))) => {
            let (i, list) = parse_many(i, next_nested)?;
            (i, Some(Value::List(list)))
        }
        Ok((i, Some(ParseValue::Dictionary))) => {
            let (i, dictionary) = parse_dictionary(i)?;
            (i, Some(Value::Dictionary(dictionary)))
        }
        Err(e) => {
            println!("{e:?}");
            return Err(ParseError);
        }
    })
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ParseError;

impl Display for ParseError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Invalid bencode")
    }
}

impl Error for ParseError {}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn valid_int() {
        assert_eq!(
            parse_all(b"i115ei-12e"),
            Ok(vec![Value::Integer(115), Value::Integer(-12)])
        );
    }

    #[test]
    fn valid_list() {
        assert_eq!(
            parse_all(b"li115ei-12ee"),
            Ok(vec![Value::List(vec![
                Value::Integer(115),
                Value::Integer(-12)
            ])])
        )
    }

    #[test]
    fn valid_dictionary() {
        assert_eq!(
            parse_all(b"di1ei2ee"),
            Ok(vec![Value::Dictionary(vec![(
                Value::Integer(1),
                Value::Integer(2),
            )])])
        )
    }

    #[test]
    fn complex_value() {
        assert_eq!(
            parse_all(b"i1eli2el3:foo3:bared3:bazi3el7:listkeye5:valueee"),
            Ok(vec![
                Value::Integer(1),
                Value::List(vec![
                    Value::Integer(2),
                    Value::List(vec![
                        Value::ByteString(b"foo".to_vec()),
                        Value::ByteString(b"bar".to_vec()),
                    ]),
                    Value::Dictionary(vec![
                        (Value::ByteString(b"baz".to_vec()), Value::Integer(3)),
                        (
                            Value::List(vec![Value::ByteString(b"listkey".to_vec())]),
                            Value::ByteString(b"value".to_vec())
                        )
                    ])
                ])
            ])
        )
    }
}