bendy 0.6.1

A rust library for encoding and decoding bencode with enforced canonicalization rules.
Documentation
//! `Value`s hold arbitrary borrowed or owneed bencode data. Unlike `Objects`,
//! they can be cloned and traversed multiple times.
//!
//! `Value` implements `FromBencode`, `ToBencode`. If the `serde` feature is
//! enabled, it also implements `Serialize` and `Deserialize`.

use alloc::{
    borrow::{Cow, ToOwned},
    collections::BTreeMap,
    vec::Vec,
};

#[cfg(feature = "serde")]
use std::{
    convert::TryInto,
    fmt::{self, Formatter},
    marker::PhantomData,
};

#[cfg(feature = "serde")]
use serde_ as serde;

#[cfg(feature = "serde")]
use serde::{
    Serialize,
    ser::{SerializeMap, SerializeSeq},
};

use crate::{
    decoding::{FromBencode, Object},
    encoding::{SingleItemEncoder, ToBencode},
};

/// An owned or borrowed bencoded value.
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum Value<'a> {
    /// An owned or borrowed byte string
    Bytes(Cow<'a, [u8]>),
    /// A dictionary mapping byte strings to values
    Dict(BTreeMap<Cow<'a, [u8]>, Value<'a>>),
    /// A signed integer
    Integer(i64),
    /// A list of values
    List(Vec<Value<'a>>),
}

impl<'a> Value<'a> {
    /// Convert this Value into an owned Value with static lifetime
    pub fn into_owned(self) -> Value<'static> {
        match self {
            Value::Bytes(bytes) => Value::Bytes(Cow::Owned(bytes.into_owned())),
            Value::Dict(dict) => Value::Dict(
                dict.into_iter()
                    .map(|(key, value)| (Cow::Owned(key.into_owned()), value.into_owned()))
                    .collect(),
            ),
            Value::Integer(integer) => Value::Integer(integer),
            Value::List(list) => Value::List(list.into_iter().map(Value::into_owned).collect()),
        }
    }
}

impl<'a> ToBencode for Value<'a> {
    /// This is set to zero to indicate a statically unknown depth.
    /// See the [`encoding`][crate::encoding#dynamic-depth] module docs.
    const MAX_DEPTH: usize = 0;

    fn encode(&self, encoder: SingleItemEncoder) -> Result<(), crate::encoding::Error> {
        match self {
            Value::Bytes(bytes) => encoder.emit_bytes(bytes),
            Value::Dict(dict) => dict.encode(encoder),
            Value::Integer(integer) => integer.encode(encoder),
            Value::List(list) => list.encode(encoder),
        }
    }
}

impl<'a> FromBencode for Value<'a> {
    /// This is set to zero to indicate a statically unknown depth.
    /// See the [`encoding`][crate::encoding#dynamic-depth] module docs.
    const EXPECTED_RECURSION_DEPTH: usize = <Self as ToBencode>::MAX_DEPTH;

    fn decode_bencode_object(object: Object) -> Result<Self, crate::decoding::Error> {
        match object {
            Object::Bytes(bytes) => Ok(Value::Bytes(Cow::Owned(bytes.to_owned()))),
            Object::Dict(mut decoder) => {
                let mut dict = BTreeMap::new();
                while let Some((key, value)) = decoder.next_pair()? {
                    dict.insert(
                        Cow::Owned(key.to_owned()),
                        Value::decode_bencode_object(value)?,
                    );
                }
                Ok(Value::Dict(dict))
            },
            Object::Integer(text) => Ok(Value::Integer(text.parse()?)),
            Object::List(mut decoder) => {
                let mut list = Vec::new();
                while let Some(object) = decoder.next_object()? {
                    list.push(Value::decode_bencode_object(object)?);
                }
                Ok(Value::List(list))
            },
        }
    }
}

#[cfg(feature = "serde")]
mod serde_impls {
    use super::*;

    use serde_bytes::Bytes;

    impl<'a> Serialize for Value<'a> {
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: serde::ser::Serializer,
        {
            match self {
                Value::Bytes(string) => serializer.serialize_bytes(string),
                Value::Integer(int) => serializer.serialize_i64(*int),
                Value::List(list) => {
                    let mut seed = serializer.serialize_seq(Some(list.len()))?;
                    for value in list {
                        seed.serialize_element(value)?;
                    }
                    seed.end()
                },
                Value::Dict(dict) => {
                    let mut seed = serializer.serialize_map(Some(dict.len()))?;
                    for (k, v) in dict {
                        let bytes = Bytes::new(k);
                        seed.serialize_entry(bytes, v)?;
                    }
                    seed.end()
                },
            }
        }
    }

