runtasktic 1.2.1

Command-line task management tool for execution of regular long sequential or parallel tasks.
Documentation
use super::TaskFst;
use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;
use std::process::{Command, Stdio};

pub fn dot_write_file<W: Write>(fst: &TaskFst, writer: &mut W) -> std::io::Result<()> {
  writeln!(writer, "digraph {{")?;
  let mut iter = fst.iter();
  while iter.has_next() {
    let node = iter.next().unwrap();
    let label = node.label.replace("\"", "\\\"");
    let id = format_id(&node.label);

    let shape = if node.next.len() == 0 {
      "doublecircle"
    } else {
      "circle"
    };

    if node.prev.len() == 0 {
      writeln!(writer, r#"  init_{}[label="", shape=point]"#, id)?;
      writeln!(writer, r#"  init_{} -> {0}"#, id)?;
    }

    writeln!(writer, r#"  {}[label="{}" shape={}]"#, id, label, shape)?;
    for next in node.next {
      writeln!(writer, "  {} -> {}", id, format_id(&fst.states[next].label))?;
    }
    iter.mark_done(node.id);
  }
  writeln!(writer, "}}")?;
  Ok(())
}

pub fn dot_write_png<R: Read, P: AsRef<Path>>(reader: &mut R, path: P) -> std::io::Result<()> {
  let file = File::create(path)?;
  let mut r: Vec<u8> = vec![];
  reader.read_to_end(&mut r)?;
  let mut child = Command::new("dot")
    .arg("-T")
    .arg("png")
    .stdin(Stdio::piped())
    .stdout(file)
    .spawn()?;

  if let Some(stdin) = &mut child.stdin {
    stdin.write_all(&r)?;
  }

  child.wait()?;

  Ok(())
}

fn format_id(id: &String) -> String {
  id.chars()
    .map(|c| if c == ' ' { '_' } else { c })
    .filter(|c| c.is_alphanumeric() || *c == '_')
    .collect::<String>()
}

#[cfg(test)]
mod test {
  use super::*;
  #[test]
  fn dot_write_file() {
    let mut fst = TaskFst::new();
    fst.add_state("\"a\"");
    fst.add_state("b a ba");
    fst.add_arc(0, 1);
    fst.add_start_state(0);

    fst.add_state("c");
    fst.add_arc(0, 2);
    fst.add_arc(1, 2);

    fst.add_state("d%s");
    fst.add_arc(2, 3);

    fst.add_state("e");
    fst.add_start_state(4);

    fst.add_arc(4, 3);

    let mut result: Vec<u8> = vec![];
    assert!(super::dot_write_file(&fst, &mut result).is_ok());
    let result = std::str::from_utf8(&result).unwrap();
    println!("{}", result);
    assert_eq!(
      result,
      r#"digraph {
  init_a[label="", shape=point]
  init_a -> a
  a[label="\"a\"" shape=circle]
  a -> b_a_ba
  a -> c
  init_e[label="", shape=point]
  init_e -> e
  e[label="e" shape=circle]
  e -> ds
  b_a_ba[label="b a ba" shape=circle]
  b_a_ba -> c
  c[label="c" shape=circle]
  c -> ds
  ds[label="d%s" shape=doublecircle]
}
"#
    );
  }
}