mm1_node/config/
validation.rs1use std::collections::BTreeSet;
2use std::ops::Deref;
3
4use mm1_address::address_range::AddressRange;
5use mm1_address::subnet::NetAddress;
6
7use crate::config::{LocalSubnetKind, Mm1NodeConfig};
8
9#[derive(Debug, Clone)]
10pub struct Valid<C>(C);
11
12#[derive(Debug, thiserror::Error)]
13#[error("validation error:\n{}", errors.iter().map(ToString::to_string).collect::<Vec<_>>().join("\n-"))]
14pub struct ValidationError<T> {
15 pub errors: Vec<String>,
16 pub rejected: Box<T>,
17}
18
19impl Mm1NodeConfig {
20 pub fn validate(self) -> Result<Valid<Self>, ValidationError<Self>> {
21 TryFrom::try_from(self)
22 }
23}
24
25impl TryFrom<Mm1NodeConfig> for Valid<Mm1NodeConfig> {
26 type Error = ValidationError<Mm1NodeConfig>;
27
28 fn try_from(node_config: Mm1NodeConfig) -> Result<Self, Self::Error> {
29 let runtime_keys = node_config.runtime.runtime_keys().collect();
30
31 let runtime_keys_validation_error_opt = node_config
32 .actor
33 .ensure_runtime_keys_are_valid(&runtime_keys)
34 .err();
35
36 let exactly_one_auto_network_error_opt = (node_config
37 .local_subnets
38 .iter()
39 .filter(|d| matches!(d.kind, LocalSubnetKind::Auto))
40 .count()
41 != 1)
42 .then_some("exactly one local-subnet with kind:auto must be defined".into());
43
44 let overlapping_local_nets = {
45 let mut nets = BTreeSet::<AddressRange>::new();
46 let mut conflicts = vec![];
47
48 for this in node_config.local_subnets.iter().map(|d| d.net) {
49 if let Some(that) = nets
50 .get(&AddressRange::from(this))
51 .map(|r| NetAddress::from(*r))
52 {
53 conflicts.push(format!("{} overlaps with {}", this, that));
54 } else {
55 nets.insert(this.into());
56 }
57 }
58 conflicts
59 };
60
61 let errors = runtime_keys_validation_error_opt
62 .into_iter()
63 .chain(exactly_one_auto_network_error_opt)
64 .chain(overlapping_local_nets)
65 .collect::<Vec<_>>();
66
67 if errors.is_empty() {
68 Ok(Valid(node_config))
69 } else {
70 Err(ValidationError {
71 errors,
72 rejected: Box::new(node_config),
73 })
74 }
75 }
76}
77
78impl<C> Deref for Valid<C> {
79 type Target = C;
80
81 fn deref(&self) -> &Self::Target {
82 &self.0
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use crate::config::validation::Valid;
89
90 #[test]
91 fn ergonomics() {
92 struct Config {
93 field: u32,
94 }
95 impl Config {
96 fn method(&self) -> u32 {
97 self.field
98 }
99 }
100
101 let valid_value = Valid(Config { field: 42 });
102 assert_eq!(valid_value.field, 42);
103 assert_eq!(valid_value.method(), 42);
104
105 let valid_reference = Valid(&Config { field: 13 });
106 assert_eq!(valid_reference.field, 13);
107 assert_eq!(valid_reference.method(), 13);
108 }
109}