Skip to main content

lb_rs/ipc/
protocol.rs

1use std::io;
2
3use serde::{Deserialize, Serialize};
4use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
5use uuid::Uuid;
6
7use crate::model::account::Account;
8use crate::model::api::{
9    AccountFilter, AccountIdentifier, AdminSetUserTierInfo, ServerIndex, StripeAccountTier,
10};
11use crate::model::file::ShareMode;
12use crate::model::file_metadata::{DocumentHmac, FileType};
13use crate::model::path_ops::Filter;
14use crate::service::activity::RankingWeights;
15use crate::service::events::Event;
16#[cfg(not(target_family = "wasm"))]
17use crate::subscribers::search::SearchConfig;
18
19#[derive(Debug, Serialize, Deserialize)]
20pub enum Frame {
21    Request { seq: u64, body: Request },
22    Response { seq: u64, output: Vec<u8> },
23    Event { stream_seq: u64, body: Event },
24    EventEnd { stream_seq: u64 },
25}
26
27impl Frame {
28    pub async fn read<R: AsyncRead + Unpin>(r: &mut R) -> io::Result<Self> {
29        let mut len_buf = [0u8; 4];
30        r.read_exact(&mut len_buf).await?;
31        let len = u32::from_le_bytes(len_buf) as usize;
32        let mut buf = vec![0u8; len];
33        r.read_exact(&mut buf).await?;
34        bincode::deserialize(&buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
35    }
36
37    pub async fn write<W: AsyncWrite + Unpin>(&self, w: &mut W) -> io::Result<()> {
38        let bytes =
39            bincode::serialize(self).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
40        let len: u32 = bytes.len().try_into().map_err(|_| {
41            io::Error::new(
42                io::ErrorKind::InvalidData,
43                format!("frame {} bytes does not fit in a u32 length prefix", bytes.len()),
44            )
45        })?;
46        w.write_all(&len.to_le_bytes()).await?;
47        w.write_all(&bytes).await?;
48        w.flush().await
49    }
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
53pub enum Request {
54    CreateAccount {
55        username: String,
56        api_url: String,
57        welcome_doc: bool,
58    },
59    ImportAccount {
60        key: String,
61        api_url: Option<String>,
62    },
63    ImportAccountPrivateKeyV1 {
64        account: Account,
65    },
66    ImportAccountPhrase {
67        phrase: Vec<String>,
68        api_url: String,
69    },
70    DeleteAccount,
71    GetAccount,
72
73    SuggestedDocs {
74        settings: RankingWeights,
75    },
76    ClearSuggested,
77    ClearSuggestedId {
78        id: Uuid,
79    },
80    AppForegrounded,
81
82    DisappearAccount {
83        username: String,
84    },
85    DisappearFile {
86        id: Uuid,
87    },
88    ListUsers {
89        filter: Option<AccountFilter>,
90    },
91    GetAccountInfo {
92        identifier: AccountIdentifier,
93    },
94    AdminValidateAccount {
95        username: String,
96    },
97    AdminValidateServer,
98    AdminFileInfo {
99        id: Uuid,
100    },
101    RebuildIndex {
102        index: ServerIndex,
103    },
104    SetUserTier {
105        username: String,
106        info: AdminSetUserTierInfo,
107    },
108
109    UpgradeAccountStripe {
110        account_tier: StripeAccountTier,
111    },
112    UpgradeAccountGooglePlay {
113        purchase_token: String,
114        account_id: String,
115    },
116    UpgradeAccountAppStore {
117        original_transaction_id: String,
118        app_account_token: String,
119    },
120    CancelSubscription,
121    GetSubscriptionInfo,
122
123    #[cfg(not(target_family = "wasm"))]
124    RecentPanic,
125    #[cfg(not(target_family = "wasm"))]
126    WritePanicToFile {
127        error_header: String,
128        bt: String,
129    },
130    #[cfg(not(target_family = "wasm"))]
131    DebugInfo {
132        os_info: String,
133        check_docs: bool,
134    },
135
136    ReadDocument {
137        id: Uuid,
138        user_activity: bool,
139    },
140    WriteDocument {
141        id: Uuid,
142        content: Vec<u8>,
143    },
144    ReadDocumentWithHmac {
145        id: Uuid,
146        user_activity: bool,
147    },
148    SafeWrite {
149        id: Uuid,
150        old_hmac: Option<DocumentHmac>,
151        content: Vec<u8>,
152    },
153
154    CreateFile {
155        name: String,
156        parent: Uuid,
157        file_type: FileType,
158    },
159    RenameFile {
160        id: Uuid,
161        new_name: String,
162    },
163    MoveFile {
164        id: Uuid,
165        new_parent: Uuid,
166    },
167    Delete {
168        id: Uuid,
169    },
170    Root,
171    ListMetadatas,
172    GetChildren {
173        id: Uuid,
174    },
175    GetAndGetChildrenRecursively {
176        id: Uuid,
177    },
178    GetFileById {
179        id: Uuid,
180    },
181    GetFileLinkUrl {
182        id: Uuid,
183    },
184    LocalChanges,
185
186    TestRepoIntegrity {
187        check_docs: bool,
188    },
189
190    CreateLinkAtPath {
191        path: String,
192        target_id: Uuid,
193    },
194    CreateAtPath {
195        path: String,
196    },
197    GetByPath {
198        path: String,
199    },
200    GetPathById {
201        id: Uuid,
202    },
203    ListPaths {
204        filter: Option<Filter>,
205    },
206    ListPathsWithIds {
207        filter: Option<Filter>,
208    },
209
210    ShareFile {
211        id: Uuid,
212        username: String,
213        mode: ShareMode,
214    },
215    GetPendingShares,
216    GetPendingShareFiles,
217    KnownUsernames,
218    RejectShare {
219        id: Uuid,
220    },
221
222    PinFile {
223        id: Uuid,
224    },
225    UnpinFile {
226        id: Uuid,
227    },
228    ListPinned,
229
230    GetUsage,
231
232    Sync,
233    Status,
234    GetLastSynced,
235    GetLastSyncedHuman,
236    Subscribe,
237    #[cfg(not(target_family = "wasm"))]
238    BuildIndex,
239    #[cfg(not(target_family = "wasm"))]
240    ReloadSearchIndex,
241    #[cfg(not(target_family = "wasm"))]
242    Search {
243        input: String,
244        cfg: SearchConfig,
245    },
246}
247
248#[cfg(test)]
249mod tests {
250    use super::*;
251    use tokio::io::duplex;
252
253    #[tokio::test]
254    async fn frame_round_trip() {
255        let (mut a, mut b) = duplex(64 * 1024);
256        let frame = Frame::Request { seq: 7, body: Request::Sync };
257        frame.write(&mut a).await.unwrap();
258        let got = Frame::read(&mut b).await.unwrap();
259        assert!(matches!(got, Frame::Request { seq: 7, body: Request::Sync }));
260    }
261}