forked from torrust/torrust-tracker
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscrape.rs
More file actions
135 lines (118 loc) · 3.9 KB
/
Copy pathscrape.rs
File metadata and controls
135 lines (118 loc) · 3.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! `Scrape` response for the HTTP tracker [`scrape`](crate::v1::requests::scrape::Scrape) request.
//!
//! Data structures and logic to build the `scrape` response.
use std::borrow::Cow;
use torrust_tracker_contrib_bencode::{ben_int, ben_map, BMutAccess};
use torrust_tracker_primitives::core::ScrapeData;
/// The `Scrape` response for the HTTP tracker.
///
/// ```rust
/// use bittorrent_http_tracker_protocol::v1::responses::scrape::Bencoded;
/// use bittorrent_primitives::info_hash::InfoHash;
/// use torrust_tracker_primitives::swarm_metadata::SwarmMetadata;
/// use torrust_tracker_primitives::core::ScrapeData;
///
/// let info_hash = InfoHash::from_bytes(&[0x69; 20]);
/// let mut scrape_data = ScrapeData::empty();
/// scrape_data.add_file(
/// &info_hash,
/// SwarmMetadata {
/// complete: 1,
/// downloaded: 2,
/// incomplete: 3,
/// },
/// );
///
/// let response = Bencoded::from(scrape_data);
///
/// let bytes = response.body();
///
/// // cspell:disable-next-line
/// let expected_bytes = b"d5:filesd20:iiiiiiiiiiiiiiiiiiiid8:completei1e10:downloadedi2e10:incompletei3eeee";
///
/// assert_eq!(
/// String::from_utf8(bytes).unwrap(),
/// String::from_utf8(expected_bytes.to_vec()).unwrap()
/// );
/// ```
#[derive(Debug, PartialEq, Default)]
pub struct Bencoded {
/// The scrape data to be bencoded.
scrape_data: ScrapeData,
}
impl Bencoded {
/// Returns the bencoded representation of the `Scrape` struct.
///
/// # Panics
///
/// Will return an error if it can't access the bencode as a mutable `BDictAccess`.
#[must_use]
pub fn body(&self) -> Vec<u8> {
let mut scrape_list = ben_map!();
let scrape_list_mut = scrape_list.dict_mut().unwrap();
for (info_hash, value) in &self.scrape_data.files {
scrape_list_mut.insert(
Cow::from(info_hash.bytes().to_vec()),
ben_map! {
"complete" => ben_int!(i64::from(value.complete)),
"downloaded" => ben_int!(i64::from(value.downloaded)),
"incomplete" => ben_int!(i64::from(value.incomplete))
},
);
}
(ben_map! {
"files" => scrape_list
})
.encode()
}
}
impl From<ScrapeData> for Bencoded {
fn from(scrape_data: ScrapeData) -> Self {
Self { scrape_data }
}
}
#[cfg(test)]
mod tests {
mod scrape_response {
use bittorrent_primitives::info_hash::InfoHash;
use torrust_tracker_primitives::core::ScrapeData;
use torrust_tracker_primitives::swarm_metadata::SwarmMetadata;
use crate::v1::responses::scrape::Bencoded;
fn sample_scrape_data() -> ScrapeData {
let info_hash = InfoHash::from_bytes(&[0x69; 20]);
let mut scrape_data = ScrapeData::empty();
scrape_data.add_file(
&info_hash,
SwarmMetadata {
complete: 1,
downloaded: 2,
incomplete: 3,
},
);
scrape_data
}
#[test]
fn should_be_converted_from_scrape_data() {
let response = Bencoded::from(sample_scrape_data());
assert_eq!(
response,
Bencoded {
scrape_data: sample_scrape_data()
}
);
}
#[test]
fn should_be_bencoded() {
let response = Bencoded {
scrape_data: sample_scrape_data(),
};
let bytes = response.body();
// cspell:disable-next-line
let expected_bytes = b"d5:filesd20:iiiiiiiiiiiiiiiiiiiid8:completei1e10:downloadedi2e10:incompletei3eeee";
assert_eq!(
String::from_utf8(bytes).unwrap(),
String::from_utf8(expected_bytes.to_vec()).unwrap()
);
}
}
}