diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 73a2a0f..36e491c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,12 +33,18 @@ jobs: - name: cargo test (no default features) run: cargo test --no-default-features --locked + - name: cargo test ("assertions") + run: cargo test --no-default-features --features assertions --locked + - name: cargo test ("lookup-ranges") run: cargo test --no-default-features --features lookup-ranges --locked - name: cargo test ("test-regex") run: cargo test --no-default-features --features test-regex --locked + - name: cargo test ("full") + run: cargo test --no-default-features --features full --locked + - name: cargo clippy run: cargo clippy --all-targets --locked -- -D warnings diff --git a/CHANGES.md b/CHANGES.md index 6ad8bf8..8c3d927 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,7 +1,17 @@ # shwild.Rust - CHANGES -## 0.1.6 - 10th July 2026 +## 0.2.0 - 10th July 2026 + +* added `assert_shwild_matches!()` and `assert_shwild_not_matches!()` test assertion macros, available with the `"assertions"` feature (enabled by default); +* added `"flexible-flags-type"` feature — optional [**base-traits**](https://github.com/synesissoftware/base-traits) dependency (`implement-AsI64-for-built_ins`) allowing macro `flags` parameters to be any type implementing `AsI64`; when disabled, `flags` must be `i64`; +* `"assertions"` no longer implies **base-traits**; use `"full"` to enable assertions, flexible flags, and lookup ranges together; +* extended `shwild_matches!()` 3-parameter form with the same flexible-`flags` behaviour; +* crate-level and macro `///` documentation for the assertion macros; +* **README.md** macros and dependencies sections updated; + + +## 0.1.6 - 9th July 2026 * preparatory changes; * consistency fixes; diff --git a/Cargo.lock b/Cargo.lock index 39aeeae..6690ee4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -354,8 +354,9 @@ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "shwild" -version = "0.1.6" +version = "0.2.0" dependencies = [ + "base-traits", "collect-rs", "criterion", "regex", diff --git a/Cargo.toml b/Cargo.toml index 0b3cf90..8f97573 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ name = "shwild" readme = "README.md" repository = "https://github.com/synesissoftware/shwild.Rust" rust-version = "1.79" -version = "0.1.6" +version = "0.2.0" # ########################################################## @@ -87,6 +87,13 @@ path = "examples/list-matching-files-compiled/main.rs" [features] default = [ + "assertions", + "lookup-ranges", +] + +full = [ + "assertions", + "flexible-flags-type", "lookup-ranges", ] @@ -101,9 +108,18 @@ null-feature = [] # Crate-specific features: # +# - "assertions" - enable assertions; +# - "flexible-flags-type" - allows flags parameters to be any type for which `base_traits::I64` is implemented; # - "lookup-ranges" - enable lookup ranges; # - "test-regex" - enable test regex; +assertions = [ +] + +flexible-flags-type = [ + "dep:base-traits", +] + lookup-ranges = [ "dep:collect-rs", ] @@ -118,6 +134,9 @@ test-regex = [ [dependencies] +base-traits = { version = "0", optional = true, default-features = false, features = [ + "implement-AsI64-for-built_ins", +]} collect-rs = { version = "0.2", optional = true, default-features = false, features = [ ]} regex = { version = "1.11", optional = true, default-features = false, features = [ diff --git a/NEWS.md b/NEWS.md index eacb5ec..07e1a19 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,7 @@ | Date | News Item | | --------------------- | ----------------------------------------- | +| 10th July 2026 | shwild.Rust 0.2.0 released | | 9th July 2026 | shwild.Rust 0.1.6 released | | 9th July 2026 | shwild.Rust 0.1.5 released | | 7th July 2026 | shwild.Rust 0.1.4 released | diff --git a/README.md b/README.md index cdd359e..49bc488 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ The library (and other **shwild** variants) support the following pattern elemen Reference in **Cargo.toml** in the usual way: ```toml -shwild = { version = "0.1" } +shwild = { version = "0.2" } ``` @@ -108,6 +108,7 @@ The following crate features are defined: | Name | Effect | Is `"default"`? | Dependent feature(s) | | --------------------------- | ------------------------------------- | --------------- | ------------------------------------- | +| `"assertions"` | Provides `assert_shwild_matches!()` and `assert_shwild_not_matches!()` test assertion macros (via **base-traits** `AsI64`) | Yes | | | `"lookup-ranges"` | Causes match/non-match ranges to be implemented in terms of `UnicodePointMap` (from **collect-rs** crate), resulting in significant performance improvements in parsing and matching | Yes | | | `"null-feature"` | A feature that has no effect (and, thus, is useful for simplifying driver scripts) | **No** | | | `"test-regex"` | Introduces a dependency to **regex** crate to support benchmark/example program(s) | **No** | | @@ -133,6 +134,20 @@ pub mod shwild { The `shwild::shwild_matches!()` macro is a shorthand for the `shwild::matches()` function, providing 2-parameter and 3-parameter forms. The 2-parameter form passes 0 for the `flags` parameter. +The `shwild::assert_shwild_matches!()` and `shwild::assert_shwild_not_matches!()` macros are test-oriented counterparts that panic on failure. Each provides 2-parameter and 3-parameter forms; the 2-parameter form passes 0 for the `flags` parameter. A parse error in the pattern panics with a descriptive message rather than returning `Err`. They are provided only when the feature `"assertions"` is enabled, which it is by default. + +```Rust + use shwild::{ + assert_shwild_matches, + assert_shwild_not_matches, + IGNORE_CASE, + }; + + assert_shwild_matches!("[a-d]", "b"); + assert_shwild_not_matches!("[a-d]", "e"); + assert_shwild_matches!("[a-d]", "B", IGNORE_CASE); +``` + ### Structures @@ -190,9 +205,10 @@ Defect reports, feature requests, and pull requests are welcome on https://githu ### Dependencies -**shwild.Rust** has two dependencies, both optional: +**shwild.Rust** has three optional dependencies: -* [**collect-rs**](https://github.com/synesissoftware/collect-rs) - required, for more efficient range matching, if feature `"lookup-ranges"` is specified; +* [**base-traits**](https://github.com/synesissoftware/base-traits) - required if feature `"assertions"` is specified; supports the `flags` parameter type in `assert_shwild_matches!()` and `assert_shwild_not_matches!()` via `AsI64`; +* [**collect-rs**](https://github.com/synesissoftware/collect-rs) - required if feature `"lookup-ranges"` is specified, for more efficient range matching; * [**regex**](https://github.com/rust-lang/regex) - required, by some benchmark/example programs only, if feature `"test-regex"` is specified; @@ -208,6 +224,7 @@ Crates upon which **shwild** has development dependencies: * [**shwild**](https://github.com/synesissoftware/shwild/); * [**shwild.Go**](https://github.com/synesissoftware/shwild.Go/); +* [**base-traits**](https://github.com/synesissoftware/base-traits/); * [**collect-rs**](https://github.com/synesissoftware/collect-rs/); diff --git a/TODO.md b/TODO.md index 90c33f3..ab1788e 100644 --- a/TODO.md +++ b/TODO.md @@ -68,7 +68,8 @@ Proposed module layout: ``` src/ - lib.rs # crate docs, re-exports, matches(), shwild_matches! macro; + lib.rs # crate docs, re-exports, matches(), shwild_matches!, + # assert_shwild_matches!/assert_shwild_not_matches!; # #[cfg(test)] API unit tests (stay here) constants.rs # IGNORE_CASE error.rs # Error + Display/Error trait impls diff --git a/benches/macros.rs b/benches/macros.rs index d189e39..49c910b 100644 --- a/benches/macros.rs +++ b/benches/macros.rs @@ -12,6 +12,12 @@ use std::hint::black_box; use shwild::shwild_matches; +#[cfg(feature = "assertions")] +use shwild::{ + assert_shwild_matches, + assert_shwild_not_matches, +}; + mod constants { #![allow(non_upper_case_globals)] @@ -307,6 +313,374 @@ mod macros_benches { } + +#[cfg(feature = "assertions")] +mod assertion_benches { + #![allow(non_snake_case)] + + use super::*; + + + pub fn assert_shwild_matches_input_empty(c : &mut Criterion) { + let pattern = constants::EMPTY_STRING; + let input = ""; + + c.bench_function("`assert_shwild_matches!()` - empty string", |b| { + b.iter(|| { + assert_shwild_matches!(black_box(pattern), black_box(input)); + }) + }); + } + + pub fn assert_shwild_matches_input_literal_small(c : &mut Criterion) { + let pattern = constants::EMPTY_STRING; + let input = ""; + + c.bench_function("`assert_shwild_matches!()` - literal (small)", |b| { + b.iter(|| { + assert_shwild_matches!(black_box(pattern), black_box(input)); + }) + }); + } + + pub fn assert_shwild_matches_input_literal_medium(c : &mut Criterion) { + let pattern = constants::EMPTY_STRING; + let input = ""; + + c.bench_function("`assert_shwild_matches!()` - literal (medium)", |b| { + b.iter(|| { + assert_shwild_matches!(black_box(pattern), black_box(input)); + }) + }); + } + + pub fn assert_shwild_matches_input_literal_large(c : &mut Criterion) { + let pattern = constants::EMPTY_STRING; + let input = ""; + + c.bench_function("`assert_shwild_matches!()` - literal (large)", |b| { + b.iter(|| { + assert_shwild_matches!(black_box(pattern), black_box(input)); + }) + }); + } + + pub fn assert_shwild_matches_against_nrange_continuum_simple(c : &mut Criterion) { + let pattern = constants::patterns::NRANGE_CONTINUUM_SIMPLE; + + let inputs = [ + // insert list: + " ", + "_", + ]; + + c.bench_function("`assert_shwild_matches!()` - nrange continuum (simple)", |b| { + b.iter(|| { + for input in &inputs { + assert_shwild_matches!(black_box(pattern), black_box(input)); + } + }) + }); + } + + pub fn assert_shwild_matches_against_nrange_continuum_reverse(c : &mut Criterion) { + let pattern = constants::patterns::NRANGE_CONTINUUM_REVERSE; + + let inputs = [ + // insert list: + " ", + "_", + ]; + + c.bench_function("`assert_shwild_matches!()` - nrange continuum (reverse)", |b| { + b.iter(|| { + for input in &inputs { + assert_shwild_matches!(black_box(pattern), black_box(input)); + } + }) + }); + } + + pub fn assert_shwild_matches_against_nrange_continuum_crosscase(c : &mut Criterion) { + let pattern = constants::patterns::NRANGE_CONTINUUM_CROSSCASE; + + let inputs = [ + // insert list: + " ", + "_", + ]; + + c.bench_function("`assert_shwild_matches!()` - nrange continuum (crosscase)", |b| { + b.iter(|| { + for input in &inputs { + assert_shwild_matches!(black_box(pattern), black_box(input)); + } + }) + }); + } + + pub fn assert_shwild_matches_against_range_continuum_simple(c : &mut Criterion) { + let pattern = constants::patterns::RANGE_CONTINUUM_SIMPLE; + + let inputs = [ + // insert list: + "a", + "b", + "c", + "d", + ]; + + c.bench_function("`assert_shwild_matches!()` - range continuum (simple)", |b| { + b.iter(|| { + for input in &inputs { + assert_shwild_matches!(black_box(pattern), black_box(input)); + } + }) + }); + } + + pub fn assert_shwild_matches_against_range_continuum_reverse(c : &mut Criterion) { + let pattern = constants::patterns::RANGE_CONTINUUM_REVERSE; + + let inputs = [ + // insert list: + "a", + "b", + "c", + "d", + ]; + + c.bench_function("`assert_shwild_matches!()` - range continuum (reverse)", |b| { + b.iter(|| { + for input in &inputs { + assert_shwild_matches!(black_box(pattern), black_box(input)); + } + }) + }); + } + + pub fn assert_shwild_matches_against_range_continuum_crosscase(c : &mut Criterion) { + let pattern = constants::patterns::RANGE_CONTINUUM_CROSSCASE; + + let inputs = [ + // insert list: + "a", + "b", + "c", + "d", + "A", + "B", + "C", + "D", + ]; + + c.bench_function("`assert_shwild_matches!()` - range continuum (crosscase)", |b| { + b.iter(|| { + for input in &inputs { + assert_shwild_matches!(black_box(pattern), black_box(input)); + } + }) + }); + } + + pub fn assert_shwild_matches_test_against_pattern_WindowsPath(c : &mut Criterion) { + let pattern = constants::patterns::WINDOWS_PATH; + let inputs = constants::windows_path_inputs::MATCHING; + + c.bench_function("`assert_shwild_matches!()` - Windows Path", |b| { + b.iter(|| { + for input in &inputs { + assert_shwild_matches!(black_box(pattern), black_box(input)); + } + }) + }); + } + + pub fn assert_shwild_not_matches_input_empty(c : &mut Criterion) { + let pattern = constants::EMPTY_STRING; + let input = " "; + + c.bench_function("`assert_shwild_not_matches!()` - empty string", |b| { + b.iter(|| { + assert_shwild_not_matches!(black_box(pattern), black_box(input)); + }) + }); + } + + pub fn assert_shwild_not_matches_input_literal_small(c : &mut Criterion) { + let pattern = constants::EMPTY_STRING; + let input = " "; + + c.bench_function("`assert_shwild_not_matches!()` - literal (small)", |b| { + b.iter(|| { + assert_shwild_not_matches!(black_box(pattern), black_box(input)); + }) + }); + } + + pub fn assert_shwild_not_matches_input_literal_medium(c : &mut Criterion) { + let pattern = constants::EMPTY_STRING; + let input = " "; + + c.bench_function("`assert_shwild_not_matches!()` - literal (medium)", |b| { + b.iter(|| { + assert_shwild_not_matches!(black_box(pattern), black_box(input)); + }) + }); + } + + pub fn assert_shwild_not_matches_input_literal_large(c : &mut Criterion) { + let pattern = constants::EMPTY_STRING; + let input = " "; + + c.bench_function("`assert_shwild_not_matches!()` - literal (large)", |b| { + b.iter(|| { + assert_shwild_not_matches!(black_box(pattern), black_box(input)); + }) + }); + } + + pub fn assert_shwild_not_matches_against_nrange_continuum_simple(c : &mut Criterion) { + let pattern = constants::patterns::NRANGE_CONTINUUM_SIMPLE; + + let inputs = [ + // insert list: + "a", + "b", + "c", + "d", + "", + "aa", + ]; + + c.bench_function("`assert_shwild_not_matches!()` - nrange continuum (simple)", |b| { + b.iter(|| { + for input in &inputs { + assert_shwild_not_matches!(black_box(pattern), black_box(input)); + } + }) + }); + } + + pub fn assert_shwild_not_matches_against_nrange_continuum_reverse(c : &mut Criterion) { + let pattern = constants::patterns::NRANGE_CONTINUUM_REVERSE; + + let inputs = [ + // insert list: + "a", + "b", + "c", + "d", + "", + "aa", + ]; + + c.bench_function("`assert_shwild_not_matches!()` - nrange continuum (reverse)", |b| { + b.iter(|| { + for input in &inputs { + assert_shwild_not_matches!(black_box(pattern), black_box(input)); + } + }) + }); + } + + pub fn assert_shwild_not_matches_against_nrange_continuum_crosscase(c : &mut Criterion) { + let pattern = constants::patterns::NRANGE_CONTINUUM_CROSSCASE; + + let inputs = [ + // insert list: + "a", + "b", + "c", + "d", + "", + "aa", + ]; + + c.bench_function("`assert_shwild_not_matches!()` - nrange continuum (crosscase)", |b| { + b.iter(|| { + for input in &inputs { + assert_shwild_not_matches!(black_box(pattern), black_box(input)); + } + }) + }); + } + + pub fn assert_shwild_not_matches_against_range_continuum_simple(c : &mut Criterion) { + let pattern = constants::patterns::RANGE_CONTINUUM_SIMPLE; + + let inputs = [ + // insert list: + "", + " ", + "aa", + "_", + ]; + + c.bench_function("`assert_shwild_not_matches!()` - range continuum (simple)", |b| { + b.iter(|| { + for input in &inputs { + assert_shwild_not_matches!(black_box(pattern), black_box(input)); + } + }) + }); + } + + pub fn assert_shwild_not_matches_against_range_continuum_reverse(c : &mut Criterion) { + let pattern = constants::patterns::RANGE_CONTINUUM_REVERSE; + + let inputs = [ + // insert list: + "", + " ", + "aa", + "_", + ]; + + c.bench_function("`assert_shwild_not_matches!()` - range continuum (reverse)", |b| { + b.iter(|| { + for input in &inputs { + assert_shwild_not_matches!(black_box(pattern), black_box(input)); + } + }) + }); + } + + pub fn assert_shwild_not_matches_against_range_continuum_crosscase(c : &mut Criterion) { + let pattern = constants::patterns::RANGE_CONTINUUM_CROSSCASE; + + let inputs = [ + // insert list: + "", + " ", + "aa", + "_", + ]; + + c.bench_function("`assert_shwild_not_matches!()` - range continuum (crosscase)", |b| { + b.iter(|| { + for input in &inputs { + assert_shwild_not_matches!(black_box(pattern), black_box(input)); + } + }) + }); + } + + pub fn assert_shwild_not_matches_test_against_pattern_WindowsPath(c : &mut Criterion) { + let pattern = constants::patterns::WINDOWS_PATH; + let inputs = constants::windows_path_inputs::NOT_MATCHING; + + c.bench_function("`assert_shwild_not_matches!()` - Windows Path", |b| { + b.iter(|| { + for input in &inputs { + assert_shwild_not_matches!(black_box(pattern), black_box(input)); + } + }) + }); + } +} + + criterion_group!( shwild_matches_benches, macros_benches::shwild_matches_input_empty, @@ -322,4 +696,44 @@ criterion_group!( macros_benches::shwild_matches_against_range_continuum_crosscase, ); +#[cfg(feature = "assertions")] +criterion_group!( + assert_shwild_matches_benches, + assertion_benches::assert_shwild_matches_input_empty, + assertion_benches::assert_shwild_matches_input_literal_small, + assertion_benches::assert_shwild_matches_input_literal_medium, + assertion_benches::assert_shwild_matches_input_literal_large, + assertion_benches::assert_shwild_matches_test_against_pattern_WindowsPath, + assertion_benches::assert_shwild_matches_against_nrange_continuum_simple, + assertion_benches::assert_shwild_matches_against_nrange_continuum_reverse, + assertion_benches::assert_shwild_matches_against_nrange_continuum_crosscase, + assertion_benches::assert_shwild_matches_against_range_continuum_simple, + assertion_benches::assert_shwild_matches_against_range_continuum_reverse, + assertion_benches::assert_shwild_matches_against_range_continuum_crosscase, +); + +#[cfg(feature = "assertions")] +criterion_group!( + assert_shwild_not_matches_benches, + assertion_benches::assert_shwild_not_matches_input_empty, + assertion_benches::assert_shwild_not_matches_input_literal_small, + assertion_benches::assert_shwild_not_matches_input_literal_medium, + assertion_benches::assert_shwild_not_matches_input_literal_large, + assertion_benches::assert_shwild_not_matches_test_against_pattern_WindowsPath, + assertion_benches::assert_shwild_not_matches_against_nrange_continuum_simple, + assertion_benches::assert_shwild_not_matches_against_nrange_continuum_reverse, + assertion_benches::assert_shwild_not_matches_against_nrange_continuum_crosscase, + assertion_benches::assert_shwild_not_matches_against_range_continuum_simple, + assertion_benches::assert_shwild_not_matches_against_range_continuum_reverse, + assertion_benches::assert_shwild_not_matches_against_range_continuum_crosscase, +); + +#[cfg(feature = "assertions")] +criterion_main!( + shwild_matches_benches, + assert_shwild_matches_benches, + assert_shwild_not_matches_benches, +); + +#[cfg(not(feature = "assertions"))] criterion_main!(shwild_matches_benches); diff --git a/src/lib.rs b/src/lib.rs index 51167f4..d6f6628 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,19 +14,25 @@ //! Reference in **Cargo.toml** in the usual way: //! //! ```toml -//! shwild = { version = "0.1" } +//! shwild = { version = "0.2" } //! ``` //! //! # Components //! //! * [`matches()`] — parse `pattern` and test `input` in one step; //! * [`shwild_matches!`] — shorthand for [`matches()`] (2- or 3-arg); +//! * [`assert_shwild_matches!`] — test assertion that `actual` matches +//! (requires `"assertions"` feature); +//! * [`assert_shwild_not_matches!`] — test assertion that `actual` does +//! not match (requires `"assertions"` feature); //! * [`CompiledMatcher`] — parse once, match many times; //! * [`Error`] and [`Result`] — parse/match error reporting; //! * [`IGNORE_CASE`] — flag for case-insensitive matching; //! //! # Features //! +//! * `assertions` (default) — [`assert_shwild_matches!`] and +//! [`assert_shwild_not_matches!`] via **base-traits**; //! * `lookup-ranges` (default) — range matching via **collect-rs** //! `UnicodePointMap`; //! * `null-feature` — no effect; useful for driver scripts; @@ -41,6 +47,19 @@ //! assert_eq!(Ok(true), shwild_matches!("*.rs", "lib.rs")); //! ``` //! +//! In unit tests, prefer [`assert_shwild_matches!`] and +//! [`assert_shwild_not_matches!`] (requires `"assertions"` feature): +//! +//! ``` +//! # #[cfg(feature = "assertions")] +//! # { +//! use shwild::{assert_shwild_matches, assert_shwild_not_matches}; +//! +//! assert_shwild_matches!("[a-d]", "b"); +//! assert_shwild_not_matches!("[a-d]", "e"); +//! # } +//! ``` +//! //! Further examples are in the repository **examples** directory and in //! the project [README](https://github.com/synesissoftware/shwild.Rust). //! @@ -58,6 +77,7 @@ use std::{ error as std_error, fmt as std_fmt, + matches as std_matches, result as std_result, }; @@ -1525,7 +1545,9 @@ impl CompiledMatcher { let mut num_bytes = 0; for c in pattern.chars() { - debug_assert!(continuum_prior.is_none() || matches!(state, ParseState::InNotRange | ParseState::InRange)); + debug_assert!( + continuum_prior.is_none() || std_matches!(state, ParseState::InNotRange | ParseState::InRange) + ); if escaped { match c { @@ -1546,7 +1568,7 @@ impl CompiledMatcher { escaped = false; - if matches!(state, ParseState::None) { + if std_matches!(state, ParseState::None) { state = ParseState::InLiteral; } } else { @@ -1620,7 +1642,7 @@ impl CompiledMatcher { }, }; - minimum_required = if matches!(state, ParseState::InRange) { + minimum_required = if std_matches!(state, ParseState::InRange) { matchers.prepend_Range(character_range, flags, minimum_required) } else { matchers.prepend_NotRange(character_range, flags, minimum_required) @@ -1923,8 +1945,113 @@ macro_rules! shwild_matches { ($pattern:expr, $input:expr $(,)?) => { $crate::matches($pattern, $input, 0) }; - ($pattern:expr, $input:expr, $flags:expr $(,)?) => { - $crate::matches($pattern, $input, $flags) + ($pattern:expr, $input:expr, $flags:expr $(,)?) => {{ + #[cfg(feature = "flexible-flags-type")] + let flags = { + let flags : &dyn base_traits::AsI64 = &$flags; + + flags.as_i64() + }; + #[cfg(not(feature = "flexible-flags-type"))] + let flags : i64 = $flags; + + $crate::matches($pattern, $input, flags) + }}; +} + +/// Defines the macro `assert_shwild_matches!()`. +/// +/// Available when the `"assertions"` feature is enabled. +/// +/// # Parameters: +/// - `$expected_pattern` - the pattern against which `$actual` is +/// evaluated; +/// - `$actual` - the string to be evaluated; +/// - `$flags` - flags that moderate the evaluation; +/// +/// Panics if `$expected_pattern` is invalid or if `$actual` does not match. +/// The 2-parameter form passes 0 for the `flags` parameter. +#[cfg(feature = "assertions")] +#[macro_export] +macro_rules! assert_shwild_matches { + ($expected_pattern:expr, $actual:expr) => { + assert_shwild_matches!($expected_pattern, $actual, 0i64); + }; + ($expected_pattern:expr, $actual:expr, $flags:expr) => { + let expected_pattern : &str = &$expected_pattern; + let actual = &$actual; + #[cfg(feature = "flexible-flags-type")] + let flags = { + let flags : &dyn base_traits::AsI64 = &$flags; + + flags.as_i64() + }; + #[cfg(not(feature = "flexible-flags-type"))] + let flags : i64 = $flags; + + match $crate::matches(expected_pattern, actual, flags) { + Err(e) => { + panic!( + "could not evaluate actual value due to a failure in the parsing of expected pattern '{}': {}", + expected_pattern, e + ); + }, + Ok(b) => { + assert!( + b, + "assertion failed: actual value '{}' does not match the expected pattern '{}'", + actual, expected_pattern + ); + }, + } + }; +} + +/// Defines the macro `assert_shwild_not_matches!()`. +/// +/// Available when the `"assertions"` feature is enabled. +/// +/// # Parameters: +/// - `$expected_pattern` - the pattern against which `$actual` is +/// evaluated; +/// - `$actual` - the string to be evaluated; +/// - `$flags` - flags that moderate the evaluation; +/// +/// Panics if `$expected_pattern` is invalid or if `$actual` matches. The +/// 2-parameter form passes 0 for the `flags` parameter. +#[cfg(feature = "assertions")] +#[macro_export] +macro_rules! assert_shwild_not_matches { + ($expected_pattern:expr, $actual:expr) => { + assert_shwild_not_matches!($expected_pattern, $actual, 0i64); + }; + ($expected_pattern:expr, $actual:expr, $flags:expr) => { + let expected_pattern : &str = &$expected_pattern; + let actual = &$actual; + #[cfg(feature = "flexible-flags-type")] + let flags = { + let flags : &dyn base_traits::AsI64 = &$flags; + + flags.as_i64() + }; + #[cfg(not(feature = "flexible-flags-type"))] + let flags : i64 = $flags; + + match $crate::matches(expected_pattern, actual, flags) { + Err(e) => { + panic!( + "could not evaluate actual value due to a failure in the parsing of expected pattern '{}': {}", + expected_pattern, e + ); + }, + Ok(b) => { + assert!( + !b, + "assertion failed: actual value '{}' matches unexpectedly with the pattern '{}'", + actual, expected_pattern + ); + }, + } }; } @@ -1933,7 +2060,10 @@ macro_rules! shwild_matches { mod tests { #![allow(non_snake_case)] - use crate as shwild; + use crate::{ + self as shwild, + constants::*, + }; mod TEST_CompiledMatcher_PARSING { @@ -1941,8 +2071,6 @@ mod tests { use super::*; - use crate::constants::*; - #[test] fn TEST_CompiledMatcher_parse_EMPTY() { @@ -3351,6 +3479,176 @@ mod tests { } } } + + + #[cfg(feature = "assertions")] + mod TEST_ASSERTION_MATCHES { + #![allow(non_snake_case)] + + use super::*; + + + #[test] + fn TEST_assert_shwild_matches_1() { + assert_shwild_matches!("[a-d]", "a"); + assert_shwild_matches!("[a-d]", "b"); + assert_shwild_matches!("[a-d]", "c"); + + assert_shwild_matches!("?", "a"); + assert_shwild_matches!("?", "z"); + + assert_shwild_matches!("*", ""); + assert_shwild_matches!("*", "anything"); + assert_shwild_matches!("ab*", "ab"); + assert_shwild_matches!("ab*", "abcd"); + } + + #[test] + fn TEST_assert_shwild_matches_WITH__IGNORE_CASE__1() { + assert_shwild_matches!("[a-d]", "A", IGNORE_CASE); + assert_shwild_matches!("[a-d]", "B", IGNORE_CASE); + assert_shwild_matches!("[a-d]", "C", IGNORE_CASE); + assert_shwild_matches!("[a-d]", "D", IGNORE_CASE); + } + + #[test] + fn TEST_assert_shwild_matches_WITH_FLAGS_ZERO_1() { + assert_shwild_matches!("[a-d]", "a", 0i64); + assert_shwild_matches!("[a-d]", "d", 0); + } + + #[test] + #[should_panic( + expected = "could not evaluate actual value due to a failure in the parsing of expected pattern '[a-d': pattern syntax error (at 0:4): incomplete range" + )] + fn TEST_assert_shwild_matches_PARSE_ERROR_1() { + assert_shwild_matches!("[a-d", "a"); + } + + #[test] + #[should_panic(expected = "assertion failed: actual value 'e' does not match the expected pattern '[a-d]'")] + fn TEST_assert_shwild_matches_MISMATCH_1() { + assert_shwild_matches!("[a-d]", "e"); + } + + #[test] + #[should_panic(expected = "assertion failed: actual value 'A' does not match the expected pattern '[a-d]'")] + fn TEST_assert_shwild_matches_MISMATCH_WITHOUT__IGNORE_CASE__1() { + assert_shwild_matches!("[a-d]", "A"); + } + + #[test] + fn TEST_assert_shwild_not_matches_1() { + assert_shwild_not_matches!("[a-d]", "e"); + assert_shwild_not_matches!("[a-d]", "f"); + assert_shwild_not_matches!("[a-d]", "D"); + + assert_shwild_not_matches!("??", "a"); + assert_shwild_not_matches!("??", "z"); + + assert_shwild_not_matches!("ab", "a"); + assert_shwild_not_matches!("ab", "abc"); + } + + #[test] + fn TEST_assert_shwild_not_matches_WITH__IGNORE_CASE__1() { + assert_shwild_not_matches!("[a-d]", "e", IGNORE_CASE); + assert_shwild_not_matches!("[a-d]", "E", IGNORE_CASE); + } + + #[test] + fn TEST_assert_shwild_not_matches_WITH_FLAGS_ZERO_1() { + assert_shwild_not_matches!("[a-d]", "e", 0i64); + assert_shwild_not_matches!("[a-d]", "D", 0); + } + + #[test] + #[should_panic( + expected = "could not evaluate actual value due to a failure in the parsing of expected pattern '[a-d': pattern syntax error (at 0:4): incomplete range" + )] + fn TEST_assert_shwild_not_matches_PARSE_ERROR_1() { + assert_shwild_not_matches!("[a-d", "a"); + } + + #[test] + #[should_panic(expected = "assertion failed: actual value 'a' matches unexpectedly with the pattern '[a-d]'")] + fn TEST_assert_shwild_not_matches_UNEXPECTED_MATCH_1() { + assert_shwild_not_matches!("[a-d]", "a"); + } + + #[test] + #[should_panic(expected = "assertion failed: actual value 'D' matches unexpectedly with the pattern '[a-d]'")] + fn TEST_assert_shwild_not_matches_UNEXPECTED_MATCH_WITH__IGNORE_CASE__1() { + assert_shwild_not_matches!("[a-d]", "D", IGNORE_CASE); + } + } + + + #[cfg(feature = "flexible-flags-type")] + mod TEST_FLEXIBLE_FLAGS_TYPE { + #![allow(non_snake_case)] + + use super::*; + + use base_traits::AsI64; + + + struct Flags(i64); + + impl AsI64 for Flags { + fn as_i64(&self) -> i64 { + self.0 + } + } + + + #[test] + fn TEST_shwild_matches_WITH__AsI64__IGNORE_CASE_1() { + assert_eq!(Ok(true), shwild_matches!("[a-d]", "A", IGNORE_CASE)); + assert_eq!(Ok(false), shwild_matches!("[a-d]", "A", 0i64)); + } + + #[test] + fn TEST_shwild_matches_WITH__AsI64__i64_1() { + let flags = IGNORE_CASE; + + assert_eq!(Ok(true), shwild_matches!("[a-d]", "B", flags)); + assert_eq!(Ok(false), shwild_matches!("[a-d]", "B", 0i64)); + } + + #[test] + fn TEST_shwild_matches_WITH__AsI64__Flags_1() { + let flags = Flags(IGNORE_CASE); + + assert_eq!(Ok(true), shwild_matches!("[a-d]", "C", flags)); + assert_eq!(Ok(false), shwild_matches!("[a-d]", "C", Flags(0))); + } + + #[cfg(feature = "assertions")] + mod TEST_ASSERTION_MATCHES { + #![allow(non_snake_case)] + + use super::*; + + + #[test] + fn TEST_assert_shwild_matches_WITH__AsI64__Flags_1() { + assert_shwild_matches!("[a-d]", "D", Flags(IGNORE_CASE)); + assert_shwild_matches!("[a-d]", "d", Flags(0)); + } + + #[test] + fn TEST_assert_shwild_not_matches_WITH__AsI64__Flags_1() { + assert_shwild_not_matches!("[a-d]", "e", Flags(0)); + assert_shwild_not_matches!("[a-d]", "D", Flags(0)); + } + + #[test] + fn TEST_assert_shwild_not_matches_WITH__AsI64__IGNORE_CASE_1() { + assert_shwild_not_matches!("[a-d]", "e", IGNORE_CASE); + } + } + } }