@@ -27,9 +27,9 @@ pub struct PersistedBridgeConfig {
2727 local_prefix : Option < Spanned < Template > > ,
2828 remote_prefix : Option < Spanned < Template > > ,
2929 #[ serde( rename = "rule" , default ) ]
30- rules : Vec < StaticBridgeRule > ,
30+ rules : Vec < Spanned < StaticBridgeRule > > ,
3131 #[ serde( rename = "template_rule" , default ) ]
32- template_rules : Vec < TemplateBridgeRule > ,
32+ template_rules : Vec < Spanned < TemplateBridgeRule > > ,
3333}
3434
3535#[ derive( Debug ) ]
@@ -40,9 +40,24 @@ pub struct ExpandedBridgeRule {
4040 pub topic : String ,
4141}
4242
43+ fn resolve_prefix (
44+ per_rule : Option < String > ,
45+ global : & Option < String > ,
46+ prefix_name : & str ,
47+ rule_type : & str ,
48+ span : std:: ops:: Range < usize > ,
49+ ) -> Result < String , ExpandError > {
50+ per_rule. or_else ( || global. clone ( ) ) . ok_or_else ( || ExpandError {
51+ message : format ! ( "Missing '{prefix_name}' for {rule_type}" ) ,
52+ help : Some ( format ! (
53+ "Add '{prefix_name}' to this {rule_type}, or define it globally at the top of the file"
54+ ) ) ,
55+ span,
56+ } )
57+ }
58+
4359impl PersistedBridgeConfig {
4460 pub fn expand ( self , config : & TEdgeConfig ) -> Result < Vec < ExpandedBridgeRule > , ExpandError > {
45- // TODO ensure the source variable is quoted
4661 let local_prefix = self
4762 . local_prefix
4863 . as_ref ( )
@@ -55,42 +70,76 @@ impl PersistedBridgeConfig {
5570 . transpose ( ) ?;
5671
5772 let mut expanded_rules = Vec :: new ( ) ;
58- for rule in self . rules {
73+ for spanned_rule in self . rules {
74+ let rule = spanned_rule. get_ref ( ) ;
75+ let rule_span = spanned_rule. span ( ) ;
76+
77+ let rule_local_prefix = rule
78+ . local_prefix
79+ . as_ref ( )
80+ . map ( |spanned| expand_spanned ( spanned, config, "Failed to expand local_prefix" ) )
81+ . transpose ( ) ?;
82+ let rule_remote_prefix = rule
83+ . remote_prefix
84+ . as_ref ( )
85+ . map ( |spanned| expand_spanned ( spanned, config, "Failed to expand remote_prefix" ) )
86+ . transpose ( ) ?;
87+
88+ let final_local_prefix = resolve_prefix (
89+ rule_local_prefix,
90+ & local_prefix,
91+ "local_prefix" ,
92+ "rule" ,
93+ rule_span. clone ( ) ,
94+ ) ?;
95+ let final_remote_prefix = resolve_prefix (
96+ rule_remote_prefix,
97+ & remote_prefix,
98+ "remote_prefix" ,
99+ "rule" ,
100+ rule_span,
101+ ) ?;
102+
59103 expanded_rules. push ( ExpandedBridgeRule {
60- // TODO error handle if neither per-rule or global exist
61- local_prefix : rule
62- . local_prefix
63- . as_ref ( )
64- . map ( |spanned| expand_spanned ( spanned, config, "Failed to expand local_prefix" ) )
65- . transpose ( ) ?
66- . unwrap_or_else ( || local_prefix. clone ( ) . unwrap ( ) ) ,
67- remote_prefix : rule
68- . remote_prefix
69- . as_ref ( )
70- . map ( |spanned| expand_spanned ( spanned, config, "Failed to expand remote_prefix" ) )
71- . transpose ( ) ?
72- . unwrap_or_else ( || remote_prefix. clone ( ) . unwrap ( ) ) ,
104+ local_prefix : final_local_prefix,
105+ remote_prefix : final_remote_prefix,
73106 direction : rule. direction ,
74107 topic : expand_spanned ( & rule. topic , config, "Failed to expand topic" ) ?,
75108 } ) ;
76109 }
77110
78- for template in self . template_rules {
79- let iterable = expand_spanned ( & template. r#for , config, "Failed to expand 'for' reference" ) ?;
111+ for spanned_template in self . template_rules {
112+ let template = spanned_template. get_ref ( ) ;
113+ let template_span = spanned_template. span ( ) ;
80114
81- // TODO error handle if neither per-rule or global exist
82- let local_prefix = template
115+ let iterable =
116+ expand_spanned ( & template. r#for , config, "Failed to expand 'for' reference" ) ?;
117+
118+ let template_local_prefix = template
83119 . local_prefix
84120 . as_ref ( )
85121 . map ( |spanned| expand_spanned ( spanned, config, "Failed to expand local_prefix" ) )
86- . transpose ( ) ?
87- . unwrap_or_else ( || local_prefix. clone ( ) . unwrap ( ) ) ;
88- let remote_prefix = template
122+ . transpose ( ) ?;
123+ let template_remote_prefix = template
89124 . remote_prefix
90125 . as_ref ( )
91126 . map ( |spanned| expand_spanned ( spanned, config, "Failed to expand remote_prefix" ) )
92- . transpose ( ) ?
93- . unwrap_or_else ( || remote_prefix. clone ( ) . unwrap ( ) ) ;
127+ . transpose ( ) ?;
128+
129+ let final_local_prefix = resolve_prefix (
130+ template_local_prefix,
131+ & local_prefix,
132+ "local_prefix" ,
133+ "template_rule" ,
134+ template_span. clone ( ) ,
135+ ) ?;
136+ let final_remote_prefix = resolve_prefix (
137+ template_remote_prefix,
138+ & remote_prefix,
139+ "remote_prefix" ,
140+ "template_rule" ,
141+ template_span,
142+ ) ?;
94143
95144 for topic in iterable. 0 {
96145 let template_config = TemplateConfig {
@@ -99,10 +148,14 @@ impl PersistedBridgeConfig {
99148 } ;
100149
101150 expanded_rules. push ( ExpandedBridgeRule {
102- local_prefix : local_prefix . clone ( ) ,
103- remote_prefix : remote_prefix . clone ( ) ,
151+ local_prefix : final_local_prefix . clone ( ) ,
152+ remote_prefix : final_remote_prefix . clone ( ) ,
104153 direction : template. direction ,
105- topic : expand_spanned ( & template. topic , template_config, "Failed to expand topic template" ) ?,
154+ topic : expand_spanned (
155+ & template. topic ,
156+ template_config,
157+ "Failed to expand topic template" ,
158+ ) ?,
106159 } ) ;
107160 }
108161 }
@@ -265,11 +318,14 @@ where
265318 // Try to deserialize as TOML first (for complex types like TemplatesSet)
266319 // If that fails, fall back to FromStr parsing (for simple string types)
267320 let deser = toml:: de:: ValueDeserializer :: parse ( & value) ;
268- deser. and_then ( Target :: deserialize) . or_else ( |_| value. parse ( ) ) . map_err ( |e : Target :: Err | TemplateError {
269- message : e. to_string ( ) ,
270- help : None ,
271- offset : 0 ,
272- } )
321+ deser
322+ . and_then ( Target :: deserialize)
323+ . or_else ( |_| value. parse ( ) )
324+ . map_err ( |e : Target :: Err | TemplateError {
325+ message : e. to_string ( ) ,
326+ help : None ,
327+ offset : 0 ,
328+ } )
273329 }
274330}
275331
@@ -278,8 +334,10 @@ struct TemplateConfig<'a> {
278334 r#for : & ' a str ,
279335}
280336
281- /// Helper function to expand a config key reference and return its value
282- /// Expects var_name to start with ".", strips it, and reads the config value
337+ /// Expands a tedge.toml config key and return its value
338+ ///
339+ /// Expects var_name to start with "." (to match tedge-flows config format),
340+ /// strips it, and reads the config value
283341fn expand_config_key (
284342 var_name : & str ,
285343 config : & TEdgeConfig ,
@@ -929,10 +987,16 @@ mod tests {
929987
930988 let config: PersistedBridgeConfig = toml:: from_str ( toml) . unwrap ( ) ;
931989 assert_eq ! ( config. rules. len( ) , 3 ) ;
932- assert ! ( matches!( config. rules[ 0 ] . direction, Direction :: Inbound ) ) ;
933- assert ! ( matches!( config. rules[ 1 ] . direction, Direction :: Outbound ) ) ;
934990 assert ! ( matches!(
935- config. rules[ 2 ] . direction,
991+ config. rules[ 0 ] . get_ref( ) . direction,
992+ Direction :: Inbound
993+ ) ) ;
994+ assert ! ( matches!(
995+ config. rules[ 1 ] . get_ref( ) . direction,
996+ Direction :: Outbound
997+ ) ) ;
998+ assert ! ( matches!(
999+ config. rules[ 2 ] . get_ref( ) . direction,
9361000 Direction :: Bidirectional
9371001 ) ) ;
9381002 }
@@ -1042,7 +1106,7 @@ topic = "test/topic"
10421106direction = "inbound"
10431107"# ;
10441108 let config: PersistedBridgeConfig = toml:: from_str ( toml) . unwrap ( ) ;
1045- let span = config. rules [ 0 ] . topic . span ( ) ;
1109+ let span = config. rules [ 0 ] . get_ref ( ) . topic . span ( ) ;
10461110
10471111 // serde_spanned includes the quotes
10481112 let topic_str = & toml[ span. clone ( ) ] ;
@@ -1058,7 +1122,7 @@ topic = "te/device/main///e/${@for}"
10581122direction = "outbound"
10591123"# ;
10601124 let config: PersistedBridgeConfig = toml:: from_str ( toml) . unwrap ( ) ;
1061- let span = config. template_rules [ 0 ] . r#for . span ( ) ;
1125+ let span = config. template_rules [ 0 ] . get_ref ( ) . r#for . span ( ) ;
10621126
10631127 // serde_spanned includes the quotes
10641128 let reference_str = & toml[ span. clone ( ) ] ;
@@ -1106,7 +1170,7 @@ direction = "inbound"
11061170 let tedge_config = tedge_config:: TEdgeConfig :: load_toml_str ( "" ) ;
11071171
11081172 // Save the span before we consume config
1109- let topic_span = config. rules [ 0 ] . topic . span ( ) ;
1173+ let topic_span = config. rules [ 0 ] . get_ref ( ) . topic . span ( ) ;
11101174
11111175 // This should fail on the first variable
11121176 let err = config. expand ( & tedge_config) . unwrap_err ( ) ;
0 commit comments