use std::fmt::{Display, Formatter, Result as FResult};
use super::spec::{Elem, ElemBranch, ElemCommit, ElemLoop, ElemParallel, ElemTask, Workflow};
macro_rules! write_iter {
($iter:expr, $conn:literal) => {{
let mut iter = $iter.peekable();
format!(
"{}",
if let Some(first) = iter.next() {
if iter.peek().is_some() {
format!(concat!("{}", $conn, "{}"), first, iter.collect::<Vec<String>>().join($conn))
} else {
format!("{}", first)
}
} else {
String::from("<none>")
}
)
}};
}
fn print_elem(f: &mut Formatter, elem: &Elem, prefix: &dyn Display) -> FResult {
match elem {
Elem::Task(ElemTask { id, name, package, version, input, output, location, metadata, next }) => {
writeln!(f, "{prefix}task")?;
writeln!(f, "{prefix} - id : {id}")?;
writeln!(f, "{prefix}")?;
writeln!(f, "{prefix} - name : {name}")?;
writeln!(f, "{prefix} - package : {package}")?;
writeln!(f, "{prefix} - version : {version}")?;
writeln!(f, "{prefix}")?;
writeln!(f, "{prefix} - input : {}", write_iter!(input.iter().map(|data| format!("'{}'", data.name)), " or "))?;
writeln!(
f,
"{} - output : {}",
prefix,
if let Some(output) = &output { format!("'{}'", output.name.as_str()) } else { "<none>".into() }
)?;
writeln!(f, "{prefix}")?;
writeln!(f, "{prefix} - location : {}", if let Some(location) = &location { location.as_str() } else { "<unplanned>" })?;
writeln!(
f,
"{} - metadata : {}",
prefix,
write_iter!(
metadata.iter().map(|metadata| format!(
"#{}.{}{}",
metadata.owner,
metadata.tag,
if let Some((assigner, signature)) = &metadata.signature { format!("#{assigner}:{signature}") } else { String::new() }
)),
", "
)
)?;
print_elem(f, next, prefix)
},
Elem::Branch(ElemBranch { branches, next }) => {
writeln!(f, "{prefix}branch")?;
for (i, branch) in branches.iter().enumerate() {
writeln!(f, "{prefix}{}<branch{}>", Indent(4), i)?;
print_elem(f, branch, &Pair(prefix, Indent(8)))?;
writeln!(f, "{prefix}")?;
}
print_elem(f, next, prefix)
},
Elem::Parallel(ElemParallel { branches, merge, next }) => {
writeln!(f, "{prefix}parallel")?;
writeln!(f, "{prefix} - merge strategy : {merge:?}")?;
for (i, branch) in branches.iter().enumerate() {
writeln!(f, "{prefix}{}<branch{}>", Indent(4), i)?;
print_elem(f, branch, &Pair(prefix, Indent(8)))?;
writeln!(f, "{prefix}")?;
}
print_elem(f, next, prefix)
},
Elem::Loop(ElemLoop { body, next }) => {
writeln!(f, "{prefix}loop")?;
writeln!(f, "{}<repeated>", Pair(prefix, Indent(4)))?;
print_elem(f, body, &Pair(prefix, Indent(8)))?;
writeln!(f)?;
print_elem(f, next, prefix)
},
Elem::Commit(ElemCommit { id, data_name, location, input, next }) => {
writeln!(f, "{prefix}commit <{} as '{}'>", write_iter!(input.iter().map(|data| format!("'{}'", data.name)), " or "), data_name)?;
writeln!(f, "{prefix} - id : {id}")?;
for i in input {
if let Some(from) = &i.from {
writeln!(f, "{prefix} - from : '{}' <- '{}'", i.name, from)?;
}
}
if let Some(location) = location {
writeln!(f, "{prefix} - to : {location}")?;
}
print_elem(f, next, prefix)
},
Elem::Next => {
writeln!(f, "{}next", prefix)
},
Elem::Stop(returns) => {
writeln!(
f,
"{}stop{}",
prefix,
if !returns.is_empty() {
format!(" <returns {}>", write_iter!(returns.iter().map(|data| format!("'{}'", data.name)), " or "))
} else {
String::new()
}
)
},
}
}
struct Pair<D1, D2>(D1, D2);
impl<D1: Display, D2: Display> Display for Pair<D1, D2> {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> FResult { write!(f, "{}{}", self.0, self.1) }
}
struct Indent(usize);
impl Display for Indent {
fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
for _ in 0..self.0 {
write!(f, " ")?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct WorkflowFormatter<'w> {
wf: &'w Workflow,
}
impl<'w> Display for WorkflowFormatter<'w> {
fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
writeln!(f, "Workflow [")?;
if !self.wf.metadata.is_empty() {
writeln!(
f,
"{}{}",
Indent(4),
write_iter!(
self.wf.metadata.iter().map(|metadata| format!(
"#{}.{}{}",
metadata.owner,
metadata.tag,
if let Some((assigner, signature)) = &metadata.signature { format!("#{assigner}:{signature}") } else { String::new() }
)),
", "
)
)?;
writeln!(f)?;
}
print_elem(f, &self.wf.start, &Indent(4))?;
write!(f, "]")
}
}
impl Workflow {
#[inline]
pub fn visualize(&self) -> WorkflowFormatter { WorkflowFormatter { wf: self } }
}