1use std::collections::HashMap;
3
4use serde::{Deserialize, Serialize};
5
6use super::config::{
7 Defaults, DeviceConfiguration, FolderConfiguration, GuiConfiguration, LDAPConfiguration,
8 NewDeviceConfiguration, ObservedDevice,
9};
10
11#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
13#[serde(rename_all = "camelCase")]
14pub struct Event {
15 pub id: u64,
16 #[serde(rename = "globalID")]
17 pub global_id: u64,
18 pub time: chrono::DateTime<chrono::Utc>,
19 #[serde(flatten)]
20 pub ty: EventType,
21}
22
23#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
24#[serde(tag = "type", content = "data")]
25pub enum EventType {
26 ClusterConfigReceived {
27 device: String,
28 },
29 #[serde(rename_all = "camelCase")]
30 ConfigSaved {
31 version: u64,
32 folders: Vec<FolderConfiguration>,
33 devices: Vec<DeviceConfiguration>,
34 gui: GuiConfiguration,
35 ldap: LDAPConfiguration,
36 remote_ignored_devices: Vec<ObservedDevice>,
37 defaults: Box<Defaults>,
38 },
39 #[serde(rename_all = "camelCase")]
40 DeviceConnected {
41 addr: String,
42 id: String,
43 device_name: String,
44 client_name: String,
45 client_version: String,
46 #[serde(rename = "type")]
47 ty: ConnectionType,
48 },
49 DeviceDisconnected {
50 error: String,
51 id: String,
52 },
53 DeviceDiscovered {
54 addrs: Vec<String>,
55 device: String,
56 },
57 DevicePaused {
58 device: String,
59 },
60 DeviceRejected {}, DeviceResumed {
62 device: String,
63 },
64 DownloadProgress {
65 #[serde(flatten)]
66 folders: HashMap<String, HashMap<String, FileDownloadProgress>>,
67 },
68 Failure(String),
69 #[serde(rename_all = "camelCase")]
70 FolderCompletion {
71 completion: f64,
72 device: String,
73 folder: String,
74 global_bytes: u64,
75 global_items: u64,
76 need_bytes: u64,
77 need_deletes: u64,
78 need_items: u64,
79 remote_state: String,
80 sequence: u64,
81 },
82 FolderErrors {
83 errors: Vec<FolderError>,
84 folder: String,
85 },
86 FolderPaused {
87 id: String,
88 label: String,
89 },
90 FolderRejected {}, FolderResumed {
92 id: String,
93 label: String,
94 },
95 FolderScanProgress {
96 total: u64,
97 rate: u64,
98 current: u64,
99 folder: String,
100 },
101 FolderSummary {
102 folder: String,
103 summary: FolderSummary,
104 },
105 FolderWatchStateChanged {
106 folder: String,
107 from: String,
108 to: String,
109 },
110 ItemFinished {
111 item: String,
112 folder: String,
113 error: Option<String>,
114 #[serde(rename = "type")]
115 ty: String,
116 action: String,
117 },
118 ItemStarted {
119 item: String,
120 folder: String,
121 #[serde(rename = "type")]
122 ty: String,
123 action: String,
124 },
125 ListenAddressesChanged {
126 address: ListenAddressChanged,
127 wan: Option<Vec<ListenAddressChanged>>,
128 lan: Option<Vec<ListenAddressChanged>>,
129 },
130 LocalChangeDetected {
131 action: String,
132 folder: String,
133 #[serde(rename = "folderID")]
134 folder_id: String,
135 label: String,
136 path: String,
137 #[serde(rename = "type")]
138 ty: String,
139 },
140 LocalIndexUpdated {
141 folder: String,
142 items: u64,
143 filenames: Vec<String>,
144 sequence: u64,
145 },
146 #[serde(rename_all = "camelCase")]
147 LoginAttempt {
148 remote_address: String,
149 username: String,
150 success: bool,
151 proxy: Option<String>,
152 },
153 PendingDevicesChanged {
154 added: Option<Vec<AddedPendingDeviceChanged>>,
155 removed: Option<Vec<RemovedPendingDeviceChanged>>,
156 },
157 PendingFoldersChanged {
158 added: Option<Vec<AddedPendingFolderChanged>>,
159 removed: Option<Vec<RemovedPendingFolderChanged>>,
160 },
161 #[serde(rename_all = "camelCase")]
162 RemoteChangeDetected {
163 #[serde(rename = "type")]
164 ty: String,
165 action: String,
166 folder: String,
167 #[serde(rename = "folderID")]
168 folder_id: String,
169 path: String,
170 label: String,
171 modified_by: String,
172 },
173 RemoteDownloadProgress {
174 state: HashMap<String, u64>,
175 device: String,
176 folder: String,
177 },
178 RemoteIndexUpdated {
179 device: String,
180 folder: String,
181 items: u64,
182 },
183 Starting {
184 home: String,
185 },
186 StartupComplete {
187 #[serde(rename = "myID")]
188 my_id: String,
189 },
190 StateChanged {
191 folder: String,
192 from: String,
193 duration: Option<f64>,
194 to: String,
195 },
196}
197
198#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
199pub enum ConnectionType {
200 #[serde(rename = "tcp-client")]
201 TCPClient,
202 #[serde(rename = "tcp-server")]
203 TCPServer,
204 #[serde(rename = "relay-client")]
205 RelayClient,
206 #[serde(rename = "relay-server")]
207 RelayServer,
208 #[serde(rename = "quic-server")]
209 QuicServer,
210}
211
212#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
213pub struct FolderError {
214 pub error: String,
215 pub path: String,
216}
217
218#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
219#[serde(rename_all = "camelCase")]
220pub struct FolderSummary {
221 pub errors: i64,
222 pub global_files: i64,
223 pub global_directories: i64,
224 pub global_symlinks: i64,
225 pub global_deleted: i64,
226 pub global_bytes: i64,
227 pub global_total_items: i64,
228 pub local_files: i64,
229 pub local_directories: i64,
230 pub local_symlinks: i64,
231 pub local_deleted: i64,
232 pub local_bytes: i64,
233 pub local_total_items: i64,
234 pub need_files: i64,
235 pub need_directories: i64,
236 pub need_symlinks: i64,
237 pub need_deletes: i64,
238 pub need_bytes: i64,
239 pub need_total_items: i64,
240 pub receive_only_changed_files: i64,
241 pub receive_only_changed_directories: i64,
242 pub receive_only_changed_symlinks: i64,
243 pub receive_only_changed_deletes: i64,
244 pub receive_only_changed_bytes: i64,
245 pub receive_only_total_items: i64,
246 pub in_sync_files: i64,
247 pub in_sync_bytes: i64,
248 pub state: String,
249 pub state_changed: chrono::DateTime<chrono::Utc>,
250 pub error: String,
251 pub sequence: i64,
252 pub remote_sequence: HashMap<String, i64>,
253 pub ignore_patterns: bool,
254 pub watch_error: String,
255}
256
257#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
258#[serde(rename_all = "PascalCase")]
259pub struct ListenAddressChanged {
260 pub fragment: String,
261 pub raw_query: String,
262 pub scheme: String,
263 pub path: String,
264 pub user: Option<String>,
265 pub force_query: bool,
266 pub host: String,
267 pub opaque: String,
268}
269
270#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
271#[serde(rename_all = "camelCase")]
272pub struct FileDownloadProgress {
273 pub total: u64,
274 pub pulling: u64,
275 pub copied_from_original: u64,
276 pub reused: u64,
277 pub copied_from_elsewhere: u64,
278 pub pulled: u64,
279 pub bytes_total: u64,
280 pub bytes_done: u64,
281}
282
283#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
287pub struct AddedPendingDeviceChanged {
288 pub address: std::net::SocketAddr,
289 #[serde(rename = "deviceID")]
290 pub device_id: String,
291 pub name: String,
292}
293
294#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
298pub struct RemovedPendingDeviceChanged {
299 #[serde(rename = "deviceID")]
300 pub device_id: String,
301}
302
303#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
307#[serde(rename_all = "camelCase")]
308pub struct AddedPendingFolderChanged {
309 #[serde(rename = "deviceID")]
310 pub device_id: String,
311 #[serde(rename = "folderID")]
312 pub folder_id: String,
313 pub folder_label: String,
314 pub receive_encrypted: bool,
315 pub remote_encrypted: bool,
316}
317
318#[derive(Serialize, Deserialize, Debug, Default, PartialEq, Eq, Clone)]
322pub struct RemovedPendingFolderChanged {
323 #[serde(rename = "deviceID")]
326 pub device_id: Option<String>,
327 #[serde(rename = "folderID")]
328 pub folder_id: String,
329}
330
331impl From<AddedPendingDeviceChanged> for NewDeviceConfiguration {
332 fn from(value: AddedPendingDeviceChanged) -> Self {
333 Self::new(value.device_id).name(value.name)
334 }
335}
336
337#[cfg(test)]
338mod tests {
339 use std::net::{IpAddr, Ipv4Addr};
340
341 use super::*;
342
343 #[test]
344 fn test_new_device() {
345 let added = AddedPendingDeviceChanged {
346 address: std::net::SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8384),
347 device_id: "foo".to_string(),
348 name: "bar".to_string(),
349 };
350
351 let new: NewDeviceConfiguration = added.into();
352
353 assert_eq!(new.get_device_id(), "foo");
354 assert_eq!(new.get_name(), &Some("bar".to_string()));
355 }
356}