qudag_cli/
config.rs

1use anyhow::Result;
2use serde::{Deserialize, Serialize};
3use std::fs;
4use std::path::PathBuf;
5use std::sync::Arc;
6use tokio::sync::RwLock;
7use tracing::info;
8
9/// Node configuration (compatibility with protocol module)
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct NodeConfig {
12    /// Data directory
13    pub data_dir: PathBuf,
14    /// Network port
15    pub network_port: u16,
16    /// Maximum peers
17    pub max_peers: usize,
18    /// Initial peers
19    pub initial_peers: Vec<String>,
20}
21
22/// Extended node configuration for CLI
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct ExtendedNodeConfig {
25    /// Data directory
26    pub data_dir: PathBuf,
27    /// Network port
28    pub port: u16,
29    /// Initial peers
30    pub peers: Vec<String>,
31    /// Log level
32    pub log_level: String,
33    /// Node identity
34    pub identity: IdentityConfig,
35    /// Network configuration
36    pub network: NetworkConfig,
37}
38
39/// Node identity configuration
40#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct IdentityConfig {
42    /// Node ID
43    pub node_id: Option<String>,
44    /// Private key file
45    pub key_file: Option<PathBuf>,
46}
47
48/// Network configuration
49#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct NetworkConfig {
51    /// Listen address
52    pub listen_addr: String,
53    /// External address
54    pub external_addr: Option<String>,
55    /// Maximum peers
56    pub max_peers: usize,
57    /// Bootstrap nodes
58    pub bootstrap_nodes: Vec<String>,
59}
60
61impl Default for NodeConfig {
62    fn default() -> Self {
63        Self {
64            data_dir: PathBuf::from("./data"),
65            network_port: 8000,
66            max_peers: 50,
67            initial_peers: Vec::new(),
68        }
69    }
70}
71
72impl Default for ExtendedNodeConfig {
73    fn default() -> Self {
74        Self {
75            data_dir: PathBuf::from("./data"),
76            port: 8000,
77            peers: Vec::new(),
78            log_level: "info".to_string(),
79            identity: IdentityConfig {
80                node_id: None,
81                key_file: None,
82            },
83            network: NetworkConfig {
84                listen_addr: "0.0.0.0".to_string(),
85                external_addr: None,
86                max_peers: 50,
87                bootstrap_nodes: Vec::new(),
88            },
89        }
90    }
91}
92
93impl NodeConfig {
94    /// Load configuration from file
95    pub fn load(path: PathBuf) -> Result<Self> {
96        let config = std::fs::read_to_string(path)?;
97        Ok(toml::from_str(&config)?)
98    }
99
100    /// Save configuration to file
101    pub fn save(&self, path: PathBuf) -> Result<()> {
102        let config = toml::to_string_pretty(self)?;
103        std::fs::write(path, config)?;
104        Ok(())
105    }
106}
107
108/// Node configuration manager for handling config updates
109pub struct NodeConfigManager {
110    config_path: PathBuf,
111    config: Arc<RwLock<NodeConfig>>,
112}
113
114impl NodeConfigManager {
115    /// Create a new configuration manager
116    pub fn new(config_path: PathBuf) -> Result<Self> {
117        // Create parent directory if needed
118        if let Some(parent) = config_path.parent() {
119            fs::create_dir_all(parent)?;
120        }
121
122        // Load or create default config
123        let config = if config_path.exists() {
124            NodeConfig::load(config_path.clone())?
125        } else {
126            let default = NodeConfig::default();
127            default.save(config_path.clone())?;
128            default
129        };
130
131        Ok(Self {
132            config_path,
133            config: Arc::new(RwLock::new(config)),
134        })
135    }
136
137    /// Load current configuration
138    pub async fn load_config(&self) -> Result<NodeConfig> {
139        Ok(self.config.read().await.clone())
140    }
141
142    /// Update configuration with a closure
143    pub async fn update_config<F>(&self, updater: F) -> Result<()>
144    where
145        F: FnOnce(&mut NodeConfig) -> Result<()>,
146    {
147        let mut config = self.config.write().await;
148        updater(&mut config)?;
149        config.save(self.config_path.clone())?;
150        info!("Configuration updated and saved");
151        Ok(())
152    }
153
154    /// Reload configuration from disk
155    pub async fn reload_config(&self) -> Result<()> {
156        let new_config = NodeConfig::load(self.config_path.clone())?;
157        *self.config.write().await = new_config;
158        info!("Configuration reloaded from disk");
159        Ok(())
160    }
161
162    /// Get configuration path
163    pub fn config_path(&self) -> &PathBuf {
164        &self.config_path
165    }
166}