1use crate::range_set::RangeSet;
10use std::collections::HashMap;
11use std::fmt;
12use std::str::FromStr;
13use thiserror::Error;
14
15#[derive(Debug, Error)]
20pub enum NodeSetError {
21 #[error("parse error: {msg} (in \"{part}\")")]
22 ParseError { part: String, msg: String },
23
24 #[error("range error: {0}")]
25 RangeError(#[from] crate::range_set::RangeSetError),
26
27 #[error("external error: {0}")]
28 ExternalError(String),
29
30 #[error("index error: {0}")]
33 IndexError(String),
34}
35
36pub type Result<T> = std::result::Result<T, NodeSetError>;
37
38#[derive(Debug, Clone)]
49struct NodeSetBase {
50 patterns: HashMap<String, Option<RangeSet>>,
51 autostep: Option<u32>,
52}
53
54impl NodeSetBase {
55 fn new() -> Self {
56 Self {
57 patterns: HashMap::new(),
58 autostep: None,
59 }
60 }
61
62 fn with_autostep(autostep: Option<u32>) -> Self {
63 Self {
64 patterns: HashMap::new(),
65 autostep,
66 }
67 }
68
69 fn add(&mut self, pat: &str, rangeset: Option<RangeSet>) {
71 if let Some(existing) = self.patterns.get_mut(pat) {
72 if let (Some(existing_rs), Some(new_rs)) = (existing.as_mut(), &rangeset) {
73 existing_rs.update(new_rs);
74 }
75 } else {
78 self.patterns.insert(pat.to_string(), rangeset);
79 }
80 }
81
82 fn len(&self) -> usize {
83 self.patterns
84 .values()
85 .map(|rs| match rs {
86 Some(rs) => rs.len(),
87 None => 1, })
89 .sum()
90 }
91
92 fn is_empty(&self) -> bool {
93 self.patterns.is_empty()
94 }
95
96 fn clear(&mut self) {
97 self.patterns.clear();
98 }
99
100 fn update(&mut self, other: &NodeSetBase) {
102 for (pat, rangeset) in &other.patterns {
103 self.add(pat, rangeset.clone());
104 }
105 }
106
107 fn intersection_update(&mut self, other: &NodeSetBase) {
109 let mut result = HashMap::new();
110 for (pat, other_rs) in &other.patterns {
111 if let Some(self_rs) = self.patterns.get(pat) {
112 match (self_rs, other_rs) {
113 (Some(s), Some(o)) => {
114 let intersection = s.intersection(o);
115 if intersection.len() > 0 {
116 result.insert(pat.clone(), Some(intersection));
117 }
118 }
119 (None, None) => {
120 result.insert(pat.clone(), None);
122 }
123 _ => {} }
125 }
126 }
127 self.patterns = result;
128 }
129
130 fn difference_update(&mut self, other: &NodeSetBase) {
132 let mut purge = Vec::new();
133 for (pat, other_rs) in &other.patterns {
134 if let Some(self_rs) = self.patterns.get_mut(pat) {
135 match (self_rs, other_rs) {
136 (Some(s), Some(o)) => {
137 s.difference_update(o);
138 if s.len() == 0 {
139 purge.push(pat.clone());
140 }
141 }
142 (None, None) => {
143 purge.push(pat.clone());
144 }
145 _ => {} }
147 }
148 }
149 for pat in purge {
150 self.patterns.remove(&pat);
151 }
152 }
153
154 fn symmetric_difference_update(&mut self, other: &NodeSetBase) {
156 let mut purge = Vec::new();
157
158 for (pat, self_rs) in self.patterns.iter_mut() {
160 if let Some(other_rs) = other.patterns.get(pat) {
161 match (self_rs.as_mut(), other_rs) {
162 (Some(s), Some(o)) => {
163 s.symmetric_difference_update(o);
164 if s.len() == 0 {
165 purge.push(pat.clone());
166 }
167 }
168 (None, None) => {
169 purge.push(pat.clone());
171 }
172 _ => {}
173 }
174 }
175 }
176
177 for (pat, other_rs) in &other.patterns {
179 if !self.patterns.contains_key(pat) {
180 self.add(pat, other_rs.clone());
181 }
182 }
183
184 for pat in purge {
185 self.patterns.remove(&pat);
186 }
187 }
188
189 fn contains(&self, node: &str) -> bool {
195 let (pat, indices) = parse_node_indices(node);
196
197 if let Some(rs_opt) = self.patterns.get(&pat) {
199 return match (rs_opt, indices.as_slice()) {
200 (Some(rs), &[(_, _)]) => {
201 let digit_str = extract_last_digit_str(node);
203 rs.contains_str(&digit_str)
204 }
205 (None, &[]) => true,
206 _ => false,
207 };
208 }
209
210 if indices.len() > 1 {
214 let digit_strs = extract_all_digit_strs(node);
216 for n_sub in 1..indices.len() {
218 let mut candidate_pat = pat.clone();
219 for i in 0..n_sub {
220 candidate_pat = candidate_pat.replacen("%s", &digit_strs[i], 1);
221 }
222 if let Some(Some(rs)) = self.patterns.get(&candidate_pat) {
223 let last_digit = &digit_strs[digit_strs.len() - 1];
225 if rs.contains_str(last_digit) {
226 return true;
227 }
228 }
229 }
230 }
231
232 false
233 }
234
235 fn sorted_patterns(&self) -> Vec<(&String, &Option<RangeSet>)> {
238 let mut sorted_pats: Vec<_> = self.patterns.iter().collect();
239 sorted_pats.sort_by(|a, b| a.0.cmp(b.0));
240 sorted_pats
241 }
242
243 fn iter(&self) -> Vec<String> {
250 let mut nodes = Vec::new();
251 for (pat, rs_opt) in self.sorted_patterns() {
252 match rs_opt {
253 Some(rs) => {
254 for s in rs.striter() {
255 nodes.push(pat.replacen("%s", &s, 1));
256 }
257 }
258 None => {
259 nodes.push(pat.clone());
260 }
261 }
262 }
263 nodes
264 }
265
266 fn position_of(&self, name: &str) -> Option<usize> {
272 let searched = parse_nodeset_string(name, self.autostep).ok()?;
273 let nodes = searched.iter();
274 if nodes.len() != 1 {
275 return None;
277 }
278 let node = &nodes[0];
279
280 let mut base = 0usize;
281 for (pat, rs_opt) in self.sorted_patterns() {
282 match rs_opt {
283 Some(rs) => {
284 if let Some(digits) = match_pattern_index(pat, node) {
285 if let Some(offset) = rs.sorted().iter().position(|s| s == &digits) {
288 return Some(base + offset);
289 }
290 }
291 }
292 None => {
294 if pat.as_str() == node {
295 return Some(base);
296 }
297 }
298 }
299 base += match rs_opt {
300 Some(rs) => rs.len(),
301 None => 1,
302 };
303 }
304 None
305 }
306
307 fn slice_base(&self, start: Option<i64>, stop: Option<i64>, step: i64) -> Result<NodeSetBase> {
314 if step == 0 {
315 return Err(NodeSetError::IndexError(
316 "slice step cannot be zero".to_string(),
317 ));
318 }
319 let length = self.len() as i64;
320 let mut sl_start = match start {
322 None => 0,
323 Some(s) if s < 0 => (length + s).max(0),
324 Some(s) => s,
325 };
326 let sl_stop = match stop {
327 None => i64::MAX,
328 Some(s) if s < 0 => (length + s).max(0),
329 Some(s) => s,
330 };
331 let sl_step = if step < 0 {
332 if start.is_some() || stop.is_some() {
334 return Err(NodeSetError::IndexError(
335 "illegal start and stop when negative step is used".to_string(),
336 ));
337 }
338 let abs_step = -step;
341 let stepmod = (length + abs_step - 1) % abs_step;
342 if stepmod > 0 {
343 sl_start += stepmod;
344 }
345 abs_step
346 } else {
347 step
348 };
349
350 let mut inst = NodeSetBase::with_autostep(self.autostep);
351 if sl_stop <= sl_start {
352 return Ok(inst);
353 }
354
355 let mut next_pick = sl_start;
360 let mut base = 0i64;
361 for (pat, rs_opt) in self.sorted_patterns() {
362 let cnt = match rs_opt {
363 Some(rs) => rs.len() as i64,
364 None => 1,
365 };
366 match rs_opt {
367 Some(rs) => {
368 if next_pick < base + cnt {
369 let mut sub = RangeSet::new();
371 sub.set_autostep(self.autostep);
372 let sorted = rs.sorted();
373 while next_pick < base + cnt && next_pick < sl_stop {
374 sub.add_str(&sorted[(next_pick - base) as usize]);
375 next_pick += sl_step;
376 }
377 inst.add(pat, Some(sub));
378 }
379 }
380 None => {
381 if next_pick == base && next_pick < sl_stop {
382 inst.add(pat, None);
383 next_pick += sl_step;
384 }
385 }
386 }
387 if next_pick >= sl_stop {
388 break;
389 }
390 base += cnt;
391 }
392 Ok(inst)
393 }
394}
395
396impl fmt::Display for NodeSetBase {
397 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
398 let mut parts: Vec<String> = Vec::new();
399 for (pat, rs_opt) in self.sorted_patterns() {
401 match rs_opt {
402 Some(rs) => {
403 let rs_str = format!("{}", rs);
404 if rs.len() == 1 {
405 parts.push(pat.replacen("%s", &rs_str, 1));
407 } else {
408 let bracketed = format!("[{}]", rs_str);
410 parts.push(pat.replacen("%s", &bracketed, 1));
411 }
412 }
413 None => {
414 parts.push(pat.clone());
415 }
416 }
417 }
418 write!(f, "{}", parts.join(","))
419 }
420}
421
422#[allow(dead_code)]
429fn scan_node_single(node: &str) -> (String, Option<i64>) {
430 let mut pat = String::new();
436 let mut has_index = false;
437 let mut last_value: Option<i64> = None;
438
439 let chars: Vec<char> = node.chars().collect();
440 let len = chars.len();
441 let mut i = 0;
442
443 while i < len {
444 let prefix_start = i;
446 while i < len && !chars[i].is_ascii_digit() {
447 i += 1;
448 }
449 let prefix = &node[prefix_start..i];
450
451 let digit_start = i;
453 while i < len && chars[i].is_ascii_digit() {
454 i += 1;
455 }
456 let digits = &node[digit_start..i];
457
458 if digits.is_empty() {
459 pat.push_str(prefix);
460 } else {
461 pat.push_str(prefix);
462 pat.push_str("%s");
463 has_index = true;
464 last_value = Some(digits.parse().unwrap_or(0));
465 }
466 }
467
468 if has_index {
469 (pat, last_value)
470 } else {
471 (pat, None)
472 }
473}
474
475fn parse_node_indices(node: &str) -> (String, Vec<(i64, u32)>) {
478 let mut pat = String::new();
479 let mut indices = Vec::new();
480
481 let chars: Vec<char> = node.chars().collect();
482 let len = chars.len();
483 let mut i = 0;
484
485 while i < len {
486 let prefix_start = i;
487 while i < len && !chars[i].is_ascii_digit() {
488 i += 1;
489 }
490 pat.push_str(&node[prefix_start..i]);
491
492 let digit_start = i;
493 while i < len && chars[i].is_ascii_digit() {
494 i += 1;
495 }
496 let digits = &node[digit_start..i];
497
498 if !digits.is_empty() {
499 pat.push_str("%s");
500 let value: i64 = digits.parse().unwrap_or(0);
501 let pad = if value != 0 {
502 let stripped = digits.trim_start_matches('0');
503 if digits.len() > stripped.len() {
504 digits.len() as u32
505 } else {
506 0
507 }
508 } else if digits.len() > 1 {
509 digits.len() as u32
510 } else {
511 0
512 };
513 indices.push((value, pad));
514 }
515 }
516
517 (pat, indices)
518}
519
520fn parse_nodeset_string(input: &str, autostep: Option<u32>) -> Result<NodeSetBase> {
529 let input = input.trim();
530 if input.is_empty() {
531 return Ok(NodeSetBase::with_autostep(autostep));
532 }
533
534 let mut result = NodeSetBase::with_autostep(autostep);
535 let mut current_op = Op::Update; let tokens = tokenize(input)?;
540
541 for token in tokens {
542 match token {
543 Token::Operator(op) => {
544 current_op = op;
545 }
546 Token::Pattern(s) => {
547 let parsed = parse_single_pattern(&s, autostep)?;
548 match current_op {
549 Op::Update => result.update(&parsed),
550 Op::Difference => result.difference_update(&parsed),
551 Op::Intersection => result.intersection_update(&parsed),
552 Op::SymmetricDifference => result.symmetric_difference_update(&parsed),
553 }
554 }
555 }
556 }
557
558 Ok(result)
559}
560
561#[derive(Debug, Clone, Copy)]
562enum Op {
563 Update,
564 Difference,
565 Intersection,
566 SymmetricDifference,
567}
568
569#[derive(Debug)]
570enum Token {
571 Operator(Op),
572 Pattern(String),
573}
574
575fn tokenize(input: &str) -> Result<Vec<Token>> {
577 let mut tokens = Vec::new();
578 let mut current = String::new();
579 let mut bracket_depth = 0;
580 let chars: Vec<char> = input.chars().collect();
581 let mut i = 0;
582
583 while i < chars.len() {
584 let ch = chars[i];
585 match ch {
586 '[' => {
587 bracket_depth += 1;
588 current.push(ch);
589 }
590 ']' => {
591 if bracket_depth == 0 {
592 return Err(NodeSetError::ParseError {
593 part: input.to_string(),
594 msg: "illegal closing bracket".to_string(),
595 });
596 }
597 bracket_depth -= 1;
598 current.push(ch);
599 }
600 ',' | '!' | '&' | '^' if bracket_depth == 0 => {
601 if !current.is_empty() {
603 tokens.push(Token::Pattern(current.trim().to_string()));
604 current = String::new();
605 }
606 let op = match ch {
607 ',' => Op::Update,
608 '!' => Op::Difference,
609 '&' => Op::Intersection,
610 '^' => Op::SymmetricDifference,
611 _ => unreachable!(),
612 };
613 tokens.push(Token::Operator(op));
614 }
615 _ => {
616 current.push(ch);
617 }
618 }
619 i += 1;
620 }
621
622 if bracket_depth != 0 {
623 return Err(NodeSetError::ParseError {
624 part: input.to_string(),
625 msg: "missing bracket".to_string(),
626 });
627 }
628
629 if !current.is_empty() {
630 tokens.push(Token::Pattern(current.trim().to_string()));
631 }
632
633 let mut result = Vec::new();
636 let mut expect_pattern = true;
637 for token in tokens {
638 match &token {
639 Token::Pattern(_) => {
640 if expect_pattern {
641 if result.is_empty() {
642 result.push(Token::Operator(Op::Update));
644 }
645 result.push(token);
646 expect_pattern = false;
647 } else {
648 result.push(Token::Operator(Op::Update));
651 result.push(token);
652 expect_pattern = false;
653 }
654 }
655 Token::Operator(_) => {
656 result.push(token);
657 expect_pattern = true;
658 }
659 }
660 }
661
662 Ok(result)
663}
664
665fn parse_single_pattern(pat: &str, autostep: Option<u32>) -> Result<NodeSetBase> {
668 let mut base = NodeSetBase::with_autostep(autostep);
669
670 if pat.contains('[') {
671 parse_bracketed(pat, autostep, &mut base)?;
673 } else {
674 let (pattern, indices) = parse_node_indices(pat);
676 if indices.is_empty() {
677 base.add(&pattern, None);
678 } else {
679 let mut rs = RangeSet::new();
681 rs.set_autostep(autostep);
682 if indices.len() == 1 {
684 let (val, pad) = indices[0];
685 rs.add_int(val, pad);
686 base.add(&pattern, Some(rs));
687 } else {
688 let digit_strs = extract_all_digit_strs(pat);
693 let mut expanded_pat = pattern.clone();
694 for ds in &digit_strs[..digit_strs.len() - 1] {
695 expanded_pat = expanded_pat.replacen("%s", ds, 1);
696 }
697 let (val, pad) = indices[indices.len() - 1];
698 rs.add_int(val, pad);
699 base.add(&expanded_pat, Some(rs));
700 }
701 }
702 }
703
704 Ok(base)
705}
706
707fn parse_bracketed(pat: &str, autostep: Option<u32>, base: &mut NodeSetBase) -> Result<()> {
709 let mut remaining = pat;
711 let mut template = String::new();
712 let mut rangesets: Vec<RangeSet> = Vec::new();
713
714 while let Some(bracket_start) = remaining.find('[') {
715 let prefix = &remaining[..bracket_start];
716
717 let (clean_prefix, leading_digits) = split_trailing_digits(prefix);
720 template.push_str(clean_prefix);
721 template.push_str("%s");
722
723 let after_bracket = &remaining[bracket_start + 1..];
724 let bracket_end = after_bracket
725 .find(']')
726 .ok_or_else(|| NodeSetError::ParseError {
727 part: pat.to_string(),
728 msg: "missing bracket".to_string(),
729 })?;
730
731 let range_str = &after_bracket[..bracket_end];
732 remaining = &after_bracket[bracket_end + 1..];
733
734 let (trailing_digits, clean_remaining) = split_leading_digits(remaining);
737
738 let amended_range = amend_range(range_str, &leading_digits, &trailing_digits)?;
740
741 let rs = RangeSet::parse(&amended_range, autostep)?;
742 rangesets.push(rs);
743
744 remaining = clean_remaining;
745 }
746
747 if !remaining.is_empty() {
749 template.push_str(remaining);
751 }
752
753 if rangesets.len() == 1 {
755 base.add(&template, Some(rangesets.into_iter().next().unwrap()));
756 } else if rangesets.is_empty() {
757 base.add(&template, None);
758 } else {
759 expand_multi_dim(&template, &rangesets, base);
762 }
763
764 Ok(())
765}
766
767fn expand_multi_dim(template: &str, rangesets: &[RangeSet], base: &mut NodeSetBase) {
769 if rangesets.is_empty() {
772 return;
773 }
774
775 if rangesets.len() == 1 {
776 base.add(template, Some(rangesets[0].clone()));
777 return;
778 }
779
780 let last_rs = &rangesets[rangesets.len() - 1];
782 let prefixes = expand_dimensions(&rangesets[..rangesets.len() - 1]);
783
784 for prefix_vals in &prefixes {
786 let mut pat = template.to_string();
787 for val in prefix_vals {
788 pat = pat.replacen("%s", val, 1);
789 }
790 base.add(&pat, Some(last_rs.clone()));
792 }
793}
794
795fn expand_dimensions(rangesets: &[RangeSet]) -> Vec<Vec<String>> {
797 if rangesets.is_empty() {
798 return vec![vec![]];
799 }
800
801 let first_vals: Vec<String> = rangesets[0].striter().collect();
802 let rest = expand_dimensions(&rangesets[1..]);
803
804 let mut result = Vec::new();
805 for val in &first_vals {
806 for suffix in &rest {
807 let mut combined = vec![val.clone()];
808 combined.extend(suffix.iter().cloned());
809 result.push(combined);
810 }
811 }
812 result
813}
814
815fn match_pattern_index(pat: &str, node: &str) -> Option<String> {
824 let (prefix, suffix) = pat.split_once("%s")?;
825 if suffix.contains("%s") {
826 return None; }
828 let middle = node.strip_prefix(prefix)?.strip_suffix(suffix)?;
829 if middle.is_empty() || !middle.bytes().all(|b| b.is_ascii_digit()) {
830 return None;
831 }
832 Some(middle.to_string())
833}
834
835fn extract_last_digit_str(node: &str) -> String {
838 let bytes = node.as_bytes();
839 let mut end = bytes.len();
840 while end > 0 && !bytes[end - 1].is_ascii_digit() {
841 end -= 1;
842 }
843 let mut start = end;
844 while start > 0 && bytes[start - 1].is_ascii_digit() {
845 start -= 1;
846 }
847 node[start..end].to_string()
848}
849
850fn extract_all_digit_strs(node: &str) -> Vec<String> {
853 let mut result = Vec::new();
854 let chars: Vec<char> = node.chars().collect();
855 let len = chars.len();
856 let mut i = 0;
857
858 while i < len {
859 while i < len && !chars[i].is_ascii_digit() {
861 i += 1;
862 }
863 let start = i;
864 while i < len && chars[i].is_ascii_digit() {
865 i += 1;
866 }
867 if i > start {
868 result.push(node[start..i].to_string());
869 }
870 }
871 result
872}
873
874fn split_trailing_digits(s: &str) -> (&str, String) {
876 let bytes = s.as_bytes();
877 let mut i = bytes.len();
878 while i > 0 && bytes[i - 1].is_ascii_digit() {
879 i -= 1;
880 }
881 (&s[..i], s[i..].to_string())
882}
883
884fn split_leading_digits(s: &str) -> (String, &str) {
886 let bytes = s.as_bytes();
887 let mut i = 0;
888 while i < bytes.len() && bytes[i].is_ascii_digit() {
889 i += 1;
890 }
891 (s[..i].to_string(), &s[i..])
892}
893
894fn amend_range(range: &str, leading: &str, trailing: &str) -> Result<String> {
899 let mut result = range.to_string();
900
901 if !leading.is_empty() {
902 let rs = RangeSet::parse(&result, None)?;
905 let mut parts = Vec::new();
906 for val_str in rs.striter() {
908 let combined = format!("{}{}", leading, val_str);
909 parts.push(combined);
910 }
911 result = parts.join(",");
913 }
914
915 if !trailing.is_empty() {
916 let rs = RangeSet::parse(&result, None)?;
918 let mut parts = Vec::new();
919 let trail_len = trailing.len();
920 let power = 10i64.pow(trail_len as u32);
921 let trail_val: i64 = trailing.parse().unwrap_or(0);
922
923 for val in rs.intiter() {
924 let combined = val * power + trail_val;
925 parts.push(format!("{}", combined));
926 }
927 result = parts.join(",");
928 }
929
930 Ok(result)
931}
932
933#[derive(Debug, Clone)]
950pub struct NodeSet {
951 base: NodeSetBase,
952}
953
954impl NodeSet {
955 pub fn new() -> Self {
957 Self {
958 base: NodeSetBase::new(),
959 }
960 }
961
962 pub fn parse(pattern: &str) -> Result<Self> {
964 let base = parse_nodeset_string(pattern, None)?;
965 Ok(Self { base })
966 }
967
968 pub fn parse_with_autostep(pattern: &str, autostep: Option<u32>) -> Result<Self> {
970 let base = parse_nodeset_string(pattern, autostep)?;
971 Ok(Self { base })
972 }
973
974 pub fn len(&self) -> usize {
976 self.base.len()
977 }
978
979 pub fn is_empty(&self) -> bool {
981 self.base.is_empty()
982 }
983
984 pub fn contains(&self, node: &str) -> bool {
986 self.base.contains(node)
987 }
988
989 pub fn update(&mut self, other: &NodeSet) {
991 self.base.update(&other.base);
992 }
993
994 pub fn update_str(&mut self, pattern: &str) -> Result<()> {
996 let other = parse_nodeset_string(pattern, self.base.autostep)?;
997 self.base.update(&other);
998 Ok(())
999 }
1000
1001 pub fn union(&self, other: &NodeSet) -> NodeSet {
1003 let mut copy = self.clone();
1004 copy.update(other);
1005 copy
1006 }
1007
1008 pub fn intersection_update(&mut self, other: &NodeSet) {
1010 self.base.intersection_update(&other.base);
1011 }
1012
1013 pub fn intersection(&self, other: &NodeSet) -> NodeSet {
1015 let mut copy = self.clone();
1016 copy.intersection_update(other);
1017 copy
1018 }
1019
1020 pub fn difference_update(&mut self, other: &NodeSet) {
1022 self.base.difference_update(&other.base);
1023 }
1024
1025 pub fn difference(&self, other: &NodeSet) -> NodeSet {
1027 let mut copy = self.clone();
1028 copy.difference_update(other);
1029 copy
1030 }
1031
1032 pub fn symmetric_difference_update(&mut self, other: &NodeSet) {
1034 self.base.symmetric_difference_update(&other.base);
1035 }
1036
1037 pub fn symmetric_difference(&self, other: &NodeSet) -> NodeSet {
1039 let mut copy = self.clone();
1040 copy.symmetric_difference_update(other);
1041 copy
1042 }
1043
1044 pub fn clear(&mut self) {
1046 self.base.clear();
1047 }
1048
1049 pub fn iter(&self) -> impl Iterator<Item = String> + '_ {
1051 self.base.iter().into_iter()
1052 }
1053
1054 pub fn index(&self, name: &str) -> Option<usize> {
1069 self.index_in(name, 0, None)
1070 }
1071
1072 fn index_in(&self, name: &str, start: i64, stop: Option<i64>) -> Option<usize> {
1077 let found = self.base.position_of(name)? as i64;
1078 if start != 0 || stop.is_some() {
1079 let length = self.len() as i64;
1080 let start = if start < 0 {
1081 (length + start).max(0)
1082 } else {
1083 start
1084 };
1085 let stop = match stop {
1086 None => length,
1087 Some(s) if s < 0 => (length + s).max(0),
1088 Some(s) => s,
1089 };
1090 if !(start <= found && found < stop) {
1091 return None;
1092 }
1093 }
1094 Some(found as usize)
1095 }
1096
1097 pub fn get(&self, index: i64) -> Option<String> {
1101 let length = self.len() as i64;
1102 let idx = if index < 0 {
1103 if index >= -length {
1104 length + index
1105 } else {
1106 return None;
1107 }
1108 } else {
1109 index
1110 };
1111 if idx >= length {
1112 return None;
1113 }
1114 self.base.iter().into_iter().nth(idx as usize)
1115 }
1116
1117 pub fn slice(&self, start: Option<i64>, stop: Option<i64>, step: i64) -> Result<NodeSet> {
1131 Ok(NodeSet {
1132 base: self.base.slice_base(start, stop, step)?,
1133 })
1134 }
1135
1136 pub fn split(&self, nbr: usize) -> Vec<NodeSet> {
1138 assert!(nbr > 0);
1139 let all_nodes: Vec<String> = self.iter().collect();
1140 let total = all_nodes.len();
1141 let slice_size = total / nbr;
1142 let leftover = total % nbr;
1143
1144 let mut result = Vec::new();
1145 let mut begin = 0;
1146
1147 for i in 0..nbr.min(total) {
1148 let length = slice_size + if i < leftover { 1 } else { 0 };
1149 let slice = &all_nodes[begin..begin + length];
1150 let pattern = slice.join(",");
1151 if let Ok(ns) = NodeSet::parse(&pattern) {
1152 result.push(ns);
1153 }
1154 begin += length;
1155 }
1156
1157 result
1158 }
1159
1160 pub fn is_subset(&self, other: &NodeSet) -> bool {
1162 self.difference(other).is_empty()
1163 }
1164
1165 pub fn is_superset(&self, other: &NodeSet) -> bool {
1167 other.is_subset(self)
1168 }
1169}
1170
1171impl Default for NodeSet {
1172 fn default() -> Self {
1173 Self::new()
1174 }
1175}
1176
1177impl fmt::Display for NodeSet {
1178 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1179 write!(f, "{}", self.base)
1180 }
1181}
1182
1183impl FromStr for NodeSet {
1184 type Err = NodeSetError;
1185
1186 fn from_str(s: &str) -> Result<Self> {
1187 NodeSet::parse(s)
1188 }
1189}
1190
1191pub fn expand(pat: &str) -> Result<Vec<String>> {
1203 let ns = NodeSet::parse(pat)?;
1204 Ok(ns.iter().collect())
1205}
1206
1207pub fn fold(pat: &str) -> Result<String> {
1215 let ns = NodeSet::parse(pat)?;
1216 Ok(format!("{}", ns))
1217}
1218
1219#[cfg(test)]
1224mod tests {
1225 use super::*;
1226
1227 #[test]
1228 fn test_new_is_empty() {
1229 let ns = NodeSet::new();
1230 assert!(ns.is_empty());
1231 assert_eq!(ns.len(), 0);
1232 }
1233
1234 #[test]
1235 fn test_parse_simple_node() {
1236 let ns = NodeSet::parse("node1").unwrap();
1237 assert_eq!(ns.len(), 1);
1238 assert!(ns.contains("node1"));
1239 assert!(!ns.contains("node2"));
1240 }
1241
1242 #[test]
1243 fn test_parse_range() {
1244 let ns = NodeSet::parse("node[1-5]").unwrap();
1245 assert_eq!(ns.len(), 5);
1246 for i in 1..=5 {
1247 assert!(ns.contains(&format!("node{}", i)));
1248 }
1249 assert!(!ns.contains("node0"));
1250 assert!(!ns.contains("node6"));
1251 }
1252
1253 #[test]
1254 fn test_parse_multiple_ranges() {
1255 let ns = NodeSet::parse("node[1-3,7-9]").unwrap();
1256 assert_eq!(ns.len(), 6);
1257 assert!(ns.contains("node1"));
1258 assert!(ns.contains("node3"));
1259 assert!(!ns.contains("node4"));
1260 assert!(ns.contains("node7"));
1261 assert!(ns.contains("node9"));
1262 }
1263
1264 #[test]
1265 fn test_parse_padded() {
1266 let ns = NodeSet::parse("node[001-003]").unwrap();
1267 assert_eq!(ns.len(), 3);
1268 assert!(ns.contains("node001"));
1269 assert!(ns.contains("node002"));
1270 assert!(ns.contains("node003"));
1271 let display = format!("{}", ns);
1272 assert!(display.contains("001"));
1273 }
1274
1275 #[test]
1276 fn test_parse_comma_separated() {
1277 let ns = NodeSet::parse("node1,node2,node3").unwrap();
1278 assert_eq!(ns.len(), 3);
1279 assert!(ns.contains("node1"));
1280 assert!(ns.contains("node2"));
1281 assert!(ns.contains("node3"));
1282 }
1283
1284 #[test]
1285 fn test_parse_difference_operator() {
1286 let ns = NodeSet::parse("node[1-10]!node[5-7]").unwrap();
1287 assert_eq!(ns.len(), 7);
1288 assert!(ns.contains("node1"));
1289 assert!(ns.contains("node4"));
1290 assert!(!ns.contains("node5"));
1291 assert!(!ns.contains("node6"));
1292 assert!(!ns.contains("node7"));
1293 assert!(ns.contains("node8"));
1294 assert!(ns.contains("node10"));
1295 }
1296
1297 #[test]
1298 fn test_parse_intersection_operator() {
1299 let ns = NodeSet::parse("node[1-10]&node[5-15]").unwrap();
1300 assert_eq!(ns.len(), 6);
1301 for i in 5..=10 {
1302 assert!(ns.contains(&format!("node{}", i)));
1303 }
1304 assert!(!ns.contains("node1"));
1305 assert!(!ns.contains("node11"));
1306 }
1307
1308 #[test]
1309 fn test_parse_xor_operator() {
1310 let ns = NodeSet::parse("node[1-5]^node[3-7]").unwrap();
1311 assert_eq!(ns.len(), 4);
1312 assert!(ns.contains("node1"));
1313 assert!(ns.contains("node2"));
1314 assert!(!ns.contains("node3"));
1315 assert!(!ns.contains("node5"));
1316 assert!(ns.contains("node6"));
1317 assert!(ns.contains("node7"));
1318 }
1319
1320 #[test]
1321 fn test_multi_pattern() {
1322 let ns = NodeSet::parse("web[1-3],db[1-2]").unwrap();
1323 assert_eq!(ns.len(), 5);
1324 assert!(ns.contains("web1"));
1325 assert!(ns.contains("web3"));
1326 assert!(ns.contains("db1"));
1327 assert!(ns.contains("db2"));
1328 }
1329
1330 #[test]
1331 fn test_display_fold() {
1332 let ns = NodeSet::parse("node3,node1,node2").unwrap();
1333 let display = format!("{}", ns);
1334 assert_eq!(display, "node[1-3]");
1335 }
1336
1337 #[test]
1338 fn test_set_union() {
1339 let a = NodeSet::parse("node[1-3]").unwrap();
1340 let b = NodeSet::parse("node[5-7]").unwrap();
1341 let c = a.union(&b);
1342 assert_eq!(c.len(), 6);
1343 }
1344
1345 #[test]
1346 fn test_set_intersection() {
1347 let a = NodeSet::parse("node[1-10]").unwrap();
1348 let b = NodeSet::parse("node[5-15]").unwrap();
1349 let c = a.intersection(&b);
1350 assert_eq!(c.len(), 6);
1351 }
1352
1353 #[test]
1354 fn test_set_difference() {
1355 let a = NodeSet::parse("node[1-10]").unwrap();
1356 let b = NodeSet::parse("node[5-7]").unwrap();
1357 let c = a.difference(&b);
1358 assert_eq!(c.len(), 7);
1359 }
1360
1361 #[test]
1362 fn test_set_symmetric_difference() {
1363 let a = NodeSet::parse("node[1-5]").unwrap();
1364 let b = NodeSet::parse("node[3-7]").unwrap();
1365 let c = a.symmetric_difference(&b);
1366 assert_eq!(c.len(), 4);
1367 }
1368
1369 #[test]
1370 fn test_iter_sorted() {
1371 let ns = NodeSet::parse("node[3,1,2]").unwrap();
1372 let nodes: Vec<String> = ns.iter().collect();
1373 assert_eq!(nodes, vec!["node1", "node2", "node3"]);
1374 }
1375
1376 #[test]
1377 fn test_non_indexed_node() {
1378 let ns = NodeSet::parse("switch").unwrap();
1379 assert_eq!(ns.len(), 1);
1380 assert!(ns.contains("switch"));
1381 }
1382
1383 #[test]
1384 fn test_expand_function() {
1385 let nodes = expand("node[1-3]").unwrap();
1386 assert_eq!(nodes, vec!["node1", "node2", "node3"]);
1387 }
1388
1389 #[test]
1390 fn test_fold_function() {
1391 let s = fold("node3,node1,node2").unwrap();
1392 assert_eq!(s, "node[1-3]");
1393 }
1394
1395 #[test]
1396 fn test_from_str() {
1397 let ns: NodeSet = "node[1-3]".parse().unwrap();
1398 assert_eq!(ns.len(), 3);
1399 }
1400
1401 #[test]
1402 fn test_update_str() {
1403 let mut ns = NodeSet::parse("node[1-3]").unwrap();
1404 ns.update_str("node[5-7]").unwrap();
1405 assert_eq!(ns.len(), 6);
1406 }
1407
1408 #[test]
1409 fn test_split() {
1410 let ns = NodeSet::parse("node[1-6]").unwrap();
1411 let parts = ns.split(3);
1412 assert_eq!(parts.len(), 3);
1413 assert_eq!(parts[0].len(), 2);
1414 assert_eq!(parts[1].len(), 2);
1415 assert_eq!(parts[2].len(), 2);
1416 }
1417
1418 #[test]
1419 fn test_subset_superset() {
1420 let a = NodeSet::parse("node[1-5]").unwrap();
1421 let b = NodeSet::parse("node[1-10]").unwrap();
1422 assert!(a.is_subset(&b));
1423 assert!(b.is_superset(&a));
1424 assert!(!b.is_subset(&a));
1425 }
1426
1427 #[test]
1428 fn test_multi_dimensional() {
1429 let ns = NodeSet::parse("rack[1-2]-node[1-3]").unwrap();
1430 assert_eq!(ns.len(), 6);
1431 assert!(ns.contains("rack1-node1"));
1432 assert!(ns.contains("rack1-node3"));
1433 assert!(ns.contains("rack2-node1"));
1434 assert!(ns.contains("rack2-node3"));
1435 assert!(!ns.contains("rack3-node1"));
1436 }
1437
1438 #[test]
1439 fn test_empty_parse() {
1440 let ns = NodeSet::parse("").unwrap();
1441 assert!(ns.is_empty());
1442 }
1443
1444 #[test]
1445 fn test_missing_bracket_error() {
1446 let result = NodeSet::parse("node[1-3");
1447 assert!(result.is_err());
1448 }
1449
1450 #[test]
1451 fn test_clear() {
1452 let mut ns = NodeSet::parse("node[1-5]").unwrap();
1453 assert!(!ns.is_empty());
1454 ns.clear();
1455 assert!(ns.is_empty());
1456 }
1457
1458 #[test]
1461 fn test_dotted_hostname_single() {
1462 let ns = NodeSet::parse("server1.example.com").unwrap();
1463 assert_eq!(ns.len(), 1);
1464 let nodes: Vec<String> = ns.iter().collect();
1465 assert_eq!(nodes, vec!["server1.example.com"]);
1466 assert_eq!(format!("{ns}"), "server1.example.com");
1467 }
1468
1469 #[test]
1470 fn test_dotted_hostname_multiple() {
1471 let ns = NodeSet::parse("server1.local,server2.local").unwrap();
1472 assert_eq!(ns.len(), 2);
1473 let nodes: Vec<String> = ns.iter().collect();
1474 assert_eq!(nodes, vec!["server1.local", "server2.local"]);
1475 }
1476
1477 #[test]
1478 fn test_ip_address_single() {
1479 let ns = NodeSet::parse("192.168.1.29").unwrap();
1480 assert_eq!(ns.len(), 1);
1481 let nodes: Vec<String> = ns.iter().collect();
1482 assert_eq!(nodes, vec!["192.168.1.29"]);
1483 assert_eq!(format!("{ns}"), "192.168.1.29");
1484 }
1485
1486 #[test]
1487 fn test_ip_address_multiple_same_subnet() {
1488 let ns = NodeSet::parse("192.168.1.3,192.168.1.29").unwrap();
1489 assert_eq!(ns.len(), 2);
1490 let mut nodes: Vec<String> = ns.iter().collect();
1491 nodes.sort();
1492 assert_eq!(nodes, vec!["192.168.1.29", "192.168.1.3"]);
1493 assert_eq!(format!("{ns}"), "192.168.1.[3,29]");
1494 }
1495
1496 #[test]
1497 fn test_ip_address_different_subnets() {
1498 let ns = NodeSet::parse("10.0.0.1,192.168.1.1").unwrap();
1499 assert_eq!(ns.len(), 2);
1500 let mut nodes: Vec<String> = ns.iter().collect();
1501 nodes.sort();
1502 assert_eq!(nodes, vec!["10.0.0.1", "192.168.1.1"]);
1503 }
1504
1505 #[test]
1506 fn test_ip_address_contains() {
1507 let ns = NodeSet::parse("192.168.1.3,192.168.1.29").unwrap();
1508 assert!(ns.contains("192.168.1.3"));
1509 assert!(ns.contains("192.168.1.29"));
1510 assert!(!ns.contains("192.168.1.4"));
1511 assert!(!ns.contains("10.0.0.1"));
1512 }
1513
1514 #[test]
1515 fn test_dotted_hostname_with_numbers() {
1516 let ns = NodeSet::parse("node1.rack2.dc3").unwrap();
1518 assert_eq!(ns.len(), 1);
1519 let nodes: Vec<String> = ns.iter().collect();
1520 assert_eq!(nodes, vec!["node1.rack2.dc3"]);
1521 }
1522
1523 #[test]
1524 fn test_mdns_hostname() {
1525 let ns = NodeSet::parse("GN9CFLM92K-MBP.local").unwrap();
1526 assert_eq!(ns.len(), 1);
1527 let nodes: Vec<String> = ns.iter().collect();
1528 assert_eq!(nodes, vec!["GN9CFLM92K-MBP.local"]);
1529 }
1530
1531 #[test]
1532 fn test_mdns_hostname_comma_list() {
1533 let ns = NodeSet::parse("CK2Q9LN7PM-MBA.local,GJHC5VVN49-MBP.local,GN9CFLM92K-MBP.local")
1534 .unwrap();
1535 assert_eq!(ns.len(), 3);
1536 let nodes: Vec<String> = ns.iter().collect();
1537 assert!(nodes.contains(&"CK2Q9LN7PM-MBA.local".to_string()));
1538 assert!(nodes.contains(&"GJHC5VVN49-MBP.local".to_string()));
1539 assert!(nodes.contains(&"GN9CFLM92K-MBP.local".to_string()));
1540 }
1541
1542 #[test]
1543 fn test_thunderbolt_hostname() {
1544 let ns = NodeSet::parse("CK2Q9LN7PM-MBA.tb").unwrap();
1545 assert_eq!(ns.len(), 1);
1546 let nodes: Vec<String> = ns.iter().collect();
1547 assert_eq!(nodes, vec!["CK2Q9LN7PM-MBA.tb"]);
1548 }
1549
1550 #[test]
1555 fn test_index() {
1556 let ns = NodeSet::parse("yeti[30,34-51,59-60]").unwrap();
1558 for (i, node) in ns.iter().enumerate() {
1560 assert_eq!(ns.index(&node), Some(i));
1561 }
1562 assert_eq!(ns.index("yeti30"), Some(0));
1563 assert_eq!(ns.index("yeti34"), Some(1));
1564 assert_eq!(ns.index("yeti51"), Some(18));
1565 assert_eq!(ns.index("yeti60"), Some(20));
1566 assert_eq!(ns.index("yeti31"), None);
1568 assert_eq!(ns.index("foo1"), None);
1569 assert_eq!(ns.index("yeti[30,34]"), None);
1571
1572 let ns = NodeSet::parse("abc,cde[3-9,11],fgh").unwrap();
1574 for (i, node) in ns.iter().enumerate() {
1575 assert_eq!(ns.index(&node), Some(i));
1576 }
1577 assert_eq!(ns.index("abc"), Some(0));
1578 assert_eq!(ns.index("cde3"), Some(1));
1579 assert_eq!(ns.index("cde11"), Some(8));
1580 assert_eq!(ns.index("fgh"), Some(9));
1581
1582 let ns = NodeSet::parse("prune[003-034,349-353/2]").unwrap();
1584 assert_eq!(ns.index("prune003"), Some(0));
1585 assert_eq!(ns.index("prune034"), Some(31));
1586 assert_eq!(ns.index("prune349"), Some(32));
1587 assert_eq!(ns.index("prune353"), Some(34));
1588 assert_eq!(ns.index("prune3"), None);
1589 assert_eq!(ns.index("prune35"), None);
1590
1591 let ns = NodeSet::parse("node[0-9]").unwrap();
1593 assert_eq!(ns.index_in("node5", 3, None), Some(5));
1594 assert_eq!(ns.index_in("node5", 5, None), Some(5));
1595 assert_eq!(ns.index_in("node5", 6, None), None);
1596 assert_eq!(ns.index_in("node8", -3, None), Some(8));
1597 assert_eq!(ns.index_in("node5", 0, Some(6)), Some(5));
1598 assert_eq!(ns.index_in("node5", 0, Some(5)), None);
1599 assert_eq!(ns.index_in("node9", 0, Some(-1)), None);
1600 }
1601
1602 #[test]
1603 fn test_nd_index() {
1604 let ns = NodeSet::parse("da[30,34-51,59-60]p[1-2]").unwrap();
1606 for (i, node) in ns.iter().enumerate() {
1607 assert_eq!(ns.index(&node), Some(i));
1608 }
1609 assert_eq!(ns.index("da30p1"), Some(0));
1610 assert_eq!(ns.index("da30p2"), Some(1));
1611 assert_eq!(ns.index("da34p1"), Some(2));
1612 assert_eq!(ns.index("da30p3"), None);
1613
1614 let ns = NodeSet::parse("da[30,34-51,59-60]p[1-2],da[70-77]p2").unwrap();
1615 for (i, node) in ns.iter().enumerate() {
1616 assert_eq!(ns.index(&node), Some(i));
1617 }
1618 }
1619
1620 #[test]
1621 fn test_get_int() {
1622 let ns = NodeSet::parse("node[0-9]").unwrap();
1624 assert_eq!(ns.get(0).as_deref(), Some("node0"));
1625 assert_eq!(ns.get(9).as_deref(), Some("node9"));
1626 assert_eq!(ns.get(-1).as_deref(), Some("node9"));
1627 assert_eq!(ns.get(-10).as_deref(), Some("node0"));
1628 assert_eq!(ns.get(10), None);
1629 assert_eq!(ns.get(-11), None);
1630
1631 let ns = NodeSet::parse("yeti[30,34-51,59-60]").unwrap();
1632 for i in 0..ns.len() as i64 {
1633 let node = ns.get(i).unwrap();
1634 assert_eq!(ns.index(&node), Some(i as usize));
1635 }
1636 }
1637
1638 fn sl(ns: &NodeSet, start: Option<i64>, stop: Option<i64>, step: i64) -> String {
1640 ns.slice(start, stop, step).unwrap().to_string()
1641 }
1642
1643 #[test]
1644 fn test_getslice() {
1645 let ns = NodeSet::parse("yeti[30,34-51,59-60]").unwrap();
1648 assert_eq!(ns.len(), 21);
1649 assert_eq!(ns.slice(Some(0), Some(2), 1).unwrap().len(), 2);
1650 assert_eq!(sl(&ns, Some(0), Some(2), 1), "yeti[30,34]");
1651 assert_eq!(ns.slice(Some(1), Some(3), 1).unwrap().len(), 2);
1652 assert_eq!(sl(&ns, Some(1), Some(3), 1), "yeti[34-35]");
1653 assert_eq!(ns.slice(Some(19), Some(21), 1).unwrap().len(), 2);
1654 assert_eq!(sl(&ns, Some(19), Some(21), 1), "yeti[59-60]");
1655 assert_eq!(ns.slice(Some(20), Some(22), 1).unwrap().len(), 1);
1656 assert_eq!(sl(&ns, Some(20), Some(22), 1), "yeti60");
1657 assert_eq!(ns.slice(Some(21), Some(24), 1).unwrap().len(), 0);
1658 assert_eq!(sl(&ns, Some(21), Some(24), 1), "");
1659 assert_eq!(sl(&ns, None, Some(-1), 1), "yeti[30,34-51,59]");
1661 assert_eq!(sl(&ns, None, Some(-2), 1), "yeti[30,34-51]");
1662 assert_eq!(sl(&ns, Some(1), Some(-2), 1), "yeti[34-51]");
1663 assert_eq!(sl(&ns, Some(2), Some(-2), 1), "yeti[35-51]");
1664 assert_eq!(sl(&ns, Some(9), Some(-3), 1), "yeti[42-50]");
1665 assert_eq!(sl(&ns, Some(10), Some(-9), 1), "yeti[43-44]");
1666 assert_eq!(sl(&ns, Some(10), Some(-10), 1), "yeti43");
1667 assert_eq!(sl(&ns, Some(11), Some(-10), 1), "");
1668 assert_eq!(sl(&ns, Some(11), Some(-11), 1), "");
1669 assert_eq!(
1670 sl(&ns, None, None, -2),
1671 "yeti[30,35,37,39,41,43,45,47,49,51,60]"
1672 );
1673 assert_eq!(sl(&ns, None, None, -3), "yeti[35,38,41,44,47,50,60]");
1674 assert_eq!(sl(&ns, Some(0), Some(10), 2), "yeti[30,35,37,39,41]");
1676 assert_eq!(sl(&ns, Some(1), Some(11), 2), "yeti[34,36,38,40,42]");
1677 assert_eq!(sl(&ns, None, Some(11), 3), "yeti[30,36,39,42]");
1678 assert_eq!(sl(&ns, Some(11), None, 4), "yeti[44,48,59]");
1679 assert_eq!(sl(&ns, Some(14), None, 1), "yeti[47-51,59-60]");
1680 assert_eq!(sl(&ns, None, None, 1), "yeti[30,34-51,59-60]");
1681 assert_eq!(sl(&ns, None, None, 5), "yeti[30,38,43,48,60]");
1682 let ns = NodeSet::parse("foo,bar,bur").unwrap();
1684 assert_eq!(ns.len(), 3);
1685 assert_eq!(ns.slice(Some(0), Some(2), 1).unwrap().len(), 2);
1686 assert_eq!(sl(&ns, Some(0), Some(2), 1), "bar,bur");
1687 assert_eq!(sl(&ns, Some(1), Some(2), 1), "bur");
1688 assert_eq!(sl(&ns, Some(1), Some(3), 1), "bur,foo");
1689 assert_eq!(sl(&ns, Some(2), Some(4), 1), "foo");
1690 let ns = NodeSet::parse("foo,bar,bur3,bur1").unwrap();
1691 assert_eq!(ns.len(), 4);
1692 assert_eq!(ns.slice(Some(0), Some(2), 1).unwrap().len(), 2);
1693 assert_eq!(ns.slice(Some(1), Some(3), 1).unwrap().len(), 2);
1694 assert_eq!(ns.slice(Some(2), Some(4), 1).unwrap().len(), 2);
1695 assert_eq!(ns.slice(Some(3), Some(5), 1).unwrap().len(), 1);
1696 assert_eq!(sl(&ns, Some(2), Some(3), 1), "bur3");
1697 assert_eq!(sl(&ns, Some(3), Some(4), 1), "foo");
1698 assert_eq!(sl(&ns, Some(0), Some(2), 1), "bar,bur1");
1699 assert_eq!(sl(&ns, Some(1), Some(3), 1), "bur[1,3]");
1700 let ns = NodeSet::parse("yeti[10-98/2]").unwrap();
1702 assert_eq!(sl(&ns, Some(1), Some(9), 3), "yeti[12,18,24]");
1703 assert_eq!(sl(&ns, None, None, 17), "yeti[10,44,78]");
1704 let ns = NodeSet::parse_with_autostep("yeti[10-98/2]", Some(2)).unwrap();
1705 assert_eq!(sl(&ns, Some(22), Some(29), 1), "yeti[54-66/2]");
1706 let ns = NodeSet::parse_with_autostep("yeti[10-9800/2]", Some(2)).unwrap();
1708 assert_eq!(sl(&ns, Some(22), Some(2900), 1), "yeti[54-5808/2]");
1709 assert_eq!(sl(&ns, Some(22), Some(2900), 3), "yeti[54-5808/6]");
1710 let ns = NodeSet::parse("yeti[10-14,20-26,30-33]").unwrap();
1711 assert_eq!(sl(&ns, Some(2), Some(6), 1), "yeti[12-14,20]");
1712 let ns = NodeSet::parse("stone[1-9],wood[1-9]").unwrap();
1714 assert_eq!(sl(&ns, None, None, 1), "stone[1-9],wood[1-9]");
1715 assert_eq!(sl(&ns, Some(1), Some(2), 1), "stone2");
1716 assert_eq!(sl(&ns, Some(8), Some(9), 1), "stone9");
1717 assert_eq!(sl(&ns, Some(8), Some(10), 1), "stone9,wood1");
1718 assert_eq!(sl(&ns, Some(9), Some(10), 1), "wood1");
1719 assert_eq!(sl(&ns, Some(9), None, 1), "wood[1-9]");
1720 assert_eq!(sl(&ns, None, None, 2), "stone[1,3,5,7,9],wood[2,4,6,8]");
1722 assert_eq!(sl(&ns, Some(1), None, 2), "stone[2,4,6,8],wood[1,3,5,7,9]");
1723 assert_eq!(sl(&ns, Some(2), Some(15), 4), "stone[3,7],wood[2,6]");
1724 assert_eq!(sl(&ns, None, None, -2), "stone[2,4,6,8],wood[1,3,5,7,9]");
1725 let ns = NodeSet::parse("stone[1-9],water[10-12],wood[1-9]").unwrap();
1726 assert_eq!(sl(&ns, Some(8), Some(10), 1), "stone9,water10");
1727 assert_eq!(sl(&ns, Some(11), Some(15), 1), "water12,wood[1-3]");
1728 let ns = NodeSet::parse("stone[1-9],water,wood[1-9]").unwrap();
1729 assert_eq!(sl(&ns, Some(8), Some(10), 1), "stone9,water");
1730 assert_eq!(sl(&ns, Some(8), Some(11), 1), "stone9,water,wood1");
1731 assert_eq!(sl(&ns, Some(9), Some(11), 1), "water,wood1");
1732 assert_eq!(sl(&ns, Some(9), Some(12), 1), "water,wood[1-2]");
1733 assert_eq!(sl(&ns, Some(8), None, 2), "stone9,wood[1,3,5,7,9]");
1734 }
1735
1736 #[test]
1737 fn test_slice_bad_params() {
1738 let ns = NodeSet::parse("cluster[1-30]c[1-2]").unwrap();
1741 assert!(ns.slice(None, None, 0).is_err());
1743 assert!(ns.slice(Some(1), None, -1).is_err());
1745 assert!(ns.slice(None, Some(2), -2).is_err());
1746 assert!(ns.slice(None, None, -1).is_ok());
1748 }
1749}