Skip to main content

pidgin_lang/
expander.rs

1use serde::Serialize;
2
3use crate::ast::{FieldValue, PgnPacket};
4use crate::registry::WorkflowRegistry;
5use crate::resolver::ResolvedRef;
6use crate::safety::SafetyResult;
7
8#[derive(Debug, Serialize)]
9pub struct ExpandedRef {
10    pub reference: String,
11    pub status: String,
12    pub confidence: f32,
13    pub path: Option<String>,
14}
15
16impl From<&ResolvedRef> for ExpandedRef {
17    fn from(r: &ResolvedRef) -> Self {
18        ExpandedRef {
19            reference: r.original.clone(),
20            status: format!("{:?}", r.status),
21            confidence: r.confidence,
22            path: r.resolved_path.as_ref().map(|p| p.display().to_string()),
23        }
24    }
25}
26
27#[derive(Debug, Serialize)]
28pub struct ExpandedRunPacket {
29    pub spec_version: String,
30    pub run_id: String,
31    pub workflow: String,
32    pub mode: String,
33    pub inputs: Vec<ExpandedRef>,
34    pub outputs: Vec<ExpandedRef>,
35    pub do_actions: Vec<String>,
36    pub deny_actions: Vec<String>,
37    pub effective_risk: String,
38    pub human_required: bool,
39    pub recommended_executor: String,
40    pub fallback_executor: String,
41    pub ttl: String,
42    pub note: Option<String>,
43}
44
45pub fn expand_to_run_packet(
46    packet: &PgnPacket,
47    resolved_refs: &[ResolvedRef],
48    safety: &SafetyResult,
49    workflows: &WorkflowRegistry,
50) -> ExpandedRunPacket {
51    let wf_name = packet
52        .fields
53        .get("wf")
54        .and_then(|v| match v {
55            FieldValue::Scalar(s) => Some(s.as_str()),
56            _ => None,
57        })
58        .unwrap_or("unknown");
59
60    let workflow_entry = workflows.workflows.get(wf_name);
61
62    let inputs: Vec<ExpandedRef> = resolved_refs
63        .iter()
64        .filter(|r| matches!(packet.fields.get("in"), Some(FieldValue::List(refs)) if refs.contains(&r.original)))
65        .map(ExpandedRef::from)
66        .collect();
67
68    let outputs: Vec<ExpandedRef> = resolved_refs
69        .iter()
70        .filter(|r| matches!(packet.fields.get("out"), Some(FieldValue::List(refs)) if refs.contains(&r.original)))
71        .map(ExpandedRef::from)
72        .collect();
73
74    let do_actions = packet
75        .fields
76        .get("do")
77        .and_then(|v| match v {
78            FieldValue::List(items) => Some(items.clone()),
79            _ => None,
80        })
81        .unwrap_or_default();
82
83    let deny_actions = packet
84        .fields
85        .get("deny")
86        .and_then(|v| match v {
87            FieldValue::List(items) => Some(items.clone()),
88            _ => None,
89        })
90        .unwrap_or_default();
91
92    let note = packet.fields.get("note").and_then(|v| match v {
93        FieldValue::Scalar(s) => Some(s.clone()),
94        _ => None,
95    });
96
97    let recommended_executor = workflow_entry
98        .map(|w| w.recommended_executor.clone())
99        .unwrap_or_else(|| "unknown".to_string());
100
101    let fallback_executor = workflow_entry
102        .map(|w| w.fallback_executor.clone())
103        .unwrap_or_else(|| "unknown".to_string());
104
105    let ttl = packet
106        .fields
107        .get("ttl")
108        .and_then(|v| match v {
109            FieldValue::Scalar(s) => Some(s.clone()),
110            _ => None,
111        })
112        .unwrap_or_else(|| "24h".to_string());
113
114    ExpandedRunPacket {
115        spec_version: "1.0".to_string(),
116        run_id: packet.run_id.clone(),
117        workflow: wf_name.to_string(),
118        mode: packet
119            .fields
120            .get("mode")
121            .and_then(|v| match v {
122                FieldValue::Scalar(s) => Some(s.clone()),
123                _ => None,
124            })
125            .unwrap_or_else(|| "unknown".to_string()),
126        inputs,
127        outputs,
128        do_actions,
129        deny_actions,
130        effective_risk: safety.effective_risk.clone(),
131        human_required: safety.human_required,
132        recommended_executor,
133        fallback_executor,
134        ttl,
135        note,
136    }
137}