use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use forge_core::workflow::{ForgeWorkflow, WorkflowContext, WorkflowInfo};
pub type BoxedWorkflowHandler = Arc<
dyn Fn(
&WorkflowContext,
serde_json::Value,
)
-> Pin<Box<dyn Future<Output = forge_core::Result<serde_json::Value>> + Send + '_>>
+ Send
+ Sync,
>;
pub struct WorkflowEntry {
pub info: WorkflowInfo,
pub handler: BoxedWorkflowHandler,
}
impl WorkflowEntry {
pub fn new<W: ForgeWorkflow>() -> Self
where
W::Input: serde::de::DeserializeOwned,
W::Output: serde::Serialize,
{
Self {
info: W::info(),
handler: Arc::new(|ctx, input| {
Box::pin(async move {
let typed_input: W::Input = serde_json::from_value(input)
.map_err(|e| forge_core::ForgeError::Validation(e.to_string()))?;
let result = W::execute(ctx, typed_input).await?;
serde_json::to_value(result).map_err(forge_core::ForgeError::from)
})
}),
}
}
}
#[derive(Default)]
pub struct WorkflowRegistry {
workflows: HashMap<String, WorkflowEntry>,
}
impl WorkflowRegistry {
pub fn new() -> Self {
Self {
workflows: HashMap::new(),
}
}
pub fn register<W: ForgeWorkflow>(&mut self)
where
W::Input: serde::de::DeserializeOwned,
W::Output: serde::Serialize,
{
let entry = WorkflowEntry::new::<W>();
self.workflows.insert(entry.info.name.to_string(), entry);
}
pub fn get(&self, name: &str) -> Option<&WorkflowEntry> {
self.workflows.get(name)
}
pub fn get_version(&self, name: &str, version: u32) -> Option<&WorkflowEntry> {
self.workflows
.get(name)
.filter(|e| e.info.version == version)
}
pub fn list(&self) -> Vec<&WorkflowEntry> {
self.workflows.values().collect()
}
pub fn len(&self) -> usize {
self.workflows.len()
}
pub fn is_empty(&self) -> bool {
self.workflows.is_empty()
}
pub fn names(&self) -> Vec<&str> {
self.workflows.keys().map(|s| s.as_str()).collect()
}
}
impl Clone for WorkflowRegistry {
fn clone(&self) -> Self {
Self {
workflows: self
.workflows
.iter()
.map(|(k, v)| {
(
k.clone(),
WorkflowEntry {
info: v.info.clone(),
handler: v.handler.clone(),
},
)
})
.collect(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_registry() {
let registry = WorkflowRegistry::new();
assert!(registry.is_empty());
assert_eq!(registry.len(), 0);
}
}