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 used_runtime_keys = node_config.actor.runtime_keys();
30 let runtime_keys = node_config.runtime.runtime_keys().collect();
31
32 let runtime_keys_validation_error_opt = node_config
33 .actor
34 .ensure_runtime_keys_are_valid(&runtime_keys)
35 .err();
36
37 let runtime_time_validation_errors = node_config
38 .runtime
39 .time_driver_validation_errors(&used_runtime_keys);
40
41 let exactly_one_auto_network_error_opt = (node_config
42 .local_subnets
43 .iter()
44 .filter(|d| matches!(d.kind, LocalSubnetKind::Auto))
45 .count()
46 != 1)
47 .then_some("exactly one local-subnet with kind:auto must be defined".into());
48
49 let overlapping_local_nets = {
50 let mut nets = BTreeSet::<AddressRange>::new();
51 let mut conflicts = vec![];
52
53 for this in node_config.local_subnets.iter().map(|d| d.net) {
54 if let Some(that) = nets
55 .get(&AddressRange::from(this))
56 .map(|r| NetAddress::from(*r))
57 {
58 conflicts.push(format!("{} overlaps with {}", this, that));
59 } else {
60 nets.insert(this.into());
61 }
62 }
63 conflicts
64 };
65
66 let errors = runtime_keys_validation_error_opt
67 .into_iter()
68 .chain(runtime_time_validation_errors)
69 .chain(exactly_one_auto_network_error_opt)
70 .chain(overlapping_local_nets)
71 .collect::<Vec<_>>();
72
73 if errors.is_empty() {
74 Ok(Valid(node_config))
75 } else {
76 Err(ValidationError {
77 errors,
78 rejected: Box::new(node_config),
79 })
80 }
81 }
82}
83
84impl<C> Deref for Valid<C> {
85 type Target = C;
86
87 fn deref(&self) -> &Self::Target {
88 &self.0
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use crate::config::validation::Valid;
95
96 #[test]
97 fn ergonomics() {
98 struct Config {
99 field: u32,
100 }
101 impl Config {
102 fn method(&self) -> u32 {
103 self.field
104 }
105 }
106
107 let valid_value = Valid(Config { field: 42 });
108 assert_eq!(valid_value.field, 42);
109 assert_eq!(valid_value.method(), 42);
110
111 let valid_reference = Valid(&Config { field: 13 });
112 assert_eq!(valid_reference.field, 13);
113 assert_eq!(valid_reference.method(), 13);
114 }
115}