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
pub use crate::api::ParamScope;
pub use crate::api::ParamScopeOps;
pub trait AsParamScope {
fn param_scope(&self) -> ParamScope;
}
impl AsParamScope for config::Config {
fn param_scope(&self) -> ParamScope {
let mut ps = ParamScope::default();
fn unpack(ps: &mut ParamScope, prefix: Option<String>, value: config::Value) {
match (prefix, value.kind) {
// Root level table - unpack all entries
(None, config::ValueKind::Table(v)) => v.iter().for_each(|(k, v)| {
unpack(ps, Some(k.to_string()), v.clone());
}),
// Nested table - unpack with prefix
(Some(prefix), config::ValueKind::Table(v)) => v.iter().for_each(|(k, v)| {
unpack(ps, Some(format!("{}.{}", prefix, k)), v.clone());
}),
// Primitive types with prefix
(Some(k), config::ValueKind::Boolean(v)) => ps.put(k, v),
(Some(k), config::ValueKind::I64(v)) => ps.put(k, v),
(Some(k), config::ValueKind::Float(v)) => ps.put(k, v),
(Some(k), config::ValueKind::String(v)) => ps.put(k, v),
// Additional integer types with prefix
(Some(k), config::ValueKind::I128(v)) => {
// Convert i128 to i64 if possible, otherwise to string
if v >= i64::MIN as i128 && v <= i64::MAX as i128 {
ps.put(k, v as i64);
} else {
ps.put(k, v.to_string());
}
}
(Some(k), config::ValueKind::U64(v)) => {
// Convert u64 to i64 if possible, otherwise to string
if v <= i64::MAX as u64 {
ps.put(k, v as i64);
} else {
ps.put(k, v.to_string());
}
}
(Some(k), config::ValueKind::U128(v)) => {
// Convert u128 to i64 if possible, otherwise to string
if v <= i64::MAX as u128 {
ps.put(k, v as i64);
} else {
ps.put(k, v.to_string());
}
}
// Array type - convert to comma-separated string
(Some(k), config::ValueKind::Array(arr)) => {
// Convert array elements to string and join with comma
let arr_str: Vec<String> = arr
.iter()
.map(|v| match &v.kind {
config::ValueKind::String(s) => s.clone(),
config::ValueKind::I64(n) => n.to_string(),
config::ValueKind::I128(n) => n.to_string(),
config::ValueKind::U64(n) => n.to_string(),
config::ValueKind::U128(n) => n.to_string(),
config::ValueKind::Float(n) => n.to_string(),
config::ValueKind::Boolean(b) => b.to_string(),
config::ValueKind::Table(_) => {
// For nested tables in arrays, use debug representation
format!("{:?}", v)
}
config::ValueKind::Array(_) => {
// For nested arrays, use debug representation
format!("{:?}", v)
}
config::ValueKind::Nil => {
// For nil values in arrays, use empty string
String::new()
}
})
.collect();
ps.put(k, arr_str.join(","));
}
// Nil type with prefix - skip null values
(Some(_k), config::ValueKind::Nil) => {
// Skip null values - don't add to parameter scope
}
// Root level non-table types - should not occur in normal config, but handle gracefully
(None, config::ValueKind::Boolean(_)) => {
// Root level boolean - skip (config root should be a table)
}
(None, config::ValueKind::I64(_)) => {
// Root level integer - skip (config root should be a table)
}
(None, config::ValueKind::I128(_)) => {
// Root level i128 - skip (config root should be a table)
}
(None, config::ValueKind::U64(_)) => {
// Root level u64 - skip (config root should be a table)
}
(None, config::ValueKind::U128(_)) => {
// Root level u128 - skip (config root should be a table)
}
(None, config::ValueKind::Float(_)) => {
// Root level float - skip (config root should be a table)
}
(None, config::ValueKind::String(_)) => {
// Root level string - skip (config root should be a table)
}
(None, config::ValueKind::Array(_)) => {
// Root level array - skip (config root should be a table)
}
(None, config::ValueKind::Nil) => {
// Root level nil - skip (config root should be a table)
}
};
}
unpack(&mut ps, None, self.cache.clone());
ps
}
}
#[cfg(test)]
mod tests {
use config::ConfigError;
// use crate::with_params;
use crate::*;
use super::AsParamScope;
#[test]
fn test_create_param_scope_from_config() -> Result<(), ConfigError> {
let mut cfg = config::Config::builder()
.set_default("a", 1)?
.set_default("b", "2")?
.set_default(
"foo",
config::Config::builder()
.set_default("a", 11)?
.set_default("b", "22")?
.build()?
.cache
.clone()
.into_table()?,
)?
.build()?
.param_scope();
with_params! {
params cfg;
with_params! {
get a = a or 0i64;
get b = b or String::from("2");
get foo_a = foo.a or 0i64;
assert_eq!(1, a);
assert_eq!("2", b);
assert_eq!(11, foo_a);
println!("a = {}", a);
println!("b = {}", b);
println!("foo.a= {}", foo_a);
}
}
Ok(())
}
}