1use std::cmp::Ordering;
2use std::fmt;
3use std::str::FromStr;
4
5use super::Patterns;
6
7#[derive(Clone, Debug, PartialEq, Eq)]
9#[non_exhaustive]
10pub enum IntersectionError {
11 TooManyPatterns,
13}
14
15impl fmt::Display for IntersectionError {
16 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17 match self {
18 Self::TooManyPatterns => write!(f, "pattern intersection exceeds the complexity limit"),
19 }
20 }
21}
22
23impl std::error::Error for IntersectionError {}
24
25#[derive(Clone, Debug, PartialEq, Eq)]
27#[non_exhaustive]
28pub enum InvalidPattern {
29 EmptySegment,
31 InvalidSegment(String),
35 MultipleGlobstars,
37 TooManySegments,
39}
40
41impl fmt::Display for InvalidPattern {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 match self {
44 Self::EmptySegment => write!(f, "empty path segment"),
45 Self::InvalidSegment(segment) => write!(f, "invalid pattern segment: {segment:?}"),
46 Self::MultipleGlobstars => write!(f, "more than one ** segment"),
47 Self::TooManySegments => write!(f, "more than {} segments", Pattern::MAX_SEGMENTS),
48 }
49 }
50}
51
52impl std::error::Error for InvalidPattern {}
53
54#[derive(Clone, Debug, PartialEq, Eq, Hash)]
56#[non_exhaustive]
57pub enum Segment {
58 Literal(String),
60 Wildcard,
62 Partial {
65 prefix: String,
67 suffix: String,
69 },
70 Globstar,
72}
73
74impl Segment {
75 fn parse(text: &str) -> Result<Self, InvalidPattern> {
77 match text {
78 "" => Err(InvalidPattern::EmptySegment),
79 "*" => Ok(Self::Wildcard),
80 "**" => Ok(Self::Globstar),
81 _ if text.contains('/') => Err(InvalidPattern::InvalidSegment(text.to_string())),
82 _ => match text.split_once('*') {
83 None => Ok(Self::Literal(text.to_string())),
84 Some((prefix, suffix)) if !suffix.contains('*') => Ok(Self::Partial {
85 prefix: prefix.to_string(),
86 suffix: suffix.to_string(),
87 }),
88 Some(_) => Err(InvalidPattern::InvalidSegment(text.to_string())),
90 },
91 }
92 }
93
94 fn covers(&self, other: &Self) -> bool {
98 match (self, other) {
99 (Self::Wildcard, Self::Literal(_) | Self::Partial { .. } | Self::Wildcard) => true,
100 (Self::Literal(a), Self::Literal(b)) => a == b,
101 (Self::Partial { .. }, Self::Literal(literal)) => self.matches(literal),
102 (
105 Self::Partial { prefix, suffix },
106 Self::Partial {
107 prefix: other_prefix,
108 suffix: other_suffix,
109 },
110 ) => other_prefix.starts_with(prefix.as_str()) && other_suffix.ends_with(suffix.as_str()),
111 _ => false,
112 }
113 }
114
115 fn compatible(&self, other: &Self) -> bool {
117 match (self, other) {
118 (
121 Self::Partial { prefix, suffix },
122 Self::Partial {
123 prefix: other_prefix,
124 suffix: other_suffix,
125 },
126 ) => {
127 (prefix.starts_with(other_prefix.as_str()) || other_prefix.starts_with(prefix.as_str()))
128 && (suffix.ends_with(other_suffix.as_str()) || other_suffix.ends_with(suffix.as_str()))
129 }
130 _ => self.covers(other) || other.covers(self),
131 }
132 }
133
134 fn matches(&self, part: &str) -> bool {
136 match self {
137 Self::Literal(literal) => literal == part,
138 Self::Wildcard => true,
139 Self::Partial { prefix, suffix } => {
140 part.len() >= prefix.len() + suffix.len()
141 && part.starts_with(prefix.as_str())
142 && part.ends_with(suffix.as_str())
143 }
144 Self::Globstar => false,
145 }
146 }
147
148 fn intersect(&self, other: &Self) -> Vec<Self> {
151 match (self, other) {
152 (Self::Globstar, _) | (_, Self::Globstar) => Vec::new(),
153 (Self::Wildcard, other) => vec![other.clone()],
154 (this, Self::Wildcard) => vec![this.clone()],
155 (Self::Literal(a), Self::Literal(b)) => (a == b).then(|| self.clone()).into_iter().collect(),
156 (Self::Literal(literal), partial @ Self::Partial { .. })
157 | (partial @ Self::Partial { .. }, Self::Literal(literal)) => partial
158 .matches(literal)
159 .then(|| Self::Literal(literal.clone()))
160 .into_iter()
161 .collect(),
162 (
163 Self::Partial { prefix, suffix },
164 Self::Partial {
165 prefix: other_prefix,
166 suffix: other_suffix,
167 },
168 ) => {
169 if !self.compatible(other) {
170 return Vec::new();
171 }
172 let prefix = if prefix.len() >= other_prefix.len() {
176 prefix
177 } else {
178 other_prefix
179 };
180 let suffix = if suffix.len() >= other_suffix.len() {
181 suffix
182 } else {
183 other_suffix
184 };
185 let mut out = vec![Self::Partial {
186 prefix: prefix.clone(),
187 suffix: suffix.clone(),
188 }];
189 for overlap in 1..=prefix.len().min(suffix.len()) {
190 if !prefix.is_char_boundary(prefix.len() - overlap) || !suffix.is_char_boundary(overlap) {
191 continue;
192 }
193 if prefix[prefix.len() - overlap..] != suffix[..overlap] {
194 continue;
195 }
196 let part = format!("{prefix}{}", &suffix[overlap..]);
197 if self.matches(&part) && other.matches(&part) {
198 out.push(Self::Literal(part));
199 }
200 }
201 out
202 }
203 }
204 }
205}
206
207fn intersect_run(a: &[Segment], b: &[Segment], limit: usize) -> Result<Vec<Vec<Segment>>, IntersectionError> {
211 debug_assert_eq!(a.len(), b.len());
212 let mut out: Vec<Vec<Segment>> = vec![Vec::with_capacity(a.len())];
213 for (a, b) in a.iter().zip(b) {
214 let choices = a.intersect(b);
215 if choices.is_empty() {
216 return Ok(Vec::new());
217 }
218 if out.len().checked_mul(choices.len()).is_none_or(|size| size > limit) {
219 return Err(IntersectionError::TooManyPatterns);
220 }
221 out = out
222 .iter()
223 .flat_map(|prefix| {
224 choices.iter().map(move |choice| {
225 let mut next = prefix.clone();
226 next.push(choice.clone());
227 next
228 })
229 })
230 .collect();
231 }
232 Ok(out)
233}
234
235fn insert_intersection(
236 out: &mut Patterns,
237 remaining: &mut usize,
238 segments: Vec<Segment>,
239) -> Result<(), IntersectionError> {
240 if *remaining == 0 {
241 return Err(IntersectionError::TooManyPatterns);
242 }
243 *remaining -= 1;
244 if let Ok(pattern) = Pattern::new(segments) {
245 out.insert(pattern);
246 }
247 Ok(())
248}
249
250fn intersect_into(
251 a: &[Segment],
252 b: &[Segment],
253 out: &mut Patterns,
254 remaining: &mut usize,
255) -> Result<(), IntersectionError> {
256 for run in intersect_run(a, b, *remaining)? {
257 insert_intersection(out, remaining, run)?;
258 }
259 Ok(())
260}
261
262fn expand(head: &[Segment], tail: &[Segment], len: usize) -> Vec<Segment> {
264 let mut out = head.to_vec();
265 out.extend(std::iter::repeat_n(Segment::Wildcard, len - head.len() - tail.len()));
266 out.extend_from_slice(tail);
267 out
268}
269
270impl fmt::Display for Segment {
271 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
272 match self {
273 Self::Literal(literal) => f.write_str(literal),
274 Self::Wildcard => f.write_str("*"),
275 Self::Partial { prefix, suffix } => write!(f, "{prefix}*{suffix}"),
276 Self::Globstar => f.write_str("**"),
277 }
278 }
279}
280
281#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
292pub struct Specificity {
293 literals: usize,
294 exact: bool,
295 partials: usize,
296 wildcards: usize,
297 pinned: usize,
298 head: usize,
299}
300
301#[derive(Clone, PartialEq, Eq, Hash)]
311pub struct Pattern {
312 text: String,
313 segments: Vec<Segment>,
314 globstar: Option<usize>,
316 head: usize,
318}
319
320impl Pattern {
321 pub const MAX_SEGMENTS: usize = 32;
323 pub const MAX_INTERSECTIONS: usize = 1024;
325
326 pub fn new(segments: impl IntoIterator<Item = Segment>) -> Result<Self, InvalidPattern> {
328 let mut segments: Vec<Segment> = segments.into_iter().collect();
329 if segments.len() > Self::MAX_SEGMENTS {
330 return Err(InvalidPattern::TooManySegments);
331 }
332
333 let mut globstar = None;
334 for (i, segment) in segments.iter().enumerate() {
335 match segment {
336 Segment::Literal(literal) if literal.is_empty() => return Err(InvalidPattern::EmptySegment),
337 Segment::Literal(literal) if literal.contains(['*', '/']) => {
338 return Err(InvalidPattern::InvalidSegment(literal.clone()));
339 }
340 Segment::Partial { prefix, suffix }
341 if (prefix.is_empty() && suffix.is_empty())
342 || prefix.contains(['*', '/'])
343 || suffix.contains(['*', '/']) =>
344 {
345 return Err(InvalidPattern::InvalidSegment(format!("{prefix}*{suffix}")));
346 }
347 Segment::Globstar if globstar.is_some() => return Err(InvalidPattern::MultipleGlobstars),
348 Segment::Globstar => globstar = Some(i),
349 _ => {}
350 }
351 }
352
353 if let Some(mut index) = globstar {
355 while index > 0 && segments[index - 1] == Segment::Wildcard {
356 segments.swap(index - 1, index);
357 index -= 1;
358 }
359 globstar = Some(index);
360 }
361
362 let mut text = String::new();
363 let mut head = 0;
364 let mut in_head = true;
365 for (i, segment) in segments.iter().enumerate() {
366 if i > 0 {
367 text.push('/');
368 }
369 match segment {
370 Segment::Literal(literal) => text.push_str(literal),
371 other => {
372 in_head = false;
373 text.push_str(&other.to_string());
374 }
375 }
376 if in_head {
377 head = text.len();
378 }
379 }
380
381 Ok(Self {
382 text,
383 segments,
384 globstar,
385 head,
386 })
387 }
388
389 pub fn all() -> Self {
391 Self::new([Segment::Globstar]).expect("** is valid")
392 }
393
394 pub fn literal(path: &str) -> Result<Self, InvalidPattern> {
400 Self::new(literal_segments(path))
401 }
402
403 pub fn subtree(path: &str) -> Result<Self, InvalidPattern> {
408 Self::new(literal_segments(path).chain([Segment::Globstar]))
409 }
410
411 pub fn as_str(&self) -> &str {
413 &self.text
414 }
415
416 pub fn segments(&self) -> &[Segment] {
418 &self.segments
419 }
420
421 pub fn head(&self) -> &str {
426 &self.text[..self.head]
427 }
428
429 pub fn is_literal(&self) -> bool {
431 self.head == self.text.len()
432 }
433
434 pub fn as_prefix(&self) -> Option<&str> {
439 match self.segments.split_last() {
440 Some((Segment::Globstar, head)) if head.iter().all(|s| matches!(s, Segment::Literal(_))) => {
441 Some(self.head())
442 }
443 _ => None,
444 }
445 }
446
447 pub fn has_globstar(&self) -> bool {
449 self.globstar.is_some()
450 }
451
452 pub fn matches(&self, path: &str) -> bool {
456 let parts: Vec<&str> = split_path(path).collect();
457 match self.globstar {
458 None => {
459 parts.len() == self.segments.len()
460 && self
461 .segments
462 .iter()
463 .zip(&parts)
464 .all(|(segment, part)| segment.matches(part))
465 }
466 Some(_) => {
467 let (head, tail) = self.split();
468 parts.len() >= head.len() + tail.len()
469 && head.iter().zip(&parts).all(|(segment, part)| segment.matches(part))
470 && tail
471 .iter()
472 .rev()
473 .zip(parts.iter().rev())
474 .all(|(segment, part)| segment.matches(part))
475 }
476 }
477 }
478
479 pub fn contains(&self, other: &Self) -> bool {
484 match (self.globstar, other.globstar) {
485 (None, None) => {
486 self.segments.len() == other.segments.len()
487 && self.segments.iter().zip(&other.segments).all(|(a, b)| a.covers(b))
488 }
489 (None, Some(_)) => false,
491 (Some(_), None) => {
492 let (head, tail) = self.split();
493 other.segments.len() >= head.len() + tail.len()
494 && head.iter().zip(&other.segments).all(|(a, b)| a.covers(b))
495 && tail
496 .iter()
497 .rev()
498 .zip(other.segments.iter().rev())
499 .all(|(a, b)| a.covers(b))
500 }
501 (Some(_), Some(_)) => {
502 let (head, tail) = self.split();
503 let (other_head, other_tail) = other.split();
504
505 let covers_run = |ours: &[Segment], theirs: &[Segment]| {
509 ours.iter().enumerate().all(|(i, a)| match theirs.get(i) {
510 Some(b) => a.covers(b),
511 None => *a == Segment::Wildcard,
512 })
513 };
514 let reversed = |run: &[Segment]| run.iter().rev().cloned().collect::<Vec<_>>();
515
516 head.len() + tail.len() <= other_head.len() + other_tail.len()
517 && covers_run(head, other_head)
518 && covers_run(&reversed(tail), &reversed(other_tail))
519 }
520 }
521 }
522
523 pub fn overlaps(&self, other: &Self) -> bool {
525 let compatible_run = |a: &[Segment], b: &[Segment]| a.iter().zip(b).all(|(a, b)| a.compatible(b));
526 let compatible_tail =
527 |a: &[Segment], b: &[Segment]| a.iter().rev().zip(b.iter().rev()).all(|(a, b)| a.compatible(b));
528
529 match (self.globstar, other.globstar) {
530 (None, None) => {
531 self.segments.len() == other.segments.len() && compatible_run(&self.segments, &other.segments)
532 }
533 (None, Some(_)) => other.overlaps(self),
534 (Some(_), None) => {
535 let (head, tail) = self.split();
536 other.segments.len() >= head.len() + tail.len()
537 && compatible_run(head, &other.segments)
538 && compatible_tail(tail, &other.segments)
539 }
540 (Some(_), Some(_)) => {
541 let (head, tail) = self.split();
544 let (other_head, other_tail) = other.split();
545 compatible_run(head, other_head) && compatible_tail(tail, other_tail)
546 }
547 }
548 }
549
550 pub fn specificity(&self) -> Specificity {
552 let count = |wanted: fn(&Segment) -> bool| self.segments.iter().filter(|s| wanted(s)).count();
553 Specificity {
554 literals: count(|s| matches!(s, Segment::Literal(_))),
555 exact: self.globstar.is_none(),
556 partials: count(|s| matches!(s, Segment::Partial { .. })),
557 wildcards: count(|s| matches!(s, Segment::Wildcard)),
558 pinned: self
559 .segments
560 .iter()
561 .map(|s| match s {
562 Segment::Partial { prefix, suffix } => prefix.len() + suffix.len(),
563 _ => 0,
564 })
565 .sum(),
566 head: self
567 .segments
568 .iter()
569 .take_while(|s| matches!(s, Segment::Literal(_)))
570 .count(),
571 }
572 }
573
574 pub fn rebase(&self, root: &str) -> Patterns {
583 let root: Vec<&str> = split_path(root).collect();
584 let mut out = Patterns::new();
585
586 let matches_run = |segments: &[Segment], parts: &[&str]| segments.iter().zip(parts).all(|(s, p)| s.matches(p));
587 let build = |segments: &[Segment]| Pattern::new(segments.to_vec()).expect("a rebased pattern is valid");
590
591 match self.globstar {
592 None => {
593 if root.len() <= self.segments.len() && matches_run(&self.segments, &root) {
594 out.insert(build(&self.segments[root.len()..]));
595 }
596 }
597 Some(index) => {
598 let (head, tail) = self.split();
599 if root.len() <= head.len() {
600 if matches_run(head, &root) {
601 out.insert(build(&self.segments[root.len()..]));
602 }
603 return out;
604 }
605 if !matches_run(head, &root) {
606 return out;
607 }
608
609 let rest = &root[head.len()..];
613 out.insert(build(&self.segments[index..]));
614 for consumed in 1..=tail.len().min(rest.len()) {
615 if matches_run(&tail[..consumed], &rest[rest.len() - consumed..]) {
616 out.insert(build(&tail[consumed..]));
617 }
618 }
619 }
620 }
621
622 out
623 }
624
625 pub fn intersect(&self, other: &Self) -> Result<Patterns, IntersectionError> {
633 if self.contains(other) {
636 return Ok(Patterns::from(other.clone()));
637 }
638 if other.contains(self) {
639 return Ok(Patterns::from(self.clone()));
640 }
641
642 let mut out = Patterns::new();
643 let mut remaining = Self::MAX_INTERSECTIONS;
644
645 match (self.globstar, other.globstar) {
646 (None, None) => {
647 if self.segments.len() == other.segments.len() {
648 intersect_into(&self.segments, &other.segments, &mut out, &mut remaining)?;
649 }
650 }
651 (Some(_), None) => {
652 let (head, tail) = self.split();
653 if other.segments.len() >= head.len() + tail.len() {
654 let stretched = expand(head, tail, other.segments.len());
655 intersect_into(&stretched, &other.segments, &mut out, &mut remaining)?;
656 }
657 }
658 (None, Some(_)) => return other.intersect(self),
659 (Some(_), Some(_)) => {
660 let (head, tail) = self.split();
661 let (other_head, other_tail) = other.split();
662 let heads = head.len().max(other_head.len());
663 let tails = tail.len().max(other_tail.len());
664 let shortest = (head.len() + tail.len()).max(other_head.len() + other_tail.len());
665
666 let long = heads + tails;
670 let open = long < Self::MAX_SEGMENTS;
671 let cap = if open { long } else { Self::MAX_SEGMENTS + 1 };
672 for len in shortest..cap {
673 let a = expand(head, tail, len);
674 let b = expand(other_head, other_tail, len);
675 intersect_into(&a, &b, &mut out, &mut remaining)?;
676 }
677
678 if open {
681 let pad = |run: &[Segment], len: usize, front: bool| -> Vec<Segment> {
682 let fill = std::iter::repeat_n(Segment::Wildcard, len - run.len());
683 if front {
684 run.iter().cloned().chain(fill).collect()
685 } else {
686 fill.chain(run.iter().cloned()).collect()
687 }
688 };
689 let fronts = intersect_run(&pad(head, heads, true), &pad(other_head, heads, true), remaining)?;
690 let backs = intersect_run(&pad(tail, tails, false), &pad(other_tail, tails, false), remaining)?;
691 if fronts
692 .len()
693 .checked_mul(backs.len())
694 .is_none_or(|size| size > remaining)
695 {
696 return Err(IntersectionError::TooManyPatterns);
697 }
698 for front in &fronts {
699 for back in &backs {
700 let mut segments = front.clone();
701 segments.push(Segment::Globstar);
702 segments.extend_from_slice(back);
703 insert_intersection(&mut out, &mut remaining, segments)?;
704 }
705 }
706 }
707 }
708 }
709
710 Ok(out)
711 }
712
713 pub fn captures(&self, matched: &Self) -> Option<Vec<Self>> {
725 if !self.contains(matched) {
726 return None;
727 }
728 let build = |segments: &[Segment]| Pattern::new(segments.to_vec()).expect("a capture is valid");
731 let mut out = Vec::new();
732
733 if self.globstar.is_none() {
734 for (segment, theirs) in self.segments.iter().zip(&matched.segments) {
735 if !matches!(segment, Segment::Literal(_)) {
736 out.push(build(std::slice::from_ref(theirs)));
737 }
738 }
739 return Some(out);
740 }
741
742 let (head, tail) = self.split();
743 let middle = matched.segments.len() - tail.len();
744 let free = matched.globstar;
748 let pinned = |at: usize, from_front: bool| match free {
749 Some(free) if from_front => at < free,
750 Some(free) => at > free,
751 None => true,
752 };
753
754 for (i, segment) in head.iter().enumerate() {
755 if !matches!(segment, Segment::Literal(_)) {
756 let capture = if pinned(i, true) { &matched.segments[i] } else { segment };
757 out.push(build(std::slice::from_ref(capture)));
758 }
759 }
760 if free.is_none_or(|free| free >= head.len() && free < middle) {
761 out.push(build(&matched.segments[head.len()..middle]));
762 } else {
763 out.push(Pattern::all());
764 }
765 for (j, segment) in tail.iter().enumerate() {
766 if !matches!(segment, Segment::Literal(_)) {
767 let at = middle + j;
768 let capture = if pinned(at, false) {
769 &matched.segments[at]
770 } else {
771 segment
772 };
773 out.push(build(std::slice::from_ref(capture)));
774 }
775 }
776 Some(out)
777 }
778
779 pub fn rooted(&self, root: &str) -> Result<Self, InvalidPattern> {
785 Self::new(literal_segments(root).chain(self.segments.iter().cloned()))
786 }
787
788 fn split(&self) -> (&[Segment], &[Segment]) {
790 match self.globstar {
791 Some(index) => (&self.segments[..index], &self.segments[index + 1..]),
792 None => (&self.segments, &[]),
793 }
794 }
795}
796
797fn split_path(path: &str) -> impl Iterator<Item = &str> {
800 path.split('/').filter(|part| !part.is_empty())
801}
802
803fn literal_segments(path: &str) -> impl Iterator<Item = Segment> + '_ {
805 split_path(path).map(|part| Segment::Literal(part.to_string()))
808}
809
810impl FromStr for Pattern {
811 type Err = InvalidPattern;
812
813 fn from_str(text: &str) -> Result<Self, InvalidPattern> {
816 if text.is_empty() {
817 return Self::new([]);
818 }
819 text.split('/')
820 .map(Segment::parse)
821 .collect::<Result<Vec<_>, _>>()
822 .and_then(Self::new)
823 }
824}
825
826impl TryFrom<&str> for Pattern {
827 type Error = InvalidPattern;
828
829 fn try_from(text: &str) -> Result<Self, InvalidPattern> {
830 text.parse()
831 }
832}
833
834impl TryFrom<String> for Pattern {
835 type Error = InvalidPattern;
836
837 fn try_from(text: String) -> Result<Self, InvalidPattern> {
838 text.parse()
839 }
840}
841
842impl Default for Pattern {
843 fn default() -> Self {
845 Self::new([]).expect("the empty pattern is valid")
846 }
847}
848
849impl fmt::Display for Pattern {
850 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
851 f.write_str(&self.text)
852 }
853}
854
855impl fmt::Debug for Pattern {
856 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
857 write!(f, "Pattern({:?})", self.text)
858 }
859}
860
861impl PartialOrd for Pattern {
862 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
863 Some(self.cmp(other))
864 }
865}
866
867impl Ord for Pattern {
868 fn cmp(&self, other: &Self) -> Ordering {
870 self.text.cmp(&other.text)
871 }
872}
873
874#[cfg(feature = "serde")]
875impl serde::Serialize for Pattern {
876 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
877 serializer.serialize_str(self.as_str())
878 }
879}
880
881#[cfg(feature = "serde")]
882impl<'de> serde::Deserialize<'de> for Pattern {
883 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
885 let text = <std::borrow::Cow<'de, str>>::deserialize(deserializer)?;
886 text.parse().map_err(serde::de::Error::custom)
887 }
888}
889
890impl AsRef<str> for Pattern {
891 fn as_ref(&self) -> &str {
892 &self.text
893 }
894}
895
896#[cfg(test)]
897mod tests {
898 use super::*;
899
900 fn pattern(text: &str) -> Pattern {
901 text.parse().unwrap_or_else(|err| panic!("{text:?}: {err}"))
902 }
903
904 #[test]
905 fn parses_and_prints_canonically() {
906 for text in [
907 "",
908 "a",
909 "a/b",
910 "*",
911 "**",
912 "a/*/b",
913 "**/transcode.pro",
914 "a/**/b/*",
915 "**/*",
916 "**/*.hang",
917 "foo*",
918 "foo.*.hang",
919 ] {
920 assert_eq!(pattern(text).to_string(), text);
921 }
922 assert_eq!(
923 pattern("a/*/**/b").segments(),
924 &[
925 Segment::Literal("a".into()),
926 Segment::Globstar,
927 Segment::Wildcard,
928 Segment::Literal("b".into()),
929 ]
930 );
931 }
932
933 #[test]
934 fn rejects_bad_syntax() {
935 assert_eq!("/a".parse::<Pattern>(), Err(InvalidPattern::EmptySegment));
936 assert_eq!("a/".parse::<Pattern>(), Err(InvalidPattern::EmptySegment));
937 assert_eq!("a//b".parse::<Pattern>(), Err(InvalidPattern::EmptySegment));
938 assert_eq!("/".parse::<Pattern>(), Err(InvalidPattern::EmptySegment));
939 assert_eq!("**/**".parse::<Pattern>(), Err(InvalidPattern::MultipleGlobstars));
940 assert_eq!(
941 "***".parse::<Pattern>(),
942 Err(InvalidPattern::InvalidSegment("***".into()))
943 );
944
945 assert_eq!(
946 "a*b*c".parse::<Pattern>(),
947 Err(InvalidPattern::InvalidSegment("a*b*c".into()))
948 );
949 assert_eq!(
950 "*a*".parse::<Pattern>(),
951 Err(InvalidPattern::InvalidSegment("*a*".into()))
952 );
953 assert_eq!(
954 "*.hang".parse::<Pattern>().unwrap().segments(),
955 &[Segment::Partial {
956 prefix: String::new(),
957 suffix: ".hang".into()
958 }]
959 );
960 assert_eq!(
961 Pattern::new([Segment::Partial {
962 prefix: String::new(),
963 suffix: String::new()
964 }]),
965 Err(InvalidPattern::InvalidSegment("*".into()))
966 );
967 assert_eq!(
968 Pattern::new([Segment::Partial {
969 prefix: "a/".into(),
970 suffix: String::new()
971 }]),
972 Err(InvalidPattern::InvalidSegment("a/*".into()))
973 );
974
975 let deep = ["a"; Pattern::MAX_SEGMENTS + 1].join("/");
976 assert_eq!(deep.parse::<Pattern>(), Err(InvalidPattern::TooManySegments));
977 let max = ["a"; Pattern::MAX_SEGMENTS].join("/");
978 assert!(max.parse::<Pattern>().is_ok());
979
980 assert_eq!(
981 Pattern::new([Segment::Literal("a/b".into())]),
982 Err(InvalidPattern::InvalidSegment("a/b".into()))
983 );
984 assert_eq!(
985 Pattern::new([Segment::Literal(String::new())]),
986 Err(InvalidPattern::EmptySegment)
987 );
988 }
989
990 #[test]
991 fn literal_and_subtree_normalize_paths() {
992 assert_eq!(Pattern::literal("/foo//bar/").unwrap(), pattern("foo/bar"));
993 assert_eq!(Pattern::literal("").unwrap(), Pattern::default());
994 assert_eq!(Pattern::subtree("foo").unwrap(), pattern("foo/**"));
995 assert_eq!(Pattern::subtree("/").unwrap(), Pattern::all());
996 assert_eq!(Pattern::literal("a/*"), Err(InvalidPattern::InvalidSegment("*".into())));
997 assert_eq!(Pattern::literal("**"), Err(InvalidPattern::InvalidSegment("**".into())));
998 }
999
1000 #[test]
1001 fn as_prefix_accepts_literals_then_globstar() {
1002 assert_eq!(Pattern::all().as_prefix(), Some(""));
1003 assert_eq!(pattern("foo/**").as_prefix(), Some("foo"));
1004 assert_eq!(pattern("foo/bar/**").as_prefix(), Some("foo/bar"));
1005 assert_eq!(pattern("foo").as_prefix(), None);
1006 assert_eq!(Pattern::default().as_prefix(), None);
1007 assert_eq!(pattern("foo/*").as_prefix(), None);
1008 assert_eq!(pattern("*/foo/**").as_prefix(), None);
1009 assert_eq!(pattern("foo/**/bar").as_prefix(), None);
1010 assert_eq!(pattern("foo*/**").as_prefix(), None);
1011 }
1012
1013 #[test]
1014 fn matches_whole_segments() {
1015 let cases = [
1016 ("", "", true),
1017 ("", "a", false),
1018 ("a", "a", true),
1019 ("a", "a/b", false),
1020 ("a", "ab", false),
1021 ("*", "a", true),
1022 ("*", "", false),
1023 ("*", "a/b", false),
1024 ("**", "", true),
1025 ("**", "a/b/c", true),
1026 ("a/**", "a", true),
1027 ("a/**", "a/b/c", true),
1028 ("a/**", "b", false),
1029 ("**/c", "c", true),
1030 ("**/c", "a/b/c", true),
1031 ("**/c", "a/c/b", false),
1032 ("a/**/c", "a/c", true),
1033 ("a/**/c", "a/x/y/c", true),
1034 ("a/**/c", "a", false),
1035 ("a/*/c", "a/x/c", true),
1036 ("a/*/c", "a/c", false),
1037 ("a/*/**", "a", false),
1038 ("a/*/**", "a/b", true),
1039 ("**/transcode.pro", "pid/foo.hang/transcode.pro", true),
1040 ("**/transcode.pro", "pid/foo.transcode.pro", false),
1041 ("**/*.hang", "pid/cam.hang", true),
1042 ("**/*.hang", ".hang", true),
1043 ("**/*.hang", "pid/cam.hang/x", false),
1044 ("foo*", "foo", true),
1045 ("foo*", "foobar", true),
1046 ("foo*", "fo", false),
1047 ("foo.*.hang", "foo..hang", true),
1048 ("foo.*.hang", "foo.1.hang", true),
1049 ("foo.*.hang", "foo.hang", false),
1050 ("a*a", "a", false),
1051 ("a*a", "aa", true),
1052 ];
1053 for (text, path, expected) in cases {
1054 assert_eq!(pattern(text).matches(path), expected, "{text} vs {path}");
1055 }
1056 assert!(pattern("a/b").matches("/a//b/"));
1058 }
1059
1060 #[test]
1061 fn contains_is_containment() {
1062 let cases = [
1063 ("**", "**", true),
1064 ("**", "", true),
1065 ("**", "a/*/b", true),
1066 ("", "**", false),
1067 ("*", "a", true),
1068 ("a", "*", false),
1069 ("a/**", "a", true),
1070 ("a/**", "a/b/**", true),
1071 ("a/**", "**", false),
1072 ("a/**", "**/a", false),
1073 ("**/a", "a", true),
1074 ("**/a", "**/b/a", true),
1075 ("**/a", "a/**", false),
1076 ("*/**", "**", false),
1077 ("*/**", "a/**", true),
1078 ("*/*/**", "a/**", false),
1079 ("*/*/**", "a/b/**", true),
1080 ("a/**/c", "a/c", true),
1081 ("a/**/c", "a/x/c", true),
1082 ("a/**/c", "a/**/x/c", true),
1083 ("a/*/**/*", "a/**/b", false),
1084 ("a/*/c", "a/b/c", true),
1085 ("a/*/c", "a/**/c", false),
1086 ("*", "*.hang", true),
1087 ("*.hang", "*", false),
1088 ("*.hang", "cam.hang", true),
1089 ("*.hang", "cam.hang2", false),
1090 ("*.hang", "*.hang", true),
1091 ("*.hang", "cam*.hang", true),
1092 ("*.hang", "cam*hang", false),
1093 ("foo*", "foo.*.hang", true),
1094 ("foo.*", "foo*", false),
1095 ("**/*.hang", "pid/*/cam.hang", true),
1096 ];
1097 for (outer, inner, expected) in cases {
1098 assert_eq!(
1099 pattern(outer).contains(&pattern(inner)),
1100 expected,
1101 "{outer} contains {inner}"
1102 );
1103 }
1104 }
1105
1106 #[test]
1107 fn overlaps_is_symmetric_intersection() {
1108 let cases = [
1109 ("a", "a", true),
1110 ("a", "b", false),
1111 ("a", "*", true),
1112 ("a", "a/*", false),
1113 ("a/**", "**/b", true),
1114 ("a/**", "b/**", false),
1115 ("a/*", "*/b", true),
1116 ("a/*", "b/*", false),
1117 ("*/*", "a/**", true),
1118 ("*", "a/**", true),
1119 ("*", "a/*/**", false),
1120 ("**", "", true),
1121 ("a/**/b", "**/c", false),
1122 ("a/**/b", "**/*", true),
1123 ("*.hang", "cam*", true),
1124 ("*.hang", "cam.msf", false),
1125 ("*.hang", "*.msf", false),
1126 ("foo*", "foo.bar*", true),
1127 ("foo*", "fob*", false),
1128 ("a*b", "ab", true),
1129 ("ab*", "*ab", true),
1130 ("a/**/b", "x/**", false),
1131 ("a/**/b", "**/x", false),
1132 ];
1133 for (a, b, expected) in cases {
1134 assert_eq!(pattern(a).overlaps(&pattern(b)), expected, "{a} overlaps {b}");
1135 assert_eq!(pattern(b).overlaps(&pattern(a)), expected, "{b} overlaps {a}");
1136 }
1137 }
1138
1139 #[test]
1140 fn specificity_ranks_by_what_is_pinned_down() {
1141 let ranked = ["a/b/c", "a/b", "a/*.hang", "a/*", "a/**", "*.hang", "*", "**"];
1143 for pair in ranked.windows(2) {
1144 assert!(
1145 pattern(pair[0]).specificity() > pattern(pair[1]).specificity(),
1146 "{} should outrank {}",
1147 pair[0],
1148 pair[1]
1149 );
1150 }
1151 assert!(pattern("a/**").specificity() > pattern("**/a").specificity());
1153 assert!(pattern("cam*.hang").specificity() > pattern("*.hang").specificity());
1155 assert!(pattern("*.hang").specificity() > pattern("*").specificity());
1156 assert_eq!(pattern("*/a").specificity(), pattern("*/b").specificity());
1158 assert_eq!(pattern("*/a/**").specificity(), pattern("*/**/a").specificity());
1159 }
1160
1161 #[test]
1162 fn rebase_is_set_valued() {
1163 let cases: &[(&str, &str, &[&str])] = &[
1164 ("**", "a", &["**"]),
1165 ("**/a", "a", &["", "**/a"]),
1166 ("a/**", "a", &["**"]),
1167 ("a/**", "a/b", &["**"]),
1168 ("a/**", "b", &[]),
1169 ("a/b", "a", &["b"]),
1170 ("a/b", "a/b", &[""]),
1171 ("a/b", "a/b/c", &[]),
1172 ("*/b", "a", &["b"]),
1173 ("a/*/c", "a/x", &["c"]),
1174 ("a/**/b/c", "a/b", &["**/b/c", "c"]),
1175 ("a/**/b", "a/b/b", &["**/b", ""]),
1176 ("**/b/c", "b", &["**/b/c", "c"]),
1177 ("", "", &[""]),
1178 ("", "a", &[]),
1179 ("**", "", &["**"]),
1180 ("*.hang/**", "cam.hang", &["**"]),
1181 ("*.hang/**", "cam.msf", &[]),
1182 ("**/*.hang", "a.hang", &["", "**/*.hang"]),
1183 ];
1184 for (text, root, expected) in cases {
1185 let got = pattern(text).rebase(root);
1186 let expected: Patterns = expected.iter().map(|e| pattern(e)).collect();
1187 assert_eq!(got, expected, "{text} rebased at {root}");
1188 }
1189 }
1190
1191 #[test]
1192 fn rooted_inverts_rebase() {
1193 assert_eq!(pattern("**").rooted("a/b").unwrap(), pattern("a/b/**"));
1194 assert_eq!(pattern("").rooted("a").unwrap(), pattern("a"));
1195 assert_eq!(pattern("*/c").rooted("").unwrap(), pattern("*/c"));
1196 assert_eq!(
1197 pattern("a").rooted("*"),
1198 Err(InvalidPattern::InvalidSegment("*".into()))
1199 );
1200
1201 let deep = ["a"; Pattern::MAX_SEGMENTS].join("/");
1202 assert_eq!(pattern("b").rooted(&deep), Err(InvalidPattern::TooManySegments));
1203 }
1204
1205 #[test]
1206 fn head_is_the_literal_prefix() {
1207 assert_eq!(pattern("a/b/*/c").head(), "a/b");
1208 assert_eq!(pattern("**/a").head(), "");
1209 assert_eq!(pattern("a/b").head(), "a/b");
1210 assert_eq!(pattern("").head(), "");
1211 assert_eq!(pattern("a/b*/c").head(), "a");
1212 assert!(!pattern("a/b*").is_literal());
1213 assert!(pattern("a/b").is_literal());
1214 assert!(!pattern("a/*").is_literal());
1215 assert!(pattern("a/**").has_globstar());
1216 assert!(!pattern("a/*").has_globstar());
1217 }
1218
1219 #[cfg(feature = "serde")]
1220 #[test]
1221 fn serde_round_trips_as_text() {
1222 let p = pattern("a/*/**");
1223 let json = serde_json::to_string(&p).unwrap();
1224 assert_eq!(json, "\"a/**/*\"");
1225 assert_eq!(serde_json::from_str::<Pattern>(&json).unwrap(), p);
1226 assert!(serde_json::from_str::<Pattern>("\"a//b\"").is_err());
1227 }
1228
1229 #[test]
1230 fn ordering_is_by_text() {
1231 let mut list = [pattern("b"), pattern("**"), pattern("a/*"), pattern("a")];
1232 list.sort();
1233 let texts: Vec<_> = list.iter().map(ToString::to_string).collect();
1234 assert_eq!(texts, ["**", "a", "a/*", "b"]);
1235 }
1236}