sos_protocol/traits.rs
1use crate::{
2 DiffRequest, DiffResponse, PatchRequest, PatchResponse, ScanRequest,
3 ScanResponse, SyncOptions,
4};
5use async_trait::async_trait;
6use sos_core::Origin;
7use sos_sync::{CreateSet, MergeOutcome, SyncPacket, SyncStatus, UpdateSet};
8
9/// Result of a sync operation with a single remote.
10#[derive(Debug)]
11pub struct RemoteResult<E> {
12 /// Origin of the remote.
13 pub origin: Origin,
14 /// Result of the sync operation.
15 pub result: Result<Option<MergeOutcome>, E>,
16}
17
18/// Result of a sync operation.
19#[derive(Debug)]
20pub struct SyncResult<E> {
21 /// Result of syncing with remote data sources.
22 pub remotes: Vec<RemoteResult<E>>,
23}
24
25impl<E> Default for SyncResult<E> {
26 fn default() -> Self {
27 Self {
28 remotes: Vec::new(),
29 }
30 }
31}
32
33impl<E> SyncResult<E> {
34 /// Find the first sync error.
35 pub fn first_error(self) -> Option<E> {
36 self.remotes.into_iter().find_map(|res| {
37 if res.result.is_err() {
38 res.result.err()
39 } else {
40 None
41 }
42 })
43 }
44
45 /// Find the first sync error by reference.
46 pub fn first_error_ref(&self) -> Option<&E> {
47 self.remotes.iter().find_map(|res| {
48 if let Err(e) = &res.result {
49 Some(e)
50 } else {
51 None
52 }
53 })
54 }
55
56 /// Determine if the sync has one or more errors.
57 pub fn has_error(&self) -> bool {
58 self.remotes.iter().any(|r| r.result.is_err())
59 }
60}
61
62/// Trait for types that can sync with a single remote.
63#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
64#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
65pub trait RemoteSync {
66 /// Error type for remote sync.
67 type Error: std::error::Error + std::fmt::Debug;
68
69 /// Perform a full sync of the account using
70 /// the default options.
71 ///
72 /// If the account does not exist on the remote
73 /// server the account will be created and
74 /// [RemoteSync::sync_file_transfers] will be called
75 /// to ensure the transfers queue is synced.
76 async fn sync(&self) -> RemoteResult<Self::Error>;
77
78 /// Perform a full sync of the account
79 /// using the given options.
80 ///
81 /// See the documentation for [RemoteSync::sync] for more details.
82 async fn sync_with_options(
83 &self,
84 options: &SyncOptions,
85 ) -> RemoteResult<Self::Error>;
86
87 /// Force update an account on remote servers.
88 ///
89 /// Should be called after making destructive
90 /// changes to an account's folders. For example, if
91 /// the encryption cipher has been changed, a folder
92 /// password was changed or folder(s) were compacted.
93 async fn force_update(
94 &self,
95 account_data: UpdateSet,
96 ) -> RemoteResult<Self::Error>;
97
98 /// Sync file transfers.
99 ///
100 /// Updates the file transfers queue with any pending
101 /// uploads or downloads by comparing the local file
102 /// state with the file state on remote server(s).
103 #[cfg(feature = "files")]
104 async fn sync_file_transfers(&self) -> RemoteResult<Self::Error>;
105}
106
107/// Trait for types that can sync with multiple remotes.
108#[async_trait]
109pub trait AccountSync {
110 /// Error type for account sync.
111 type Error: std::error::Error + std::fmt::Debug;
112
113 /// Perform a full sync of the account using
114 /// the default options.
115 ///
116 /// If the account does not exist on the remote
117 /// server the account will be created and
118 /// [RemoteSync::sync_file_transfers] will be called
119 /// to ensure the transfers queue is synced.
120 async fn sync(&self) -> SyncResult<Self::Error>;
121
122 /// Perform a full sync of the account
123 /// using the given options.
124 ///
125 /// See the documentation for [RemoteSync::sync] for more details.
126 async fn sync_with_options(
127 &self,
128 options: &SyncOptions,
129 ) -> SyncResult<Self::Error>;
130
131 /// Sync file transfers.
132 ///
133 /// Updates the file transfers queue with any pending
134 /// uploads or downloads by comparing the local file
135 /// state with the file state on remote server(s).
136 #[cfg(feature = "files")]
137 async fn sync_file_transfers(
138 &self,
139 options: &SyncOptions,
140 ) -> SyncResult<Self::Error>;
141
142 /// Force update an account on remote servers.
143 ///
144 /// Should be called after making destructive
145 /// changes to an account's folders. For example, if
146 /// the encryption cipher has been changed, a folder
147 /// password was changed or folder(s) were compacted.
148 async fn force_update(
149 &self,
150 account_data: UpdateSet,
151 options: &SyncOptions,
152 ) -> SyncResult<Self::Error>;
153}
154
155/// Client that can communicate with a remote data source.
156#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
157#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
158pub trait SyncClient {
159 /// Error type for sync client.
160 type Error: std::error::Error + std::fmt::Debug;
161
162 /// Origin of the remote server.
163 fn origin(&self) -> &Origin;
164
165 /// Check if an account already exists.
166 async fn account_exists(&self) -> Result<bool, Self::Error>;
167
168 /// Create a new account.
169 async fn create_account(
170 &self,
171 account: CreateSet,
172 ) -> Result<(), Self::Error>;
173
174 /// Update an account.
175 async fn update_account(
176 &self,
177 account: UpdateSet,
178 ) -> Result<(), Self::Error>;
179
180 /// Fetch an account from a remote server.
181 async fn fetch_account(&self) -> Result<CreateSet, Self::Error>;
182
183 /// Delete the account on the server.
184 async fn delete_account(&self) -> Result<(), Self::Error>;
185
186 /// Sync status on the server.
187 async fn sync_status(&self) -> Result<SyncStatus, Self::Error>;
188
189 /// Sync with a remote.
190 async fn sync(
191 &self,
192 packet: SyncPacket,
193 ) -> Result<SyncPacket, Self::Error>;
194
195 /// Scan commits in an event log.
196 async fn scan(
197 &self,
198 request: ScanRequest,
199 ) -> Result<ScanResponse, Self::Error>;
200
201 /// Fetch a collection of event records since a given commit hash.
202 async fn diff(
203 &self,
204 request: DiffRequest,
205 ) -> Result<DiffResponse, Self::Error>;
206
207 /// Patch an event log.
208 ///
209 /// If the request contains a commit hash then the remote will
210 /// attempt to rewind to the commit before applying the patch.
211 async fn patch(
212 &self,
213 request: PatchRequest,
214 ) -> Result<PatchResponse, Self::Error>;
215}