1use serde::{Deserialize, Serialize};
8
9fn default_max_processes() -> u32 {
11 64
12}
13
14fn default_health_check_interval_secs() -> u64 {
16 30
17}
18
19fn default_enabled() -> bool {
21 true
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct ClusterNetworkConfig {
31 #[serde(default = "default_replication_factor", alias = "replicationFactor")]
33 pub replication_factor: usize,
34
35 #[serde(default = "default_shard_count", alias = "shardCount")]
37 pub shard_count: u32,
38
39 #[serde(
41 default = "default_cluster_heartbeat",
42 alias = "heartbeatIntervalSecs"
43 )]
44 pub heartbeat_interval_secs: u64,
45
46 #[serde(default = "default_node_timeout", alias = "nodeTimeoutSecs")]
48 pub node_timeout_secs: u64,
49
50 #[serde(default = "default_enable_consensus", alias = "enableConsensus")]
52 pub enable_consensus: bool,
53
54 #[serde(default = "default_min_quorum", alias = "minQuorumSize")]
56 pub min_quorum_size: usize,
57
58 #[serde(default, alias = "seedNodes")]
60 pub seed_nodes: Vec<String>,
61
62 #[serde(default, alias = "nodeName")]
64 pub node_name: Option<String>,
65}
66
67fn default_replication_factor() -> usize {
68 3
69}
70fn default_shard_count() -> u32 {
71 64
72}
73fn default_cluster_heartbeat() -> u64 {
74 5
75}
76fn default_node_timeout() -> u64 {
77 30
78}
79fn default_enable_consensus() -> bool {
80 true
81}
82fn default_min_quorum() -> usize {
83 2
84}
85
86impl Default for ClusterNetworkConfig {
87 fn default() -> Self {
88 Self {
89 replication_factor: default_replication_factor(),
90 shard_count: default_shard_count(),
91 heartbeat_interval_secs: default_cluster_heartbeat(),
92 node_timeout_secs: default_node_timeout(),
93 enable_consensus: default_enable_consensus(),
94 min_quorum_size: default_min_quorum(),
95 seed_nodes: Vec::new(),
96 node_name: None,
97 }
98 }
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct KernelConfig {
120 #[serde(default = "default_enabled")]
125 pub enabled: bool,
126
127 #[serde(default = "default_max_processes", alias = "maxProcesses")]
129 pub max_processes: u32,
130
131 #[serde(
133 default = "default_health_check_interval_secs",
134 alias = "healthCheckIntervalSecs"
135 )]
136 pub health_check_interval_secs: u64,
137
138 #[serde(default, skip_serializing_if = "Option::is_none")]
140 pub cluster: Option<ClusterNetworkConfig>,
141
142 #[serde(default, skip_serializing_if = "Option::is_none")]
144 pub chain: Option<ChainConfig>,
145
146 #[serde(default, skip_serializing_if = "Option::is_none", alias = "resourceTree")]
148 pub resource_tree: Option<ResourceTreeConfig>,
149}
150
151impl Default for KernelConfig {
152 fn default() -> Self {
153 Self {
154 enabled: true,
155 max_processes: default_max_processes(),
156 health_check_interval_secs: default_health_check_interval_secs(),
157 cluster: None,
158 chain: None,
159 resource_tree: None,
160 }
161 }
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct ChainConfig {
167 #[serde(default = "default_true")]
169 pub enabled: bool,
170
171 #[serde(default = "default_checkpoint_interval", alias = "checkpointInterval")]
173 pub checkpoint_interval: u64,
174
175 #[serde(default)]
177 pub chain_id: u32,
178
179 #[serde(
182 default,
183 skip_serializing_if = "Option::is_none",
184 alias = "checkpointPath"
185 )]
186 pub checkpoint_path: Option<String>,
187}
188
189fn default_true() -> bool {
190 true
191}
192fn default_checkpoint_interval() -> u64 {
193 1000
194}
195
196impl Default for ChainConfig {
197 fn default() -> Self {
198 Self {
199 enabled: true,
200 checkpoint_interval: default_checkpoint_interval(),
201 chain_id: 0,
202 checkpoint_path: None,
203 }
204 }
205}
206
207impl ChainConfig {
208 pub fn effective_checkpoint_path(&self) -> Option<String> {
213 if self.checkpoint_path.is_some() {
214 return self.checkpoint_path.clone();
215 }
216 #[cfg(feature = "native")]
217 {
218 dirs::home_dir().map(|h| {
219 h.join(".clawft")
220 .join("chain.json")
221 .to_string_lossy()
222 .into_owned()
223 })
224 }
225 #[cfg(not(feature = "native"))]
226 {
227 None
228 }
229 }
230}
231
232#[derive(Debug, Clone, Serialize, Deserialize)]
234pub struct ResourceTreeConfig {
235 #[serde(default = "default_true_rt")]
237 pub enabled: bool,
238
239 #[serde(
241 default,
242 skip_serializing_if = "Option::is_none",
243 alias = "checkpointPath"
244 )]
245 pub checkpoint_path: Option<String>,
246}
247
248fn default_true_rt() -> bool {
249 true
250}
251
252impl Default for ResourceTreeConfig {
253 fn default() -> Self {
254 Self {
255 enabled: true,
256 checkpoint_path: None,
257 }
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264
265 #[test]
266 fn default_kernel_config() {
267 let cfg = KernelConfig::default();
268 assert!(!cfg.enabled);
269 assert_eq!(cfg.max_processes, 64);
270 assert_eq!(cfg.health_check_interval_secs, 30);
271 }
272
273 #[test]
274 fn deserialize_empty() {
275 let cfg: KernelConfig = serde_json::from_str("{}").unwrap();
276 assert!(!cfg.enabled);
277 assert_eq!(cfg.max_processes, 64);
278 }
279
280 #[test]
281 fn deserialize_camel_case() {
282 let json = r#"{"maxProcesses": 128, "healthCheckIntervalSecs": 15}"#;
283 let cfg: KernelConfig = serde_json::from_str(json).unwrap();
284 assert_eq!(cfg.max_processes, 128);
285 assert_eq!(cfg.health_check_interval_secs, 15);
286 }
287
288 #[test]
289 fn serde_roundtrip() {
290 let cfg = KernelConfig {
291 enabled: true,
292 max_processes: 256,
293 health_check_interval_secs: 10,
294 cluster: None,
295 chain: None,
296 resource_tree: None,
297 };
298 let json = serde_json::to_string(&cfg).unwrap();
299 let restored: KernelConfig = serde_json::from_str(&json).unwrap();
300 assert_eq!(restored.enabled, cfg.enabled);
301 assert_eq!(restored.max_processes, cfg.max_processes);
302 }
303}