mlua_swarm/store/enhance_setting/
mod.rs1pub mod sqlite;
13pub use sqlite::SqliteEnhanceSettingStore;
14
15use crate::enhance::setting::EnhanceSetting;
16use async_trait::async_trait;
17use serde::{Deserialize, Serialize};
18use std::collections::HashMap;
19use std::sync::Mutex;
20use thiserror::Error;
21
22#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
24pub struct EnhanceSettingId(pub String);
25
26impl EnhanceSettingId {
27 pub fn new(s: impl Into<String>) -> Self {
29 Self(s.into())
30 }
31
32 pub fn default_id() -> Self {
34 Self("default".into())
35 }
36
37 pub fn as_str(&self) -> &str {
39 &self.0
40 }
41}
42
43impl std::fmt::Display for EnhanceSettingId {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 f.write_str(&self.0)
46 }
47}
48
49#[derive(Debug, Error)]
51pub enum EnhanceSettingStoreError {
52 #[error("not found: {0}")]
54 NotFound(EnhanceSettingId),
55 #[error("other: {0}")]
58 Other(String),
59}
60
61#[async_trait]
63pub trait EnhanceSettingStore: Send + Sync {
64 fn name(&self) -> &str;
66
67 async fn get(&self, id: &EnhanceSettingId) -> Result<EnhanceSetting, EnhanceSettingStoreError>;
69
70 async fn put(
72 &self,
73 id: &EnhanceSettingId,
74 setting: EnhanceSetting,
75 ) -> Result<(), EnhanceSettingStoreError>;
76
77 async fn delete(&self, id: &EnhanceSettingId) -> Result<(), EnhanceSettingStoreError>;
79
80 async fn list(&self) -> Result<Vec<EnhanceSettingId>, EnhanceSettingStoreError>;
82}
83
84#[derive(Default)]
87pub struct InMemoryEnhanceSettingStore {
88 inner: Mutex<HashMap<EnhanceSettingId, EnhanceSetting>>,
89}
90
91impl InMemoryEnhanceSettingStore {
92 pub fn new() -> Self {
94 Self::default()
95 }
96}
97
98#[async_trait]
99impl EnhanceSettingStore for InMemoryEnhanceSettingStore {
100 fn name(&self) -> &str {
101 "in-memory"
102 }
103
104 async fn get(&self, id: &EnhanceSettingId) -> Result<EnhanceSetting, EnhanceSettingStoreError> {
105 self.inner
106 .lock()
107 .unwrap()
108 .get(id)
109 .cloned()
110 .ok_or_else(|| EnhanceSettingStoreError::NotFound(id.clone()))
111 }
112
113 async fn put(
114 &self,
115 id: &EnhanceSettingId,
116 setting: EnhanceSetting,
117 ) -> Result<(), EnhanceSettingStoreError> {
118 self.inner.lock().unwrap().insert(id.clone(), setting);
119 Ok(())
120 }
121
122 async fn delete(&self, id: &EnhanceSettingId) -> Result<(), EnhanceSettingStoreError> {
123 if self.inner.lock().unwrap().remove(id).is_none() {
124 return Err(EnhanceSettingStoreError::NotFound(id.clone()));
125 }
126 Ok(())
127 }
128
129 async fn list(&self) -> Result<Vec<EnhanceSettingId>, EnhanceSettingStoreError> {
130 Ok(self.inner.lock().unwrap().keys().cloned().collect())
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137 use crate::application::VersionSelector;
138 use crate::blueprint::store::BlueprintId;
139 use crate::enhance::setting::EnhanceSettingMeta;
140
141 fn dummy_setting(id: &str, bp: &str) -> EnhanceSetting {
142 EnhanceSetting {
143 id: id.into(),
144 blueprint_id: BlueprintId::new(bp.to_string()),
145 ttl_secs: 10,
146 version: VersionSelector::default(),
147 verifier_axes: vec!["des".into()],
148 spawner: None,
149 meta: EnhanceSettingMeta::default(),
150 }
151 }
152
153 #[test]
154 fn enhance_setting_id_default_is_default_literal() {
155 assert_eq!(EnhanceSettingId::default_id().as_str(), "default");
156 }
157
158 #[test]
159 fn enhance_setting_id_display_is_inner_string() {
160 let id = EnhanceSettingId::new("foo");
161 assert_eq!(format!("{id}"), "foo");
162 }
163
164 #[tokio::test]
165 async fn inmemory_put_then_get_returns_same_setting() {
166 let store = InMemoryEnhanceSettingStore::new();
167 let id = EnhanceSettingId::new("s1");
168 let s = dummy_setting("s1", "bp-1");
169 store.put(&id, s.clone()).await.unwrap();
170 let got = store.get(&id).await.unwrap();
171 assert_eq!(got.id, "s1");
172 assert_eq!(got.blueprint_id.as_str(), "bp-1");
173 }
174
175 #[tokio::test]
176 async fn inmemory_get_missing_returns_not_found() {
177 let store = InMemoryEnhanceSettingStore::new();
178 let err = store.get(&EnhanceSettingId::new("nope")).await.unwrap_err();
179 assert!(matches!(err, EnhanceSettingStoreError::NotFound(_)));
180 }
181
182 #[tokio::test]
183 async fn inmemory_delete_missing_returns_not_found() {
184 let store = InMemoryEnhanceSettingStore::new();
185 let err = store
186 .delete(&EnhanceSettingId::new("nope"))
187 .await
188 .unwrap_err();
189 assert!(matches!(err, EnhanceSettingStoreError::NotFound(_)));
190 }
191
192 #[tokio::test]
193 async fn inmemory_put_then_delete_then_get_is_not_found() {
194 let store = InMemoryEnhanceSettingStore::new();
195 let id = EnhanceSettingId::new("s2");
196 store.put(&id, dummy_setting("s2", "bp-x")).await.unwrap();
197 store.delete(&id).await.unwrap();
198 assert!(matches!(
199 store.get(&id).await.unwrap_err(),
200 EnhanceSettingStoreError::NotFound(_)
201 ));
202 }
203
204 #[tokio::test]
205 async fn inmemory_list_returns_all_inserted_ids() {
206 let store = InMemoryEnhanceSettingStore::new();
207 store
208 .put(&EnhanceSettingId::new("a"), dummy_setting("a", "bp-a"))
209 .await
210 .unwrap();
211 store
212 .put(&EnhanceSettingId::new("b"), dummy_setting("b", "bp-b"))
213 .await
214 .unwrap();
215 let mut ids: Vec<String> = store
216 .list()
217 .await
218 .unwrap()
219 .into_iter()
220 .map(|i| i.0)
221 .collect();
222 ids.sort();
223 assert_eq!(ids, vec!["a", "b"]);
224 }
225
226 #[tokio::test]
227 async fn inmemory_put_overwrites_existing_setting() {
228 let store = InMemoryEnhanceSettingStore::new();
229 let id = EnhanceSettingId::new("s3");
230 store.put(&id, dummy_setting("s3", "bp-old")).await.unwrap();
231 store.put(&id, dummy_setting("s3", "bp-new")).await.unwrap();
232 let got = store.get(&id).await.unwrap();
233 assert_eq!(got.blueprint_id.as_str(), "bp-new");
234 }
235
236 #[tokio::test]
237 async fn inmemory_name_is_in_memory() {
238 let store = InMemoryEnhanceSettingStore::new();
239 assert_eq!(store.name(), "in-memory");
240 }
241}