diff options
Diffstat (limited to 'src/repo/profile/useflags/mod.rs')
| -rw-r--r-- | src/repo/profile/useflags/mod.rs | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/src/repo/profile/useflags/mod.rs b/src/repo/profile/useflags/mod.rs new file mode 100644 index 0000000..d7fb96c --- /dev/null +++ b/src/repo/profile/useflags/mod.rs @@ -0,0 +1,94 @@ +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<P: AsRef<Path>>( + parents: &[Profile], + kind: Kind, + path: P, +) -> Result<Vec<UseFlag>, 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<FlagOperation>) -> Vec<UseFlag> { + 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<Vec<FlagOperation>, Error> { + Ok(LineBasedFileExpr::<FlagOperation>::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()) +} |
