gosh_dl/protocol/
types.rs1use serde::{Deserialize, Serialize};
6use uuid::Uuid;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub struct DownloadId(Uuid);
11
12impl DownloadId {
13 pub fn new() -> Self {
15 Self(Uuid::new_v4())
16 }
17
18 pub fn from_uuid(uuid: Uuid) -> Self {
20 Self(uuid)
21 }
22
23 pub fn as_uuid(&self) -> &Uuid {
25 &self.0
26 }
27
28 pub fn to_gid(&self) -> String {
31 hex::encode(&self.0.as_bytes()[0..8])
32 }
33
34 pub fn from_gid(gid: &str) -> Option<Self> {
36 if gid.len() != 16 {
37 return None;
38 }
39 let bytes = hex::decode(gid).ok()?;
40 if bytes.len() != 8 {
41 return None;
42 }
43 let mut uuid_bytes = [0u8; 16];
45 uuid_bytes[0..8].copy_from_slice(&bytes);
46 Some(Self(Uuid::from_bytes(uuid_bytes)))
47 }
48}
49
50impl Default for DownloadId {
51 fn default() -> Self {
52 Self::new()
53 }
54}
55
56impl std::fmt::Display for DownloadId {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 write!(f, "{}", self.to_gid())
59 }
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
64#[serde(rename_all = "lowercase")]
65pub enum DownloadKind {
66 Http,
68 Torrent,
70 Magnet,
72}
73
74#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
76#[serde(tag = "state", rename_all = "lowercase")]
77pub enum DownloadState {
78 Queued,
80 Connecting,
82 Downloading,
84 Seeding,
86 Paused,
88 Completed,
90 Error {
92 kind: String,
93 message: String,
94 retryable: bool,
95 },
96}
97
98impl DownloadState {
99 pub fn is_active(&self) -> bool {
101 matches!(self, Self::Downloading | Self::Seeding | Self::Connecting)
102 }
103
104 pub fn is_finished(&self) -> bool {
106 matches!(self, Self::Completed | Self::Error { .. })
107 }
108
109 pub fn to_aria2_status(&self) -> &'static str {
111 match self {
112 Self::Queued => "waiting",
113 Self::Connecting => "active",
114 Self::Downloading => "active",
115 Self::Seeding => "active",
116 Self::Paused => "paused",
117 Self::Completed => "complete",
118 Self::Error { .. } => "error",
119 }
120 }
121}
122
123#[derive(Debug, Clone, Default, Serialize, Deserialize)]
125pub struct DownloadProgress {
126 pub total_size: Option<u64>,
128 pub completed_size: u64,
130 pub download_speed: u64,
132 pub upload_speed: u64,
134 pub connections: u32,
136 pub seeders: u32,
138 pub peers: u32,
140 pub eta_seconds: Option<u64>,
142}
143
144impl DownloadProgress {
145 pub fn percentage(&self) -> f64 {
147 match self.total_size {
148 Some(total) if total > 0 => (self.completed_size as f64 / total as f64) * 100.0,
149 _ => 0.0,
150 }
151 }
152}
153
154mod hex {
156 pub fn encode(bytes: &[u8]) -> String {
157 bytes.iter().map(|b| format!("{:02x}", b)).collect()
158 }
159
160 pub fn decode(s: &str) -> Result<Vec<u8>, ()> {
161 if !s.len().is_multiple_of(2) {
162 return Err(());
163 }
164 (0..s.len())
165 .step_by(2)
166 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).map_err(|_| ()))
167 .collect()
168 }
169}