ninja-files-data 0.1.0

Core data structures for ninja files
Documentation
use std::collections::{BTreeMap, BTreeSet};

use camino::Utf8Path;

use crate::{
    Build, FileName, InvalidFileName, InvalidRuleId, InvalidVariable, InvalidVariableId, RuleId,
    Variable, VariableId,
};

#[derive(Debug, Clone)]
pub struct BuildBuilder {
    rule: Option<RuleId>,
    explicit: BTreeSet<FileName>,
    implicit: BTreeSet<FileName>,
    order: BTreeSet<FileName>,
    dyndep: Option<FileName>,
    shadow: BTreeMap<VariableId, Variable>,
    errors: Vec<BuildBuilderError>,
}

#[derive(Debug, Clone)]
pub enum BuildBuilderError {
    InvalidRuleId(InvalidRuleId),
    InvalidFileName(InvalidFileName),
    InvalidVariable(InvalidVariable),
    InvalidVariableId(InvalidVariableId),
    DynDepAlreadyDefined {
        previous: FileName,
        dyndep: FileName,
    },
    VariableAlreadyDefined {
        id: VariableId,
        previous: Variable,
        var: Variable,
    },
}

impl BuildBuilder {
    pub fn new<Id>(rule_id: Id) -> Self
    where
        Id: AsRef<str>,
    {
        let rule_id = RuleId::try_create(rule_id);
        let mut errors = vec![];
        if let Some(err) = rule_id.clone().err() {
            errors.push(BuildBuilderError::InvalidRuleId(err));
        }

        let rule = rule_id.ok();
        BuildBuilder {
            rule,
            errors,
            explicit: BTreeSet::new(),
            implicit: BTreeSet::new(),
            order: BTreeSet::new(),
            dyndep: None,
            shadow: BTreeMap::new(),
        }
    }

    pub fn build(&self) -> Result<Build, Vec<BuildBuilderError>> {
        if !self.errors.is_empty() {
            return Err(self.errors.clone());
        }
        //Bug if none at this point
        let build = Build {
            rule: self.rule.clone().unwrap(),
            explicit: self.explicit.clone(),
            implicit: self.implicit.clone(),
            order: self.order.clone(),
            dyndep: self.dyndep.clone(),
            shadow: self.shadow.clone(),
        };
        Ok(build)
    }

    pub fn explicit<Dep>(mut self, dep: Dep) -> Self
    where
        Dep: AsRef<Utf8Path>,
    {
        let filename = FileName::try_create(dep);
        match filename {
            Ok(file) => {
                self.explicit.insert(file);
            }
            Err(err) => self.errors.push(BuildBuilderError::InvalidFileName(err)),
        }
        self
    }

    pub fn implicit<Dep>(mut self, dep: Dep) -> Self
    where
        Dep: AsRef<Utf8Path>,
    {
        let filename = FileName::try_create(dep);
        match filename {
            Ok(file) => {
                self.implicit.insert(file);
            }
            Err(err) => self.errors.push(BuildBuilderError::InvalidFileName(err)),
        }
        self
    }

    pub fn order<Dep>(mut self, dep: Dep) -> Self
    where
        Dep: AsRef<Utf8Path>,
    {
        let filename = FileName::try_create(dep);
        match filename {
            Ok(file) => {
                self.order.insert(file);
            }
            Err(err) => self.errors.push(BuildBuilderError::InvalidFileName(err)),
        }
        self
    }

    pub fn dyndep<Dep>(mut self, dep: Dep) -> Self
    where
        Dep: AsRef<Utf8Path>,
    {
        let filename = FileName::try_create(dep);
        match (filename, self.dyndep.clone()) {
            (Ok(file), None) => self.dyndep = Some(file),
            (Ok(dyndep), Some(previous)) => self
                .errors
                .push(BuildBuilderError::DynDepAlreadyDefined { previous, dyndep }),
            (Err(err), _) => self.errors.push(BuildBuilderError::InvalidFileName(err)),
        }
        self
    }

    pub fn variable<Id, Var>(mut self, id: Id, var: Var) -> Self
    where
        Id: AsRef<str>,
        Var: AsRef<str>,
    {
        let id = VariableId::try_create(id);
        let var = Variable::try_create(var);
        match (id, var) {
            (Ok(id), Ok(var)) => match self.shadow.insert(id.clone(), var.clone()) {
                Some(previous) if var == previous => (),
                Some(previous) => self.errors.push(BuildBuilderError::VariableAlreadyDefined {
                    id,
                    previous,
                    var,
                }),
                None => (),
            },
            (Ok(_), Err(v)) => self.errors.push(BuildBuilderError::InvalidVariable(v)),
            (Err(i), Ok(_)) => self.errors.push(BuildBuilderError::InvalidVariableId(i)),
            (Err(i), Err(v)) => {
                self.errors.push(BuildBuilderError::InvalidVariable(v));
                self.errors.push(BuildBuilderError::InvalidVariableId(i));
            }
        }
        self
    }
}

impl From<Build> for BuildBuilder {
    fn from(value: Build) -> Self {
        BuildBuilder {
            rule: Some(value.rule),
            explicit: value.explicit,
            implicit: value.implicit,
            order: value.order,
            dyndep: value.dyndep,
            shadow: value.shadow,
            errors: vec![],
        }
    }
}