use super::EvalContext;
use crate::paths;
use crate::policy::PolicyError;
impl EvalContext {
pub(crate) fn validate_moved_dirs(&self) -> Result<(), PolicyError> {
for (default, _) in &self.moved_dirs {
let trimmed = default.trim();
let valid = !trimmed.is_empty()
&& trimmed != "/"
&& (trimmed == "~" || trimmed.starts_with("~/") || paths::is_absolute(trimmed));
if !valid {
return Err(PolicyError::Rule {
rule: "context.moved_dirs".to_owned(),
problem: format!(
"default directory `{default}` must be absolute (`/…`) or home-rooted (`~/…`), \
not empty, whitespace or a bare `/`"
),
});
}
}
Ok(())
}
pub(crate) fn spellings(&self, raw: &str) -> Vec<String> {
let (negated, body) = crate::pattern::split_negation(raw);
let bang = if negated { "!" } else { "" };
let moved = self.moved_dirs.iter().filter_map(|(default, dir)| {
let rest = body.strip_prefix(default.as_str())?;
(rest.is_empty() || rest.starts_with('/')).then(|| format!("{bang}{dir}{rest}"))
});
let raws: Vec<String> = std::iter::once(raw.to_owned()).chain(moved).collect();
let projects: Vec<Option<&str>> = match &self.project {
Some(p) => [Some(p), self.real_project.as_ref()]
.into_iter()
.flatten()
.map(|p| Some(p.as_str()))
.collect(),
None => vec![None],
};
let mut out = Vec::new();
for home in [Some(&self.home), self.real_home.as_ref()]
.into_iter()
.flatten()
{
for project in &projects {
out.extend(
raws.iter()
.filter_map(|raw| paths::expand_pattern(raw, home, *project)),
);
}
}
out.sort();
out.dedup();
out
}
}