net_shell/config/
mod.rs

1use anyhow::{Context, Result};
2use serde_yaml;
3use std::path::Path;
4use std::collections::HashMap;
5
6use crate::models::RemoteExecutionConfig;
7use crate::vars::VariableManager;
8
9/// 配置管理器
10pub struct ConfigManager;
11
12impl ConfigManager {
13    /// 从YAML文件加载配置(不处理变量替换)
14    pub fn from_yaml_file_raw<P: AsRef<Path>>(path: P) -> Result<RemoteExecutionConfig> {
15        let content = std::fs::read_to_string(path)
16            .context("Failed to read YAML configuration file")?;
17        
18        Self::from_yaml_str_raw(&content)
19    }
20
21    /// 从YAML字符串加载配置(不处理变量替换)
22    pub fn from_yaml_str_raw(yaml_content: &str) -> Result<RemoteExecutionConfig> {
23        let config: RemoteExecutionConfig = serde_yaml::from_str(yaml_content)
24            .context("Failed to parse YAML configuration")?;
25        
26        Ok(config)
27    }
28
29    /// 从YAML文件加载配置并应用变量替换
30    pub fn from_yaml_file_with_variables<P: AsRef<Path>>(path: P, variable_manager: &VariableManager) -> Result<RemoteExecutionConfig> {
31        let content = std::fs::read_to_string(path)
32            .context("Failed to read YAML configuration file")?;
33        
34        Self::from_yaml_str_with_variables(&content, variable_manager)
35    }
36
37    /// 从YAML字符串加载配置并应用变量替换
38    pub fn from_yaml_str_with_variables(yaml_content: &str, variable_manager: &VariableManager) -> Result<RemoteExecutionConfig> {
39        // 对整个YAML内容进行变量替换(当作字符串处理)
40        let replaced_content = variable_manager.replace_variables(yaml_content);
41        
42        // 解析替换后的内容为最终配置
43        let config: RemoteExecutionConfig = serde_yaml::from_str(&replaced_content)
44            .context("Failed to parse YAML configuration after variable replacement")?;
45        
46        Ok(config)
47    }
48
49    /// 提取YAML中的初始变量
50    pub fn extract_initial_variables(yaml_content: &str) -> Result<Option<HashMap<String, String>>> {
51        let yaml_value: serde_yaml::Value = serde_yaml::from_str(yaml_content)
52            .context("Failed to parse YAML for variable extraction")?;
53        
54        let initial_variables = if let Some(vars) = yaml_value.get("variables") {
55            if let Ok(vars_map) = serde_yaml::from_value::<HashMap<String, String>>(vars.clone()) {
56                Some(vars_map)
57            } else {
58                None
59            }
60        } else {
61            None
62        };
63        
64        Ok(initial_variables)
65    }
66
67    /// 从YAML文件加载配置(保持向后兼容)
68    pub fn from_yaml_file<P: AsRef<Path>>(path: P) -> Result<RemoteExecutionConfig> {
69        let content = std::fs::read_to_string(path)
70            .context("Failed to read YAML configuration file")?;
71        
72        Self::from_yaml_str(&content)
73    }
74
75    /// 从YAML字符串加载配置(保持向后兼容)
76    pub fn from_yaml_str(yaml_content: &str) -> Result<RemoteExecutionConfig> {
77        // 提取初始变量
78        let initial_variables = Self::extract_initial_variables(yaml_content)?;
79        
80        // 创建变量管理器
81        let variable_manager = VariableManager::new(initial_variables);
82        
83        // 应用变量替换
84        Self::from_yaml_str_with_variables(yaml_content, &variable_manager)
85    }
86
87    /// 验证配置的有效性
88    pub fn validate_config(config: &RemoteExecutionConfig) -> Result<()> {
89        // 检查是否有客户端配置
90        if config.clients.is_empty() {
91            return Err(anyhow::anyhow!("No clients configured"));
92        }
93
94        // 检查是否有流水线配置
95        if config.pipelines.is_empty() {
96            return Err(anyhow::anyhow!("No pipelines configured"));
97        }
98
99        // 检查每个流水线的步骤
100        for pipeline in &config.pipelines {
101            if pipeline.steps.is_empty() {
102                return Err(anyhow::anyhow!("Pipeline '{}' has no steps", pipeline.name));
103            }
104
105            for step in &pipeline.steps {
106                // 允许空服务器列表用于本地执行
107                if !step.servers.is_empty() {
108                    // 检查步骤中引用的服务器是否存在
109                    for server in &step.servers {
110                        if !config.clients.contains_key(server) {
111                            return Err(anyhow::anyhow!("Server '{}' referenced in step '{}' not found in clients", 
112                                                      server, step.name));
113                        }
114                    }
115                }
116            }
117        }
118
119        Ok(())
120    }
121}