rubit_bencode/
torrent_file.rs

1use core::fmt;
2use std::{collections::HashMap, process::exit};
3
4use crate::{
5    decode::{decode_dict, BencodeTypes},
6    unwrap_announce_list, unwrap_dict, unwrap_info_hash, unwrap_integer, unwrap_pieces,
7    unwrap_string,
8};
9
10pub struct Info {
11    pub name: String,
12    pub length: u64,
13    pub piece_length: u64,
14    pub pieces: Vec<[u8; 20]>,
15}
16
17pub struct TorrentFile {
18    pub info_hash: [u8; 20],
19    pub announce: String,
20    pub announce_list: Option<Vec<Vec<String>>>,
21    pub created_by: Option<String>,
22    pub creation_date: Option<u64>,
23    pub encoding: Option<String>,
24    pub info: Info,
25}
26
27fn make_torrent_file<'a>(dict: &'a mut HashMap<String, BencodeTypes>) -> Option<TorrentFile> {
28    let info_hash = unwrap_info_hash(dict.remove("info_hash")?)?;
29    let announce = unwrap_string(dict.remove("announce")?)?;
30    let mut info_dict = unwrap_dict(dict.remove("info")?)?;
31
32    let name = unwrap_string(info_dict.remove("name")?)?;
33    let length = unwrap_integer(info_dict.remove("length")?)?;
34    let piece_length = unwrap_integer(info_dict.remove("piece length")?)?;
35    let pieces = unwrap_pieces(info_dict.remove("pieces")?)?;
36
37    let info = Info {
38        name,
39        length,
40        piece_length,
41        pieces,
42    };
43
44    let announce_list = match dict.remove("announce-list") {
45        Some(l) => unwrap_announce_list(l),
46        None => None,
47    };
48
49    let created_by = match dict.remove("created by") {
50        Some(s) => unwrap_string(s),
51        None => None,
52    };
53
54    let creation_date = match dict.remove("creation date") {
55        Some(i) => unwrap_integer(i),
56        None => None,
57    };
58
59    let encoding = match dict.remove("encoding") {
60        Some(s) => unwrap_string(s),
61        None => None,
62    };
63
64    Some(TorrentFile {
65        info_hash,
66        announce,
67        announce_list,
68        created_by,
69        creation_date,
70        encoding,
71        info,
72    })
73}
74
75impl From<Vec<u8>> for TorrentFile {
76    fn from(buf: Vec<u8>) -> Self {
77        let mut pointer = 0;
78        let mut dict = match decode_dict(&mut pointer, &buf) {
79            Ok(d) => d,
80            Err(e) => {
81                println!("bad torrent file!: {e:?}");
82                exit(1);
83            }
84        };
85
86        match make_torrent_file(&mut dict) {
87            Some(t) => t,
88            None => {
89                println!("Bad torrent file!");
90                println!("Is it a single-file torrent?");
91                exit(1);
92            }
93        }
94    }
95}
96
97impl fmt::Debug for TorrentFile {
98    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99        f.debug_struct("TorrentFile")
100            .field("info_hash", &self.info_hash)
101            .field("announce", &self.announce)
102            .field("announce-list", &self.announce_list)
103            .field("info", &self.info)
104            .finish()
105    }
106}
107
108impl fmt::Debug for Info {
109    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110        f.debug_struct("TorrentFile")
111            .field("name", &self.name)
112            .field("length", &self.length)
113            .field("piece length", &self.piece_length)
114            .field("pieces", &format!("too much to show!"))
115            .finish()
116    }
117}