torrust_tracker/servers/http/v1/responses/
scrape.rs1use std::borrow::Cow;
5
6use axum::http::StatusCode;
7use axum::response::{IntoResponse, Response};
8use torrust_tracker_contrib_bencode::{ben_int, ben_map, BMutAccess};
9
10use crate::core::ScrapeData;
11
12#[derive(Debug, PartialEq, Default)]
44pub struct Bencoded {
45 scrape_data: ScrapeData,
47}
48
49impl Bencoded {
50 #[must_use]
56 pub fn body(&self) -> Vec<u8> {
57 let mut scrape_list = ben_map!();
58
59 let scrape_list_mut = scrape_list.dict_mut().unwrap();
60
61 for (info_hash, value) in &self.scrape_data.files {
62 scrape_list_mut.insert(
63 Cow::from(info_hash.bytes().to_vec()),
64 ben_map! {
65 "complete" => ben_int!(i64::from(value.complete)),
66 "downloaded" => ben_int!(i64::from(value.downloaded)),
67 "incomplete" => ben_int!(i64::from(value.incomplete))
68 },
69 );
70 }
71
72 (ben_map! {
73 "files" => scrape_list
74 })
75 .encode()
76 }
77}
78
79impl From<ScrapeData> for Bencoded {
80 fn from(scrape_data: ScrapeData) -> Self {
81 Self { scrape_data }
82 }
83}
84
85impl IntoResponse for Bencoded {
86 fn into_response(self) -> Response {
87 (StatusCode::OK, self.body()).into_response()
88 }
89}
90
91#[cfg(test)]
92mod tests {
93
94 mod scrape_response {
95 use torrust_tracker_primitives::info_hash::InfoHash;
96 use torrust_tracker_primitives::swarm_metadata::SwarmMetadata;
97
98 use crate::core::ScrapeData;
99 use crate::servers::http::v1::responses::scrape::Bencoded;
100
101 fn sample_scrape_data() -> ScrapeData {
102 let info_hash = InfoHash::from_bytes(&[0x69; 20]);
103 let mut scrape_data = ScrapeData::empty();
104 scrape_data.add_file(
105 &info_hash,
106 SwarmMetadata {
107 complete: 1,
108 downloaded: 2,
109 incomplete: 3,
110 },
111 );
112 scrape_data
113 }
114
115 #[test]
116 fn should_be_converted_from_scrape_data() {
117 let response = Bencoded::from(sample_scrape_data());
118
119 assert_eq!(
120 response,
121 Bencoded {
122 scrape_data: sample_scrape_data()
123 }
124 );
125 }
126
127 #[test]
128 fn should_be_bencoded() {
129 let response = Bencoded {
130 scrape_data: sample_scrape_data(),
131 };
132
133 let bytes = response.body();
134
135 let expected_bytes = b"d5:filesd20:iiiiiiiiiiiiiiiiiiiid8:completei1e10:downloadedi2e10:incompletei3eeee";
137
138 assert_eq!(
139 String::from_utf8(bytes).unwrap(),
140 String::from_utf8(expected_bytes.to_vec()).unwrap()
141 );
142 }
143 }
144}