sos_backend/
lib.rs

1#![deny(missing_docs)]
2#![forbid(unsafe_code)]
3#![cfg_attr(all(doc, CHANNEL_NIGHTLY), feature(doc_auto_cfg))]
4//! Backend database and file system storage.
5mod access_point;
6#[cfg(feature = "archive")]
7pub mod archive;
8#[cfg(feature = "audit")]
9pub mod audit;
10pub mod compact;
11mod error;
12mod event_log;
13mod folder;
14mod helpers;
15#[cfg(feature = "preferences")]
16mod preferences;
17mod server_origins;
18#[cfg(feature = "system-messages")]
19mod system_messages;
20mod vault_writer;
21
22pub use access_point::BackendAccessPoint as AccessPoint;
23pub use error::{Error, StorageError};
24pub use event_log::{
25    AccountEventLog, BackendEventLog, DeviceEventLog, FolderEventLog,
26};
27pub use folder::Folder;
28pub use helpers::extract_vault;
29#[cfg(feature = "preferences")]
30pub use preferences::BackendPreferences as Preferences;
31pub use server_origins::ServerOrigins;
32pub use sos_database as database;
33#[cfg(feature = "system-messages")]
34pub use system_messages::SystemMessages;
35pub use vault_writer::VaultWriter;
36
37#[cfg(feature = "files")]
38pub use event_log::FileEventLog;
39
40/// Result type for the library.
41pub(crate) type Result<T> = std::result::Result<T, Error>;
42
43use sos_core::{AccountId, Paths, PublicIdentity};
44use sos_database::{
45    async_sqlite::Client,
46    entity::{AccountEntity, AccountRecord, FolderEntity, FolderRecord},
47    open_file,
48};
49use sos_vault::Summary;
50use std::{fmt, sync::Arc};
51
52#[cfg(feature = "files")]
53use {indexmap::IndexSet, sos_core::ExternalFile};
54
55/// Target backend.
56#[derive(Clone)]
57pub enum BackendTarget {
58    /// File system backend
59    FileSystem(Arc<Paths>),
60    /// Database backend.
61    Database(Arc<Paths>, Client),
62}
63
64impl fmt::Display for BackendTarget {
65    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66        write!(f, "{}", {
67            match self {
68                Self::FileSystem(_) => "filesystem",
69                Self::Database(_, _) => "database",
70            }
71        })
72    }
73}
74
75impl BackendTarget {
76    /// Create a backend target from paths.
77    pub async fn from_paths<T: AsRef<Paths>>(
78        paths: T,
79    ) -> Result<BackendTarget> {
80        Ok(if paths.as_ref().is_using_db() {
81            let client = open_file(paths.as_ref().database_file()).await?;
82            BackendTarget::Database(Arc::new(paths.as_ref().clone()), client)
83        } else {
84            BackendTarget::FileSystem(Arc::new(paths.as_ref().clone()))
85        })
86    }
87
88    /// Paths for the backend target.
89    pub fn paths(&self) -> Arc<Paths> {
90        match self {
91            Self::FileSystem(paths) => paths.clone(),
92            Self::Database(paths, _) => paths.clone(),
93        }
94    }
95
96    /// Set paths to be for an account identifier.
97    pub fn with_account_id(self, account_id: &AccountId) -> Self {
98        match self {
99            Self::FileSystem(paths) => {
100                Self::FileSystem(paths.with_account_id(account_id))
101            }
102            Self::Database(paths, client) => {
103                Self::Database(paths.with_account_id(account_id), client)
104            }
105        }
106    }
107
108    /// List accounts.
109    pub async fn list_accounts(&self) -> Result<Vec<PublicIdentity>> {
110        match self {
111            BackendTarget::FileSystem(paths) => {
112                Ok(sos_vault::list_accounts(Some(paths)).await?)
113            }
114            BackendTarget::Database(_, client) => {
115                let account_rows = client
116                    .conn_and_then(move |conn| {
117                        let account = AccountEntity::new(&conn);
118                        account.list_accounts()
119                    })
120                    .await?;
121                let mut accounts = Vec::new();
122                for row in account_rows {
123                    let record: AccountRecord = row.try_into()?;
124                    accounts.push(record.identity);
125                }
126                Ok(accounts)
127            }
128        }
129    }
130
131    /// List user folders for an account.
132    pub async fn list_folders(
133        &self,
134        account_id: &AccountId,
135    ) -> Result<Vec<Summary>> {
136        match self {
137            BackendTarget::FileSystem(paths) => {
138                let paths = paths.with_account_id(account_id);
139                Ok(sos_vault::list_local_folders(&paths)
140                    .await?
141                    .into_iter()
142                    .map(|(s, _)| s)
143                    .collect())
144            }
145            BackendTarget::Database(_, client) => {
146                let account_id = *account_id;
147                let folder_rows = client
148                    .conn_and_then(move |conn| {
149                        let account = AccountEntity::new(&conn);
150                        let folders = FolderEntity::new(&conn);
151                        let account_row = account.find_one(&account_id)?;
152                        folders.list_user_folders(account_row.row_id)
153                    })
154                    .await?;
155                let mut folders = Vec::new();
156                for row in folder_rows {
157                    let record = FolderRecord::from_row(row).await?;
158                    folders.push(record.summary);
159                }
160                Ok(folders)
161            }
162        }
163    }
164
165    /// List external files for this backend target.
166    #[cfg(feature = "files")]
167    pub async fn list_files(&self) -> Result<IndexSet<ExternalFile>> {
168        Ok(match self {
169            BackendTarget::FileSystem(paths) => {
170                sos_external_files::list_external_files(paths).await?
171            }
172            BackendTarget::Database(paths, _) => {
173                sos_external_files::list_external_files(paths).await?
174            }
175        })
176    }
177}