use std::fs;
use std::io::{self, Write};
use std::path::PathBuf;
use clap::{Parser, ValueEnum};
use fomod_oxide::{DeclarativeConfig, FomodInfo, ModuleConfig};
#[derive(Parser)]
#[command(name = "fomod-oxide", about = "Generate declarative configs from FOMOD installers")]
struct Cli {
input: PathBuf,
#[arg(long)]
info: Option<PathBuf>,
#[arg(long)]
rev: Option<String>,
#[arg(short, long)]
output: Option<PathBuf>,
#[arg(short, long, default_value = "nix")]
format: Format,
#[arg(long)]
all: bool,
}
#[derive(Clone, ValueEnum)]
enum Format {
Nix,
Ron,
Json,
}
fn main() {
if let Err(e) = run() {
eprintln!("error: {e}");
std::process::exit(1);
}
}
fn run() -> Result<(), Box<dyn std::error::Error>> {
let cli = Cli::parse();
let xml = fs::read_to_string(&cli.input)?;
let config = ModuleConfig::parse(&xml)?;
let rev = if let Some(rev) = cli.rev {
rev
} else if let Some(ref info_path) = cli.info {
let info_xml = fs::read_to_string(info_path)?;
let info = FomodInfo::parse(&info_xml)?;
info.version
.or(info.name)
.unwrap_or_else(|| config.module_name.value.clone())
} else {
config.module_name.value.clone()
};
let decl = if cli.all {
DeclarativeConfig::from_all(&xml, rev, &config)
} else {
DeclarativeConfig::from_defaults(&xml, rev, &config)
};
let output = match cli.format {
Format::Nix => ronix::to_nix(&decl)?,
Format::Ron => ron::ser::to_string_pretty(&decl, ron::ser::PrettyConfig::default())?,
Format::Json => serde_json::to_string_pretty(&decl)?,
};
let output = if output.ends_with('\n') {
output
} else {
format!("{output}\n")
};
match cli.output {
Some(path) => fs::write(path, &output)?,
None => io::stdout().write_all(output.as_bytes())?,
}
Ok(())
}