Skip to main content

tramli_plugins/api/
mod.rs

1use std::fmt;
2use tramli::{FlowDefinition, FlowEngine, FlowState};
3
4/// Plugin kind classification.
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum PluginKind {
7    Analysis,
8    Store,
9    Engine,
10    RuntimeAdapter,
11    Generation,
12    Documentation,
13}
14
15/// Plugin descriptor.
16#[derive(Debug, Clone)]
17pub struct PluginDescriptor {
18    pub id: &'static str,
19    pub display_name: &'static str,
20    pub description: &'static str,
21}
22
23/// Describes where in a flow definition a finding is located.
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub enum FindingLocation {
26    Transition { from_state: String, to_state: String },
27    State { state: String },
28    Data { data_key: String },
29    Flow,
30}
31
32impl fmt::Display for FindingLocation {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        match self {
35            FindingLocation::Transition { from_state, to_state } => {
36                write!(f, "transition({} -> {})", from_state, to_state)
37            }
38            FindingLocation::State { state } => write!(f, "state({})", state),
39            FindingLocation::Data { data_key } => write!(f, "data({})", data_key),
40            FindingLocation::Flow => write!(f, "flow"),
41        }
42    }
43}
44
45/// A single report finding.
46#[derive(Debug, Clone)]
47pub struct Finding {
48    pub plugin_id: String,
49    pub severity: String,
50    pub message: String,
51    pub location: Option<FindingLocation>,
52}
53
54/// Collects analysis findings across plugins.
55#[derive(Debug, Default)]
56pub struct PluginReport {
57    entries: Vec<Finding>,
58}
59
60impl PluginReport {
61    pub fn new() -> Self {
62        Self { entries: Vec::new() }
63    }
64
65    pub fn add(&mut self, plugin_id: &str, severity: &str, message: &str) {
66        self.entries.push(Finding {
67            plugin_id: plugin_id.to_string(),
68            severity: severity.to_string(),
69            message: message.to_string(),
70            location: None,
71        });
72    }
73
74    pub fn warn(&mut self, plugin_id: &str, message: &str) {
75        self.add(plugin_id, "WARN", message);
76    }
77
78    pub fn error(&mut self, plugin_id: &str, message: &str) {
79        self.add(plugin_id, "ERROR", message);
80    }
81
82    pub fn warn_at(&mut self, plugin_id: &str, message: &str, location: FindingLocation) {
83        self.entries.push(Finding {
84            plugin_id: plugin_id.to_string(),
85            severity: "WARN".to_string(),
86            message: message.to_string(),
87            location: Some(location),
88        });
89    }
90
91    pub fn error_at(&mut self, plugin_id: &str, message: &str, location: FindingLocation) {
92        self.entries.push(Finding {
93            plugin_id: plugin_id.to_string(),
94            severity: "ERROR".to_string(),
95            message: message.to_string(),
96            location: Some(location),
97        });
98    }
99
100    pub fn findings(&self) -> &[Finding] {
101        &self.entries
102    }
103
104    pub fn as_text(&self) -> String {
105        if self.entries.is_empty() {
106            return "No findings.".to_string();
107        }
108        self.entries
109            .iter()
110            .map(|e| {
111                let base = format!("[{}] {}: {}", e.severity, e.plugin_id, e.message);
112                match &e.location {
113                    Some(loc) => format!("{} @ {}", base, loc),
114                    None => base,
115                }
116            })
117            .collect::<Vec<_>>()
118            .join("\n")
119    }
120}
121
122impl fmt::Display for PluginReport {
123    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124        write!(f, "{}", self.as_text())
125    }
126}
127
128/// Analysis plugin — static analysis of FlowDefinition.
129pub trait AnalysisPlugin<S: FlowState>: Send + Sync {
130    fn descriptor(&self) -> PluginDescriptor;
131    fn analyze(&self, definition: &FlowDefinition<S>, report: &mut PluginReport);
132}
133
134/// Engine plugin — installs hooks on FlowEngine.
135pub trait EnginePlugin<S: FlowState>: Send + Sync {
136    fn descriptor(&self) -> PluginDescriptor;
137    fn install(&self, engine: &mut FlowEngine<S>);
138}
139
140/// Runtime adapter plugin — binds FlowEngine to return richer API.
141pub trait RuntimeAdapterPlugin<S: FlowState>: Send + Sync {
142    fn descriptor(&self) -> PluginDescriptor;
143    fn id(&self) -> &str { self.descriptor().id }
144}