    impl<'de: 'a, 'a> serde::de::Deserialize<'de> for Value<'a> {
        #[inline]
        fn deserialize<D>(deserializer: D) -> Result<Value<'a>, D::Error>
        where
            D: serde::de::Deserializer<'de>,
        {
            deserializer.deserialize_any(Visitor(PhantomData))
        }
    }

    struct Visitor<'a>(PhantomData<&'a ()>);

    impl<'de: 'a, 'a> serde::de::Visitor<'de> for Visitor<'a> {
        type Value = Value<'a>;

        fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
            formatter.write_str("any valid BEncode value")
        }

        fn visit_i64<E>(self, value: i64) -> Result<Value<'a>, E> {
            Ok(Value::Integer(value))
        }

        fn visit_u64<E>(self, value: u64) -> Result<Value<'a>, E> {
            Ok(Value::Integer(value.try_into().unwrap()))
        }

        fn visit_borrowed_bytes<E>(self, value: &'de [u8]) -> Result<Value<'a>, E>
        where
            E: serde::de::Error,
        {
            Ok(Value::Bytes(Cow::Borrowed(value)))
        }

        fn visit_borrowed_str<E>(self, value: &'de str) -> Result<Value<'a>, E>
        where
            E: serde::de::Error,
        {
            Ok(Value::Bytes(Cow::Borrowed(value.as_bytes())))
        }

        fn visit_str<E>(self, value: &str) -> Result<Value<'a>, E>
        where
            E: serde::de::Error,
        {
            Ok(Value::Bytes(Cow::Owned(value.as_bytes().to_vec())))
        }

        fn visit_string<E>(self, value: String) -> Result<Value<'a>, E> {
            Ok(Value::Bytes(Cow::Owned(value.into_bytes())))
        }

        fn visit_byte_buf<E>(self, value: Vec<u8>) -> Result<Value<'a>, E> {
            Ok(Value::Bytes(Cow::Owned(value)))
        }

        fn visit_seq<V>(self, mut access: V) -> Result<Value<'a>, V::Error>
        where
            V: serde::de::SeqAccess<'de>,
        {
            let mut list = Vec::new();
            while let Some(e) = access.next_element()? {
                list.push(e);
            }
            Ok(Value::List(list))
        }

        fn visit_map<V>(self, mut access: V) -> Result<Value<'a>, V::Error>
        where
            V: serde::de::MapAccess<'de>,
        {
            let mut map = BTreeMap::new();
            while let Some((k, v)) = access.next_entry::<&Bytes, _>()? {
                map.insert(Cow::Borrowed(k.as_ref()), v);
            }
            Ok(Value::Dict(map))
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::{decoding::Decoder, encoding::Encoder};

    use super::*;

    use alloc::{string::String, vec};

    /// Round-trip and verify encoding. Uses depth 0.
    fn case(value: Value, expected: impl AsRef<[u8]>) {
        case_impl(value, 0, expected);
    }

    fn case_impl(value: Value, depth: usize, expected: impl AsRef<[u8]>) {
        let expected = expected.as_ref();

        let mut encoder = Encoder::new().with_max_depth(depth);

        match encoder.emit(&value) {
            Err(err) => panic!("Failed to encode `{:?}`: {}", value, err),
            Ok(()) => (),
        };

        let encoded = match encoder.get_output() {
            Ok(bytes) => bytes,
            Err(err) => panic!("Failed to encode `{:?}`: {}", value, err),
        };

        if encoded != expected {
            panic!(
                "Expected `{:?}` to encode as `{}`, but got `{}",
                value,
                String::from_utf8_lossy(expected),
                String::from_utf8_lossy(&encoded)
            )
        }

        let mut decoder = Decoder::new(&encoded).with_max_depth(depth);
        let object = match decoder.next_object() {
            Ok(Some(obj)) => obj,
            Ok(None) => panic!(
                "Failed to decode value from `{}`: Unexpected EOF.",
                String::from_utf8_lossy(&encoded),
            ),
            Err(err) => panic!(
                "Failed to decode value from `{}`: {}",
                String::from_utf8_lossy(&encoded),
                err,
            ),
        };
        let decoded = match Value::decode_bencode_object(object) {
            Ok(decoded) => decoded,
            Err(err) => panic!(
                "Failed to decode value from `{}`: {}",
                String::from_utf8_lossy(&encoded),
                err,
            ),
        };

        assert_eq!(decoded, value);

        #[cfg(feature = "serde")]
        {
            let deserialized = match crate::serde::de::from_bytes::<Value>(expected) {
                Ok(deserialized) => deserialized,
                Err(err) => panic!(
                    "Failed to deserialize value from `{}`: {}",
                    String::from_utf8_lossy(&expected),
                    err
                ),
            };

            if deserialized != value {
                panic!(
                    "Deserialize Serialize produced unexpected value: `{:?}` != `{:?}`",
                    deserialized, value
                );
            }

            let serialized = match crate::serde::ser::to_bytes(&value) {
                Ok(serialized) => serialized,
                Err(err) => panic!("Failed to serialize `{:?}`: {}", value, err),
            };

            if serialized != expected {
                panic!(
                    "Serialize Serialize produced unexpected bencode: `{:?}` != `{:?}`",
                    String::from_utf8_lossy(&serialized),
                    String::from_utf8_lossy(expected)
                );
            }
        }
    }

    #[test]
    fn bytes() {
        case(Value::Bytes(Cow::Borrowed(&[1, 2, 3])), b"3:\x01\x02\x03");
        case(Value::Bytes(Cow::Owned(vec![1, 2, 3])), b"3:\x01\x02\x03");
    }

    #[test]
    fn dict() {
        case_impl(Value::Dict(BTreeMap::new()), 1, "de");

        let mut dict = BTreeMap::new();
        dict.insert(Cow::Borrowed("foo".as_bytes()), Value::Integer(1));
        dict.insert(Cow::Borrowed("bar".as_bytes()), Value::Integer(2));
        case_impl(Value::Dict(dict), 1, "d3:bari2e3:fooi1ee");
    }

    #[test]
    fn integer() {
        case(Value::Integer(0), "i0e");
        case(Value::Integer(-1), "i-1e");
    }

    #[test]
    fn list() {
        case_impl(Value::List(Vec::new()), 1, "le");
        case_impl(
            Value::List(vec![
                Value::Integer(0),
                Value::Bytes(Cow::Borrowed(&[1, 2, 3])),
            ]),
            1,
            b"li0e3:\x01\x02\x03e",
        );
    }
}