//! Types which augment or modify the behavior of an underlying parser. use crate::error::ParseError; #[cfg(feature = "either")] use crate::label::Label; use crate::parse::Parser; use crate::storage::StorageLock; use crate::tag::KeyValueSep; use crate::tag::PathSep; #[cfg(feature = "either")] use crate::tag::Tag; #[cfg(feature = "convert_case")] pub use convert_case::Case; #[cfg(feature = "convert_case")] use convert_case::Casing as _; #[cfg(feature = "either")] use either::Either; #[cfg(feature = "regex")] use regex::Regex; #[cfg(feature = "regex")] use regex::Replacer; use std::hash::BuildHasher; use string_interner::backend::Backend as InternerBackend; #[cfg(feature = "either")] use string_interner::Symbol; // Helper macro to generate parser adapters. macro_rules! adapters { ( $( $( #[$($attrss:meta)*] )* $struct:ident $(< $($type_var:ident: $type_bound:ident),* >)? $(($($field_ty:ty),*))? => { $adapter:expr } )* ) => { $( adapters! { @single $( #[$($attrss)*] )* $struct $(< $($type_var: $type_bound),* >)* $(($($field_ty),*))* => { $adapter } } )* }; ( @single $(#[$($attrss:meta)*] )* $struct:ident $(< $($type_var:ident: $type_bound:ident),* >)? $(($($field_ty:ty),* ))? => { $adapter:expr } ) => { $( #[$($attrss)*] )* #[derive(Debug, Clone)] pub struct $struct<$($($type_var: $type_bound),*,)* P: Parser>($($(pub $field_ty),*,)* pub P); impl<$($($type_var: $type_bound),*,)* P: Parser> $struct<$($($type_var),*,)* P> { /// Parse a token with the given `interner` and `separator`. #[allow(clippy::redundant_closure_call)] fn parse( &self, storage: &mut StorageLock<'_, ::Label, B, H>, key_value_separator: KeyValueSep, path_separator: PathSep, raw: &str, ) -> Result where B: InternerBackend::Symbol>, H: BuildHasher { ($adapter)(self, storage, key_value_separator, path_separator, raw) } } impl<$($($type_var: $type_bound),*,)* P: Parser> Parser for $struct<$($($type_var),*,)* P> { type Tag = P::Tag; fn parse( &self, storage: &mut StorageLock<'_, ::Label, B, H>, key_value_separator: KeyValueSep, path_separator: PathSep, raw: &str, ) -> Result where B: InternerBackend::Symbol>, H: BuildHasher { self.parse(storage, key_value_separator, path_separator, raw) } } }; } adapters! { /// Trim whitespace from one or both sides of the tag. Trim(TrimBounds) => { |this: &Trim

, interner, kv_sep, path_sep, raw: &str| { let Trim::

(bounds, sub_parser) = this; let raw = match bounds { TrimBounds::Both => raw.trim(), TrimBounds::Start => raw.trim_start(), TrimBounds::End => raw.trim_end(), }; sub_parser.parse(interner, kv_sep, path_sep, raw) } } /// Filter out tags longer than a maximum number of characters. MaxChar(usize) => { |this: &MaxChar

, interner, kv_sep, path_sep, raw: &str| { let MaxChar::

(limit, sub_parser) = this; if raw.chars().count() > *limit { return Err(ParseError::TagTooManyChars); } sub_parser.parse(interner, kv_sep, path_sep, raw) } } /// Filter out tags longer than a maximum number of bytes. MaxBytes(usize) => { |this: &MaxBytes

, interner, kv_sep, path_sep, raw: &str| { let MaxBytes::

(limit, sub_parser) = this; if raw.len() > *limit { return Err(ParseError::TagTooManyBytes); } sub_parser.parse(interner, kv_sep, path_sep, raw) } } } #[cfg(feature = "convert_case")] adapters! { /// Change the case of the tag. ChangeCase(Case) => { |this: &ChangeCase

, interner, kv_sep, path_sep, raw: &str| { let ChangeCase::

(case, sub_parser) = this; let raw = raw.to_case(*case); sub_parser.parse(interner, kv_sep, path_sep, &raw) } } } #[cfg(feature = "regex")] adapters! { /// Filter tags by matching against a regex. Match(Regex) => { |this: &Match

, interner, kv_sep, path_sep, raw: &str| { let Match::

(regex, sub_parser) = this; let raw = regex .is_match(raw) .then_some(raw) .ok_or(ParseError::TagDidntMatchRegex)?; sub_parser.parse(interner, kv_sep, path_sep, raw) } } /// Replace the content of a tag according to a regex. Replace(Regex, R, ReplaceCount) => { |this: &Replace, interner, kv_sep, path_sep, raw: &str| { let Replace::(regex, replacer, count, sub_parser) = this; let raw = match count { ReplaceCount::First => regex.replace(raw, replacer.clone()), ReplaceCount::N(count) => regex.replacen(raw, *count, replacer.clone()), ReplaceCount::All => regex.replace_all(raw, replacer.clone()), }; sub_parser.parse(interner, kv_sep, path_sep, &raw) } } } /// A `regex::Replacer` that can be [`Clone`]d. /// /// This is automatically implemented for any type that implements both /// `regex::Replacer` and [`Clone`]. pub trait CloneableReplacer: Replacer + Clone {} impl CloneableReplacer for T {} // The `Or` adapter is implemented by hand, because making the adapter-generating // macro learn how to handle all of these bounds and everything isn't worth it. /// Apply one parser, and if it fails, apply the other one. /// /// Note that the tokens produced by the two parsers have to support the same underlying /// symbol type, as they're both being backed by the same interner for storage. #[cfg(feature = "either")] #[derive(Debug)] pub struct Or(pub P1, pub P2) where L: Label, S: Symbol, T1: Tag