use std::sync::Arc;
use crate::ast::analyzed::{AnalyzedWorkflow, TaskId, TaskTable};
#[derive(Debug)]
pub struct WorkflowMeta {
task_table: TaskTable,
provider: Option<String>,
model: Option<String>,
}
impl WorkflowMeta {
pub fn from_workflow(wf: &AnalyzedWorkflow) -> Arc<Self> {
Arc::new(Self {
task_table: wf.task_table.clone(),
provider: wf.provider.clone(),
model: wf.model.clone(),
})
}
pub fn task_name(&self, id: TaskId) -> &str {
self.task_table
.get_name(id)
.expect("TaskId must be valid — created by analyzer")
}
pub fn task_id(&self, name: &str) -> Option<TaskId> {
self.task_table.get_id(name)
}
pub fn provider(&self) -> Option<&str> {
self.provider.as_deref()
}
pub fn model(&self) -> Option<&str> {
self.model.as_deref()
}
pub fn task_table(&self) -> &TaskTable {
&self.task_table
}
pub fn task_count(&self) -> usize {
self.task_table.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::analyzed::AnalyzedWorkflow;
fn make_workflow(
tasks: &[&str],
provider: Option<&str>,
model: Option<&str>,
) -> AnalyzedWorkflow {
let mut wf = AnalyzedWorkflow::default();
for name in tasks {
wf.task_table.insert(name);
}
wf.provider = provider.map(String::from);
wf.model = model.map(String::from);
wf
}
#[test]
fn from_workflow_roundtrip() {
let wf = make_workflow(
&["step1", "step2", "step3"],
Some("anthropic"),
Some("claude-sonnet-4-6"),
);
let ctx = WorkflowMeta::from_workflow(&wf);
assert_eq!(ctx.task_count(), 3);
assert_eq!(ctx.provider(), Some("anthropic"));
assert_eq!(ctx.model(), Some("claude-sonnet-4-6"));
}
#[test]
fn task_name_roundtrip() {
let wf = make_workflow(&["alpha", "beta"], None, None);
let ctx = WorkflowMeta::from_workflow(&wf);
let id_alpha = ctx.task_id("alpha").unwrap();
let id_beta = ctx.task_id("beta").unwrap();
assert_eq!(ctx.task_name(id_alpha), "alpha");
assert_eq!(ctx.task_name(id_beta), "beta");
}
#[test]
fn unknown_name_returns_none() {
let wf = make_workflow(&["task1"], None, None);
let ctx = WorkflowMeta::from_workflow(&wf);
assert!(ctx.task_id("nonexistent").is_none());
}
#[test]
fn provider_and_model_none() {
let wf = make_workflow(&["task1"], None, None);
let ctx = WorkflowMeta::from_workflow(&wf);
assert!(ctx.provider().is_none());
assert!(ctx.model().is_none());
}
#[test]
fn task_table_accessible() {
let wf = make_workflow(&["a", "b", "c"], None, None);
let ctx = WorkflowMeta::from_workflow(&wf);
let table = ctx.task_table();
assert_eq!(table.len(), 3);
assert!(table.contains("a"));
assert!(table.contains("b"));
assert!(table.contains("c"));
}
#[test]
fn arc_sharing() {
let wf = make_workflow(&["task1"], Some("openai"), None);
let ctx = WorkflowMeta::from_workflow(&wf);
let ctx2 = Arc::clone(&ctx);
assert_eq!(ctx2.task_name(TaskId::new(0)), "task1");
assert_eq!(ctx2.provider(), Some("openai"));
}
}