use anyhow::{Context, Result};
use serde_yaml;
use std::path::Path;
use std::collections::HashMap;
use crate::models::RemoteExecutionConfig;
use crate::vars::VariableManager;
pub struct ConfigManager;
impl ConfigManager {
pub fn from_yaml_file_raw<P: AsRef<Path>>(path: P) -> Result<RemoteExecutionConfig> {
let content = std::fs::read_to_string(path)
.context("Failed to read YAML configuration file")?;
Self::from_yaml_str_raw(&content)
}
pub fn from_yaml_str_raw(yaml_content: &str) -> Result<RemoteExecutionConfig> {
let config: RemoteExecutionConfig = serde_yaml::from_str(yaml_content)
.context("Failed to parse YAML configuration")?;
Ok(config)
}
pub fn from_yaml_file_with_variables<P: AsRef<Path>>(path: P, variable_manager: &VariableManager) -> Result<RemoteExecutionConfig> {
let content = std::fs::read_to_string(path)
.context("Failed to read YAML configuration file")?;
Self::from_yaml_str_with_variables(&content, variable_manager)
}
pub fn from_yaml_str_with_variables(yaml_content: &str, variable_manager: &VariableManager) -> Result<RemoteExecutionConfig> {
let replaced_content = variable_manager.replace_variables(yaml_content);
let config: RemoteExecutionConfig = serde_yaml::from_str(&replaced_content)
.context("Failed to parse YAML configuration after variable replacement")?;
Ok(config)
}
pub fn extract_initial_variables(yaml_content: &str) -> Result<Option<HashMap<String, String>>> {
let yaml_value: serde_yaml::Value = serde_yaml::from_str(yaml_content)
.context("Failed to parse YAML for variable extraction")?;
let initial_variables = if let Some(vars) = yaml_value.get("variables") {
if let Ok(vars_map) = serde_yaml::from_value::<HashMap<String, String>>(vars.clone()) {
Some(vars_map)
} else {
None
}
} else {
None
};
Ok(initial_variables)
}
pub fn from_yaml_file<P: AsRef<Path>>(path: P) -> Result<RemoteExecutionConfig> {
let content = std::fs::read_to_string(path)
.context("Failed to read YAML configuration file")?;
Self::from_yaml_str(&content)
}
pub fn from_yaml_str(yaml_content: &str) -> Result<RemoteExecutionConfig> {
let initial_variables = Self::extract_initial_variables(yaml_content)?;
let variable_manager = VariableManager::new(initial_variables);
Self::from_yaml_str_with_variables(yaml_content, &variable_manager)
}
pub fn validate_config(config: &RemoteExecutionConfig) -> Result<()> {
if config.clients.is_empty() {
return Err(anyhow::anyhow!("No clients configured"));
}
if config.pipelines.is_empty() {
return Err(anyhow::anyhow!("No pipelines configured"));
}
for pipeline in &config.pipelines {
if pipeline.steps.is_empty() {
return Err(anyhow::anyhow!("Pipeline '{}' has no steps", pipeline.name));
}
for step in &pipeline.steps {
if !step.servers.is_empty() {
for server in &step.servers {
if !config.clients.contains_key(server) {
return Err(anyhow::anyhow!("Server '{}' referenced in step '{}' not found in clients",
server, step.name));
}
}
}
}
}
Ok(())
}
}