bitwarden_state/settings/setting.rs
1//! Setting types for type-safe access to individual settings.
2
3use std::sync::Arc;
4
5use serde::{Deserialize, Serialize};
6use thiserror::Error;
7
8use crate::{persist::Persist, registry::StateRegistryError, sdk_managed::DatabaseError};
9
10/// Internal setting value as stored in the SDK-managed database.
11///
12/// This type wraps a JSON value for flexible storage. Users should not work with
13/// this type directly - use the [`Setting<T>`] handle via `StateClient::setting()` instead,
14/// which provides type-safe access.
15#[doc(hidden)]
16#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
17pub struct SettingItem(pub(crate) serde_json::Value);
18
19crate::register_repository_item!(String => SettingItem, "Setting");
20
21#[doc(hidden)]
22#[async_trait::async_trait]
23pub trait SettingTrait<T: Persist>: Send + Sync {
24 async fn get(&self) -> Result<Option<T>, SettingsError>;
25 async fn set(&self, value: T) -> Result<(), SettingsError>;
26 async fn remove(&self) -> Result<(), SettingsError>;
27}
28
29/// A handle to a single setting value in storage.
30///
31/// This type provides async methods to get, update, and delete the setting value.
32/// Obtained via `StateClient::setting()`.
33///
34/// # Example
35/// ```rust,ignore
36/// use bitwarden_state::register_setting_key;
37///
38/// register_setting_key!(const THEME: String = "theme");
39///
40/// let setting = client.platform().state().setting(THEME)?;
41///
42/// // Get the current value
43/// let value: Option<String> = setting.get().await?;
44///
45/// // Update the value
46/// setting.update("dark".to_string()).await?;
47///
48/// // Delete the value
49/// setting.delete().await?;
50/// ```
51#[derive(Clone)]
52pub struct Setting<T: Persist> {
53 backend: Arc<dyn SettingTrait<T>>,
54}
55
56impl<T: Persist> Setting<T> {
57 /// Create a new setting handle from a backend.
58 ///
59 /// The backend is already bound to a single key, so it decides where the value is stored.
60 pub fn new(backend: Arc<dyn SettingTrait<T>>) -> Self {
61 Self { backend }
62 }
63
64 /// Get the current value of this setting.
65 ///
66 /// Returns `None` if the setting doesn't exist in storage.
67 ///
68 /// # Errors
69 ///
70 /// Returns an error if deserialization fails, which may indicate:
71 /// - Schema evolution problems (type definition changed)
72 /// - Data corruption
73 /// - Type mismatch (wrong `Key<T>` type for stored data)
74 pub async fn get(&self) -> Result<Option<T>, SettingsError> {
75 self.backend.get().await
76 }
77
78 /// Update (or create) this setting with a new value.
79 pub async fn update(&self, value: T) -> Result<(), SettingsError> {
80 self.backend.set(value).await
81 }
82
83 /// Delete this setting from storage.
84 pub async fn delete(&self) -> Result<(), SettingsError> {
85 self.backend.remove().await
86 }
87}
88
89/// Errors that can occur when working with settings.
90#[derive(Debug, Error)]
91pub enum SettingsError {
92 /// Failed to serialize/deserialize setting value
93 #[error("Failed to serialize/deserialize setting: {0}")]
94 Json(#[from] serde_json::Error),
95 /// Database operation failed
96 #[error(transparent)]
97 Database(#[from] DatabaseError),
98 /// State registry operation failed
99 #[error(transparent)]
100 Registry(#[from] StateRegistryError),
101}