summaryrefslogtreecommitdiffstats
path: root/rust/vendor/bendy/examples
diff options
context:
space:
mode:
Diffstat (limited to 'rust/vendor/bendy/examples')
-rw-r--r--rust/vendor/bendy/examples/decode_torrent.rs212
-rw-r--r--rust/vendor/bendy/examples/encode_torrent.rs115
-rw-r--r--rust/vendor/bendy/examples/torrent_files/debian-9.4.0-amd64-netinst.iso.torrentbin0 -> 23821 bytes
-rw-r--r--rust/vendor/bendy/examples/torrent_files/pieces.isobin0 -> 23280 bytes
4 files changed, 327 insertions, 0 deletions
diff --git a/rust/vendor/bendy/examples/decode_torrent.rs b/rust/vendor/bendy/examples/decode_torrent.rs
new file mode 100644
index 0000000..3960a89
--- /dev/null
+++ b/rust/vendor/bendy/examples/decode_torrent.rs
@@ -0,0 +1,212 @@
+//! A decoder for torrent files.
+//!
+//! This example will ...
+//!
+//! - read a torrent file,
+//! - deserialize the bencode formatted information
+//! - and print the result into stdout.
+//!
+//! *Attention*: Please consider to pipe the output into a file of your choice.
+//!
+//! # Run the Example
+//!
+//! ```
+//! cargo run --example decode_torrent > parsing_output.txt
+//! ```
+
+use bendy::{
+ decoding::{Error, FromBencode, Object, ResultExt},
+ encoding::AsString,
+};
+
+static EXAMPLE_TORRENT: &[u8] =
+ include_bytes!("torrent_files/debian-9.4.0-amd64-netinst.iso.torrent");
+
+/// Main struct containing all required information.
+///
+/// Based on: [http://fileformats.wikia.com/wiki/Torrent_file].
+///
+/// # Design Decision
+///
+/// To keep the example simple we won't parse the integers fields
+/// into a concrete number type as the bencode integer definition
+/// is actually a `BigNum` and the content may not fit.
+#[derive(Debug)]
+struct MetaInfo {
+ pub announce: String,
+ pub info: Info,
+ pub comment: Option<String>, // not official element
+ pub creation_date: Option<u64>, // not official element
+ pub http_seeds: Option<Vec<String>>, // not official element
+}
+
+/// File related information (Single-file format)
+#[derive(Debug)]
+struct Info {
+ pub piece_length: String,
+ pub pieces: Vec<u8>,
+ pub name: String,
+ pub file_length: String,
+}
+
+impl FromBencode for MetaInfo {
+ // Try to parse with a `max_depth` of two.
+ //
+ // The required max depth of a data structure is calculated as follows:
+ //
+ // - Every potential nesting level encoded as bencode dictionary or list count as +1,
+ // - everything else is ignored.
+ //
+ // This typically means that we only need to count the amount of nested structs and container
+ // types. (Potentially ignoring lists of bytes as they are normally encoded as strings.)
+ //
+ // struct MetaInfo { // encoded as dictionary (+1)
+ // announce: String,
+ // info: Info { // encoded as dictionary (+1)
+ // piece_length: String,
+ // pieces: Vec<u8>, // encoded as string and therefore ignored
+ // name: String,
+ // file_length: String,
+ // },
+ // comment: Option<String>,
+ // creation_date: Option<u64>,
+ // http_seeds: Option<Vec<String>> // if available encoded as list but even then doesn't
+ // increase the limit over the deepest chain including
+ // info
+ // }
+ const EXPECTED_RECURSION_DEPTH: usize = Info::EXPECTED_RECURSION_DEPTH + 1;
+
+ /// Entry point for decoding a torrent. The dictionary is parsed for all
+ /// non-optional and optional fields. Missing optional fields are ignored
+ /// but any other missing fields result in stopping the decoding and in
+ /// spawning [`DecodingError::MissingField`].
+ fn decode_bencode_object(object: Object) -> Result<Self, Error>
+ where
+ Self: Sized,
+ {
+ let mut announce = None;
+ let mut comment = None;
+ let mut creation_date = None;
+ let mut http_seeds = None;
+ let mut info = None;
+
+ let mut dict_dec = object.try_into_dictionary()?;
+ while let Some(pair) = dict_dec.next_pair()? {
+ match pair {
+ (b"announce", value) => {
+ announce = String::decode_bencode_object(value)
+ .context("announce")
+ .map(Some)?;
+ },
+ (b"comment", value) => {
+ comment = String::decode_bencode_object(value)
+ .context("comment")
+ .map(Some)?;
+ },
+ (b"creation date", value) => {
+ creation_date = u64::decode_bencode_object(value)
+ .context("creation_date")
+ .map(Some)?;
+ },
+ (b"httpseeds", value) => {
+ http_seeds = Vec::decode_bencode_object(value)
+ .context("http_seeds")
+ .map(Some)?;
+ },
+ (b"info", value) => {
+ info = Info::decode_bencode_object(value)
+ .context("info")
+ .map(Some)?;
+ },
+ (unknown_field, _) => {
+ return Err(Error::unexpected_field(String::from_utf8_lossy(
+ unknown_field,
+ )));
+ },
+ }
+ }
+
+ let announce = announce.ok_or_else(|| Error::missing_field("announce"))?;
+ let info = info.ok_or_else(|| Error::missing_field("info"))?;
+
+ Ok(MetaInfo {
+ announce,
+ info,
+ comment,
+ creation_date,
+ http_seeds,
+ })
+ }
+}
+
+impl FromBencode for Info {
+ const EXPECTED_RECURSION_DEPTH: usize = 1;
+
+ /// Treats object as dictionary containing all fields for the info struct.
+ /// On success the dictionary is parsed for the fields of info which are
+ /// necessary for torrent. Any missing field will result in a missing field
+ /// error which will stop the decoding.
+ fn decode_bencode_object(object: Object) -> Result<Self, Error>
+ where
+ Self: Sized,
+ {
+ let mut file_length = None;
+ let mut name = None;
+ let mut piece_length = None;
+ let mut pieces = None;
+
+ let mut dict_dec = object.try_into_dictionary()?;
+ while let Some(pair) = dict_dec.next_pair()? {
+ match pair {
+ (b"length", value) => {
+ file_length = value
+ .try_into_integer()
+ .context("file.length")
+ .map(ToString::to_string)
+ .map(Some)?;
+ },
+ (b"name", value) => {
+ name = String::decode_bencode_object(value)
+ .context("name")
+ .map(Some)?;
+ },
+ (b"piece length", value) => {
+ piece_length = value
+ .try_into_integer()
+ .context("length")
+ .map(ToString::to_string)
+ .map(Some)?;
+ },
+ (b"pieces", value) => {
+ pieces = AsString::decode_bencode_object(value)
+ .context("pieces")
+ .map(|bytes| Some(bytes.0))?;
+ },
+ (unknown_field, _) => {
+ return Err(Error::unexpected_field(String::from_utf8_lossy(
+ unknown_field,
+ )));
+ },
+ }
+ }
+
+ let file_length = file_length.ok_or_else(|| Error::missing_field("file_length"))?;
+ let name = name.ok_or_else(|| Error::missing_field("name"))?;
+ let piece_length = piece_length.ok_or_else(|| Error::missing_field("piece_length"))?;
+ let pieces = pieces.ok_or_else(|| Error::missing_field("pieces"))?;
+
+ // Check that we discovered all necessary fields
+ Ok(Info {
+ file_length,
+ name,
+ piece_length,
+ pieces,
+ })
+ }
+}
+
+fn main() -> Result<(), Error> {
+ let torrent = MetaInfo::from_bencode(EXAMPLE_TORRENT)?;
+ println!("{:#?}", torrent);
+ Ok(())
+}
diff --git a/rust/vendor/bendy/examples/encode_torrent.rs b/rust/vendor/bendy/examples/encode_torrent.rs
new file mode 100644
index 0000000..1e9b8c2
--- /dev/null
+++ b/rust/vendor/bendy/examples/encode_torrent.rs
@@ -0,0 +1,115 @@
+//! An encoder for torrent files
+//!
+//! This example will ...
+//!
+//! - serialize a torrent file representing object in bencode format
+//! - and print the result into stdout.
+//!
+//! *Attention*: Please consider to pipe the output into a file of your choice.
+//!
+//! # Run the Example
+//!
+//! ```
+//! cargo run --example encode_torrent > example.torrent
+//! ```
+
+use std::io::Write;
+
+use bendy::encoding::{AsString, Error as EncodingError, SingleItemEncoder, ToBencode};
+use failure::Error;
+
+/// Main struct containing all required information.
+///
+/// Based on: [http://fileformats.wikia.com/wiki/Torrent_file].
+///
+/// # Design Decision
+///
+/// To keep the example simple we won't parse the integers fields
+/// into a concrete number type as the bencode integer definition
+/// is actually a `BigNum` and the content may not fit.
+#[derive(Debug)]
+struct MetaInfo {
+ pub announce: String,
+ pub info: Info,
+ pub comment: Option<String>, // not official element
+ pub creation_date: Option<String>, // not official element
+ pub http_seeds: Option<Vec<String>>, // not official element
+}
+
+/// File related information (Single-file format)
+#[derive(Debug)]
+struct Info {
+ pub piece_length: String,
+ pub pieces: Vec<u8>,
+ pub name: String,
+ pub file_length: String,
+}
+
+impl ToBencode for MetaInfo {
+ // Adds an additional recursion level -- itself formatted as dictionary --
+ // around the info struct.
+ const MAX_DEPTH: usize = Info::MAX_DEPTH + 1;
+
+ fn encode(&self, encoder: SingleItemEncoder) -> Result<(), EncodingError> {
+ encoder.emit_dict(|mut e| {
+ e.emit_pair(b"announce", &self.announce)?;
+
+ if let Some(comment) = &self.comment {
+ e.emit_pair(b"comment", comment)?;
+ }
+
+ if let Some(creation_date) = &self.creation_date {
+ e.emit_pair(b"creation date", creation_date)?;
+ }
+
+ if let Some(seeds) = &self.http_seeds {
+ // List is a simple iterable wrapper that allows to encode
+ // any list like container as bencode list object.
+ e.emit_pair(b"httpseeds", seeds)?;
+ }
+
+ e.emit_pair(b"info", &self.info)
+ })?;
+
+ Ok(())
+ }
+}
+
+impl ToBencode for Info {
+ // The struct is encoded as dictionary and all of it internals are encoded
+ // as flat values, i.e. strings or integers.
+ const MAX_DEPTH: usize = 1;
+
+ fn encode(&self, encoder: SingleItemEncoder) -> Result<(), EncodingError> {
+ encoder.emit_dict(|mut e| {
+ e.emit_pair(b"length", &self.file_length)?;
+ e.emit_pair(b"name", &self.name)?;
+ e.emit_pair(b"piece length", &self.piece_length)?;
+ e.emit_pair(b"pieces", AsString(&self.pieces))
+ })?;
+ Ok(())
+ }
+}
+
+fn main() -> Result<(), Error> {
+ let torrent = MetaInfo {
+ announce: "http://bttracker.debian.org:6969/announce".to_owned(),
+ comment: Some("\"Debian CD from cdimage.debian.org\"".to_owned()),
+ creation_date: Some(1_520_682_848.to_string()),
+ http_seeds: Some(vec![
+ "https://cdimage.debian.org/cdimage/release/9.4.0//srv/cdbuilder.debian.org/dst/deb-cd/weekly-builds/amd64/iso-cd/debian-9.4.0-amd64-netinst.iso".to_owned(),
+ "https://cdimage.debian.org/cdimage/archive/9.4.0//srv/cdbuilder.debian.org/dst/deb-cd/weekly-builds/amd64/iso-cd/debian-9.4.0-amd64-netinst.iso".to_owned(),
+ ]),
+ info: Info {
+ piece_length: 262_144.to_string(),
+ pieces: include_bytes!("torrent_files/pieces.iso").to_vec(),
+ name: "debian-9.4.0-amd64-netinst.iso".to_owned(),
+ file_length: 305_135_616.to_string(),
+ },
+ };
+
+ let data = torrent.to_bencode()?;
+ std::io::stdout().write_all(&data)?;
+
+ Ok(())
+}
diff --git a/rust/vendor/bendy/examples/torrent_files/debian-9.4.0-amd64-netinst.iso.torrent b/rust/vendor/bendy/examples/torrent_files/debian-9.4.0-amd64-netinst.iso.torrent
new file mode 100644
index 0000000..5c8652e
--- /dev/null
+++ b/rust/vendor/bendy/examples/torrent_files/debian-9.4.0-amd64-netinst.iso.torrent
Binary files differ
diff --git a/rust/vendor/bendy/examples/torrent_files/pieces.iso b/rust/vendor/bendy/examples/torrent_files/pieces.iso
new file mode 100644
index 0000000..1b35971
--- /dev/null
+++ b/rust/vendor/bendy/examples/torrent_files/pieces.iso
Binary files differ