1use std::{collections::HashMap, fmt::Display};
4
5use brume::concrete::{
6 FSBackend, FsInstanceDescription, Named, local::LocalDir, nextcloud::NextcloudFs,
7};
8
9use brume::synchro::FullSyncStatus;
10use serde::{Deserialize, Serialize};
11use uuid::Uuid;
12
13pub use brume::concrete::{local::LocalDirCreationInfo, nextcloud::NextcloudFsCreationInfo};
14pub use brume::synchro::SynchroSide;
15pub use brume::vfs::virtual_path::{VirtualPath, VirtualPathBuf};
16
17pub const BRUME_SOCK_NAME: &str = "brume.socket";
19
20#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
22pub struct SynchroId(Uuid);
23
24impl Default for SynchroId {
25 fn default() -> Self {
26 Self::new()
27 }
28}
29
30impl From<Uuid> for SynchroId {
31 fn from(value: Uuid) -> Self {
32 Self(value)
33 }
34}
35
36impl SynchroId {
37 pub fn new() -> Self {
38 Self(Uuid::new_v4())
39 }
40
41 pub fn id(&self) -> Uuid {
42 self.0
43 }
44
45 pub fn as_bytes(&self) -> &[u8] {
46 self.0.as_bytes()
47 }
48
49 pub fn short(&self) -> u32 {
50 self.0.as_fields().0
51 }
52}
53
54#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
56pub struct AnyFsRef {
57 id: Uuid,
58 description: AnyFsDescription,
59}
60
61impl Display for AnyFsRef {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 write!(f, "{}", self.description)
64 }
65}
66
67impl From<AnyFsCreationInfo> for AnyFsRef {
68 fn from(value: AnyFsCreationInfo) -> Self {
69 Self {
70 id: Uuid::new_v4(),
71 description: value.into(),
72 }
73 }
74}
75
76impl AnyFsRef {
77 pub fn new(id: Uuid, description: AnyFsDescription) -> Self {
78 Self { id, description }
79 }
80
81 pub fn description(&self) -> &AnyFsDescription {
82 &self.description
83 }
84
85 pub fn name(&self) -> &str {
86 self.description.name()
87 }
88
89 pub fn id(&self) -> Uuid {
90 self.id
91 }
92}
93
94#[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
101pub enum SynchroStatus {
102 #[default]
104 Ok,
105 Conflict,
107 Error,
109 Desync,
112 SyncInProgress,
114}
115
116impl Display for SynchroStatus {
117 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118 write!(f, "{:?}", self)
119 }
120}
121
122impl TryFrom<&str> for SynchroStatus {
123 type Error = ();
124
125 fn try_from(value: &str) -> Result<Self, <SynchroStatus as TryFrom<&str>>::Error> {
126 match value {
127 "Ok" => Ok(Self::Ok),
128 "Conflict" => Ok(Self::Conflict),
129 "Error" => Ok(Self::Error),
130 "Desync" => Ok(Self::Desync),
131 "SyncInProgress" => Ok(Self::SyncInProgress),
132 _ => Err(()),
133 }
134 }
135}
136
137impl From<FullSyncStatus> for SynchroStatus {
138 fn from(value: FullSyncStatus) -> Self {
139 match value {
140 FullSyncStatus::Ok => Self::Ok,
141 FullSyncStatus::Conflict => Self::Conflict,
142 FullSyncStatus::Error => Self::Error,
143 FullSyncStatus::Desync => Self::Desync,
144 }
145 }
146}
147
148impl SynchroStatus {
149 pub fn is_synchronizable(self) -> bool {
151 !matches!(self, SynchroStatus::Desync | SynchroStatus::SyncInProgress)
152 }
153}
154
155#[derive(Default, PartialEq, Eq, Copy, Clone, Debug, Serialize, Deserialize)]
157pub enum SynchroState {
158 #[default]
159 Running,
160 Paused,
161}
162
163impl Display for SynchroState {
164 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
165 write!(f, "{:?}", self)
166 }
167}
168
169impl TryFrom<&str> for SynchroState {
170 type Error = ();
171
172 fn try_from(value: &str) -> Result<Self, Self::Error> {
173 match value {
174 "Running" => Ok(Self::Running),
175 "Paused" => Ok(Self::Paused),
176 _ => Err(()),
177 }
178 }
179}
180
181#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
189pub struct AnySynchroRef {
190 local: AnyFsRef,
191 remote: AnyFsRef,
192 status: SynchroStatus,
195 state: SynchroState,
197 name: String,
198}
199
200impl Display for AnySynchroRef {
201 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202 write!(f, "synchro between {} and {}", self.local, self.remote)
203 }
204}
205
206impl AnySynchroRef {
207 pub fn new(local_ref: AnyFsRef, remote_ref: AnyFsRef, name: String) -> Self {
208 AnySynchroRef {
209 local: local_ref,
210 remote: remote_ref,
211 status: SynchroStatus::default(),
212 state: SynchroState::default(),
213 name,
214 }
215 }
216
217 pub fn local(&self) -> &AnyFsRef {
219 &self.local
220 }
221
222 pub fn remote(&self) -> &AnyFsRef {
224 &self.remote
225 }
226
227 pub fn name(&self) -> &str {
232 &self.name
233 }
234
235 pub fn status(&self) -> SynchroStatus {
237 self.status
238 }
239
240 pub fn set_status(&mut self, status: SynchroStatus) {
242 self.status = status
243 }
244
245 pub fn state(&self) -> SynchroState {
247 self.state
248 }
249
250 pub fn set_state(&mut self, state: SynchroState) {
252 self.state = state
253 }
254}
255
256#[derive(Clone, Debug, Serialize, Deserialize)]
258pub enum AnyFsCreationInfo {
259 LocalDir(<LocalDir as FSBackend>::CreationInfo),
260 Nextcloud(<NextcloudFs as FSBackend>::CreationInfo),
261}
262
263impl AnyFsCreationInfo {
264 pub async fn validate(&self) -> Result<(), String> {
265 match self {
266 AnyFsCreationInfo::LocalDir(info) => LocalDir::validate(info)
267 .await
268 .map_err(|_| "Invalid directory for synchronization".to_string()),
269 AnyFsCreationInfo::Nextcloud(info) => NextcloudFs::validate(info).await.map_err(|e| {
270 let msg = if let Some(msg) = e.protocol_error_message() {
271 msg
272 } else {
273 e.to_string()
274 };
275 format!("Failed to connect to Nextcloud server: {msg}")
276 }),
277 }
278 }
279}
280
281#[derive(Debug, Clone)]
283pub struct AnySynchroCreationInfo {
284 local: AnyFsCreationInfo,
285 remote: AnyFsCreationInfo,
286 name: Option<String>,
287}
288
289impl AnySynchroCreationInfo {
290 pub fn new(local: AnyFsCreationInfo, remote: AnyFsCreationInfo, name: Option<String>) -> Self {
291 Self {
292 local,
293 remote,
294 name,
295 }
296 }
297
298 pub fn local(&self) -> &AnyFsCreationInfo {
299 &self.local
300 }
301
302 pub fn remote(&self) -> &AnyFsCreationInfo {
303 &self.remote
304 }
305
306 pub fn name(&self) -> Option<&str> {
307 self.name.as_deref()
308 }
309}
310
311#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
315pub enum AnyFsDescription {
316 LocalDir(<LocalDir as FSBackend>::Description),
317 Nextcloud(<NextcloudFs as FSBackend>::Description),
318}
319
320impl AnyFsDescription {
321 pub fn name(&self) -> &str {
322 match self {
323 AnyFsDescription::LocalDir(desc) => desc.name(),
324 AnyFsDescription::Nextcloud(desc) => desc.name(),
325 }
326 }
327
328 pub fn type_name(&self) -> &str {
329 match self {
330 AnyFsDescription::LocalDir(_) => LocalDir::TYPE_NAME,
331 AnyFsDescription::Nextcloud(_) => NextcloudFs::TYPE_NAME,
332 }
333 }
334}
335
336impl Display for AnyFsDescription {
337 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
338 match self {
339 AnyFsDescription::LocalDir(local) => local.fmt(f),
340 AnyFsDescription::Nextcloud(nextcloud) => nextcloud.fmt(f),
341 }
342 }
343}
344
345impl From<AnyFsCreationInfo> for AnyFsDescription {
346 fn from(value: AnyFsCreationInfo) -> Self {
347 match value {
348 AnyFsCreationInfo::LocalDir(dir) => Self::LocalDir(dir.into()),
349 AnyFsCreationInfo::Nextcloud(nextcloud) => Self::Nextcloud(nextcloud.into()),
350 }
351 }
352}
353
354#[tarpc::service]
355pub trait BrumeService {
356 async fn new_synchro(
358 local: AnyFsCreationInfo,
359 remote: AnyFsCreationInfo,
360 name: Option<String>,
361 ) -> Result<(), String>;
362
363 async fn list_synchros() -> HashMap<SynchroId, AnySynchroRef>;
365
366 async fn delete_synchro(id: SynchroId) -> Result<(), String>;
368
369 async fn pause_synchro(id: SynchroId) -> Result<(), String>;
371
372 async fn resume_synchro(id: SynchroId) -> Result<(), String>;
374
375 async fn resolve_conflict(
377 id: SynchroId,
378 path: VirtualPathBuf,
379 side: SynchroSide,
380 ) -> Result<(), String>;
381}