Skip to content

Commit fe14598

Browse files
committed
refactor(http-protocol): restructure scrape responses into layered modules
Flat scrape.rs and scrape_deserialization.rs replaced by scrape/{data,encoding,deserialization}.rs directory module. - data.rs - DTO layer: SwarmMetadata, ScrapeData - encoding.rs - Encoding layer: Bencoded struct + tests - deserialization.rs - Client-side: Response, File, ResponseBuilder, BencodeParseError - Deleted both old flat files - Updated all 7 import sites - All linters and tests pass - Progress: T12-T14 done, spec updated
1 parent 548659b commit fe14598

12 files changed

Lines changed: 65 additions & 54 deletions

File tree

console/tracker-client/src/console/clients/checker/checks/http.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use torrust_tracker_client::http::client::Client;
77
use torrust_tracker_http_protocol::v1::requests::announce::AnnounceBuilder;
88
use torrust_tracker_http_protocol::v1::requests::scrape_builder;
99
use torrust_tracker_http_protocol::v1::responses::announce::deserialization::DeserializedNormal;
10-
use torrust_tracker_http_protocol::v1::responses::scrape_deserialization;
10+
use torrust_tracker_http_protocol::v1::responses::scrape::deserialization;
1111
use url::Url;
1212

1313
use crate::console::clients::http::Error;
@@ -83,7 +83,7 @@ async fn check_http_announce(url: &Url, timeout: Duration) -> Result<Deserialize
8383
Ok(response)
8484
}
8585

