use serde::{Deserialize, Serialize};
use crate::symbol::{Confidence, SymbolId};
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
pub struct Flow {
pub steps: Vec<Step>,
}
impl Flow {
pub fn new(steps: Vec<Step>) -> Self {
Self { steps }
}
pub fn is_empty(&self) -> bool {
self.steps.is_empty()
}
pub fn calls(&self) -> impl Iterator<Item = &Call> {
let mut out = Vec::new();
collect_calls(&self.steps, &mut out);
out.into_iter()
}
pub fn call_count(&self) -> usize {
self.calls().count()
}
pub fn depth(&self) -> usize {
fn depth(steps: &[Step]) -> usize {
steps
.iter()
.map(|s| match s {
Step::Call(_) | Step::Return(_) => 0,
Step::Branch { arms, .. } => 1 + arms.iter().map(|a| depth(&a.steps)).max().unwrap_or(0),
Step::Loop { body, .. } | Step::Optional { body, .. } => 1 + depth(body),
Step::Parallel { arms } => 1 + arms.iter().map(|a| depth(&a.steps)).max().unwrap_or(0),
})
.max()
.unwrap_or(0)
}
depth(&self.steps)
}
}
fn collect_calls<'a>(steps: &'a [Step], out: &mut Vec<&'a Call>) {
for step in steps {
match step {
Step::Call(c) => out.push(c),
Step::Branch { arms, .. } | Step::Parallel { arms } => {
for arm in arms {
collect_calls(&arm.steps, out);
}
}
Step::Loop { body, .. } | Step::Optional { body, .. } => collect_calls(body, out),
Step::Return(_) => {}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "step", rename_all = "snake_case")]
pub enum Step {
Call(Call),
Branch {
arms: Vec<Arm>,
},
Loop {
label: String,
body: Vec<Step>,
},
Optional {
label: String,
body: Vec<Step>,
},
Parallel {
arms: Vec<Arm>,
},
Return(Exit),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Arm {
pub label: String,
pub steps: Vec<Step>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CallKind {
Function,
Method,
Macro,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Call {
pub target: SymbolId,
pub label: String,
pub kind: CallKind,
pub confidence: Confidence,
pub awaited: bool,
pub fallible: bool,
pub line: u32,
}
impl Call {
pub fn new(target: SymbolId, label: impl Into<String>, confidence: Confidence) -> Self {
Self {
target,
label: label.into(),
kind: CallKind::Function,
confidence,
awaited: false,
fallible: false,
line: 0,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Exit {
pub label: String,
pub line: u32,
}