Rust · 4405 bytes Raw Blame History
1 use anyhow::{Context, Result};
2 use serde::{Deserialize, Serialize};
3 use std::path::PathBuf;
4 use std::fs;
5
6 #[derive(Debug, Clone, Serialize, Deserialize)]
7 pub struct Config {
8 pub node: NodeConfig,
9 pub network: NetworkConfig,
10 pub storage: StorageConfig,
11 pub coordinator: CoordinatorConfig,
12 }
13
14 #[derive(Debug, Clone, Serialize, Deserialize)]
15 pub struct NodeConfig {
16 pub id: Option<String>,
17 pub name: Option<String>,
18 pub data_dir: PathBuf,
19 pub listen_port: u16,
20 }
21
22 #[derive(Debug, Clone, Serialize, Deserialize)]
23 pub struct NetworkConfig {
24 pub bootstrap_peers: Vec<String>,
25 pub max_connections: usize,
26 pub connection_timeout: u64,
27 }
28
29 #[derive(Debug, Clone, Serialize, Deserialize)]
30 pub struct StorageConfig {
31 pub max_storage_gb: u64,
32 pub chunk_size_mb: u32,
33 pub replication_factor: u32,
34 }
35
36 #[derive(Debug, Clone, Serialize, Deserialize)]
37 pub struct CoordinatorConfig {
38 pub endpoint: String,
39 pub timeout: u64,
40 pub retry_attempts: u32,
41 }
42
43 impl Default for Config {
44 fn default() -> Self {
45 Self {
46 node: NodeConfig {
47 id: None,
48 name: None,
49 data_dir: dirs::home_dir()
50 .unwrap_or_else(|| PathBuf::from("."))
51 .join(".zephyrfs"),
52 listen_port: 8080,
53 },
54 network: NetworkConfig {
55 bootstrap_peers: vec![
56 "127.0.0.1:8081".to_string(),
57 "127.0.0.1:8082".to_string(),
58 ],
59 max_connections: 50,
60 connection_timeout: 30,
61 },
62 storage: StorageConfig {
63 max_storage_gb: 10,
64 chunk_size_mb: 1,
65 replication_factor: 3,
66 },
67 coordinator: CoordinatorConfig {
68 endpoint: "http://127.0.0.1:9000".to_string(),
69 timeout: 30,
70 retry_attempts: 3,
71 },
72 }
73 }
74 }
75
76 impl Config {
77 pub fn load(config_path: Option<&str>) -> Result<Self> {
78 let path = match config_path {
79 Some(p) => PathBuf::from(p),
80 None => Self::default_config_path()?,
81 };
82
83 if !path.exists() {
84 tracing::info!("Config file not found, using defaults: {:?}", path);
85 return Ok(Self::default());
86 }
87
88 let content = fs::read_to_string(&path)
89 .with_context(|| format!("Failed to read config file: {:?}", path))?;
90
91 let config: Config = if path.extension().and_then(|s| s.to_str()) == Some("toml") {
92 toml::from_str(&content)
93 .with_context(|| format!("Failed to parse TOML config: {:?}", path))?
94 } else {
95 serde_yaml::from_str(&content)
96 .with_context(|| format!("Failed to parse YAML config: {:?}", path))?
97 };
98
99 tracing::info!("Loaded config from: {:?}", path);
100 Ok(config)
101 }
102
103 pub fn save(&self, config_path: Option<&str>) -> Result<()> {
104 let path = match config_path {
105 Some(p) => PathBuf::from(p),
106 None => Self::default_config_path()?,
107 };
108
109 if let Some(parent) = path.parent() {
110 fs::create_dir_all(parent)
111 .with_context(|| format!("Failed to create config directory: {:?}", parent))?;
112 }
113
114 let content = if path.extension().and_then(|s| s.to_str()) == Some("toml") {
115 toml::to_string_pretty(self)
116 .context("Failed to serialize config to TOML")?
117 } else {
118 serde_yaml::to_string(self)
119 .context("Failed to serialize config to YAML")?
120 };
121
122 fs::write(&path, content)
123 .with_context(|| format!("Failed to write config file: {:?}", path))?;
124
125 tracing::info!("Saved config to: {:?}", path);
126 Ok(())
127 }
128
129 fn default_config_path() -> Result<PathBuf> {
130 let config_dir = dirs::config_dir()
131 .or_else(|| dirs::home_dir().map(|h| h.join(".config")))
132 .context("Could not determine config directory")?;
133
134 Ok(config_dir.join("zephyrfs").join("config.yaml"))
135 }
136
137 pub fn ensure_data_dir(&self) -> Result<()> {
138 fs::create_dir_all(&self.node.data_dir)
139 .with_context(|| format!("Failed to create data directory: {:?}", self.node.data_dir))?;
140 Ok(())
141 }
142 }