yash_fnmatch/
lib.rs

1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2022 WATANABE Yuki
3
4//! This crate provides the `fnmatch` function that performs pattern matching
5//! based on a globbing pattern.
6//!
7//! This implementation supports the following syntax in patterns:
8//!
9//! - Any single character (`?`)
10//! - Any character sequence (`*`)
11//! - Bracket expression (`[...]`)
12//!     - Character literals
13//!     - Character ranges (e.g. `a-z`)
14//!     - Complement (`[!...]`)
15//!     - Collating symbols (e.g. `[.ch.]`)
16//!     - Equivalence classes (e.g. `[=a=]`)
17//!     - Character classes (e.g. `[:alpha:]`)
18//!
19//! The current implementation does not support any locale-specific
20//! characteristics. Especially, collating symbols and equivalent classes only
21//! match the specified character sequence itself, and character classes only
22//! match ASCII characters.
23//!
24//! This crate is very similar to the [`fnmatch-regex`] crate in that both
25//! perform matching by converting the pattern to a regular expression. The
26//! `yash-fnmatch` crate tries to support the POSIX specification as much as
27//! possible rather than introducing unique (non-portable) functionalities.
28//!
29//! # Example
30//!
31//! ```
32//! use yash_fnmatch::{Pattern, without_escape};
33//! let p = Pattern::parse(without_escape("r*g")).unwrap();
34//! assert_eq!(p.find("string"), Some(2..6));
35//! ```
36//!
37//! [`fnmatch-regex`]: https://crates.io/crates/fnmatch-regex
38
39pub mod ast;
40mod char_iter;
41
42use self::ast::Ast;
43pub use self::char_iter::*;
44use regex::Regex;
45use regex::RegexBuilder;
46use std::ops::Range;
47use thiserror::Error;
48
49/// Configuration for a pattern
50#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
51#[non_exhaustive]
52pub struct Config {
53    /// Whether the pattern matches only at the beginning of text
54    ///
55    /// For example, the pattern `in` matches the text `begin` iff
56    /// `anchor_begin` is `false`.
57    pub anchor_begin: bool,
58
59    /// Whether the pattern matches only at the end of text
60    ///
61    /// For example, the pattern `mat` matches the text `match` iff `anchor_end`
62    /// is `false`.
63    pub anchor_end: bool,
64
65    /// Whether a leading period has to be matched explicitly
66    ///
67    /// When `literal_period` is `true`, a leading period in the text, if any,
68    /// must be matched by a literal period in the pattern. In other words, a
69    /// wildcard pattern (`*` or `?`) or bracket expression (`[...]`) does not
70    /// match a leading period. For example, the pattern `*.txt` does not match
71    /// the filename `.foo.txt`.
72    ///
73    /// When `literal_period` is `false`, the above restriction does not apply.
74    pub literal_period: bool,
75
76    /// Whether the pattern matches shortest part of text
77    ///
78    /// When matching the pattern `a*a` against the text `banana`, for example,
79    /// the shortest match will be `ana` while the longest `anana`.
80    pub shortest_match: bool,
81
82    /// Whether the pattern should match case-insensitively
83    ///
84    /// For patterns that are literal (i.e., [`Pattern::as_literal`] returns
85    /// `Some(literal)`), this flag is ignored.
86    /// For non-literal patterns, the "simple" case folding rules defined by
87    /// Unicode are applied to allow case-insensitive matches.
88    pub case_insensitive: bool,
89}
90
91/// Error that may happen in building a pattern.
92#[derive(Clone, Debug, Error, PartialEq)]
93#[non_exhaustive]
94pub enum Error {
95    /// Empty bracket expression
96    ///
97    /// This error should not occur in any pattern that was parsed from a
98    /// string. It may happen only when converting a hand-crafted AST to a
99    /// regular expression.
100    #[error("empty bracket expression")]
101    EmptyBracket,
102
103    /// Empty collating symbol or equivalence class
104    #[error("empty collating symbol")]
105    EmptyCollatingSymbol,
106
107    /// Character class with an undefined name
108    ///
109    /// The associated value is the name that caused the error.
110    /// For example, the pattern `[[:nothing:]]` will produce
111    /// `Error::UndefinedCharClass("nothing".to_string())`.
112    #[error("undefined character class [:{0}:]")]
113    UndefinedCharClass(String),
114
115    /// Character class used as a range bound
116    ///
117    /// The associated value is the name that caused the error.
118    /// For example, the pattern `[[:digit:]-0]` will produce
119    /// `Error::CharClassInRange("digit".to_string())`.
120    #[error("character class [:{0}:] used as range bound")]
121    CharClassInRange(String),
122
123    /// Error in underlying regular expression processing
124    #[error(transparent)]
125    RegexError(#[from] regex::Error),
126}
127
128/// Main part of compiled pattern
129#[derive(Clone, Debug)]
130enum Body {
131    /// Literal string pattern
132    Literal(String),
133    /// Compiled regular expression
134    Regex {
135        regex: Regex,
136        starts_with_literal_dot: bool,
137    },
138}
139
140/// Compiled globbing pattern
141#[derive(Clone, Debug)]
142#[must_use = "creating a pattern without doing pattern matching is nonsense"]
143pub struct Pattern {
144    body: Body,
145    config: Config,
146}
147
148impl Pattern {
149    /// Compiles a pattern with defaulted configuration.
150    #[inline]
151    pub fn parse<I>(pattern: I) -> Result<Self, Error>
152    where
153        I: IntoIterator<Item = PatternChar>,
154        <I as IntoIterator>::IntoIter: Clone,
155    {
156        Self::from_ast(&Ast::new(pattern))
157    }
158
159    /// Compiles a pattern with a specified configuration.
160    pub fn parse_with_config<I>(pattern: I, config: Config) -> Result<Self, Error>
161    where
162        I: IntoIterator<Item = PatternChar>,
163        <I as IntoIterator>::IntoIter: Clone,
164    {
165        Self::from_ast_and_config(&Ast::new(pattern), config)
166    }
167
168    /// Compiles a pattern from the given AST with defaulted configuration.
169    pub fn from_ast(ast: &Ast) -> Result<Self, Error> {
170        Self::from_ast_and_config(ast, Config::default())
171    }
172
173    /// Compiles a pattern from the given AST.
174    pub fn from_ast_and_config(ast: &Ast, config: Config) -> Result<Self, Error> {
175        let body = if let Some(literal) = ast.to_literal() {
176            Body::Literal(literal)
177        } else {
178            Body::Regex {
179                regex: RegexBuilder::new(&ast.to_regex(&config)?)
180                    .case_insensitive(config.case_insensitive)
181                    .dot_matches_new_line(true)
182                    .swap_greed(config.shortest_match)
183                    .build()?,
184                starts_with_literal_dot: ast.starts_with_literal_dot(),
185            }
186        };
187        Ok(Pattern { body, config })
188    }
189
190    /// Returns the configuration for this pattern.
191    #[inline]
192    #[must_use]
193    pub fn config(&self) -> &Config {
194        &self.config
195    }
196
197    /// Returns the only string that matches the pattern, if any.
198    ///
199    /// If the pattern is made up only of literal characters, this function
200    /// returns the characters as a string. If the pattern contains any `?`,
201    /// `*`, or bracket expression, the result is `None`.
202    #[must_use]
203    pub fn as_literal(&self) -> Option<&str> {
204        match &self.body {
205            Body::Literal(s) => Some(s),
206            Body::Regex { .. } => None,
207        }
208    }
209
210    /// Returns the only string that matches the pattern, if any.
211    ///
212    /// If the pattern is made up only of literal characters, this function
213    /// returns the characters as a string. If the pattern contains any `?`,
214    /// `*`, or bracket expression, the result is `Err(self)`.
215    pub fn into_literal(self) -> Result<String, Self> {
216        match self.body {
217            Body::Literal(s) => Ok(s),
218            Body::Regex { .. } => Err(self),
219        }
220    }
221
222    /// Tests whether this pattern matches the given text.
223    #[must_use]
224    pub fn is_match(&self, text: &str) -> bool {
225        match &self.body {
226            Body::Literal(s) => match (self.config.anchor_begin, self.config.anchor_end) {
227                (false, false) => text.contains(s),
228                (true, false) => text.starts_with(s),
229                (false, true) => text.ends_with(s),
230                (true, true) => text == s,
231            },
232            Body::Regex {
233                regex,
234                starts_with_literal_dot,
235            } => {
236                let reject_initial_dot =
237                    self.config.literal_period && !starts_with_literal_dot && text.starts_with('.');
238                #[allow(clippy::bool_to_int_with_if)]
239                let at_index = if reject_initial_dot { 1 } else { 0 };
240                regex.is_match_at(text, at_index)
241            }
242        }
243    }
244
245    /// Returns the index range where this pattern matches in the given text.
246    ///
247    /// If `self` matches (part of) `text`, this function returns the index
248    /// range of the first match. Otherwise, the result is `None`.
249    #[must_use]
250    pub fn find(&self, text: &str) -> Option<Range<usize>> {
251        match &self.body {
252            Body::Literal(s) => match (self.config.anchor_begin, self.config.anchor_end) {
253                (false, false) => text.find(s).map(|pos| pos..pos + s.len()),
254                (true, false) => text.starts_with(s).then(|| 0..s.len()),
255                (false, true) => text.ends_with(s).then(|| text.len() - s.len()..text.len()),
256                (true, true) => (text == s).then(|| 0..s.len()),
257            },
258            Body::Regex {
259                regex,
260                starts_with_literal_dot,
261            } => {
262                let reject_initial_dot =
263                    self.config.literal_period && !starts_with_literal_dot && text.starts_with('.');
264                #[allow(clippy::bool_to_int_with_if)]
265                let at_index = if reject_initial_dot { 1 } else { 0 };
266                regex.find_at(text, at_index).map(|m| m.range())
267            }
268        }
269    }
270
271    /// Returns the index range where this pattern matches in the given text.
272    ///
273    /// If `self` matches (part of) `text`, this function returns the index
274    /// range of the last match. Otherwise, the result is `None`.
275    #[must_use]
276    pub fn rfind(&self, text: &str) -> Option<Range<usize>> {
277        match &self.body {
278            Body::Literal(s) => match (self.config.anchor_begin, self.config.anchor_end) {
279                (false, false) => text.rfind(s).map(|pos| pos..pos + s.len()),
280                (true, false) => text.starts_with(s).then(|| 0..s.len()),
281                (false, true) => text.ends_with(s).then(|| text.len() - s.len()..text.len()),
282                (true, true) => (text == s).then(|| 0..s.len()),
283            },
284
285            Body::Regex {
286                regex,
287                starts_with_literal_dot: _,
288            } => {
289                let mut range = self.find(text)?;
290
291                while let Some(next_range) = (range.start + 1..=text.len())
292                    .find(|&index| text.is_char_boundary(index))
293                    .and_then(|index| regex.find_at(text, index).map(|m| m.range()))
294                {
295                    range = next_range;
296                }
297
298                Some(range)
299            }
300        }
301    }
302}
303
304#[cfg(test)]
305mod tests {
306    use super::*;
307    use assert_matches::assert_matches;
308
309    #[test]
310    fn empty_pattern() {
311        let p = Pattern::parse(without_escape("")).unwrap();
312        assert_eq!(p.as_literal(), Some(""));
313
314        assert!(p.is_match(""));
315        assert!(p.is_match("a"));
316        assert!(p.is_match("."));
317        assert!(p.is_match("*"));
318
319        assert_eq!(p.find(""), Some(0..0));
320        assert_eq!(p.find("a"), Some(0..0));
321        assert_eq!(p.find("."), Some(0..0));
322        assert_eq!(p.find("*"), Some(0..0));
323
324        assert_eq!(p.rfind(""), Some(0..0));
325        assert_eq!(p.rfind("a"), Some(1..1));
326        assert_eq!(p.rfind("."), Some(1..1));
327        assert_eq!(p.rfind("*"), Some(1..1));
328    }
329
330    #[test]
331    fn single_character_pattern() {
332        let p = Pattern::parse(without_escape("a")).unwrap();
333        assert_eq!(p.as_literal(), Some("a"));
334
335        assert!(!p.is_match(""));
336        assert!(p.is_match("a"));
337        assert!(p.is_match("aa"));
338        assert!(!p.is_match("b"));
339        assert!(p.is_match("ab"));
340        assert!(p.is_match("ba"));
341
342        assert_eq!(p.find(""), None);
343        assert_eq!(p.find("a"), Some(0..1));
344        assert_eq!(p.find("aa"), Some(0..1));
345        assert_eq!(p.find("b"), None);
346        assert_eq!(p.find("ab"), Some(0..1));
347        assert_eq!(p.find("ba"), Some(1..2));
348
349        assert_eq!(p.rfind(""), None);
350        assert_eq!(p.rfind("a"), Some(0..1));
351        assert_eq!(p.rfind("aa"), Some(1..2));
352        assert_eq!(p.rfind("b"), None);
353        assert_eq!(p.rfind("ab"), Some(0..1));
354        assert_eq!(p.rfind("ba"), Some(1..2));
355    }
356
357    #[test]
358    fn double_character_pattern() {
359        let p = Pattern::parse(without_escape("in")).unwrap();
360        assert_eq!(p.as_literal(), Some("in"));
361
362        assert!(!p.is_match(""));
363        assert!(!p.is_match("i"));
364        assert!(!p.is_match("n"));
365        assert!(p.is_match("bin"));
366        assert!(p.is_match("inn"));
367        assert!(!p.is_match("nit"));
368
369        assert_eq!(p.find(""), None);
370        assert_eq!(p.find("i"), None);
371        assert_eq!(p.find("n"), None);
372        assert_eq!(p.find("bin"), Some(1..3));
373        assert_eq!(p.find("inn"), Some(0..2));
374        assert_eq!(p.find("nit"), None);
375
376        assert_eq!(p.rfind(""), None);
377        assert_eq!(p.rfind("i"), None);
378        assert_eq!(p.rfind("n"), None);
379        assert_eq!(p.rfind("bin"), Some(1..3));
380        assert_eq!(p.rfind("inn"), Some(0..2));
381        assert_eq!(p.rfind("nit"), None);
382    }
383
384    #[test]
385    fn characters_that_needs_escaping() {
386        let p = Pattern::parse(without_escape(r".\+()][{}^$-?")).unwrap();
387        assert_eq!(p.as_literal(), None);
388
389        assert_eq!(p.find(r".\+()][{}^$-X"), Some(0..13));
390        assert_eq!(p.find(r".\+()][{}^$-Y"), Some(0..13));
391        assert_eq!(p.find(r".\+()][{}^$-"), None);
392
393        assert_eq!(p.rfind(r".\+()][{}^$-X"), Some(0..13));
394        assert_eq!(p.rfind(r".\+()][{}^$-Y"), Some(0..13));
395        assert_eq!(p.rfind(r".\+()][{}^$-"), None);
396    }
397
398    #[test]
399    fn matching_newline() {
400        let p = Pattern::parse(without_escape("a\nb")).unwrap();
401        assert_eq!(p.as_literal(), Some("a\nb"));
402        assert_eq!(p.find("a\nb"), Some(0..3));
403        assert_eq!(p.rfind("a\nb"), Some(0..3));
404
405        let p = Pattern::parse(without_escape("a?b")).unwrap();
406        assert_eq!(p.as_literal(), None);
407        assert_eq!(p.find("a\nb"), Some(0..3));
408        assert_eq!(p.rfind("a\nb"), Some(0..3));
409    }
410
411    #[test]
412    fn any_single_character_pattern() {
413        let p = Pattern::parse(without_escape("?")).unwrap();
414        assert_eq!(p.as_literal(), None);
415
416        assert_eq!(p.find(""), None);
417        assert_eq!(p.find("i"), Some(0..1));
418        assert_eq!(p.find("yes"), Some(0..1));
419
420        assert_eq!(p.rfind(""), None);
421        assert_eq!(p.rfind("i"), Some(0..1));
422        assert_eq!(p.rfind("yes"), Some(2..3));
423    }
424
425    #[test]
426    fn any_single_character_pattern_combined() {
427        let p = Pattern::parse(without_escape("a?c")).unwrap();
428        assert_eq!(p.as_literal(), None);
429
430        assert_eq!(p.find(""), None);
431        assert_eq!(p.find("ab"), None);
432        assert_eq!(p.find("ac"), None);
433        assert_eq!(p.find("bc"), None);
434        assert_eq!(p.find("abc"), Some(0..3));
435
436        assert_eq!(p.rfind(""), None);
437        assert_eq!(p.rfind("ab"), None);
438        assert_eq!(p.rfind("ac"), None);
439        assert_eq!(p.rfind("bc"), None);
440        assert_eq!(p.rfind("abc"), Some(0..3));
441    }
442
443    #[test]
444    fn any_multi_character_pattern() {
445        let p = Pattern::parse(without_escape("*")).unwrap();
446        assert_eq!(p.as_literal(), None);
447
448        assert_eq!(p.find(""), Some(0..0));
449        assert_eq!(p.find("i"), Some(0..1));
450        assert_eq!(p.find("yes"), Some(0..3));
451
452        assert_eq!(p.rfind(""), Some(0..0));
453        assert_eq!(p.rfind("i"), Some(1..1));
454        assert_eq!(p.rfind("yes"), Some(3..3));
455    }
456
457    #[test]
458    fn any_multi_character_pattern_combined() {
459        let p = Pattern::parse(without_escape("a*b")).unwrap();
460        assert_eq!(p.as_literal(), None);
461
462        assert_eq!(p.find(""), None);
463        assert_eq!(p.find("a"), None);
464        assert_eq!(p.find("ab"), Some(0..2));
465        assert_eq!(p.find("aabb"), Some(0..4));
466        assert_eq!(p.find("lambda"), Some(1..4));
467
468        assert_eq!(p.rfind(""), None);
469        assert_eq!(p.rfind("a"), None);
470        assert_eq!(p.rfind("ab"), Some(0..2));
471        assert_eq!(p.rfind("aabb"), Some(1..4));
472        assert_eq!(p.rfind("lambda"), Some(1..4));
473    }
474
475    #[test]
476    fn unmatched_bracket_1() {
477        let p = Pattern::parse(without_escape("[a")).unwrap();
478        assert_eq!(p.as_literal(), Some("[a"));
479
480        assert_eq!(p.find(""), None);
481        assert_eq!(p.find("a"), None);
482        assert_eq!(p.find("[a]"), Some(0..2));
483    }
484
485    #[test]
486    fn unmatched_bracket_2() {
487        let p = Pattern::parse(without_escape("a]")).unwrap();
488        assert_eq!(p.as_literal(), Some("a]"));
489
490        assert_eq!(p.find(""), None);
491        assert_eq!(p.find("a"), None);
492        assert_eq!(p.find("[a]"), Some(1..3));
493    }
494
495    #[test]
496    fn unmatched_bracket_3() {
497        let p = Pattern::parse(without_escape("][")).unwrap();
498        assert_eq!(p.as_literal(), Some("]["));
499
500        assert_eq!(p.find(""), None);
501        assert_eq!(p.find("]["), Some(0..2));
502        assert_eq!(p.find("[][]"), Some(1..3));
503    }
504
505    #[test]
506    fn unmatched_bracket_4() {
507        let p = Pattern::parse(without_escape("[]")).unwrap();
508        assert_eq!(p.as_literal(), Some("[]"));
509
510        assert_eq!(p.find(""), None);
511        assert_eq!(p.find("[]"), Some(0..2));
512        assert_eq!(p.find("][]["), Some(1..3));
513    }
514
515    #[test]
516    fn unmatched_bracket_after_another_bracket() {
517        let p = Pattern::parse(without_escape("[a][")).unwrap();
518        assert_eq!(p.as_literal(), None);
519
520        assert_eq!(p.find(""), None);
521        assert_eq!(p.find("a"), None);
522        assert_eq!(p.find("["), None);
523        assert_eq!(p.find("a["), Some(0..2));
524        assert_eq!(p.find("]]a[[a][]"), Some(2..4));
525    }
526
527    #[test]
528    fn single_character_bracket_expression_pattern() {
529        let p = Pattern::parse(without_escape("[a]")).unwrap();
530        assert_eq!(p.as_literal(), None);
531
532        assert_eq!(p.find(""), None);
533        assert_eq!(p.find("a"), Some(0..1));
534        assert_eq!(p.find("[a]"), Some(1..2));
535    }
536
537    #[test]
538    fn multi_character_bracket_expression_pattern() {
539        let p = Pattern::parse(without_escape("[abc]")).unwrap();
540        assert_eq!(p.as_literal(), None);
541
542        assert_eq!(p.find(""), None);
543        assert_eq!(p.find("a"), Some(0..1));
544        assert_eq!(p.find("b"), Some(0..1));
545        assert_eq!(p.find("c"), Some(0..1));
546        assert_eq!(p.find("d"), None);
547    }
548
549    #[test]
550    fn ampersand_in_bracket_expression() {
551        let p = Pattern::parse(without_escape("[a&&b]")).unwrap();
552        assert_eq!(p.as_literal(), None);
553
554        assert_eq!(p.find(""), None);
555        assert_eq!(p.find("a"), Some(0..1));
556        assert_eq!(p.find("b"), Some(0..1));
557        assert_eq!(p.find("&"), Some(0..1));
558    }
559
560    #[test]
561    fn tilde_in_bracket_expression() {
562        let p = Pattern::parse(without_escape("[a~~b]")).unwrap();
563        assert_eq!(p.as_literal(), None);
564
565        assert_eq!(p.find(""), None);
566        assert_eq!(p.find("a"), Some(0..1));
567        assert_eq!(p.find("b"), Some(0..1));
568        assert_eq!(p.find("~"), Some(0..1));
569    }
570
571    #[test]
572    fn characters_that_needs_escaping_in_bracket_expression() {
573        let p = Pattern::parse(without_escape("[.?*-][&|~^][][]")).unwrap();
574        assert_eq!(p.as_literal(), None);
575
576        assert_eq!(p.find(".&["), Some(0..3));
577        assert_eq!(p.find("?|["), Some(0..3));
578        assert_eq!(p.find("*~]"), Some(0..3));
579        assert_eq!(p.find("-^]"), Some(0..3));
580        assert_eq!(p.find("?&]"), Some(0..3));
581    }
582
583    #[test]
584    fn brackets_in_bracket_expression() {
585        let p = Pattern::parse(without_escape("[]a[]")).unwrap();
586        assert_eq!(p.as_literal(), None);
587
588        assert_eq!(p.find(""), None);
589        assert_eq!(p.find("a"), Some(0..1));
590        assert_eq!(p.find("["), Some(0..1));
591        assert_eq!(p.find("]"), Some(0..1));
592    }
593
594    #[test]
595    fn character_range() {
596        let p = Pattern::parse(without_escape("[3-5]")).unwrap();
597        assert_eq!(p.as_literal(), None);
598
599        assert_eq!(p.find(""), None);
600        assert_eq!(p.find("2"), None);
601        assert_eq!(p.find("3"), Some(0..1));
602        assert_eq!(p.find("4"), Some(0..1));
603        assert_eq!(p.find("5"), Some(0..1));
604        assert_eq!(p.find("6"), None);
605        assert_eq!(p.find("02468"), Some(2..3));
606    }
607
608    #[test]
609    fn dash_at_start_of_bracket_expression() {
610        // This bracket expression should match only '-' and '0'.
611        let p = Pattern::parse(without_escape("[-0]")).unwrap();
612        assert_eq!(p.as_literal(), None);
613
614        assert_eq!(p.find(""), None);
615        assert_eq!(p.find("-"), Some(0..1));
616        assert_eq!(p.find("."), None);
617        assert_eq!(p.find("0"), Some(0..1));
618        assert_eq!(p.find("1"), None);
619    }
620
621    #[test]
622    fn dash_at_end_of_bracket_expression() {
623        // This bracket expression should match only '+' and '-'.
624        let p = Pattern::parse(without_escape("[+-]")).unwrap();
625        assert_eq!(p.as_literal(), None);
626
627        assert_eq!(p.find(""), None);
628        assert_eq!(p.find("+"), Some(0..1));
629        assert_eq!(p.find(","), None);
630        assert_eq!(p.find("-"), Some(0..1));
631        assert_eq!(p.find("."), None);
632    }
633
634    #[test]
635    fn ambiguous_character_range() {
636        let p = Pattern::parse(without_escape("[2-4-6]")).unwrap();
637        assert_eq!(p.as_literal(), None);
638
639        // POSIX leaves the expected results unspecified.
640        // The results below depend on the current behavior of the regex crate.
641        assert_eq!(p.find("1"), None);
642        assert_eq!(p.find("2"), Some(0..1));
643        assert_eq!(p.find("3"), Some(0..1));
644        assert_eq!(p.find("4"), Some(0..1));
645        assert_eq!(p.find("5"), None);
646        assert_eq!(p.find("6"), Some(0..1));
647        assert_eq!(p.find("7"), None);
648    }
649
650    #[test]
651    fn double_dash_in_bracket_expression() {
652        // This bracket expression should be parsed as a union of the character
653        // range between '+' and '-', and a single dot.
654        let p = Pattern::parse(without_escape("[+--.]")).unwrap();
655        assert_eq!(p.as_literal(), None);
656
657        assert_eq!(p.find(""), None);
658        assert_eq!(p.find("+"), Some(0..1));
659        assert_eq!(p.find(","), Some(0..1));
660        assert_eq!(p.find("-"), Some(0..1));
661        assert_eq!(p.find("."), Some(0..1));
662    }
663
664    #[test]
665    fn double_dash_at_start_of_bracket_expression() {
666        // This bracket expression should be parsed as the character range
667        // between '-' and '0'.
668        let p = Pattern::parse(without_escape("[--0]")).unwrap();
669        assert_eq!(p.as_literal(), None);
670
671        assert_eq!(p.find(""), None);
672        assert_eq!(p.find("-"), Some(0..1));
673        assert_eq!(p.find("."), Some(0..1));
674        assert_eq!(p.find("0"), Some(0..1));
675        assert_eq!(p.find("1"), None);
676    }
677
678    #[test]
679    fn double_dash_at_end_of_bracket_expression() {
680        // This bracket expression should be parsed as the character range
681        // between '+' and '-'.
682        let p = Pattern::parse(without_escape("[+--]")).unwrap();
683        assert_eq!(p.as_literal(), None);
684
685        assert_eq!(p.find(""), None);
686        assert_eq!(p.find("+"), Some(0..1));
687        assert_eq!(p.find(","), Some(0..1));
688        assert_eq!(p.find("-"), Some(0..1));
689        assert_eq!(p.find("."), None);
690    }
691
692    #[test]
693    fn bracket_expression_complement() {
694        let p = Pattern::parse(without_escape("[!ab]")).unwrap();
695        assert_eq!(p.as_literal(), None);
696
697        assert_eq!(p.find(""), None);
698        assert_eq!(p.find("a"), None);
699        assert_eq!(p.find("b"), None);
700        assert_eq!(p.find("c"), Some(0..1));
701        assert_eq!(p.find("!"), Some(0..1));
702        assert_eq!(p.find("abcd"), Some(2..3));
703
704        assert_eq!(p.rfind(""), None);
705        assert_eq!(p.rfind("a"), None);
706        assert_eq!(p.rfind("b"), None);
707        assert_eq!(p.rfind("c"), Some(0..1));
708        assert_eq!(p.rfind("!"), Some(0..1));
709        assert_eq!(p.rfind("abcd"), Some(3..4));
710    }
711
712    #[test]
713    fn exclamation_in_bracket_expression() {
714        let p = Pattern::parse(without_escape("[ab!]")).unwrap();
715        assert_eq!(p.as_literal(), None);
716
717        assert_eq!(p.find(""), None);
718        assert_eq!(p.find("a"), Some(0..1));
719        assert_eq!(p.find("b"), Some(0..1));
720        assert_eq!(p.find("c"), None);
721        assert_eq!(p.find("!"), Some(0..1));
722    }
723
724    #[test]
725    fn exclamation_in_bracket_expression_complement() {
726        let p = Pattern::parse(without_escape("[!!]")).unwrap();
727        assert_eq!(p.as_literal(), None);
728
729        assert_eq!(p.find(""), None);
730        assert_eq!(p.find("!"), None);
731        assert_eq!(p.find("a"), Some(0..1));
732        assert_eq!(p.find("!x!"), Some(1..2));
733    }
734
735    #[test]
736    fn bracket_in_bracket_expression_complement() {
737        let p = Pattern::parse(without_escape("[!]a]")).unwrap();
738        assert_eq!(p.as_literal(), None);
739
740        assert_eq!(p.find(""), None);
741        assert_eq!(p.find("!"), Some(0..1));
742        assert_eq!(p.find("]"), None);
743        assert_eq!(p.find("a"), None);
744        assert_eq!(p.find("b"), Some(0..1));
745        assert_eq!(p.find("abc"), Some(1..2));
746    }
747
748    #[test]
749    fn caret_in_bracket_expression() {
750        let p = Pattern::parse(without_escape("[^]a]")).unwrap();
751        assert_eq!(p.as_literal(), None);
752
753        assert_eq!(p.find(""), None);
754        assert_eq!(p.find("^"), Some(0..1));
755        assert_eq!(p.find("]"), None);
756        assert_eq!(p.find("a"), None);
757        assert_eq!(p.find("b"), Some(0..1));
758        assert_eq!(p.find("abc"), Some(1..2));
759
760        let p = Pattern::parse(without_escape("[^^]")).unwrap();
761        assert_eq!(p.as_literal(), None);
762        assert!(!p.is_match("^"));
763    }
764
765    #[test]
766    fn single_character_collating_symbol() {
767        let p = Pattern::parse(without_escape("[[.a.]]")).unwrap();
768        assert_eq!(p.as_literal(), None);
769
770        assert_eq!(p.find(""), None);
771        assert_eq!(p.find("a"), Some(0..1));
772        assert_eq!(p.find("x"), None);
773        assert_eq!(p.find("."), None);
774        assert_eq!(p.find("[a]"), Some(1..2));
775    }
776
777    #[test]
778    fn multi_character_collating_symbol() {
779        let p = Pattern::parse(without_escape("[[.ch.]]")).unwrap();
780        assert_eq!(p.as_literal(), None);
781
782        assert_eq!(p.find(""), None);
783        assert_eq!(p.find("c"), None);
784        assert_eq!(p.find("h"), None);
785        assert_eq!(p.find("."), None);
786        assert_eq!(p.find("ch"), Some(0..2));
787        assert_eq!(p.find("[ch]"), Some(1..3));
788    }
789
790    #[test]
791    fn single_character_equivalence_class() {
792        let p = Pattern::parse(without_escape("[[=a=]]")).unwrap();
793        assert_eq!(p.as_literal(), None);
794
795        assert_eq!(p.find(""), None);
796        assert_eq!(p.find("a"), Some(0..1));
797        assert_eq!(p.find("x"), None);
798        assert_eq!(p.find("="), None);
799        assert_eq!(p.find("[a]"), Some(1..2));
800    }
801
802    #[test]
803    fn multi_character_equivalence_class() {
804        let p = Pattern::parse(without_escape("[[=ij=]]")).unwrap();
805        assert_eq!(p.as_literal(), None);
806
807        assert_eq!(p.find(""), None);
808        assert_eq!(p.find("i"), None);
809        assert_eq!(p.find("j"), None);
810        assert_eq!(p.find("."), None);
811        assert_eq!(p.find("ij"), Some(0..2));
812        assert_eq!(p.find("[ij]"), Some(1..3));
813    }
814
815    #[test]
816    fn character_class_alnum() {
817        let p = Pattern::parse(without_escape("[[:alnum:]]")).unwrap();
818        assert_eq!(p.as_literal(), None);
819
820        assert_eq!(p.find(""), None);
821        assert_eq!(p.find("a"), Some(0..1));
822        assert_eq!(p.find("x"), Some(0..1));
823        assert_eq!(p.find("7"), Some(0..1));
824        assert_eq!(p.find("="), None);
825        assert_eq!(p.find(":"), None);
826        assert_eq!(p.find("[A]"), Some(1..2));
827    }
828
829    #[test]
830    fn character_class_alpha() {
831        let p = Pattern::parse(without_escape("[[:alpha:]]")).unwrap();
832        assert_eq!(p.as_literal(), None);
833
834        assert_eq!(p.find(""), None);
835        assert_eq!(p.find("a"), Some(0..1));
836        assert_eq!(p.find("x"), Some(0..1));
837        assert_eq!(p.find("7"), None);
838        assert_eq!(p.find("="), None);
839        assert_eq!(p.find(":"), None);
840        assert_eq!(p.find("[A]"), Some(1..2));
841    }
842
843    #[test]
844    fn character_class_blank() {
845        let p = Pattern::parse(without_escape("[[:blank:]]")).unwrap();
846        assert_eq!(p.as_literal(), None);
847
848        assert_eq!(p.find(""), None);
849        assert_eq!(p.find("a"), None);
850        assert_eq!(p.find("="), None);
851        assert_eq!(p.find(" "), Some(0..1));
852        assert_eq!(p.find("\t"), Some(0..1));
853        assert_eq!(p.find("\n"), None);
854        assert_eq!(p.find("\r"), None);
855        assert_eq!(p.find("[A]"), None);
856    }
857
858    #[test]
859    fn character_class_cntrl() {
860        let p = Pattern::parse(without_escape("[[:cntrl:]]")).unwrap();
861        assert_eq!(p.as_literal(), None);
862
863        assert_eq!(p.find(""), None);
864        assert_eq!(p.find("a"), None);
865        assert_eq!(p.find("="), None);
866        assert_eq!(p.find(" "), None);
867        assert_eq!(p.find("\t"), Some(0..1));
868        assert_eq!(p.find("\n"), Some(0..1));
869        assert_eq!(p.find("\r"), Some(0..1));
870        assert_eq!(p.find("[A]"), None);
871    }
872
873    #[test]
874    fn character_class_digit() {
875        let p = Pattern::parse(without_escape("[[:digit:]]")).unwrap();
876        assert_eq!(p.as_literal(), None);
877
878        assert_eq!(p.find(""), None);
879        assert_eq!(p.find("a"), None);
880        assert_eq!(p.find("x"), None);
881        assert_eq!(p.find("7"), Some(0..1));
882        assert_eq!(p.find("="), None);
883        assert_eq!(p.find(":"), None);
884        assert_eq!(p.find("[A]"), None);
885    }
886
887    #[test]
888    fn character_class_graph() {
889        let p = Pattern::parse(without_escape("[[:graph:]]")).unwrap();
890        assert_eq!(p.as_literal(), None);
891
892        assert_eq!(p.find(""), None);
893        assert_eq!(p.find("a"), Some(0..1));
894        assert_eq!(p.find("x"), Some(0..1));
895        assert_eq!(p.find("7"), Some(0..1));
896        assert_eq!(p.find("="), Some(0..1));
897        assert_eq!(p.find(":"), Some(0..1));
898        assert_eq!(p.find(" "), None);
899        assert_eq!(p.find("\t"), None);
900        assert_eq!(p.find("[A]"), Some(0..1));
901    }
902
903    #[test]
904    fn character_class_lower() {
905        let p = Pattern::parse(without_escape("[[:lower:]]")).unwrap();
906        assert_eq!(p.as_literal(), None);
907
908        assert_eq!(p.find(""), None);
909        assert_eq!(p.find("A"), None);
910        assert_eq!(p.find("a"), Some(0..1));
911        assert_eq!(p.find("x"), Some(0..1));
912        assert_eq!(p.find("7"), None);
913        assert_eq!(p.find("="), None);
914        assert_eq!(p.find("\t"), None);
915        assert_eq!(p.find("[a]"), Some(1..2));
916    }
917
918    #[test]
919    fn character_class_print() {
920        let p = Pattern::parse(without_escape("[[:print:]]")).unwrap();
921        assert_eq!(p.as_literal(), None);
922
923        assert_eq!(p.find(""), None);
924        assert_eq!(p.find("a"), Some(0..1));
925        assert_eq!(p.find("x"), Some(0..1));
926        assert_eq!(p.find("7"), Some(0..1));
927        assert_eq!(p.find("="), Some(0..1));
928        assert_eq!(p.find(":"), Some(0..1));
929        assert_eq!(p.find(" "), Some(0..1));
930        assert_eq!(p.find("\t"), None);
931        assert_eq!(p.find("[A]"), Some(0..1));
932    }
933
934    #[test]
935    fn character_class_punct() {
936        let p = Pattern::parse(without_escape("[[:punct:]]")).unwrap();
937        assert_eq!(p.as_literal(), None);
938
939        assert_eq!(p.find(""), None);
940        assert_eq!(p.find("a"), None);
941        assert_eq!(p.find("x"), None);
942        assert_eq!(p.find("7"), None);
943        assert_eq!(p.find("="), Some(0..1));
944        assert_eq!(p.find(":"), Some(0..1));
945        assert_eq!(p.find("[A]"), Some(0..1));
946    }
947
948    #[test]
949    fn character_class_space() {
950        let p = Pattern::parse(without_escape("[[:space:]]")).unwrap();
951        assert_eq!(p.as_literal(), None);
952
953        assert_eq!(p.find(""), None);
954        assert_eq!(p.find("a"), None);
955        assert_eq!(p.find("="), None);
956        assert_eq!(p.find(" "), Some(0..1));
957        assert_eq!(p.find("\t"), Some(0..1));
958        assert_eq!(p.find("\n"), Some(0..1));
959        assert_eq!(p.find("\r"), Some(0..1));
960        assert_eq!(p.find("[A]"), None);
961    }
962
963    #[test]
964    fn character_class_upper() {
965        let p = Pattern::parse(without_escape("[[:upper:]]")).unwrap();
966        assert_eq!(p.as_literal(), None);
967
968        assert_eq!(p.find(""), None);
969        assert_eq!(p.find("A"), Some(0..1));
970        assert_eq!(p.find("a"), None);
971        assert_eq!(p.find("X"), Some(0..1));
972        assert_eq!(p.find("7"), None);
973        assert_eq!(p.find("="), None);
974        assert_eq!(p.find("\t"), None);
975        assert_eq!(p.find("[A]"), Some(1..2));
976    }
977
978    #[test]
979    fn character_class_xdigit() {
980        let p = Pattern::parse(without_escape("[[:xdigit:]]")).unwrap();
981        assert_eq!(p.as_literal(), None);
982
983        assert_eq!(p.find(""), None);
984        assert_eq!(p.find("a"), Some(0..1));
985        assert_eq!(p.find("x"), None);
986        assert_eq!(p.find("7"), Some(0..1));
987        assert_eq!(p.find("="), None);
988        assert_eq!(p.find(":"), None);
989        assert_eq!(p.find("[A]"), Some(1..2));
990    }
991
992    #[test]
993    fn undefined_character_class() {
994        let e = Pattern::parse(without_escape("[[:foo_bar:]]")).unwrap_err();
995        assert_matches!(e, Error::UndefinedCharClass(name) if name == "foo_bar");
996    }
997
998    #[test]
999    fn combinations_of_inner_bracket_expressions() {
1000        let p = Pattern::parse(without_escape("[][.-.]-[=0=][:blank:]]")).unwrap();
1001        assert_eq!(p.as_literal(), None);
1002
1003        assert_eq!(p.find(""), None);
1004        assert_eq!(p.find("]"), Some(0..1));
1005        assert_eq!(p.find("-"), Some(0..1));
1006        assert_eq!(p.find("."), Some(0..1));
1007        assert_eq!(p.find("/"), Some(0..1));
1008        assert_eq!(p.find("0"), Some(0..1));
1009        assert_eq!(p.find("1"), None);
1010        assert_eq!(p.find(" "), Some(0..1));
1011        assert_eq!(p.find("\t"), Some(0..1));
1012        assert_eq!(p.find("["), None);
1013    }
1014
1015    #[test]
1016    fn escaped_pattern() {
1017        let p = Pattern::parse(with_escape(r"\*\?\[a]")).unwrap();
1018        assert_eq!(p.as_literal(), Some("*?[a]"));
1019
1020        assert_eq!(p.find(""), None);
1021        assert_eq!(p.find("*?[a]"), Some(0..5));
1022        assert_eq!(p.find("aaa"), None);
1023    }
1024
1025    #[test]
1026    fn literal_with_anchor_begin() {
1027        let config = Config {
1028            anchor_begin: true,
1029            ..Config::default()
1030        };
1031        let p = Pattern::parse_with_config(without_escape("a"), config).unwrap();
1032        assert_eq!(p.as_literal(), Some("a"));
1033
1034        assert!(!p.is_match(""));
1035        assert!(p.is_match("a"));
1036        assert!(!p.is_match(".a"));
1037        assert!(p.is_match("a."));
1038
1039        assert_eq!(p.find(""), None);
1040        assert_eq!(p.find("a"), Some(0..1));
1041        assert_eq!(p.find(".a"), None);
1042        assert_eq!(p.find("a."), Some(0..1));
1043
1044        assert_eq!(p.rfind(""), None);
1045        assert_eq!(p.rfind("a"), Some(0..1));
1046        assert_eq!(p.rfind(".a"), None);
1047        assert_eq!(p.rfind("a."), Some(0..1));
1048    }
1049
1050    #[test]
1051    fn non_literal_with_anchor_begin() {
1052        let config = Config {
1053            anchor_begin: true,
1054            ..Config::default()
1055        };
1056        let p = Pattern::parse_with_config(without_escape("a?"), config).unwrap();
1057        assert_eq!(p.as_literal(), None);
1058
1059        assert!(!p.is_match(""));
1060        assert!(!p.is_match("a"));
1061        assert!(p.is_match("as"));
1062        assert!(p.is_match("apple"));
1063        assert!(!p.is_match("bass"));
1064
1065        assert_eq!(p.find(""), None);
1066        assert_eq!(p.find("a"), None);
1067        assert_eq!(p.find("as"), Some(0..2));
1068        assert_eq!(p.find("apple"), Some(0..2));
1069        assert_eq!(p.find("bass"), None);
1070
1071        assert_eq!(p.rfind(""), None);
1072        assert_eq!(p.rfind("a"), None);
1073        assert_eq!(p.rfind("as"), Some(0..2));
1074        assert_eq!(p.rfind("apple"), Some(0..2));
1075        assert_eq!(p.rfind("bass"), None);
1076    }
1077
1078    #[test]
1079    fn literal_with_anchor_end() {
1080        let config = Config {
1081            anchor_end: true,
1082            ..Config::default()
1083        };
1084        let p = Pattern::parse_with_config(without_escape("a"), config).unwrap();
1085        assert_eq!(p.as_literal(), Some("a"));
1086
1087        assert!(!p.is_match(""));
1088        assert!(p.is_match("a"));
1089        assert!(p.is_match("..a"));
1090        assert!(!p.is_match("a.."));
1091
1092        assert_eq!(p.find(""), None);
1093        assert_eq!(p.find("a"), Some(0..1));
1094        assert_eq!(p.find("..a"), Some(2..3));
1095        assert_eq!(p.find("a.."), None);
1096
1097        assert_eq!(p.rfind(""), None);
1098        assert_eq!(p.rfind("a"), Some(0..1));
1099        assert_eq!(p.rfind("..a"), Some(2..3));
1100        assert_eq!(p.rfind("a.."), None);
1101    }
1102
1103    #[test]
1104    fn non_literal_with_anchor_end() {
1105        let config = Config {
1106            anchor_end: true,
1107            ..Config::default()
1108        };
1109        let p = Pattern::parse_with_config(without_escape("?n"), config).unwrap();
1110        assert_eq!(p.as_literal(), None);
1111
1112        assert!(!p.is_match(""));
1113        assert!(!p.is_match("n"));
1114        assert!(p.is_match("in"));
1115        assert!(p.is_match("begin"));
1116        assert!(!p.is_match("beginning"));
1117
1118        assert_eq!(p.find(""), None);
1119        assert_eq!(p.find("n"), None);
1120        assert_eq!(p.find("in"), Some(0..2));
1121        assert_eq!(p.find("begin"), Some(3..5));
1122        assert_eq!(p.find("beginning"), None);
1123
1124        assert_eq!(p.rfind(""), None);
1125        assert_eq!(p.rfind("n"), None);
1126        assert_eq!(p.rfind("in"), Some(0..2));
1127        assert_eq!(p.rfind("begin"), Some(3..5));
1128        assert_eq!(p.rfind("beginning"), None);
1129    }
1130
1131    #[test]
1132    fn literal_with_anchor_both() {
1133        let config = Config {
1134            anchor_begin: true,
1135            anchor_end: true,
1136            ..Config::default()
1137        };
1138        let p = Pattern::parse_with_config(without_escape("a"), config).unwrap();
1139        assert_eq!(p.as_literal(), Some("a"));
1140
1141        assert!(!p.is_match(""));
1142        assert!(p.is_match("a"));
1143        assert!(!p.is_match("..a"));
1144        assert!(!p.is_match("a.."));
1145
1146        assert_eq!(p.find(""), None);
1147        assert_eq!(p.find("a"), Some(0..1));
1148        assert_eq!(p.find("..a"), None);
1149        assert_eq!(p.find("a.."), None);
1150
1151        assert_eq!(p.rfind(""), None);
1152        assert_eq!(p.rfind("a"), Some(0..1));
1153        assert_eq!(p.rfind("..a"), None);
1154        assert_eq!(p.rfind("a.."), None);
1155    }
1156
1157    #[test]
1158    fn non_literal_with_anchor_both() {
1159        let config = Config {
1160            anchor_begin: true,
1161            anchor_end: true,
1162            ..Config::default()
1163        };
1164        let p = Pattern::parse_with_config(without_escape("???"), config).unwrap();
1165        assert_eq!(p.as_literal(), None);
1166
1167        assert!(!p.is_match("in"));
1168        assert!(p.is_match("out"));
1169        assert!(!p.is_match("from"));
1170
1171        assert_eq!(p.find("in"), None);
1172        assert_eq!(p.find("out"), Some(0..3));
1173        assert_eq!(p.find("from"), None);
1174
1175        assert_eq!(p.rfind("in"), None);
1176        assert_eq!(p.rfind("out"), Some(0..3));
1177        assert_eq!(p.rfind("from"), None);
1178    }
1179
1180    #[test]
1181    fn initial_period_with_literal_period() {
1182        let config = Config {
1183            literal_period: true,
1184            ..Config::default()
1185        };
1186
1187        let p = Pattern::parse_with_config(without_escape("?"), config).unwrap();
1188        assert!(!p.is_match("."));
1189        assert!(p.is_match(".."));
1190        assert!(p.is_match("a"));
1191        assert_eq!(p.find("."), None);
1192        assert_eq!(p.find(".."), Some(1..2));
1193        assert_eq!(p.find("a"), Some(0..1));
1194        assert_eq!(p.rfind("."), None);
1195        assert_eq!(p.rfind(".."), Some(1..2));
1196        assert_eq!(p.rfind("a"), Some(0..1));
1197
1198        let p = Pattern::parse_with_config(without_escape("*"), config).unwrap();
1199        assert!(p.is_match("."));
1200        assert!(p.is_match(".."));
1201        assert!(p.is_match("a"));
1202        assert_eq!(p.find("."), Some(1..1));
1203        assert_eq!(p.find(".."), Some(1..2));
1204        assert_eq!(p.find("a"), Some(0..1));
1205        assert_eq!(p.rfind("."), Some(1..1));
1206        assert_eq!(p.rfind(".."), Some(2..2));
1207        assert_eq!(p.rfind("a"), Some(1..1));
1208
1209        let p = Pattern::parse_with_config(without_escape("[.a]"), config).unwrap();
1210        assert!(!p.is_match("."));
1211        assert!(p.is_match(".."));
1212        assert!(p.is_match("a"));
1213        assert_eq!(p.find("."), None);
1214        assert_eq!(p.find(".."), Some(1..2));
1215        assert_eq!(p.find("a"), Some(0..1));
1216        assert_eq!(p.rfind("."), None);
1217        assert_eq!(p.rfind(".."), Some(1..2));
1218        assert_eq!(p.rfind("a"), Some(0..1));
1219
1220        let p = Pattern::parse_with_config(without_escape(".*"), config).unwrap();
1221        assert!(p.is_match("."));
1222        assert!(p.is_match(".."));
1223        assert_eq!(p.find("."), Some(0..1));
1224        assert_eq!(p.find(".."), Some(0..2));
1225        assert_eq!(p.rfind("."), Some(0..1));
1226        assert_eq!(p.rfind(".."), Some(1..2));
1227    }
1228
1229    #[test]
1230    fn initial_period_without_literal_period() {
1231        let config = Config::default();
1232
1233        let p = Pattern::parse_with_config(without_escape("?"), config).unwrap();
1234        assert!(p.is_match("."));
1235        assert!(p.is_match(".."));
1236        assert!(p.is_match("a"));
1237        assert_eq!(p.find("."), Some(0..1));
1238        assert_eq!(p.find(".."), Some(0..1));
1239        assert_eq!(p.find("a"), Some(0..1));
1240        assert_eq!(p.rfind("."), Some(0..1));
1241        assert_eq!(p.rfind(".."), Some(1..2));
1242        assert_eq!(p.rfind("a"), Some(0..1));
1243
1244        let p = Pattern::parse_with_config(without_escape("*"), config).unwrap();
1245        assert!(p.is_match("."));
1246        assert!(p.is_match(".."));
1247        assert!(p.is_match("a"));
1248        assert_eq!(p.find("."), Some(0..1));
1249        assert_eq!(p.find(".."), Some(0..2));
1250        assert_eq!(p.find("a"), Some(0..1));
1251        assert_eq!(p.rfind("."), Some(1..1));
1252        assert_eq!(p.rfind(".."), Some(2..2));
1253        assert_eq!(p.rfind("a"), Some(1..1));
1254
1255        let p = Pattern::parse_with_config(without_escape("[.a]"), config).unwrap();
1256        assert!(p.is_match("."));
1257        assert!(p.is_match(".."));
1258        assert!(p.is_match("a"));
1259        assert_eq!(p.find("."), Some(0..1));
1260        assert_eq!(p.find(".."), Some(0..1));
1261        assert_eq!(p.find("a"), Some(0..1));
1262        assert_eq!(p.rfind("."), Some(0..1));
1263        assert_eq!(p.rfind(".."), Some(1..2));
1264        assert_eq!(p.rfind("a"), Some(0..1));
1265
1266        let p = Pattern::parse_with_config(without_escape(".*"), config).unwrap();
1267        assert!(p.is_match("."));
1268        assert!(p.is_match(".."));
1269        assert_eq!(p.find("."), Some(0..1));
1270        assert_eq!(p.find(".."), Some(0..2));
1271        assert_eq!(p.rfind("."), Some(0..1));
1272        assert_eq!(p.rfind(".."), Some(1..2));
1273    }
1274
1275    #[test]
1276    fn non_literal_with_shortest_match() {
1277        let p = Pattern::parse_with_config(without_escape("1*9"), Config::default()).unwrap();
1278        assert!(p.is_match("119"));
1279        assert!(p.is_match("11999"));
1280        assert_eq!(p.find("119"), Some(0..3));
1281        assert_eq!(p.find("11999"), Some(0..5));
1282        assert_eq!(p.rfind("119"), Some(1..3));
1283        assert_eq!(p.rfind("11999"), Some(1..5));
1284
1285        let config = Config {
1286            shortest_match: true,
1287            ..Config::default()
1288        };
1289        let p = Pattern::parse_with_config(without_escape("1*9"), config).unwrap();
1290        assert!(p.is_match("119"));
1291        assert!(p.is_match("11999"));
1292        assert_eq!(p.find("119"), Some(0..3));
1293        assert_eq!(p.find("11999"), Some(0..3));
1294        assert_eq!(p.rfind("119"), Some(1..3));
1295        assert_eq!(p.rfind("11999"), Some(1..3));
1296    }
1297
1298    #[test]
1299    fn non_literal_with_case_insensitive() {
1300        let config = Config {
1301            case_insensitive: true,
1302            ..Config::default()
1303        };
1304        let p = Pattern::parse_with_config(without_escape("a?z"), config).unwrap();
1305        assert_eq!(p.as_literal(), None);
1306
1307        assert!(!p.is_match(""));
1308        assert!(p.is_match("a-z"));
1309        assert!(p.is_match("A-Z"));
1310        assert!(!p.is_match("b&b"));
1311
1312        assert_eq!(p.find(""), None);
1313        assert_eq!(p.find("a-z"), Some(0..3));
1314        assert_eq!(p.find("A-Z"), Some(0..3));
1315        assert_eq!(p.find("b&b"), None);
1316
1317        assert_eq!(p.rfind(""), None);
1318        assert_eq!(p.rfind("a-z"), Some(0..3));
1319        assert_eq!(p.rfind("A-Z"), Some(0..3));
1320        assert_eq!(p.rfind("b&b"), None);
1321    }
1322}