1pub(crate) mod none_string_as_none {
9 use serde::{Deserialize, Deserializer};
10
11 pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
12 where
13 D: Deserializer<'de>,
14 T: Deserialize<'de>,
15 {
16 #[derive(Deserialize)]
19 #[serde(untagged)]
20 enum StringOrT<T> {
21 String(String),
22 T(T),
23 }
24
25 match Option::<StringOrT<T>>::deserialize(deserializer)? {
26 None => Ok(None),
27 Some(StringOrT::String(s)) if s.eq_ignore_ascii_case("none") => Ok(None),
28 Some(StringOrT::String(s)) => {
29 Err(serde::de::Error::custom(format!(
32 "unexpected string value: {s}"
33 )))
34 }
35 Some(StringOrT::T(v)) => Ok(Some(v)),
36 }
37 }
38}
39
40pub mod env_var {
41 use regex::Regex;
42 use serde::{Deserialize, Deserializer};
43 use std::net::SocketAddr;
44 use std::path::PathBuf;
45
46 #[derive(Deserialize, PartialEq)]
47 struct WrappedString(String);
48
49 pub fn str<'de, D>(deserializer: D) -> Result<String, D::Error>
50 where
51 D: Deserializer<'de>,
52 {
53 let value = String::deserialize(deserializer)?;
54
55 match is_env_var(&value) {
56 true => Ok(get_env_var(&value)),
57 false => Ok(value),
58 }
59 }
60
61 pub fn opt_str<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
62 where
63 D: Deserializer<'de>,
64 {
65 Option::<WrappedString>::deserialize(deserializer).map(|option: Option<WrappedString>| {
66 option.map(|wrapped: WrappedString| {
67 let value = wrapped.0;
68
69 match is_env_var(&value) {
70 true => get_env_var(&value),
71 false => value,
72 }
73 })
74 })
75 }
76
77 pub fn socket_addr<'de, D>(deserializer: D) -> Result<SocketAddr, D::Error>
78 where
79 D: Deserializer<'de>,
80 {
81 let value = String::deserialize(deserializer)?;
82
83 match is_env_var(&value) {
84 true => Ok(get_env_var(&value).parse().unwrap()),
85 false => Ok(value.parse().unwrap()),
86 }
87 }
88
89 pub fn path_buf<'de, D>(deserializer: D) -> Result<PathBuf, D::Error>
90 where
91 D: Deserializer<'de>,
92 {
93 let value = String::deserialize(deserializer)?;
94
95 match is_env_var(&value) {
96 true => Ok(PathBuf::from(get_env_var(&value))),
97 false => Ok(PathBuf::from(value)),
98 }
99 }
100
101 fn is_env_var(value: &str) -> bool {
102 Regex::new(r"^\$\{env\.\w+\}$")
103 .expect("env_var: regex is invalid")
104 .is_match(value)
105 }
106
107 fn get_env_var(wrapped_key: &str) -> String {
108 let key: String = String::from(wrapped_key)
109 .drain(6..(wrapped_key.len() - 1))
110 .collect();
111
112 std::env::var(&key).unwrap_or_else(|_| panic!("env_var: variable '{key}' is not set"))
119 }
120
121 #[cfg(test)]
122 mod tests {
123 use super::*;
124
125 #[test]
126 fn it_checks_environment_variable_patterns() {
127 assert!(is_env_var("${env.XXX}"));
128 assert!(!is_env_var("${env.XXX"));
129 assert!(!is_env_var("${env.XXX}a"));
130 assert!(!is_env_var("a${env.XXX}"));
131 assert!(!is_env_var("{env.XXX}"));
132 assert!(!is_env_var("$env.XXX}"));
133 assert!(!is_env_var("${envXXX}"));
134 assert!(!is_env_var("${.XXX}"));
135 assert!(!is_env_var("${XXX}"));
136 }
137
138 #[test]
139 fn it_gets_environment_variable() {
140 unsafe { std::env::set_var("TEST", "test") };
141
142 assert_eq!(get_env_var("${env.TEST}"), "test");
143
144 unsafe { std::env::remove_var("TEST") };
145 }
146 }
147}