use std::path::{Path, PathBuf};
use super::loading::{CONFIG_NAMES, EXAMPLE_SUFFIXES, try_copy_example};
use super::schema::{validate_schema, CommandConfig};
pub fn load_command(dir: &Path) -> Result<CommandConfig, String> {
load_command_with_env(dir, None)
}
pub fn load_command_with_env(dir: &Path, env: Option<&str>) -> Result<CommandConfig, String> {
let mut base_path: Option<PathBuf> = None;
for name in CONFIG_NAMES {
let path = dir.join(name);
if path.is_file() {
base_path = Some(path);
break;
}
}
if base_path.is_none() {
for name in CONFIG_NAMES {
for suffix in EXAMPLE_SUFFIXES {
let example = dir.join(format!("{name}{suffix}"));
if example.is_file() {
let target = dir.join(name);
let src = if try_copy_example(&example, &target) {
target
} else {
example
};
base_path = Some(src);
break;
}
}
if base_path.is_some() {
break;
}
}
}
let base_path = base_path
.ok_or_else(|| format!("no fdl.yml found in {}", dir.display()))?;
let mut layers = crate::overlay::resolve_chain(&base_path)?;
if let Some(name) = env {
if let Some(p) = crate::overlay::find_env_file(&base_path, name) {
layers.extend(crate::overlay::resolve_chain(&p)?);
}
}
let mut seen = std::collections::HashSet::new();
layers.retain(|(path, _)| seen.insert(path.clone()));
let merged = crate::overlay::merge_layers(
layers.into_iter().map(|(_, v)| v).collect::<Vec<_>>(),
);
let merged_str = serde_yaml_ng::to_string(&merged).map_err(|e| {
format!(
"{}: failed to re-serialize merged YAML for diagnostics: {e}",
base_path.display()
)
})?;
let mut cfg: CommandConfig = serde_yaml_ng::from_str(&merged_str)
.map_err(|e| format!("{}: {}", base_path.display(), e))?;
if let Some(schema) = &cfg.schema {
validate_schema(schema)
.map_err(|e| format!("schema error in {}/fdl.yml: {e}", dir.display()))?;
}
let cmd_name = dir
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("_");
let cache = crate::schema_cache::cache_path(dir, cmd_name);
let mut refs: Vec<std::path::PathBuf> = CONFIG_NAMES
.iter()
.map(|n| dir.join(n))
.filter(|p| p.exists())
.collect();
if cfg.compile.unwrap_or(false) {
refs.extend(crate::schema_cache::schema_source_refs(dir));
}
if !crate::schema_cache::is_stale(&cache, &refs) {
if let Some(cached) = crate::schema_cache::read_cache(&cache) {
cfg.schema = Some(cached);
}
} else if let Some(entry) = cfg.entry.as_deref() {
let opts_into_compile = cfg.compile.unwrap_or(false);
let should_probe =
!crate::schema_cache::is_cargo_entry(entry) || opts_into_compile;
if should_probe {
if let Ok(probed) =
crate::schema_cache::probe(entry, dir, cfg.docker.as_deref())
{
let _ = crate::schema_cache::write_cache(&cache, &probed);
cfg.schema = Some(probed);
}
}
}
Ok(cfg)
}