mod ast;
mod parser;
pub use ast::{format_duration, Deployment, Execution, FluxConfig, Step, Timeout, CONDITION_VARS};
#[cfg(test)]
pub use parser::parse;
pub use parser::{parse_with_warnings, ParseWarning};
use std::collections::HashSet;
use std::path::Path;
use crate::core::logging as log;
pub const CONFIG_FILE: &str = ".flux";
pub fn load(path: &Path) -> anyhow::Result<FluxConfig> {
let src = std::fs::read_to_string(path)
.map_err(|e| anyhow::anyhow!("could not read {}: {e}", path.display()))?;
let (mut cfg, warnings) =
parse_with_warnings(&src).map_err(|e| anyhow::anyhow!("{}: {e}", path.display()))?;
report_warnings(path, &warnings);
let base = path.parent().unwrap_or_else(|| Path::new("."));
resolve_modules(&mut cfg, base)?;
Ok(cfg)
}
pub fn report_warnings(path: &Path, warnings: &[ParseWarning]) {
for w in warnings {
log::warn_line(&format!("{}: {w}", path.display()));
}
}
fn resolve_modules(cfg: &mut FluxConfig, base: &Path) -> anyhow::Result<()> {
if cfg.uses.is_empty() {
return Ok(());
}
let mut module_steps: Vec<Step> = Vec::new();
let mut visited: HashSet<String> = HashSet::new();
for name in cfg.uses.clone() {
expand_module(&name, base, &mut module_steps, &mut visited)?;
}
let explicit: HashSet<String> = cfg.steps.iter().map(|s| s.name.clone()).collect();
let mut merged: Vec<Step> = Vec::new();
for s in module_steps {
if !explicit.contains(&s.name) && !merged.iter().any(|m| m.name == s.name) {
merged.push(s);
}
}
merged.append(&mut cfg.steps);
cfg.steps = merged;
Ok(())
}
fn expand_module(
name: &str,
base: &Path,
out: &mut Vec<Step>,
visited: &mut HashSet<String>,
) -> anyhow::Result<()> {
if !visited.insert(name.to_string()) {
return Ok(());
}
let path = base.join("modules").join(format!("{name}.flux"));
let src = std::fs::read_to_string(&path)
.map_err(|e| anyhow::anyhow!("could not load module '{name}' ({}): {e}", path.display()))?;
let (modcfg, warnings) =
parse_with_warnings(&src).map_err(|e| anyhow::anyhow!("module '{name}': {e}"))?;
report_warnings(&path, &warnings);
for nested in &modcfg.uses {
expand_module(nested, base, out, visited)?;
}
out.extend(modcfg.steps);
Ok(())
}