gosh_lan_transfer/protocol.rs
1// SPDX-License-Identifier: MIT
2//! Protocol types for gosh-lan-transfer
3//!
4//! This module contains all types that cross the engine boundary:
5//! - Wire protocol types (sent between peers over HTTP)
6//! - Event types (emitted from engine to consumers)
7//! - Shared status enums
8//!
9//! Rule: If it crosses the engine boundary, it belongs here.
10
11use chrono::{DateTime, Utc};
12use serde::{Deserialize, Serialize};
13
14// =============================================================================
15// Status Enums - Shared vocabulary for transfer state
16// =============================================================================
17
18/// Direction of a transfer
19#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
20#[serde(rename_all = "lowercase")]
21pub enum TransferDirection {
22 Sent,
23 Received,
24}
25
26/// Status of a transfer
27#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
28#[serde(rename_all = "lowercase")]
29pub enum TransferStatus {
30 Pending,
31 InProgress,
32 Completed,
33 Failed,
34 Rejected,
35}
36
37/// Transfer approval decision
38#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
39#[serde(rename_all = "lowercase")]
40pub enum TransferDecision {
41 Pending,
42 Accepted,
43 Rejected,
44 NotFound,
45}
46
47// =============================================================================
48// Wire Protocol Types - Sent between peers over HTTP
49// =============================================================================
50
51/// A single file in a transfer
52#[derive(Debug, Clone, Serialize, Deserialize)]
53#[serde(rename_all = "camelCase")]
54pub struct TransferFile {
55 /// File name (not full path for security)
56 pub name: String,
57 /// File size in bytes
58 pub size: u64,
59 /// MIME type (if detected)
60 pub mime_type: Option<String>,
61 /// Unique identifier for this file in the transfer
62 pub id: String,
63 /// Relative path within a directory transfer (e.g., "subdir/file.txt")
64 /// When present, the receiver will recreate the directory structure
65 #[serde(skip_serializing_if = "Option::is_none")]
66 pub relative_path: Option<String>,
67}
68
69/// Metadata for a transfer request (sent before actual data)
70#[derive(Debug, Clone, Serialize, Deserialize)]
71#[serde(rename_all = "camelCase")]
72pub struct TransferRequest {
73 /// Unique transfer session ID
74 pub transfer_id: String,
75 /// Optional friendly name of the sender
76 pub sender_name: Option<String>,
77 /// List of files to be transferred
78 pub files: Vec<TransferFile>,
79 /// Total size of all files
80 pub total_size: u64,
81}
82
83/// Response to a transfer request
84#[derive(Debug, Clone, Serialize, Deserialize)]
85#[serde(rename_all = "camelCase")]
86pub struct TransferResponse {
87 /// Whether the transfer was accepted
88 pub accepted: bool,
89 /// Optional message (e.g., rejection reason)
90 pub message: Option<String>,
91 /// Token for subsequent chunk uploads (if accepted)
92 pub token: Option<String>,
93}
94
95/// Status response for a transfer awaiting approval
96#[derive(Debug, Clone, Serialize, Deserialize)]
97#[serde(rename_all = "camelCase")]
98pub struct TransferApprovalStatus {
99 /// Current approval status
100 pub status: TransferDecision,
101 /// Token for subsequent chunk uploads (if accepted)
102 pub token: Option<String>,
103 /// Optional message
104 pub message: Option<String>,
105}
106
107/// Peer device information
108#[derive(Debug, Clone, Serialize, Deserialize)]
109#[serde(rename_all = "camelCase")]
110pub struct PeerInfo {
111 /// Device name
112 pub device_name: String,
113 /// Protocol version
114 pub version: String,
115}
116
117// =============================================================================
118// Event Payload Types - Data carried in engine events
119// =============================================================================
120
121/// Progress update for an ongoing transfer
122#[derive(Debug, Clone, Serialize, Deserialize)]
123#[serde(rename_all = "camelCase")]
124pub struct TransferProgress {
125 /// Transfer ID
126 pub transfer_id: String,
127 /// Current file being transferred
128 pub current_file: Option<String>,
129 /// Bytes transferred so far
130 pub bytes_transferred: u64,
131 /// Total bytes to transfer
132 pub total_bytes: u64,
133 /// Transfer speed in bytes/sec
134 pub speed_bps: u64,
135}
136
137/// An incoming transfer pending user approval
138#[derive(Debug, Clone, Serialize, Deserialize)]
139#[serde(rename_all = "camelCase")]
140pub struct PendingTransfer {
141 /// Transfer ID
142 pub id: String,
143 /// Source IP address
144 pub source_ip: String,
145 /// Optional sender name
146 pub sender_name: Option<String>,
147 /// Files to be received
148 pub files: Vec<TransferFile>,
149 /// Total size
150 pub total_size: u64,
151 /// When the request was received
152 pub received_at: DateTime<Utc>,
153}
154
155// =============================================================================
156// Engine Events - Emitted from engine to consumers
157// =============================================================================
158
159/// Events emitted by the engine
160///
161/// These events cross the engine boundary and are delivered to consumers
162/// via the `EventHandler` trait.
163#[derive(Debug, Clone)]
164pub enum EngineEvent {
165 /// New transfer request awaiting approval
166 TransferRequest(PendingTransfer),
167
168 /// Progress update for an active transfer
169 TransferProgress(TransferProgress),
170
171 /// Transfer completed successfully
172 TransferComplete { transfer_id: String },
173
174 /// Transfer failed
175 TransferFailed { transfer_id: String, error: String },
176
177 /// Retrying a failed operation
178 TransferRetry {
179 transfer_id: String,
180 /// Current attempt number (1-based)
181 attempt: u32,
182 /// Maximum attempts allowed
183 max_attempts: u32,
184 /// Error that triggered the retry
185 error: String,
186 },
187
188 /// Server started successfully
189 ServerStarted { port: u16 },
190
191 /// Server stopped
192 ServerStopped,
193
194 /// Server port changed at runtime
195 PortChanged { old_port: u16, new_port: u16 },
196}