config_rw 1.0.2

配置文件读取与写入
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
use config_rw::{get_arg, init_config, save_config, set_arg};
use config_rw::{get_bool, get_f64, get_i64, get_string};
use config_rw::{set_bool, set_f64, set_i64, set_string};
type StdBoxError = Box<dyn std::error::Error + Send + Sync>;
type R<V = ()> = Result<V, StdBoxError>;

use serde_json::Value;

fn main() -> R {
    // 运行配置管理器示例
    test_config_rw()?;
    demo_cross_module_config()?;
    demo_complex_config()?;

    Ok(())
}

fn test_config_rw() -> R {
    // 初始化配置管理器
    init_config("config.toml")?;

    // 读取配置并验证
    // 使用 get_arg 获取原始 JSON 值
    let host: Value = get_arg("database.host");
    assert_eq!(host.is_string(), true); // 验证主机配置存在且为字符串

    // 使用类型化的获取函数
    let port = get_i64("database.port").unwrap_or(3306);
    assert!(port > 0); // 验证端口号有效

    let timeout = get_f64("database.timeout").unwrap_or(10.0);
    assert!(timeout > 0.0); // 验证超时时间有效

    let _debug_enabled = get_bool("app.debug").unwrap_or(false);
    // 调试模式可以是 true 或 false,都是有效的

    let app_name = get_string("app.name").unwrap_or("unknown".to_string());
    assert_ne!(app_name, ""); // 验证应用名称不为空

    // 读取嵌套配置
    let _cache_enabled = get_bool("app.features.cache_enabled").unwrap_or(false);
    // 缓存启用状态可以是 true 或 false

    let max_position = get_i64("trading.max_position_size").unwrap_or(0);
    assert!(max_position >= 0); // 验证最大仓位不为负数

    // 修改现有配置
    set_string("database.host", "127.0.0.1".to_string())?;
    set_i64("database.port", 3306)?;
    set_bool("app.debug", true)?;
    set_f64("trading.stop_loss_percent", 0.03)?;

    // 添加新的配置
    set_string("new_section.new_key", "new_value".to_string())?;
    set_i64("performance.max_threads", 8)?;

    // 验证修改是否生效
    let new_host = get_string("database.host").unwrap();
    assert_eq!(new_host, "127.0.0.1");

    let new_port = get_i64("database.port").unwrap();
    assert_eq!(new_port, 3306);

    let debug_mode = get_bool("app.debug").unwrap();
    assert_eq!(debug_mode, true);

    let new_config = get_string("new_section.new_key").unwrap();
    assert_eq!(new_config, "new_value");

    let max_threads = get_i64("performance.max_threads").unwrap();
    assert_eq!(max_threads, 8);

    // 保存配置到文件
    save_config()?;

    // 验证配置文件确实被保存(通过重新读取)
    let saved_host = get_string("database.host").unwrap();
    assert_eq!(saved_host, "127.0.0.1");

    Ok(())
}

/// 演示如何在不同模块间共享配置
pub fn demo_cross_module_config() -> R {
    // 在任何模块中都可以直接使用配置
    let server_port = get_i64("network.server_port").unwrap_or(8080);
    assert!(server_port > 0); // 验证服务器端口有效

    let log_level = get_string("logging.level").unwrap_or("info".to_string());
    assert_ne!(log_level, ""); // 验证日志级别不为空

    // 动态修改配置
    set_i64("network.server_port", 9090)?;
    set_string("logging.level", "debug".to_string())?;

    // 验证修改
    let new_port = get_i64("network.server_port").unwrap();
    let new_level = get_string("logging.level").unwrap();

    assert_eq!(new_port, 9090);
    assert_eq!(new_level, "debug");

    Ok(())
}



/// 演示跨模块配置访问
pub fn demo_cross_module_access() -> R {
    // 模拟不同模块中的函数
    module_a()?;
    module_b()?;
    
    Ok(())
}

