use std::path::Path;
use serde_json::Value;
use crate::PluginKind;
use super::{ConfigError, Merged, Origin, SettingPath};
pub fn resolve_document_relative_paths(settings: &mut Merged) -> Result<(), ConfigError> {
let mut rewrites: Vec<(SettingPath, Value)> = Vec::new();
for setting in settings.values() {
let Origin::File { path: document } = &setting.origin else {
continue;
};
let Some((source, field)) = source_config_field(&setting.key) else {
continue;
};
let Some(declared) = declared_paths_of(settings, source) else {
continue;
};
if !declared.contains(&field.as_str()) {
continue;
}
let Some(raw) = setting.value.as_str() else {
continue;
};
if raw.is_empty() || Path::new(raw).is_absolute() {
continue;
}
let Some(directory) = document.parent() else {
continue;
};
let rebased = directory
.join(raw)
.into_os_string()
.into_string()
.map_err(|_| {
ConfigError::setting(
setting.key.to_string(),
format!(
"this path is measured from {}, the directory holding the configuration \
document that set it, and that directory's name is not valid UTF-8, so \
the path it resolves to cannot be written down",
directory.display()
),
"give this setting an absolute path, or move the configuration document \
under a directory whose name is valid UTF-8.",
)
})?;
rewrites.push((setting.key.clone(), Value::String(rebased)));
}
for (key, value) in rewrites {
settings.set_value(&key, value);
}
Ok(())
}
fn source_config_field(key: &SettingPath) -> Option<(&str, String)> {
match key.segments() {
[sources, name, config, field @ ..]
if sources == "sources" && config == "config" && !field.is_empty() =>
{
Some((name.as_str(), field.join(".")))
}
_ => None,
}
}
fn declared_paths_of(settings: &Merged, name: &str) -> Option<&'static [&'static str]> {
let key = SettingPath::new(
vec!["sources".to_owned(), name.to_owned(), "plugin".to_owned()],
"sources",
)
.ok()?;
let kind = PluginKind::parse(settings.get(&key)?.value.as_str()?)?;
Some(kind.plugin().document_relative_paths())
}