use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::RwLock;
use tracing::info;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeConfig {
pub data_dir: PathBuf,
pub network_port: u16,
pub max_peers: usize,
pub initial_peers: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExtendedNodeConfig {
pub data_dir: PathBuf,
pub port: u16,
pub peers: Vec<String>,
pub log_level: String,
pub identity: IdentityConfig,
pub network: NetworkConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IdentityConfig {
pub node_id: Option<String>,
pub key_file: Option<PathBuf>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkConfig {
pub listen_addr: String,
pub external_addr: Option<String>,
pub max_peers: usize,
pub bootstrap_nodes: Vec<String>,
}
impl Default for NodeConfig {
fn default() -> Self {
Self {
data_dir: PathBuf::from("./data"),
network_port: 8000,
max_peers: 50,
initial_peers: Vec::new(),
}
}
}
impl Default for ExtendedNodeConfig {
fn default() -> Self {
Self {
data_dir: PathBuf::from("./data"),
port: 8000,
peers: Vec::new(),
log_level: "info".to_string(),
identity: IdentityConfig {
node_id: None,
key_file: None,
},
network: NetworkConfig {
listen_addr: "0.0.0.0".to_string(),
external_addr: None,
max_peers: 50,
bootstrap_nodes: Vec::new(),
},
}
}
}
impl NodeConfig {
pub fn load(path: PathBuf) -> Result<Self> {
let config = std::fs::read_to_string(path)?;
Ok(toml::from_str(&config)?)
}
pub fn save(&self, path: PathBuf) -> Result<()> {
let config = toml::to_string_pretty(self)?;
std::fs::write(path, config)?;
Ok(())
}
}
pub struct NodeConfigManager {
config_path: PathBuf,
config: Arc<RwLock<NodeConfig>>,
}
impl NodeConfigManager {
pub fn new(config_path: PathBuf) -> Result<Self> {
if let Some(parent) = config_path.parent() {
fs::create_dir_all(parent)?;
}
let config = if config_path.exists() {
NodeConfig::load(config_path.clone())?
} else {
let default = NodeConfig::default();
default.save(config_path.clone())?;
default
};
Ok(Self {
config_path,
config: Arc::new(RwLock::new(config)),
})
}
pub async fn load_config(&self) -> Result<NodeConfig> {
Ok(self.config.read().await.clone())
}
pub async fn update_config<F>(&self, updater: F) -> Result<()>
where
F: FnOnce(&mut NodeConfig) -> Result<()>,
{
let mut config = self.config.write().await;
updater(&mut config)?;
config.save(self.config_path.clone())?;
info!("Configuration updated and saved");
Ok(())
}
pub async fn reload_config(&self) -> Result<()> {
let new_config = NodeConfig::load(self.config_path.clone())?;
*self.config.write().await = new_config;
info!("Configuration reloaded from disk");
Ok(())
}
pub fn config_path(&self) -> &PathBuf {
&self.config_path
}
}