summaryrefslogtreecommitdiffstats
path: root/rust/vendor/bendy/src/value.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/vendor/bendy/src/value.rs')
-rw-r--r--rust/vendor/bendy/src/value.rs312
1 files changed, 312 insertions, 0 deletions
diff --git a/rust/vendor/bendy/src/value.rs b/rust/vendor/bendy/src/value.rs
new file mode 100644
index 0000000..3df0338
--- /dev/null
+++ b/rust/vendor/bendy/src/value.rs
@@ -0,0 +1,312 @@
+//! `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::{
+ ser::{SerializeMap, SerializeSeq},
+ Serialize,
+};
+
+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 leaves some room for external containers.
+ // TODO(#38): Change this to 0 for v0.4
+ const MAX_DEPTH: usize = usize::max_value() / 4;
+
+ 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> {
+ 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_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 super::*;
+
+ use alloc::{string::String, vec};
+
+ fn case(value: Value, expected: impl AsRef<[u8]>) {
+ let expected = expected.as_ref();
+
+ let encoded = match value.to_bencode() {
+ 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 decoded = match Value::from_bencode(&encoded) {
+ 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(Value::Dict(BTreeMap::new()), "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(Value::Dict(dict), "d3:bari2e3:fooi1ee");
+ }
+
+ #[test]
+ fn integer() {
+ case(Value::Integer(0), "i0e");
+ case(Value::Integer(-1), "i-1e");
+ }
+
+ #[test]
+ fn list() {
+ case(Value::List(Vec::new()), "le");
+ case(
+ Value::List(vec![
+ Value::Integer(0),
+ Value::Bytes(Cow::Borrowed(&[1, 2, 3])),
+ ]),
+ b"li0e3:\x01\x02\x03e",
+ );
+ }
+}