scrolling_window_pattern_matcher 0.3.2

A library for matching patterns in a scrolling window of data.
Documentation

Scrolling Window Pattern Matcher

This crate provides a generic pattern matcher that operates over a scrolling window (queue) of items. Patterns can be defined as sequences of values, functions, or a mix of both. When a pattern matches, an optional user-defined callback is invoked. The matcher supports optional deduplication of matches and per-pattern overlap settings.

Features

  • Match patterns using values, functions, or both
  • Optional deduplication of matches
  • Support for overlapping matches (per-pattern control)
  • Callback invocation on match (per-pattern)
  • No unnecessary trait bounds: PartialEq is only required for value-based patterns

Usage: Value/Mixed Patterns with Multiple Patterns

use scrolling_window_pattern_matcher::{ScrollingWindowPatternMatcherRef, PatternElem};
let window = vec![&1, &2, &1, &2, &1];
let patterns = vec![
    vec![PatternElem::Value(1), PatternElem::Value(2)],
    vec![PatternElem::Value(2), PatternElem::Value(1)],
    vec![PatternElem::Value(1)],
    vec![PatternElem::Value(2)],
];
let matcher = ScrollingWindowPatternMatcherRef::new(5);
let matches = matcher.find_matches(&window, &patterns, false, None::<fn(usize, usize)>);
assert!(matches.contains(&(0, 0))); // [1,2] at 0
assert!(matches.contains(&(1, 1))); // [2,1] at 1
assert!(matches.contains(&(2, 0))); // [1] at 0
assert!(matches.contains(&(3, 1))); // [2] at 1

Usage: Patterns with Callbacks and Overlap Settings

use scrolling_window_pattern_matcher::{ScrollingWindowPatternMatcherRef, PatternElem, PatternWithCallback};
use std::rc::Rc;
use std::cell::RefCell;
let window = vec![&1, &2, &1, &2, &1];
let results: Rc<RefCell<Vec<Vec<i32>>>> = Rc::new(RefCell::new(vec![]));
let results1 = results.clone();
let results2 = results.clone();
let patterns = vec![
    PatternWithCallback {
        pattern: vec![PatternElem::Value(1), PatternElem::Value(2)],
        callback: Box::new(move |matched| results1.borrow_mut().push(matched.iter().map(|x| **x).collect::<Vec<_>>())),
        allow_overlap_with_others: false,
        allow_others_to_overlap: true,
    },
    PatternWithCallback {
        pattern: vec![PatternElem::Value(2), PatternElem::Value(1)],
        callback: Box::new(move |matched| results2.borrow_mut().push(matched.iter().map(|x| **x).collect::<Vec<_>>())),
        allow_overlap_with_others: true,
        allow_others_to_overlap: true,
    },
];
let matcher = ScrollingWindowPatternMatcherRef::new(5);
matcher.find_matches_with_callbacks(&window, &patterns);
let results = results.borrow();
assert!(results.contains(&vec![1, 2]));
assert!(results.contains(&vec![2, 1]));

Usage: Function-Only Patterns (New API)

use scrolling_window_pattern_matcher::ScrollingWindowFunctionPatternMatcherRef;
let window = vec![&1, &2, &3, &4];
let patterns_fn: Vec<Vec<Box<dyn Fn(&i32) -> bool>>> = vec![
    vec![Box::new(|x| *x == 1)],
    vec![Box::new(|x| *x == 4)],
];
let matcher = ScrollingWindowFunctionPatternMatcherRef::new(4);
let matches = matcher.find_matches(&window, &patterns_fn, false, None::<fn(usize, usize)>);
assert!(matches.contains(&(0, 0)));
assert!(matches.contains(&(1, 3)));

Usage: Function-Only Patterns with Callbacks and Overlap

use scrolling_window_pattern_matcher::{ScrollingWindowFunctionPatternMatcherRef, PatternWithCallbackFn};
use std::rc::Rc;
use std::cell::RefCell;
let window = vec![&1, &2, &3, &4, &5];
let results: Rc<RefCell<Vec<Vec<i32>>>> = Rc::new(RefCell::new(vec![]));
let results1 = results.clone();
let results2 = results.clone();
let patterns = vec![
    PatternWithCallbackFn {
        pattern: vec![Box::new(|x: &i32| *x == 1), Box::new(|x: &i32| *x == 2)],
        callback: Box::new(move |matched| results1.borrow_mut().push(matched.iter().map(|x| **x).collect::<Vec<_>>())),
        allow_overlap_with_others: false,
        allow_others_to_overlap: true,
    },
    PatternWithCallbackFn {
        pattern: vec![Box::new(|x: &i32| *x == 2), Box::new(|x: &i32| *x == 3)],
        callback: Box::new(move |matched| results2.borrow_mut().push(matched.iter().map(|x| **x).collect::<Vec<_>>())),
        allow_overlap_with_others: true,
        allow_others_to_overlap: false,
    },
];
let matcher = ScrollingWindowFunctionPatternMatcherRef::new(4);
matcher.find_matches_with_callbacks(&window, &patterns);
let results = results.borrow();
assert!(results.contains(&vec![1, 2]));
assert!(results.contains(&vec![2, 3]));

Overlap Settings

  • allow_overlap_with_others: If false, this pattern will not match if it would overlap with any previous match.
  • allow_others_to_overlap: If false, once this pattern matches, no future matches can overlap its matched region.

Debug Logging

This crate uses the log crate for debug logging. To enable debug output during development, add the following to your main function or test harness:

env_logger::init();

Then run your program or tests with:

RUST_LOG=debug cargo test

To disable logging (e.g., in production), do not initialize a logger, or set a higher log level:

RUST_LOG=info cargo run

Debug logs provide detailed information about matcher execution, pattern matching, overlap checks, and callback invocations.

API

  • ScrollingWindowPatternMatcherRef::find_matches: Value/mixed patterns with multiple patterns and multi-element support.
  • ScrollingWindowPatternMatcherRef::find_matches_with_callbacks: Value/mixed patterns with per-pattern callbacks and overlap settings.
  • ScrollingWindowFunctionPatternMatcherRef::find_matches: Function-only patterns with multiple patterns.
  • ScrollingWindowFunctionPatternMatcherRef::find_matches_with_callbacks: Function-only patterns with per-pattern callbacks and overlap settings.

See the test module for more comprehensive examples and edge cases.

Edge Cases

  • Empty window or patterns: returns no matches
  • Patterns can be all values, all functions, or mixed
  • Multiple patterns and multi-element patterns supported
  • Deduplication and overlap settings can be combined
  • Patterns of length 1 and longer are supported
  • Overlap exclusion can prevent some matches (see tests)
  • Function-only API works for any type, even if T does not implement PartialEq