use crate::config_clap::error::{ConfigClapError, Result};
use ggen_core::config_lib::GgenConfig;
use std::path::Path;
pub trait LoadConfigFromGgenToml: Sized {
fn from_ggen_toml<P: AsRef<Path>>(path: P) -> Result<Self>;
fn merge_with_cli(self, cli_args: Self) -> Self;
}
pub fn load_ggen_config<P: AsRef<Path>>(path: P) -> Result<GgenConfig> {
let config_str = std::fs::read_to_string(path.as_ref())
.map_err(|e| ConfigClapError::LoadError(format!("Failed to read ggen.toml: {e}")))?;
let config: GgenConfig = toml::from_str(&config_str)
.map_err(|e| ConfigClapError::ParseError(format!("Failed to parse ggen.toml: {e}")))?;
Ok(config)
}
#[must_use]
pub fn expand_env_vars(input: &str) -> String {
let mut result = input.to_string();
let mut last_pos = 0;
while let Some(start) = result[last_pos..].find("${") {
let start = last_pos + start;
if let Some(end) = result[start..].find('}') {
let var_name = &result[start + 2..start + end];
if let Ok(value) = std::env::var(var_name) {
result.replace_range(start..start + end + 1, &value);
last_pos = start + value.len();
} else {
last_pos = start + end + 1;
}
} else {
break;
}
}
let mut chars: Vec<char> = result.chars().collect();
let mut i = 0;
while i < chars.len() {
if chars[i] == '$' && i + 1 < chars.len() && chars[i + 1].is_alphabetic() {
let start = i;
i += 1;
while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
i += 1;
}
let var_name: String = chars[start + 1..i].iter().collect();
if let Ok(value) = std::env::var(&var_name) {
result.replace_range(start..start + var_name.len() + 1, &value);
chars = result.chars().collect();
i = start + value.len();
} else {
i = start + var_name.len() + 1;
}
} else {
i += 1;
}
}
result
}
#[cfg(test)]
mod tests {
use super::*;
use serial_test::serial;
use std::env;
struct EnvVarGuard {
key: &'static str,
previous: Option<std::ffi::OsString>,
}
impl EnvVarGuard {
fn set(key: &'static str, value: &str) -> Self {
let previous = env::var_os(key);
env::set_var(key, value);
Self { key, previous }
}
}
impl Drop for EnvVarGuard {
fn drop(&mut self) {
match &self.previous {
None => env::remove_var(self.key),
Some(v) => env::set_var(self.key, v),
}
}
}
#[test]
#[serial(TEST_VAR)]
fn test_expand_env_vars_dollar_brace() {
let _guard = EnvVarGuard::set("TEST_VAR", "test_value");
let input = "Path: ${TEST_VAR}/output";
let result = expand_env_vars(input);
assert_eq!(result, "Path: test_value/output");
}
#[test]
#[serial(USER)]
fn test_expand_env_vars_dollar() {
let _guard = EnvVarGuard::set("USER", "testuser");
let input = "Home: /home/$USER";
let result = expand_env_vars(input);
assert_eq!(result, "Home: /home/testuser");
}
#[test]
fn test_expand_env_vars_missing() {
let input = "Path: ${NONEXISTENT_VAR}/output";
let result = expand_env_vars(input);
assert!(result.contains("${NONEXISTENT_VAR}") || result == input);
}
#[test]
fn test_load_missing_config() {
let result = load_ggen_config("/nonexistent/ggen.toml");
assert!(result.is_err());
assert!(matches!(result, Err(ConfigClapError::LoadError(_))));
}
}