bitwarden_state/persisted_value/
handle.rs1use std::{marker::PhantomData, sync::Arc};
4
5use serde::{Deserialize, Serialize};
6use thiserror::Error;
7
8use super::PersistedValueKey;
9use crate::{persist::Persist, sdk_managed::DatabaseError};
10
11#[doc(hidden)]
17#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
18pub struct PersistedValueItem(pub(crate) serde_json::Value);
19
20crate::register_repository_item!(String => PersistedValueItem, "PersistedValue");
21
22#[doc(hidden)]
23#[async_trait::async_trait]
24pub trait PersistedValueTrait<T: Persist>: Send + Sync {
25 async fn get(&self) -> Result<Option<T>, PersistedValueError>;
26 async fn set(&self, value: T) -> Result<(), PersistedValueError>;
27 async fn remove(&self) -> Result<(), PersistedValueError>;
28}
29
30macro_rules! trace_failure {
33 ($operation:literal, $name:expr, $error:expr) => {
34 tracing::error!(
35 persisted_value = $name,
36 operation = $operation,
37 error = %$error,
38 "persisted value operation failed"
39 )
40 };
41}
42
43pub struct PersistedValue<K: PersistedValueKey> {
69 backend: Arc<dyn PersistedValueTrait<K::Value>>,
70 _marker: PhantomData<K>,
71}
72
73impl<K: PersistedValueKey> Clone for PersistedValue<K> {
75 fn clone(&self) -> Self {
76 Self {
77 backend: self.backend.clone(),
78 _marker: PhantomData,
79 }
80 }
81}
82
83impl<K: PersistedValueKey> PersistedValue<K> {
84 pub fn new(backend: Arc<dyn PersistedValueTrait<K::Value>>) -> Self {
88 Self {
89 backend,
90 _marker: PhantomData,
91 }
92 }
93
94 pub async fn get(&self) -> Result<K::Value, PersistedValueError> {
97 self.backend
98 .get()
99 .await
100 .and_then(|value| value.ok_or(PersistedValueError::ValueNotFound(K::NAME)))
101 .inspect_err(|e| trace_failure!("get", K::NAME, e))
102 }
103
104 pub async fn try_get(&self) -> Result<Option<K::Value>, PersistedValueError> {
115 self.backend
116 .get()
117 .await
118 .inspect_err(|e| trace_failure!("try_get", K::NAME, e))
119 }
120
121 pub async fn set(&self, value: K::Value) -> Result<(), PersistedValueError> {
123 self.backend
124 .set(value)
125 .await
126 .inspect_err(|e| trace_failure!("set", K::NAME, e))
127 }
128
129 pub async fn remove(&self) -> Result<(), PersistedValueError> {
131 self.backend
132 .remove()
133 .await
134 .inspect_err(|e| trace_failure!("remove", K::NAME, e))
135 }
136}
137
138#[derive(Debug, Error)]
140pub enum PersistedValueError {
141 #[error("Persisted value '{0}' not found")]
143 ValueNotFound(&'static str),
144 #[error("Persisted value storage is closed")]
146 Closed,
147 #[error("{0}")]
149 Internal(String),
150}
151
152impl From<DatabaseError> for PersistedValueError {
153 fn from(e: DatabaseError) -> Self {
154 match e {
155 DatabaseError::Closed => Self::Closed,
156 other => Self::Internal(other.to_string()),
157 }
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164 use crate::{register_persisted_value, registry::StateRegistry};
165
166 register_persisted_value!(THEME: String = "persisted_value_test_theme");
167
168 #[tokio::test]
169 async fn test_get_fails_when_absent() {
170 let value = StateRegistry::new_with_memory_db().value::<THEME>();
171
172 assert!(matches!(
173 value.get().await,
174 Err(PersistedValueError::ValueNotFound(
175 "persisted_value_test_theme"
176 ))
177 ));
178 assert_eq!(value.try_get().await.unwrap(), None);
179 }
180
181 #[tokio::test]
182 async fn test_deserialization_error_does_not_contain_stored_value() {
183 register_persisted_value!(AS_STRING: String = "persisted_value_test_aliased");
184 register_persisted_value!(AS_NUMBER: u32 = "persisted_value_test_aliased");
185
186 let registry = StateRegistry::new_with_memory_db();
187 registry
188 .value::<AS_STRING>()
189 .set("secret-token".to_string())
190 .await
191 .unwrap();
192
193 let error = registry.value::<AS_NUMBER>().get().await.unwrap_err();
194
195 assert!(matches!(error, PersistedValueError::Internal(_)));
196 assert!(!error.to_string().contains("secret-token"));
197 }
198}