Skip to content

Commit f643b44

Browse files
committed
refactor(qbittorrent-e2e): introduce TorrentHash newtype for TorrentInfo::hash
Replace TorrentInfo::hash: String with TorrentHash(String). The type documents the 40-character lowercase hex SHA-1 invariant returned by the qBittorrent Web API, distinguishing it from other String fields such as the save path. A manual Deserialize impl follows the same pattern as TorrentProgress.
1 parent cf2faf4 commit f643b44

2 files changed

Lines changed: 46 additions & 2 deletions

File tree

src/console/ci/qbittorrent/qbittorrent_client.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use reqwest::multipart::{Form, Part};
77
use serde::Deserialize;
88
use tokio::sync::Mutex;
99

10-
use super::types::{TorrentProgress, TorrentState};
10+
use super::types::{TorrentHash, TorrentProgress, TorrentState};
1111

1212
const QBITTORRENT_WEBUI_PORT: u16 = 8080;
1313

@@ -73,7 +73,7 @@ pub struct QbittorrentClient {
7373

7474
#[derive(Debug, Deserialize)]
7575
pub struct TorrentInfo {
76-
pub hash: String,
76+
pub hash: TorrentHash,
7777
pub progress: TorrentProgress,
7878
pub state: TorrentState,
7979
}

src/console/ci/qbittorrent/types.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,3 +399,47 @@ impl fmt::Display for QbittorrentImage {
399399
f.write_str(&self.0)
400400
}
401401
}
402+
403+
/// A qBittorrent torrent hash — a 40-character lowercase hex-encoded SHA-1
404+
/// string, as returned by the `/api/v2/torrents/info` endpoint.
405+
///
406+
/// Distinct from the binary [`InfoHash`](primitives::InfoHash) type in the
407+
/// `primitives` package: the API delivers hex strings, not raw bytes. Wrapping
408+
/// it here documents the invariant and disambiguates the field from other
409+
/// [`String`] fields such as the torrent name or save path.
410+
#[derive(Debug, Clone)]
411+
pub struct TorrentHash(String);
412+
413+
impl TorrentHash {
414+
/// Creates a new [`TorrentHash`] from any value that converts into a [`String`].
415+
pub fn new(hash: impl Into<String>) -> Self {
416+
Self(hash.into())
417+
}
418+
419+
/// Returns the hash as a `&str`.
420+
#[must_use]
421+
pub fn as_str(&self) -> &str {
422+
&self.0
423+
}
424+
}
425+
426+
impl Deref for TorrentHash {
427+
type Target = str;
428+
429+
fn deref(&self) -> &Self::Target {
430+
&self.0
431+
}
432+
}
433+
434+
impl fmt::Display for TorrentHash {
435+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
436+
f.write_str(&self.0)
437+
}
438+
}
439+
440+
impl<'de> serde::Deserialize<'de> for TorrentHash {
441+
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
442+
let value = <String as serde::Deserialize>::deserialize(deserializer)?;
443+
Ok(Self(value))
444+
}
445+
}

0 commit comments

Comments
 (0)