86-
async fn check_http_scrape(url: &Url, timeout: Duration) -> Result<scrape_deserialization::Response, Error> {
86+
async fn check_http_scrape(url: &Url, timeout: Duration) -> Result<deserialization::Response, Error> {
8787
let info_hashes: Vec<String> = vec!["9c38422213e30bff212b30c360d26f9a02136422".to_string()]; // DevSkim: ignore DS173237
8888
let query = scrape_builder::Query::try_from(info_hashes).expect("a valid array of info-hashes is required");
8989

@@ -93,7 +93,7 @@ async fn check_http_scrape(url: &Url, timeout: Duration) -> Result<scrape_deseri
9393

9494
let response = response.bytes().await.map_err(|e| Error::ResponseError { err: e.into() })?;
9595

96-
let response = scrape_deserialization::Response::try_from_bencoded(&response).map_err(|e| Error::BencodeParseError {
96+
let response = deserialization::Response::try_from_bencoded(&response).map_err(|e| Error::BencodeParseError {
9797
data: response,
9898
err: e.into(),
9999
})?;

console/tracker-client/src/console/clients/http/app.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ use torrust_tracker_client::http::client::Client;
8282
use torrust_tracker_http_protocol::v1::requests::announce::{AnnounceBuilder, Compact, Event};
8383
use torrust_tracker_http_protocol::v1::requests::scrape_builder;
8484
use torrust_tracker_http_protocol::v1::responses::announce::deserialization::{DeserializedCompact, DeserializedNormal};
85-
use torrust_tracker_http_protocol::v1::responses::scrape_deserialization;
85+
use torrust_tracker_http_protocol::v1::responses::scrape::deserialization;
8686

8787
use crate::DEFAULT_NETWORK_TIMEOUT;
8888

@@ -345,7 +345,7 @@ async fn scrape_command(
345345

346346
let body = response.bytes().await?;
347347

348-
let Ok(scrape_response) = scrape_deserialization::Response::try_from_bencoded(&body) else {
348+
let Ok(scrape_response) = deserialization::Response::try_from_bencoded(&body) else {
349349
let fallback = bencode_to_fallback_json_or_raw_bytes(&body, output_format)
350350
.context("failed to serialize fallback scrape response into JSON")?;
351351

console/tracker-client/src/console/clients/http/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::sync::Arc;
22

33
use serde::Serialize;
44
use thiserror::Error;
5-
use torrust_tracker_http_protocol::v1::responses::scrape_deserialization::BencodeParseError;
5+
use torrust_tracker_http_protocol::v1::responses::scrape::deserialization::BencodeParseError;
66

77
pub mod app;
88

console/tracker-client/src/console/clients/unified/http.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use torrust_tracker_client::http::client::Client;
1212
use torrust_tracker_http_protocol::v1::requests::announce::{AnnounceBuilder, Compact, Event};
1313
use torrust_tracker_http_protocol::v1::requests::scrape_builder;
1414
use torrust_tracker_http_protocol::v1::responses::announce::deserialization::{DeserializedCompact, DeserializedNormal};
15-
use torrust_tracker_http_protocol::v1::responses::scrape_deserialization;
15+
use torrust_tracker_http_protocol::v1::responses::scrape::deserialization;
1616

1717
use super::app::OutputFormat;
1818
use crate::DEFAULT_NETWORK_TIMEOUT;
@@ -218,7 +218,7 @@ async fn scrape_command(
218218

219219
let body = response.bytes().await?;
220220

221-
let Ok(scrape_response) = scrape_deserialization::Response::try_from_bencoded(&body) else {
221+
let Ok(scrape_response) = deserialization::Response::try_from_bencoded(&body) else {
222222
let fallback = bencode_to_fallback_json_or_raw_bytes(&body, output_format)
223223
.context("failed to serialize fallback scrape response into JSON")?;
224224

docs/issues/open/1965-1669-si-34-consolidate-duplicate-http-types/ISSUE.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -226,9 +226,9 @@ Status values: `TODO`, `IN_PROGRESS`, `BLOCKED`, `DONE`.
226226
| | | **Response-side restructuring (DD7 + DD8)** | |
227227
| T10 | DONE | Restructure announce responses into layered module | Created `announce/{data,encoding}.rs` with `mod.rs` re-exports. Deleted old `announce.rs`. No call sites needed updating (backward compatible) |
228228
| T11 | DONE | Partial merge of announce DTO layer | Moved deserialization types into `announce/deserialization.rs`. Renamed `Announce``DeserializedNormal`, `Compact``DeserializedCompactParsed`. Added `peers6` to `DeserializedCompact`. Replaced `CompactPeer` (IPv4-only struct) with shared `encoding::CompactPeer` enum. Deleted `announce_deserialization.rs`. Updated 8 import sites. |
229-
| T12 | TODO | Restructure scrape responses into layered module | Same pattern: `scrape/{data,encoding,deserialization}.rs` |
230-
| T13 | TODO | Partial merge of scrape DTO layer | Same pattern as announce |
231-
| T14 | TODO | Update all call sites for restructured response types | Update imports in tracker-client, axum-http-server tests, CLI apps |
229+
| T12 | DONE | Restructure scrape responses into layered module | Created `scrape/{data,encoding,deserialization}.rs`. Deleted `scrape.rs` and `scrape_deserialization.rs`. Updated 7 import sites. Backward compatible re-exports. |
230+
| T13 | DONE | Partial merge of scrape DTO layer | Merged `scrape_deserialization.rs` into `scrape/deserialization.rs`. Updated all import sites. Done together with T12. |
231+
| T14 | DONE | Update all call sites for restructured response types | Updated all import sites for both announce and scrape restructuring. Done together with T10-T13. |
232232
| T15 | TODO | Run full verification after response restructure | `linter all`, `cargo test --workspace`, pre-commit, pre-push |
233233
| | | **Finalization** | |
234234
| T16 | TODO | Replace duplicate HTTP test client (DD9) | Remove `packages/axum-http-server/tests/server/client.rs`; update tests to use `tracker-client` package |
@@ -260,6 +260,7 @@ Status values: `TODO`, `IN_PROGRESS`, `BLOCKED`, `DONE`.
260260
- 2026-07-14 10:00 UTC - Copilot - Implementation (T7-T9) completed: merged `announce_builder::Query` into `Announce`, updated all call sites, all verifications passed.
261261
- 2026-07-14 14:00 UTC - Copilot - Implementation (T10) completed: restructured announce responses into `announce/{data,encoding}.rs` layered module.
262262
- 2026-07-14 15:00 UTC - Copilot - Implementation (T11) completed: partial merge of announce DTO layer into `announce/deserialization.rs`.
263+
- 2026-07-14 16:00 UTC - Copilot - Implementation (T12-T14) completed: restructured scrape responses into `scrape/{data,encoding,deserialization}.rs`, merged DTO layer, updated all import sites.
263264

264265
## Acceptance Criteria
265266

packages/axum-http-server/tests/server/asserts.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use torrust_tracker_http_protocol::v1::responses::announce::deserialization::{
55
DeserializedCompact, DeserializedCompactParsed, DeserializedNormal,
66
};
77
use torrust_tracker_http_protocol::v1::responses::error::Error;
8-
use torrust_tracker_http_protocol::v1::responses::scrape_deserialization;
8+
use torrust_tracker_http_protocol::v1::responses::scrape::deserialization;
99

1010
pub fn assert_bencoded_error(response_text: &String, expected_failure_reason: &str, location: &'static Location<'static>) {
1111
let error_failure_reason = serde_bencode::from_str::<Error>(response_text)
@@ -59,10 +59,10 @@ pub async fn assert_compact_announce_response(response: Response, expected_respo
5959
/// ```text
6060
/// b"d5:filesd20:\x9c8B\"\x13\xe3\x0b\xff!+0\xc3`\xd2o\x9a\x02\x13d\"d8:completei1e10:downloadedi0e10:incompletei0eeee"
6161
/// ```
62-
pub async fn assert_scrape_response(response: Response, expected_response: &scrape_deserialization::Response) {
62+
pub async fn assert_scrape_response(response: Response, expected_response: &deserialization::Response) {
6363
assert_eq!(response.status(), 200);
6464

65-
let scrape_response = scrape_deserialization::Response::try_from_bencoded(&response.bytes().await.unwrap()).unwrap();
65+
let scrape_response = deserialization::Response::try_from_bencoded(&response.bytes().await.unwrap()).unwrap();
6666

6767
assert_eq!(scrape_response, *expected_response);
6868
}

packages/axum-http-server/tests/server/v1/contract.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,7 +1145,7 @@ mod for_all_config_modes {
11451145
use torrust_info_hash::InfoHash;
11461146
use torrust_tracker_axum_http_server::testing::environment::Started;
11471147
use torrust_tracker_http_protocol::v1::requests::scrape_builder::QueryBuilder;
1148-
use torrust_tracker_http_protocol::v1::responses::scrape_deserialization::{self, File, ResponseBuilder};
1148+
use torrust_tracker_http_protocol::v1::responses::scrape::deserialization::{self, File, ResponseBuilder};
11491149
use torrust_tracker_primitives::PeerId;
11501150
use torrust_tracker_primitives::peer::fixture::PeerBuilder;
11511151
use torrust_tracker_test_helpers::{configuration, logging};
@@ -1288,11 +1288,7 @@ mod for_all_config_modes {
12881288
.scrape(&QueryBuilder::default().with_one_info_hash(&info_hash).query())
12891289
.await;
12901290

1291-
assert_scrape_response(
1292-
response,
1293-
&scrape_deserialization::Response::with_one_file(info_hash, File::zeroed()),
1294-
)
1295-
.await;
1291+
assert_scrape_response(response, &deserialization::Response::with_one_file(info_hash, File::zeroed())).await;
12961292

12971293
env.stop().await;
12981294
}
@@ -1467,7 +1463,7 @@ mod configured_as_whitelisted {
14671463
use torrust_info_hash::InfoHash;
14681464
use torrust_tracker_axum_http_server::testing::environment::Started;
14691465
use torrust_tracker_http_protocol::v1::requests::scrape_builder::QueryBuilder;
1470-
use torrust_tracker_http_protocol::v1::responses::scrape_deserialization::{File, ResponseBuilder};
1466+
use torrust_tracker_http_protocol::v1::responses::scrape::deserialization::{File, ResponseBuilder};
14711467
use torrust_tracker_primitives::PeerId;
14721468
use torrust_tracker_primitives::peer::fixture::PeerBuilder;
14731469
use torrust_tracker_test_helpers::logging::logs_contains_a_line_with;
@@ -1678,7 +1674,7 @@ mod configured_as_private {
16781674
use torrust_tracker_axum_http_server::testing::environment::Started;
16791675
use torrust_tracker_core::authentication::Key;
16801676
use torrust_tracker_http_protocol::v1::requests::scrape_builder::QueryBuilder;
1681-
use torrust_tracker_http_protocol::v1::responses::scrape_deserialization::{File, ResponseBuilder};
1677+
use torrust_tracker_http_protocol::v1::responses::scrape::deserialization::{File, ResponseBuilder};
16821678
use torrust_tracker_primitives::PeerId;
16831679
use torrust_tracker_primitives::peer::fixture::PeerBuilder;
16841680
use torrust_tracker_test_helpers::{configuration, logging};

packages/http-protocol/src/v1/responses/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,5 @@
22
pub mod announce;
33
pub mod error;
44
pub mod scrape;
5-
pub mod scrape_deserialization;
65

76
pub use announce::{Announce, Compact, Normal};
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//! Data types for the `Scrape` response.
2+
//!
3+
//! These protocol DTOs intentionally mirror some domain fields but must remain
4+
//! protocol-owned. Keeping this type local avoids protocol->domain coupling and
5+
//! confines translation to boundary adapters.
6+
use std::collections::BTreeMap;
7+
8+
use torrust_info_hash::InfoHash;
9+
10+
// Intentional boundary duplication: this represents scrape response payload
11+
// semantics for the HTTP protocol crate, not tracker-domain semantics.
12+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
13+
pub struct SwarmMetadata {
14+
pub complete: u32,
15+
pub downloaded: u32,
16+
pub incomplete: u32,
17+
}
18+
19+
// Intentional boundary duplication: this represents scrape response payload
20+
// semantics for the HTTP protocol crate, not tracker-domain semantics.
21+
#[derive(Clone, Debug, PartialEq, Default)]
22+
pub struct ScrapeData {
23+
pub files: BTreeMap<InfoHash, SwarmMetadata>,
24+
}
25+
26+
impl ScrapeData {
27+
#[must_use]
28+
pub fn empty() -> Self {
29+
Self::default()
30+
}
31+
32+
pub fn add_file(&mut self, info_hash: &InfoHash, swarm_metadata: SwarmMetadata) {
33+
self.files.insert(*info_hash, swarm_metadata);
34+
}
35+
}

packages/http-protocol/src/v1/responses/scrape_deserialization.rs renamed to packages/http-protocol/src/v1/responses/scrape/deserialization.rs

File renamed without changes.

0 commit comments

Comments
 (0)