Skip to content

Commit abe063b

Browse files
committed
Add allow_unconnected_components config option
When `true`, the graph will be considered valid even if it has unconnected components that are not reachable from the root. Signed-off-by: Sahas Subramanian <[email protected]>
1 parent f386756 commit abe063b

File tree

5 files changed

+50
-12
lines changed

5 files changed

+50
-12
lines changed

src/config.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
/// Configuration options for the `ComponentGraph`.
77
#[derive(Clone, Default, Debug)]
88
pub struct ComponentGraphConfig {
9+
/// Whether to allow unconnected components in the graph, that are not
10+
/// reachable from the root.
11+
pub allow_unconnected_components: bool,
12+
913
/// Whether to allow untyped inverters in the graph. When this is `true`,
1014
/// inverters that have `InverterType::Unspecified` will be assumed to be
1115
/// Battery inverters.

src/graph/creation.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,8 @@ mod tests {
175175
// are treated as battery inverters.
176176
assert!(builder
177177
.build(Some(ComponentGraphConfig {
178-
allow_unspecified_inverters: true
178+
allow_unspecified_inverters: true,
179+
..Default::default()
179180
}))
180181
.is_ok());
181182

src/graph/formulas/generators/battery.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ mod tests {
206206

207207
let graph = builder.build(Some(ComponentGraphConfig {
208208
allow_unspecified_inverters: true,
209+
..Default::default()
209210
}))?;
210211
let formula = graph.battery_formula(None)?;
211212
assert_eq!(

src/graph/validation.rs

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,14 @@ where
3838
validator.validate_acyclicity(root, vec![])?;
3939

4040
let mut errors = vec![];
41+
let mut validation_failed = false;
42+
43+
if let Err(err) = validator.validate_connected_graph(root) {
44+
errors.push(err);
45+
validation_failed = !self.config.allow_unconnected_components;
46+
}
47+
4148
for result in [
42-
validator.validate_connected_graph(root),
4349
validator.validate_root(),
4450
validator.validate_meters(),
4551
validator.validate_inverters(),
@@ -49,18 +55,33 @@ where
4955
] {
5056
if let Err(e) = result {
5157
errors.push(e);
58+
validation_failed = true;
5259
}
5360
}
54-
if errors.len() == 1 {
55-
return Err(errors[0].clone());
56-
} else if !errors.is_empty() {
57-
let error_messages = "Multiple validation failures:\n ".to_string()
58-
+ &errors
59-
.into_iter()
60-
.map(|e| e.to_string())
61-
.collect::<Vec<_>>()
62-
.join("\n ");
63-
return Err(Error::invalid_graph(error_messages));
61+
match errors.len() {
62+
0 => {}
63+
1 => {
64+
if validation_failed {
65+
return Err(errors[0].clone());
66+
} else {
67+
tracing::warn!("{}", errors[0]);
68+
}
69+
}
70+
_ => {
71+
let err = Error::invalid_graph(format!(
72+
"Multiple validation failures:\n {}",
73+
errors
74+
.iter()
75+
.map(ToString::to_string)
76+
.collect::<Vec<_>>()
77+
.join("\n ")
78+
));
79+
if validation_failed {
80+
return Err(err);
81+
} else {
82+
tracing::warn!("{}", err);
83+
}
84+
}
6485
}
6586
Ok(())
6687
}

src/graph/validation/validate_graph.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,23 @@ mod tests {
152152

153153
connections.push(TestConnection::new(11, 12));
154154

155+
// With the default config, this fails validation.
155156
assert!(
156157
ComponentGraph::try_new(components.clone(), connections.clone(), config.clone())
157158
.is_err_and(
158159
|e| e == Error::invalid_graph("Nodes [11, 12] are not connected to the root.")
159160
)
160161
);
162+
// With `allow_unconnected_components=true`, this passes validation.
163+
assert!(ComponentGraph::try_new(
164+
components.clone(),
165+
connections.clone(),
166+
ComponentGraphConfig {
167+
allow_unconnected_components: true,
168+
..config.clone()
169+
}
170+
)
171+
.is_ok());
161172

162173
connections.pop();
163174
components.pop();

0 commit comments

Comments
 (0)