rubit_bencode/
torrent_file.rs1use 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}