use crate::error::{CliError, Result}; use serde::{Deserialize, Serialize}; use std::fs; use std::path::PathBuf; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Config { pub network: NetworkConfig, pub account: AccountConfig, pub transaction: TransactionConfig, pub logging: LoggingConfig, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct NetworkConfig { pub rpc_url: String, pub chain_id: u64, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AccountConfig { pub default_account: Option, pub keystore_dir: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TransactionConfig { pub gas_limit: u64, pub gas_price: u64, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct LoggingConfig { pub level: String, } impl Default for Config { fn default() -> Self { Self { network: NetworkConfig { rpc_url: "http://localhost:8545".to_string(), chain_id: 1, }, account: AccountConfig { default_account: None, keystore_dir: Self::default_keystore_dir() .to_str() .expect("Failed to convert path to string") .to_string(), }, transaction: TransactionConfig { gas_limit: 21000, gas_price: 1000000000, }, logging: LoggingConfig { level: "info".to_string(), }, } } } impl Config { /// 获取配置文件路径 pub fn config_path() -> PathBuf { let home = dirs::home_dir().expect("无法获取用户主目录"); home.join(".nac").join("config.toml") } /// 获取默认keystore目录 pub fn default_keystore_dir() -> PathBuf { let home = dirs::home_dir().expect("无法获取用户主目录"); home.join(".nac").join("keystore") } /// 加载配置 pub fn load() -> Result { let path = Self::config_path(); if !path.exists() { return Ok(Self::default()); } let content = fs::read_to_string(&path) .map_err(|e| CliError::Config(format!("读取配置文件失败: {}", e)))?; toml::from_str(&content) .map_err(|e| CliError::Config(format!("解析配置文件失败: {}", e))) } /// 保存配置 pub fn save(&self) -> Result<()> { let path = Self::config_path(); // 创建目录 if let Some(parent) = path.parent() { fs::create_dir_all(parent) .map_err(|e| CliError::Config(format!("创建配置目录失败: {}", e)))?; } // 序列化配置 let content = toml::to_string_pretty(self) .map_err(|e| CliError::Config(format!("序列化配置失败: {}", e)))?; // 写入文件 fs::write(&path, content) .map_err(|e| CliError::Config(format!("写入配置文件失败: {}", e)))?; Ok(()) } /// 获取配置项 pub fn get(&self, key: &str) -> Option { match key { "network.rpc_url" => Some(self.network.rpc_url.clone()), "network.chain_id" => Some(self.network.chain_id.to_string()), "account.default_account" => self.account.default_account.clone(), "account.keystore_dir" => Some(self.account.keystore_dir.clone()), "transaction.gas_limit" => Some(self.transaction.gas_limit.to_string()), "transaction.gas_price" => Some(self.transaction.gas_price.to_string()), "logging.level" => Some(self.logging.level.clone()), _ => None, } } /// 设置配置项 pub fn set(&mut self, key: &str, value: &str) -> Result<()> { match key { "network.rpc_url" => self.network.rpc_url = value.to_string(), "network.chain_id" => { self.network.chain_id = value .parse() .map_err(|_| CliError::Config("无效的chain_id".to_string()))?; } "account.default_account" => self.account.default_account = Some(value.to_string()), "account.keystore_dir" => self.account.keystore_dir = value.to_string(), "transaction.gas_limit" => { self.transaction.gas_limit = value .parse() .map_err(|_| CliError::Config("无效的gas_limit".to_string()))?; } "transaction.gas_price" => { self.transaction.gas_price = value .parse() .map_err(|_| CliError::Config("无效的gas_price".to_string()))?; } "logging.level" => self.logging.level = value.to_string(), _ => return Err(CliError::Config(format!("未知的配置项: {}", key))), } Ok(()) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_default_config() { let config = Config::default(); assert_eq!(config.network.rpc_url, "http://localhost:8545"); assert_eq!(config.network.chain_id, 1); } #[test] fn test_get_set_config() { let mut config = Config::default(); config .set("network.rpc_url", "http://example.com") .expect("Failed to set config"); assert_eq!( config.get("network.rpc_url"), Some("http://example.com".to_string()) ); } }