1use noether_core::stage::{Stage, StageId, StageLifecycle};
2use std::collections::BTreeMap;
3
4#[derive(Debug, thiserror::Error)]
5pub enum StoreError {
6 #[error("stage with id {0:?} already exists")]
7 AlreadyExists(StageId),
8 #[error("stage with id {0:?} not found")]
9 NotFound(StageId),
10 #[error("invalid lifecycle transition: {reason}")]
11 InvalidTransition { reason: String },
12 #[error("invalid successor: {reason}")]
13 InvalidSuccessor { reason: String },
14 #[error("validation failed: {0:?}")]
15 ValidationFailed(Vec<String>),
16 #[error("I/O error: {message}")]
17 IoError { message: String },
18}
19
20#[derive(Debug, Clone)]
22pub struct StoreStats {
23 pub total: usize,
24 pub by_lifecycle: BTreeMap<String, usize>,
25 pub by_effect: BTreeMap<String, usize>,
26}
27
28pub trait StageStore {
30 fn put(&mut self, stage: Stage) -> Result<StageId, StoreError>;
31 fn upsert(&mut self, stage: Stage) -> Result<StageId, StoreError>;
34 fn remove(&mut self, id: &StageId) -> Result<(), StoreError>;
36 fn get(&self, id: &StageId) -> Result<Option<&Stage>, StoreError>;
37 fn contains(&self, id: &StageId) -> bool;
38 fn list(&self, lifecycle: Option<&StageLifecycle>) -> Vec<&Stage>;
39 fn update_lifecycle(
40 &mut self,
41 id: &StageId,
42 lifecycle: StageLifecycle,
43 ) -> Result<(), StoreError>;
44 fn stats(&self) -> StoreStats;
45
46 fn get_owned(&self, id: &StageId) -> Result<Option<Stage>, StoreError> {
51 Ok(self.get(id)?.cloned())
52 }
53
54 fn list_owned(&self, lifecycle: Option<&StageLifecycle>) -> Vec<Stage> {
56 self.list(lifecycle).into_iter().cloned().collect()
57 }
58
59 fn find_by_name(&self, name: &str) -> Vec<&Stage> {
65 self.list(None)
66 .into_iter()
67 .filter(|s| s.name.as_deref() == Some(name))
68 .collect()
69 }
70}