Skip to main content

shared/extensions/
settings.rs

1use std::{collections::HashMap, sync::Arc};
2
3pub struct SettingsSerializer {
4    pub database: Arc<crate::database::Database>,
5
6    prefix: compact_str::CompactString,
7    keys: Vec<compact_str::CompactString>,
8    values: Vec<compact_str::CompactString>,
9}
10
11impl SettingsSerializer {
12    pub(crate) fn new(
13        database: Arc<crate::database::Database>,
14        prefix: impl Into<compact_str::CompactString>,
15    ) -> Self {
16        Self {
17            database,
18            prefix: prefix.into(),
19            keys: Vec::new(),
20            values: Vec::new(),
21        }
22    }
23
24    pub(crate) fn key_prefix(&self, key: impl AsRef<str>) -> compact_str::CompactString {
25        compact_str::format_compact!("{}::{}", self.prefix, key.as_ref())
26    }
27
28    pub(crate) fn nest_prefix(&self, nested_prefix: &str) -> compact_str::CompactString {
29        compact_str::format_compact!(
30            "{}{}{}",
31            self.prefix,
32            if self.prefix.is_empty() { "" } else { "::" },
33            nested_prefix
34        )
35    }
36
37    /// Write a raw setting with the given key and value. The value will be stored as-is without any serialization or encryption.
38    pub fn write_raw_setting(
39        mut self,
40        key: impl AsRef<str>,
41        value: impl Into<compact_str::CompactString>,
42    ) -> Self {
43        self.keys.push(self.key_prefix(key));
44        self.values.push(value.into());
45
46        self
47    }
48
49    /// Write a raw setting with the given key and value, encrypting the value before storage. The value will be encrypted and stored as a base64 string.
50    pub async fn write_raw_encrypted_setting(
51        mut self,
52        key: impl AsRef<str>,
53        value: impl Into<compact_str::CompactString>,
54    ) -> Result<Self, anyhow::Error> {
55        let encrypted_value = self.database.encrypt_base64(value.into()).await?;
56
57        self.keys.push(self.key_prefix(key));
58        self.values.push(encrypted_value);
59
60        Ok(self)
61    }
62
63    /// Write a setting with the given key and value, serializing the value to JSON before storage. The value will be stored as a JSON string.
64    pub fn write_serde_setting(
65        mut self,
66        key: impl AsRef<str>,
67        value: &impl serde::Serialize,
68    ) -> Result<Self, serde_json::Error> {
69        let serialized = serde_json::to_string(value)?;
70        self.keys.push(self.key_prefix(key));
71        self.values
72            .push(compact_str::CompactString::from(serialized));
73
74        Ok(self)
75    }
76
77    /// Write a setting with the given key and value, serializing the value to JSON and encrypting it before storage. The value will be encrypted and stored as a base64 string.
78    pub async fn write_serde_encrypted_setting(
79        mut self,
80        key: impl AsRef<str>,
81        value: &impl serde::Serialize,
82    ) -> Result<Self, anyhow::Error> {
83        let serialized = serde_json::to_string(value)?;
84        let encrypted_value = self.database.encrypt_base64(serialized).await?;
85
86        self.keys.push(self.key_prefix(key));
87        self.values.push(encrypted_value);
88
89        Ok(self)
90    }
91
92    /// Nest another set of settings under the given nested prefix. The nested settings will be serialized with a prefix of `self.prefix::nested_prefix`.
93    pub async fn nest(
94        mut self,
95        nested_prefix: &str,
96        settings: &impl SettingsSerializeExt,
97    ) -> Result<Self, anyhow::Error> {
98        let serializer = Self::new(self.database.clone(), self.nest_prefix(nested_prefix));
99
100        let nested_serializer = settings.serialize(serializer).await?;
101
102        self.keys.extend(nested_serializer.keys);
103        self.values.extend(nested_serializer.values);
104
105        Ok(self)
106    }
107
108    pub fn merge(mut self, other: SettingsSerializer) -> Self {
109        self.keys.extend(other.keys);
110        self.values.extend(other.values);
111
112        self
113    }
114
115    pub(crate) fn into_parts(
116        self,
117    ) -> (
118        Vec<compact_str::CompactString>,
119        Vec<compact_str::CompactString>,
120    ) {
121        (self.keys, self.values)
122    }
123}
124
125pub struct SettingsDeserializer<'a> {
126    pub database: Arc<crate::database::Database>,
127
128    pub(crate) prefix: compact_str::CompactString,
129    pub(crate) settings: &'a mut HashMap<compact_str::CompactString, compact_str::CompactString>,
130}
131
132impl<'a> SettingsDeserializer<'a> {
133    pub(crate) fn new(
134        database: Arc<crate::database::Database>,
135        prefix: impl Into<compact_str::CompactString>,
136        settings: &'a mut HashMap<compact_str::CompactString, compact_str::CompactString>,
137    ) -> Self {
138        Self {
139            database,
140            prefix: prefix.into(),
141            settings,
142        }
143    }
144
145    pub(crate) fn key_prefix(&self, key: impl AsRef<str>) -> compact_str::CompactString {
146        compact_str::format_compact!("{}::{}", self.prefix, key.as_ref())
147    }
148
149    pub(crate) fn nest_prefix(&self, nested_prefix: &str) -> compact_str::CompactString {
150        compact_str::format_compact!(
151            "{}{}{}",
152            self.prefix,
153            if self.prefix.is_empty() { "" } else { "::" },
154            nested_prefix
155        )
156    }
157
158    /// Read a raw setting with the given key. The value will be returned as-is without any deserialization or decryption.
159    pub fn read_raw_setting(&self, key: impl AsRef<str>) -> Option<&compact_str::CompactString> {
160        self.settings.get(&self.key_prefix(key))
161    }
162
163    /// Read a raw setting with the given key, decrypting the value before returning. The value will be decrypted from a base64 string and returned as-is without any deserialization.
164    pub async fn read_raw_encrypted_setting(
165        &self,
166        key: impl AsRef<str>,
167    ) -> Result<Option<compact_str::CompactString>, anyhow::Error> {
168        match self.settings.get(&self.key_prefix(key)) {
169            Some(encrypted_value) => {
170                let decrypted = self.database.decrypt_base64(encrypted_value).await?;
171                Ok(Some(decrypted))
172            }
173            None => Ok(None),
174        }
175    }
176
177    /// Remove and return a raw setting with the given key. The value will be returned as-is without any deserialization or decryption.
178    pub fn take_raw_setting(&mut self, key: impl AsRef<str>) -> Option<compact_str::CompactString> {
179        self.settings.remove(&self.key_prefix(key))
180    }
181
182    /// Remove and return a raw setting with the given key, decrypting the value before returning. The value will be decrypted from a base64 string and returned as-is without any deserialization.
183    pub fn read_serde_setting<T: serde::de::DeserializeOwned>(
184        &self,
185        key: impl AsRef<str>,
186    ) -> Result<T, serde_json::Error> {
187        let value = match self.settings.get(&self.key_prefix(key)) {
188            Some(v) => v,
189            None => return serde_json::from_value(serde_json::Value::Null),
190        };
191
192        serde_json::from_str(value)
193    }
194
195    /// Read a setting with the given key, decrypting the value before returning. The value will be decrypted from a base64 string and deserialized from JSON.
196    pub async fn read_serde_encrypted_setting<T: serde::de::DeserializeOwned>(
197        &self,
198        key: impl AsRef<str>,
199    ) -> Result<Option<T>, anyhow::Error> {
200        match self.settings.get(&self.key_prefix(key)) {
201            Some(encrypted_value) => {
202                let decrypted = self.database.decrypt_base64(encrypted_value).await?;
203                let deserialized = serde_json::from_str(&decrypted)?;
204                Ok(Some(deserialized))
205            }
206            None => Ok(None),
207        }
208    }
209
210    /// Nest another set of settings under the given nested prefix. The nested settings will be deserialized with a prefix of `self.prefix::nested_prefix`.
211    pub async fn nest<T: 'static>(
212        &mut self,
213        nested_prefix: &str,
214        deserializer: &(dyn SettingsDeserializeExt + Send + Sync),
215    ) -> Result<T, anyhow::Error> {
216        let settings_deserializer = SettingsDeserializer::new(
217            self.database.clone(),
218            self.nest_prefix(nested_prefix),
219            self.settings,
220        );
221
222        let boxed = deserializer
223            .deserialize_boxed(settings_deserializer)
224            .await?;
225
226        match (boxed as Box<dyn std::any::Any + Send + Sync>).downcast::<T>() {
227            Ok(concrete_box) => Ok(*concrete_box),
228            Err(_) => Err(anyhow::anyhow!(
229                "Type mismatch: expected {}, but nested prefix {} returned a different type",
230                std::any::type_name::<T>(),
231                nested_prefix
232            )),
233        }
234    }
235}
236
237pub type ExtensionSettings = Box<dyn SettingsSerializeExt + Send + Sync + 'static>;
238pub type ExtensionSettingsDeserializer = Arc<dyn SettingsDeserializeExt + Send + Sync + 'static>;
239
240#[async_trait::async_trait]
241pub trait SettingsSerializeExt: std::any::Any + Send + Sync {
242    async fn serialize(
243        &self,
244        serializer: SettingsSerializer,
245    ) -> Result<SettingsSerializer, anyhow::Error>;
246}
247
248#[async_trait::async_trait]
249pub trait SettingsDeserializeExt {
250    async fn deserialize_boxed(
251        &self,
252        deserializer: SettingsDeserializer<'_>,
253    ) -> Result<ExtensionSettings, anyhow::Error>;
254}
255
256#[async_trait::async_trait]
257impl<T: SettingsSerializeExt + ?Sized + Send + Sync> SettingsSerializeExt for Box<T> {
258    async fn serialize(
259        &self,
260        serializer: SettingsSerializer,
261    ) -> Result<SettingsSerializer, anyhow::Error> {
262        (**self).serialize(serializer).await
263    }
264}
265
266#[async_trait::async_trait]
267impl<T: SettingsDeserializeExt + ?Sized + Send + Sync> SettingsDeserializeExt for Arc<T> {
268    async fn deserialize_boxed(
269        &self,
270        deserializer: SettingsDeserializer<'_>,
271    ) -> Result<ExtensionSettings, anyhow::Error> {
272        (**self).deserialize_boxed(deserializer).await
273    }
274}
275
276pub struct EmptySettings;
277
278#[async_trait::async_trait]
279impl SettingsSerializeExt for EmptySettings {
280    async fn serialize(
281        &self,
282        serializer: SettingsSerializer,
283    ) -> Result<SettingsSerializer, anyhow::Error> {
284        Ok(serializer)
285    }
286}
287
288#[async_trait::async_trait]
289impl SettingsDeserializeExt for EmptySettings {
290    async fn deserialize_boxed(
291        &self,
292        _deserializer: SettingsDeserializer<'_>,
293    ) -> Result<ExtensionSettings, anyhow::Error> {
294        Ok(Box::new(EmptySettings))
295    }
296}