feat: add utility to convert TOML configuration file to list of environment variables (#3096)

This commit is contained in:
Sanchith Hegde
2023-12-11 13:20:19 +05:30
committed by GitHub
parent 4e8de46423
commit 2c4599a1cd
30 changed files with 485 additions and 466 deletions

View File

@ -0,0 +1,43 @@
use std::path::PathBuf;
/// Utility to import a hyperswitch TOML configuration file, convert it into environment variable
/// key-value pairs, and export it in the specified format.
#[derive(clap::Parser, Debug)]
#[command(arg_required_else_help = true)]
pub(crate) struct Args {
/// Input TOML configuration file.
#[arg(short, long, value_name = "FILE")]
pub(crate) input_file: PathBuf,
/// The format to convert the environment variables to.
#[arg(
value_enum,
short = 'f',
long,
value_name = "FORMAT",
default_value = "kubernetes-json"
)]
pub(crate) output_format: OutputFormat,
/// Output file. Output will be written to stdout if not specified.
#[arg(short, long, value_name = "FILE")]
pub(crate) output_file: Option<PathBuf>,
/// Prefix to be used for each environment variable in the generated output.
#[arg(short, long, default_value = "ROUTER")]
pub(crate) prefix: String,
}
/// The output format to convert environment variables to.
#[derive(clap::ValueEnum, Clone, Copy, Debug)]
pub(crate) enum OutputFormat {
/// Converts each environment variable to an object containing `name` and `value` fields.
///
/// ```json
/// {
/// "name": "ENVIRONMENT",
/// "value": "PRODUCTION"
/// }
/// ```
KubernetesJson,
}

View File

@ -0,0 +1,98 @@
mod cli;
use std::io::{BufWriter, Write};
use anyhow::Context;
/// The separator used in environment variable names.
const ENV_VAR_SEPARATOR: &str = "__";
#[cfg(not(feature = "preserve_order"))]
type EnvironmentVariableMap = std::collections::HashMap<String, String>;
#[cfg(feature = "preserve_order")]
type EnvironmentVariableMap = indexmap::IndexMap<String, String>;
fn main() -> anyhow::Result<()> {
let args = <cli::Args as clap::Parser>::parse();
// Read input TOML file
let toml_contents =
std::fs::read_to_string(args.input_file).context("Failed to read input file")?;
let table = toml_contents
.parse::<toml::Table>()
.context("Failed to parse TOML file contents")?;
// Parse TOML file contents to a `HashMap` of environment variable name and value pairs
let env_vars = table
.iter()
.flat_map(|(key, value)| process_toml_value(&args.prefix, key, value))
.collect::<EnvironmentVariableMap>();
let writer: BufWriter<Box<dyn Write>> = match args.output_file {
// Write to file if output file is specified
Some(file) => BufWriter::new(Box::new(
std::fs::OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(file)
.context("Failed to open output file")?,
)),
// Write to stdout otherwise
None => BufWriter::new(Box::new(std::io::stdout().lock())),
};
// Write environment variables in specified format
match args.output_format {
cli::OutputFormat::KubernetesJson => {
let k8s_env_vars = env_vars
.into_iter()
.map(|(name, value)| KubernetesEnvironmentVariable { name, value })
.collect::<Vec<_>>();
serde_json::to_writer_pretty(writer, &k8s_env_vars)
.context("Failed to serialize environment variables as JSON")?
}
}
Ok(())
}
fn process_toml_value(
prefix: impl std::fmt::Display + Clone,
key: impl std::fmt::Display + Clone,
value: &toml::Value,
) -> Vec<(String, String)> {
let key_with_prefix = format!("{prefix}{ENV_VAR_SEPARATOR}{key}").to_ascii_uppercase();
match value {
toml::Value::String(s) => vec![(key_with_prefix, s.to_owned())],
toml::Value::Integer(i) => vec![(key_with_prefix, i.to_string())],
toml::Value::Float(f) => vec![(key_with_prefix, f.to_string())],
toml::Value::Boolean(b) => vec![(key_with_prefix, b.to_string())],
toml::Value::Datetime(dt) => vec![(key_with_prefix, dt.to_string())],
toml::Value::Array(values) => {
if values.is_empty() {
return vec![(key_with_prefix, String::new())];
}
// This logic does not support / account for arrays of tables or arrays of arrays.
let (_processed_keys, processed_values) = values
.iter()
.flat_map(|v| process_toml_value(prefix.clone(), key.clone(), v))
.unzip::<_, _, Vec<String>, Vec<String>>();
vec![(key_with_prefix, processed_values.join(","))]
}
toml::Value::Table(map) => map
.into_iter()
.flat_map(|(k, v)| process_toml_value(key_with_prefix.clone(), k, v))
.collect(),
}
}
/// The Kubernetes environment variable structure containing a name and a value.
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub(crate) struct KubernetesEnvironmentVariable {
name: String,
value: String,
}