Skip to main content

sequoia_openpgp/regex/
mod.rs

1//! OpenPGP regex parser.
2//!
3//! OpenPGP defines a [regular expression language].  It is used with
4//! [trust signatures] to scope the trust that they extend.
5//!
6//!   [regular expression language]: https://www.rfc-editor.org/rfc/rfc9580.html#section-8
7//!   [trust signatures]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.21
8//!
9//! Compared with most regular expression languages, OpenPGP's is
10//! quite simple.  In particular, it only includes the following
11//! features:
12//!
13//!   - Alternations using `|`,
14//!   - Grouping using `(` and `)`,
15//!   - The `*`, `+`, and `?` glob operators,
16//!   - The `^`, and `$` anchors,
17//!   - The '.' operator, positive *non-empty* ranges
18//!     (e.g. `[a-zA-Z]`) and negative *non-empty* ranges (`[^@]`), and
19//!   - The backslash operator to escape special characters (except
20//!     in ranges).
21//!
22//! The regular expression engine defined in this module implements
23//! that language with two differences.  The first difference is that
24//! the compiler only works on UTF-8 strings (not bytes).  The second
25//! difference is that ranges in character classes are between UTF-8
26//! characters, not just ASCII characters.
27//!
28//! # Data Structures
29//!
30//! This module defines two data structures.  [`Regex`] encapsulates a
31//! valid regular expression, and provides methods to check whether
32//! the regular expression matches a string or a [`UserID`].
33//! [`RegexSet`] is similar, but encapsulates zero or more regular
34//! expressions, which may or may not be valid.  Its match methods
35//! return `true` if there are no regular expressions, or, if there is
36//! at least one regular expression, they return whether at least one
37//! of the regular expressions matches it.  `RegexSet`'s matcher
38//! handles invalid regular expressions by considering them to be
39//! regular expressions that don't match anything.  These semantics
40//! are consistent with a trust signature's scoping rules.  Further,
41//! strings that contain control characters never match.  This
42//! behavior can be overridden using [`Regex::disable_sanitizations`]
43//! and [`RegexSet::disable_sanitizations`].
44//!
45//!   [`UserID`]: crate::packet::UserID
46//!   [`Regex::disable_sanitizations`]: Regex::disable_sanitizations()
47//!   [`RegexSet::disable_sanitizations`]: RegexSet::disable_sanitizations()
48//!
49//! # Scoped Trust Signatures
50//!
51//! To create a trust signature, you create a signature whose [type]
52//! is either [GenericCertification], [PersonaCertification],
53//! [CasualCertification], [PositiveCertification], or [DirectKey],
54//! and add a [Trust Signature] subpacket using, for instance, the
55//! [`SignatureBuilder::set_trust_signature`] method.
56//!
57//!   [type]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.1
58//!   [GenericCertification]: crate::types::SignatureType::GenericCertification
59//!   [PersonaCertification]: crate::types::SignatureType::PersonaCertification
60//!   [CasualCertification]: crate::types::SignatureType::CasualCertification
61//!   [PositiveCertification]: crate::types::SignatureType::PositiveCertification
62//!   [DirectKey]: crate::types::SignatureType::DirectKey
63//!   [Trust Signature]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.21
64//!   [`SignatureBuilder::set_trust_signature`]: crate::packet::signature::SignatureBuilder::set_trust_signature()
65//!
66//! To scope a trust signature, you add a [Regular Expression
67//! subpacket] to it using
68//! [`SignatureBuilder::set_regular_expression`] or
69//! [`SignatureBuilder::add_regular_expression`].
70//!
71//! To extract any regular expressions, you can use
72//! [`SubpacketAreas::regular_expressions`].
73//!
74//!   [Regular Expression subpacket]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.22
75//!   [`SignatureBuilder::set_regular_expression`]: crate::packet::signature::SignatureBuilder::set_regular_expression()
76//!   [`SignatureBuilder::add_regular_expression`]: crate::packet::signature::SignatureBuilder::add_regular_expression()
77//!   [`SubpacketAreas::regular_expressions`]: crate::packet::signature::subpacket::SubpacketAreas::regular_expressions()
78//!
79//! # Caveat Emptor
80//!
81//! Note: GnuPG has [very limited regular expression support].  In
82//! particular, it only recognizes regular expressions with the
83//! following form:
84//!
85//!   [very limited regular expression support]: https://dev.gnupg.org/source/gnupg/browse/master/g10/trustdb.c;15e065dee891eef9545556f210b4199107999869$1558
86//!
87//! ```text
88//! <[^>]+[@.]example\.com>$
89//! ```
90//!
91//! Further, it escapes any operators between the `<[^>]+[@.]` and the
92//! `>$` except `.` and `\`.  Otherwise, GnuPG treats the regular
93//! expression as a literal domain (e.g., `example.com`).
94//!
95//! Further, until [version 2.2.22] (released in August 2020), GnuPG
96//! did not support regular expressions on Windows, and other systems
97//! that don't include `regcomp`.  On these systems, if a trust
98//! signature included a regular expression, GnuPG conservatively
99//! considered the whole trust signature to match nothing.
100//!
101//!   [version 2.2.22]: https://dev.gnupg.org/T5030
102//!
103//! # Examples
104//!
105//! A CA signs two certificates, one for Alice, who works at
106//! `example.com`, and one for Bob, who is associated with `some.org`.
107//! Carol then creates a trust signature for the CA, which she scopes
108//! to `example.org` and `example.com`.  We then confirm that Carol
109//! can use the CA to authenticate Alice, but not Bob.
110//!
111//! ```
112//! use sequoia_openpgp as openpgp;
113//! use openpgp::cert::prelude::*;
114//! use openpgp::packet::prelude::*;
115//! use openpgp::policy::StandardPolicy;
116//! use openpgp::regex::RegexSet;
117//! use openpgp::types::SignatureType;
118//!
119//! # fn main() -> openpgp::Result<()> {
120//! let p = &StandardPolicy::new();
121//!
122//! let (ca, _)
123//!     = CertBuilder::general_purpose(Some("OpenPGP CA <openpgp-ca@example.com>"))
124//!         .generate()?;
125//! let mut ca_signer = ca.primary_key().key().clone()
126//!     .parts_into_secret()?.into_keypair()?;
127//! let ca_userid = ca.with_policy(p, None)?
128//!     .userids().nth(0).expect("Added a User ID").userid();
129//!
130//! // The CA certifies "Alice <alice@example.com>".
131//! let (alice, _)
132//!     = CertBuilder::general_purpose(Some("Alice <alice@example.com>"))
133//!         .generate()?;
134//! let alice_userid = alice.with_policy(p, None)?
135//!     .userids().nth(0).expect("Added a User ID").userid();
136//! let alice_certification = SignatureBuilder::new(SignatureType::GenericCertification)
137//!     .sign_userid_binding(
138//!         &mut ca_signer,
139//!         alice.primary_key().component(),
140//!         alice_userid)?;
141//! let alice = alice.insert_packets(alice_certification.clone())?.0;
142//! # assert!(alice.clone().into_packets().any(|p| {
143//! #   match p {
144//! #       Packet::Signature(sig) => sig == alice_certification,
145//! #       _ => false,
146//! #   }
147//! # }));
148//!
149//! // The CA certifies "Bob <bob@some.org>".
150//! let (bob, _)
151//!     = CertBuilder::general_purpose(Some("Bob <bob@some.org>"))
152//!         .generate()?;
153//! let bob_userid = bob.with_policy(p, None)?
154//!     .userids().nth(0).expect("Added a User ID").userid();
155//! let bob_certification = SignatureBuilder::new(SignatureType::GenericCertification)
156//!     .sign_userid_binding(
157//!         &mut ca_signer,
158//!         bob.primary_key().component(),
159//!         bob_userid)?;
160//! let bob = bob.insert_packets(bob_certification.clone())?.0;
161//! # assert!(bob.clone().into_packets().any(|p| {
162//! #   match p {
163//! #       Packet::Signature(sig) => sig == bob_certification,
164//! #       _ => false,
165//! #   }
166//! # }));
167//!
168//!
169//! // Carol tsigns the CA's certificate.
170//! let (carol, _)
171//!     = CertBuilder::general_purpose(Some("Carol <carol@another.net>"))
172//!         .generate()?;
173//! let mut carol_signer = carol.primary_key().key().clone()
174//!     .parts_into_secret()?.into_keypair()?;
175//!
176//! let ca_tsig = SignatureBuilder::new(SignatureType::GenericCertification)
177//!     .set_trust_signature(2, 120)?
178//!     .set_regular_expression("<[^>]+[@.]example\\.org>$")?
179//!     .add_regular_expression("<[^>]+[@.]example\\.com>$")?
180//!     .sign_userid_binding(
181//!         &mut carol_signer,
182//!         ca.primary_key().component(),
183//!         ca_userid)?;
184//! let ca = ca.insert_packets(ca_tsig.clone())?.0;
185//! # assert!(ca.clone().into_packets().any(|p| {
186//! #   match p {
187//! #       Packet::Signature(sig) => sig == ca_tsig,
188//! #       _ => false,
189//! #   }
190//! # }));
191//!
192//!
193//! // Carol now tries to authenticate Alice and Bob's certificates
194//! // using the CA as a trusted introducer based on `ca_tsig`.
195//! let res = RegexSet::from_signature(&ca_tsig)?;
196//!
197//! // Should be able to authenticate Alice.
198//! let alice_ua = alice.with_policy(p, None)?
199//!     .userids().nth(0).expect("Added a User ID");
200//! # assert!(res.matches_userid(alice_ua.userid()));
201//! let mut authenticated = false;
202//! for c in alice_ua.certifications() {
203//!     if c.get_issuers().into_iter().any(|h| h.aliases(ca.key_handle())) {
204//!         if c.clone().verify_userid_binding(
205//!             ca.primary_key().key(),
206//!             alice.primary_key().key(),
207//!             alice_ua.userid()).is_ok()
208//!         {
209//!             authenticated |= res.matches_userid(alice_ua.userid());
210//!         }
211//!     }
212//! }
213//! assert!(authenticated);
214//!
215//! // But, although the CA has certified Bob's key, Carol doesn't rely
216//! // on it, because Bob's email address ("bob@some.org") is out of
217//! // scope (some.org, not example.com).
218//! let bob_ua = bob.with_policy(p, None)?
219//!     .userids().nth(0).expect("Added a User ID");
220//! # assert!(! res.matches_userid(bob_ua.userid()));
221//! let mut have_certification = false;
222//! let mut authenticated = false;
223//! for c in bob_ua.certifications() {
224//!     if c.get_issuers().into_iter().any(|h| h.aliases(ca.key_handle())) {
225//!         if c.clone().verify_userid_binding(
226//!             ca.primary_key().key(),
227//!             bob.primary_key().key(),
228//!             bob_ua.userid()).is_ok()
229//!         {
230//!             have_certification = true;
231//!             authenticated |= res.matches_userid(bob_ua.userid());
232//!         }
233//!     }
234//! }
235//! assert!(have_certification);
236//! assert!(! authenticated);
237//! # Ok(()) }
238//! ```
239
240use std::borrow::Borrow;
241use std::fmt;
242
243use lalrpop_util::ParseError;
244use regex_syntax::hir::{self, Hir};
245
246use crate::Error;
247use crate::Result;
248use crate::packet::prelude::*;
249use crate::types::SignatureType;
250
251pub(crate) mod lexer;
252lalrpop_util::lalrpop_mod!(
253    #[allow(clippy::all)]
254    #[allow(unused_parens)]
255    grammar,
256    "/regex/grammar.rs"
257);
258
259pub(crate) use self::lexer::Token;
260pub(crate) use self::lexer::{Lexer, LexicalError};
261
262const TRACE: bool = false;
263
264// Convert tokens into strings.
265//
266// Unfortunately, we can't implement From, because we don't define
267// ParseError in this crate.
268pub(crate) fn parse_error_downcast(e: ParseError<usize, Token, LexicalError>)
269    -> ParseError<usize, String, LexicalError>
270{
271    match e {
272        ParseError::UnrecognizedToken {
273            token: (start, t, end),
274            expected,
275        } => ParseError::UnrecognizedToken {
276            token: (start, t.into(), end),
277            expected,
278        },
279
280        ParseError::ExtraToken {
281            token: (start, t, end),
282        } => ParseError::ExtraToken {
283            token: (start, t.into(), end),
284        },
285
286        ParseError::InvalidToken { location }
287        => ParseError::InvalidToken { location },
288
289        ParseError::User { error }
290        => ParseError::User { error },
291
292        ParseError::UnrecognizedEof { location, expected }
293        => ParseError::UnrecognizedEof { location, expected },
294    }
295}
296
297// Used by grammar.lalrpop to generate a regex class (e.g. '[a-ce]').
298fn generate_class(caret: bool, chars: impl Iterator<Item=char>) -> Hir
299{
300    tracer!(TRACE, "generate_class");
301
302    // Dealing with ranges is a bit tricky.  We need to examine three
303    // tokens.  If the middle one is a dash, it's a range.
304
305    let chars: Vec<Option<char>> = chars
306        // Pad it out so what we can use windows to get three
307        // characters at a time, and be sure to process all
308        // characters.
309        .map(Some)
310        .chain(std::iter::once(None))
311        .chain(std::iter::once(None))
312        .collect();
313    if chars.len() == 2 {
314        // The grammar doesn't allow an empty class.
315        unreachable!();
316    } else {
317        let r = chars
318            .windows(3)
319            .scan(0,
320                  |skip: &mut usize, x: &[Option<char>]|
321                      // Scan stops if the result is None.
322                      // filter_map keeps only those elements that
323                      // are Some.
324                      -> Option<Option<hir::ClassUnicodeRange>>
325                  {
326                      if *skip > 0 {
327                          *skip -= 1;
328                          t!("Skipping: {:?} (skip now: {})", x, skip);
329                          Some(None)
330                      } else {
331                          match (x[0], x[1], x[2]) {
332                              (Some(a), Some('-'), Some(c)) => {
333                                  // We've got a real range.
334                                  *skip = 2;
335                                  t!("range for '{}-{}'", a, c);
336                                  Some(Some(hir::ClassUnicodeRange::new(a, c)))
337                              }
338                              (Some(a), _, _) => {
339                                  t!("range for '{}'", a);
340                                  Some(Some(hir::ClassUnicodeRange::new(a, a)))
341                              }
342                              (None, _, _) => unreachable!(),
343                          }
344                      }
345                  })
346            .flatten();
347        let mut class = hir::Class::Unicode(hir::ClassUnicode::new(r));
348        if caret {
349            class.negate();
350        }
351        Hir::class(class)
352    }
353}
354
355/// A compiled OpenPGP regular expression for matching UTF-8 encoded
356/// strings.
357///
358/// A `Regex` contains a regular expression compiled according to the
359/// rules defined in [Section 8 of RFC 9580] modulo two differences.
360/// First, the compiler only works on UTF-8 strings (not bytes).
361/// Second, ranges in character classes are between UTF-8 characters,
362/// not just ASCII characters.  Further, by default, strings that
363/// don't pass a sanity check (in particular, include Unicode control
364/// characters) never match.  This behavior can be customized using
365/// [`Regex::disable_sanitizations`].
366///
367///   [Section 8 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-8
368///   [trust signatures]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.21
369///   [`Regex::disable_sanitizations`]: Regex::disable_sanitizations()
370///
371/// Regular expressions are used to scope the trust that [trust
372/// signatures] extend.
373///
374/// When working with trust signatures, you'll usually want to use the
375/// [`RegexSet`] data structure, which already implements the correct
376/// semantics.
377///
378///
379/// See the [module-level documentation] for more details.
380///
381///   [module-level documentation]: self
382///
383/// # A note on equality
384///
385/// We define equality on `Regex` as the equality of the uncompiled
386/// regular expression given to the constructor and whether
387/// sanitizations are enabled.
388#[derive(Clone, Debug)]
389pub struct Regex {
390    /// The original regular expression.
391    ///
392    /// Equality is defined using this and `disable_sanitizations`.
393    re: String,
394    regex: regex::Regex,
395    disable_sanitizations: bool,
396}
397assert_send_and_sync!(Regex);
398
399impl PartialEq for Regex {
400    fn eq(&self, other: &Self) -> bool {
401        self.re == other.re
402            && self.disable_sanitizations == other.disable_sanitizations
403    }
404}
405
406impl Eq for Regex {}
407
408impl Regex {
409    /// Parses and compiles the regular expression.
410    ///
411    /// By default, strings that don't pass a sanity check (in
412    /// particular, include Unicode control characters) never match.
413    /// This behavior can be customized using
414    /// [`Regex::disable_sanitizations`].
415    ///
416    ///   [`Regex::disable_sanitizations`]: Regex::disable_sanitizations()
417    pub fn new(re: &str) -> Result<Self>
418    {
419        let lexer = Lexer::new(re);
420        let hir = match grammar::RegexParser::new().parse(re, lexer) {
421            Ok(hir) => hir,
422            Err(err) => return Err(parse_error_downcast(err).into()),
423        };
424
425        // Converting the Hir to a string and the compiling that is
426        // apparently the canonical way to convert a Hir to a Regex
427        // (at least it is what rip-grep does), which the author of
428        // regex also wrote.  See
429        // ripgrep/crates/regex/src/config.rs:ConfiguredHir::regex.
430        let regex = regex::RegexBuilder::new(&hir.to_string())
431            .build()?;
432
433        Ok(Self {
434            re: re.into(),
435            regex,
436            disable_sanitizations: false,
437        })
438    }
439
440    /// Parses and compiles the regular expression.
441    ///
442    /// Returns an error if `re` is not a valid UTF-8 string.
443    ///
444    /// By default, strings that don't pass a sanity check (in
445    /// particular, include Unicode control characters) never match.
446    /// This behavior can be customized using
447    /// [`Regex::disable_sanitizations`].
448    ///
449    ///   [`Regex::disable_sanitizations`]: Regex::disable_sanitizations()
450    pub fn from_bytes(re: &[u8]) -> Result<Self> {
451        Self::new(std::str::from_utf8(re)?)
452    }
453
454    /// Returns the string-representation of the regular expression.
455    pub fn as_str(&self) -> &str {
456        &self.re
457    }
458
459    /// Controls whether matched strings must pass a sanity check.
460    ///
461    /// If `false` (the default), i.e., sanity checks are enabled, and
462    /// the string doesn't pass the sanity check (in particular, it
463    /// contains a Unicode control character according to
464    /// [`char::is_control`], including newlines and an embedded `NUL`
465    /// byte), this returns `false`.
466    ///
467    ///   [`char::is_control`]: https://doc.rust-lang.org/std/primitive.char.html#method.is_control
468    pub fn disable_sanitizations(&mut self, disabled: bool) {
469        self.disable_sanitizations = disabled;
470    }
471
472    /// Returns whether the regular expression matches the string.
473    ///
474    /// If sanity checks are enabled (the default) and the string
475    /// doesn't pass the sanity check (in particular, it contains a
476    /// Unicode control character according to [`char::is_control`],
477    /// including newlines and an embedded `NUL` byte), this returns
478    /// `false`.
479    ///
480    ///   [`char::is_control`]: https://doc.rust-lang.org/std/primitive.char.html#method.is_control
481    pub fn is_match(&self, s: &str) -> bool {
482        if ! self.disable_sanitizations && s.chars().any(char::is_control) {
483            return false;
484        }
485
486        self.is_match_clean(s)
487    }
488
489    // is_match, but without the sanity check.
490    fn is_match_clean(&self, s: &str) -> bool {
491        self.regex.is_match(s)
492    }
493
494    /// Returns whether the regular expression matches the User ID.
495    ///
496    /// If the User ID is not a valid UTF-8 string, this returns
497    /// `false`.
498    ///
499    /// If sanity checks are enabled (the default) and the string
500    /// doesn't pass the sanity check (in particular, it contains a
501    /// Unicode control character according to [`char::is_control`],
502    /// including newlines and an embedded `NUL` byte), this returns
503    /// `false`.
504    ///
505    ///   [`char::is_control`]: https://doc.rust-lang.org/std/primitive.char.html#method.is_control
506    pub fn matches_userid(&self, u: &UserID) -> bool {
507        if let Ok(u) = std::str::from_utf8(u.value()) {
508            self.is_match(u)
509        } else {
510            false
511        }
512    }
513}
514
515#[derive(Clone, Debug)]
516enum RegexSet_ {
517    Regex(Regex),
518    Invalid,
519    Everything,
520}
521assert_send_and_sync!(RegexSet_);
522
523/// A set of regular expressions.
524///
525/// A `RegexSet` encapsulates a set of regular expressions.  The
526/// regular expressions are compiled according to the rules defined in
527/// [Section 8 of RFC 9580] modulo two differences.  First, the
528/// compiler only works on UTF-8 strings (not bytes).  Second, ranges
529/// in character classes are between UTF-8 characters, not just ASCII
530/// characters.  Further, by default, strings that don't pass a sanity
531/// check (in particular, include Unicode control characters) never
532/// match.  This behavior can be customized using
533/// [`RegexSet::disable_sanitizations`].
534///
535///   [Section 8 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-8
536///   [`RegexSet::disable_sanitizations`]: RegexSet::disable_sanitizations()
537///
538/// `RegexSet` implements the semantics of [regular expression]s used
539/// in [Trust Signatures].  In particular, a `RegexSet` makes it
540/// easier to deal with trust signatures that:
541///
542///   - Contain multiple Regular Expression subpackts,
543///   - Have no Regular Expression subpackets, and/or
544///   - Include one or more Regular Expression subpackets that are invalid.
545///
546///   [regular expressions]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.22
547///   [Trust Signatures]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.21
548///
549/// `RegexSet` compiles each regular expression individually.  If
550/// there are no regular expressions, the `RegexSet` matches
551/// everything.  If a regular expression is invalid, `RegexSet` treats
552/// it as if it doesn't match anything.  Thus, if all regular
553/// expressions are invalid, the `RegexSet` matches nothing (not
554/// everything!).
555///
556/// See the [module-level documentation] for more details.
557///
558///   [module-level documentation]: self
559///
560/// # A note on equality
561///
562/// We define equality on `RegexSet` as the equality of the uncompiled
563/// regular expressions given to the constructor and whether
564/// sanitizations are enabled.
565#[derive(Clone)]
566pub struct RegexSet {
567    /// The original regular expressions.
568    ///
569    /// Equality is defined using this and `disable_sanitizations`.
570    re_bytes: Vec<Vec<u8>>,
571    re_set: RegexSet_,
572    disable_sanitizations: bool,
573}
574assert_send_and_sync!(RegexSet);
575
576impl PartialEq for RegexSet {
577    fn eq(&self, other: &Self) -> bool {
578        self.re_bytes == other.re_bytes
579            && self.disable_sanitizations == other.disable_sanitizations
580    }
581}
582
583impl Eq for RegexSet {}
584
585impl fmt::Debug for RegexSet {
586    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
587        let mut d = f.debug_struct("RegexSet");
588        match self.re_set {
589            RegexSet_::Everything => {
590                d.field("regex", &"<Everything>")
591            }
592            RegexSet_::Invalid => {
593                d.field("regex", &"<Invalid>")
594            }
595            RegexSet_::Regex(ref r) => {
596                d.field("regex", &r.regex)
597            }
598        }
599        .field("sanitizations", &!self.disable_sanitizations)
600            .finish()
601    }
602}
603
604impl RegexSet {
605    /// Parses and compiles the regular expressions.
606    ///
607    /// Invalid regular expressions do not cause this to fail.  See
608    /// [`RegexSet`]'s top-level documentation for details.
609    ///
610    ///
611    /// By default, strings that don't pass a sanity check (in
612    /// particular, include Unicode control characters) never match.
613    /// This behavior can be customized using
614    /// [`RegexSet::disable_sanitizations`].
615    ///
616    ///   [`RegexSet::disable_sanitizations`]: RegexSet::disable_sanitizations()
617    ///
618    /// # Examples
619    ///
620    /// ```
621    /// use sequoia_openpgp as openpgp;
622    /// use openpgp::regex::RegexSet;
623    ///
624    /// # fn main() -> openpgp::Result<()> {
625    /// // Extract the regex and compile it.
626    /// let res = &[
627    ///     "<[^>]+[@.]example\\.org>$",
628    ///     // Invalid.
629    ///     "[..",
630    /// ];
631    ///
632    /// let res = RegexSet::new(res)?;
633    ///
634    /// assert!(res.is_match("Alice <alice@example.org>"));
635    /// assert!(! res.is_match("Bob <bob@example.com>"));
636    /// # Ok(()) }
637    /// ```
638    pub fn new<'a, RE, I>(res: I) -> Result<Self>
639    where RE: Borrow<&'a str>,
640          I: IntoIterator<Item=RE>,
641    {
642        tracer!(TRACE, "RegexSet::new");
643
644        let mut regexes = Vec::with_capacity(2);
645        let mut had_good = false;
646        let mut had_bad = false;
647
648        let mut re_bytes = Vec::new();
649        for re in res {
650            let re = re.borrow();
651            re_bytes.push(re.as_bytes().into());
652
653            let lexer = Lexer::new(re);
654            match grammar::RegexParser::new().parse(re, lexer) {
655                Ok(hir) => {
656                    had_good = true;
657                    regexes.push(hir);
658                }
659                Err(err) => {
660                    had_bad = true;
661                    t!("Compiling {:?}: {}", re, err);
662                }
663            }
664        }
665
666        if had_bad && ! had_good {
667            t!("All regular expressions were invalid.");
668            Ok(RegexSet {
669                re_bytes,
670                re_set: RegexSet_::Invalid,
671                disable_sanitizations: false,
672            })
673        } else if ! had_bad && ! had_good {
674            // Match everything.
675            t!("No regular expressions provided.");
676            Ok(RegexSet {
677                re_bytes,
678                re_set: RegexSet_::Everything,
679                disable_sanitizations: false,
680            })
681        } else {
682            // Match any of the regular expressions.
683            Ok(RegexSet {
684                re_bytes,
685                re_set: RegexSet_::Regex(
686                    Regex {
687                        re: String::new(),
688                        regex: regex::RegexBuilder::new(
689                            &Hir::alternation(regexes).to_string())
690                            .build()?,
691                        disable_sanitizations: false,
692                    }),
693                disable_sanitizations: false,
694            })
695        }
696    }
697
698    /// Parses and compiles the regular expressions.
699    ///
700    /// The regular expressions are first converted to UTF-8 strings.
701    /// Byte sequences that are not valid UTF-8 strings are considered
702    /// to be invalid regular expressions.  Invalid regular
703    /// expressions do not cause this to fail.  See [`RegexSet`]'s
704    /// top-level documentation for details.
705    ///
706    ///
707    /// By default, strings that don't pass a sanity check (in
708    /// particular, include Unicode control characters) never match.
709    /// This behavior can be customized using
710    /// [`RegexSet::disable_sanitizations`].
711    ///
712    ///   [`RegexSet::disable_sanitizations`]: RegexSet::disable_sanitizations()
713    ///
714    /// # Examples
715    ///
716    /// ```
717    /// use sequoia_openpgp as openpgp;
718    /// use openpgp::regex::RegexSet;
719    ///
720    /// # fn main() -> openpgp::Result<()> {
721    /// // A valid and an invalid UTF-8 byte sequence.  The invalid
722    /// // sequence doesn't match anything.  But, that doesn't impact
723    /// // the other regular expressions.
724    /// let res: &[ &[u8] ] = &[
725    ///     &b"<[^>]+[@.]example\\.org>$"[..],
726    ///     // Invalid UTF-8.
727    ///     &b"\xC3\x28"[..],
728    /// ];
729    /// assert!(std::str::from_utf8(res[0]).is_ok());
730    /// assert!(std::str::from_utf8(res[1]).is_err());
731    ///
732    /// let re_set = RegexSet::from_bytes(res.into_iter())?;
733    ///
734    /// assert!(re_set.is_match("Alice <alice@example.org>"));
735    /// assert!(! re_set.is_match("Bob <bob@example.com>"));
736    ///
737    /// // If we only have invalid UTF-8 strings, then nothing
738    /// // matches.
739    /// let res: &[ &[u8] ] = &[
740    ///     // Invalid UTF-8.
741    ///     &b"\xC3\x28"[..],
742    /// ];
743    /// assert!(std::str::from_utf8(res[0]).is_err());
744    ///
745    /// let re_set = RegexSet::from_bytes(res.into_iter())?;
746    ///
747    /// assert!(! re_set.is_match("Alice <alice@example.org>"));
748    /// assert!(! re_set.is_match("Bob <bob@example.com>"));
749    ///
750    ///
751    /// // But, if we have no regular expressions, everything matches.
752    /// let res: &[ &[u8] ] = &[];
753    /// let re_set = RegexSet::from_bytes(res.into_iter())?;
754    ///
755    /// assert!(re_set.is_match("Alice <alice@example.org>"));
756    /// assert!(re_set.is_match("Bob <bob@example.com>"));
757    /// # Ok(()) }
758    /// ```
759    pub fn from_bytes<'a, I, RE>(res: I) -> Result<Self>
760    where I: IntoIterator<Item=RE>,
761          RE: Borrow<&'a [u8]>,
762    {
763        let mut have_valid_utf8 = false;
764        let mut have_invalid_utf8 = false;
765        let mut re_bytes = Vec::new();
766        let re_set = Self::new(
767            res
768                .into_iter()
769                .scan((&mut have_valid_utf8, &mut have_invalid_utf8),
770                      |(valid, invalid), re|
771                      {
772                          re_bytes.push(re.borrow().to_vec());
773                          if let Ok(re) = std::str::from_utf8(re.borrow()) {
774                              **valid = true;
775                              Some(Some(re))
776                          } else {
777                              **invalid = true;
778                              Some(None)
779                          }
780                      })
781                .flatten());
782
783        if !have_valid_utf8 && have_invalid_utf8 {
784            // None of the strings were valid UTF-8.  Reject
785            // everything.
786            Ok(RegexSet {
787                re_bytes,
788                re_set: RegexSet_::Invalid,
789                disable_sanitizations: false,
790            })
791        } else {
792            // We had nothing or at least one string was valid UTF-8.
793            // RegexSet::new did the right thing.
794            re_set.map(|mut r| { r.re_bytes = re_bytes; r })
795        }
796    }
797
798    /// Returns the bytes-representation of the regular expressions.
799    pub fn as_bytes(&self) -> &[Vec<u8>] {
800        &self.re_bytes
801    }
802
803    /// Creates a `RegexSet` from the regular expressions stored in a
804    /// trust signature.
805    ///
806    /// This method is a convenience function, which extracts any
807    /// regular expressions from a [Trust Signature] and wraps them in a
808    /// `RegexSet`.
809    ///
810    ///   [Trust Signature]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.21
811    ///
812    /// If the signature is not a valid trust signature (its [type] is
813    /// [GenericCertification], [PersonaCertification],
814    /// [CasualCertification], [PositiveCertification], or [DirectKey],
815    /// and the [Trust Signature] subpacket is present), this returns
816    /// an error.
817    ///
818    ///   [type]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.1
819    ///   [GenericCertification]: crate::types::SignatureType::GenericCertification
820    ///   [PersonaCertification]: crate::types::SignatureType::PersonaCertification
821    ///   [CasualCertification]: crate::types::SignatureType::CasualCertification
822    ///   [PositiveCertification]: crate::types::SignatureType::PositiveCertification
823    ///   [DirectKey]: crate::types::SignatureType::DirectKey
824    ///
825    /// By default, strings that don't pass a sanity check (in
826    /// particular, include Unicode control characters) never match.
827    /// This behavior can be customized using
828    /// [`RegexSet::disable_sanitizations`].
829    ///
830    ///   [`RegexSet::disable_sanitizations`]: RegexSet::disable_sanitizations()
831    ///
832    /// # Examples
833    ///
834    /// ```
835    /// use sequoia_openpgp as openpgp;
836    /// # use openpgp::cert::prelude::*;
837    /// # use openpgp::packet::prelude::*;
838    /// # use openpgp::policy::StandardPolicy;
839    /// use openpgp::regex::RegexSet;
840    /// # use openpgp::types::SignatureType;
841    /// #
842    /// # fn main() -> openpgp::Result<()> {
843    /// # let p = &StandardPolicy::new();
844    /// #
845    /// # let (alice, _)
846    /// #     = CertBuilder::general_purpose(Some("Alice <alice@example.org>"))
847    /// #         .generate()?;
848    /// # let mut alices_signer = alice.primary_key().key().clone()
849    /// #     .parts_into_secret()?.into_keypair()?;
850    /// #
851    /// # let (example_com, _)
852    /// #     = CertBuilder::general_purpose(Some("OpenPGP CA <openpgp-ca@example.com>"))
853    /// #         .generate()?;
854    /// # let example_com_userid = example_com.with_policy(p, None)?
855    /// #     .userids().nth(0).expect("Added a User ID").userid();
856    /// #
857    /// # let certification = SignatureBuilder::new(SignatureType::GenericCertification)
858    /// #     .set_trust_signature(1, 120)?
859    /// #     .set_regular_expression("<[^>]+[@.]example\\.org>$")?
860    /// #     .add_regular_expression("<[^>]+[@.]example\\.com>$")?
861    /// #     .sign_userid_binding(
862    /// #         &mut alices_signer,
863    /// #         example_com.primary_key().component(),
864    /// #         example_com_userid)?;
865    ///
866    /// // certification is a trust signature, which contains two regular
867    /// // expressions: one that matches all mail addresses for 'example.org'
868    /// // and another that matches all mail addresses for 'example.com'.
869    /// let certification: &Signature = // ...;
870    /// # &certification;
871    ///
872    /// // Extract the regex and compile it.
873    /// let res = RegexSet::from_signature(certification)?;
874    ///
875    /// // Some positive examples.
876    /// assert!(res.is_match("Alice <alice@example.org>"));
877    /// assert!(res.is_match("Bob <bob@example.com>"));
878    ///
879    /// // Wrong domain.
880    /// assert!(! res.is_match("Carol <carol@acme.com>"));
881    ///
882    /// // The standard regex, "<[^>]+[@.]example\\.org>$" only matches
883    /// // email addresses wrapped in <>.
884    /// assert!(! res.is_match("dave@example.com"));
885    ///
886    /// // And, it is case-sensitive.
887    /// assert!(res.is_match("Ellen <ellen@example.com>"));
888    /// assert!(! res.is_match("Ellen <ellen@EXAMPLE.COM>"));
889    /// # Ok(()) }
890    /// ```
891    pub fn from_signature(sig: &Signature) -> Result<Self>
892    {
893        use SignatureType::*;
894        match sig.typ() {
895            GenericCertification => (),
896            PersonaCertification => (),
897            CasualCertification => (),
898            PositiveCertification => (),
899            DirectKey => (),
900            t => return Err(
901                Error::InvalidArgument(
902                    format!(
903                        "Expected a certification or direct key signature, found a {}",
904                        t))
905                    .into()),
906        }
907
908        if sig.trust_signature().is_none() {
909            return Err(
910                Error::InvalidArgument(
911                    "Expected a trust signature, \
912                     but the signature does not include \
913                     a valid Trust Signature subpacket".into())
914                    .into());
915        }
916
917        Self::from_bytes(sig.regular_expressions())
918    }
919
920    /// Returns a `RegexSet` that matches everything.
921    ///
922    /// Note: sanitizations are still enabled.  So, to really match
923    /// everything, you still need to call
924    /// [`RegexSet::disable_sanitizations`].
925    ///
926    ///   [`RegexSet::disable_sanitizations`]: RegexSet::disable_sanitizations()
927    ///
928    /// This can be used to optimize the evaluation of scoping rules
929    /// along a path: if a `RegexSet` matches everything, then it
930    /// doesn't further constrain the path.
931    pub fn everything() -> Self
932    {
933        Self {
934            re_bytes: vec![vec![]],
935            re_set: RegexSet_::Everything,
936            disable_sanitizations: false,
937        }
938    }
939
940    /// Returns whether a `RegexSet` matches everything.
941    ///
942    /// Normally, this only returns true if the `RegexSet` was created
943    /// using [`RegexSet::everything`].  [`RegexSet::new`],
944    /// [`RegexSet::from_bytes`], [`RegexSet::from_signature`] do
945    /// detect some regular expressions that match everything (e.g.,
946    /// if no regular expressions are supplied).  But, they do not
947    /// guarantee that a `RegexSet` containing a regular expression
948    /// like `.?`, which does in fact match everything, is detected as
949    /// matching everything.
950    ///
951    ///   [`RegexSet::everything`]: RegexSet::everything()
952    ///   [`RegexSet::new`]: RegexSet::everything()
953    ///   [`RegexSet::from_bytes`]: RegexSet::from_bytes()
954    ///   [`RegexSet::from_signature`]: RegexSet::from_signature()
955    ///
956    /// # Examples
957    ///
958    /// ```
959    /// use sequoia_openpgp as openpgp;
960    /// use openpgp::regex::RegexSet;
961    ///
962    /// # fn main() -> openpgp::Result<()> {
963    /// assert!(RegexSet::everything().matches_everything());
964    /// let empty: &[ &str ] = &[];
965    /// assert!(RegexSet::new(empty)?.matches_everything());
966    ///
967    /// // A regular expression that matches everything.  But
968    /// // `RegexSet` returns false, because it can't detect it.
969    /// let res: &[ &str ] = &[
970    ///     &".?"[..],
971    /// ];
972    /// let re_set = RegexSet::new(res.into_iter())?;
973    /// assert!(! re_set.matches_everything());
974    /// # Ok(()) }
975    /// ```
976    pub fn matches_everything(&self) -> bool {
977        matches!(self.re_set, RegexSet_::Everything)
978    }
979
980    /// Controls whether strings with control characters are allowed.
981    ///
982    /// If `false` (the default), i.e., sanity checks are enabled, and
983    /// the string doesn't pass the sanity check (in particular, it
984    /// contains a Unicode control character according to
985    /// [`char::is_control`], including newlines and an embedded `NUL`
986    /// byte), this returns `false`.
987    ///
988    ///   [`char::is_control`]: https://doc.rust-lang.org/std/primitive.char.html#method.is_control
989    pub fn disable_sanitizations(&mut self, allowed: bool) {
990        self.disable_sanitizations = allowed;
991        if let RegexSet_::Regex(ref mut re) = self.re_set {
992            re.disable_sanitizations(allowed);
993        }
994    }
995
996    /// Returns whether the regular expression set matches the string.
997    ///
998    /// If sanity checks are enabled (the default) and the string
999    /// doesn't pass the sanity check (in particular, it contains a
1000    /// Unicode control character according to [`char::is_control`],
1001    /// including newlines and an embedded `NUL` byte), this returns
1002    /// `false`.
1003    ///
1004    ///   [`char::is_control`]: https://doc.rust-lang.org/std/primitive.char.html#method.is_control
1005    ///
1006    /// If the `RegexSet` contains one or more regular expressions,
1007    /// this method returns whether at least one of the regular
1008    /// expressions matches.  Invalid regular expressions never match.
1009    ///
1010    /// If the `RegexSet` does not contain any regular expressions
1011    /// (valid or otherwise), this method returns `true`.
1012    ///
1013    /// # Examples
1014    ///
1015    /// ```
1016    /// use sequoia_openpgp as openpgp;
1017    /// use openpgp::regex::RegexSet;
1018    ///
1019    /// # fn main() -> openpgp::Result<()> {
1020    /// // A regular expression that matches anything.  (Note: this is
1021    /// // equivalent to providing no regular expressions.)
1022    /// let res: &[ &str ] = &[
1023    ///     &""[..],
1024    /// ];
1025    /// let re_set = RegexSet::new(res.into_iter())?;
1026    ///
1027    /// assert!(re_set.is_match("Alice Lovelace <alice@example.org>"));
1028    ///
1029    /// // If a User ID has an embedded control character, it doesn't
1030    /// // match.
1031    /// assert!(! re_set.is_match("Alice <alice@example.org>\0"));
1032    /// # Ok(()) }
1033    /// ```
1034    pub fn is_match(&self, s: &str) -> bool {
1035        if ! self.disable_sanitizations && s.chars().any(char::is_control) {
1036            return false;
1037        }
1038
1039        match self.re_set {
1040            RegexSet_::Regex(ref re) =>
1041                re.is_match_clean(s),
1042            RegexSet_::Invalid =>
1043                false,
1044            RegexSet_::Everything =>
1045                true,
1046        }
1047    }
1048
1049    /// Returns whether the regular expression matches the User ID.
1050    ///
1051    /// If the User ID is not a valid UTF-8 string, this returns `false`.
1052    ///
1053    /// If sanity checks are enabled (the default) and the string
1054    /// doesn't pass the sanity check (in particular, it contains a
1055    /// Unicode control character according to [`char::is_control`],
1056    /// including newlines and an embedded `NUL` byte), this returns
1057    /// `false`.
1058    ///
1059    ///   [`char::is_control`]: https://doc.rust-lang.org/std/primitive.char.html#method.is_control
1060    ///
1061    /// If the `RegexSet` contains one or more regular expressions,
1062    /// this method returns whether at least one of the regular
1063    /// expressions matches.  Invalid regular expressions never match.
1064    ///
1065    /// If the `RegexSet` does not contain any regular expressions
1066    /// (valid or otherwise), this method returns `true`.
1067    ///
1068    /// # Examples
1069    ///
1070    /// ```
1071    /// use sequoia_openpgp as openpgp;
1072    /// use openpgp::packet::UserID;
1073    /// use openpgp::regex::RegexSet;
1074    ///
1075    /// # fn main() -> openpgp::Result<()> {
1076    /// // A regular expression that matches anything.  (Note: this is
1077    /// // equivalent to providing no regular expressions.)
1078    /// let res: &[ &str ] = &[
1079    ///     "",
1080    /// ];
1081    /// let re_set = RegexSet::new(res.into_iter())?;
1082    ///
1083    /// assert!(re_set.matches_userid(
1084    ///     &UserID::from(&b"Alice Lovelace <alice@example.org>"[..])));
1085    ///
1086    /// // If a User ID is not valid UTF-8, it never matches.
1087    /// assert!(! re_set.matches_userid(
1088    ///     &UserID::from(&b"Alice \xC3\x28 Lovelace <alice@example.org>"[..])));
1089    ///
1090    /// // If a User ID has an embedded control character, it doesn't
1091    /// // match.
1092    /// assert!(! re_set.matches_userid(
1093    ///     &UserID::from(&b"Alice <alice@example.org>\0"[..])));
1094    /// # Ok(()) }
1095    /// ```
1096    pub fn matches_userid(&self, u: &UserID) -> bool
1097    {
1098        if let Ok(u) = std::str::from_utf8(u.value()) {
1099            self.is_match(u)
1100        } else {
1101            false
1102        }
1103    }
1104}
1105
1106#[cfg(test)]
1107mod tests {
1108    use super::*;
1109
1110    #[test]
1111    fn regex() -> Result<()> {
1112        fn a(regex: &str, matches: &[(bool, &str)]) {
1113            eprint!("{} -> ", regex);
1114            let mut compiled = Regex::new(regex).unwrap();
1115            compiled.disable_sanitizations(true);
1116            eprintln!("{:?}", compiled);
1117            for &(matches, text) in matches {
1118                assert_eq!(matches, compiled.is_match(text),
1119                           "regex: {}\n text: {:?} should{} match",
1120                           regex, text, if matches { "" } else { " not" });
1121            }
1122        }
1123        fn f(regex: &str) {
1124            eprint!("{} -> ", regex);
1125            let compiled = Regex::new(regex);
1126            assert!(compiled.is_err());
1127            eprintln!("failed (expected)");
1128        }
1129
1130        // Test an important corner case: the + should only apply to
1131        // the b!  See: https://github.com/rust-lang/regex/issues/731
1132        a("xab+y", &[
1133            (true, "xaby"),
1134            (true, "xabby"),
1135            (false, "xababy"),
1136        ]);
1137        a("x(ab+)y", &[
1138            (false, "xy"),
1139            (false, "xay"),
1140            (true, "xaby"),
1141            (true, "xabby"),
1142            (true, "xabbby"),
1143            (false, "xababy"),
1144        ]);
1145        // But here the + matches "ab", not just the "b".
1146        a("x(ab)+y", &[
1147            (false, "xy"),
1148            (true, "xaby"),
1149            (false, "xabby"),
1150            (true, "xababy"),
1151            (true, "xabababy"),
1152            (false, "x(ab)y"),
1153        ]);
1154
1155
1156
1157        a("", &[
1158            (true, "s"),
1159            (true, "ss"),
1160        ]);
1161        a("s", &[
1162            (true, "s"),
1163            (true, "ss"),
1164            (false, "a"),
1165            (true, "hello, my prettiessss"),
1166            (false, "S"),
1167        ]);
1168        a("ss", &[
1169            (false, "s"),
1170            (true, "ss"),
1171            (true, "sss"),
1172            (false, "this has lots of ses, but not two ses together"),
1173            (true, "halloss"),
1174        ]);
1175
1176        a("a|b", &[
1177            (true, "a"),
1178            (true, "b"),
1179            (false, "c"),
1180            (true, "xxxaxxxbxxx"),
1181        ]);
1182        a("a|b|c", &[
1183            (true, "a"),
1184            (true, "b"),
1185            (true, "c"),
1186            (false, "d"),
1187            (true, "xxxaxxxbxxx"),
1188        ]);
1189        // This should match anything.
1190        a("|a", &[
1191            (true, "a"),
1192            (true, "b"),
1193        ]);
1194        a("a|", &[
1195            (true, "a"),
1196            (true, "b"),
1197        ]);
1198        a("|a|b", &[
1199            (true, "a"),
1200            (true, "b"),
1201            (true, "c"),
1202        ]);
1203        a("|a|b|c|d", &[
1204            (true, "a"),
1205            (true, "b"),
1206            (true, "c"),
1207            (true, "d"),
1208            (true, "eeee"),
1209        ]);
1210        a("a|b|", &[
1211            (true, "a"),
1212            (true, "b"),
1213            (true, "c"),
1214        ]);
1215        a("a|b|c|", &[
1216            (true, "a"),
1217            (true, "b"),
1218            (true, "c"),
1219            (true, "d"),
1220            (true, "eeee"),
1221        ]);
1222        a("|", &[
1223            (true, "a"),
1224            (true, "b"),
1225            (true, "c"),
1226            (true, "d"),
1227            (true, "eeee"),
1228        ]);
1229        a("|a|", &[
1230            (true, "a"),
1231            (true, "b"),
1232            (true, "c"),
1233            (true, "d"),
1234            (true, "eeee"),
1235        ]);
1236        a("|a|b|", &[
1237            (true, "a"),
1238            (true, "b"),
1239            (true, "c"),
1240            (true, "d"),
1241            (true, "eeee"),
1242        ]);
1243        // A nested empty.
1244        a("(a|)|b", &[
1245            (true, "a"),
1246            (true, "b"),
1247        ]);
1248        // empty+
1249        a("(a|b|()+)", &[
1250            (true, "a"),
1251            (true, "b"),
1252        ]);
1253        // (empty)+
1254        a("(a|b|(())+)", &[
1255            (true, "a"),
1256            (true, "b"),
1257        ]);
1258        // Multiple empty branches.
1259        a("(a|b|(()())())", &[
1260            (true, "a"),
1261            (true, "b"),
1262        ]);
1263        a("(a|b|(()())())|", &[
1264            (true, "a"),
1265            (true, "b"),
1266        ]);
1267
1268        // This is: "ab" or "cd", not a followed by b or c followed by d:
1269        //
1270        //   A regular expression is zero or more branches, separated by '|'.
1271        //   ...
1272        //   A branch is zero or more pieces, concatenated.
1273        //   ...
1274        //   A piece is an atom
1275        //   ...
1276        //   An atom is... a single character.
1277        a("ab|cd", &[
1278            (true, "abd"),
1279            (true, "acd"),
1280            (true, "abcd"),
1281            (false, "ad"),
1282            (false, "b"),
1283            (false, "c"),
1284            (false, "bb"),
1285        ]);
1286
1287        a("a*", &[
1288            (true, ""),
1289            (true, "a"),
1290            (true, "aa"),
1291            (true, "b"),
1292        ]);
1293        a("xa*y", &[
1294            (true, "xy"),
1295            (true, "xay"),
1296            (true, "xaay"),
1297            (false, "y"),
1298            (false, "ay"),
1299            (false, "aay"),
1300            (false, "x y"),
1301            (false, "x ay"),
1302            (false, "x aay"),
1303        ]);
1304        f("*");
1305
1306        a("a+", &[
1307            (false, ""),
1308            (true, "a"),
1309            (true, "aa"),
1310            (false, "b"),
1311            (true, "baab"),
1312            (true, "by ab"),
1313            (true, "baa b"),
1314        ]);
1315        a("ab+", &[
1316            (false, ""),
1317            (false, "a"),
1318            (false, "b"),
1319            (true, "ab"),
1320            (false, "bb"),
1321            (true, "baab"),
1322            (true, "by ab"),
1323            (false, "baa b"),
1324        ]);
1325        f("+");
1326
1327        a("a?", &[
1328            (true, ""),
1329            (true, "a"),
1330            (true, "aa"),
1331            (true, "aaa"),
1332            (true, "b"),
1333            (true, "baab"),
1334            (true, "by ab"),
1335            (true, "baa b"),
1336        ]);
1337        a("xa?y", &[
1338            (false, ""),
1339            (true, "xy"),
1340            (false, "a"),
1341            (true, "xay"),
1342            (false, "aa"),
1343            (false, "xaay"),
1344            (false, "b"),
1345            (false, "bxaayb"),
1346            (true, "by xayb"),
1347            (true, "baxay b"),
1348        ]);
1349        f("?");
1350
1351        f("a*?");
1352        a("a*b?c+", &[
1353            (false, ""),
1354            (true, "c"),
1355            (true, "abc"),
1356            (true, "aabbcc"),
1357            (false, "aab"),
1358            (true, "aaaaaabcccccccc"),
1359        ]);
1360        f("a?*+");
1361
1362        a("a?|b+", &[
1363            (true, ""),
1364            (true, "aaa"),
1365            (true, "bbb"),
1366            (true, "abaa"),
1367        ]);
1368        a("a+|b+", &[
1369            (false, ""),
1370            (true, "a"),
1371            (true, "aaa"),
1372            (true, "b"),
1373            (true, "bbb"),
1374            (true, "abaa"),
1375        ]);
1376        a("a+|b+|c+", &[
1377            (false, ""),
1378            (true, "a"),
1379            (true, "aaa"),
1380            (true, "b"),
1381            (true, "bbb"),
1382            (true, "abaa"),
1383            (true, "c"),
1384            (true, "ccc"),
1385            (true, "abaaccc"),
1386        ]);
1387        a("xa+|b+|c+y", &[
1388            (false, ""),
1389            (true, "xa"),
1390            (true, "xaa"),
1391            (true, "b"),
1392            (true, "bb"),
1393            (true, "cy"),
1394            (true, "ccy"),
1395
1396            (false, "a"),
1397            (false, "aaa"),
1398            (false, "c"),
1399            (false, "ccc"),
1400        ]);
1401        a("xa+y|sb+u", &[
1402            (false, ""),
1403            (true, "xay"),
1404            (true, "xaay"),
1405            (true, "sbu"),
1406            (true, "sbbu"),
1407            (true, "xysbu"),
1408
1409            (false, "a"),
1410            (false, "aaa"),
1411            (false, "xyu"),
1412            (false, "ccc"),
1413        ]);
1414        a("a*|a+|ab+cd+|", &[
1415            (true, ""),
1416        ]);
1417
1418        a("()", &[
1419            (true, ""),
1420            (true, "xyzzy"),
1421        ]);
1422        a("(())", &[
1423            (true, ""),
1424            (true, "xyzzy"),
1425        ]);
1426        a("((()))", &[
1427            (true, ""),
1428            (true, "xyzzy"),
1429        ]);
1430        f("((())");
1431        f("((())))");
1432        a("(a)", &[
1433            (true, "a"),
1434            (true, "(a)"),
1435            (false, "b"),
1436        ]);
1437        a("x(a)y", &[
1438            (false, "xy"),
1439            (true, "xay"),
1440            (false, "x(a)y"),
1441            (true, "(xay)"),
1442            (false, "a"),
1443            (false, "yax"),
1444        ]);
1445        a("x(ab)y", &[
1446            (false, "xy"),
1447            (false, "xay"),
1448            (false, "xby"),
1449            (true, "xaby"),
1450            (false, "x(ab)y"),
1451            (true, "(xaby)"),
1452        ]);
1453        a("x(ab)(cd)y", &[
1454            (true, "xabcdy"),
1455            (true, "zxabcdyz"),
1456        ]);
1457        a("a(bc)d(ef)g", &[
1458            (true, "abcdefg"),
1459            (true, "xabcdefgy"),
1460            (false, "xa(bc)d(ef)gy"),
1461        ]);
1462        a("a((bc))d((ef))g", &[
1463            (true, "abcdefg"),
1464            (true, "xabcdefgy"),
1465            (false, "xa(bc)d(ef)gy"),
1466        ]);
1467        a("a(b(c)d)e", &[
1468            (true, "abcde"),
1469            (true, "xabcdey"),
1470            (false, "xa(b(c)d)ey"),
1471        ]);
1472        a("x(a|b)y", &[
1473            (false, "xy"),
1474            (true, "xay"),
1475            (true, "xby"),
1476            (false, "xaay"),
1477            (false, "xbby"),
1478            (false, "xaby"),
1479            (false, "xaaby"),
1480            (false, "xabby"),
1481            (false, "xaabby"),
1482            (false, "xcy"),
1483        ]);
1484        a("x(a|bc)y", &[
1485            (false, "xy"),
1486            (true, "xay"),
1487            (false, "xby"),
1488            (true, "xbcy"),
1489            (false, "xaay"),
1490            (false, "xbby"),
1491            (false, "xaby"),
1492            (false, "xabcy"),
1493            (false, "xabby"),
1494            (false, "xaabby"),
1495            (false, "xcy"),
1496            (false, "xacy"),
1497        ]);
1498        a("x(a|b|c)y", &[
1499            (false, "xy"),
1500            (true, "xay"),
1501            (true, "xby"),
1502            (true, "xcy"),
1503            (false, "xaay"),
1504            (false, "xbby"),
1505            (false, "xaby"),
1506            (false, "xabcy"),
1507            (false, "xabby"),
1508            (false, "xaabby"),
1509            (false, "xacy"),
1510        ]);
1511        a("x(a|b)(c|d)y", &[
1512            (false, "xy"),
1513            (false, "xay"),
1514            (false, "xby"),
1515            (false, "xcy"),
1516            (false, "xdy"),
1517            (false, "xaay"),
1518            (false, "xbby"),
1519            (false, "xccy"),
1520            (false, "xddy"),
1521            (false, "xaby"),
1522            (false, "xcdy"),
1523            (true, "xacy"),
1524            (true, "xady"),
1525            (true, "xbcy"),
1526            (true, "xbdy"),
1527            (false, "xabcy"),
1528            (false, "xabby"),
1529            (false, "xaabby"),
1530        ]);
1531        a("x(a+|b+)y", &[
1532            (false, "xy"),
1533            (true, "xay"),
1534            (true, "xby"),
1535            (true, "xaay"),
1536            (true, "xbby"),
1537            (false, "xaby"),
1538            (false, "xaaby"),
1539            (false, "xabby"),
1540            (false, "xaabby"),
1541            (false, "xcy"),
1542        ]);
1543
1544        a(".", &[
1545            (false, ""),
1546            (true, "a"),
1547            (true, "ab"),
1548            (true, "ab\nc"),
1549            (true, "ab.c"),
1550        ]);
1551        a("x.y", &[
1552            (false, ""),
1553            (false, "xy"),
1554            (true, "xay"),
1555            (true, "x\ny"),
1556            (true, "x.y"),
1557            (false, "x..y"),
1558        ]);
1559
1560        a("^", &[
1561            (true, ""),
1562            (true, "xx"),
1563        ]);
1564        a("^abc", &[
1565            (false, ""),
1566            (true, "abcdef"),
1567            (false, "xabcdef"),
1568            (false, "\nabcdef"),
1569        ]);
1570        a("(^abc|^def)", &[
1571            (false, ""),
1572            (true, "abcd"),
1573            (true, "defg"),
1574            (false, "xabcd"),
1575            (false, "xdefg"),
1576            (false, "^abc"),
1577            (false, "^(abc|def)"),
1578            (false, "\nabcdef"),
1579        ]);
1580        a("(^abc|def)", &[
1581            (false, ""),
1582            (true, "abcd"),
1583            (true, "defg"),
1584            (false, "xabcd"),
1585            (true, "xdefg"),
1586            (false, "^abc"),
1587            (true, "^(abc|def)"),
1588            (false, "\nabcde"),
1589        ]);
1590        a("^^", &[
1591            (true, ""),
1592            (true, "abcdef"),
1593        ]);
1594        a("^abc^", &[
1595            (false, ""),
1596            (false, "abcdef"),
1597            (false, "xabcdef"),
1598            (false, "abc\n"),
1599            (false, "\nabc\n"),
1600            (false, "^abc^"),
1601        ]);
1602
1603        a("$", &[
1604            (true, ""),
1605            (true, "abc"),
1606        ]);
1607        a("abc$", &[
1608            (false, ""),
1609            (true, "abc"),
1610            (false, "abcx"),
1611            (false, "abc\n"),
1612            (false, "abc$"),
1613        ]);
1614        a("abc$$", &[
1615            (false, ""),
1616            (true, "abc"),
1617            (false, "abcx"),
1618            (false, "abc\n"),
1619            (false, "abc$"),
1620        ]);
1621        a("(abc$)x", &[
1622            (false, ""),
1623            (false, "abc"),
1624            (false, "abcx"),
1625            (false, "abc\nx"),
1626            (false, "abc$x"),
1627        ]);
1628        a("abc$|def$", &[
1629            (false, ""),
1630            (true, "abc"),
1631            (false, "abcx"),
1632            (false, "abc\n"),
1633            (false, "abc$"),
1634            (true, "def"),
1635            (false, "defx"),
1636            (false, "def\n"),
1637            (false, "def$"),
1638            (true, "abcdef"),
1639        ]);
1640
1641        a("\\|", &[
1642            (true, "|"),
1643            (false, ""),
1644            (false, "a"),
1645        ]);
1646        a("\\*", &[
1647            (true, "*"),
1648            (false, ""),
1649            (false, "a"),
1650        ]);
1651        a("\\+", &[
1652            (true, "+"),
1653            (false, ""),
1654            (false, "a"),
1655        ]);
1656        a("\\?", &[
1657            (true, "?"),
1658            (false, ""),
1659            (false, "a"),
1660        ]);
1661        a("\\.", &[
1662            (true, "."),
1663            (false, ""),
1664            (false, "a"),
1665        ]);
1666        a("\\^", &[
1667            (true, "^"),
1668            (false, ""),
1669            (false, "a"),
1670        ]);
1671        a("\\$", &[
1672            (true, "$"),
1673            (false, ""),
1674            (false, "a"),
1675        ]);
1676        a("\\\\", &[
1677            (true, "\\"),
1678            (false, ""),
1679            (false, "a"),
1680        ]);
1681        a("\\[", &[
1682            (true, "["),
1683            (false, ""),
1684            (false, "a"),
1685        ]);
1686        a("\\]", &[
1687            (true, "]"),
1688            (false, ""),
1689            (false, "a"),
1690        ]);
1691        a("\\-", &[
1692            (true, "-"),
1693            (false, ""),
1694            (false, "a"),
1695        ]);
1696        f("\\");
1697
1698        a("[a]", &[
1699            (true, "a"),
1700            (false, "b"),
1701        ]);
1702        a("[abc]", &[
1703            (true, "a"),
1704            (true, "b"),
1705            (true, "c"),
1706            (false, "d"),
1707        ]);
1708        a("[a-c]", &[
1709            (true, "a"),
1710            (true, "b"),
1711            (true, "c"),
1712            (false, "d"),
1713        ]);
1714        a("[xa-c]", &[
1715            (true, "a"),
1716            (true, "b"),
1717            (true, "c"),
1718            (true, "x"),
1719            (false, "d"),
1720        ]);
1721        a("[a-cxyz]", &[
1722            (true, "a"),
1723            (true, "b"),
1724            (true, "c"),
1725            (true, "x"),
1726            (false, "d"),
1727        ]);
1728        a("[a-c]x", &[
1729            (false, "a"),
1730            (false, "b"),
1731            (false, "c"),
1732            (false, "x"),
1733            (true, "ax"),
1734            (true, "bx"),
1735            (true, "cx"),
1736            (false, "d"),
1737            (false, "dx"),
1738        ]);
1739        a("[a-cxy]", &[
1740            (true, "a"),
1741            (true, "b"),
1742            (true, "c"),
1743            (true, "x"),
1744            (true, "y"),
1745            (false, "d"),
1746        ]);
1747        a("[a-c]xy", &[
1748            (false, "a"),
1749            (false, "b"),
1750            (false, "c"),
1751            (false, "x"),
1752            (false, "ax"),
1753            (false, "bx"),
1754            (false, "cx"),
1755            (true, "axy"),
1756            (true, "bxy"),
1757            (true, "cxy"),
1758            (false, "d"),
1759        ]);
1760        a("[a-cxyz]", &[
1761            (true, "a"),
1762            (true, "b"),
1763            (true, "c"),
1764            (true, "x"),
1765            (true, "y"),
1766            (true, "z"),
1767            (false, "d"),
1768        ]);
1769        a("[a-c]xyz", &[
1770            (false, "a"),
1771            (false, "b"),
1772            (false, "c"),
1773            (false, "x"),
1774            (false, "ax"),
1775            (false, "bx"),
1776            (false, "cx"),
1777            (false, "axy"),
1778            (false, "bxy"),
1779            (false, "cxy"),
1780            (true, "axyz"),
1781            (true, "bxyz"),
1782            (true, "cxyz"),
1783            (false, "d"),
1784        ]);
1785        a("xyz[a-c]", &[
1786            (false, "a"),
1787            (false, "b"),
1788            (false, "c"),
1789            (false, "x"),
1790            (false, "xa"),
1791            (false, "xb"),
1792            (false, "xc"),
1793            (false, "xya"),
1794            (false, "xyb"),
1795            (false, "xyc"),
1796            (true, "xyza"),
1797            (true, "xyzb"),
1798            (true, "xyzc"),
1799            (false, "d"),
1800        ]);
1801        a("[xyza-c]", &[
1802            (true, "a"),
1803            (true, "b"),
1804            (true, "c"),
1805            (true, "x"),
1806            (true, "y"),
1807            (true, "z"),
1808            (false, "d"),
1809        ]);
1810        a("[xya-cyz]", &[
1811            (true, "a"),
1812            (true, "b"),
1813            (true, "c"),
1814            (true, "x"),
1815            (true, "y"),
1816            (true, "z"),
1817            (false, "d"),
1818        ]);
1819        a("[x-za-c]", &[
1820            (true, "a"),
1821            (true, "b"),
1822            (true, "c"),
1823            (true, "x"),
1824            (true, "y"),
1825            (true, "z"),
1826            (false, "d"),
1827        ]);
1828        a("[x-zmna-c]", &[
1829            (true, "a"),
1830            (true, "b"),
1831            (true, "c"),
1832            (true, "x"),
1833            (true, "y"),
1834            (true, "z"),
1835            (true, "m"),
1836            (true, "n"),
1837            (false, "d"),
1838        ]);
1839        a("[-]", &[
1840            (true, "-"),
1841            (false, "d"),
1842        ]);
1843        a("[a-]", &[
1844            (true, "-"),
1845            (true, "a"),
1846            (false, "d"),
1847        ]);
1848        a("[-b]", &[
1849            (true, "-"),
1850            (true, "b"),
1851            (false, "d"),
1852        ]);
1853        a("[-bd-g]", &[
1854            (false, "a"),
1855            (true, "-"),
1856            (true, "b"),
1857            (true, "d"),
1858            (true, "f"),
1859        ]);
1860        a("[bd-g-]", &[
1861            (false, "a"),
1862            (true, "-"),
1863            (true, "b"),
1864            (true, "d"),
1865            (true, "f"),
1866        ]);
1867        // Backwards ranges.
1868        a("[9-0]", &[
1869            (false, "a"),
1870            (false, "-"),
1871            (true, "9"),
1872            (true, "0"),
1873            (true, "5"),
1874        ]);
1875
1876        a("[^a]", &[
1877            (false, "a"),
1878            (true, "b"),
1879        ]);
1880        a("[^abc]", &[
1881            (false, "a"),
1882            (false, "b"),
1883            (false, "c"),
1884            (true, "d"),
1885        ]);
1886        a("[^a-c]", &[
1887            (false, "a"),
1888            (false, "b"),
1889            (false, "c"),
1890            (true, "d"),
1891        ]);
1892        a("[^xa-c]", &[
1893            (false, "a"),
1894            (false, "b"),
1895            (false, "c"),
1896            (false, "x"),
1897            (true, "d"),
1898        ]);
1899        a("[^a-cxyz]", &[
1900            (false, "a"),
1901            (false, "b"),
1902            (false, "c"),
1903            (false, "x"),
1904            (true, "d"),
1905        ]);
1906        a("[^a-c]x", &[
1907            (false, "a"),
1908            (false, "b"),
1909            (false, "c"),
1910            (false, "x"),
1911            (false, "ax"),
1912            (false, "bx"),
1913            (false, "cx"),
1914            (false, "d"),
1915            (true, "dx"),
1916        ]);
1917        a("[^a-cxy]", &[
1918            (false, "a"),
1919            (false, "b"),
1920            (false, "c"),
1921            (false, "x"),
1922            (false, "y"),
1923            (true, "d"),
1924        ]);
1925        a("[^a-c]xy", &[
1926            (false, "a"),
1927            (false, "b"),
1928            (false, "c"),
1929            (false, "x"),
1930            (false, "ax"),
1931            (false, "bx"),
1932            (false, "cx"),
1933            (false, "axy"),
1934            (false, "bxy"),
1935            (false, "cxy"),
1936            (true, "dxy"),
1937            (false, "d"),
1938        ]);
1939        a("[^a-cxyz]", &[
1940            (false, "a"),
1941            (false, "b"),
1942            (false, "c"),
1943            (false, "x"),
1944            (false, "y"),
1945            (false, "z"),
1946            (true, "d"),
1947        ]);
1948        a("[^a-c]xyz", &[
1949            (false, "a"),
1950            (false, "b"),
1951            (false, "c"),
1952            (false, "x"),
1953            (false, "ax"),
1954            (false, "bx"),
1955            (false, "cx"),
1956            (false, "axy"),
1957            (false, "bxy"),
1958            (false, "cxy"),
1959            (false, "axyz"),
1960            (false, "bxyz"),
1961            (false, "cxyz"),
1962            (true, "dxyz"),
1963            (false, "d"),
1964        ]);
1965        a("xyz[^a-c]", &[
1966            (false, "a"),
1967            (false, "b"),
1968            (false, "c"),
1969            (false, "x"),
1970            (false, "xa"),
1971            (false, "xb"),
1972            (false, "xc"),
1973            (false, "xya"),
1974            (false, "xyb"),
1975            (false, "xyc"),
1976            (false, "xyza"),
1977            (false, "xyzb"),
1978            (false, "xyzc"),
1979            (true, "xyzd"),
1980            (false, "d"),
1981        ]);
1982        a("[^xyza-c]", &[
1983            (false, "a"),
1984            (false, "b"),
1985            (false, "c"),
1986            (false, "x"),
1987            (false, "y"),
1988            (false, "z"),
1989            (true, "d"),
1990        ]);
1991        a("[^xya-cyz]", &[
1992            (false, "a"),
1993            (false, "b"),
1994            (false, "c"),
1995            (false, "x"),
1996            (false, "y"),
1997            (false, "z"),
1998            (true, "d"),
1999        ]);
2000        a("[^x-za-c]", &[
2001            (false, "a"),
2002            (false, "b"),
2003            (false, "c"),
2004            (false, "x"),
2005            (false, "y"),
2006            (false, "z"),
2007            (true, "d"),
2008        ]);
2009        a("[^x-zmna-c]", &[
2010            (false, "a"),
2011            (false, "b"),
2012            (false, "c"),
2013            (false, "x"),
2014            (false, "y"),
2015            (false, "z"),
2016            (false, "m"),
2017            (false, "n"),
2018            (true, "d"),
2019        ]);
2020        a("[^-]", &[
2021            (false, "-"),
2022            (true, "d"),
2023        ]);
2024        a("[^a-]", &[
2025            (false, "-"),
2026            (false, "a"),
2027            (true, "d"),
2028        ]);
2029        a("[^-b]", &[
2030            (false, "-"),
2031            (false, "b"),
2032            (true, "d"),
2033        ]);
2034        a("[^-bd-g]", &[
2035            (true, "a"),
2036            (false, "-"),
2037            (false, "b"),
2038            (false, "d"),
2039            (false, "f"),
2040        ]);
2041        a("[^bd-g-]", &[
2042            (true, "a"),
2043            (false, "-"),
2044            (false, "b"),
2045            (false, "d"),
2046            (false, "f"),
2047        ]);
2048
2049        a("[a|b]", &[
2050            (true, "a"),
2051            (true, "|"),
2052            (false, "c"),
2053        ]);
2054        a("[a\\|b]", &[
2055            (true, "a"),
2056            (true, "|"),
2057            (true, "\\"),
2058            (false, "c"),
2059        ]);
2060        a("[a(b]", &[
2061            (true, "a"),
2062            (true, "("),
2063            (false, "c"),
2064        ]);
2065        a("[a)b]", &[
2066            (true, "a"),
2067            (true, ")"),
2068            (false, "c"),
2069        ]);
2070        a("[a^b]", &[
2071            (true, "a"),
2072            (true, "^"),
2073            (false, "c"),
2074        ]);
2075
2076        f("[]");
2077        f("[^]");
2078        a("[^]]", &[
2079            (true, "a"),
2080            (false, "]"),
2081            (true, "^"),
2082        ]);
2083        a("[]]", &[
2084            (false, "a"),
2085            (true, "]"),
2086        ]);
2087        // Matches [ or ].
2088        a("[][]", &[
2089            (false, "a"),
2090            (true, "["),
2091            (true, "]"),
2092        ]);
2093        // Matches anything but [ or ].
2094        a("[^][]", &[
2095            (true, "a"),
2096            (false, "["),
2097            (false, "]"),
2098        ]);
2099        // Anything but ^.
2100        a("[^^]", &[
2101            (true, "a"),
2102            (false, "^"),
2103            (true, "c"),
2104        ]);
2105
2106        // Make sure - is recognized as an atom when it is not part of
2107        // a range.  That is: a-z matches a or - or z, but it doesn't
2108        // match b (it's not a range).
2109        a("a-z", &[
2110            (true, "a-z"),
2111            (false, "a"),
2112            (false, "-"),
2113            (false, "z"),
2114            (false, "c"),
2115        ]);
2116
2117        a("a|-|z", &[
2118            (true, "a"),
2119            (true, "-"),
2120            (true, "z"),
2121            (false, "c"),
2122        ]);
2123
2124        Ok(())
2125    }
2126
2127    #[test]
2128    fn regex_set() -> Result<()> {
2129        let re = RegexSet::new(&[ "ab", "cd" ])?;
2130        assert!(re.is_match("ab"));
2131        assert!(re.is_match("cdef"));
2132        assert!(!re.is_match("xxx"));
2133
2134        // Try to make sure one re does not leak into another.
2135        let re = RegexSet::new(&[ "cd$", "^ab" ])?;
2136        assert!(re.is_match("abxx"));
2137        assert!(! re.is_match("xabxx"));
2138        assert!(re.is_match("xxcd"));
2139        assert!(! re.is_match("xxcdx"));
2140        assert!(re.is_match("abcdx"));
2141
2142        // Invalid regular expressions should be ignored.
2143        let re = RegexSet::new(&[ "[ab", "cd]", "x" ])?;
2144        assert!(!re.is_match("a"));
2145        assert!(!re.is_match("ab"));
2146        assert!(!re.is_match("[ab"));
2147        assert!(!re.is_match("c"));
2148        assert!(!re.is_match("cd"));
2149        assert!(!re.is_match("cd]"));
2150        assert!(re.is_match("x"));
2151
2152        // If all regular expressions are invalid, nothing should
2153        // match.
2154        let re = RegexSet::new(&[ "[ab", "cd]" ])?;
2155        assert!(!re.is_match("a"));
2156        assert!(!re.is_match("ab"));
2157        assert!(!re.is_match("[ab"));
2158        assert!(!re.is_match("c"));
2159        assert!(!re.is_match("cd"));
2160        assert!(!re.is_match("cd]"));
2161        assert!(!re.is_match("x"));
2162
2163        // If there are no regular expressions, everything should
2164        // match.
2165        let s: [&str; 0] = [];
2166        let re = RegexSet::new(&s)?;
2167        assert!(re.is_match("a"));
2168        assert!(re.is_match("ab"));
2169        assert!(re.is_match("[ab"));
2170        assert!(re.is_match("c"));
2171        assert!(re.is_match("cd"));
2172        assert!(re.is_match("cd]"));
2173        assert!(re.is_match("x"));
2174
2175        // The empty branch of the alternation should match everything.
2176        let re = RegexSet::new(&[ "ab|", "cd" ])?;
2177        assert!(re.is_match("a"));
2178        assert!(re.is_match("b"));
2179        assert!(re.is_match("x"));
2180        assert!(re.is_match("xyx"));
2181        assert!(re.is_match(""));
2182
2183        Ok(())
2184    }
2185
2186    #[test]
2187    fn regex_set_from_direct_key_signature() -> Result<()> {
2188        use crate::cert::CertBuilder;
2189
2190        let (alice, _) =
2191            CertBuilder::general_purpose(Some("Alice <alice@example.org>")).generate()?;
2192        let mut alices_signer = alice
2193            .primary_key()
2194            .key()
2195            .clone()
2196            .parts_into_secret()?
2197            .into_keypair()?;
2198
2199        let (ca, _) =
2200            CertBuilder::general_purpose(Some("OpenPGP CA <openpgp-ca@example.com>")).generate()?;
2201
2202        let trust_signature = SignatureBuilder::new(SignatureType::DirectKey)
2203            .set_trust_signature(1, 120)?
2204            .set_regular_expression("<[^>]+[@.]example\\.com>$")?
2205            .sign_direct_key(&mut alices_signer, ca.primary_key().key())?;
2206
2207        let re = RegexSet::from_signature(&trust_signature)?;
2208        assert!(re.is_match("Alice <alice@example.com>"));
2209        assert!(!re.is_match("Alice <alice@example.org>"));
2210
2211        Ok(())
2212    }
2213
2214    #[test]
2215    fn regex_set_sequoia() -> Result<()> {
2216        let re = RegexSet::new(&["<[^>]+[@.]sequoia-pgp\\.org>$"])?;
2217        dbg!(&re);
2218        assert!(re.is_match("<justus@sequoia-pgp.org>"));
2219        assert!(!re.is_match("<justus@gnupg.org>"));
2220        Ok(())
2221    }
2222
2223    #[test]
2224    fn regex_set_sequoia_nodash() -> Result<()> {
2225        let re = RegexSet::new(&["<[^>]+[@.]sequoiapgp\\.org>$"])?;
2226        dbg!(&re);
2227        assert!(re.is_match("<justus@sequoiapgp.org>"));
2228        assert!(!re.is_match("<justus@gnupg.org>"));
2229        Ok(())
2230    }
2231}