sos_preferences/
preferences.rs

1//! Global preferences and account-specific preferences
2//! cached in-memory.
3//!
4//! Preference are backed by a storage provider which may
5//! be either a JSON document on disc or a database table
6//! depending upon the backend implementation.
7use crate::Error;
8use async_trait::async_trait;
9use serde::{Deserialize, Serialize};
10use serde_json::Value;
11use sos_core::{AccountId, PublicIdentity};
12use std::{
13    collections::HashMap,
14    fmt,
15    ops::{Deref, DerefMut},
16    sync::Arc,
17};
18use tokio::sync::Mutex;
19
20/// Boxed storage provider.
21pub type PreferenceStorageProvider<E> =
22    Box<dyn PreferencesStorage<Error = E> + Send + Sync + 'static>;
23
24/// Preference manager for global and account preferences.
25#[async_trait]
26pub trait PreferenceManager {
27    /// Error type.
28    type Error: std::error::Error
29        + std::fmt::Debug
30        + From<Error>
31        + Send
32        + 'static;
33
34    /// Load global preferences.
35    async fn load_global_preferences(&mut self) -> Result<(), Self::Error>;
36
37    /// Load and initialize preferences for a list of accounts.
38    async fn load_account_preferences(
39        &self,
40        accounts: &[PublicIdentity],
41    ) -> Result<(), Self::Error>;
42
43    /// Global preferences for all accounts.
44    fn global_preferences(&self) -> Arc<Mutex<Preferences<Self::Error>>>;
45
46    /// Preferences for an account.
47    async fn account_preferences(
48        &self,
49        account_id: &AccountId,
50    ) -> Option<Arc<Mutex<Preferences<Self::Error>>>>;
51
52    /// Add a new account to the preference manager.
53    ///
54    /// If preferences exist for an account they are loaded
55    /// into memory otherwise empty preferences are used.
56    async fn new_account(
57        &self,
58        account_id: &AccountId,
59    ) -> Result<(), Self::Error>;
60}
61
62/// Storage provider for account preferences.
63#[async_trait]
64pub trait PreferencesStorage {
65    /// Error type.
66    type Error: std::error::Error
67        + std::fmt::Debug
68        + From<Error>
69        + Send
70        + 'static;
71
72    /// Load preferences from storage.
73    async fn load_preferences(
74        &self,
75        account_id: Option<&AccountId>,
76    ) -> Result<PreferenceMap, Self::Error>;
77
78    /// Insert preference into storage.
79    async fn insert_preference(
80        &self,
81        account_id: Option<&AccountId>,
82        // preferences: &PreferenceMap,
83        key: &str,
84        pref: &Preference,
85    ) -> Result<(), Self::Error>;
86
87    /// Remove preference from storage.
88    async fn remove_preference(
89        &self,
90        account_id: Option<&AccountId>,
91        // preferences: &PreferenceMap,
92        key: &str,
93    ) -> Result<(), Self::Error>;
94
95    /// Remove all preferences from storage.
96    async fn clear_preferences(
97        &self,
98        account_id: Option<&AccountId>,
99        // preferences: &PreferenceMap,
100    ) -> Result<(), Self::Error>;
101}
102
103/// Global preferences and account preferences loaded into memory.
104pub struct CachedPreferences<E>
105where
106    E: std::error::Error + std::fmt::Debug + From<Error> + Send + 'static,
107{
108    provider: Arc<PreferenceStorageProvider<E>>,
109    globals: Arc<Mutex<Preferences<E>>>,
110    accounts: Mutex<HashMap<AccountId, Arc<Mutex<Preferences<E>>>>>,
111}
112
113#[async_trait]
114impl<E> PreferenceManager for CachedPreferences<E>
115where
116    E: std::error::Error + std::fmt::Debug + From<Error> + Send + 'static,
117{
118    type Error = E;
119
120    /// Load global preferences.
121    async fn load_global_preferences(&mut self) -> Result<(), E> {
122        let mut globals = self.globals.lock().await;
123        let map = globals.provider.load_preferences(None).await?;
124        globals.values = map;
125        Ok(())
126    }
127
128    /// Load and initialize account preferences from disc.
129    async fn load_account_preferences(
130        &self,
131        accounts: &[PublicIdentity],
132    ) -> Result<(), E> {
133        for account in accounts {
134            self.new_account(account.account_id()).await?;
135        }
136        Ok(())
137    }
138
139    /// Global preferences for all accounts.
140    fn global_preferences(&self) -> Arc<Mutex<Preferences<E>>> {
141        self.globals.clone()
142    }
143
144    /// Preferences for an account.
145    async fn account_preferences(
146        &self,
147        account_id: &AccountId,
148    ) -> Option<Arc<Mutex<Preferences<E>>>> {
149        let cache = self.accounts.lock().await;
150        cache.get(account_id).map(Arc::clone)
151    }
152
153    /// Add a new account to the cached preferences.
154    ///
155    /// If a preferences file exists for an account it is loaded
156    /// into memory otherwise empty preferences are used.
157    async fn new_account(&self, account_id: &AccountId) -> Result<(), E> {
158        let mut prefs =
159            Preferences::<E>::new(self.provider.clone(), Some(*account_id));
160        prefs.load().await?;
161
162        let mut cache = self.accounts.lock().await;
163        cache.insert(*account_id, Arc::new(Mutex::new(prefs)));
164        Ok(())
165    }
166}
167
168impl<E> CachedPreferences<E>
169where
170    E: std::error::Error + std::fmt::Debug + From<Error> + Send + 'static,
171{
172    /// Create new cached preferences.
173    pub fn new(provider: Arc<PreferenceStorageProvider<E>>) -> Self {
174        Self {
175            globals: Arc::new(Mutex::new(Preferences::<E>::new(
176                provider.clone(),
177                None,
178            ))),
179            accounts: Mutex::new(HashMap::new()),
180            provider,
181        }
182    }
183}
184
185/// Preference value.
186#[derive(Debug, Clone, Serialize, Deserialize)]
187#[serde(untagged)]
188pub enum Preference {
189    /// Boolean value.
190    Bool(bool),
191    /// Number value.
192    Number(f64),
193    /// String value.
194    String(String),
195    /// List of strings.
196    StringList(Vec<String>),
197    /// Complex types.
198    Json(Value),
199}
200
201impl fmt::Display for Preference {
202    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203        match self {
204            Self::Bool(val) => write!(f, "{}", val),
205            Self::Number(val) => write!(f, "{}", val),
206            Self::String(val) => write!(f, "{}", val),
207            Self::StringList(val) => {
208                write!(f, "[")?;
209                for (index, s) in val.iter().enumerate() {
210                    write!(f, r#""{}""#, s)?;
211                    if index < val.len() - 1 {
212                        write!(f, ", ")?;
213                    }
214                }
215                write!(f, "]")
216            }
217            Self::Json(val) => write!(f, "{:?}", serde_json::to_string(val)),
218        }
219    }
220}
221
222impl From<bool> for Preference {
223    fn from(value: bool) -> Self {
224        Self::Bool(value)
225    }
226}
227
228impl From<f64> for Preference {
229    fn from(value: f64) -> Self {
230        Self::Number(value)
231    }
232}
233
234impl From<i64> for Preference {
235    fn from(value: i64) -> Self {
236        Self::Number(value as f64)
237    }
238}
239
240impl From<String> for Preference {
241    fn from(value: String) -> Self {
242        Self::String(value)
243    }
244}
245
246impl From<Vec<String>> for Preference {
247    fn from(value: Vec<String>) -> Self {
248        Self::StringList(value)
249    }
250}
251
252impl From<Value> for Preference {
253    fn from(value: Value) -> Self {
254        Self::Json(value)
255    }
256}
257
258/// Collection of preferences.
259#[derive(Debug, Default, Clone, Serialize, Deserialize)]
260pub struct PreferenceMap(HashMap<String, Preference>);
261
262impl Deref for PreferenceMap {
263    type Target = HashMap<String, Preference>;
264
265    fn deref(&self) -> &Self::Target {
266        &self.0
267    }
268}
269
270impl DerefMut for PreferenceMap {
271    fn deref_mut(&mut self) -> &mut Self::Target {
272        &mut self.0
273    }
274}
275
276/// Preferences collection with a backing storage provider.
277pub struct Preferences<E>
278where
279    E: std::error::Error + std::fmt::Debug + From<Error> + Send + 'static,
280{
281    /// Account identifier.
282    account_id: Option<AccountId>,
283    /// Preference values.
284    values: PreferenceMap,
285    /// Storage provider.
286    provider: Arc<PreferenceStorageProvider<E>>,
287}
288
289impl<E> Preferences<E>
290where
291    E: std::error::Error + std::fmt::Debug + From<Error> + Send + 'static,
292{
293    /// Create new preferences using the given storage provider.
294    pub fn new(
295        provider: Arc<PreferenceStorageProvider<E>>,
296        account_id: Option<AccountId>,
297    ) -> Self {
298        Self {
299            account_id,
300            values: Default::default(),
301            provider,
302        }
303    }
304
305    /// Load the preferences from storage.
306    pub async fn load(&mut self) -> Result<(), E> {
307        self.values = self
308            .provider
309            .load_preferences(self.account_id.as_ref())
310            .await?;
311        Ok(())
312    }
313
314    /// Number of preferences.
315    pub fn len(&self) -> usize {
316        self.values.0.len()
317    }
318
319    /// Whether the preferences collection is empty.
320    pub fn is_empty(&self) -> bool {
321        self.values.0.is_empty()
322    }
323
324    /// Map of preferences.
325    pub fn values(&self) -> &PreferenceMap {
326        &self.values
327    }
328
329    /// Iterator of the preferences.
330    pub fn iter(&self) -> impl Iterator<Item = (&String, &Preference)> {
331        self.values.0.iter()
332    }
333
334    /// Get a number preference.
335    pub fn get_number(
336        &self,
337        key: impl AsRef<str>,
338    ) -> Result<Option<&Preference>, E> {
339        let result = self.values.0.get(key.as_ref());
340        if let Some(res) = result.as_ref() {
341            if matches!(res, Preference::Number(_)) {
342                Ok(result)
343            } else {
344                Err(Error::PreferenceTypeNumber(key.as_ref().to_owned())
345                    .into())
346            }
347        } else {
348            Ok(None)
349        }
350    }
351
352    /// Get a boolean preference.
353    pub fn get_bool(
354        &self,
355        key: impl AsRef<str>,
356    ) -> Result<Option<&Preference>, E> {
357        let result = self.values.0.get(key.as_ref());
358        if let Some(res) = result.as_ref() {
359            if matches!(res, Preference::Bool(_)) {
360                Ok(result)
361            } else {
362                Err(Error::PreferenceTypeBool(key.as_ref().to_owned()).into())
363            }
364        } else {
365            Ok(None)
366        }
367    }
368
369    /// Get a string preference.
370    pub fn get_string(
371        &self,
372        key: impl AsRef<str>,
373    ) -> Result<Option<&Preference>, E> {
374        let result = self.values.0.get(key.as_ref());
375        if let Some(res) = result.as_ref() {
376            if matches!(res, Preference::String(_)) {
377                Ok(result)
378            } else {
379                Err(Error::PreferenceTypeString(key.as_ref().to_owned())
380                    .into())
381            }
382        } else {
383            Ok(None)
384        }
385    }
386
387    /// Get a string list preference.
388    pub fn get_string_list(
389        &self,
390        key: impl AsRef<str>,
391    ) -> Result<Option<&Preference>, E> {
392        let result = self.values.0.get(key.as_ref());
393        if let Some(res) = result.as_ref() {
394            if matches!(res, Preference::StringList(_)) {
395                Ok(result)
396            } else {
397                Err(Error::PreferenceTypeStringList(key.as_ref().to_owned())
398                    .into())
399            }
400        } else {
401            Ok(None)
402        }
403    }
404
405    /// Get a JSON value preference.
406    pub fn get_json_value(
407        &self,
408        key: impl AsRef<str>,
409    ) -> Result<Option<&Preference>, E> {
410        let result = self.values.0.get(key.as_ref());
411        if let Some(res) = result.as_ref() {
412            if matches!(res, Preference::Json(_)) {
413                Ok(result)
414            } else {
415                Err(Error::PreferenceTypeJsonValue(key.as_ref().to_owned())
416                    .into())
417            }
418        } else {
419            Ok(None)
420        }
421    }
422
423    /// Get a preference without checking the type.
424    pub fn get_unchecked(&self, key: impl AsRef<str>) -> Option<&Preference> {
425        self.values.0.get(key.as_ref())
426    }
427
428    /// Insert a preference.
429    ///
430    /// If the preference already exists it is overwritten.
431    pub async fn insert(
432        &mut self,
433        key: String,
434        value: Preference,
435    ) -> Result<(), E> {
436        self.provider
437            .insert_preference(self.account_id.as_ref(), &key, &value)
438            .await?;
439        self.values.0.insert(key, value);
440        Ok(())
441    }
442
443    /// Remove a preference.
444    pub async fn remove(
445        &mut self,
446        key: impl AsRef<str>,
447    ) -> Result<Option<Preference>, E> {
448        let pref = self.values.0.remove(key.as_ref());
449        self.provider
450            .remove_preference(self.account_id.as_ref(), key.as_ref())
451            .await?;
452        Ok(pref)
453    }
454
455    /// Clear all preferences.
456    pub async fn clear(&mut self) -> Result<(), E> {
457        self.values = Default::default();
458        self.provider
459            .clear_preferences(self.account_id.as_ref())
460            .await?;
461        Ok(())
462    }
463}