@@ -8,7 +8,54 @@ use crate::config::{
8
8
json,
9
9
types:: { AllChartSettings , CounterInfo , LineChartCategory , LineChartInfo } ,
10
10
} ;
11
- use std:: collections:: { btree_map:: Entry , BTreeMap , VecDeque } ;
11
+ use std:: collections:: { btree_map:: Entry , BTreeMap } ;
12
+
13
+ /// Prioritize values from environment
14
+ pub fn override_charts (
15
+ target : & mut json:: charts:: Config ,
16
+ source : env:: charts:: Config ,
17
+ ) -> Result < ( ) , anyhow:: Error > {
18
+ override_chart_settings ( & mut target. counters , source. counters ) . context ( "updating counters" ) ?;
19
+ override_chart_settings ( & mut target. line_charts , source. line_charts )
20
+ . context ( "updating line categories" ) ?;
21
+ target. template_values . extend ( source. template_values ) ;
22
+ Ok ( ( ) )
23
+ }
24
+
25
+ /// Prioritize values from environment
26
+ pub fn override_layout (
27
+ target : & mut json:: layout:: Config ,
28
+ source : env:: layout:: Config ,
29
+ ) -> Result < ( ) , anyhow:: Error > {
30
+ override_ordered ( & mut target. counters_order , source. counters_order , |_, _| {
31
+ Ok ( ( ) )
32
+ } ) ?;
33
+ override_ordered (
34
+ & mut target. line_chart_categories ,
35
+ source. line_chart_categories ,
36
+ override_field:: line_categories,
37
+ ) ?;
38
+ Ok ( ( ) )
39
+ }
40
+
41
+ /// Prioritize values from environment
42
+ pub fn override_update_groups (
43
+ target : & mut json:: update_groups:: Config ,
44
+ source : env:: update_groups:: Config ,
45
+ ) -> Result < ( ) , anyhow:: Error > {
46
+ for ( group_name, added_settings) in source. schedules {
47
+ match target. schedules . entry ( group_name) {
48
+ Entry :: Vacant ( v) => {
49
+ v. insert ( added_settings) ;
50
+ }
51
+ Entry :: Occupied ( mut o) => {
52
+ let target_group = o. get_mut ( ) ;
53
+ target_group. update_schedule = added_settings. update_schedule ;
54
+ }
55
+ }
56
+ }
57
+ Ok ( ( ) )
58
+ }
12
59
13
60
trait GetOrder {
14
61
fn order ( & self ) -> Option < usize > ;
@@ -57,57 +104,56 @@ impl_get_key!(CounterInfo<C>);
57
104
impl_get_key ! ( LineChartInfo <C >) ;
58
105
impl_get_key ! ( LineChartCategory ) ;
59
106
107
+ /// Returns `target` with updated order of elements.
108
+ ///
109
+ /// Each element with key in `new_order` is placed on `new_order.get(key)`'th position
110
+ /// in `target` (or at the end, if the position exceeds target's length).
111
+ fn with_updated_order < T : GetKey > ( target : Vec < T > , new_order : BTreeMap < String , usize > ) -> Vec < T > {
112
+ let ( moved_elements, mut other_elements) : ( BTreeMap < _ , _ > , Vec < _ > ) =
113
+ target. into_iter ( ) . partition_map ( |t| {
114
+ if let Some ( move_to) = new_order. get ( t. key ( ) ) {
115
+ Either :: Left ( ( move_to, t) )
116
+ } else {
117
+ Either :: Right ( t)
118
+ }
119
+ } ) ;
120
+ // it's important to iterate in ascending order to not mess up
121
+ // ordering of previous elements
122
+ for ( & new_idx, element) in moved_elements {
123
+ if new_idx <= other_elements. len ( ) {
124
+ other_elements. insert ( new_idx, element)
125
+ } else {
126
+ other_elements. push ( element)
127
+ }
128
+ }
129
+ other_elements
130
+ }
131
+
60
132
/// `update_t` should update 1st parameter with values from the 2nd
61
133
fn override_ordered < T , S , F > (
62
134
target : & mut Vec < T > ,
63
- source : BTreeMap < String , S > ,
135
+ mut source : BTreeMap < String , S > ,
64
136
update_t : F ,
65
137
) -> Result < ( ) , anyhow:: Error >
66
138
where
67
139
T : GetKey + Clone ,
68
140
S : GetOrder ,
69
141
F : Fn ( & mut T , S ) -> Result < ( ) , anyhow:: Error > ,
70
142
{
71
- // Override values and order
72
- let mut target_with_order : BTreeMap < String , ( Option < usize > , T ) > = std :: mem :: take ( target )
73
- . into_iter ( )
74
- . map ( |t| ( t . key ( ) . to_owned ( ) , ( None , t ) ) )
143
+ // elements with overwritten order (collect to safely consume `source`)
144
+ let orders_overwrite : BTreeMap < _ , _ > = source
145
+ . iter ( )
146
+ . filter_map ( | ( key , val ) | Some ( ( key . clone ( ) , val . order ( ) ? ) ) )
75
147
. collect ( ) ;
76
- for ( key, val_with_order) in source {
77
- let Some ( ( target_order, target_val) ) = target_with_order. get_mut ( & key) else {
78
- return Err ( anyhow:: anyhow!( "Unknown key: {}" , key) ) ;
79
- } ;
80
- if let Some ( order_override) = val_with_order. order ( ) {
81
- * target_order = Some ( order_override) ;
148
+
149
+ // first overwrite only contents (not order)
150
+ for element in target. iter_mut ( ) {
151
+ if let Some ( overwrite) = source. remove ( element. key ( ) ) {
152
+ update_t ( element, overwrite) ?
82
153
}
83
- update_t ( target_val, val_with_order)
84
- . context ( format ! ( "updating values for key: {}" , key) ) ?;
85
154
}
86
- // Sort according to original & overridden order
87
- let total_items = target_with_order. len ( ) ;
88
- let ( mut target_overridden_order, mut target_default_order) : ( BTreeMap < usize , T > , VecDeque < T > ) =
89
- target_with_order. into_values ( ) . partition_map ( |( order, v) | {
90
- if let Some ( o) = order {
91
- Either :: Left ( ( o, v. clone ( ) ) )
92
- } else {
93
- Either :: Right ( v. clone ( ) )
94
- }
95
- } ) ;
96
- let new_target = {
97
- let mut v = Vec :: with_capacity ( total_items) ;
98
- for i in 0 ..total_items {
99
- if let Some ( value) = target_overridden_order. remove ( & i) {
100
- v. push ( value)
101
- } else if let Some ( value) = target_default_order. pop_front ( ) {
102
- v. push ( value)
103
- } else {
104
- v. extend ( target_overridden_order. into_values ( ) ) ;
105
- break ;
106
- }
107
- }
108
- v
109
- } ;
110
- * target = new_target;
155
+ // overwrite order
156
+ * target = with_updated_order ( std:: mem:: take ( target) , orders_overwrite) ;
111
157
Ok ( ( ) )
112
158
}
113
159
@@ -143,53 +189,6 @@ fn override_chart_settings(
143
189
Ok ( ( ) )
144
190
}
145
191
146
- /// Prioritize values from environment
147
- pub fn override_charts (
148
- target : & mut json:: charts:: Config ,
149
- source : env:: charts:: Config ,
150
- ) -> Result < ( ) , anyhow:: Error > {
151
- override_chart_settings ( & mut target. counters , source. counters ) . context ( "updating counters" ) ?;
152
- override_chart_settings ( & mut target. line_charts , source. line_charts )
153
- . context ( "updating line categories" ) ?;
154
- target. template_values . extend ( source. template_values ) ;
155
- Ok ( ( ) )
156
- }
157
-
158
- /// Prioritize values from environment
159
- pub fn override_layout (
160
- target : & mut json:: layout:: Config ,
161
- source : env:: layout:: Config ,
162
- ) -> Result < ( ) , anyhow:: Error > {
163
- override_ordered ( & mut target. counters_order , source. counters_order , |_, _| {
164
- Ok ( ( ) )
165
- } ) ?;
166
- override_ordered (
167
- & mut target. line_chart_categories ,
168
- source. line_chart_categories ,
169
- override_field:: line_categories,
170
- ) ?;
171
- Ok ( ( ) )
172
- }
173
-
174
- /// Prioritize values from environment
175
- pub fn override_update_groups (
176
- target : & mut json:: update_groups:: Config ,
177
- source : env:: update_groups:: Config ,
178
- ) -> Result < ( ) , anyhow:: Error > {
179
- for ( group_name, added_settings) in source. schedules {
180
- match target. schedules . entry ( group_name) {
181
- Entry :: Vacant ( v) => {
182
- v. insert ( added_settings) ;
183
- }
184
- Entry :: Occupied ( mut o) => {
185
- let target_group = o. get_mut ( ) ;
186
- target_group. update_schedule = added_settings. update_schedule ;
187
- }
188
- }
189
- }
190
- Ok ( ( ) )
191
- }
192
-
193
192
#[ cfg( test) ]
194
193
mod tests {
195
194
use std:: collections:: HashMap ;
@@ -378,6 +377,58 @@ mod tests {
378
377
assert_eq ! ( overridden_config, expected_config)
379
378
}
380
379
380
+ const EXAMPLE_LAYOUT_CONFIG_2 : & str = r#"{
381
+ "counters_order": [
382
+ "total_txns",
383
+ "total_blocks",
384
+ "total_accounts"
385
+ ],
386
+ "line_chart_categories": [
387
+ {
388
+ "id": "transactions",
389
+ "title": "Transactions",
390
+ "charts_order": [
391
+ "average_txn_fee",
392
+ "txns_fee"
393
+ ]
394
+ },
395
+ {
396
+ "id": "blocks",
397
+ "title": "Blocks",
398
+ "charts_order": [
399
+ "total_blocks",
400
+ "blocks_growth"
401
+ ]
402
+ },
403
+ {
404
+ "id": "accounts",
405
+ "title": "Accounts",
406
+ "charts_order": [
407
+ "new_accounts",
408
+ "accounts_growth"
409
+ ]
410
+ }
411
+ ]
412
+ }"# ;
413
+
414
+ #[ test]
415
+ fn layout_order_is_preserved ( ) {
416
+ let mut json_config: json:: layout:: Config =
417
+ serde_json:: from_str ( EXAMPLE_LAYOUT_CONFIG_2 ) . unwrap ( ) ;
418
+
419
+ let env_override: env:: layout:: Config =
420
+ config_from_env ( "STATS_LAYOUT" , HashMap :: new ( ) ) . unwrap ( ) ;
421
+
422
+ override_layout ( & mut json_config, env_override) . unwrap ( ) ;
423
+ let overridden_config = serde_json:: to_value ( json_config) . unwrap ( ) ;
424
+
425
+ let expected_config: json:: layout:: Config =
426
+ serde_json:: from_str ( EXAMPLE_LAYOUT_CONFIG_2 ) . unwrap ( ) ;
427
+ let expected_config = serde_json:: to_value ( expected_config) . unwrap ( ) ;
428
+
429
+ assert_eq ! ( overridden_config, expected_config)
430
+ }
431
+
381
432
const EXAMPLE_SCHEDULE_CONFIG : & str = r#"{
382
433
"schedules": {
383
434
"average_block_time": "0 0 15 * * * *",
0 commit comments