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>( parents: &[Profile], kind: Kind, path: P, ) -> Result, 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) -> Vec { 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, Error> { Ok(LineBasedFileExpr::::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()) }