/// 模拟模块 A 中的函数
fn module_a() -> R {
    // 设置并验证配置
    set_i64("cross_module.server_port", 8080)?;
    let port = get_i64("cross_module.server_port").unwrap();
    assert_eq!(port, 8080);
    
    // 设置最大连接数
    set_i64("cross_module.max_connections", 500)?;
    
    // 验证设置结果 - 使用 unwrap 确保确定性
    let max_conn = get_i64("cross_module.max_connections").unwrap();
    assert_eq!(max_conn, 500);
    
    Ok(())
}

/// 模拟模块 B 中的函数
fn module_b() -> R {
    // 读取配置值 - 使用 unwrap 确保确定性
    let port = get_i64("cross_module.server_port").unwrap();
    let max_conn = get_i64("cross_module.max_connections").unwrap();
    
    // 验证从模块 A 读取到的值
    assert_eq!(port, 8080);
    assert_eq!(max_conn, 500); // 模块 A 设置的值
    
    Ok(())
}

/// 演示复杂配置操作
pub fn demo_complex_config() -> R {
    // 设置嵌套配置
    set_string("complex_demo.nested.key", "nested_value".to_string())?;
    
    // 设置数组配置
    let array_value = Value::Array(vec![
        Value::String("item1".to_string()),
        Value::String("item2".to_string()),
        Value::Number(serde_json::Number::from(42)),
    ]);
    set_arg("complex_demo.test_array", array_value)?;
    
    // 设置对象配置
    let mut obj = serde_json::Map::new();
    obj.insert("name".to_string(), Value::String("test_object".to_string()));
    obj.insert("count".to_string(), Value::Number(serde_json::Number::from(99)));
    obj.insert("enabled".to_string(), Value::Bool(true));
    set_arg("complex_demo.test_object", Value::Object(obj))?;
    
    // 验证嵌套配置 - 使用 unwrap 确保确定性
    let nested_value = get_string("complex_demo.nested.key").unwrap();
    assert_eq!(nested_value, "nested_value");
    
    // 验证数组配置
    let array_config = get_arg("complex_demo.test_array");
    assert_eq!(array_config.is_array(), true);
    
    let Value::Array(arr) = array_config else {
        panic!("Expected array configuration");
    };
    assert_eq!(arr.len(), 3);
    assert_eq!(arr[0], Value::String("item1".to_string()));
    assert_eq!(arr[1], Value::String("item2".to_string()));
    assert_eq!(arr[2], Value::Number(serde_json::Number::from(42)));
    
    // 验证对象配置
    let object_config = get_arg("complex_demo.test_object");
    assert_eq!(object_config.is_object(), true);
    
    let Value::Object(obj) = object_config else {
        panic!("Expected object configuration");
    };
    assert_eq!(obj.len(), 3);
    assert_eq!(obj.get("name").unwrap(), &Value::String("test_object".to_string()));
    assert_eq!(obj.get("count").unwrap(), &Value::Number(serde_json::Number::from(99)));
    assert_eq!(obj.get("enabled").unwrap(), &Value::Bool(true));
    
    // 保存配置
    save_config()?;
    
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_demo_priority() -> R {
        // 初始化配置
        init_config("config.toml")?;
        
        // 测试数据库主机配置
        let host = get_string("database.host");
        assert!(host.is_some(), "数据库主机配置应该存在");
        
        // 测试数据库端口配置
        let port = get_i64("database.port");
        assert!(port.is_some(), "数据库端口配置应该存在");
        assert!(port.unwrap() > 0, "数据库端口应该大于0");
        
        // 测试调试模式配置
        let debug = get_bool("app.debug");
        assert!(debug.is_some(), "调试模式配置应该存在");
        
        // 测试环境变量优先级(如果存在)
        let _server_port = get_i64("SERVER_PORT");
        // 环境变量可能存在也可能不存在,这是正常的
        
        let _custom_config = get_string("CUSTOM_CONFIG");
        // 环境变量可能存在也可能不存在,这是正常的
        
        // 验证配置优先级:配置文件中的值应该能被正确读取
        let app_name = get_string("app.name");
        assert!(app_name.is_some(), "应用名称配置应该存在");
        assert!(!app_name.unwrap().is_empty(), "应用名称不应该为空");
        
        Ok(())
    }
    
    #[test]
    fn test_demo_auto_save() -> R {
        // 初始化配置
        init_config("config.toml")?;
        
        // 1. 测试自动保存功能
        let test_value = "自动保存的值";
        let test_number = 12345i64;
        let test_bool = true;
        
        // 设置配置值
        set_string("demo.auto_save_test", test_value.to_string())?;
        set_i64("demo.number", test_number)?;
        set_bool("demo.enabled", test_bool)?;
        
        // 验证设置成功
        let saved_value = get_string("demo.auto_save_test");
        assert_eq!(saved_value, Some(test_value.to_string()), "自动保存的字符串值应该正确");
        
        let saved_number = get_i64("demo.number");
        assert_eq!(saved_number, Some(test_number), "自动保存的数字值应该正确");
        
        let saved_bool = get_bool("demo.enabled");
        assert_eq!(saved_bool, Some(test_bool), "自动保存的布尔值应该正确");
        
        // 2. 测试来源保护功能
        // 测试配置文件中的值可以被修改
        if let Some(_original_name) = get_string("app.name") {
            let new_name = "更新后的应用名";
            
            // 修改配置文件中的值应该成功
            let result = set_string("app.name", new_name.to_string());
            assert!(result.is_ok(), "修改配置文件中的值应该成功");
            
            // 验证修改成功
            let updated_name = get_string("app.name");
            assert_eq!(updated_name, Some(new_name.to_string()), "配置文件中的值应该被正确更新");
        }
        
        // 3. 测试数据库主机配置修改
        let new_host = "不应该被保存";
        let result = set_string("database.host", new_host.to_string());
        
        // 这个操作应该成功,因为它会修改配置文件中的值
        assert!(result.is_ok(), "修改数据库主机配置应该成功");
        
        // 验证配置确实被修改
        let updated_host = get_string("database.host");
        assert!(updated_host.is_some(), "数据库主机配置应该存在");
        
        Ok(())
    }
    
    #[test]
    fn demo_config_manager() -> R {
        // 使用固定的配置文件路径,避免全局状态冲突
        let config_path = "test_config.toml";
        
        // 尝试初始化配置,如果已经初始化则忽略
        let _ = init_config(config_path);
        
        // 使用唯一的配置键前缀避免测试间冲突
        let prefix = "demo_manager";
        
        // 设置测试需要的配置值
        set_string(&format!("{}.app.name", prefix), "tick_rhino".to_string())?;
        set_bool(&format!("{}.app.debug", prefix), false)?;
        set_string(&format!("{}.app.version", prefix), "1.0.0".to_string())?;
        set_string(&format!("{}.database.host", prefix), "localhost".to_string())?;
        set_i64(&format!("{}.database.port", prefix), 5432)?;
        set_i64(&format!("{}.network.server_port", prefix), 8080)?;
        set_i64(&format!("{}.network.max_connections", prefix), 100)?;
        
        // 验证读取的确切值 - 使用 unwrap 确保确定性
        let app_name = get_string(&format!("{}.app.name", prefix)).unwrap();
        let debug_mode = get_bool(&format!("{}.app.debug", prefix)).unwrap();
        let server_port = get_i64(&format!("{}.network.server_port", prefix)).unwrap();
        let db_port = get_i64(&format!("{}.database.port", prefix)).unwrap();
        
        assert_eq!(app_name, "tick_rhino");
        assert_eq!(debug_mode, false);
        assert_eq!(server_port, 8080);
        assert_eq!(db_port, 5432);
        
        // 使用 get_arg 读取原始 JSON 值
        let host_value: Value = get_arg(&format!("{}.database.host", prefix));
        assert_eq!(host_value, Value::String("localhost".to_string()));
        
        let port_value: Value = get_arg(&format!("{}.database.port", prefix));
        assert_eq!(port_value, Value::Number(serde_json::Number::from(5432)));
        
        // 修改配置
        set_bool(&format!("{}.app.debug", prefix), true)?;
        set_string(&format!("{}.app.version", prefix), "1.0.1".to_string())?;
        set_i64(&format!("{}.network.max_connections", prefix), 200)?;
        
        // 验证修改后的确切值 - 使用 unwrap 确保确定性
        let new_debug = get_bool(&format!("{}.app.debug", prefix)).unwrap();
        let new_version = get_string(&format!("{}.app.version", prefix)).unwrap();
        let new_max_conn = get_i64(&format!("{}.network.max_connections", prefix)).unwrap();
        
        assert_eq!(new_debug, true);
        assert_eq!(new_version, "1.0.1");
        assert_eq!(new_max_conn, 200);
        
        // 保存配置
        save_config()?;
        
        Ok(())
    }
    
    #[test]
    fn test_config_demo() {
        // 使用固定的配置文件路径,避免全局状态冲突
        let config_path = "test_config.toml";
        
        // 尝试初始化配置,如果已经初始化则忽略
        let _ = init_config(config_path);
        
        // 使用唯一的配置键前缀避免测试间冲突
        let prefix = "test_demo";
        
        // 设置测试需要的配置值
        set_string(&format!("{}.app.name", prefix), "test_demo_app".to_string()).unwrap();
        set_bool(&format!("{}.app.debug", prefix), false).unwrap();
        set_string(&format!("{}.app.version", prefix), "1.0.0".to_string()).unwrap();
        set_string(&format!("{}.database.host", prefix), "localhost".to_string()).unwrap();
        set_i64(&format!("{}.database.port", prefix), 5432).unwrap();
        set_i64(&format!("{}.network.server_port", prefix), 8080).unwrap();
        set_i64(&format!("{}.network.max_connections", prefix), 100).unwrap();
        
        // 验证基本读取的确切值 - 使用 unwrap 确保确定性
        let app_name = get_string(&format!("{}.app.name", prefix)).unwrap();
        let debug_mode = get_bool(&format!("{}.app.debug", prefix)).unwrap();
        let server_port = get_i64(&format!("{}.network.server_port", prefix)).unwrap();
        let db_host = get_string(&format!("{}.database.host", prefix)).unwrap();
        let db_port = get_i64(&format!("{}.database.port", prefix)).unwrap();
        
        assert_eq!(app_name, "test_demo_app");
        assert_eq!(debug_mode, false);
        assert_eq!(server_port, 8080);
        assert_eq!(db_host, "localhost");
        assert_eq!(db_port, 5432);
        
        // 测试修改配置
        set_bool(&format!("{}.app.debug", prefix), true).unwrap();
        set_i64(&format!("{}.network.max_connections", prefix), 200).unwrap();
        set_string(&format!("{}.app.version", prefix), "2.0.0".to_string()).unwrap();
        
        // 验证修改后的确切值 - 使用 unwrap 确保确定性
        let new_debug = get_bool(&format!("{}.app.debug", prefix)).unwrap();
        let new_max_conn = get_i64(&format!("{}.network.max_connections", prefix)).unwrap();
        let new_version = get_string(&format!("{}.app.version", prefix)).unwrap();
        
        assert_eq!(new_debug, true);
        assert_eq!(new_max_conn, 200);
        assert_eq!(new_version, "2.0.0");
        
        // 测试保存
        save_config().unwrap();
    }

    #[test]
    fn test_cross_module_access() {
        // 使用固定的配置文件路径,避免全局状态冲突
        let config_path = "test_config.toml";
        
        // 尝试初始化配置,如果已经初始化则忽略
        let _ = init_config(config_path);
        
        // 使用唯一的配置键前缀避免测试间冲突
        let prefix = "test_cross";
        
        // 设置测试需要的配置值
        set_i64(&format!("{}.network.server_port", prefix), 8080).unwrap();
        set_i64(&format!("{}.network.max_connections", prefix), 100).unwrap();
        set_string(&format!("{}.app.name", prefix), "cross_module_test".to_string()).unwrap();
        
        // 验证初始值 - 使用 unwrap 确保确定性
        let initial_port = get_i64(&format!("{}.network.server_port", prefix)).unwrap();
        let initial_max_conn = get_i64(&format!("{}.network.max_connections", prefix)).unwrap();
        let app_name = get_string(&format!("{}.app.name", prefix)).unwrap();
        
        assert_eq!(initial_port, 8080);
        assert_eq!(initial_max_conn, 100);
        assert_eq!(app_name, "cross_module_test");
        
        // 测试跨模块配置访问
        let result = demo_cross_module_access();
        assert_eq!(result.is_ok(), true);
        
        // 验证跨模块访问后的值 - 使用 unwrap 确保确定性
        let final_port = get_i64("cross_module.server_port").unwrap();
        let final_max_conn = get_i64("cross_module.max_connections").unwrap();
        
        assert_eq!(final_port, 8080); // 端口未改变
        assert_eq!(final_max_conn, 500); // 模块A设置的值
    }

    #[test]
    fn test_complex_config_operations() {
        // 使用固定的配置文件路径,避免全局状态冲突
        let config_path = "test_config.toml";
        
        // 尝试初始化配置,如果已经初始化则忽略
        let _ = init_config(config_path);
        
        // 使用唯一的配置键前缀避免测试间冲突
        let prefix = "test_complex";
        
        // 设置测试需要的配置值
        set_string(&format!("{}.app.name", prefix), "complex_test_app".to_string()).unwrap();
        set_string(&format!("{}.app.version", prefix), "1.0.0".to_string()).unwrap();
        
        // 验证初始值 - 使用 unwrap 确保确定性
        let app_name = get_string(&format!("{}.app.name", prefix)).unwrap();
        let app_version = get_string(&format!("{}.app.version", prefix)).unwrap();
        
        assert_eq!(app_name, "complex_test_app");
        assert_eq!(app_version, "1.0.0");
        
        // 测试复杂配置操作
        let result = demo_complex_config();
        assert_eq!(result.is_ok(), true);
        
        // 验证嵌套配置 - 使用 unwrap 确保确定性
        let nested_value = get_string("complex_demo.nested.key").unwrap();
        assert_eq!(nested_value, "nested_value");
        
        // 验证数组配置
        let array_config = get_arg("complex_demo.test_array");
        assert_eq!(array_config.is_array(), true);
        
        let Value::Array(arr) = array_config else {
            panic!("Expected array configuration");
        };
        assert_eq!(arr.len(), 3);
        assert_eq!(arr[0], Value::String("item1".to_string()));
        assert_eq!(arr[1], Value::String("item2".to_string()));
        assert_eq!(arr[2], Value::Number(serde_json::Number::from(42)));
        
        // 验证对象配置
        let object_config = get_arg("complex_demo.test_object");
        assert_eq!(object_config.is_object(), true);
        
        let Value::Object(obj) = object_config else {
            panic!("Expected object configuration");
        };
        assert_eq!(obj.len(), 3);
        assert_eq!(obj.get("name").unwrap(), &Value::String("test_object".to_string()));
        assert_eq!(obj.get("count").unwrap(), &Value::Number(serde_json::Number::from(99)));
        assert_eq!(obj.get("enabled").unwrap(), &Value::Bool(true));
        
        // 保存配置
        save_config().unwrap();
    }
}