oxihuman_core/
file_transfer_stub.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum TransferState {
10 Pending,
11 InProgress { bytes_sent: u64, total_bytes: u64 },
12 Completed,
13 Failed(String),
14}
15
16impl TransferState {
17 pub fn is_done(&self) -> bool {
18 matches!(self, TransferState::Completed | TransferState::Failed(_))
19 }
20
21 pub fn progress_pct(&self) -> f32 {
22 match self {
23 TransferState::InProgress {
24 bytes_sent,
25 total_bytes,
26 } => {
27 if *total_bytes == 0 {
28 1.0
29 } else {
30 *bytes_sent as f32 / *total_bytes as f32
31 }
32 }
33 TransferState::Completed => 1.0,
34 _ => 0.0,
35 }
36 }
37}
38
39#[derive(Debug, Clone)]
41pub struct TransferJob {
42 pub id: u64,
43 pub source: String,
44 pub destination: String,
45 pub state: TransferState,
46}
47
48impl TransferJob {
49 pub fn new(id: u64, source: &str, destination: &str, size_bytes: u64) -> Self {
50 TransferJob {
51 id,
52 source: source.to_string(),
53 destination: destination.to_string(),
54 state: TransferState::InProgress {
55 bytes_sent: 0,
56 total_bytes: size_bytes,
57 },
58 }
59 }
60
61 pub fn mark_complete(&mut self) {
62 self.state = TransferState::Completed;
63 }
64
65 pub fn mark_failed(&mut self, reason: &str) {
66 self.state = TransferState::Failed(reason.to_string());
67 }
68
69 pub fn advance(&mut self, bytes: u64) {
70 if let TransferState::InProgress {
71 ref mut bytes_sent,
72 total_bytes,
73 } = self.state
74 {
75 *bytes_sent = (*bytes_sent + bytes).min(total_bytes);
76 if *bytes_sent == total_bytes {
77 self.state = TransferState::Completed;
78 }
79 }
80 }
81}
82
83pub struct TransferManager {
85 jobs: Vec<TransferJob>,
86 next_id: u64,
87}
88
89impl TransferManager {
90 pub fn new() -> Self {
91 TransferManager {
92 jobs: Vec::new(),
93 next_id: 1,
94 }
95 }
96
97 pub fn enqueue(&mut self, source: &str, destination: &str, size_bytes: u64) -> u64 {
98 let id = self.next_id;
99 self.next_id += 1;
100 self.jobs
101 .push(TransferJob::new(id, source, destination, size_bytes));
102 id
103 }
104
105 pub fn get_job(&self, id: u64) -> Option<&TransferJob> {
106 self.jobs.iter().find(|j| j.id == id)
107 }
108
109 pub fn get_job_mut(&mut self, id: u64) -> Option<&mut TransferJob> {
110 self.jobs.iter_mut().find(|j| j.id == id)
111 }
112
113 pub fn completed_count(&self) -> usize {
114 self.jobs
115 .iter()
116 .filter(|j| j.state == TransferState::Completed)
117 .count()
118 }
119
120 pub fn pending_count(&self) -> usize {
121 self.jobs.iter().filter(|j| !j.state.is_done()).count()
122 }
123}
124
125impl Default for TransferManager {
126 fn default() -> Self {
127 Self::new()
128 }
129}
130
131pub fn new_transfer_manager() -> TransferManager {
133 TransferManager::new()
134}
135
136pub fn tick_all(mgr: &mut TransferManager, chunk_size: u64) {
138 for job in &mut mgr.jobs {
139 job.advance(chunk_size);
140 }
141}
142
143pub fn cancel_job(mgr: &mut TransferManager, id: u64) {
145 if let Some(job) = mgr.get_job_mut(id) {
146 job.mark_failed("cancelled");
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[test]
155 fn test_enqueue_job() {
156 let mut m = new_transfer_manager();
157 let id = m.enqueue("/src", "/dst", 1000);
158 assert_eq!(id, 1);
159 assert!(m.get_job(id).is_some());
160 }
161
162 #[test]
163 fn test_advance_completes() {
164 let mut m = new_transfer_manager();
165 let id = m.enqueue("/src", "/dst", 100);
166 if let Some(job) = m.get_job_mut(id) {
167 job.advance(100);
168 }
169 assert_eq!(m.completed_count(), 1);
170 }
171
172 #[test]
173 fn test_progress_pct() {
174 let state = TransferState::InProgress {
175 bytes_sent: 50,
176 total_bytes: 100,
177 };
178 assert!((state.progress_pct() - 0.5).abs() < 0.01);
179 }
180
181 #[test]
182 fn test_is_done_completed() {
183 assert!(TransferState::Completed.is_done());
184 }
185
186 #[test]
187 fn test_is_done_failed() {
188 assert!(TransferState::Failed("err".to_string()).is_done());
189 }
190
191 #[test]
192 fn test_cancel_job() {
193 let mut m = new_transfer_manager();
194 let id = m.enqueue("/src", "/dst", 1000);
195 cancel_job(&mut m, id);
196 assert!(matches!(
197 m.get_job(id).expect("should succeed").state,
198 TransferState::Failed(_)
199 ));
200 }
201
202 #[test]
203 fn test_tick_all() {
204 let mut m = new_transfer_manager();
205 m.enqueue("/s", "/d", 100);
206 tick_all(&mut m, 50);
207 let j = m.get_job(1).expect("should succeed");
208 assert!((j.state.progress_pct() - 0.5).abs() < 0.01);
209 }
210
211 #[test]
212 fn test_pending_count() {
213 let mut m = new_transfer_manager();
214 m.enqueue("/s1", "/d1", 100);
215 m.enqueue("/s2", "/d2", 200);
216 assert_eq!(m.pending_count(), 2);
217 }
218
219 #[test]
220 fn test_mark_failed() {
221 let mut job = TransferJob::new(1, "/s", "/d", 100);
222 job.mark_failed("network error");
223 assert!(matches!(job.state, TransferState::Failed(_)));
224 }
225
226 #[test]
227 fn test_mark_complete() {
228 let mut job = TransferJob::new(1, "/s", "/d", 0);
229 job.mark_complete();
230 assert_eq!(job.state, TransferState::Completed);
231 }
232}