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}