summaryrefslogtreecommitdiff
path: root/src/repo/profile/useflags/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/repo/profile/useflags/mod.rs')
-rw-r--r--src/repo/profile/useflags/mod.rs94
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())
+}