mm1_node/config/
validation.rs

1use 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}