Skip to content

Commit 48fda8b

Browse files
committed
Improve error handling
Signed-off-by: James Rhodes <[email protected]>
1 parent 88bcb96 commit 48fda8b

File tree

1 file changed

+106
-42
lines changed
  • crates/extensions/tedge_mqtt_bridge/src

1 file changed

+106
-42
lines changed

crates/extensions/tedge_mqtt_bridge/src/config.rs

Lines changed: 106 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
4359
impl 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
283341
fn 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"
10421106
direction = "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}"
10581122
direction = "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

Comments
 (0)