diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/rust/bindgen/regex_set.rs | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/bindgen/regex_set.rs')
-rw-r--r-- | third_party/rust/bindgen/regex_set.rs | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/third_party/rust/bindgen/regex_set.rs b/third_party/rust/bindgen/regex_set.rs new file mode 100644 index 0000000000..b78424aae1 --- /dev/null +++ b/third_party/rust/bindgen/regex_set.rs @@ -0,0 +1,204 @@ +//! A type that represents the union of a set of regular expressions. +#![deny(clippy::missing_docs_in_private_items)] + +use regex::RegexSet as RxSet; +use std::cell::Cell; + +/// A dynamic set of regular expressions. +#[derive(Clone, Debug, Default)] +pub struct RegexSet { + items: Vec<Box<str>>, + /// Whether any of the items in the set was ever matched. The length of this + /// vector is exactly the length of `items`. + matched: Vec<Cell<bool>>, + set: Option<RxSet>, + /// Whether we should record matching items in the `matched` vector or not. + record_matches: bool, +} + +impl RegexSet { + /// Create a new RegexSet + pub fn new() -> RegexSet { + RegexSet::default() + } + + /// Is this set empty? + pub fn is_empty(&self) -> bool { + self.items.is_empty() + } + + /// Insert a new regex into this set. + pub fn insert<S>(&mut self, string: S) + where + S: AsRef<str>, + { + self.items.push(string.as_ref().to_owned().into_boxed_str()); + self.matched.push(Cell::new(false)); + self.set = None; + } + + /// Returns slice of String from its field 'items' + pub fn get_items(&self) -> &[Box<str>] { + &self.items + } + + /// Returns an iterator over regexes in the set which didn't match any + /// strings yet. + pub fn unmatched_items(&self) -> impl Iterator<Item = &str> { + self.items.iter().enumerate().filter_map(move |(i, item)| { + if !self.record_matches || self.matched[i].get() { + return None; + } + + Some(item.as_ref()) + }) + } + + /// Construct a RegexSet from the set of entries we've accumulated. + /// + /// Must be called before calling `matches()`, or it will always return + /// false. + #[inline] + pub fn build(&mut self, record_matches: bool) { + self.build_inner(record_matches, None) + } + + #[cfg(all(feature = "__cli", feature = "experimental"))] + /// Construct a RegexSet from the set of entries we've accumulated and emit diagnostics if the + /// name of the regex set is passed to it. + /// + /// Must be called before calling `matches()`, or it will always return + /// false. + #[inline] + pub fn build_with_diagnostics( + &mut self, + record_matches: bool, + name: Option<&'static str>, + ) { + self.build_inner(record_matches, name) + } + + #[cfg(all(not(feature = "__cli"), feature = "experimental"))] + /// Construct a RegexSet from the set of entries we've accumulated and emit diagnostics if the + /// name of the regex set is passed to it. + /// + /// Must be called before calling `matches()`, or it will always return + /// false. + #[inline] + pub(crate) fn build_with_diagnostics( + &mut self, + record_matches: bool, + name: Option<&'static str>, + ) { + self.build_inner(record_matches, name) + } + + fn build_inner( + &mut self, + record_matches: bool, + _name: Option<&'static str>, + ) { + let items = self.items.iter().map(|item| format!("^({})$", item)); + self.record_matches = record_matches; + self.set = match RxSet::new(items) { + Ok(x) => Some(x), + Err(e) => { + warn!("Invalid regex in {:?}: {:?}", self.items, e); + #[cfg(feature = "experimental")] + if let Some(name) = _name { + invalid_regex_warning(self, e, name); + } + None + } + } + } + + /// Does the given `string` match any of the regexes in this set? + pub fn matches<S>(&self, string: S) -> bool + where + S: AsRef<str>, + { + let s = string.as_ref(); + let set = match self.set { + Some(ref set) => set, + None => return false, + }; + + if !self.record_matches { + return set.is_match(s); + } + + let matches = set.matches(s); + if !matches.matched_any() { + return false; + } + for i in matches.iter() { + self.matched[i].set(true); + } + + true + } +} + +#[cfg(feature = "experimental")] +fn invalid_regex_warning( + set: &RegexSet, + err: regex::Error, + name: &'static str, +) { + use crate::diagnostics::{Diagnostic, Level, Slice}; + + let mut diagnostic = Diagnostic::default(); + + match err { + regex::Error::Syntax(string) => { + if string.starts_with("regex parse error:\n") { + let mut source = String::new(); + + let mut parsing_source = true; + + for line in string.lines().skip(1) { + if parsing_source { + if line.starts_with(' ') { + source.push_str(line); + source.push('\n'); + continue; + } + parsing_source = false; + } + let error = "error: "; + if line.starts_with(error) { + let (_, msg) = line.split_at(error.len()); + diagnostic.add_annotation(msg.to_owned(), Level::Error); + } else { + diagnostic.add_annotation(line.to_owned(), Level::Info); + } + } + let mut slice = Slice::default(); + slice.with_source(source); + diagnostic.add_slice(slice); + + diagnostic.with_title( + "Error while parsing a regular expression.", + Level::Warn, + ); + } else { + diagnostic.with_title(string, Level::Warn); + } + } + err => { + let err = err.to_string(); + diagnostic.with_title(err, Level::Warn); + } + } + + diagnostic.add_annotation( + format!("This regular expression was passed via `{}`.", name), + Level::Note, + ); + + if set.items.iter().any(|item| item.as_ref() == "*") { + diagnostic.add_annotation("Wildcard patterns \"*\" are no longer considered valid. Use \".*\" instead.", Level::Help); + } + diagnostic.display(); +} |