1use serde::{Deserialize, Serialize};
2use std::time::Duration;
3
4pub const DEFAULT_CHUNK_SIZE: usize = 1024 * 1024;
5
6pub type ChunkIndex = u32;
7pub type ChunkChecksum = u64;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
10pub struct TransferSessionId([u8; 16]);
11
12impl TransferSessionId {
13 pub fn new() -> Self {
14 TransferSessionId(*uuid::Uuid::new_v4().as_bytes())
15 }
16
17 pub fn from_bytes(bytes: &[u8]) -> Result<Self, &'static str> {
18 if bytes.len() != 16 {
19 return Err("TransferSessionId must be 16 bytes long");
20 }
21 let mut arr = [0u8; 16];
22 arr.copy_from_slice(bytes);
23 Ok(TransferSessionId(arr))
24 }
25
26 pub fn as_bytes(&self) -> &[u8; 16] {
27 &self.0
28 }
29}
30
31impl Default for TransferSessionId {
32 fn default() -> Self {
33 Self::new()
34 }
35}
36
37impl From<[u8; 16]> for TransferSessionId {
38 fn from(bytes: [u8; 16]) -> Self {
39 TransferSessionId(bytes)
40 }
41}
42
43impl std::fmt::Display for TransferSessionId {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 let uuid = uuid::Uuid::from_bytes(self.0);
46 write!(f, "{uuid}")
47 }
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
51pub enum CompressionAlgorithm {
52 #[default]
53 None,
54 Zstd,
55 ZstdLevel(i32),
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
59pub enum HashAlgorithm {
60 #[default]
61 Sha256,
62 Blake3,
63 XxHash64,
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
67pub enum TransferMode {
68 #[default]
69 Copy,
70 Move,
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
74pub enum TransferState {
75 Initializing,
76 InProgress,
77 Paused,
78 Verifying,
79 Completed,
80 Failed,
81 Cancelled,
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
85pub struct RetryPolicy {
86 pub max_retries: u8,
87 pub initial_delay: Duration,
88 pub max_delay: Duration,
89 pub backoff_multiplier: f64,
90 pub jitter: bool,
91}
92
93impl Default for RetryPolicy {
94 fn default() -> Self {
95 RetryPolicy {
96 max_retries: 3,
97 initial_delay: Duration::from_millis(100),
98 max_delay: Duration::from_secs(10),
99 backoff_multiplier: 2.0,
100 jitter: true,
101 }
102 }
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct TransferOptions {
107 pub chunk_size: usize,
108 pub concurrency: usize,
109 pub compression: CompressionAlgorithm,
110 pub bandwidth_limit: Option<u64>,
111 pub retry_policy: RetryPolicy,
112 pub verify_on_complete: bool,
113 pub hash_algorithm: HashAlgorithm,
114 pub resumable: bool,
115 pub overwrite: bool,
116 pub mode: TransferMode,
117 pub preserve_metadata: bool,
118}
119
120impl Default for TransferOptions {
121 fn default() -> Self {
122 TransferOptions {
123 chunk_size: DEFAULT_CHUNK_SIZE,
124 concurrency: 4,
125 compression: CompressionAlgorithm::None,
126 bandwidth_limit: None,
127 retry_policy: RetryPolicy::default(),
128 verify_on_complete: true,
129 hash_algorithm: HashAlgorithm::Sha256,
130 resumable: true,
131 overwrite: false,
132 mode: TransferMode::Copy,
133 preserve_metadata: true,
134 }
135 }
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize)]
139pub struct ChunkMeta {
140 pub index: ChunkIndex,
141 pub offset: u64,
142 pub size: u32,
143 pub checksum: ChunkChecksum,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct TransferManifest {
148 pub version: u16,
149 pub session_id: TransferSessionId,
150 pub source_path: String,
151 pub dest_path: String,
152 pub file_size: u64,
153 pub file_hash: Vec<u8>,
154 pub hash_algorithm: HashAlgorithm,
155 pub chunk_size: u32,
156 pub chunk_count: u32,
157 pub chunks: Vec<ChunkMeta>,
158 pub metadata: Option<FileMetadata>,
159 pub options: TransferOptions,
160 pub created_at: u64,
161}
162
163#[derive(Serialize, Deserialize, Debug, Clone)]
164pub struct FileTransferFrame {
165 pub session_id: TransferSessionId,
166 pub message: FileTransferMessage,
167}
168
169#[derive(Serialize, Deserialize, Debug, Clone)]
170pub enum FileTransferMessage {
171 TransferRequest(TransferRequest),
172 TransferResponse(TransferResponse),
173 Manifest(TransferManifest),
174 ManifestAck(ManifestAck),
175 ChunkAck(ChunkAck),
176 ChunkRequest(ChunkRequest),
177 Progress(ProgressUpdate),
178 Cancel(CancelRequest),
179 Complete(TransferComplete),
180 Error(TransferErrorMessage),
181 ExistsRequest(ExistsRequest),
182 ExistsResponse(ExistsResponse),
183 RemoveRequest(RemoveRequest),
184 RemoveResponse(RemoveResponse),
185 MetadataRequest(MetadataRequest),
186 MetadataResponse(MetadataResponse),
187 ListRequest(ListRequest),
188 ListResponse(ListResponse),
189}
190
191#[derive(Serialize, Deserialize, Debug, Clone)]
192pub struct TransferRequest {
193 pub source_path: String,
194 pub dest_path: String,
195 pub file_size: u64,
196 pub chunk_count: u32,
197 pub chunk_size: u32,
198 pub mode: TransferMode,
199 pub options: TransferOptions,
200 pub metadata: Option<FileMetadata>,
201}
202
203#[derive(Serialize, Deserialize, Debug, Clone)]
204pub struct TransferResponse {
205 pub accepted: bool,
206 pub rejection_reason: Option<String>,
207 pub existing_chunks: Vec<ChunkIndex>,
208}
209
210#[derive(Serialize, Deserialize, Debug, Clone)]
211pub struct ManifestAck {
212 pub accepted: bool,
213 pub skip_chunks: Vec<ChunkIndex>,
214 pub error: Option<String>,
215}
216
217#[derive(Serialize, Deserialize, Debug, Clone)]
218pub struct ChunkAck {
219 pub index: ChunkIndex,
220 pub verified: bool,
221 pub error: Option<String>,
222}
223
224#[derive(Serialize, Deserialize, Debug, Clone)]
225pub struct ChunkRequest {
226 pub indices: Vec<ChunkIndex>,
227}
228
229#[derive(Serialize, Deserialize, Debug, Clone)]
230pub struct ProgressUpdate {
231 pub chunks_completed: u32,
232 pub bytes_transferred: u64,
233 pub state: TransferState,
234}
235
236#[derive(Serialize, Deserialize, Debug, Clone)]
237pub struct CancelRequest {
238 pub reason: String,
239}
240
241#[derive(Serialize, Deserialize, Debug, Clone)]
242pub struct TransferComplete {
243 pub bytes_transferred: u64,
244 pub duration_ms: u64,
245 pub file_hash: Vec<u8>,
246 pub hash_algorithm: HashAlgorithm,
247}
248
249#[derive(Serialize, Deserialize, Debug, Clone)]
250pub struct TransferErrorMessage {
251 pub code: u32,
252 pub message: String,
253 pub recoverable: bool,
254}
255
256#[derive(Serialize, Deserialize, Debug, Clone)]
257pub struct ExistsRequest {
258 pub path: String,
259}
260
261#[derive(Serialize, Deserialize, Debug, Clone)]
262pub struct ExistsResponse {
263 pub exists: bool,
264 pub is_file: bool,
265 pub is_directory: bool,
266}
267
268#[derive(Serialize, Deserialize, Debug, Clone)]
269pub struct RemoveRequest {
270 pub path: String,
271 pub recursive: bool,
272}
273
274#[derive(Serialize, Deserialize, Debug, Clone)]
275pub struct RemoveResponse {
276 pub success: bool,
277 pub error: Option<String>,
278}
279
280#[derive(Serialize, Deserialize, Debug, Clone)]
281pub struct MetadataRequest {
282 pub path: String,
283}
284
285#[derive(Serialize, Deserialize, Debug, Clone)]
286pub struct MetadataResponse {
287 pub found: bool,
288 pub metadata: Option<FileMetadata>,
289 pub size: Option<u64>,
290 pub error: Option<String>,
291}
292
293#[derive(Serialize, Deserialize, Debug, Clone)]
294pub struct ListRequest {
295 pub path: String,
296 pub recursive: bool,
297 pub include_hidden: bool,
298}
299
300#[derive(Serialize, Deserialize, Debug, Clone)]
301pub struct ListResponse {
302 pub files: Vec<FileInfo>,
303 pub error: Option<String>,
304}
305
306#[derive(Debug, Clone, Serialize, Deserialize)]
307pub struct FileMetadata {
308 pub created_at: Option<u64>,
309 pub modified_at: Option<u64>,
310 pub permissions: Option<u32>,
311 pub file_type: FileType,
312 pub size: Option<u64>,
313}
314
315#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
316pub enum FileType {
317 File,
318 Directory,
319 Symlink,
320}
321
322#[derive(Serialize, Deserialize, Debug, Clone)]
323pub struct FileInfo {
324 pub path: String,
325 pub size: u64,
326 pub modified_at: u64,
327 pub file_type: FileType,
328}