rustybit_lib/
parser.rs

1use core::str;
2use std::borrow::Cow;
3
4use anyhow::Context;
5use serde::{Deserialize, Serialize};
6
7/// Multiple File Mode info
8#[derive(Clone, Debug, Serialize, Deserialize)]
9pub struct File<'a> {
10    /// Length of the file in bytes
11    pub length: u64,
12    /// MD5 sum of the file
13    #[serde(default, skip_serializing_if = "Option::is_none", borrow)]
14    pub md5sum: Option<Cow<'a, str>>,
15    /// A list containing one or more string elements that together represent the path and filename.
16    /// Each element in the list corresponds to either a directory name or the filename.
17    /// "dir1/dir2/file.ext" -> ["dir1", "dir2", "file.ext"]
18    #[serde(borrow)]
19    pub path: Vec<Cow<'a, str>>,
20}
21
22#[derive(Debug, Serialize, Deserialize)]
23pub struct Info<'a> {
24    /// A list of Files (Multi File Mode)
25    #[serde(default, skip_serializing_if = "Option::is_none")]
26    pub files: Option<Vec<File<'a>>>,
27    /// Length of the file in bytes (Single File Mode)
28    #[serde(default, skip_serializing_if = "Option::is_none")]
29    pub length: Option<u64>,
30    /// Filename (Single File Mode) / Name of the directory (Multi File Mode)
31    #[serde(borrow)]
32    pub name: Cow<'a, str>,
33    /// Number of bytes in each piece
34    #[serde(rename = "piece length")]
35    pub piece_length: usize,
36    /// Concatenated piece hashes (20-byte SHA1 hash values). Must be a multiple of 20
37    #[serde(with = "serde_bytes", borrow)]
38    pub pieces: Cow<'a, [u8]>,
39    /// External peer source (Can be either 0 or 1)
40    #[serde(default, skip_serializing_if = "Option::is_none")]
41    pub private: Option<u8>,
42    /// MD5 sum of the file (Single File Mode)
43    #[serde(default, skip_serializing_if = "Option::is_none", borrow)]
44    pub md5sum: Option<Cow<'a, str>>,
45}
46
47impl<'a> Info<'a> {
48    /// SHA1 Hash of bencoded self
49    pub fn hash(&self) -> anyhow::Result<[u8; 20]> {
50        let mut hasher = crypto_hash::Hasher::new(crypto_hash::Algorithm::SHA1);
51        serde_bencode::to_writer(self, &mut hasher).context("serializing torrent's Info failed")?;
52        let mut resulting_hash = [0u8; 20];
53        resulting_hash.copy_from_slice(&hasher.finish());
54        Ok(resulting_hash)
55    }
56}
57
58#[derive(Debug, Deserialize)]
59pub struct MetaInfo<'a> {
60    /// Description of the file(s) of the torrent
61    #[serde(borrow)]
62    pub info: Info<'a>,
63    /// The announce URL of the tracker
64    #[serde(borrow)]
65    pub announce: Cow<'a, str>,
66    /// The string encoding that is used in the info.pieces
67    #[serde(default, borrow)]
68    pub encoding: Option<Cow<'a, str>>,
69    /// A list of annouce URLs of trackers. Is used if the multitracker specification is supported
70    #[serde(default)]
71    #[serde(rename = "announce-list", borrow)]
72    pub announce_list: Option<Vec<Vec<Cow<'a, str>>>>,
73    /// The creation time of the torrent (UNIX epoch format)
74    #[serde(default)]
75    #[serde(rename = "creation date")]
76    pub creation_date: Option<u64>,
77    /// Free-form comments of the author
78    #[serde(default, borrow)]
79    pub comment: Option<Cow<'a, str>>,
80    /// Name and version of the program used to create the Metainfo file
81    #[serde(default)]
82    #[serde(rename = "created by", borrow)]
83    pub created_by: Option<Cow<'a, str>>,
84}