1use nom::bytes::complete::tag;
28use nom::bytes::complete::{take_while, take_while1};
29use nom::character::is_digit;
30use nom::combinator::opt;
31use nom::multi::many0;
32use nom::multi::separated_list1;
33use nom::sequence::delimited;
34use nom::sequence::pair;
35use nom::sequence::preceded;
36use nom::sequence::tuple;
37use nom::*;
38use std::str;
39
40struct DigitInfo {
41 value: u32,
42 leading_zeros: usize,
43 num_digits: usize,
44}
45
46#[derive(Debug)]
47struct RangeList {
48 ranges: Vec<(u32, u32)>,
49 num_digits: usize,
50}
51
52fn hostname_part(input: &[u8]) -> IResult<&[u8], &[u8]> {
54 let hpart = take_while1(|ch| (ch != b'[' && ch != b','));
55 hpart(input)
56}
57
58fn take_digits(i: &[u8]) -> IResult<&[u8], DigitInfo> {
61 let (i, digits) = take_while(is_digit)(i)?;
62
63 if digits.is_empty() {
64 return Err(nom::Err::Error(nom::error::Error {
65 input: i,
66 code: nom::error::ErrorKind::Eof,
67 }));
68 }
69
70 let s = str::from_utf8(digits).expect("Invalid data, expected UTF-8 string");
71 let res = s
72 .parse()
73 .expect("Invalid string, expected ASCII representation of a number");
74
75 let mut clz = 0;
76 for &c in digits {
77 if c == b'0' {
78 clz += 1;
79 } else {
80 break;
81 }
82 }
83 let di = DigitInfo {
84 value: res,
85 leading_zeros: clz,
86 num_digits: digits.len(),
87 };
88 Ok((i, di))
89}
90
91fn listexpr(input: &[u8]) -> IResult<&[u8], RangeList> {
93 let digits = take_digits;
94 let range = tuple((&digits, opt(preceded(tag("-"), &digits))));
95 let mut snl = separated_list1(tag(","), range);
96 let (i, les) = snl(input)?;
97 let mut ri = RangeList {
98 ranges: Vec::new(),
99 num_digits: 0,
100 };
101 let mut max_lz = 0;
102 for le in les {
103 if le.0.leading_zeros > max_lz {
104 max_lz = le.0.leading_zeros;
105 ri.num_digits = le.0.num_digits;
106 }
107 let mut vals = (le.0.value, le.0.value);
108 match le.1 {
109 Some(u) => {
110 if u.value >= le.0.value {
111 vals.1 = u.value;
112 } else {
113 vals = (u.value, le.0.value);
114 }
115 if u.leading_zeros > max_lz {
116 max_lz = u.leading_zeros;
117 ri.num_digits = u.num_digits;
118 }
119 }
120 None => {}
121 }
122 ri.ranges.push(vals);
123 }
124 Ok((i, ri))
125}
126
127fn range(input: &[u8]) -> IResult<&[u8], RangeList> {
129 let mut r = delimited(tag("["), listexpr, tag("]"));
130 r(input)
131}
132
133fn hnrangepair(input: &[u8]) -> IResult<&[u8], (&[u8], Vec<RangeList>)> {
135 let mut t = pair(hostname_part, many0(range));
136 t(input)
137}
138
139fn hostlist(input: &[u8]) -> IResult<&[u8], Vec<Vec<(&[u8], Vec<RangeList>)>>> {
141 let m = many0(hnrangepair);
142 let mut snl = separated_list1(tag(","), m);
143 snl(input)
144}
145
146fn cartesian<T: AsRef<str> + ToString>(v1: &[T], v2: &[T]) -> Vec<String> {
148 let oldsz = v1.len();
149 let mut res = Vec::with_capacity(oldsz * v2.len());
150 for e1 in v1 {
151 for e2 in v2 {
152 let mut t: String = e1.to_string();
154 t.push_str(e2.to_string().as_str());
155 res.push(t);
156 }
157 }
158 res
159}
160
161pub fn expand(a_str: &str) -> Result<Vec<String>, &'static str> {
171 let p = hostlist(a_str.as_bytes());
172 let parsed = match p {
173 Ok((_, o)) => o,
174 _ => return Err("Invalid hostlist"),
175 };
176 let mut allres = Vec::new();
177 for e in &parsed {
178 let mut res: Vec<String> = vec!["".to_string()];
179 for rangepair in e {
180 let base = str::from_utf8(&rangepair.0).unwrap();
181 let mut res2 = vec![base.to_string()];
182 for range in &rangepair.1 {
183 let mut res3: Vec<String> = Vec::new();
184 for r2 in &range.ranges {
185 for i in r2.0..(r2.1 + 1) {
186 res3.push(format!("{:0width$}", i, width = range.num_digits));
188 }
189 }
190 res2 = cartesian(&res2, &res3);
191 }
192 res = cartesian(&res, &res2);
193 }
194 for host in res {
195 allres.push(host);
196 }
197 }
198 Ok(allres)
199}
200
201#[test]
203fn check_base() {
204 let hostlist = b"foo[1-3]";
205 let res = hostname_part(hostlist);
206 let out = match res {
207 Ok((_, o)) => str::from_utf8(&o).unwrap(),
208 _ => panic!(),
209 };
210 assert_eq!(out, "foo");
211}
212
213#[test]
214fn listexpr_1() {
215 let le = b"1";
216 let res = listexpr(le);
217 let out = match res {
218 Ok((_, o)) => o.ranges[0].0,
219 _ => panic!(),
220 };
221 assert_eq!(out, 1);
222}
223
224#[test]
225fn listexpr_2() {
226 let le = b"1,2,3-5";
227 let res = listexpr(le);
228 let out = match res {
229 Ok((_, o)) => o,
230 _ => panic!(),
231 };
232 assert_eq!(out.ranges[0].0, 1);
233 assert_eq!(out.ranges[1].0, 2);
234 assert_eq!(out.ranges[2].0, 3);
235 assert_eq!(out.ranges[2].1, 5);
236}
237
238#[test]
239fn hostrange() {
240 let hostlist = b"[1,2,3-5]";
241 let res = range(hostlist);
242 let out = match res {
243 Ok((_, o)) => o,
244 _ => {
245 println!("{:?}", res);
246 panic!();
247 }
248 };
249 assert_eq!(out.ranges[0].0, 1);
250 assert_eq!(out.ranges[1].0, 2);
251 assert_eq!(out.ranges[2].0, 3);
252 assert_eq!(out.ranges[2].1, 5);
253}
254
255#[test]
272fn hnrangepair_1() {
273 let hostlist = b"foo[1,2,3-5]";
274 let res = hnrangepair(hostlist);
275 let out = match res {
276 Ok((_, o)) => o,
277 _ => {
278 println!("{:?}", res);
279 panic!();
280 }
281 };
282 assert_eq!(str::from_utf8(&out.0).unwrap(), "foo");
283 let r = &out.1[0];
284 assert_eq!(r.ranges[0].0, 1);
285 assert_eq!(r.ranges[1].0, 2);
286 assert_eq!(r.ranges[2].0, 3);
287 assert_eq!(r.ranges[2].1, 5);
288}
289
290#[test]
291fn hnrangepair_hostonly() {
292 let hostlist = b"foo";
293 let res = hnrangepair(hostlist);
294 let out = match res {
295 Ok((_, o)) => str::from_utf8(&o.0).unwrap(),
296 _ => {
297 println!("{:?}", res);
298 panic!();
299 }
300 };
301 assert_eq!(out, "foo");
302}
303
304#[test]
325fn hostlist_1() {
326 let myhl = b"foo[1,2,3-5]";
327 let res = hostlist(myhl);
328 let out = match res {
329 Ok((_, o)) => o,
330 _ => {
331 println!("{:?}", res);
332 panic!();
333 }
334 };
335 assert_eq!(str::from_utf8(&out[0][0].0).unwrap(), "foo");
336 let r = &out[0][0].1[0];
337 assert_eq!(r.ranges[0].0, 1);
338 assert_eq!(r.ranges[1].0, 2);
339 assert_eq!(r.ranges[2].0, 3);
340 assert_eq!(r.ranges[2].1, 5);
341}
342
343#[test]
359fn test_cartesian() {
360 let a = vec!["ab", "c"];
361 let b = vec!["1", "23"];
362 let r = cartesian(&a, &b);
363 assert_eq!("ab1", r[0]);
364 assert_eq!("ab23", r[1]);
365 assert_eq!("c1", r[2]);
366 assert_eq!("c23", r[3]);
367 assert_eq!(4, r.len());
368}
369
370#[cfg(test)]
372mod tests {
373 use super::*;
374
375 #[test]
376 fn it_works() {}
377
378 #[test]
379 fn test_expand() {
380 assert_eq!(expand("foo[1,2,3]").unwrap(), vec!["foo1", "foo2", "foo3"]);
381 assert_eq!(expand("foo[1-3]").unwrap(), vec!["foo1", "foo2", "foo3"]);
382 assert_eq!(expand("foo").unwrap(), vec!["foo"]);
383 }
384
385 #[test]
386 fn test_full_name_with_comma_works() {
387 assert_eq!(
388 expand("hostname1.foo.com,hostname2.foo.com").unwrap(),
389 vec!["hostname1.foo.com", "hostname2.foo.com"]
390 );
391 }
392
393 #[test]
394 fn test_trailing_parts() {
395 assert_eq!(
396 expand("hostname1.foo.com").unwrap(),
397 vec!["hostname1.foo.com"]
398 );
399 }
400
401 #[test]
402 fn test_single_host_expansion() {
403 assert_eq!(
404 expand("hostname[6].foo.com").unwrap(),
405 vec!["hostname6.foo.com"]
406 )
407 }
408
409 #[test]
410 fn test_prefix_expansion() {
411 assert_eq!(
412 expand("hostname[009-011]").unwrap(),
413 vec!["hostname009", "hostname010", "hostname011"]
414 );
415 }
416
417 #[test]
418 fn test_reverse_order() {
419 assert_eq!(
420 expand("hostname[7-5]").unwrap(),
421 vec!["hostname5", "hostname6", "hostname7"],
422 );
423 }
424
425 #[test]
426 fn test_single_item_two_ranges() {
427 assert_eq!(
428 expand("hostname[6,7]-[9-11].foo.com").unwrap(),
429 vec![
430 "hostname6-9.foo.com",
431 "hostname6-10.foo.com",
432 "hostname6-11.foo.com",
433 "hostname7-9.foo.com",
434 "hostname7-10.foo.com",
435 "hostname7-11.foo.com"
436 ]
437 );
438 }
439}