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#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct NodeConfig {
12 pub data_dir: PathBuf,
14 pub network_port: u16,
16 pub max_peers: usize,
18 pub initial_peers: Vec<String>,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct ExtendedNodeConfig {
25 pub data_dir: PathBuf,
27 pub port: u16,
29 pub peers: Vec<String>,
31 pub log_level: String,
33 pub identity: IdentityConfig,
35 pub network: NetworkConfig,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct IdentityConfig {
42 pub node_id: Option<String>,
44 pub key_file: Option<PathBuf>,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct NetworkConfig {
51 pub listen_addr: String,
53 pub external_addr: Option<String>,
55 pub max_peers: usize,
57 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 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 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
108pub struct NodeConfigManager {
110 config_path: PathBuf,
111 config: Arc<RwLock<NodeConfig>>,
112}
113
114impl NodeConfigManager {
115 pub fn new(config_path: PathBuf) -> Result<Self> {
117 if let Some(parent) = config_path.parent() {
119 fs::create_dir_all(parent)?;
120 }
121
122 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 pub async fn load_config(&self) -> Result<NodeConfig> {
139 Ok(self.config.read().await.clone())
140 }
141
142 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 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 pub fn config_path(&self) -> &PathBuf {
164 &self.config_path
165 }
166}