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
19#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct ClusterNetworkConfig {
26 #[serde(default = "default_replication_factor", alias = "replicationFactor")]
28 pub replication_factor: usize,
29
30 #[serde(default = "default_shard_count", alias = "shardCount")]
32 pub shard_count: u32,
33
34 #[serde(
36 default = "default_cluster_heartbeat",
37 alias = "heartbeatIntervalSecs"
38 )]
39 pub heartbeat_interval_secs: u64,
40
41 #[serde(default = "default_node_timeout", alias = "nodeTimeoutSecs")]
43 pub node_timeout_secs: u64,
44
45 #[serde(default = "default_enable_consensus", alias = "enableConsensus")]
47 pub enable_consensus: bool,
48
49 #[serde(default = "default_min_quorum", alias = "minQuorumSize")]
51 pub min_quorum_size: usize,
52
53 #[serde(default, alias = "seedNodes")]
55 pub seed_nodes: Vec<String>,
56
57 #[serde(default, alias = "nodeName")]
59 pub node_name: Option<String>,
60}
61
62fn default_replication_factor() -> usize {
63 3
64}
65fn default_shard_count() -> u32 {
66 64
67}
68fn default_cluster_heartbeat() -> u64 {
69 5
70}
71fn default_node_timeout() -> u64 {
72 30
73}
74fn default_enable_consensus() -> bool {
75 true
76}
77fn default_min_quorum() -> usize {
78 2
79}
80
81impl Default for ClusterNetworkConfig {
82 fn default() -> Self {
83 Self {
84 replication_factor: default_replication_factor(),
85 shard_count: default_shard_count(),
86 heartbeat_interval_secs: default_cluster_heartbeat(),
87 node_timeout_secs: default_node_timeout(),
88 enable_consensus: default_enable_consensus(),
89 min_quorum_size: default_min_quorum(),
90 seed_nodes: Vec::new(),
91 node_name: None,
92 }
93 }
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize)]
114pub struct KernelConfig {
115 #[serde(default)]
120 pub enabled: bool,
121
122 #[serde(default = "default_max_processes", alias = "maxProcesses")]
124 pub max_processes: u32,
125
126 #[serde(
128 default = "default_health_check_interval_secs",
129 alias = "healthCheckIntervalSecs"
130 )]
131 pub health_check_interval_secs: u64,
132
133 #[serde(default, skip_serializing_if = "Option::is_none")]
135 pub cluster: Option<ClusterNetworkConfig>,
136
137 #[serde(default, skip_serializing_if = "Option::is_none")]
139 pub chain: Option<ChainConfig>,
140
141 #[serde(default, skip_serializing_if = "Option::is_none", alias = "resourceTree")]
143 pub resource_tree: Option<ResourceTreeConfig>,
144}
145
146impl Default for KernelConfig {
147 fn default() -> Self {
148 Self {
149 enabled: false,
150 max_processes: default_max_processes(),
151 health_check_interval_secs: default_health_check_interval_secs(),
152 cluster: None,
153 chain: None,
154 resource_tree: None,
155 }
156 }
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize)]
161pub struct ChainConfig {
162 #[serde(default = "default_true")]
164 pub enabled: bool,
165
166 #[serde(default = "default_checkpoint_interval", alias = "checkpointInterval")]
168 pub checkpoint_interval: u64,
169
170 #[serde(default)]
172 pub chain_id: u32,
173
174 #[serde(
177 default,
178 skip_serializing_if = "Option::is_none",
179 alias = "checkpointPath"
180 )]
181 pub checkpoint_path: Option<String>,
182}
183
184fn default_true() -> bool {
185 true
186}
187fn default_checkpoint_interval() -> u64 {
188 1000
189}
190
191impl Default for ChainConfig {
192 fn default() -> Self {
193 Self {
194 enabled: true,
195 checkpoint_interval: default_checkpoint_interval(),
196 chain_id: 0,
197 checkpoint_path: None,
198 }
199 }
200}
201
202impl ChainConfig {
203 pub fn effective_checkpoint_path(&self) -> Option<String> {
208 if self.checkpoint_path.is_some() {
209 return self.checkpoint_path.clone();
210 }
211 #[cfg(feature = "native")]
212 {
213 dirs::home_dir().map(|h| {
214 h.join(".clawft")
215 .join("chain.json")
216 .to_string_lossy()
217 .into_owned()
218 })
219 }
220 #[cfg(not(feature = "native"))]
221 {
222 None
223 }
224 }
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize)]
229pub struct ResourceTreeConfig {
230 #[serde(default = "default_true_rt")]
232 pub enabled: bool,
233
234 #[serde(
236 default,
237 skip_serializing_if = "Option::is_none",
238 alias = "checkpointPath"
239 )]
240 pub checkpoint_path: Option<String>,
241}
242
243fn default_true_rt() -> bool {
244 true
245}
246
247impl Default for ResourceTreeConfig {
248 fn default() -> Self {
249 Self {
250 enabled: true,
251 checkpoint_path: None,
252 }
253 }
254}
255
256#[cfg(test)]
257mod tests {
258 use super::*;
259
260 #[test]
261 fn default_kernel_config() {
262 let cfg = KernelConfig::default();
263 assert!(!cfg.enabled);
264 assert_eq!(cfg.max_processes, 64);
265 assert_eq!(cfg.health_check_interval_secs, 30);
266 }
267
268 #[test]
269 fn deserialize_empty() {
270 let cfg: KernelConfig = serde_json::from_str("{}").unwrap();
271 assert!(!cfg.enabled);
272 assert_eq!(cfg.max_processes, 64);
273 }
274
275 #[test]
276 fn deserialize_camel_case() {
277 let json = r#"{"maxProcesses": 128, "healthCheckIntervalSecs": 15}"#;
278 let cfg: KernelConfig = serde_json::from_str(json).unwrap();
279 assert_eq!(cfg.max_processes, 128);
280 assert_eq!(cfg.health_check_interval_secs, 15);
281 }
282
283 #[test]
284 fn serde_roundtrip() {
285 let cfg = KernelConfig {
286 enabled: true,
287 max_processes: 256,
288 health_check_interval_secs: 10,
289 cluster: None,
290 chain: None,
291 resource_tree: None,
292 };
293 let json = serde_json::to_string(&cfg).unwrap();
294 let restored: KernelConfig = serde_json::from_str(&json).unwrap();
295 assert_eq!(restored.enabled, cfg.enabled);
296 assert_eq!(restored.max_processes, cfg.max_processes);
297 }
298}