nym_config/
helpers.rs

1// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
2// SPDX-License-Identifier: Apache-2.0
3
4use nym_network_defaults::mainnet::read_var_if_not_default;
5use nym_network_defaults::var_names::CONFIGURED;
6use std::any::type_name;
7use std::fmt::Debug;
8use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
9use std::str::FromStr;
10
11pub const MISSING_VALUE: &str = "MISSING VALUE";
12
13/// Helper for providing default value for templated config fields.
14pub fn missing_string_value<T: From<String>>() -> T {
15    MISSING_VALUE.to_string().into()
16}
17
18/// Helper for providing default INADDR_ANY IpAddr, i.e. `0.0.0.0`
19pub fn inaddr_any() -> IpAddr {
20    IpAddr::V4(Ipv4Addr::UNSPECIFIED)
21}
22
23/// Helper for providing default IN6ADDR_ANY_INIT IpAddr, i.e. `::`
24pub fn in6addr_any_init() -> IpAddr {
25    IpAddr::V6(Ipv6Addr::UNSPECIFIED)
26}
27
28// TODO: is it really part of 'Config'?
29pub trait OptionalSet {
30    /// If the value is available (i.e. `Some`), the provided closure is applied.
31    /// Otherwise `self` is returned with no modifications.
32    fn with_optional<F, T>(self, f: F, val: Option<T>) -> Self
33    where
34        F: Fn(Self, T) -> Self,
35        Self: Sized,
36    {
37        if let Some(val) = val {
38            f(self, val)
39        } else {
40            self
41        }
42    }
43
44    /// If the value is available (i.e. `Some`) it is validated and then the provided closure is applied.
45    /// Otherwise `self` is returned with no modifications.
46    fn with_validated_optional<F, T, V, E>(
47        self,
48        f: F,
49        value: Option<T>,
50        validate: V,
51    ) -> Result<Self, E>
52    where
53        F: Fn(Self, T) -> Self,
54        V: Fn(&T) -> Result<(), E>,
55        Self: Sized,
56    {
57        if let Some(val) = value {
58            validate(&val)?;
59            Ok(f(self, val))
60        } else {
61            Ok(self)
62        }
63    }
64
65    /// If the value is available (i.e. `Some`), the provided closure is applied.
66    /// Otherwise, if the environment was configured and the corresponding variable was set,
67    /// the value is parsed using the `FromStr` implementation and the closure is applied on that instead.
68    /// Finally, if none of those were available, `self` is returned with no modifications.
69    fn with_optional_env<F, T>(self, f: F, val: Option<T>, env_var: &str) -> Self
70    where
71        F: Fn(Self, T) -> Self,
72        T: FromStr,
73        <T as FromStr>::Err: Debug,
74        Self: Sized,
75    {
76        if let Some(val) = val {
77            return f(self, val);
78        } else if std::env::var(CONFIGURED).is_ok() {
79            if let Some(raw) = read_var_if_not_default(env_var) {
80                return f(
81                    self,
82                    raw.parse().unwrap_or_else(|err| {
83                        panic!(
84                            "failed to parse value of {raw} into type {}. the error was {:?}",
85                            type_name::<T>(),
86                            err
87                        )
88                    }),
89                );
90            }
91        }
92        self
93    }
94
95    /// If the value is available (i.e. `Some`), the provided closure is applied.
96    /// Otherwise, if the environment was configured and the corresponding variable was set,
97    /// the value is parsed using the provided parser and the closure is applied on that instead.
98    /// Finally, if none of those were available, `self` is returned with no modifications.
99    fn with_optional_custom_env<F, T, G>(
100        self,
101        f: F,
102        val: Option<T>,
103        env_var: &str,
104        parser: G,
105    ) -> Self
106    where
107        F: Fn(Self, T) -> Self,
108        G: Fn(&str) -> T,
109        Self: Sized,
110    {
111        if let Some(val) = val {
112            return f(self, val);
113        } else if std::env::var(CONFIGURED).is_ok() {
114            if let Some(raw) = read_var_if_not_default(env_var) {
115                return f(self, parser(&raw));
116            }
117        }
118        self
119    }
120}
121
122// helper for when we want to use `OptionalSet` on an inner field
123// (used by clients wanting to set the `BaseConfig` values)
124#[macro_export]
125macro_rules! define_optional_set_inner {
126    ( $x: ident, $inner_field_name: ident, $inner_field_typ: ty ) => {
127        impl $x {
128            pub fn with_optional_inner<F, T>(mut self, f: F, val: Option<T>) -> Self
129            where
130                F: Fn($inner_field_typ, T) -> $inner_field_typ,
131            {
132                self.$inner_field_name = self.$inner_field_name.with_optional(f, val);
133                self
134            }
135
136            pub fn with_validated_optional_inner<F, T, V, E>(
137                mut self,
138                f: F,
139                value: Option<T>,
140                validate: V,
141            ) -> Result<Self, E>
142            where
143                F: Fn($inner_field_typ, T) -> $inner_field_typ,
144                V: Fn(&T) -> Result<(), E>,
145            {
146                self.$inner_field_name = self
147                    .$inner_field_name
148                    .with_validated_optional(f, value, validate)?;
149                Ok(self)
150            }
151
152            pub fn with_optional_env_inner<F, T>(
153                mut self,
154                f: F,
155                val: Option<T>,
156                env_var: &str,
157            ) -> Self
158            where
159                F: Fn($inner_field_typ, T) -> $inner_field_typ,
160                T: FromStr,
161                <T as FromStr>::Err: Debug,
162            {
163                self.$inner_field_name = self.$inner_field_name.with_optional_env(f, val, env_var);
164                self
165            }
166
167            pub fn with_optional_custom_env_inner<F, T, G>(
168                mut self,
169                f: F,
170                val: Option<T>,
171                env_var: &str,
172                parser: G,
173            ) -> Self
174            where
175                F: Fn($inner_field_typ, T) -> $inner_field_typ,
176                G: Fn(&str) -> T,
177            {
178                self.$inner_field_name = self
179                    .$inner_field_name
180                    .with_optional_custom_env(f, val, env_var, parser);
181                self
182            }
183        }
184    };
185}
186
187// this function is only used for parsing values from the network defaults and thus the "expect" there are fine
188pub fn parse_urls(raw: &str) -> Vec<url::Url> {
189    raw.split(',')
190        .map(|raw_url| {
191            raw_url
192                .trim()
193                .parse()
194                .expect("one of the provided urls was invalid")
195        })
196        .collect()
197}
198
199impl<T> OptionalSet for T {}