1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
use std::{
io,
path::{Path, PathBuf},
};
use mon::{Parser, ParserIter, ascii_whitespace1, input::InputIter};
use crate::{
Parseable,
repo::profile::{FlagOperation, LineBasedFileExpr, Profile, read_config_files},
useflag::UseFlag,
};
#[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 {
Force,
Mask,
StableForce,
StableMask,
}
#[allow(clippy::unnecessary_wraps)]
pub(super) fn evaluate<P: AsRef<Path>>(
parents: &[Profile],
kind: Kind,
path: P,
) -> Result<Vec<UseFlag>, Error> {
let file_path = match kind {
Kind::Force => "use.force",
Kind::Mask => "use.mask",
Kind::StableForce => "use.stable.force",
Kind::StableMask => "use.stable.mask",
};
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, operations: Vec<FlagOperation>) -> Vec<UseFlag> {
let mut accumulated = Vec::new();
for parent in parents {
let source = match kind {
Kind::Force => parent.use_force(),
Kind::Mask => parent.use_mask(),
Kind::StableForce => parent.use_stable_force(),
Kind::StableMask => parent.use_stable_mask(),
};
for flag in source {
accumulated.push(flag.clone());
}
}
for operation in operations {
match operation {
FlagOperation::Add(flag) => {
accumulated.push(flag);
}
FlagOperation::Remove(flag) => {
accumulated.retain(|v| *v != flag);
}
}
}
accumulated
}
fn parse(contents: &str) -> Result<Vec<FlagOperation>, Error> {
Ok(LineBasedFileExpr::<FlagOperation>::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(flag_operation) => Some(flag_operation),
})
.collect())
}
|