1use std::{fmt, sync::Arc};
2
3pub(crate) const MAX_SLICES_PER_FILE: usize = 32_768;
4
5pub(crate) const MAX_TOTAL_INPUT_SLICES: usize = 32_768;
8
9pub(crate) const MAX_FILES_PER_SET: usize = 32_768;
11
12#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
18pub struct FileId(pub(crate) [u8; 16]);
19
20impl FileId {
21 pub fn from_bytes(bytes: [u8; 16]) -> Self {
22 Self(bytes)
23 }
24
25 pub fn as_bytes(&self) -> &[u8; 16] {
26 &self.0
27 }
28}
29
30impl fmt::Debug for FileId {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 write!(f, "FileId(")?;
33 for b in &self.0 {
34 write!(f, "{b:02x}")?;
35 }
36 write!(f, ")")
37 }
38}
39
40impl fmt::Display for FileId {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 for b in &self.0 {
43 write!(f, "{b:02x}")?;
44 }
45 Ok(())
46 }
47}
48
49#[derive(Clone, Copy, PartialEq, Eq, Hash)]
51pub struct RecoverySetId(pub(crate) [u8; 16]);
52
53impl RecoverySetId {
54 pub fn from_bytes(bytes: [u8; 16]) -> Self {
55 Self(bytes)
56 }
57
58 pub fn as_bytes(&self) -> &[u8; 16] {
59 &self.0
60 }
61}
62
63impl fmt::Debug for RecoverySetId {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 write!(f, "RecoverySetId(")?;
66 for b in &self.0 {
67 write!(f, "{b:02x}")?;
68 }
69 write!(f, ")")
70 }
71}
72
73impl fmt::Display for RecoverySetId {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 for b in &self.0 {
76 write!(f, "{b:02x}")?;
77 }
78 Ok(())
79 }
80}
81
82pub type SliceIndex = u32;
84
85pub type RecoveryExponent = u32;
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90pub struct SliceChecksum {
91 pub crc32: u32,
92 pub md5: [u8; 16],
93}
94
95#[derive(Clone)]
99pub struct CancellationToken(std::sync::Arc<std::sync::atomic::AtomicBool>);
100
101impl CancellationToken {
102 pub fn new() -> Self {
103 Self(std::sync::Arc::new(std::sync::atomic::AtomicBool::new(
104 false,
105 )))
106 }
107
108 pub fn cancel(&self) {
109 self.0.store(true, std::sync::atomic::Ordering::Relaxed);
110 }
111
112 pub fn is_cancelled(&self) -> bool {
113 self.0.load(std::sync::atomic::Ordering::Relaxed)
114 }
115}
116
117impl Default for CancellationToken {
118 fn default() -> Self {
119 Self::new()
120 }
121}
122
123#[derive(Debug, Clone)]
130pub struct ProgressUpdate {
131 pub stage: ProgressStage,
133 pub current: u32,
135 pub total: u32,
137 pub bytes_processed: u64,
139 pub total_bytes: Option<u64>,
141 pub phase: ProgressPhase,
143}
144
145#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
154pub enum ProgressPhase {
155 #[default]
157 Whole,
158 SourceScan,
160 RecoveryEncode,
162}
163
164#[derive(Debug, Clone, Copy, PartialEq, Eq)]
166pub enum ProgressStage {
167 Creating,
169 Verifying,
171 ReadingRecovery,
173 Repairing,
175 WritingRepaired,
177}
178
179pub type ProgressCallback = Arc<dyn Fn(ProgressUpdate) + Send + Sync>;
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185
186 #[test]
187 fn file_id_display() {
188 let id = FileId([
189 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54,
190 0x32, 0x10,
191 ]);
192 assert_eq!(format!("{id}"), "0123456789abcdeffedcba9876543210");
193 }
194
195 #[test]
196 fn file_id_debug() {
197 let id = FileId([0; 16]);
198 let dbg = format!("{id:?}");
199 assert!(dbg.starts_with("FileId("));
200 assert!(dbg.ends_with(')'));
201 }
202
203 #[test]
204 fn recovery_set_id_roundtrip() {
205 let bytes = [1u8; 16];
206 let id = RecoverySetId::from_bytes(bytes);
207 assert_eq!(*id.as_bytes(), bytes);
208 }
209
210 #[test]
211 fn file_id_equality() {
212 let a = FileId([0xAA; 16]);
213 let b = FileId([0xAA; 16]);
214 let c = FileId([0xBB; 16]);
215 assert_eq!(a, b);
216 assert_ne!(a, c);
217 }
218}