aster_forge_cloud_files_windows/
progress.rs1use std::cmp::Ordering;
4
5use crate::{Result, WindowsCloudFilesError};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub struct WindowsFetchDataProgress {
10 total: u64,
11 completed: u64,
12}
13
14impl WindowsFetchDataProgress {
15 pub fn new(total: u64, completed: u64) -> Result<Self> {
21 if completed > total {
22 return Err(WindowsCloudFilesError::InvalidProviderProgress {
23 reason: "completed bytes exceed total bytes",
24 });
25 }
26 if total > i64::MAX as u64 {
27 return Err(WindowsCloudFilesError::InvalidProviderProgress {
28 reason: "total bytes exceed signed CFAPI boundary",
29 });
30 }
31 if completed > i64::MAX as u64 {
32 return Err(WindowsCloudFilesError::InvalidProviderProgress {
33 reason: "completed bytes exceed signed CFAPI boundary",
34 });
35 }
36 Ok(Self { total, completed })
37 }
38
39 #[must_use]
41 pub const fn total(self) -> u64 {
42 self.total
43 }
44
45 #[must_use]
47 pub const fn completed(self) -> u64 {
48 self.completed
49 }
50
51 #[must_use]
53 pub const fn as_cfapi(self) -> (i64, i64) {
54 (self.total.cast_signed(), self.completed.cast_signed())
55 }
56
57 pub fn advance_from(self, previous: Self) -> Result<Self> {
63 if self.total != previous.total {
64 return Err(WindowsCloudFilesError::InvalidProviderProgress {
65 reason: "progress total changed during one transfer",
66 });
67 }
68 match self.completed.cmp(&previous.completed) {
69 Ordering::Less => Err(WindowsCloudFilesError::InvalidProviderProgress {
70 reason: "completed bytes regressed during one transfer",
71 }),
72 Ordering::Equal | Ordering::Greater => Ok(self),
73 }
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn progress_accepts_zero_and_exact_completion() {
83 assert_eq!(
84 WindowsFetchDataProgress::new(0, 0).unwrap().as_cfapi(),
85 (0, 0)
86 );
87 assert_eq!(
88 WindowsFetchDataProgress::new(i64::MAX as u64, i64::MAX as u64)
89 .unwrap()
90 .as_cfapi(),
91 (i64::MAX, i64::MAX)
92 );
93 }
94
95 #[test]
96 fn progress_rejects_overflow_and_regression() {
97 assert!(matches!(
98 WindowsFetchDataProgress::new(1, 2),
99 Err(WindowsCloudFilesError::InvalidProviderProgress { .. })
100 ));
101 assert!(matches!(
102 WindowsFetchDataProgress::new(i64::MAX as u64 + 1, 0),
103 Err(WindowsCloudFilesError::InvalidProviderProgress { .. })
104 ));
105 let previous = WindowsFetchDataProgress::new(10, 5).unwrap();
106 let next = WindowsFetchDataProgress::new(10, 4).unwrap();
107 assert!(matches!(
108 next.advance_from(previous),
109 Err(WindowsCloudFilesError::InvalidProviderProgress { .. })
110 ));
111 let changed_total = WindowsFetchDataProgress::new(11, 5).unwrap();
112 assert!(changed_total.advance_from(previous).is_err());
113 }
114}