1#[doc(hidden)]
88pub mod __internal;
89mod definition;
90mod feattle_value;
91pub mod json_reading;
92pub mod last_reload;
93#[doc(hidden)]
95pub mod macros;
96pub mod persist;
97
98use crate::__internal::{FeattlesStruct, InnerFeattles};
99use crate::json_reading::FromJsonError;
100use crate::last_reload::LastReload;
101use async_trait::async_trait;
102use chrono::Utc;
103pub use definition::*;
104pub use feattle_value::*;
105use parking_lot::{MappedRwLockReadGuard, RwLockReadGuard, RwLockWriteGuard};
106use persist::*;
107use serde_json::Value;
108use std::error::Error;
109use std::fmt::Debug;
110use std::sync::Arc;
111use thiserror::Error;
112
113pub type BoxError = Box<dyn Error + Send + Sync>;
115
116#[derive(Error, Debug)]
118pub enum UpdateError {
119 #[error("cannot update because current values were never successfully loaded from the persist layer")]
121 NeverReloaded,
122 #[error("the key {0} is unknown")]
124 UnknownKey(String),
125 #[error("failed to parse the value from JSON")]
127 Parsing(
128 #[source]
129 #[from]
130 FromJsonError,
131 ),
132 #[error("failed to persist new state")]
134 Persistence(#[source] BoxError),
135}
136
137#[derive(Error, Debug)]
139pub enum HistoryError {
140 #[error("the key {0} is unknown")]
142 UnknownKey(String),
143 #[error("failed to load persisted state")]
145 Persistence(#[source] BoxError),
146}
147
148#[async_trait]
153pub trait Feattles: FeattlesPrivate {
154 fn new(persistence: Arc<dyn Persist>) -> Self;
159
160 fn persistence(&self) -> &Arc<dyn Persist>;
162
163 fn keys(&self) -> &'static [&'static str];
165
166 fn definition(&self, key: &str) -> Option<FeattleDefinition>;
169
170 fn last_reload(&self) -> LastReload {
172 self._read().last_reload
173 }
174
175 fn current_values(&self) -> Option<MappedRwLockReadGuard<CurrentValues>> {
179 let inner = self._read();
180 if inner.current_values.is_none() {
181 None
182 } else {
183 Some(RwLockReadGuard::map(inner, |x| {
184 x.current_values.as_ref().unwrap()
185 }))
186 }
187 }
188
189 async fn reload(&self) -> Result<(), BoxError> {
196 let current_values = self.persistence().load_current().await?;
197 let mut inner = self._write();
198 let now = Utc::now();
199 match current_values {
200 None => {
201 inner.last_reload = LastReload::NoData { reload_date: now };
202 let empty = CurrentValues {
203 version: 0,
204 date: now,
205 feattles: Default::default(),
206 };
207 inner.current_values = Some(empty);
208 }
209 Some(current_values) => {
210 inner.last_reload = LastReload::Data {
211 reload_date: now,
212 version: current_values.version,
213 version_date: current_values.date,
214 };
215 for &key in self.keys() {
216 let value = current_values.feattles.get(key).cloned();
217 log::debug!("Will update {} with {:?}", key, value);
218 if let Err(error) = inner.feattles_struct.try_update(key, value) {
219 log::error!("Failed to update {}: {:?}", key, error);
220 }
221 }
222 inner.current_values = Some(current_values);
223 }
224 }
225 Ok(())
226 }
227
228 async fn update(
239 &self,
240 key: &str,
241 value: Value,
242 modified_by: String,
243 ) -> Result<(), UpdateError> {
244 use UpdateError::*;
245
246 if !self.keys().contains(&key) {
255 return Err(UnknownKey(key.to_owned()));
256 }
257
258 let new_value = CurrentValue {
259 modified_at: Utc::now(),
260 modified_by,
261 value,
262 };
263
264 let (new_values, old_value) = {
265 let mut inner = self._write();
266
267 let mut new_values = inner.current_values.clone().ok_or(NeverReloaded)?;
269 new_values
270 .feattles
271 .insert(key.to_owned(), new_value.clone());
272 new_values.version += 1;
273
274 let old_value = inner
276 .feattles_struct
277 .try_update(key, Some(new_value.clone()))?;
278
279 (new_values, old_value)
280 };
281
282 log::debug!("new_values = {:?}", new_values);
283
284 let rollback_step_1 = || {
285 let _ = self
287 ._write()
288 .feattles_struct
289 .try_update(key, old_value.clone());
290 };
291
292 let persistence = self.persistence();
294 let old_history = persistence
295 .load_history(key)
296 .await
297 .map_err(|err| {
298 rollback_step_1();
299 Persistence(err)
300 })?
301 .unwrap_or_default();
302
303 let new_definition = self
305 .definition(key)
306 .expect("the key is guaranteed to exist");
307 let mut new_history = old_history.clone();
308 new_history.entries.push(HistoryEntry {
309 value: new_value.value.clone(),
310 value_overview: new_definition.value_overview,
311 modified_at: new_value.modified_at,
312 modified_by: new_value.modified_by.clone(),
313 });
314
315 persistence
316 .save_history(key, &new_history)
317 .await
318 .map_err(|err| {
319 rollback_step_1();
320 Persistence(err)
321 })?;
322
323 if let Err(err) = persistence.save_current(&new_values).await {
325 rollback_step_1();
326 if let Err(err) = self.persistence().save_history(key, &old_history).await {
327 log::warn!("Failed to rollback history for {}: {:?}", key, err);
328 }
329 return Err(Persistence(err));
330 }
331
332 self._write().current_values = Some(new_values);
334
335 Ok(())
336 }
337
338 fn definitions(&self) -> Vec<FeattleDefinition> {
340 self.keys()
341 .iter()
342 .map(|&key| {
343 self.definition(key)
344 .expect("since we iterate over the list of known keys, this should always work")
345 })
346 .collect()
347 }
348
349 async fn history(&self, key: &str) -> Result<ValueHistory, HistoryError> {
351 if !self.keys().contains(&key) {
353 return Err(HistoryError::UnknownKey(key.to_owned()));
354 }
355
356 let history = self
357 .persistence()
358 .load_history(key)
359 .await
360 .map_err(HistoryError::Persistence)?;
361
362 Ok(history.unwrap_or_default())
363 }
364}
365
366#[doc(hidden)]
369pub trait FeattlesPrivate {
370 type FeattleStruct: FeattlesStruct;
371 fn _read(&self) -> RwLockReadGuard<InnerFeattles<Self::FeattleStruct>>;
372 fn _write(&self) -> RwLockWriteGuard<InnerFeattles<Self::FeattleStruct>>;
373}
374
375#[cfg(test)]
376mod tests {
377 use super::*;
378 use parking_lot::Mutex;
379 use serde_json::json;
380 use std::collections::BTreeMap;
381 use std::sync::Arc;
382
383 #[derive(Debug, thiserror::Error)]
384 #[error("Some error")]
385 struct SomeError;
386
387 #[derive(Default)]
388 struct MockPersistence(Mutex<MockPersistenceInner>);
389
390 #[derive(Default)]
391 struct MockPersistenceInner {
392 current: Option<CurrentValues>,
393 history: BTreeMap<String, ValueHistory>,
394 next_error: Option<BoxError>,
395 }
396
397 impl MockPersistence {
398 fn put_error(&self) {
399 let previous = self.0.lock().next_error.replace(Box::new(SomeError));
400 assert!(previous.is_none());
401 }
402
403 fn get_error(&self) -> Result<(), BoxError> {
404 match self.0.lock().next_error.take() {
405 None => Ok(()),
406 Some(e) => Err(e),
407 }
408 }
409
410 fn unwrap_current(&self) -> CurrentValues {
411 self.0.lock().current.clone().unwrap()
412 }
413
414 fn unwrap_history(&self, key: &str) -> ValueHistory {
415 self.0.lock().history.get(key).cloned().unwrap()
416 }
417 }
418
419 #[async_trait]
420 impl Persist for MockPersistence {
421 async fn save_current(&self, value: &CurrentValues) -> Result<(), BoxError> {
422 self.get_error().map(|_| {
423 self.0.lock().current = Some(value.clone());
424 })
425 }
426
427 async fn load_current(&self) -> Result<Option<CurrentValues>, BoxError> {
428 self.get_error().map(|_| self.0.lock().current.clone())
429 }
430
431 async fn save_history(&self, key: &str, value: &ValueHistory) -> Result<(), BoxError> {
432 self.get_error().map(|_| {
433 self.0.lock().history.insert(key.to_owned(), value.clone());
434 })
435 }
436
437 async fn load_history(&self, key: &str) -> Result<Option<ValueHistory>, BoxError> {
438 self.get_error()
439 .map(|_| self.0.lock().history.get(key).cloned())
440 }
441 }
442
443 #[tokio::test]
444 async fn test() {
445 feattles! {
446 struct Config {
447 a: i32,
449 b: i32 = 17
450 }
451 }
452
453 let persistence = Arc::new(MockPersistence::default());
454 let config = Config::new(persistence.clone());
455
456 assert_eq!(*config.a(), 0);
458 assert_eq!(*config.b(), 17);
459 assert_eq!(config.keys(), &["a", "b"]);
460 assert!(config.last_reload() == LastReload::Never);
461 assert!(config.current_values().is_none());
462
463 config.reload().await.unwrap();
465 assert_eq!(*config.a(), 0);
466 assert_eq!(*config.b(), 17);
467 let last_reload = config.last_reload();
468 assert!(matches!(last_reload, LastReload::NoData { .. }));
469 assert!(config.current_values().is_some());
470
471 persistence.put_error();
473 config.reload().await.unwrap_err();
474 assert_eq!(config.last_reload(), last_reload);
475
476 config
478 .update("a", json!(27i32), "somebody".to_owned())
479 .await
480 .unwrap();
481 assert_eq!(*config.a(), 27);
482 let values = persistence.unwrap_current();
483 assert_eq!(values.version, 1);
484 let value = values.feattles.get("a").unwrap();
485 assert_eq!(value.modified_by, "somebody");
486 assert_eq!(value.value, json!(27i32));
487 let history = persistence.unwrap_history("a");
488 assert_eq!(history.entries.len(), 1);
489 assert_eq!(&history.entries[0].value, &json!(27i32));
490 assert_eq!(&history.entries[0].value_overview, "27");
491 assert_eq!(&history.entries[0].modified_by, "somebody");
492
493 persistence.put_error();
495 config
496 .update("a", json!(207i32), "somebody else".to_owned())
497 .await
498 .unwrap_err();
499 assert_eq!(*config.a(), 27);
500 let values = persistence.unwrap_current();
501 assert_eq!(values.version, 1);
502 let value = values.feattles.get("a").unwrap();
503 assert_eq!(value.modified_by, "somebody");
504 assert_eq!(value.value, json!(27i32));
505 let history = persistence.unwrap_history("a");
506 assert_eq!(history.entries.len(), 1);
507 assert_eq!(&history.entries[0].value, &json!(27i32));
508 assert_eq!(&history.entries[0].value_overview, "27");
509 assert_eq!(&history.entries[0].modified_by, "somebody");
510 }
511}