mod common;
use std::collections::HashMap;
use dcbor_parse::parse_dcbor_item;
use dcbor_pattern::{
FormatPathsOpts, Matcher, Pattern, format_paths_with_captures,
};
use indoc::indoc;
#[cfg(test)]
mod test_comprehensive_variadic_sequences {
use super::*;
#[test]
fn test_zero_or_more_greedy() {
let pattern = Pattern::parse("[(*)*]").unwrap();
let empty_array = parse_dcbor_item("[]").unwrap();
let single_element = parse_dcbor_item("[42]").unwrap();
let multiple_elements = parse_dcbor_item("[1, 2, 3]").unwrap();
let (paths, captures) = pattern.paths_with_captures(&empty_array);
assert!(!paths.is_empty(), "[(*)*] should match empty array");
assert!(
captures.is_empty(),
"No captures expected for basic quantifier"
);
#[rustfmt::skip]
let expected_empty = indoc! {r#"
[]
"#}.trim();
assert_actual_expected!(
format_paths_with_captures(
&paths,
&captures,
FormatPathsOpts::default()
),
expected_empty
);
let (paths, captures) = pattern.paths_with_captures(&single_element);
assert!(
!paths.is_empty(),
"[(*)*] should match single element array"
);
#[rustfmt::skip]
let expected_single = indoc! {r#"
[42]
"#}.trim();
assert_actual_expected!(
format_paths_with_captures(
&paths,
&captures,
FormatPathsOpts::default()
),
expected_single
);
let (paths, captures) = pattern.paths_with_captures(&multiple_elements);
assert!(
!paths.is_empty(),
"[(*)*] should match multiple element array"
);
#[rustfmt::skip]
let expected_multiple = indoc! {r#"
[1, 2, 3]
"#}.trim();
assert_actual_expected!(
format_paths_with_captures(
&paths,
&captures,
FormatPathsOpts::default()
),
expected_multiple
);
}
#[test]
fn test_one_or_more_greedy() {
let pattern = Pattern::parse("[(*)+]").unwrap();
let empty_array = parse_dcbor_item("[]").unwrap();
let single_element = parse_dcbor_item("[42]").unwrap();
let multiple_elements = parse_dcbor_item("[1, 2, 3]").unwrap();
let (paths, captures) = pattern.paths_with_captures(&empty_array);
assert!(paths.is_empty(), "[(*)+] should NOT match empty array");
assert!(captures.is_empty(), "No captures expected when no match");
let (paths, captures) = pattern.paths_with_captures(&single_element);
assert!(
!paths.is_empty(),
"[(*)+] should match single element array"
);
#[rustfmt::skip]
let expected_single = indoc! {r#"
[42]
"#}.trim();
assert_actual_expected!(
format_paths_with_captures(
&paths,
&captures,
FormatPathsOpts::default()
),
expected_single
);
let (paths, captures) = pattern.paths_with_captures(&multiple_elements);
assert!(
!paths.is_empty(),
"[(*)+] should match multiple element array"
);
#[rustfmt::skip]
let expected_multiple = indoc! {r#"
[1, 2, 3]
"#}.trim();
assert_actual_expected!(
format_paths_with_captures(
&paths,
&captures,
FormatPathsOpts::default()
),
expected_multiple
);
}
#[test]
fn test_zero_or_one_greedy() {
let pattern = Pattern::parse("[(*)?]").unwrap();
let empty_array = parse_dcbor_item("[]").unwrap();
let single_element = parse_dcbor_item("[42]").unwrap();
let multiple_elements = parse_dcbor_item("[1, 2]").unwrap();
let (paths, captures) = pattern.paths_with_captures(&empty_array);
assert!(!paths.is_empty(), "[(*)?] should match empty array");
#[rustfmt::skip]
let expected_empty = indoc! {r#"
[]
"#}.trim();
assert_actual_expected!(
format_paths_with_captures(
&paths,
&captures,
FormatPathsOpts::default()
),
expected_empty
);
let (paths, captures) = pattern.paths_with_captures(&single_element);
assert!(
!paths.is_empty(),
"[(*)?] should match single element array"
);
#[rustfmt::skip]
let expected_single = indoc! {r#"
[42]
"#}.trim();
assert_actual_expected!(
format_paths_with_captures(
&paths,
&captures,
FormatPathsOpts::default()
),
expected_single
);
let (paths, captures) = pattern.paths_with_captures(&multiple_elements);
assert!(
paths.is_empty(),
"[(*)?] should NOT match multiple element array"
);
assert!(captures.is_empty(), "No captures expected when no match");
}
#[test]
fn test_exactly_once_default() {
let pattern = Pattern::parse("[(*)]").unwrap();
let empty_array = parse_dcbor_item("[]").unwrap();
let single_element = parse_dcbor_item("[42]").unwrap();
let multiple_elements = parse_dcbor_item("[1, 2]").unwrap();
let (paths, _captures) = pattern.paths_with_captures(&empty_array);
assert!(paths.is_empty(), "[(*)] should NOT match empty array");
let (paths, captures) = pattern.paths_with_captures(&single_element);
assert!(!paths.is_empty(), "[(*)] should match single element array");
#[rustfmt::skip]
let expected_single = indoc! {r#"
[42]
"#}.trim();
assert_actual_expected!(
format_paths_with_captures(
&paths,
&captures,
FormatPathsOpts::default()
),
expected_single
);
let (paths, _captures) =
pattern.paths_with_captures(&multiple_elements);
assert!(
paths.is_empty(),
"[(*)] should NOT match multiple element array"
);
}
#[test]
fn test_zero_or_more_lazy() {
let pattern = Pattern::parse("[(*)*?]").unwrap();
let empty_array = parse_dcbor_item("[]").unwrap();
let single_element = parse_dcbor_item("[42]").unwrap();
let multiple_elements = parse_dcbor_item("[1, 2, 3]").unwrap();
let (paths, _) = pattern.paths_with_captures(&empty_array);
assert!(!paths.is_empty(), "[(*)*?] should match empty array");
let (paths, _) = pattern.paths_with_captures(&single_element);
assert!(
!paths.is_empty(),
"[(*)*?] should match single element array"
);
let (paths, _) = pattern.paths_with_captures(&multiple_elements);
assert!(
!paths.is_empty(),
"[(*)*?] should match multiple element array"
);
}
#[test]
fn test_one_or_more_lazy() {
let pattern = Pattern::parse("[(*)+?]").unwrap();
let empty_array = parse_dcbor_item("[]").unwrap();
let single_element = parse_dcbor_item("[42]").unwrap();
let multiple_elements = parse_dcbor_item("[1, 2, 3]").unwrap();
let (paths, _) = pattern.paths_with_captures(&empty_array);
assert!(paths.is_empty(), "[(*)+?] should NOT match empty array");
let (paths, _) = pattern.paths_with_captures(&single_element);
assert!(
!paths.is_empty(),
"[(*)+?] should match single element array"
);
let (paths, _) = pattern.paths_with_captures(&multiple_elements);
assert!(
!paths.is_empty(),
"[(*)+?] should match multiple element array"
);
}
#[test]
fn test_zero_or_one_lazy() {
let pattern = Pattern::parse("[(*)??]").unwrap();
let empty_array = parse_dcbor_item("[]").unwrap();
let single_element = parse_dcbor_item("[42]").unwrap();
let multiple_elements = parse_dcbor_item("[1, 2]").unwrap();
let (paths, _) = pattern.paths_with_captures(&empty_array);
assert!(!paths.is_empty(), "[(*)??] should match empty array");
let (paths, _) = pattern.paths_with_captures(&single_element);
assert!(
!paths.is_empty(),
"[(*)??] should match single element array"
);
let (paths, _) = pattern.paths_with_captures(&multiple_elements);
assert!(
paths.is_empty(),
"[(*)??] should NOT match multiple element array"
);
}
#[test]
fn test_zero_or_more_possessive() {
let pattern = Pattern::parse("[(*)*+]").unwrap();
let empty_array = parse_dcbor_item("[]").unwrap();
let single_element = parse_dcbor_item("[42]").unwrap();
let multiple_elements = parse_dcbor_item("[1, 2, 3]").unwrap();
let (paths, _) = pattern.paths_with_captures(&empty_array);
assert!(!paths.is_empty(), "[(*)*+] should match empty array");
let (paths, _) = pattern.paths_with_captures(&single_element);
assert!(
!paths.is_empty(),
"[(*)*+] should match single element array"
);
let (paths, _) = pattern.paths_with_captures(&multiple_elements);
assert!(
!paths.is_empty(),
"[(*)*+] should match multiple element array"
);
}
#[test]
fn test_one_or_more_possessive() {
let pattern = Pattern::parse("[(*)++]").unwrap();
let empty_array = parse_dcbor_item("[]").unwrap();
let single_element = parse_dcbor_item("[42]").unwrap();
let multiple_elements = parse_dcbor_item("[1, 2, 3]").unwrap();
let (paths, _) = pattern.paths_with_captures(&empty_array);
assert!(paths.is_empty(), "[(*)++] should NOT match empty array");
let (paths, _) = pattern.paths_with_captures(&single_element);
assert!(
!paths.is_empty(),
"[(*)++] should match single element array"
);
let (paths, _) = pattern.paths_with_captures(&multiple_elements);
assert!(
!paths.is_empty(),
"[(*)++] should match multiple element array"
);
}
#[test]
fn test_zero_or_one_possessive() {
let pattern = Pattern::parse("[(*)?+]").unwrap();
let empty_array = parse_dcbor_item("[]").unwrap();
let single_element = parse_dcbor_item("[42]").unwrap();
let multiple_elements = parse_dcbor_item("[1, 2]").unwrap();
let (paths, _) = pattern.paths_with_captures(&empty_array);
assert!(!paths.is_empty(), "[(*)?+] should match empty array");
let (paths, _) = pattern.paths_with_captures(&single_element);
assert!(
!paths.is_empty(),
"[(*)?+] should match single element array"
);
let (paths, _) = pattern.paths_with_captures(&multiple_elements);
assert!(
paths.is_empty(),
"[(*)?+] should NOT match multiple element array"
);
}
#[test]
fn test_exact_count_interval() {
let pattern = Pattern::parse("[(*){3}]").unwrap();
let empty_array = parse_dcbor_item("[]").unwrap();
let two_elements = parse_dcbor_item("[1, 2]").unwrap();
let three_elements = parse_dcbor_item("[1, 2, 3]").unwrap();
let four_elements = parse_dcbor_item("[1, 2, 3, 4]").unwrap();
let (paths, _) = pattern.paths_with_captures(&empty_array);
assert!(paths.is_empty(), "[(*){{3}}] should NOT match empty array");
let (paths, _) = pattern.paths_with_captures(&two_elements);
assert!(
paths.is_empty(),
"[(*){{3}}] should NOT match 2-element array"
);
let (paths, _) = pattern.paths_with_captures(&three_elements);
assert!(!paths.is_empty(), "[(*){{3}}] should match 3-element array");
#[rustfmt::skip]
let expected_three = indoc! {r#"
[1, 2, 3]
"#}.trim();
assert_actual_expected!(
format_paths_with_captures(
&paths,
&HashMap::new(),
FormatPathsOpts::default()
),
expected_three
);
let (paths, _) = pattern.paths_with_captures(&four_elements);
assert!(
paths.is_empty(),
"[(*){{3}}] should NOT match 4-element array"
);
}
#[test]
fn test_range_interval() {
let pattern = Pattern::parse("[(*){2,4}]").unwrap();
let one_element = parse_dcbor_item("[1]").unwrap();
let two_elements = parse_dcbor_item("[1, 2]").unwrap();
let three_elements = parse_dcbor_item("[1, 2, 3]").unwrap();
let four_elements = parse_dcbor_item("[1, 2, 3, 4]").unwrap();
let five_elements = parse_dcbor_item("[1, 2, 3, 4, 5]").unwrap();
let (paths, _) = pattern.paths_with_captures(&one_element);
assert!(
paths.is_empty(),
"[(*){{2,4}}] should NOT match 1-element array"
);
let (paths, _) = pattern.paths_with_captures(&two_elements);
assert!(
!paths.is_empty(),
"[(*){{2,4}}] should match 2-element array"
);
let (paths, _) = pattern.paths_with_captures(&three_elements);
assert!(
!paths.is_empty(),
"[(*){{2,4}}] should match 3-element array"
);
let (paths, _) = pattern.paths_with_captures(&four_elements);
assert!(
!paths.is_empty(),
"[(*){{2,4}}] should match 4-element array"
);
let (paths, _) = pattern.paths_with_captures(&five_elements);
assert!(
paths.is_empty(),
"[(*){{2,4}}] should NOT match 5-element array"
);
}
#[test]
fn test_minimum_interval() {
let pattern = Pattern::parse("[(*){2,}]").unwrap();
let one_element = parse_dcbor_item("[1]").unwrap();
let two_elements = parse_dcbor_item("[1, 2]").unwrap();
let five_elements = parse_dcbor_item("[1, 2, 3, 4, 5]").unwrap();
let (paths, _) = pattern.paths_with_captures(&one_element);
assert!(
paths.is_empty(),
"[(*){{2,}}] should NOT match 1-element array"
);
let (paths, _) = pattern.paths_with_captures(&two_elements);
assert!(
!paths.is_empty(),
"[(*){{2,}}] should match 2-element array"
);
let (paths, _) = pattern.paths_with_captures(&five_elements);
assert!(
!paths.is_empty(),
"[(*){{2,}}] should match 5-element array"
);
}
#[test]
fn test_maximum_interval() {
let pattern = Pattern::parse("[(*){0,3}]").unwrap();
let empty_array = parse_dcbor_item("[]").unwrap();
let two_elements = parse_dcbor_item("[1, 2]").unwrap();
let three_elements = parse_dcbor_item("[1, 2, 3]").unwrap();
let four_elements = parse_dcbor_item("[1, 2, 3, 4]").unwrap();
let (paths, _) = pattern.paths_with_captures(&empty_array);
assert!(!paths.is_empty(), "[(*){{0,3}}] should match empty array");
let (paths, _) = pattern.paths_with_captures(&two_elements);
assert!(
!paths.is_empty(),
"[(*){{0,3}}] should match 2-element array"
);
let (paths, _) = pattern.paths_with_captures(&three_elements);
assert!(
!paths.is_empty(),
"[(*){{0,3}}] should match 3-element array"
);
let (paths, _) = pattern.paths_with_captures(&four_elements);
assert!(
paths.is_empty(),
"[(*){{0,3}}] should NOT match 4-element array"
);
}
#[test]
fn test_quantifiers_with_captures() {
let pattern = Pattern::parse("[(number)*, @item(text)]").unwrap();
let numbers_then_text = parse_dcbor_item(r#"[1, 2, "hello"]"#).unwrap();
let only_text = parse_dcbor_item(r#"["hello"]"#).unwrap();
let only_numbers = parse_dcbor_item("[1, 2]").unwrap();
let (paths, captures) = pattern.paths_with_captures(&numbers_then_text);
assert!(
!paths.is_empty(),
"[(number)*, @item(text)] should match numbers then text"
);
assert!(captures.contains_key("item"), "Should have @item capture");
let (paths, captures) = pattern.paths_with_captures(&only_text);
assert!(
!paths.is_empty(),
"[(number)*, @item(text)] should match only text"
);
assert!(
captures.contains_key("item"),
"Should have @item capture for text-only"
);
let (paths, captures) = pattern.paths_with_captures(&only_numbers);
assert!(
paths.is_empty(),
"[(number)*, @item(text)] should NOT match only numbers"
);
assert!(captures.is_empty(), "Should have no captures when no match");
let first_capture_pattern =
Pattern::parse("[@first(number), (*)*]").unwrap();
let multi_element = parse_dcbor_item(r#"[42, "text", true]"#).unwrap();
let (paths, captures) =
first_capture_pattern.paths_with_captures(&multi_element);
assert!(
!paths.is_empty(),
"[@first(number), (*)*] should match multi-element array"
);
assert!(
captures.contains_key("first"),
"Should capture the first number"
);
if let Some(first_captures) = captures.get("first") {
assert_eq!(
first_captures.len(),
1,
"Should capture exactly one first element"
);
}
}
#[test]
fn test_multiple_quantifiers_in_pattern() {
let pattern = Pattern::parse("[(number)*, (text)+]").unwrap();
let numbers_then_texts =
parse_dcbor_item(r#"[1, 2, "a", "b"]"#).unwrap();
let only_texts = parse_dcbor_item(r#"["a", "b"]"#).unwrap();
let only_numbers = parse_dcbor_item("[1, 2]").unwrap();
let empty_array = parse_dcbor_item("[]").unwrap();
let (paths, _) = pattern.paths_with_captures(&numbers_then_texts);
assert!(
!paths.is_empty(),
"[(number)*, (text)+] should match numbers then texts"
);
let (paths, _) = pattern.paths_with_captures(&only_texts);
assert!(
!paths.is_empty(),
"[(number)*, (text)+] should match only texts"
);
let (paths, _) = pattern.paths_with_captures(&only_numbers);
assert!(
paths.is_empty(),
"[(number)*, (text)+] should NOT match only numbers"
);
let (paths, _) = pattern.paths_with_captures(&empty_array);
assert!(
paths.is_empty(),
"[(number)*, (text)+] should NOT match empty array"
);
}
}