diff --git a/Cargo.toml b/Cargo.toml index f372dd0..98efde3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rematch" -version = "0.3.1" +version = "0.3.2" authors = ["Avril "] edition = "2024" @@ -27,3 +27,4 @@ unstable = ["regex/unstable"] pcre2 = { version = "0.2.9", optional = true } regex = { version = "1.11.1", features = ["use_std"] } color-eyre = { version = "0.6.3", default-features = false, features = ["track-caller"] } +owo-colors = { version = "3.5.0", features = ["alloc", "supports-colors"] } diff --git a/src/ext.rs b/src/ext.rs new file mode 100644 index 0000000..f3b584c --- /dev/null +++ b/src/ext.rs @@ -0,0 +1,127 @@ +//! Extensions +use super::*; +use std::{ + fmt, +}; + +/// Run an expression on an named value with a result type `Result`. +/// Where `T` and `U` have *the same API surface* for the duration of the provided expression. +/// +/// # Example +/// If there is a value `let mut value: Result`, where `T: Write` & `U: BufWrite`; +/// the expression `value.flush()` is valid for both `T` and `U`. +/// Therefore, it can be simplified to be called as so: `unwrap_either(mut value => value.flush())`. +/// +/// # Reference capture vs. `move` capture. +/// Note that by default, the identified value is **moved** *into* the expression. +/// The type of reference can be controlled by appending `ref`, `mut`, or `ref mut` to the ident. +/// +/// Identifier capture table: +/// - **none** ~default~ - Capture by move, value is immutable in expression. +/// - `mut` - Capture by move, value is mutable in expression. +/// - `ref` - Capture by ref, value is immutable (`&value`) in expression. +/// - `ref mut` - Capture by mutable ref, value is mutable (`&mut value`) in expression. (__NOTE__: `value` must be defined as mutable to take a mutable reference of it.) +/// +/// Essentially the same rules as any `match` branch pattern. +macro_rules! unwrap_either { + ($res:ident => $($rest:tt)+) => { + match $res { + Ok(ref mut $res) => $($rest)+, + Err(ref mut $res) => $($rest)+, + } + }; + (ref mut $res:ident => $($rest:tt)+) => { + match $res { + Ok(ref mut $res) => $($rest)+, + Err(ref mut $res) => $($rest)+, + } + }; + (ref $res:ident => $($rest:tt)+) => { + match $res { + Ok(ref $res) => $($rest)+, + Err(ref $res) => $($rest)+, + } + }; + (mut $res:ident => $($rest:tt)+) => { + match $res { + Ok(mut $res) => $($rest)+, + Err(mut $res) => $($rest)+, + } + }; +} + +pub(crate) use unwrap_either; + + +#[derive(Debug, PartialEq, Eq, Hash)] +#[repr(transparent)] +pub struct DisjointString<'a, T: ?Sized>([&'a T]); + +macro_rules! disjoint { + [$($ex:expr),+] => { + $crate::ext::DisjointString::from_array(& [$($ex),+]) + }; +} + +impl<'a, T: ?Sized> DisjointString<'a, T> + where T: fmt::Display +{ + #[inline] + pub const fn from_array<'o: 'a, const N: usize>(strings: &'o [&'a T; N]) -> &'o Self + { + Self::new(strings.as_slice()) + } + #[inline] + pub const fn new<'o: 'a>(strings: &'o [&'a T]) -> &'o Self + { + // SAFETY: Transparent newtype wrapper over `[&'a T]` + unsafe { + std::mem::transmute(strings) + } + } + +} +impl<'a, T: ?Sized> DisjointString<'a, T> +{ + #[inline] + pub const fn len(&self) -> usize + { + self.0.len() + } + + #[inline] + pub fn iter(&self) -> impl Iterator + ExactSizeIterator + std::iter::FusedIterator + std::iter::DoubleEndedIterator + { + self.0.iter().map(|&x| x) + } + + #[inline] + pub fn into_iter<'o: 'a>(&'o self) -> impl Iterator + ExactSizeIterator + std::iter::FusedIterator + std::iter::DoubleEndedIterator + 'o + { + self.0.into_iter().map(|&x|x) + } +} + +impl<'a, T: ?Sized> AsRef<[&'a T]> for DisjointString<'a, T> +{ + #[inline] + fn as_ref(&self) -> &[&'a T] + { + &self.0 + } +} + +impl<'a, T: ?Sized> fmt::Display for DisjointString<'a, T> + where T: fmt::Display +{ + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + for &s in &self.0 { + s.fmt(f)?; + } + Ok(()) + } +} + +pub(crate) use disjoint; diff --git a/src/main.rs b/src/main.rs index cfa050b..51e70f8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,52 +2,7 @@ mod re; mod text; - -/// Run an expression on an named value with a result type `Result`. -/// Where `T` and `U` have *the same API surface* for the duration of the provided expression. -/// -/// # Example -/// If there is a value `let mut value: Result`, where `T: Write` & `U: BufWrite`; -/// the expression `value.flush()` is valid for both `T` and `U`. -/// Therefore, it can be simplified to be called as so: `unwrap_either(mut value => value.flush())`. -/// -/// # Reference capture vs. `move` capture. -/// Note that by default, the identified value is **moved** *into* the expression. -/// The type of reference can be controlled by appending `ref`, `mut`, or `ref mut` to the ident. -/// -/// Identifier capture table: -/// - **none** ~default~ - Capture by move, value is immutable in expression. -/// - `mut` - Capture by move, value is mutable in expression. -/// - `ref` - Capture by ref, value is immutable (`&value`) in expression. -/// - `ref mut` - Capture by mutable ref, value is mutable (`&mut value`) in expression. (__NOTE__: `value` must be defined as mutable to take a mutable reference of it.) -/// -/// Essentially the same rules as any `match` branch pattern. -macro_rules! unwrap_either { - ($res:ident => $($rest:tt)+) => { - match $res { - Ok(ref mut $res) => $($rest)+, - Err(ref mut $res) => $($rest)+, - } - }; - (ref mut $res:ident => $($rest:tt)+) => { - match $res { - Ok(ref mut $res) => $($rest)+, - Err(ref mut $res) => $($rest)+, - } - }; - (ref $res:ident => $($rest:tt)+) => { - match $res { - Ok(ref $res) => $($rest)+, - Err(ref $res) => $($rest)+, - } - }; - (mut $res:ident => $($rest:tt)+) => { - match $res { - Ok(mut $res) => $($rest)+, - Err(mut $res) => $($rest)+, - } - }; -} +mod ext; use ext::*; use color_eyre::{ eyre::{ @@ -116,6 +71,18 @@ fn main() -> eyre::Result<()> let args: re::FrozenVec = std::env::args().map(String::into_boxed_str).collect(); if args.len() < 4 { + use owo_colors::OwoColorize; + use owo_colors::Stream; + + macro_rules! colour { + (in $name:ident: $fmt:expr => $col:ident) => { + $fmt.if_supports_color(Stream::$name, |text| text.$col()) + }; + ($fmt:expr => $col:ident) => { + colour!(in Stdout: $fmt => $col) + } + } + println!("rematch v{}: Regular-expression group matcher", env!("CARGO_PKG_VERSION")); println!(""); println!("Usage: {} ...", args[0]); @@ -123,14 +90,14 @@ fn main() -> eyre::Result<()> println!(""); println!("Enabled Features:"); if cfg!(feature="perl") { - println!("+perl\t\t\tEnable PCRE2 (extended) regular-expressions.\n\t\t\tNote that PCRE2 regex engine matches on *bytes*, not *characters*; meaning if a match cuts a vlid UTF8 codepoint into an invalid one, the output will replace the invalid characters with U+FFFD REPLACEMENT CHARACTER."); + println!("{}\t\t\tEnable PCRE2 (extended) regular-expressions.\n\t\t\tNote that PCRE2 regex engine matches on *bytes*, not *characters*; meaning if a match cuts a vlid UTF8 codepoint into an invalid one, the output will replace the invalid characters with U+FFFD REPLACEMENT CHARACTER.", colour!(disjoint!["+", "perl"] => bright_red)); } else { - println!("-perl\t\tPCRE2 (extended) features are disabled; a faster but less featureful regular expression engine (that matches on UTF8 strings instead of raw bytes) is used instead."); + println!("{}\t\t\tPCRE2 (extended) features are disabled; a faster but less featureful regular expression engine (that matches on UTF8 strings instead of raw bytes) is used instead.", colour!(disjoint!["-", "perl"] => blue)); } if cfg!(feature="unstable") { - println!("+unstable\t\tUnstable optimisations evailable & enabled for build."); + println!("{}\t\tUnstable optimisations evailable & enabled for build.", colour!(disjoint!["+", "unstable"] => red)); } else { - println!("-unstable\t\tUnstable optimisations disabled / not available for build."); + println!("{}\t\tUnstable optimisations disabled / not available for build.", colour!(disjoint!["-", "unstable"] => bright_blue)); } std::process::exit(1) } else {