summaryrefslogtreecommitdiff
path: root/src/repo/profile/packages/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/repo/profile/packages/mod.rs')
-rw-r--r--src/repo/profile/packages/mod.rs75
1 files changed, 75 insertions, 0 deletions
diff --git a/src/repo/profile/packages/mod.rs b/src/repo/profile/packages/mod.rs
new file mode 100644
index 0000000..4fd6559
--- /dev/null
+++ b/src/repo/profile/packages/mod.rs
@@ -0,0 +1,75 @@
+use std::{
+ fs, io,
+ path::{Path, PathBuf},
+};
+
+use mon::{Parser, ParserIter, ascii_whitespace1, input::InputIter};
+
+use crate::{
+ Parseable,
+ atom::Atom,
+ repo::profile::{LineBasedFileExpr, Profile},
+};
+
+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)]
+enum Package {
+ Add(Atom),
+ Remove(Atom),
+}
+
+pub(super) fn evaluate<P: AsRef<Path>>(parents: &[Profile], path: P) -> Result<Vec<Atom>, Error> {
+ let parsed = match fs::read_to_string(path.as_ref().join("packages")) {
+ 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, parsed))
+}
+
+fn inherit(parents: &[Profile], packages: Vec<Package>) -> Vec<Atom> {
+ let mut accumulated = Vec::new();
+
+ for parent in parents {
+ for package in parent.packages() {
+ 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())
+}