litcheck_filecheck/pattern/
prefix.rs

1use crate::{
2    ast::{Match, RegexPattern},
3    common::*,
4};
5
6#[derive(Debug)]
7pub enum PatternPrefix<'a> {
8    /// The full pattern is the prefix (a literal string)
9    Literal {
10        id: usize,
11        prefix: Span<Cow<'a, str>>,
12    },
13    /// The prefix is a substring of a larger string or pattern
14    Substring {
15        id: usize,
16        prefix: Span<Cow<'a, str>>,
17    },
18    /// The prefix is a regular expression
19    Regex { id: usize, prefix: RegexPattern<'a> },
20    /// The prefix is a match block or substitution
21    /// The prefix is a match block or substitution
22    Dynamic {
23        id: usize,
24        prefix: Cow<'a, Match<'a>>,
25    },
26}
27impl<'a> PatternPrefix<'a> {
28    pub fn id(&self) -> usize {
29        match self {
30            Self::Literal { id, .. }
31            | Self::Substring { id, .. }
32            | Self::Regex { id, .. }
33            | Self::Dynamic { id, .. } => *id,
34        }
35    }
36
37    pub fn span(&self) -> SourceSpan {
38        match self {
39            Self::Literal { prefix, .. } | Self::Substring { prefix, .. } => prefix.span(),
40            Self::Regex { prefix, .. } => prefix.span(),
41            Self::Dynamic { prefix, .. } => prefix.span(),
42        }
43    }
44
45    pub fn as_str(&self) -> Option<&str> {
46        match self {
47            Self::Literal { prefix, .. } | Self::Substring { prefix, .. } => Some(prefix.as_ref()),
48            Self::Regex { prefix, .. } => Some(prefix.as_ref()),
49            Self::Dynamic { .. } => None,
50        }
51    }
52
53    pub fn to_str(&self) -> Option<Span<Cow<'a, str>>> {
54        match self {
55            Self::Literal { prefix, .. } | Self::Substring { prefix, .. } => Some(prefix.clone()),
56            Self::Regex { prefix, .. } => Some(prefix.pattern.clone()),
57            Self::Dynamic { .. } => None,
58        }
59    }
60
61    pub fn into_str(self) -> Option<Span<Cow<'a, str>>> {
62        match self {
63            Self::Literal { prefix, .. } | Self::Substring { prefix, .. } => Some(prefix),
64            Self::Regex { prefix, .. } => Some(prefix.pattern),
65            Self::Dynamic { .. } => None,
66        }
67    }
68
69    pub fn into_regex_pattern(self) -> Option<RegexPattern<'a>> {
70        match self {
71            Self::Literal { prefix, .. } | Self::Substring { prefix, .. } => Some(
72                RegexPattern::new(prefix.map(|s| Cow::Owned(regex::escape(s.as_ref())))),
73            ),
74            Self::Regex { prefix, .. } => Some(prefix),
75            Self::Dynamic { .. } => None,
76        }
77    }
78
79    pub fn is_regex(&self) -> bool {
80        matches!(self, Self::Regex { .. })
81    }
82
83    pub fn is_regex_compatible(&self) -> bool {
84        matches!(
85            self,
86            Self::Regex { .. } | Self::Literal { .. } | Self::Substring { .. }
87        )
88    }
89
90    pub fn is_dynamic(&self) -> bool {
91        matches!(self, Self::Dynamic { .. })
92    }
93
94    pub fn is_capturing(&self) -> bool {
95        match self {
96            Self::Regex { prefix, .. } if !prefix.captures.is_empty() => true,
97            Self::Dynamic { .. } => true,
98            _ => false,
99        }
100    }
101
102    /// Returns true if `self` starts with `prefix`
103    pub fn overlaps(&self, prefix: &str) -> bool {
104        self.as_str()
105            .map(|p| p.starts_with(prefix))
106            .unwrap_or(false)
107    }
108
109    /// Returns true if `prefix` starts with `self`
110    pub fn is_overlapped_by(&self, prefix: &str) -> bool {
111        self.as_str()
112            .map(|p| prefix.starts_with(p))
113            .unwrap_or(false)
114    }
115
116    /// Returns true if `prefix` is a duplicate of `self`
117    pub fn is_duplicate_prefix(&self, prefix: &str) -> bool {
118        self.as_str().map(|p| p == prefix).unwrap_or(false)
119    }
120}