use std::sync::Arc;
use swf_core::models::workflow::WorkflowDefinition;
pub trait WorkflowResolver: Send + Sync {
fn resolve(&self, name: &str) -> Option<WorkflowDefinition>;
}
pub struct MapWorkflowResolver {
workflows: std::collections::HashMap<String, WorkflowDefinition>,
}
impl MapWorkflowResolver {
pub fn new() -> Self {
Self {
workflows: std::collections::HashMap::new(),
}
}
pub fn add(&mut self, name: impl Into<String>, workflow: WorkflowDefinition) {
self.workflows.insert(name.into(), workflow);
}
}
impl Default for MapWorkflowResolver {
fn default() -> Self {
Self::new()
}
}
impl WorkflowResolver for MapWorkflowResolver {
fn resolve(&self, name: &str) -> Option<WorkflowDefinition> {
self.workflows.get(name).cloned()
}
}
pub struct CachingWorkflowResolver {
inner: Arc<dyn WorkflowResolver>,
cache: std::sync::Mutex<std::collections::HashMap<String, Option<WorkflowDefinition>>>,
}
impl CachingWorkflowResolver {
pub fn new(inner: Arc<dyn WorkflowResolver>) -> Self {
Self {
inner,
cache: std::sync::Mutex::new(std::collections::HashMap::new()),
}
}
pub fn clear_cache(&self) {
self.cache.lock().unwrap_or_else(|e| e.into_inner()).clear();
}
}
impl WorkflowResolver for CachingWorkflowResolver {
fn resolve(&self, name: &str) -> Option<WorkflowDefinition> {
{
let cache = self.cache.lock().unwrap_or_else(|e| e.into_inner());
if let Some(cached) = cache.get(name) {
return cached.clone();
}
}
let result = self.inner.resolve(name);
if result.is_some() {
let mut cache = self.cache.lock().unwrap_or_else(|e| e.into_inner());
cache.insert(name.to_string(), result.clone());
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
use swf_core::models::workflow::{WorkflowDefinition, WorkflowDefinitionMetadata};
fn test_workflow(name: &str) -> WorkflowDefinition {
WorkflowDefinition::new(WorkflowDefinitionMetadata::new(
"default", name, "1.0.0", None, None, None,
))
}
#[test]
fn test_map_resolver_found() {
let mut resolver = MapWorkflowResolver::new();
resolver.add("test-wf", test_workflow("test-wf"));
let result = resolver.resolve("test-wf");
assert!(result.is_some());
}
#[test]
fn test_map_resolver_not_found() {
let resolver = MapWorkflowResolver::new();
let result = resolver.resolve("nonexistent");
assert!(result.is_none());
}
#[test]
fn test_caching_resolver_caches_success() {
let mut inner = MapWorkflowResolver::new();
inner.add("cached-wf", test_workflow("cached-wf"));
let caching = CachingWorkflowResolver::new(Arc::new(inner));
let result1 = caching.resolve("cached-wf");
assert!(result1.is_some());
let result2 = caching.resolve("cached-wf");
assert!(result2.is_some());
}
#[test]
fn test_caching_resolver_does_not_cache_not_found() {
let inner = MapWorkflowResolver::new();
let caching = CachingWorkflowResolver::new(Arc::new(inner));
let result1 = caching.resolve("missing");
assert!(result1.is_none());
let result2 = caching.resolve("missing");
assert!(result2.is_none());
}
#[test]
fn test_caching_resolver_clear_cache() {
let mut inner = MapWorkflowResolver::new();
inner.add("clear-wf", test_workflow("clear-wf"));
let caching = CachingWorkflowResolver::new(Arc::new(inner));
let _ = caching.resolve("clear-wf");
caching.clear_cache();
let result = caching.resolve("clear-wf");
assert!(result.is_some());
}
}