13
13
// limitations under the License.
14
14
15
15
use std:: borrow:: Cow ;
16
+ use std:: collections:: BTreeSet ;
16
17
use std:: collections:: HashMap ;
17
- use std:: collections:: HashSet ;
18
18
use std:: env;
19
19
use std:: fmt;
20
20
use std:: path:: Path ;
@@ -103,28 +103,40 @@ pub fn resolved_config_values(
103
103
stacked_config : & StackedConfig ,
104
104
filter_prefix : & ConfigNamePathBuf ,
105
105
) -> Vec < AnnotatedValue > {
106
- // Collect annotated values from each config.
106
+ // Collect annotated values in reverse order and mark each value shadowed by
107
+ // value or table in upper layers.
107
108
let mut config_vals = vec ! [ ] ;
108
- for layer in stacked_config. layers ( ) {
109
- // TODO: Err(item) means all descendant paths are overridden by the
110
- // current layer. For example, the default ui.pager.<field> should be
111
- // marked as overridden if user had ui.pager = [...] set.
112
- let Ok ( Some ( top_item) ) = layer. look_up_item ( filter_prefix) else {
113
- continue ;
109
+ let mut upper_value_names = BTreeSet :: new ( ) ;
110
+ for layer in stacked_config. layers ( ) . iter ( ) . rev ( ) {
111
+ let top_item = match layer. look_up_item ( filter_prefix) {
112
+ Ok ( Some ( item) ) => item,
113
+ Ok ( None ) => continue , // parent is a table, but no value found
114
+ Err ( _) => {
115
+ // parent is not a table, shadows lower layers
116
+ upper_value_names. insert ( filter_prefix. clone ( ) ) ;
117
+ continue ;
118
+ }
114
119
} ;
115
- let mut config_stack = vec ! [ ( filter_prefix. clone( ) , top_item) ] ;
116
- while let Some ( ( name, item) ) = config_stack. pop ( ) {
120
+ let mut config_stack = vec ! [ ( filter_prefix. clone( ) , top_item, false ) ] ;
121
+ while let Some ( ( name, item, is_parent_overridden ) ) = config_stack. pop ( ) {
117
122
if let Some ( table) = item. as_table ( ) {
118
- // table.iter() does not implement DoubleEndedIterator as of
119
- // toml_edit 0.22.22.
120
- let frame = config_stack. len ( ) ;
123
+ // current table and children may be shadowed by value in upper layer
124
+ let is_overridden = is_parent_overridden || upper_value_names. contains ( & name) ;
121
125
for ( k, v) in table {
122
126
let mut sub_name = name. clone ( ) ;
123
127
sub_name. push ( k) ;
124
- config_stack. push ( ( sub_name, v) ) ;
128
+ config_stack. push ( ( sub_name, v, is_overridden ) ) ; // in reverse order
125
129
}
126
- config_stack[ frame..] . reverse ( ) ;
127
130
} else {
131
+ // current value may be shadowed by value or table in upper layer
132
+ let maybe_child = upper_value_names
133
+ . range ( & name..)
134
+ . next ( )
135
+ . filter ( |next| next. starts_with ( & name) ) ;
136
+ let is_overridden = is_parent_overridden || maybe_child. is_some ( ) ;
137
+ if maybe_child != Some ( & name) {
138
+ upper_value_names. insert ( name. clone ( ) ) ;
139
+ }
128
140
let value = item
129
141
. clone ( )
130
142
. into_value ( )
@@ -133,20 +145,12 @@ pub fn resolved_config_values(
133
145
name,
134
146
value,
135
147
source : layer. source ,
136
- // Note: Value updated below.
137
- is_overridden : false ,
148
+ is_overridden,
138
149
} ) ;
139
150
}
140
151
}
141
152
}
142
-
143
- // Walk through config values in reverse order and mark each overridden value as
144
- // overridden.
145
- let mut names_found = HashSet :: new ( ) ;
146
- for val in config_vals. iter_mut ( ) . rev ( ) {
147
- val. is_overridden = !names_found. insert ( & val. name ) ;
148
- }
149
-
153
+ config_vals. reverse ( ) ;
150
154
config_vals
151
155
}
152
156
@@ -702,6 +706,8 @@ impl TryFrom<Vec<String>> for NonEmptyCommandArgsVec {
702
706
703
707
#[ cfg( test) ]
704
708
mod tests {
709
+ use std:: fmt:: Write as _;
710
+
705
711
use anyhow:: anyhow;
706
712
use assert_matches:: assert_matches;
707
713
use indoc:: indoc;
@@ -1040,6 +1046,112 @@ mod tests {
1040
1046
) ;
1041
1047
}
1042
1048
1049
+ #[ test]
1050
+ fn test_resolved_config_values_overridden ( ) {
1051
+ let list = |layers : & [ & ConfigLayer ] , prefix : & str | -> String {
1052
+ let mut config = StackedConfig :: empty ( ) ;
1053
+ config. extend_layers ( layers. iter ( ) . copied ( ) . cloned ( ) ) ;
1054
+ let prefix = if prefix. is_empty ( ) {
1055
+ ConfigNamePathBuf :: root ( )
1056
+ } else {
1057
+ prefix. parse ( ) . unwrap ( )
1058
+ } ;
1059
+ let mut output = String :: new ( ) ;
1060
+ for annotated in resolved_config_values ( & config, & prefix) {
1061
+ let AnnotatedValue { name, value, .. } = & annotated;
1062
+ let sigil = if annotated. is_overridden { '!' } else { ' ' } ;
1063
+ writeln ! ( output, "{sigil}{name} = {value}" ) . unwrap ( ) ;
1064
+ }
1065
+ output
1066
+ } ;
1067
+
1068
+ let mut layer0 = ConfigLayer :: empty ( ConfigSource :: User ) ;
1069
+ layer0. set_value ( "a.b.e" , "0.0" ) . unwrap ( ) ;
1070
+ layer0. set_value ( "a.b.c.f" , "0.1" ) . unwrap ( ) ;
1071
+ layer0. set_value ( "a.b.d" , "0.2" ) . unwrap ( ) ;
1072
+ let mut layer1 = ConfigLayer :: empty ( ConfigSource :: User ) ;
1073
+ layer1. set_value ( "a.b" , "1.0" ) . unwrap ( ) ;
1074
+ layer1. set_value ( "a.c" , "1.1" ) . unwrap ( ) ;
1075
+ let mut layer2 = ConfigLayer :: empty ( ConfigSource :: User ) ;
1076
+ layer2. set_value ( "a.b.g" , "2.0" ) . unwrap ( ) ;
1077
+ layer2. set_value ( "a.b.d" , "2.1" ) . unwrap ( ) ;
1078
+
1079
+ // a.b.* is shadowed by a.b
1080
+ let layers = [ & layer0, & layer1] ;
1081
+ insta:: assert_snapshot!( list( & layers, "" ) , @r#"
1082
+ !a.b.e = "0.0"
1083
+ !a.b.c.f = "0.1"
1084
+ !a.b.d = "0.2"
1085
+ a.b = "1.0"
1086
+ a.c = "1.1"
1087
+ "# ) ;
1088
+ insta:: assert_snapshot!( list( & layers, "a.b" ) , @r#"
1089
+ !a.b.e = "0.0"
1090
+ !a.b.c.f = "0.1"
1091
+ !a.b.d = "0.2"
1092
+ a.b = "1.0"
1093
+ "# ) ;
1094
+ insta:: assert_snapshot!( list( & layers, "a.b.c" ) , @r#"!a.b.c.f = "0.1""# ) ;
1095
+ insta:: assert_snapshot!( list( & layers, "a.b.d" ) , @r#"!a.b.d = "0.2""# ) ;
1096
+
1097
+ // a.b is shadowed by a.b.*
1098
+ let layers = [ & layer1, & layer2] ;
1099
+ insta:: assert_snapshot!( list( & layers, "" ) , @r#"
1100
+ !a.b = "1.0"
1101
+ a.c = "1.1"
1102
+ a.b.g = "2.0"
1103
+ a.b.d = "2.1"
1104
+ "# ) ;
1105
+ insta:: assert_snapshot!( list( & layers, "a.b" ) , @r#"
1106
+ !a.b = "1.0"
1107
+ a.b.g = "2.0"
1108
+ a.b.d = "2.1"
1109
+ "# ) ;
1110
+
1111
+ // a.b.d is shadowed by a.b.d
1112
+ let layers = [ & layer0, & layer2] ;
1113
+ insta:: assert_snapshot!( list( & layers, "" ) , @r#"
1114
+ a.b.e = "0.0"
1115
+ a.b.c.f = "0.1"
1116
+ !a.b.d = "0.2"
1117
+ a.b.g = "2.0"
1118
+ a.b.d = "2.1"
1119
+ "# ) ;
1120
+ insta:: assert_snapshot!( list( & layers, "a.b" ) , @r#"
1121
+ a.b.e = "0.0"
1122
+ a.b.c.f = "0.1"
1123
+ !a.b.d = "0.2"
1124
+ a.b.g = "2.0"
1125
+ a.b.d = "2.1"
1126
+ "# ) ;
1127
+ insta:: assert_snapshot!( list( & layers, "a.b.c" ) , @r#" a.b.c.f = "0.1""# ) ;
1128
+ insta:: assert_snapshot!( list( & layers, "a.b.d" ) , @r#"
1129
+ !a.b.d = "0.2"
1130
+ a.b.d = "2.1"
1131
+ "# ) ;
1132
+
1133
+ // a.b.* is shadowed by a.b, which is shadowed by a.b.*
1134
+ let layers = [ & layer0, & layer1, & layer2] ;
1135
+ insta:: assert_snapshot!( list( & layers, "" ) , @r#"
1136
+ !a.b.e = "0.0"
1137
+ !a.b.c.f = "0.1"
1138
+ !a.b.d = "0.2"
1139
+ !a.b = "1.0"
1140
+ a.c = "1.1"
1141
+ a.b.g = "2.0"
1142
+ a.b.d = "2.1"
1143
+ "# ) ;
1144
+ insta:: assert_snapshot!( list( & layers, "a.b" ) , @r#"
1145
+ !a.b.e = "0.0"
1146
+ !a.b.c.f = "0.1"
1147
+ !a.b.d = "0.2"
1148
+ !a.b = "1.0"
1149
+ a.b.g = "2.0"
1150
+ a.b.d = "2.1"
1151
+ "# ) ;
1152
+ insta:: assert_snapshot!( list( & layers, "a.b.c" ) , @r#"!a.b.c.f = "0.1""# ) ;
1153
+ }
1154
+
1043
1155
#[ test]
1044
1156
fn test_config_path_home_dir_existing ( ) -> anyhow:: Result < ( ) > {
1045
1157
TestCase {
0 commit comments