parameters_lib/
parameters.rs

1
2include!(concat!(env!("OUT_DIR"), "/config.rs"));
3
4use std::env::vars;
5use regex::Regex;
6
7use crate::types::Search;
8use crate::types::Environment;
9use crate::types::Instance;
10use crate::types::EnvsMap;
11
12pub struct Parameters {
13    regexp: Regex,
14    search: Search,
15    env_name: Environment,
16    instance: Instance,
17    no_header: bool,
18    // is_quiet: bool,
19}
20
21impl Parameters {
22    pub fn new(regexp: String, search: String, env_name: Environment, instance: Instance, no_header: bool) -> Self {
23        #[cfg(debug_assertions)]
24        println!("-> Parameters::new({}, {}, e={:?}, i={:?}, nh={})", regexp, search, env_name, instance, no_header);
25
26        Self {
27            regexp: Regex::new(&regexp).expect("Invalid regexp"),
28            search: search,
29            env_name: env_name,
30            instance: instance,
31            no_header: no_header,
32        }
33    }
34
35    pub fn process(&self, input: &String) -> String {
36        if cfg!(debug_assertions) {
37            println!("-> Parameters::process()");
38            println!("-> input: '{}'", input); println!();
39        }
40
41        let mut output: String = String::from(input);
42
43        // Collect relevant environment variables.
44        let mut env_vars: EnvsMap = EnvsMap::new();
45        for (ename, evalue) in vars() {
46            if self.regexp.is_match(&ename) {
47                env_vars.insert(ename, evalue);
48            }
49        }
50
51        for (ename, evalue) in env_vars.iter().rev() {
52            #[cfg(debug_assertions)]
53            println!("-> var '{}': '{}'", ename, evalue);
54
55            // Copy for mut.
56            let mut enamec = String::from(ename);
57
58            // Instance
59            if let Some(_instance) = &self.instance {
60                #[cfg(debug_assertions)]
61                println!("  -> instance is some:  '{}'", _instance);
62
63                let sub_instance = &enamec[(enamec.len() - _instance.len())..];
64
65                #[cfg(debug_assertions)]
66                println!("  -> sub instance: '{}'", sub_instance);
67
68                if _instance == sub_instance {
69                    let _end = enamec.len() - _instance.len() - 1;
70                    enamec = String::from(&enamec[0.._end]);
71
72                    #[cfg(debug_assertions)]
73                    println!("  -> new enamec: '{}'", enamec);
74                }
75            }
76
77            // Environment
78            if let Some(_env_name) = &self.env_name {
79                #[cfg(debug_assertions)]
80                println!("  -> env is some: '{}'", _env_name);
81
82                let sub_env = &enamec[(enamec.len() - _env_name.len())..];
83
84                #[cfg(debug_assertions)]
85                println!("  -> sub env: '{}'", sub_env);
86
87                if _env_name == sub_env {
88                    let _end = enamec.len() - _env_name.len() - 1;
89                    enamec = String::from(&enamec[0.._end]);
90
91                    #[cfg(debug_assertions)]
92                    println!("  -> new enamec: '{}'", enamec);
93                }
94            }
95
96            let mut tpl_var: String = String::from(&self.search);
97            tpl_var.push_str(&enamec);
98            tpl_var.push_str(&self.search);
99
100            #[cfg(debug_assertions)]
101            println!("  -> template variable: '{}'", tpl_var);
102
103            output = output.replace(&tpl_var, &evalue);
104
105            if cfg!(debug_assertions) {
106                println!("  -> output: '{}'", output);
107                println!();
108            }
109        }
110
111        if cfg!(debug_assertions) {
112            println!();
113            println!("-> end output: '{}'", output);
114        }
115
116        if self.no_header {
117            // Raw out
118            output
119        } else {
120            // Add header
121            format!("# Rendered by {} v{} @ {}\n\n{}", APP_NAME, APP_VERSION, chrono::Local::now().format("%Y-%m-%d %H:%M:%S %Z"), output)
122        }
123    }
124}
125
126#[cfg(test)]
127mod tests_sub_string {
128    #[test]
129    fn basic_test() {
130        assert!(true);
131    }
132
133    #[test]
134    fn test_substring1() {
135        let s1 = String::from("abcefg");
136        assert_eq!("ab", &s1[0..2]);
137    }
138
139    #[test]
140    fn test_substring2() {
141        let s1 = String::from("abcefg");
142        assert_eq!("ab", &s1[..2]);
143    }
144
145    #[test]
146    fn test_substring3() {
147        let s1 = String::from("abcefg");
148        assert_eq!("cefg", &s1[2..]);
149    }
150
151    #[test]
152    fn test_substring4() {
153        let s2 = String::from("cefg");
154        let s1 = String::from("abcefg");
155        assert_eq!("cefg", &s1[(s1.len() - s2.len())..]);
156    }
157}
158
159#[cfg(test)]
160mod tests_parameters {
161    use super::Parameters;
162    use crate::types::Search;
163    use crate::types::Environment;
164    use crate::types::Instance;
165
166    #[test]
167    fn test_parameters_single() {
168        let regexp: String = "^SYMF_".into();
169        let search: String = "@".into();
170        let input: String = "DB_USER=_@SYMF_DB_USER@#".into();
171
172        let p1 = Parameters::new(regexp, search, None, None, true);
173        assert_eq!("DB_USER=_user1#", p1.process(&input));
174    }
175
176    type SS = &'static str;
177    type BasicDto = (SS, SS, SS, SS);
178
179    #[test]
180    fn test_parameters_basic() {
181        let _data: Vec<BasicDto> = vec![
182            // Variable
183            ("^SYMF_", "@", "DB_USER=@SYMF_DB_USER@", "DB_USER=user1"),
184            ("^SYMF_", "@", "DB_USER=_@SYMF_DB_USER@#", "DB_USER=_user1#"),
185            ("^SYMF_", "@", "DB_USER=/@SYMF_DB_USER@/", "DB_USER=/user1/"),
186
187            ("^SYMF_", "@", "DB_PASS=@SYMF_DB_PASS@", "DB_PASS=password1"),
188            ("^SYMF_", "@", "DB_PASS=_B_@SYMF_DB_PASS@_E_", "DB_PASS=_B_password1_E_"),
189            ("^SYMF_", "@", "DB_PASS=/@SYMF_DB_PASS@/@SYMF_DB_PASS@/", "DB_PASS=/password1/password1/"),
190
191            // Exact Environment
192            ("^SYMF_", "@", "DB_PASS1=@SYMF_DB_PASS@/DB_PASS2=@SYMF_DB_PASS_PRODUCTION@", "DB_PASS1=password1/DB_PASS2=password2"),
193
194            // Exact Instance
195            ("^SYMF_", "@", "DB_PASS1=@SYMF_DB_PASS@/DB_PASS2=@SYMF_DB_PASS_PRODUCTION@/DB_PASS3=@SYMF_DB_PASS_PRODUCTION_INSTANCE1@", "DB_PASS1=password1/DB_PASS2=password2/DB_PASS3=password3"),
196
197            // Non-existing
198            ("^SYMF_", "@", "DB_PASS=/@SYMF_XYZ@/", "DB_PASS=/@SYMF_XYZ@/"),
199            ("^TEST_", "@", "DB_PASS=/@SYMF_DB_USER@/", "DB_PASS=/@SYMF_DB_USER@/"),
200        ];
201
202        for _t in _data {
203            let regexp: String = _t.0.into();
204            let search: Search = _t.1.into();
205            let input: String = _t.2.into();
206
207            let p1 = Parameters::new(regexp, search, None, None, true);
208            assert_eq!(_t.3, p1.process(&input));
209        }
210    }
211
212    type EnvDto = (SS, SS, SS, SS, SS);
213
214    #[test]
215    fn test_parameters_environment() {
216        let _data: Vec<EnvDto> = vec![
217            // Environment
218            ("^SYMF_", "@", "PRODUCTION", "DB_PASS=@SYMF_DB_PASS@", "DB_PASS=password2"),
219
220            // Fall-back to basic Variable
221            ("^SYMF_", "@", "ABC", "DB_PASS=@SYMF_DB_PASS_PRODUCTION@", "DB_PASS=password2"),
222
223            // Non-machting Environment
224            ("^SYMF_", "@", "ABC", "DB_PASS=@SYMF_DB_PASS@", "DB_PASS=password1"),
225
226            // Non-existing Environment
227            ("^SYMF_", "@", "ABC", "DB_PASS=@SYMF_DB_PASS_XYZ@", "DB_PASS=@SYMF_DB_PASS_XYZ@"),
228
229            // No fall-back
230            ("^SYMF_", "@", "PRODUCTION", "DB_NAME=@SYMF_DB_NAME@", "DB_NAME=password5b"),
231            ("^SYMF_", "@", "ABC", "DB_NAME=@SYMF_DB_NAME@", "DB_NAME=@SYMF_DB_NAME@"),
232        ];
233
234        for _t in _data {
235            let regexp: String = _t.0.into();
236            let search: Search = _t.1.into();
237            let env_name: Environment = Some(_t.2.into());
238            let input: String = _t.3.into();
239
240            let p1 = Parameters::new(regexp, search, env_name, None, true);
241            assert_eq!(_t.4, p1.process(&input));
242        }
243    }
244
245    type InstanceDto = (SS, SS, SS, SS, SS, SS);
246
247    #[test]
248    fn test_parameters_instance() {
249        let _data: Vec<InstanceDto> = vec![
250            // Instance
251            ("^SYMF_", "@", "PRODUCTION", "INSTANCE1", "DB_PASS=@SYMF_DB_PASS@", "DB_PASS=password3"),
252            ("^SYMF_", "@", "PRODUCTION", "INSTANCE2", "DB_PASS=@SYMF_DB_PASS@", "DB_PASS=password4"),
253
254            // Non-machting Instance
255            ("^SYMF_", "@", "PRODUCTION", "ABC", "DB_PASS=@SYMF_DB_PASS_PRODUCTION_INSTANCE1@", "DB_PASS=password3"),
256
257            // Fall-back to Environment
258            ("^SYMF_", "@", "PRODUCTION", "ABC", "DB_PASS=@SYMF_DB_PASS@", "DB_PASS=password2"),
259
260            // Non-existing Instance
261            ("^SYMF_", "@", "PRODUCTION", "ABC", "DB_PASS=@SYMF_DB_PASS_PRODUCTION@", "DB_PASS=@SYMF_DB_PASS_PRODUCTION@"),
262        ];
263
264        for _t in _data {
265            let regexp: String = _t.0.into();
266            let search: Search = _t.1.into();
267            let env_name: Environment = Some(_t.2.into());
268            let instance: Instance = Some(_t.3.into());
269            let input: String = _t.4.into();
270
271            let p1 = Parameters::new(regexp, search, env_name, instance, true);
272            assert_eq!(_t.5, p1.process(&input));
273        }
274    }
275}