summaryrefslogtreecommitdiff
path: root/src/repo/profile/package
diff options
context:
space:
mode:
authorJohn Turner <jturner.usa@gmail.com>2025-11-29 20:43:28 +0000
committerJohn Turner <jturner.usa@gmail.com>2025-11-29 20:50:59 +0000
commitd1127df296aa7871555293e324d125e6d8a843e1 (patch)
treebaddfab365df586207fa9c1dadf270ee94c3f46f /src/repo/profile/package
parent94f3397d197e47eb58a7391acd9c63c5565fa26e (diff)
downloadgentoo-utils-profiles.tar.gz
impl profile evaluationprofiles
Diffstat (limited to 'src/repo/profile/package')
-rw-r--r--src/repo/profile/package/mod.rs95
-rw-r--r--src/repo/profile/package/parsers.rs13
2 files changed, 108 insertions, 0 deletions
diff --git a/src/repo/profile/package/mod.rs b/src/repo/profile/package/mod.rs
new file mode 100644
index 0000000..facf9ea
--- /dev/null
+++ b/src/repo/profile/package/mod.rs
@@ -0,0 +1,95 @@
+use std::{
+ io,
+ path::{Path, PathBuf},
+};
+
+use mon::{Parser, ParserIter, ascii_whitespace1, input::InputIter};
+
+use crate::{
+ Parseable,
+ atom::Atom,
+ repo::profile::{LineBasedFileExpr, Profile, read_config_files},
+};
+
+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, Copy)]
+pub(super) enum Kind {
+ Mask,
+ Provided,
+}
+
+#[derive(Debug, Clone)]
+enum Package {
+ Add(Atom),
+ Remove(Atom),
+}
+
+pub(super) fn evaluate<P: AsRef<Path>>(
+ parents: &[Profile],
+ kind: Kind,
+ path: P,
+) -> Result<Vec<Atom>, Error> {
+ let file_path = match kind {
+ Kind::Mask => "package.mask",
+ Kind::Provided => "package.provided",
+ };
+
+ 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, packages: Vec<Package>) -> Vec<Atom> {
+ let mut accumulated = Vec::new();
+
+ for parent in parents {
+ let source = match kind {
+ Kind::Mask => parent.package_mask(),
+ Kind::Provided => parent.package_provided(),
+ };
+
+ for package in source {
+ accumulated.push(package.clone());
+ }
+ }
+
+ for package in packages {
+ match package {
+ Package::Add(package) => {
+ accumulated.push(package);
+ }
+ Package::Remove(package) => {
+ accumulated.retain(|p| *p != package);
+ }
+ }
+ }
+
+ accumulated
+}
+
+fn parse(contents: &str) -> Result<Vec<Package>, Error> {
+ Ok(LineBasedFileExpr::<Package>::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(package) => Some(package),
+ })
+ .collect())
+}
diff --git a/src/repo/profile/package/parsers.rs b/src/repo/profile/package/parsers.rs
new file mode 100644
index 0000000..74d9acc
--- /dev/null
+++ b/src/repo/profile/package/parsers.rs
@@ -0,0 +1,13 @@
+use mon::{Parser, tag};
+
+use crate::{Parseable, atom::Atom, repo::profile::package::Package};
+
+impl<'a> Parseable<'a, &'a str> for Package {
+ type Parser = impl Parser<&'a str, Output = Self>;
+
+ fn parser() -> Self::Parser {
+ Atom::parser()
+ .map(Package::Add)
+ .or(Atom::parser().preceded_by(tag("-")).map(Package::Remove))
+ }
+}