yao 0.1.4

a fast, tiny, extensiable workflow engine
Documentation
use crate::{utils, Step, TaskState, Workflow};
use colored::Colorize;
use std::io::{Result, Write};

pub fn tree(workflow: &Workflow) -> Result<()> {
    let mut buffer = Vec::new();
    let mut levels: Vec<bool> = Vec::new();
    let (start, end, elapsed) = utils::fmt_timestamp(
        workflow.start_time(),
        workflow.end_time(),
        "%Y-%m-%d %H:%M:%S",
    );
    writeln!(&mut buffer, "workflow: {}", workflow.name)?;
    writeln!(&mut buffer, "version: {}", workflow.ver)?;
    writeln!(&mut buffer, "state: {}", colered_state(workflow.state()))?;
    writeln!(&mut buffer, "time: {} - {}", start, end)?;
    writeln!(&mut buffer, "elapsed: {}ms", elapsed)?;

    for (i, job) in workflow.jobs.iter().map(|job| job.clone()).enumerate() {
        levels.push(i == workflow.jobs.len() - 1);
        let (.., elapsed) =
            utils::fmt_timestamp(job.start_time(), job.end_time(), "%Y-%m-%d %H:%M:%S");

        let line = print_line(
            i,
            workflow.jobs.len(),
            &mut levels,
            format!(
                "{}",
                &format!(
                    "job:{}  result: {}  elapsed:{}ms",
                    job.name,
                    colered_state(job.state()),
                    elapsed,
                )
            ),
        );
        writeln!(&mut buffer, "{}", line)?;
        let step_count = job.steps.len();
        for (j, step) in job.steps.iter().enumerate() {
            let (.., elapsed) =
                utils::fmt_timestamp(step.start_time(), step.end_time(), "%Y-%m-%d %H:%M:%S");
            levels.push(j == job.steps.len() - 1);

            let line = print_line(
                j,
                step_count,
                &mut levels,
                format!(
                    "step:{}({})  result: {} elapsed:{}ms",
                    step.name,
                    step.id,
                    colered_state(step.state()),
                    elapsed,
                ),
            );
            writeln!(&mut buffer, "{}", line)?;
            output_step(step, &mut levels, &mut buffer)?;
            levels.pop();
        }
        levels.pop();
    }

    println!("{}", String::from_utf8(buffer).unwrap());
    Ok(())
}

fn print_line(i: usize, count: usize, levels: &mut Vec<bool>, text: String) -> String {
    let mut line = String::new();
    let items = &levels[0..levels.len() - 1];
    for is_last in items {
        if *is_last {
            line.push_str("   ");
        } else {
            line.push_str("|  ");
        }
    }
    if i == count - 1 {
        line.push_str("|_");
    } else {
        line.push_str("|-");
    }
    line.push_str(&text);
    line
}

fn output_step(step: &Step, levels: &mut Vec<bool>, buffer: &mut Vec<u8>) -> Result<()> {
    let branch_count = step.branches.len();

    let acts = step.acts.read().unwrap();
    for (i, act) in acts.iter().enumerate() {
        levels.push(i == acts.len() - 1);

        let (.., elapsed) =
            utils::fmt_timestamp(act.start_time(), act.end_time(), "%Y-%m-%d %H:%M:%S");

        let line = print_line(
            i,
            acts.len(),
            levels,
            format!(
                "act: {}  result: {} owner: {} user: {} elapsed:{}ms",
                act.id,
                colered_state(act.state()),
                act.owner,
                match act.user() {
                    Some(u) => u,
                    None => "none".to_string(),
                },
                elapsed,
            ),
        );
        writeln!(buffer, "{}", line)?;
        levels.pop();
    }

    for (i, branch) in step.branches.iter().enumerate() {
        let (.., elapsed) =
            utils::fmt_timestamp(branch.start_time(), branch.end_time(), "%Y-%m-%d %H:%M:%S");
        levels.push(i == step.branches.len() - 1);
        let line = print_line(
            i,
            branch_count,
            levels,
            format!(
                "branch: {}  result: {} elapsed:{}ms",
                branch.id,
                colered_state(branch.state()),
                elapsed,
            ),
        );
        writeln!(buffer, "{}", line)?;
        let count = branch.steps.len();
        for (j, step) in branch.steps.iter().enumerate() {
            let (.., elapsed) =
                utils::fmt_timestamp(step.start_time(), step.end_time(), "%Y-%m-%d %H:%M:%S");
            levels.push(j == branch.steps.len() - 1);
            let line = print_line(
                j,
                count,
                levels,
                format!(
                    "step: {}({})  result: {} elapsed:{}ms",
                    step.name,
                    step.id,
                    colered_state(step.state()),
                    elapsed,
                ),
            );
            writeln!(buffer, "{}", line)?;
            output_step(step, levels, buffer)?;
            levels.pop();
        }

        levels.pop();
    }

    Ok(())
}

fn colered_state(state: TaskState) -> String {
    let s: String = state.clone().into();
    let colored_value = match state {
        TaskState::None | TaskState::Skip | TaskState::WaitingEvent | TaskState::Pending => {
            s.yellow()
        }
        TaskState::Running => s.blue(),
        TaskState::Fail(..) | TaskState::Abort(..) => s.red(),
        TaskState::Success => s.green(),
    };

    colored_value.to_string()
}