impl profile evaluation
This commit is contained in:
135
src/repo/profile/make_defaults/mod.rs
Normal file
135
src/repo/profile/make_defaults/mod.rs
Normal file
@@ -0,0 +1,135 @@
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
fs, io,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use mon::{Parser, ParserIter, ascii_whitespace1, input::InputIter};
|
||||
|
||||
use crate::{
|
||||
Parseable,
|
||||
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, PartialEq, Eq, Hash)]
|
||||
struct Key(String);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Literal(String);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Interpolation(String);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Segment {
|
||||
Literal(Literal),
|
||||
Interpolation(Interpolation),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Assignment(Key, Vec<Segment>);
|
||||
|
||||
pub(super) fn evaluate<P: AsRef<Path>>(
|
||||
parents: &[Profile],
|
||||
path: P,
|
||||
) -> Result<HashMap<String, String>, Error> {
|
||||
let parsed = match fs::read_to_string(path.as_ref().join("make.defaults")) {
|
||||
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)),
|
||||
};
|
||||
|
||||
let mut vars = interpolate(parents, parsed);
|
||||
|
||||
incremental(parents, &mut vars);
|
||||
|
||||
Ok(vars)
|
||||
}
|
||||
|
||||
fn incremental(parents: &[Profile], vars: &mut HashMap<String, String>) {
|
||||
for key in [
|
||||
"USE",
|
||||
"USE_EXPAND",
|
||||
"USE_EXPAND_HIDDEN",
|
||||
"CONFIG_PROTECT",
|
||||
"CONFIG_PROTECT_MASK",
|
||||
] {
|
||||
let mut accumulated = Vec::new();
|
||||
|
||||
for parent in parents {
|
||||
if let Some(values) = parent.make_defaults().get(key) {
|
||||
accumulated.extend(values.split_ascii_whitespace());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(values) = vars.get(key) {
|
||||
accumulated.extend(values.split_ascii_whitespace());
|
||||
}
|
||||
|
||||
let mut final_values = Vec::new();
|
||||
|
||||
for var in accumulated {
|
||||
if var == "-*" {
|
||||
final_values.clear();
|
||||
} else if let Some(stripped) = var.strip_prefix("-") {
|
||||
final_values.retain(|v| *v != stripped);
|
||||
} else {
|
||||
final_values.push(var);
|
||||
}
|
||||
}
|
||||
|
||||
let mut seen = HashSet::new();
|
||||
final_values.retain(|v| seen.insert(*v));
|
||||
|
||||
if !final_values.is_empty() {
|
||||
vars.insert(key.to_string(), final_values.join(" "));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn interpolate(parents: &[Profile], vars: HashMap<Key, Vec<Segment>>) -> HashMap<String, String> {
|
||||
let parent_vars = parents
|
||||
.iter()
|
||||
.flat_map(|parent| parent.make_defaults().clone().into_iter())
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
vars.into_iter()
|
||||
.map(|(key, segments)| {
|
||||
let interpolated = segments
|
||||
.into_iter()
|
||||
.map(|segment| match segment {
|
||||
Segment::Interpolation(i) => parent_vars.get(&i.0).cloned().unwrap_or_default(),
|
||||
Segment::Literal(literal) => literal.0.trim().to_string(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let joined = interpolated.join("");
|
||||
|
||||
(key.0, joined)
|
||||
})
|
||||
.collect::<HashMap<String, String>>()
|
||||
}
|
||||
|
||||
fn parse(contents: &str) -> Result<HashMap<Key, Vec<Segment>>, Error> {
|
||||
Ok(LineBasedFileExpr::<Assignment>::parser()
|
||||
.separated_by_with_opt_trailing(ascii_whitespace1())
|
||||
.many()
|
||||
.parse_finished(InputIter::new(contents))
|
||||
.map_err(|e| Error::Parser(dbg!(e).rest().to_string()))?
|
||||
.into_iter()
|
||||
.filter_map(|expr| match expr {
|
||||
LineBasedFileExpr::Comment => None,
|
||||
LineBasedFileExpr::Expr(Assignment(key, value)) => Some((key, value)),
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
Reference in New Issue
Block a user