diff options
Diffstat (limited to 'rust/vendor/bendy/src/encoding')
-rw-r--r-- | rust/vendor/bendy/src/encoding/encoder.rs | 482 | ||||
-rw-r--r-- | rust/vendor/bendy/src/encoding/error.rs | 56 | ||||
-rw-r--r-- | rust/vendor/bendy/src/encoding/printable_integer.rs | 15 | ||||
-rw-r--r-- | rust/vendor/bendy/src/encoding/to_bencode.rs | 265 |
4 files changed, 818 insertions, 0 deletions
diff --git a/rust/vendor/bendy/src/encoding/encoder.rs b/rust/vendor/bendy/src/encoding/encoder.rs new file mode 100644 index 0000000..ba4a164 --- /dev/null +++ b/rust/vendor/bendy/src/encoding/encoder.rs @@ -0,0 +1,482 @@ +#[cfg(not(feature = "std"))] +use alloc::{ + borrow::ToOwned, + collections::BTreeMap, + format, + string::{String, ToString}, + vec::Vec, +}; +#[cfg(feature = "std")] +use std::{collections::BTreeMap, vec::Vec}; + +use crate::{ + encoding::{Error, PrintableInteger, ToBencode}, + state_tracker::{StateTracker, StructureError, Token}, +}; + +/// The actual encoder. Unlike the decoder, this is not zero-copy, as that would +/// result in a horrible interface +#[derive(Default, Debug)] +pub struct Encoder { + state: StateTracker<Vec<u8>, Error>, + output: Vec<u8>, +} + +impl Encoder { + /// Create a new encoder + pub fn new() -> Self { + <Self as Default>::default() + } + + /// Set the max depth of the encoded object + #[must_use] + pub fn with_max_depth(mut self, max_depth: usize) -> Self { + self.state.set_max_depth(max_depth); + self + } + + /// Emit a single token to the encoder + pub(crate) fn emit_token(&mut self, token: Token) -> Result<(), Error> { + self.state.check_error()?; + self.state.observe_token(&token)?; + match token { + Token::List => self.output.push(b'l'), + Token::Dict => self.output.push(b'd'), + Token::String(s) => { + // Writing to a vec can't fail + let length = s.len().to_string(); + self.output.extend_from_slice(length.as_bytes()); + self.output.push(b':'); + self.output.extend_from_slice(s); + }, + Token::Num(num) => { + // Alas, this doesn't verify that the given number is valid + self.output.push(b'i'); + self.output.extend_from_slice(num.as_bytes()); + self.output.push(b'e'); + }, + Token::End => self.output.push(b'e'), + } + + Ok(()) + } + + /// Emit an arbitrary encodable object + pub fn emit<E: ToBencode>(&mut self, value: E) -> Result<(), Error> { + self.emit_with(|e| value.encode(e)) + } + + /// Emit a single object using an encoder + pub fn emit_with<F>(&mut self, value_cb: F) -> Result<(), Error> + where + F: FnOnce(SingleItemEncoder) -> Result<(), Error>, + { + let mut value_written = false; + let ret = value_cb(SingleItemEncoder { + encoder: self, + value_written: &mut value_written, + }); + + self.state.latch_err(ret)?; + + if !value_written { + return self + .state + .latch_err(Err(Error::from(StructureError::invalid_state( + "No value was emitted", + )))); + } + + Ok(()) + } + + /// Emit an integer + pub fn emit_int<T: PrintableInteger>(&mut self, value: T) -> Result<(), Error> { + // This doesn't use emit_token, as that would require that I write the integer to a + // temporary buffer and then copy it to the output; writing it directly saves at + // least one memory allocation + self.state.check_error()?; + // We observe an int here, as we need something that isn't a string (and therefore + // possibly valid as a key) but we also want to require as few state transitions as + // possible (for performance) + self.state.observe_token(&Token::Num(""))?; + self.output.push(b'i'); + self.output.extend_from_slice(value.to_string().as_bytes()); + self.output.push(b'e'); + Ok(()) + } + + /// Emit a string + pub fn emit_str(&mut self, value: &str) -> Result<(), Error> { + self.emit_token(Token::String(value.as_bytes())) + } + + /// Emit a byte array + pub fn emit_bytes(&mut self, value: &[u8]) -> Result<(), Error> { + self.emit_token(Token::String(value)) + } + + /// Emit a dictionary where you know that the keys are already + /// sorted. The callback must emit key/value pairs to the given + /// encoder in sorted order. If the key/value pairs may not be + /// sorted, [`emit_unsorted_dict`] should be used instead. + /// + /// [`emit_unsorted_dict`]: SingleItemEncoder::emit_unsorted_dict + /// + /// Example: + /// + /// ``` + /// # use bendy::encoding::{Encoder, Error}; + /// # + /// # fn main() -> Result<(), Error>{ + /// let mut encoder = Encoder::new(); + /// encoder.emit_dict(|mut e| { + /// e.emit_pair(b"a", "foo")?; + /// e.emit_pair(b"b", 2) + /// }) + /// # } + /// ``` + pub fn emit_dict<F>(&mut self, content_cb: F) -> Result<(), Error> + where + F: FnOnce(SortedDictEncoder) -> Result<(), Error>, + { + self.emit_token(Token::Dict)?; + content_cb(SortedDictEncoder { encoder: self })?; + self.emit_token(Token::End) + } + + /// Emit an arbitrary list. The callback should emit the contents + /// of the list to the given encoder. + /// + /// E.g., to emit the list `[1,2,3]`, you would write + /// + /// ``` + /// # use bendy::encoding::{Encoder, Error}; + /// # fn main() -> Result<(), Error> { + /// let mut encoder = Encoder::new(); + /// encoder.emit_list(|e| { + /// e.emit_int(1)?; + /// e.emit_int(2)?; + /// e.emit_int(3) + /// }) + /// # } + /// ``` + pub fn emit_list<F>(&mut self, list_cb: F) -> Result<(), Error> + where + F: FnOnce(&mut Encoder) -> Result<(), Error>, + { + self.emit_token(Token::List)?; + list_cb(self)?; + self.emit_token(Token::End) + } + + /// Emit a dictionary that may have keys out of order. This will write the dict + /// values to temporary memory, then sort them before adding them to the serialized + /// stream + /// + /// Example. + /// + /// ``` + /// # use bendy::encoding::{Encoder, Error}; + /// # + /// # fn main() -> Result<(), Error> { + /// let mut encoder = Encoder::new(); + /// encoder.emit_and_sort_dict(|e| { + /// // Unlike in the example for Encoder::emit_dict(), these keys aren't sorted + /// e.emit_pair(b"b", 2)?; + /// e.emit_pair(b"a", "foo") + /// }) + /// # } + /// ``` + pub fn emit_and_sort_dict<F>(&mut self, content_cb: F) -> Result<(), Error> + where + F: FnOnce(&mut UnsortedDictEncoder) -> Result<(), Error>, + { + let mut encoder = self.begin_unsorted_dict()?; + + content_cb(&mut encoder)?; + + self.end_unsorted_dict(encoder) + } + + /// Return the encoded string, if all objects written are complete + pub fn get_output(mut self) -> Result<Vec<u8>, Error> { + self.state.observe_eof()?; + Ok(self.output) + } + + pub(crate) fn begin_unsorted_dict(&mut self) -> Result<UnsortedDictEncoder, Error> { + // emit the dict token so that a pre-existing state error is reported early + self.emit_token(Token::Dict)?; + + Ok(UnsortedDictEncoder::new(self.state.remaining_depth())) + } + + pub(crate) fn end_unsorted_dict(&mut self, encoder: UnsortedDictEncoder) -> Result<(), Error> { + let content = encoder.done()?; + + for (k, v) in content { + self.emit_bytes(&k)?; + // We know that the output is a single object by construction + self.state.observe_token(&Token::Num(""))?; + self.output.extend_from_slice(&v); + } + + self.emit_token(Token::End)?; + + Ok(()) + } +} + +/// An encoder that can only encode a single item. See [`Encoder`] +/// for usage examples; the only difference between these classes is +/// that `SingleItemEncoder` can only be used once. +pub struct SingleItemEncoder<'a> { + encoder: &'a mut Encoder, + /// Whether we attempted to write a value to the encoder. The value + /// of the referent of this field is meaningless if the encode method + /// failed. + value_written: &'a mut bool, +} + +impl<'a> SingleItemEncoder<'a> { + /// Emit an arbitrary encodable object + pub fn emit<E: ToBencode + ?Sized>(self, value: &E) -> Result<(), Error> { + value.encode(self) + } + + /// Emit a single object using an encoder + pub fn emit_with<F>(self, value_cb: F) -> Result<(), Error> + where + F: FnOnce(SingleItemEncoder) -> Result<(), Error>, + { + value_cb(self) + } + + /// Emit an integer + pub fn emit_int<T: PrintableInteger>(self, value: T) -> Result<(), Error> { + *self.value_written = true; + self.encoder.emit_int(value) + } + + /// Emit a string + pub fn emit_str(self, value: &str) -> Result<(), Error> { + *self.value_written = true; + self.encoder.emit_str(value) + } + + /// Emit a byte array + pub fn emit_bytes(self, value: &[u8]) -> Result<(), Error> { + *self.value_written = true; + self.encoder.emit_bytes(value) + } + + /// Emit an arbitrary list + pub fn emit_list<F>(self, list_cb: F) -> Result<(), Error> + where + F: FnOnce(&mut Encoder) -> Result<(), Error>, + { + *self.value_written = true; + self.encoder.emit_list(list_cb) + } + + /// Emit a sorted dictionary. If the input dictionary is unsorted, this will return an error. + pub fn emit_dict<F>(self, content_cb: F) -> Result<(), Error> + where + F: FnOnce(SortedDictEncoder) -> Result<(), Error>, + { + *self.value_written = true; + self.encoder.emit_dict(content_cb) + } + + /// Emit a dictionary that may have keys out of order. This will write the dict + /// values to temporary memory, then sort them before adding them to the serialized + /// stream + pub fn emit_unsorted_dict<F>(self, content_cb: F) -> Result<(), Error> + where + F: FnOnce(&mut UnsortedDictEncoder) -> Result<(), Error>, + { + *self.value_written = true; + self.encoder.emit_and_sort_dict(content_cb) + } + + /// Emit an arbitrary list. + /// + /// Attention: If this method is used while canonical output is required + /// the caller needs to ensure that the iterator has a defined order. + pub fn emit_unchecked_list( + self, + iterable: impl Iterator<Item = impl ToBencode>, + ) -> Result<(), Error> { + self.emit_list(|e| { + for item in iterable { + e.emit(item)?; + } + Ok(()) + }) + } +} + +/// Encodes a map with pre-sorted keys +pub struct SortedDictEncoder<'a> { + encoder: &'a mut Encoder, +} + +impl<'a> SortedDictEncoder<'a> { + /// Emit a key/value pair + pub fn emit_pair<E>(&mut self, key: &[u8], value: E) -> Result<(), Error> + where + E: ToBencode, + { + self.encoder.emit_token(Token::String(key))?; + self.encoder.emit(value) + } + + /// Equivalent to [`SortedDictEncoder::emit_pair()`], but forces the type of the value + /// to be a callback + pub fn emit_pair_with<F>(&mut self, key: &[u8], value_cb: F) -> Result<(), Error> + where + F: FnOnce(SingleItemEncoder) -> Result<(), Error>, + { + self.encoder.emit_token(Token::String(key))?; + self.encoder.emit_with(value_cb) + } +} + +/// Helper to write a dictionary that may have keys out of order. This will buffer the +/// dict values in temporary memory, then sort them before adding them to the serialized +/// stream +pub struct UnsortedDictEncoder { + content: BTreeMap<Vec<u8>, Vec<u8>>, + error: Result<(), Error>, + remaining_depth: usize, +} + +impl UnsortedDictEncoder { + pub(crate) fn new(remaining_depth: usize) -> Self { + Self { + content: BTreeMap::new(), + error: Ok(()), + remaining_depth, + } + } + + /// Emit a key/value pair + pub fn emit_pair<E>(&mut self, key: &[u8], value: E) -> Result<(), Error> + where + E: ToBencode, + { + self.emit_pair_with(key, |e| value.encode(e)) + } + + /// Emit a key/value pair where the value is produced by a callback + pub fn emit_pair_with<F>(&mut self, key: &[u8], value_cb: F) -> Result<(), Error> + where + F: FnOnce(SingleItemEncoder) -> Result<(), Error>, + { + let mut value_written = false; + + let mut encoder = Encoder::new().with_max_depth(self.remaining_depth); + + let ret = value_cb(SingleItemEncoder { + encoder: &mut encoder, + value_written: &mut value_written, + }); + + if ret.is_err() { + self.error = ret.clone(); + return ret; + } + + if !value_written { + self.error = Err(Error::from(StructureError::InvalidState( + "No value was emitted".to_owned(), + ))); + } else { + self.error = encoder.state.observe_eof().map_err(Error::from); + } + + if self.error.is_err() { + return self.error.clone(); + } + + let encoded_object = encoder + .get_output() + .expect("Any errors should have been caught by observe_eof"); + + self.save_pair(key, encoded_object) + } + + #[cfg(feature = "serde")] + pub(crate) fn remaining_depth(&self) -> usize { + self.remaining_depth + } + + pub(crate) fn save_pair( + &mut self, + unencoded_key: &[u8], + encoded_value: Vec<u8>, + ) -> Result<(), Error> { + #[cfg(not(feature = "std"))] + use alloc::collections::btree_map::Entry; + #[cfg(feature = "std")] + use std::collections::btree_map::Entry; + + if self.error.is_err() { + return self.error.clone(); + } + + let vacancy = match self.content.entry(unencoded_key.to_owned()) { + Entry::Vacant(vacancy) => vacancy, + Entry::Occupied(occupation) => { + self.error = Err(Error::from(StructureError::InvalidState(format!( + "Duplicate key {}", + String::from_utf8_lossy(occupation.key()) + )))); + return self.error.clone(); + }, + }; + + vacancy.insert(encoded_value); + + Ok(()) + } + + pub(crate) fn done(self) -> Result<BTreeMap<Vec<u8>, Vec<u8>>, Error> { + self.error?; + Ok(self.content) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + pub fn simple_encoding_works() { + let mut encoder = Encoder::new(); + encoder + .emit_dict(|mut e| { + e.emit_pair(b"bar", 25)?; + e.emit_pair_with(b"foo", |e| { + e.emit_list(|e| { + e.emit_str("baz")?; + e.emit_str("qux") + }) + }) + }) + .expect("Encoding shouldn't fail"); + assert_eq!( + &encoder + .get_output() + .expect("Complete object should have been written"), + &b"d3:bari25e3:fool3:baz3:quxee" + ); + } + + #[test] + fn emit_cb_must_emit() { + let mut encoder = Encoder::new(); + assert!(encoder.emit_with(|_| Ok(())).is_err()); + } +} diff --git a/rust/vendor/bendy/src/encoding/error.rs b/rust/vendor/bendy/src/encoding/error.rs new file mode 100644 index 0000000..cc651d8 --- /dev/null +++ b/rust/vendor/bendy/src/encoding/error.rs @@ -0,0 +1,56 @@ +#[cfg(feature = "std")] +use std::sync::Arc; + +use failure::Fail; + +use crate::state_tracker::StructureError; + +#[derive(Debug, Clone, Fail)] +#[fail(display = "encoding failed: {}", _0)] +pub struct Error(#[fail(cause)] pub ErrorKind); + +/// An enumeration of potential errors that appear during bencode encoding. +#[derive(Debug, Clone, Fail)] +pub enum ErrorKind { + /// Error that occurs if the serialized structure contains invalid semantics. + #[cfg(feature = "std")] + #[fail(display = "malformed content discovered: {}", _0)] + MalformedContent(Arc<failure::Error>), + /// Error that occurs if the serialized structure contains invalid semantics. + #[cfg(not(feature = "std"))] + #[fail(display = "malformed content discovered")] + MalformedContent, + /// Error in the bencode structure (e.g. a missing field end separator). + #[fail(display = "bencode encoding corrupted")] + StructureError(#[fail(cause)] StructureError), +} + +impl Error { + /// Raised when there is a general error while deserializing a type. + /// The message should not be capitalized and should not end with a period. + /// + /// Note that, when building with no_std, this method accepts any type as + /// its argument. + #[cfg(feature = "std")] + pub fn malformed_content(cause: impl Into<failure::Error>) -> Error { + let error = Arc::new(cause.into()); + Self(ErrorKind::MalformedContent(error)) + } + + #[cfg(not(feature = "std"))] + pub fn malformed_content<T>(_cause: T) -> Error { + Self(ErrorKind::MalformedContent) + } +} + +impl From<StructureError> for Error { + fn from(error: StructureError) -> Self { + Self(ErrorKind::StructureError(error)) + } +} + +impl From<ErrorKind> for Error { + fn from(kind: ErrorKind) -> Self { + Self(kind) + } +} diff --git a/rust/vendor/bendy/src/encoding/printable_integer.rs b/rust/vendor/bendy/src/encoding/printable_integer.rs new file mode 100644 index 0000000..8140dca --- /dev/null +++ b/rust/vendor/bendy/src/encoding/printable_integer.rs @@ -0,0 +1,15 @@ +#[cfg(not(feature = "std"))] +use core::fmt::Display; +#[cfg(feature = "std")] +use std::fmt::Display; + +/// A value that can be formatted as a decimal integer +pub trait PrintableInteger: Display {} + +macro_rules! impl_integer { + ($($type:ty)*) => {$( + impl PrintableInteger for $type {} + )*} +} + +impl_integer!(u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize); diff --git a/rust/vendor/bendy/src/encoding/to_bencode.rs b/rust/vendor/bendy/src/encoding/to_bencode.rs new file mode 100644 index 0000000..a19dcb3 --- /dev/null +++ b/rust/vendor/bendy/src/encoding/to_bencode.rs @@ -0,0 +1,265 @@ +#[cfg(not(feature = "std"))] +use alloc::{ + collections::{BTreeMap, LinkedList, VecDeque}, + rc::Rc, + string::String, + sync::Arc, + vec::Vec, +}; + +#[cfg(feature = "std")] +use std::{ + collections::{BTreeMap, HashMap, LinkedList, VecDeque}, + hash::{BuildHasher, Hash}, + rc::Rc, + sync::Arc, +}; + +use crate::encoding::{Encoder, Error, SingleItemEncoder}; + +/// An object that can be encoded into a single bencode object +pub trait ToBencode { + /// The maximum depth that this object could encode to. Leaves do not consume a level, so an + /// `i1e` has depth 0 and `li1ee` has depth 1. + const MAX_DEPTH: usize; + + /// Encode this object into the bencode stream + fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error>; + + /// Encode this object to a byte string + fn to_bencode(&self) -> Result<Vec<u8>, Error> { + let mut encoder = Encoder::new().with_max_depth(Self::MAX_DEPTH); + encoder.emit_with(|e| self.encode(e).map_err(Error::into))?; + + let bytes = encoder.get_output()?; + Ok(bytes) + } +} + +/// Wrapper to allow `Vec<u8>` encoding as bencode string element. +#[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq, PartialOrd, Ord)] +pub struct AsString<I>(pub I); + +// Forwarding impls +impl<'a, E: 'a + ToBencode + Sized> ToBencode for &'a E { + const MAX_DEPTH: usize = E::MAX_DEPTH; + + fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { + E::encode(self, encoder) + } +} + +#[cfg(feature = "std")] +impl<E: ToBencode> ToBencode for Box<E> { + const MAX_DEPTH: usize = E::MAX_DEPTH; + + fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { + E::encode(&*self, encoder) + } +} + +impl<E: ToBencode> ToBencode for Rc<E> { + const MAX_DEPTH: usize = E::MAX_DEPTH; + + fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { + E::encode(&*self, encoder) + } +} + +impl<E: ToBencode> ToBencode for Arc<E> { + const MAX_DEPTH: usize = E::MAX_DEPTH; + + fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { + E::encode(&*self, encoder) + } +} + +// Base type impls +impl<'a> ToBencode for &'a str { + const MAX_DEPTH: usize = 0; + + fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { + encoder.emit_str(self).map_err(Error::from) + } +} + +impl ToBencode for String { + const MAX_DEPTH: usize = 0; + + fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { + encoder.emit_str(self).map_err(Error::from) + } +} + +macro_rules! impl_encodable_integer { + ($($type:ty)*) => {$( + impl ToBencode for $type { + const MAX_DEPTH: usize = 1; + + fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { + encoder.emit_int(*self).map_err(Error::from) + } + } + )*} +} + +impl_encodable_integer!(u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize); + +macro_rules! impl_encodable_iterable { + ($($type:ident)*) => {$( + impl <ContentT> ToBencode for $type<ContentT> + where + ContentT: ToBencode + { + const MAX_DEPTH: usize = ContentT::MAX_DEPTH + 1; + + fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { + encoder.emit_list(|e| { + for item in self { + e.emit(item)?; + } + Ok(()) + })?; + + Ok(()) + } + } + )*} +} + +impl_encodable_iterable!(Vec VecDeque LinkedList); + +impl<'a, ContentT> ToBencode for &'a [ContentT] +where + ContentT: ToBencode, +{ + const MAX_DEPTH: usize = ContentT::MAX_DEPTH + 1; + + fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { + encoder.emit_list(|e| { + for item in *self { + e.emit(item)?; + } + Ok(()) + })?; + + Ok(()) + } +} + +impl<K: AsRef<[u8]>, V: ToBencode> ToBencode for BTreeMap<K, V> { + const MAX_DEPTH: usize = V::MAX_DEPTH + 1; + + fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { + encoder.emit_dict(|mut e| { + for (k, v) in self { + e.emit_pair(k.as_ref(), v)?; + } + Ok(()) + })?; + + Ok(()) + } +} + +#[cfg(feature = "std")] +impl<K, V, S> ToBencode for HashMap<K, V, S> +where + K: AsRef<[u8]> + Eq + Hash, + V: ToBencode, + S: BuildHasher, +{ + const MAX_DEPTH: usize = V::MAX_DEPTH + 1; + + fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { + encoder.emit_dict(|mut e| { + let mut pairs = self + .iter() + .map(|(k, v)| (k.as_ref(), v)) + .collect::<Vec<_>>(); + pairs.sort_by_key(|&(k, _)| k); + for (k, v) in pairs { + e.emit_pair(k, v)?; + } + Ok(()) + })?; + + Ok(()) + } +} + +impl<I> ToBencode for AsString<I> +where + I: AsRef<[u8]>, +{ + const MAX_DEPTH: usize = 1; + + fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { + encoder.emit_bytes(self.0.as_ref())?; + Ok(()) + } +} + +impl<I> AsRef<[u8]> for AsString<I> +where + I: AsRef<[u8]>, +{ + fn as_ref(&self) -> &'_ [u8] { + self.0.as_ref() + } +} + +impl<'a, I> From<&'a [u8]> for AsString<I> +where + I: From<&'a [u8]>, +{ + fn from(content: &'a [u8]) -> Self { + AsString(I::from(content)) + } +} + +#[cfg(test)] +mod test { + + #[cfg(not(feature = "std"))] + use alloc::{borrow::ToOwned, vec}; + + use super::*; + + struct Foo { + bar: u32, + baz: Vec<String>, + qux: Vec<u8>, + } + + impl ToBencode for Foo { + const MAX_DEPTH: usize = 2; + + fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { + encoder.emit_dict(|mut e| { + e.emit_pair(b"bar", &self.bar)?; + e.emit_pair(b"baz", &self.baz)?; + e.emit_pair(b"qux", AsString(&self.qux))?; + Ok(()) + })?; + + Ok(()) + } + } + + #[test] + fn simple_encodable_works() { + let mut encoder = Encoder::new(); + encoder + .emit(Foo { + bar: 5, + baz: vec!["foo".to_owned(), "bar".to_owned()], + qux: b"qux".to_vec(), + }) + .unwrap(); + assert_eq!( + &encoder.get_output().unwrap()[..], + &b"d3:bari5e3:bazl3:foo3:bare3:qux3:quxe"[..] + ); + } +} |