use crate::flow::FlowDef;
pub trait WorkflowStorage: Send + Sync {
fn get_by_name(&self, name: &str) -> Option<FlowDef>;
fn get_by_id(&self, id: &str) -> Option<FlowDef>;
fn list(&self) -> Vec<String>;
}
pub struct InMemoryStorage {
flows: std::sync::RwLock<std::collections::HashMap<String, FlowDef>>,
}
impl InMemoryStorage {
#[must_use]
pub fn new() -> Self {
Self {
flows: std::sync::RwLock::new(std::collections::HashMap::new()),
}
}
pub fn insert(&self, flow: FlowDef) {
self.flows
.write()
.expect("storage lock poisoned")
.insert(flow.name.clone(), flow);
}
pub fn remove(&self, name: &str) -> Option<FlowDef> {
self.flows
.write()
.expect("storage lock poisoned")
.remove(name)
}
}
impl Default for InMemoryStorage {
fn default() -> Self {
Self::new()
}
}
impl WorkflowStorage for InMemoryStorage {
fn get_by_name(&self, name: &str) -> Option<FlowDef> {
self.flows
.read()
.expect("storage lock poisoned")
.get(name)
.cloned()
}
fn get_by_id(&self, id: &str) -> Option<FlowDef> {
let uuid: uuid::Uuid = id.parse().ok()?;
self.flows
.read()
.expect("storage lock poisoned")
.values()
.find(|f| f.id == uuid)
.cloned()
}
fn list(&self) -> Vec<String> {
self.flows
.read()
.expect("storage lock poisoned")
.keys()
.cloned()
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::flow::FlowMode;
use crate::step::StepDef;
#[test]
fn in_memory_insert_and_get() {
let storage = InMemoryStorage::new();
let mut flow = FlowDef::new("deploy", FlowMode::Sequential);
flow.add_step(StepDef::new("build"));
let flow_id = flow.id.to_string();
storage.insert(flow);
let by_name = storage.get_by_name("deploy");
assert!(by_name.is_some());
assert_eq!(by_name.unwrap().name, "deploy");
let by_id = storage.get_by_id(&flow_id);
assert!(by_id.is_some());
assert!(storage.get_by_name("missing").is_none());
}
#[test]
fn in_memory_list() {
let storage = InMemoryStorage::new();
storage.insert(FlowDef::new("a", FlowMode::Sequential));
storage.insert(FlowDef::new("b", FlowMode::Parallel));
let names = storage.list();
assert_eq!(names.len(), 2);
assert!(names.contains(&"a".to_string()));
assert!(names.contains(&"b".to_string()));
}
#[test]
fn in_memory_remove() {
let storage = InMemoryStorage::new();
storage.insert(FlowDef::new("x", FlowMode::Sequential));
assert!(storage.remove("x").is_some());
assert!(storage.get_by_name("x").is_none());
}
}