origen_metal 1.4.0

Bare metal APIs for the Origen SDK
use crate::prog_gen::{FlowCondition, PGM};
use crate::Result;
use crate::ast::{Node, Processor, Return};

pub struct Jobs {
    negative: Vec<Node<PGM>>,
    conflicting: Vec<(Node<PGM>, Node<PGM>)>,
    stack: Vec<(Node<PGM>, bool)>,
}

pub fn run(node: &Node<PGM>) -> Result<()> {
    let mut p = Jobs {
        negative: vec![],
        conflicting: vec![],
        stack: vec![],
    };
    let _ = node.process(&mut p)?;

    let mut failed = false;
    let mut msg = "".to_string();

    if !p.conflicting.is_empty() {
        msg += "if_job and unless_job conditions cannot both be applied to the same tests. ";
        msg += "The following conflicts were found:";
        if crate::PROG_GEN_CONFIG.debug_enabled() {
            for (a, b) in &p.conflicting {
                if let PGM::Condition(n) = &a.attrs {
                    let a_condition = match matches!(n, FlowCondition::IfJob(_)) {
                        true => "if_job:    ",
                        false => "unless_job:",
                    };
                    msg += &format!("\n  {} {}", a_condition, a.meta_string());
                }
                if let PGM::Condition(n) = &b.attrs {
                    let b_condition = match matches!(n, FlowCondition::IfJob(_)) {
                        true => "if_job:    ",
                        false => "unless_job:",
                    };
                    msg += &format!("\n  {} {}", b_condition, b.meta_string());
                }
            }
        } else {
            msg += "\n  run again with the --debug switch to see them";
        }
        failed = true;
    }

    if !p.negative.is_empty() {
        msg += "\nJob names should not be negated, use unless_job if you want to specify !JOB";
        msg += "\nThe following negative job names were found:";
        if crate::PROG_GEN_CONFIG.debug_enabled() {
            for node in &p.negative {
                if let PGM::Condition(n) = &node.attrs {
                    match n {
                        FlowCondition::IfJob(jobs) | FlowCondition::UnlessJob(jobs) => {
                            msg += &format!("\n  {} - {}", jobs.join(", "), node.meta_string());
                        }
                        _ => unreachable!(),
                    }
                }
            }
        } else {
            msg += "\n  run again with the --debug switch to see them";
        }
        failed = true
    }

    if failed {
        bail!("{}", msg)
    } else {
        Ok(())
    }
}

impl Processor<PGM> for Jobs {
    fn on_node(&mut self, node: &Node<PGM>) -> crate::Result<Return<PGM>> {
        Ok(match &node.attrs {
            PGM::Condition(cond) => match cond {
                FlowCondition::IfJob(jobs) | FlowCondition::UnlessJob(jobs) => {
                    let state = matches!(cond, FlowCondition::IfJob(_));
                    if jobs
                        .into_iter()
                        .any(|j| j.starts_with("!") | j.starts_with("~"))
                    {
                        self.negative.push(node.without_children());
                    }
                    if !self.stack.is_empty() && self.stack.last().unwrap().1 != state {
                        self.conflicting.push((
                            self.stack.last().unwrap().0.clone(),
                            node.without_children(),
                        ));
                    } else {
                        self.stack.push((node.without_children(), state));
                        let _ = node.process_children(self)?;
                        self.stack.pop();
                    }
                    Return::None
                }
                _ => Return::ProcessChildren,
            },
            _ => Return::ProcessChildren,
        })
    }
}