1#![deny(missing_docs)]
2#![forbid(unsafe_code)]
3#![cfg_attr(all(doc, CHANNEL_NIGHTLY), feature(doc_auto_cfg))]
4mod 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
40pub(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#[derive(Clone)]
57pub enum BackendTarget {
58 FileSystem(Arc<Paths>),
60 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 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 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 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 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 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 #[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}