impl profile evaluation

This commit is contained in:
John Turner
2025-11-29 20:43:28 +00:00
parent 94f3397d19
commit d1127df296
16 changed files with 1365 additions and 2 deletions

View File

@@ -0,0 +1,125 @@
use std::{
collections::HashMap,
io,
path::{Path, PathBuf},
};
use mon::{Parser, ParserIter, ascii_whitespace1, input::InputIter};
use crate::{
Parseable,
atom::Atom,
repo::profile::{FlagOperation, LineBasedFileExpr, Profile, read_config_files},
useflag::UseFlag,
};
mod parsers;
#[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)]
struct Expr(Atom, Vec<FlagOperation>);
#[derive(Debug, Clone, Copy)]
pub(super) enum Kind {
Use,
Force,
Mask,
StableForce,
StableMask,
}
pub(super) fn evaluate<P: AsRef<Path>>(
parents: &[Profile],
kind: Kind,
path: P,
) -> Result<HashMap<Atom, Vec<UseFlag>>, Error> {
let file_path = match kind {
Kind::Use => "package.use",
Kind::Force => "package.use.force",
Kind::Mask => "package.use.mask",
Kind::StableForce => "package.use.stable.force",
Kind::StableMask => "package.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) => HashMap::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,
vars: HashMap<Atom, Vec<FlagOperation>>,
) -> HashMap<Atom, Vec<UseFlag>> {
let mut accumulated: HashMap<Atom, Vec<UseFlag>> = HashMap::new();
for parent in parents {
let source = match kind {
Kind::Use => parent.package_use(),
Kind::Force => parent.package_use_force(),
Kind::Mask => parent.package_use_mask(),
Kind::StableForce => parent.package_use_stable_force(),
Kind::StableMask => parent.package_use_stable_mask(),
};
for (atom, flags) in source {
accumulated
.entry(atom.clone())
.and_modify(|f| f.extend(flags.iter().cloned()))
.or_insert(flags.clone());
}
}
for (atom, flags) in vars {
match accumulated.get_mut(&atom) {
Some(accumulated) => {
for flag in flags {
match flag {
FlagOperation::Add(flag) => accumulated.push(flag),
FlagOperation::Remove(flag) => accumulated.retain(|v| *v != flag),
}
}
}
None => {
accumulated.insert(
atom.clone(),
flags
.iter()
.filter_map(|flag| match flag {
FlagOperation::Add(flag) => Some(flag),
FlagOperation::Remove(_) => None,
})
.cloned()
.collect(),
);
}
}
}
accumulated
}
fn parse(contents: &str) -> Result<HashMap<Atom, Vec<FlagOperation>>, Error> {
Ok(LineBasedFileExpr::<Expr>::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(Expr(atom, operations)) => Some((atom, operations)),
})
.collect())
}