rosrust/api/
resolve.rs

1use std::{self, env};
2
3pub fn master() -> String {
4    if let Some(v) = find_with_prefix("__master:=") {
5        return v;
6    }
7    env::var("ROS_MASTER_URI").unwrap_or_else(|_| String::from("http://localhost:11311/"))
8}
9
10pub fn hostname() -> String {
11    if let Some(v) = find_with_prefix("__hostname:=") {
12        return v;
13    }
14    if let Some(v) = find_with_prefix("__ip:=") {
15        return v;
16    }
17    if let Ok(v) = env::var("ROS_HOSTNAME") {
18        return v;
19    }
20    if let Ok(v) = env::var("ROS_IP") {
21        return v;
22    }
23    system_hostname()
24}
25
26pub fn namespace() -> String {
27    if let Some(v) = find_with_prefix("__ns:=") {
28        return v;
29    }
30    env::var("ROS_NAMESPACE").unwrap_or_default()
31}
32
33pub fn name(default: &str) -> String {
34    find_with_prefix("__name:=").unwrap_or_else(|| String::from(default))
35}
36
37pub fn mappings() -> Vec<(String, String)> {
38    args()
39        .skip(1)
40        .filter(|v| !v.starts_with('_'))
41        .map(|v| v.split(":=").map(String::from).collect::<Vec<String>>())
42        .filter_map(|v| match &v[..] {
43            [key, value] => Some((key.clone(), value.clone())),
44            _ => None,
45        })
46        .collect()
47}
48
49pub fn params() -> Vec<(String, String)> {
50    args()
51        .skip(1)
52        .filter(|v| v.starts_with('_'))
53        .filter(|v| !v.starts_with("__"))
54        .map(|v| v.splitn(2, ":=").map(String::from).collect::<Vec<String>>())
55        .filter_map(|v| match &v[..] {
56            [key, value] => Some((key.replacen('_', "~", 1), value.clone())),
57            _ => None,
58        })
59        .collect()
60}
61
62pub fn get_unused_args() -> Vec<String> {
63    args()
64        .enumerate()
65        .filter_map(|(idx, v)| {
66            if idx == 0 || !v.contains(":=") {
67                Some(v)
68            } else {
69                None
70            }
71        })
72        .collect()
73}
74
75fn find_with_prefix(prefix: &str) -> Option<String> {
76    args()
77        .skip(1)
78        .find(|v| v.starts_with(prefix))
79        .map(|v| String::from(v.trim_start_matches(prefix)))
80}
81
82#[cfg(not(test))]
83fn system_hostname() -> String {
84    ::hostname::get()
85        .expect("Unable to retrieve hostname from the system.")
86        .to_string_lossy()
87        .into_owned()
88}
89
90#[cfg(test)]
91fn system_hostname() -> String {
92    String::from("myhostname")
93}
94
95#[cfg(not(test))]
96#[inline]
97fn args() -> std::env::Args {
98    env::args()
99}
100
101#[cfg(test)]
102#[inline]
103fn args() -> std::vec::IntoIter<String> {
104    tests::args_mock()
105}
106
107#[cfg(test)]
108mod tests {
109    use lazy_static::lazy_static;
110
111    use super::*;
112    use crate::util::FAILED_TO_LOCK;
113    use std::sync::Mutex;
114    use std::{self, env};
115
116    lazy_static! {
117        static ref DATA: Mutex<Vec<String>> = Mutex::new(Vec::new());
118        static ref TESTCASE: Mutex<()> = Mutex::new(());
119    }
120
121    pub fn args_mock() -> std::vec::IntoIter<String> {
122        DATA.lock().expect(FAILED_TO_LOCK).clone().into_iter()
123    }
124
125    fn set_args(args: &[&str]) {
126        let mut data = DATA.lock().expect(FAILED_TO_LOCK);
127        data.clear();
128        data.push(String::from("IGNORE"));
129        for &arg in args {
130            data.push(String::from(arg));
131        }
132    }
133
134    #[test]
135    #[allow(unused_variables)]
136    fn mappings_default_to_empty_vector() {
137        let testcase = TESTCASE.lock().expect(FAILED_TO_LOCK);
138        set_args(&[]);
139        assert_eq!(Vec::<(String, String)>::new(), mappings());
140    }
141
142    #[test]
143    #[allow(unused_variables)]
144    fn mappings_maps_good_and_ignores_everything_else() {
145        let testcase = TESTCASE.lock().expect(FAILED_TO_LOCK);
146        set_args(&[]);
147        assert_eq!(Vec::<(String, String)>::new(), mappings());
148        set_args(&[
149            "a:=x",
150            "b=e",
151            "/c:=d",
152            "e:=/f_g",
153            "__name:=something",
154            "_param:=something",
155            "a:=b:=c",
156            "~oo_e:=/ab_c",
157            "/x_y:=~i",
158        ]);
159        assert_eq!(
160            vec![
161                (String::from("a"), String::from("x")),
162                (String::from("/c"), String::from("d")),
163                (String::from("e"), String::from("/f_g")),
164                (String::from("~oo_e"), String::from("/ab_c")),
165                (String::from("/x_y"), String::from("~i")),
166            ],
167            mappings()
168        );
169    }
170
171    #[test]
172    #[allow(unused_variables)]
173    fn params_default_to_empty_vector() {
174        let testcase = TESTCASE.lock().expect(FAILED_TO_LOCK);
175        set_args(&[]);
176        assert_eq!(Vec::<(String, String)>::new(), params());
177    }
178
179    #[test]
180    #[allow(unused_variables)]
181    fn params_maps_good_and_ignores_everything_else() {
182        let testcase = TESTCASE.lock().expect(FAILED_TO_LOCK);
183        set_args(&[]);
184        assert_eq!(Vec::<(String, String)>::new(), params());
185        set_args(&[
186            "a:=x",
187            "b=e",
188            "/c:=d",
189            "e:=/f_g",
190            "__name:=something",
191            "_param:=something",
192            "a:=b:=c",
193            "~oo_e:=/ab_c",
194            "_foo:=123:=456",
195            "/x_y:=~i",
196        ]);
197        assert_eq!(
198            vec![
199                (String::from("~param"), String::from("something")),
200                (String::from("~foo"), String::from("123:=456")),
201            ],
202            params()
203        );
204    }
205
206    #[test]
207    #[allow(unused_variables)]
208    fn get_unused_args_gets_everything_without_equal_sign() {
209        let testcase = TESTCASE.lock().expect(FAILED_TO_LOCK);
210        set_args(&[]);
211        assert_eq!(vec![String::from("IGNORE")], get_unused_args());
212        set_args(&[
213            "a:=x",
214            "b=e",
215            "/c:=d",
216            "this",
217            "e:=/f_g",
218            "__name:=something",
219            "_param:=something",
220            "a:=b:=c",
221            "foo",
222            "~oo_e:=/ab_c",
223            "_foo:=123:=456",
224            "bar=:baz",
225            "/x_y:=~i",
226        ]);
227        assert_eq!(
228            vec![
229                String::from("IGNORE"),
230                String::from("b=e"),
231                String::from("this"),
232                String::from("foo"),
233                String::from("bar=:baz"),
234            ],
235            get_unused_args()
236        );
237    }
238
239    #[test]
240    #[allow(unused_variables)]
241    fn name_uses_passed_value_by_default() {
242        let testcase = TESTCASE.lock().expect(FAILED_TO_LOCK);
243        set_args(&[]);
244        assert_eq!(String::from("myname"), name("myname"));
245        set_args(&["unimportant", "also_unimportant"]);
246        assert_eq!(String::from("othername"), name("othername"));
247    }
248
249    #[test]
250    #[allow(unused_variables)]
251    fn name_uses_argument_when_provided() {
252        let testcase = TESTCASE.lock().expect(FAILED_TO_LOCK);
253        set_args(&[]);
254        assert_eq!(String::from("myname"), name("myname"));
255        set_args(&["__name:=othername"]);
256        assert_eq!(String::from("othername"), name("myname"));
257    }
258
259    #[test]
260    #[allow(unused_variables)]
261    fn namespace_uses_empty_string_by_default() {
262        let testcase = TESTCASE.lock().expect(FAILED_TO_LOCK);
263        set_args(&[]);
264        env::remove_var("ROS_NAMESPACE");
265        assert_eq!(String::from(""), namespace());
266        set_args(&["unimportant", "also_unimportant"]);
267        assert_eq!(String::from(""), namespace());
268    }
269
270    #[test]
271    #[allow(unused_variables)]
272    fn namespace_uses_environment_when_passed() {
273        let testcase = TESTCASE.lock().expect(FAILED_TO_LOCK);
274        set_args(&[]);
275        env::remove_var("ROS_NAMESPACE");
276        assert_eq!(String::from(""), namespace());
277        env::set_var("ROS_NAMESPACE", "/myns");
278        assert_eq!(String::from("/myns"), namespace());
279    }
280
281    #[test]
282    #[allow(unused_variables)]
283    fn namespace_uses_argument_when_passed() {
284        let testcase = TESTCASE.lock().expect(FAILED_TO_LOCK);
285        set_args(&[]);
286        env::remove_var("ROS_NAMESPACE");
287        assert_eq!(String::from(""), namespace());
288        set_args(&["__ns:=/myns"]);
289        assert_eq!(String::from("/myns"), namespace());
290    }
291
292    #[test]
293    #[allow(unused_variables)]
294    fn namespace_prioritizes_argument_when_both_passed() {
295        let testcase = TESTCASE.lock().expect(FAILED_TO_LOCK);
296        set_args(&[]);
297        env::remove_var("ROS_NAMESPACE");
298        assert_eq!(String::from(""), namespace());
299        env::set_var("ROS_NAMESPACE", "/myns1");
300        set_args(&["__ns:=/myns2"]);
301        assert_eq!(String::from("/myns2"), namespace());
302    }
303
304    #[test]
305    #[allow(unused_variables)]
306    fn master_uses_default_uri_by_default() {
307        let testcase = TESTCASE.lock().expect(FAILED_TO_LOCK);
308        set_args(&[]);
309        env::remove_var("ROS_MASTER_URI");
310        assert_eq!(String::from("http://localhost:11311/"), master());
311        set_args(&["unimportant", "also_unimportant"]);
312        assert_eq!(String::from("http://localhost:11311/"), master());
313    }
314
315    #[test]
316    #[allow(unused_variables)]
317    fn master_uses_environment_when_passed() {
318        let testcase = TESTCASE.lock().expect(FAILED_TO_LOCK);
319        set_args(&[]);
320        env::remove_var("ROS_MASTER_URI");
321        assert_eq!(String::from("http://localhost:11311/"), master());
322        env::set_var("ROS_MASTER_URI", "http://somebody:21212/");
323        assert_eq!(String::from("http://somebody:21212/"), master());
324    }
325
326    #[test]
327    #[allow(unused_variables)]
328    fn master_uses_argument_when_passed() {
329        let testcase = TESTCASE.lock().expect(FAILED_TO_LOCK);
330        set_args(&[]);
331        env::remove_var("ROS_MASTER_URI");
332        assert_eq!(String::from("http://localhost:11311/"), master());
333        set_args(&["__master:=http://somebody:21212/"]);
334        assert_eq!(String::from("http://somebody:21212/"), master());
335    }
336
337    #[test]
338    #[allow(unused_variables)]
339    fn master_prioritizes_argument_when_both_passed() {
340        let testcase = TESTCASE.lock().expect(FAILED_TO_LOCK);
341        set_args(&[]);
342        env::remove_var("ROS_MASTER_URI");
343        assert_eq!(String::from("http://localhost:11311/"), master());
344        env::set_var("ROS_MASTER_URI", "http://somebody1:21212/");
345        set_args(&["__master:=http://somebody2:21212/"]);
346        assert_eq!(String::from("http://somebody2:21212/"), master());
347    }
348
349    #[test]
350    #[allow(unused_variables)]
351    fn hostname_uses_default_uri_by_default() {
352        let testcase = TESTCASE.lock().expect(FAILED_TO_LOCK);
353        set_args(&[]);
354        env::remove_var("ROS_HOSTNAME");
355        env::remove_var("ROS_IP");
356        assert_eq!(String::from("myhostname"), hostname());
357        set_args(&["unimportant", "also_unimportant"]);
358        assert_eq!(String::from("myhostname"), hostname());
359    }
360
361    #[test]
362    #[allow(unused_variables)]
363    fn hostname_uses_hostname_environment_when_passed() {
364        let testcase = TESTCASE.lock().expect(FAILED_TO_LOCK);
365        set_args(&[]);
366        env::remove_var("ROS_HOSTNAME");
367        env::remove_var("ROS_IP");
368        assert_eq!(String::from("myhostname"), hostname());
369        env::set_var("ROS_HOSTNAME", "host");
370        assert_eq!(String::from("host"), hostname());
371    }
372
373    #[test]
374    #[allow(unused_variables)]
375    fn hostname_uses_ip_environment_when_passed() {
376        let testcase = TESTCASE.lock().expect(FAILED_TO_LOCK);
377        set_args(&[]);
378        env::remove_var("ROS_HOSTNAME");
379        env::remove_var("ROS_IP");
380        assert_eq!(String::from("myhostname"), hostname());
381        env::set_var("ROS_IP", "192.168.0.1");
382        assert_eq!(String::from("192.168.0.1"), hostname());
383    }
384
385    #[test]
386    #[allow(unused_variables)]
387    fn hostname_prioritizes_hostname_over_ip_environment_when_passed() {
388        let testcase = TESTCASE.lock().expect(FAILED_TO_LOCK);
389        set_args(&[]);
390        env::remove_var("ROS_HOSTNAME");
391        env::remove_var("ROS_IP");
392        assert_eq!(String::from("myhostname"), hostname());
393        env::set_var("ROS_HOSTNAME", "host");
394        env::set_var("ROS_IP", "192.168.0.1");
395        assert_eq!(String::from("host"), hostname());
396    }
397
398    #[test]
399    #[allow(unused_variables)]
400    fn hostname_uses_hostname_argument_when_passed() {
401        let testcase = TESTCASE.lock().expect(FAILED_TO_LOCK);
402        set_args(&[]);
403        env::remove_var("ROS_HOSTNAME");
404        env::remove_var("ROS_IP");
405        assert_eq!(String::from("myhostname"), hostname());
406        set_args(&["__hostname:=host"]);
407        assert_eq!(String::from("host"), hostname());
408    }
409
410    #[test]
411    #[allow(unused_variables)]
412    fn hostname_uses_ip_argument_when_passed() {
413        let testcase = TESTCASE.lock().expect(FAILED_TO_LOCK);
414        set_args(&[]);
415        env::remove_var("ROS_HOSTNAME");
416        env::remove_var("ROS_IP");
417        assert_eq!(String::from("myhostname"), hostname());
418        set_args(&["__ip:=192.168.0.1"]);
419        assert_eq!(String::from("192.168.0.1"), hostname());
420    }
421
422    #[test]
423    #[allow(unused_variables)]
424    fn hostname_prioritizes_hostname_over_ip_argument_when_passed() {
425        let testcase = TESTCASE.lock().expect(FAILED_TO_LOCK);
426        set_args(&[]);
427        env::remove_var("ROS_HOSTNAME");
428        env::remove_var("ROS_IP");
429        assert_eq!(String::from("myhostname"), hostname());
430        set_args(&["__hostname:=host", "__ip:=192.168.0.1"]);
431        assert_eq!(String::from("host"), hostname());
432    }
433
434    #[test]
435    #[allow(unused_variables)]
436    fn hostname_prioritizes_argument_when_both_passed() {
437        let testcase = TESTCASE.lock().expect(FAILED_TO_LOCK);
438        set_args(&[]);
439        env::remove_var("ROS_HOSTNAME");
440        env::remove_var("ROS_IP");
441        assert_eq!(String::from("myhostname"), hostname());
442        env::set_var("ROS_HOSTNAME", "host");
443        env::set_var("ROS_IP", "192.168.0.1");
444        set_args(&["__hostname:=host2", "__ip:=127.0.0.1"]);
445        assert_eq!(String::from("host2"), hostname());
446    }
447}