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