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