use anyhow::{Context, Result};
use libp2p::Multiaddr;
use serde::{Deserialize, Serialize};
use std::path::Path;
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct FireCloudConfig {
#[serde(default)]
pub network: NetworkConfig,
#[serde(default)]
pub bootstrap: BootstrapConfig,
#[serde(default)]
pub storage_quota: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkConfig {
#[serde(default)]
pub port: u16,
#[serde(default = "default_true")]
pub enable_mdns: bool,
#[serde(default = "default_connection_timeout")]
pub connection_timeout_secs: u64,
}
impl Default for NetworkConfig {
fn default() -> Self {
Self {
port: 0,
enable_mdns: true,
connection_timeout_secs: 60,
}
}
}
fn default_true() -> bool {
true
}
fn default_connection_timeout() -> u64 {
60
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct BootstrapConfig {
#[serde(default)]
pub peers: Vec<String>,
}
impl FireCloudConfig {
pub fn load(config_path: &Path) -> Result<Self> {
if config_path.exists() {
let content = std::fs::read_to_string(config_path)
.with_context(|| format!("Failed to read config file: {}", config_path.display()))?;
let config: FireCloudConfig = toml::from_str(&content)
.with_context(|| format!("Failed to parse config file: {}", config_path.display()))?;
Ok(config)
} else {
Ok(Self::default())
}
}
pub fn save(&self, config_path: &Path) -> Result<()> {
let content = toml::to_string_pretty(self)
.context("Failed to serialize config")?;
std::fs::write(config_path, content)
.with_context(|| format!("Failed to write config file: {}", config_path.display()))?;
Ok(())
}
pub fn create_default_if_missing(config_path: &Path) -> Result<()> {
if !config_path.exists() {
let config = Self::default();
config.save(config_path)?;
}
Ok(())
}
pub fn parse_bootstrap_peers(&self) -> Vec<Multiaddr> {
self.bootstrap
.peers
.iter()
.filter_map(|s| {
match s.parse::<Multiaddr>() {
Ok(addr) => Some(addr),
Err(e) => {
tracing::warn!("Invalid bootstrap peer address '{}': {}", s, e);
None
}
}
})
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = FireCloudConfig::default();
assert_eq!(config.network.port, 0);
assert!(config.network.enable_mdns);
assert!(config.bootstrap.peers.is_empty());
}
#[test]
fn test_parse_config() {
let toml_str = r#"
[network]
port = 4001
enable_mdns = true
[bootstrap]
peers = [
"/ip4/127.0.0.1/udp/4001/quic-v1/p2p/12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN",
]
"#;
let config: FireCloudConfig = toml::from_str(toml_str).unwrap();
assert_eq!(config.network.port, 4001);
assert!(config.network.enable_mdns);
assert_eq!(config.bootstrap.peers.len(), 1);
}
#[test]
fn test_parse_bootstrap_peers() {
let toml_str = r#"
[bootstrap]
peers = [
"/ip4/127.0.0.1/udp/4001/quic-v1/p2p/12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN",
"invalid-address",
]
"#;
let config: FireCloudConfig = toml::from_str(toml_str).unwrap();
let addrs = config.parse_bootstrap_peers();
assert_eq!(addrs.len(), 1);
}
}