diff --git a/Cargo.toml b/Cargo.toml index cf28425..27cdd46 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,11 +32,13 @@ codegen-units = 1 ### DEPENDENCIES ############################################################### [dependencies] +rustversion = "1.0.4" serde_ = { version = "^1.0" , optional = true, package = "serde" } serde_bytes = { version = "^0.11.3", optional = true } snafu = { version = "^0.6.9", default_features = false } [dev-dependencies] +doc-comment = "0.3.3" regex = "^1.0" serde_derive = "^1.0" diff --git a/README.md b/README.md index 0c5fc30..08db258 100644 --- a/README.md +++ b/README.md @@ -94,16 +94,12 @@ it is enough to import the trait and call the `to_bencode()` function on the obj ```rust use bendy::encoding::{ToBencode, Error}; -fn main() {} +let my_data = vec!["hello", "world"]; +let encoded = my_data.to_bencode()?; -#[test] -fn encode_vector() -> Result<(), Error> { - let my_data = vec!["hello", "world"]; - let encoded = my_data.to_bencode()?; +assert_eq!(b"l5:hello5:worlde", encoded.as_slice()); - assert_eq!(b"l5:hello5:worlde", encoded.as_slice()); - Ok(()) -} +Ok::<(), Error>(()) ``` ### Implementing `ToBencode` @@ -140,20 +136,15 @@ impl ToBencode for IntegerWrapper { } } -fn main() {} - -#[test] -fn encode_integer() -> Result<(), Error> { - let example = IntegerWrapper(21); +let example = IntegerWrapper(21); - let encoded = example.to_bencode()?; - assert_eq!(b"i21e", encoded.as_slice()); +let encoded = example.to_bencode()?; +assert_eq!(b"i21e", encoded.as_slice()); - let encoded = 21.to_bencode()?; - assert_eq!(b"i21e", encoded.as_slice()); +let encoded = 21.to_bencode()?; +assert_eq!(b"i21e", encoded.as_slice()); - Ok(()) -} +Ok::<(), Error>(()) ``` **Encode a byte string** @@ -174,20 +165,15 @@ impl ToBencode for StringWrapper { } } -fn main() {} - -#[test] -fn encode_string() -> Result<(), Error> { - let example = StringWrapper("content".to_string()); +let example = StringWrapper("content".to_string()); - let encoded = example.to_bencode()?; - assert_eq!(b"7:content", encoded.as_slice()); +let encoded = example.to_bencode()?; +assert_eq!(b"7:content", encoded.as_slice()); - let encoded = "content".to_bencode()?; - assert_eq!(b"7:content", encoded.as_slice()); +let encoded = "content".to_bencode()?; +assert_eq!(b"7:content", encoded.as_slice()); - Ok(()) -} +Ok::<(), Error>(()) ``` As its a very common pattern to represent a byte string as `Vec` bendy @@ -209,20 +195,15 @@ impl ToBencode for ByteStringWrapper { } } -fn main() {} - -#[test] -fn encode_byte_string() -> Result<(), Error> { - let example = ByteStringWrapper(b"content".to_vec()); +let example = ByteStringWrapper(b"content".to_vec()); - let encoded = example.to_bencode()?; - assert_eq!(b"7:content", encoded.as_slice()); +let encoded = example.to_bencode()?; +assert_eq!(b"7:content", encoded.as_slice()); - let encoded = AsString(b"content").to_bencode()?; - assert_eq!(b"7:content", encoded.as_slice()); +let encoded = AsString(b"content").to_bencode()?; +assert_eq!(b"7:content", encoded.as_slice()); - Ok(()) -} +Ok::<(), Error>(()) ``` **Encode a dictionary** @@ -258,17 +239,12 @@ impl ToBencode for Example { } } -fn main() {} - -#[test] -fn encode_dictionary() -> Result<(), Error> { - let example = Example { label: "Example".to_string(), counter: 0 }; +let example = Example { label: "Example".to_string(), counter: 0 }; - let encoded = example.to_bencode()?; - assert_eq!(b"d7:counteri0e5:label7:Examplee", encoded.as_slice()); +let encoded = example.to_bencode()?; +assert_eq!(b"d7:counteri0e5:label7:Examplee", encoded.as_slice()); - Ok(()) -} +Ok::<(), Error>(()) ``` **Encode a list** @@ -293,17 +269,12 @@ impl ToBencode for Location { } } -fn main() {} - -#[test] -fn encode_list() -> Result<(), Error> { - let example = Location(2, 3); +let example = Location(2, 3); - let encoded = example.to_bencode()?; - assert_eq!(b"li2ei3ee", encoded.as_slice()); +let encoded = example.to_bencode()?; +assert_eq!(b"li2ei3ee", encoded.as_slice()); - Ok(()) -} +Ok::<(), Error>(()) ``` ### Decoding with `FromBencode` @@ -314,17 +285,12 @@ it is enough to import the trait and call the `from_bencode()` function on the o ```rust use bendy::decoding::{FromBencode, Error}; -fn main() {} +let encoded = b"l5:hello5:worlde".to_vec(); +let decoded = Vec::::from_bencode(&encoded)?; -#[test] -fn decode_vector() -> Result<(), Error> { - let encoded = b"l5:hello5:worlde".to_vec(); - let decoded = Vec::::from_bencode(&encoded)?; - - assert_eq!(vec!["hello", "world"], decoded); - Ok(()) -} +assert_eq!(vec!["hello", "world"], decoded); +Ok::<(), Error>(()) ``` ### Implementing `FromBencode` @@ -373,20 +339,15 @@ impl FromBencode for IntegerWrapper { } } -fn main() {} - -#[test] -fn decode_integer() -> Result<(), Error> { - let encoded = b"i21e".to_vec(); +let encoded = b"i21e".to_vec(); - let example = IntegerWrapper::from_bencode(&encoded)?; - assert_eq!(IntegerWrapper(21), example); +let example = IntegerWrapper::from_bencode(&encoded)?; +assert_eq!(IntegerWrapper(21), example); - let example = i64::from_bencode(&encoded)?; - assert_eq!(21, example); +let example = i64::from_bencode(&encoded)?; +assert_eq!(21, example); - Ok(()) -} +Ok::<(), Error>(()) ``` **Decode a byte string** @@ -414,20 +375,15 @@ impl FromBencode for StringWrapper { } } -fn main() {} +let encoded = b"7:content".to_vec(); -#[test] -fn decode_string() -> Result<(), Error> { - let encoded = b"7:content".to_vec(); +let example = StringWrapper::from_bencode(&encoded)?; +assert_eq!(StringWrapper("content".to_string()), example); - let example = StringWrapper::from_bencode(&encoded)?; - assert_eq!(StringWrapper("content".to_string()), example); +let example = String::from_bencode(&encoded)?; +assert_eq!("content".to_string(), example); - let example = String::from_bencode(&encoded)?; - assert_eq!("content".to_string(), example); - - Ok(()) -} +Ok::<(), Error>(()) ``` If the content is a non utf8 encoded string or an actual byte sequence the @@ -452,20 +408,15 @@ impl FromBencode for ByteStringWrapper { } } -fn main() {} +let encoded = b"7:content".to_vec(); -#[test] -fn decode_byte_string() -> Result<(), Error> { - let encoded = b"7:content".to_vec(); +let example = ByteStringWrapper::from_bencode(&encoded)?; +assert_eq!(ByteStringWrapper(b"content".to_vec()), example); - let example = ByteStringWrapper::from_bencode(&encoded)?; - assert_eq!(ByteStringWrapper(b"content".to_vec()), example); +let example = AsString::from_bencode(&encoded)?; +assert_eq!(b"content".to_vec(), example.0); - let example = AsString::from_bencode(&encoded)?; - assert_eq!(b"content".to_vec(), example.0); - - Ok(()) -} +Ok::<(), Error>(()) ``` **Decode a dictionary** @@ -522,18 +473,13 @@ impl FromBencode for Example { } } -fn main() {} +let encoded = b"d7:counteri0e5:label7:Examplee".to_vec(); +let expected = Example { label: "Example".to_string(), counter: 0 }; -#[test] -fn decode_dictionary() -> Result<(), Error> { - let encoded = b"d7:counteri0e5:label7:Examplee".to_vec(); - let expected = Example { label: "Example".to_string(), counter: 0 }; +let example = Example::from_bencode(&encoded)?; +assert_eq!(expected, example); - let example = Example::from_bencode(&encoded)?; - assert_eq!(expected, example); - - Ok(()) -} +Ok::<(), Error>(()) ``` **Decode a list** @@ -563,18 +509,13 @@ impl FromBencode for Location { } } -fn main() {} +let encoded = b"li2ei3ee".to_vec(); +let expected = Location(2, 3); -#[test] -fn decode_list() -> Result<(), Error> { - let encoded = b"li2ei3ee".to_vec(); - let expected = Location(2, 3); +let example = Location::from_bencode(&encoded)?; +assert_eq!(expected, example); - let example = Location::from_bencode(&encoded)?; - assert_eq!(expected, example); - - Ok(()) -} +Ok::<(), Error>(()) ``` ### Optional: Limitation of recursive parsing @@ -617,22 +558,32 @@ respectively: ```rust -use serde::{Deserialize, Serialize}; +# #[cfg(not(feature = "serde"))] +# fn main() {} +# #[cfg(feature = "serde")] +# fn main() -> Result<(), bendy::serde::Error> { +use serde_derive::{Deserialize, Serialize}; + +#[serde(crate = "serde_")] #[derive(Serialize, Deserialize, PartialEq, Debug)] struct Foo { bar: String, } -fn main() { - let value = Foo { - bar: "hello".into(), - }; - let bencode = bendy::serde::to_bytes(&value).unwrap(); - assert_eq!(bencode, b"d3:bar5:helloe"); - let deserialized = bendy::serde::from_bytes::(&bencode).unwrap(); - assert_eq!(deserialized, value); -} +let value = Foo { + bar: "hello".into(), +}; + +let bencode = bendy::serde::to_bytes(&value)?; +assert_eq!(bencode, b"d3:bar5:helloe"); + +let deserialized = bendy::serde::from_bytes::(&bencode)?; +assert_eq!(deserialized, value); + +Ok(()) + +# } ``` Information on how Rust types are represented in bencode is available in the diff --git a/rustfmt.toml b/rustfmt.toml index c22222a..9619212 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,6 +1,5 @@ unstable_features = true -required_version = "1.4.26" edition = "2018" format_code_in_doc_comments = true diff --git a/src/lib.rs b/src/lib.rs index 58b35e5..b6e554d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,3 +21,9 @@ pub mod state_tracker; pub mod serde; pub mod value; + +#[rustversion::since(1.40)] +const _: () = { + #[cfg(doctest)] + doc_comment::doctest!("../README.md"); +}; diff --git a/tests/readme.rs b/tests/readme.rs deleted file mode 100644 index 35b342f..0000000 --- a/tests/readme.rs +++ /dev/null @@ -1,365 +0,0 @@ -// Please keep the code below in sync with `README.md`. -// -// If `cfg(doctest)` gets stablized or `cfg(test)` gets fixed, we can use -// doc-comment for running tests in `README.md`. - -mod encoding_1 { - use bendy::encoding::{Error, ToBencode}; - - #[test] - fn encode_vector() -> Result<(), Error> { - let my_data = vec!["hello", "world"]; - let encoded = my_data.to_bencode()?; - - assert_eq!(b"l5:hello5:worlde", encoded.as_slice()); - Ok(()) - } -} - -mod encoding_2 { - use bendy::encoding::{Error, SingleItemEncoder, ToBencode}; - - struct IntegerWrapper(i64); - - impl ToBencode for IntegerWrapper { - const MAX_DEPTH: usize = 0; - - fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { - encoder.emit_int(self.0) - } - } - - #[test] - fn encode_integer() -> Result<(), Error> { - let example = IntegerWrapper(21); - - let encoded = example.to_bencode()?; - assert_eq!(b"i21e", encoded.as_slice()); - - let encoded = 21.to_bencode()?; - assert_eq!(b"i21e", encoded.as_slice()); - - Ok(()) - } -} - -mod encoding_3 { - use bendy::encoding::{Error, SingleItemEncoder, ToBencode}; - - struct StringWrapper(String); - - impl ToBencode for StringWrapper { - const MAX_DEPTH: usize = 0; - - fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { - encoder.emit_str(&self.0) - } - } - - #[test] - fn encode_string() -> Result<(), Error> { - let example = StringWrapper("content".to_string()); - - let encoded = example.to_bencode()?; - assert_eq!(b"7:content", encoded.as_slice()); - - let encoded = "content".to_bencode()?; - assert_eq!(b"7:content", encoded.as_slice()); - - Ok(()) - } -} - -mod encoding_4 { - use bendy::encoding::{AsString, Error, SingleItemEncoder, ToBencode}; - - struct ByteStringWrapper(Vec); - - impl ToBencode for ByteStringWrapper { - const MAX_DEPTH: usize = 0; - - fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { - let content = AsString(&self.0); - encoder.emit(&content) - } - } - - #[test] - fn encode_byte_string() -> Result<(), Error> { - let example = ByteStringWrapper(b"content".to_vec()); - - let encoded = example.to_bencode()?; - assert_eq!(b"7:content", encoded.as_slice()); - - let encoded = AsString(b"content").to_bencode()?; - assert_eq!(b"7:content", encoded.as_slice()); - - Ok(()) - } -} - -mod encoding_5 { - use bendy::encoding::{Error, SingleItemEncoder, ToBencode}; - - struct Example { - label: String, - counter: u64, - } - - impl ToBencode for Example { - const MAX_DEPTH: usize = 1; - - fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { - encoder.emit_dict(|mut e| { - e.emit_pair(b"counter", &self.counter)?; - e.emit_pair(b"label", &self.label)?; - - Ok(()) - }) - } - } - - #[test] - fn encode_dictionary() -> Result<(), Error> { - let example = Example { - label: "Example".to_string(), - counter: 0, - }; - - let encoded = example.to_bencode()?; - assert_eq!(b"d7:counteri0e5:label7:Examplee", encoded.as_slice()); - - Ok(()) - } -} - -mod encoding_6 { - use bendy::encoding::{Error, SingleItemEncoder, ToBencode}; - - struct Location(i64, i64); - - impl ToBencode for Location { - const MAX_DEPTH: usize = 1; - - fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { - encoder.emit_list(|e| { - e.emit_int(self.0)?; - e.emit_int(self.1) - }) - } - } - - #[test] - fn encode_list() -> Result<(), Error> { - let example = Location(2, 3); - - let encoded = example.to_bencode()?; - assert_eq!(b"li2ei3ee", encoded.as_slice()); - - Ok(()) - } -} - -mod decoding_1 { - use bendy::decoding::{Error, FromBencode}; - - #[test] - fn decode_vector() -> Result<(), Error> { - let encoded = b"l5:hello5:worlde".to_vec(); - let decoded = Vec::::from_bencode(&encoded)?; - - assert_eq!(vec!["hello", "world"], decoded); - Ok(()) - } -} - -mod decoding_2 { - use bendy::decoding::{Error, FromBencode, Object}; - - #[derive(Debug, Eq, PartialEq)] - struct IntegerWrapper(i64); - - impl FromBencode for IntegerWrapper { - const EXPECTED_RECURSION_DEPTH: usize = 0; - - fn decode_bencode_object(object: Object) -> Result { - // This is an example for content handling. It would also be possible - // to call `i64::decode_bencode_object(object)` directly. - let content = object.try_into_integer()?; - let number = content.parse::()?; - - Ok(IntegerWrapper(number)) - } - } - - #[test] - fn decode_integer() -> Result<(), Error> { - let encoded = b"i21e".to_vec(); - - let example = IntegerWrapper::from_bencode(&encoded)?; - assert_eq!(IntegerWrapper(21), example); - - let example = i64::from_bencode(&encoded)?; - assert_eq!(21, example); - - Ok(()) - } -} - -mod decoding_3 { - use bendy::decoding::{Error, FromBencode, Object}; - - #[derive(Debug, Eq, PartialEq)] - struct StringWrapper(String); - - impl FromBencode for StringWrapper { - const EXPECTED_RECURSION_DEPTH: usize = 0; - - fn decode_bencode_object(object: Object) -> Result { - // This is an example for content handling. It would also be possible - // to call `String::decode_bencode_object(object)` directly. - let content = object.try_into_bytes()?; - let content = String::from_utf8(content.to_vec())?; - - Ok(StringWrapper(content)) - } - } - - #[test] - fn decode_string() -> Result<(), Error> { - let encoded = b"7:content".to_vec(); - - let example = StringWrapper::from_bencode(&encoded)?; - assert_eq!(StringWrapper("content".to_string()), example); - - let example = String::from_bencode(&encoded)?; - assert_eq!("content".to_string(), example); - - Ok(()) - } -} - -mod decoding_4 { - use bendy::{ - decoding::{Error, FromBencode, Object}, - encoding::AsString, - }; - - #[derive(Debug, Eq, PartialEq)] - struct ByteStringWrapper(Vec); - - impl FromBencode for ByteStringWrapper { - const EXPECTED_RECURSION_DEPTH: usize = 0; - - fn decode_bencode_object(object: Object) -> Result { - let content = AsString::decode_bencode_object(object)?; - Ok(ByteStringWrapper(content.0)) - } - } - - #[test] - fn decode_byte_string() -> Result<(), Error> { - let encoded = b"7:content".to_vec(); - - let example = ByteStringWrapper::from_bencode(&encoded)?; - assert_eq!(ByteStringWrapper(b"content".to_vec()), example); - - let example = AsString::from_bencode(&encoded)?; - assert_eq!(b"content".to_vec(), example.0); - - Ok(()) - } -} - -mod decoding_5 { - use bendy::decoding::{Error, FromBencode, Object, ResultExt}; - - #[derive(Debug, Eq, PartialEq)] - struct Example { - label: String, - counter: u64, - } - - impl FromBencode for Example { - const EXPECTED_RECURSION_DEPTH: usize = 1; - - fn decode_bencode_object(object: Object) -> Result { - let mut counter = None; - let mut label = None; - - let mut dict = object.try_into_dictionary()?; - while let Some(pair) = dict.next_pair()? { - match pair { - (b"counter", value) => { - counter = u64::decode_bencode_object(value) - .context("counter") - .map(Some)?; - }, - (b"label", value) => { - label = String::decode_bencode_object(value) - .context("label") - .map(Some)?; - }, - (unknown_field, _) => { - return Err(Error::unexpected_field(String::from_utf8_lossy( - unknown_field, - ))); - }, - } - } - - let counter = counter.ok_or_else(|| Error::missing_field("counter"))?; - let label = label.ok_or_else(|| Error::missing_field("label"))?; - - Ok(Example { counter, label }) - } - } - - #[test] - fn decode_dictionary() -> Result<(), Error> { - let encoded = b"d7:counteri0e5:label7:Examplee".to_vec(); - let expected = Example { - label: "Example".to_string(), - counter: 0, - }; - - let example = Example::from_bencode(&encoded)?; - assert_eq!(expected, example); - - Ok(()) - } -} - -mod decoding_6 { - use bendy::decoding::{Error, FromBencode, Object}; - - #[derive(Debug, PartialEq, Eq)] - struct Location(i64, i64); - - impl FromBencode for Location { - const EXPECTED_RECURSION_DEPTH: usize = 1; - - fn decode_bencode_object(object: Object) -> Result { - let mut list = object.try_into_list()?; - - let x = list.next_object()?.ok_or(Error::missing_field("x"))?; - let x = i64::decode_bencode_object(x)?; - - let y = list.next_object()?.ok_or(Error::missing_field("y"))?; - let y = i64::decode_bencode_object(y)?; - - Ok(Location(x, y)) - } - } - - #[test] - fn decode_list() -> Result<(), Error> { - let encoded = b"li2ei3ee".to_vec(); - let expected = Location(2, 3); - - let example = Location::from_bencode(&encoded)?; - assert_eq!(expected, example); - - Ok(()) - } -}