use std::{
collections::HashMap,
io::{self, Write},
path::PathBuf,
};
use awsm_env::{EnvFormatter, Formatter, JsonFormatter, ShellFormatter, parse, process_entries};
use clap::{Parser, ValueEnum};
use indexmap::IndexMap;
use tokio::fs;
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Args {
#[arg(default_value = ".env.example")]
spec: PathBuf,
#[arg(long, short, value_enum, default_value = "env")]
format: Format,
#[arg(long, short)]
output: Option<PathBuf>,
#[arg(long = "var", short, value_parser = parse_key_val)]
vars: Option<Vec<(String, String)>>,
#[arg(long, short, value_parser = parse_key_val)]
placeholders: Option<Vec<(String, String)>>,
#[arg(long)]
no_defaults: bool,
}
fn parse_key_val(s: &str) -> Result<(String, String), String> {
let mut split = s.split("=");
let key = split
.next()
.ok_or(format!("Key value pairs should be of the form key=value"))?;
let value = split
.next()
.ok_or(format!("Key value pairs should be of the form key=value",))?;
Ok((key.to_string(), value.to_owned()))
}
#[derive(Clone, ValueEnum)]
enum Format {
Env,
Shell,
Json,
}
#[tokio::main]
async fn main() {
let args = Args::parse();
let vars: IndexMap<String, String> = args
.vars
.unwrap_or(Vec::with_capacity(0))
.into_iter()
.collect();
let placeholders: HashMap<String, String> = args
.placeholders
.unwrap_or(Vec::with_capacity(0))
.into_iter()
.collect();
let input = match fs::read_to_string(args.spec).await {
Ok(file) => file,
Err(err) => {
eprintln!("Error reading file: {}", err);
return;
}
};
let mut input_entries = match parse(&input) {
Ok(entries) => entries,
Err(err) => {
eprintln!("Error parsing file: {}", err);
return;
}
};
if args.no_defaults {
for entry in input_entries.iter_mut() {
entry.value = None;
}
}
let output_entries = match process_entries(input_entries, &vars, &placeholders).await {
Ok(entries) => entries,
Err(err) => {
eprintln!("Error fetching secrets: {}", err);
return;
}
};
let output = match args.format {
Format::Env => EnvFormatter::new().format(&output_entries),
Format::Shell => ShellFormatter::new().format(&output_entries),
Format::Json => JsonFormatter::new().format(&output_entries),
};
let result = match args.output {
Some(path) => fs::write(path, output.as_bytes()).await,
None => io::stdout().write_all(output.as_bytes()),
};
match result {
Ok(_) => (),
Err(err) => {
eprintln!("Error writing output: {}", err);
}
};
}