use std::{ io, path::{Path, PathBuf}, }; use mon::{Parser, ParserIter, ascii_whitespace1, input::InputIter}; use crate::{ Parseable, repo::profile::{FlagOperation, LineBasedFileExpr, Profile, read_config_files}, useflag::UseFlag, }; #[derive(Debug, thiserror::Error)] pub enum Error { #[error("{0}: io error: {1}")] Io(PathBuf, io::Error), #[error("parser error: {0}")] Parser(String), } #[derive(Debug, Clone, Copy)] pub(super) enum Kind { Force, Mask, StableForce, StableMask, } #[allow(clippy::unnecessary_wraps)] pub(super) fn evaluate>( parents: &[Profile], kind: Kind, path: P, ) -> Result, Error> { let file_path = match kind { Kind::Force => "use.force", Kind::Mask => "use.mask", Kind::StableForce => "use.stable.force", Kind::StableMask => "use.stable.mask", }; let parsed = match read_config_files(path.as_ref().join(file_path)) { Ok(contents) => parse(&contents)?, Err(e) if matches!(e.kind(), io::ErrorKind::NotFound) => Vec::new(), Err(e) => return Err(Error::Io(path.as_ref().to_path_buf(), e)), }; Ok(inherit(parents, kind, parsed)) } fn inherit(parents: &[Profile], kind: Kind, operations: Vec) -> Vec { let mut accumulated = Vec::new(); for parent in parents { let source = match kind { Kind::Force => parent.use_force(), Kind::Mask => parent.use_mask(), Kind::StableForce => parent.use_stable_force(), Kind::StableMask => parent.use_stable_mask(), }; for flag in source { accumulated.push(flag.clone()); } } for operation in operations { match operation { FlagOperation::Add(flag) => { accumulated.push(flag); } FlagOperation::Remove(flag) => { accumulated.retain(|v| *v != flag); } } } accumulated } fn parse(contents: &str) -> Result, Error> { Ok(LineBasedFileExpr::::parser() .separated_by_with_opt_trailing(ascii_whitespace1()) .many() .parse_finished(InputIter::new(contents)) .map_err(|e| Error::Parser(e.rest().to_string()))? .into_iter() .filter_map(|expr| match expr { LineBasedFileExpr::Comment => None, LineBasedFileExpr::Expr(flag_operation) => Some(flag_operation), }) .collect()) }