use std::{ collections::HashMap, fs::{self, File}, io::{self, Read}, path::{Path, PathBuf}, }; use get::Get; use itertools::Itertools; use crate::{Parseable, atom::Atom, repo::ebuild::Eapi, useflag::UseFlag}; mod make_defaults; mod package; mod package_use; mod packages; mod parsers; mod useflags; #[derive(Debug, Clone)] enum LineBasedFileExpr { Comment, Expr(T), } #[derive(Debug, Clone)] enum FlagOperation { Add(UseFlag), Remove(UseFlag), } #[derive(Debug, thiserror::Error)] pub enum Error { #[error("{0}: io error: {1}")] Io(PathBuf, io::Error), #[error("error evaluating make.defaults settings: {0}")] MakeDefaults(#[from] make_defaults::Error), #[error("error evaluating packages settings: {0}")] Packages(#[from] packages::Error), #[error("error evaluating package settings: {0}")] Package(#[from] package::Error), #[error("error evaluating package.use settings: {0}")] PackageUse(#[from] package_use::Error), #[error("error evaluating use settings: {0}")] Use(#[from] useflags::Error), #[error("parser error: {0}")] Parser(String), } #[derive(Debug, Clone, Get)] pub struct Profile { #[get(kind = "deref")] path: PathBuf, eapi: Option, #[get(kind = "deref")] parents: Vec, make_defaults: HashMap, #[get(kind = "deref")] packages: Vec, #[get(kind = "deref")] package_mask: Vec, #[get(kind = "deref")] package_provided: Vec, package_use: HashMap>, package_use_force: HashMap>, package_use_mask: HashMap>, package_use_stable_force: HashMap>, package_use_stable_mask: HashMap>, #[get(kind = "deref")] use_force: Vec, #[get(kind = "deref")] use_mask: Vec, #[get(kind = "deref")] use_stable_force: Vec, #[get(kind = "deref")] use_stable_mask: Vec, } impl Profile { pub(super) fn evaluate>(path: P) -> Result { let parents_path = path.as_ref().join("parent"); let parents = match fs::read_to_string(&parents_path) { Ok(parents) => parents .lines() .map(|line| path.as_ref().join(line)) .map(Profile::evaluate) .collect::>()?, Err(e) if matches!(e.kind(), io::ErrorKind::NotFound) => Vec::new(), Err(e) => return Err(Error::Io(parents_path, e)), }; let eapi_path = path.as_ref().join("eapi"); let eapi = match fs::read_to_string(&eapi_path) .map(|s| Eapi::parse(s.trim()).map_err(str::to_string)) { Ok(Ok(eapi)) => Some(eapi), Ok(Err(rest)) => return Err(Error::Parser(rest)), Err(e) if matches!(e.kind(), io::ErrorKind::NotFound) => None, Err(e) => return Err(Error::Io(eapi_path, e)), }; let make_defaults = make_defaults::evaluate(&parents, &path)?; let packages = packages::evaluate(&parents, &path)?; let package_mask = package::evaluate(&parents, package::Kind::Mask, &path)?; let package_provided = package::evaluate(&parents, package::Kind::Provided, &path)?; let package_use = package_use::evaluate(&parents, package_use::Kind::Use, &path)?; let package_use_force = package_use::evaluate(&parents, package_use::Kind::Force, &path)?; let package_use_mask = package_use::evaluate(&parents, package_use::Kind::Mask, &path)?; let package_use_stable_force = package_use::evaluate(&parents, package_use::Kind::StableForce, &path)?; let package_use_stable_mask = package_use::evaluate(&parents, package_use::Kind::StableMask, &path)?; let use_force = useflags::evaluate(&parents, useflags::Kind::Force, &path)?; let use_mask = useflags::evaluate(&parents, useflags::Kind::Mask, &path)?; let use_stable_force = useflags::evaluate(&parents, useflags::Kind::StableForce, &path)?; let use_stable_mask = useflags::evaluate(&parents, useflags::Kind::StableMask, &path)?; Ok(Self { path: path.as_ref().to_path_buf(), parents, eapi, make_defaults, packages, package_mask, package_provided, package_use, package_use_force, package_use_mask, package_use_stable_force, package_use_stable_mask, use_force, use_mask, use_stable_force, use_stable_mask, }) } } fn read_config_files>(path: P) -> Result { let metadata = fs::metadata(&path)?; if metadata.is_file() { fs::read_to_string(&path) } else if metadata.is_dir() { let mut buffer = String::new(); let paths = fs::read_dir(&path)? .collect::, _>>()? .into_iter() .map(|entry| entry.path()) .filter(|path| path.starts_with(".")) .sorted() .collect::>(); for path in &paths { let mut file = File::open(path)?; file.read_to_string(&mut buffer)?; } Ok(buffer) } else { let path = fs::canonicalize(&path)?; read_config_files(path) } }