mod common;
use dcbor::prelude::*;
use dcbor_parse::parse_dcbor_item;
use dcbor_pattern::{Matcher, Pattern, format_paths};
use indoc::indoc;
fn cbor(s: &str) -> CBOR { parse_dcbor_item(s).unwrap() }
#[test]
fn test_any_pattern() {
let pattern = Pattern::any();
let number_cbor = cbor("42");
let paths = pattern.paths(&number_cbor);
let expected = "42";
assert_actual_expected!(format_paths(&paths), expected);
let text_cbor = cbor(r#""hello""#);
let paths = pattern.paths(&text_cbor);
let expected = r#""hello""#;
assert_actual_expected!(format_paths(&paths), expected);
let bool_cbor = cbor("true");
let paths = pattern.paths(&bool_cbor);
let expected = "true";
assert_actual_expected!(format_paths(&paths), expected);
let array_cbor = cbor("[1, 2, 3]");
let paths = pattern.paths(&array_cbor);
let expected = "[1, 2, 3]";
assert_actual_expected!(format_paths(&paths), expected);
let null_cbor = cbor("null");
let paths = pattern.paths(&null_cbor);
let expected = "null";
assert_actual_expected!(format_paths(&paths), expected);
assert_eq!(pattern.to_string(), "*");
}
#[test]
fn test_not_any_pattern() {
let pattern = Pattern::parse("!*").unwrap();
assert!(!pattern.matches(&cbor("42")));
assert!(!pattern.matches(&cbor(r#""hello""#)));
assert!(!pattern.matches(&cbor("true")));
assert!(!pattern.matches(&cbor("[1, 2, 3]")));
assert!(!pattern.matches(&cbor("null")));
assert_eq!(pattern.to_string(), "!*");
}
#[test]
fn test_and_pattern() {
let pattern = Pattern::and(vec![
Pattern::number_greater_than(5),
Pattern::number_less_than(10),
]);
let seven_cbor = cbor("7");
let paths = pattern.paths(&seven_cbor);
let expected = "7";
assert_actual_expected!(format_paths(&paths), expected);
let six_cbor = cbor("6");
let paths = pattern.paths(&six_cbor);
let expected = "6";
assert_actual_expected!(format_paths(&paths), expected);
let nine_cbor = cbor("9");
let paths = pattern.paths(&nine_cbor);
let expected = "9";
assert_actual_expected!(format_paths(&paths), expected);
assert!(!pattern.matches(&cbor("3"))); assert!(!pattern.matches(&cbor("12"))); assert!(!pattern.matches(&cbor(r#""hello""#)));
assert_eq!(pattern.to_string(), ">5 & <10");
}
#[test]
fn test_or_pattern() {
let pattern = Pattern::or(vec![
Pattern::number(5),
Pattern::text("hello"),
Pattern::bool(true),
]);
let five_cbor = cbor("5");
let paths = pattern.paths(&five_cbor);
let expected = "5";
assert_actual_expected!(format_paths(&paths), expected);
let hello_cbor = cbor(r#""hello""#);
let paths = pattern.paths(&hello_cbor);
let expected = r#""hello""#;
assert_actual_expected!(format_paths(&paths), expected);
let true_cbor = cbor("true");
let paths = pattern.paths(&true_cbor);
let expected = "true";
assert_actual_expected!(format_paths(&paths), expected);
assert!(!pattern.matches(&cbor("42")));
assert!(!pattern.matches(&cbor(r#""world""#)));
assert!(!pattern.matches(&cbor("false")));
assert_eq!(pattern.to_string(), r#"5 | "hello" | true"#);
}
#[test]
fn test_not_pattern() {
let pattern = Pattern::not_matching(Pattern::number(5));
let forty_two_cbor = cbor("42");
let paths = pattern.paths(&forty_two_cbor);
let expected = "42";
assert_actual_expected!(format_paths(&paths), expected);
let hello_cbor = cbor(r#""hello""#);
let paths = pattern.paths(&hello_cbor);
let expected = r#""hello""#;
assert_actual_expected!(format_paths(&paths), expected);
let true_cbor = cbor("true");
let paths = pattern.paths(&true_cbor);
let expected = "true";
assert_actual_expected!(format_paths(&paths), expected);
assert!(!pattern.matches(&cbor("5")));
assert_eq!(pattern.to_string(), "!5");
}
#[test]
fn test_not_pattern_complex() {
let inner = Pattern::and(vec![
Pattern::number_greater_than(5),
Pattern::number_less_than(10),
]);
let pattern = Pattern::not_matching(inner);
let three_cbor = cbor("3");
let paths = pattern.paths(&three_cbor);
let expected = "3";
assert_actual_expected!(format_paths(&paths), expected);
let twelve_cbor = cbor("12");
let paths = pattern.paths(&twelve_cbor);
let expected = "12";
assert_actual_expected!(format_paths(&paths), expected);
let hello_cbor = cbor(r#""hello""#);
let paths = pattern.paths(&hello_cbor);
let expected = r#""hello""#;
assert_actual_expected!(format_paths(&paths), expected);
assert!(!pattern.matches(&cbor("7")));
assert_eq!(pattern.to_string(), "!(>5 & <10)");
}
#[test]
fn test_nested_meta_patterns() {
let pattern = Pattern::or(vec![
Pattern::and(vec![
Pattern::number_greater_than(5),
Pattern::number_less_than(10),
]),
Pattern::text("hello"),
]);
let seven_cbor = cbor("7");
let paths = pattern.paths(&seven_cbor);
let expected = "7";
assert_actual_expected!(format_paths(&paths), expected);
let hello_cbor = cbor(r#""hello""#);
let paths = pattern.paths(&hello_cbor);
let expected = r#""hello""#;
assert_actual_expected!(format_paths(&paths), expected);
assert!(!pattern.matches(&cbor("3")));
assert!(!pattern.matches(&cbor("12")));
assert!(!pattern.matches(&cbor(r#""world""#)));
assert_eq!(pattern.to_string(), r#">5 & <10 | "hello""#);
}
#[test]
fn test_empty_and_pattern() {
let pattern = Pattern::and(vec![]);
assert!(pattern.matches(&cbor("42")));
assert!(pattern.matches(&cbor(r#""hello""#)));
assert_eq!(pattern.to_string(), "");
}
#[test]
fn test_empty_or_pattern() {
let pattern = Pattern::or(vec![]);
assert!(!pattern.matches(&cbor("42")));
assert!(!pattern.matches(&cbor(r#""hello""#)));
assert_eq!(pattern.to_string(), "");
}
#[test]
fn test_capture_pattern_basic() {
let pattern = Pattern::capture("test", Pattern::number(42));
let forty_two_cbor = cbor("42");
let paths = pattern.paths(&forty_two_cbor);
let expected = "42";
assert_actual_expected!(format_paths(&paths), expected);
assert!(!pattern.matches(&cbor("43")));
assert!(!pattern.matches(&cbor(r#""hello""#)));
assert_eq!(pattern.to_string(), "@test(42)");
}
#[test]
fn test_capture_pattern_text() {
let pattern = Pattern::capture("name", Pattern::text("hello"));
let hello_cbor = cbor(r#""hello""#);
let paths = pattern.paths(&hello_cbor);
let expected = r#""hello""#;
assert_actual_expected!(format_paths(&paths), expected);
assert!(!pattern.matches(&cbor(r#""world""#)));
assert!(!pattern.matches(&cbor("42")));
assert_eq!(pattern.to_string(), r#"@name("hello")"#);
}
#[test]
fn test_capture_pattern_any() {
let pattern = Pattern::capture("anything", Pattern::any());
let forty_two_cbor = cbor("42");
let paths = pattern.paths(&forty_two_cbor);
let expected = "42";
assert_actual_expected!(format_paths(&paths), expected);
let hello_cbor = cbor(r#""hello""#);
let paths = pattern.paths(&hello_cbor);
let expected = r#""hello""#;
assert_actual_expected!(format_paths(&paths), expected);
let true_cbor = cbor("true");
let paths = pattern.paths(&true_cbor);
let expected = "true";
assert_actual_expected!(format_paths(&paths), expected);
let array_cbor = cbor("[1, 2, 3]");
let paths = pattern.paths(&array_cbor);
let expected = "[1, 2, 3]";
assert_actual_expected!(format_paths(&paths), expected);
assert_eq!(pattern.to_string(), "@anything(*)");
}
#[test]
fn test_capture_pattern_not_any() {
let pattern = Pattern::capture("nothing", Pattern::parse("!*").unwrap());
assert!(!pattern.matches(&cbor("42")));
assert!(!pattern.matches(&cbor(r#""hello""#)));
assert!(!pattern.matches(&cbor("true")));
assert_eq!(pattern.to_string(), "@nothing(!*)");
}
#[test]
fn test_capture_pattern_complex() {
let pattern = Pattern::capture(
"range",
Pattern::and(vec![
Pattern::number_greater_than(5),
Pattern::number_less_than(10),
]),
);
let seven_cbor = cbor("7");
let paths = pattern.paths(&seven_cbor);
let expected = "7";
assert_actual_expected!(format_paths(&paths), expected);
let six_cbor = cbor("6");
let paths = pattern.paths(&six_cbor);
let expected = "6";
assert_actual_expected!(format_paths(&paths), expected);
let nine_cbor = cbor("9");
let paths = pattern.paths(&nine_cbor);
let expected = "9";
assert_actual_expected!(format_paths(&paths), expected);
assert!(!pattern.matches(&cbor("5")));
assert!(!pattern.matches(&cbor("10")));
assert!(!pattern.matches(&cbor("15")));
let display = pattern.to_string();
assert!(display.starts_with("@range("));
assert!(display.contains("&"));
assert!(display.ends_with(")"));
}
#[test]
fn test_nested_capture_patterns() {
let pattern = Pattern::capture(
"outer",
Pattern::or(vec![
Pattern::capture("inner1", Pattern::number(42)),
Pattern::capture("inner2", Pattern::text("hello")),
]),
);
let forty_two_cbor = cbor("42");
let paths = pattern.paths(&forty_two_cbor);
let expected = "42";
assert_actual_expected!(format_paths(&paths), expected);
let hello_cbor = cbor(r#""hello""#);
let paths = pattern.paths(&hello_cbor);
let expected = r#""hello""#;
assert_actual_expected!(format_paths(&paths), expected);
assert!(!pattern.matches(&cbor("43")));
assert!(!pattern.matches(&cbor(r#""world""#)));
let display = pattern.to_string();
assert!(display.starts_with("@outer("));
assert!(display.contains("@inner1"));
assert!(display.contains("@inner2"));
assert!(display.contains("|"));
assert!(display.ends_with(")"));
}
#[test]
fn test_capture_pattern_name_access() {
let inner_pattern = Pattern::number(42);
let pattern = Pattern::capture("test_name", inner_pattern.clone());
if let dcbor_pattern::Pattern::Meta(dcbor_pattern::MetaPattern::Capture(
capture,
)) = &pattern
{
assert_eq!(capture.name(), "test_name");
assert_eq!(capture.pattern(), &inner_pattern);
} else {
panic!("Expected capture pattern");
}
}
#[test]
fn test_capture_pattern_is_complex() {
let simple = Pattern::capture("simple", Pattern::number(42));
assert!(!simple.is_complex());
let complex = Pattern::capture(
"complex",
Pattern::and(vec![Pattern::number(1), Pattern::number(2)]),
);
assert!(complex.is_complex());
}
#[test]
fn test_repeat_pattern_basic() {
let pattern = Pattern::group(Pattern::number(42));
let forty_two_cbor = cbor("42");
let paths = pattern.paths(&forty_two_cbor);
let expected = "42";
assert_actual_expected!(format_paths(&paths), expected);
assert!(!pattern.matches(&cbor("41")));
assert!(!pattern.matches(&cbor(r#""hello""#)));
assert_eq!(pattern.to_string(), "(42){1}");
}
#[test]
fn test_repeat_pattern_with_quantifier() {
use dcbor_pattern::{Quantifier, Reluctance};
let optional_pattern = Pattern::repeat(
Pattern::number(42),
Quantifier::new(0..=1, Reluctance::Greedy),
);
let forty_two_cbor = cbor("42");
let paths = optional_pattern.paths(&forty_two_cbor);
let expected = "42";
assert_actual_expected!(format_paths(&paths), expected);
assert_eq!(optional_pattern.to_string(), "(42)?");
}
#[test]
fn test_repeat_pattern_zero_or_more() {
use dcbor_pattern::{Quantifier, Reluctance};
let star_pattern = Pattern::repeat(
Pattern::number(42),
Quantifier::new(0.., Reluctance::Greedy),
);
let forty_two_cbor = cbor("42");
let paths = star_pattern.paths(&forty_two_cbor);
let expected = "42";
assert_actual_expected!(format_paths(&paths), expected);
assert!(star_pattern.matches(&cbor("41")));
assert_eq!(star_pattern.to_string(), "(42)*");
}
#[test]
fn test_repeat_pattern_one_or_more() {
use dcbor_pattern::{Quantifier, Reluctance};
let plus_pattern = Pattern::repeat(
Pattern::number(42),
Quantifier::new(1.., Reluctance::Greedy),
);
let forty_two_cbor = cbor("42");
let paths = plus_pattern.paths(&forty_two_cbor);
let expected = "42";
assert_actual_expected!(format_paths(&paths), expected);
assert!(!plus_pattern.matches(&cbor("41")));
assert_eq!(plus_pattern.to_string(), "(42)+");
}
#[test]
fn test_repeat_pattern_exact_count() {
use dcbor_pattern::{Quantifier, Reluctance};
let exact_pattern = Pattern::repeat(
Pattern::number(42),
Quantifier::new(3..=3, Reluctance::Greedy),
);
assert!(!exact_pattern.matches(&cbor("42")));
assert_eq!(exact_pattern.to_string(), "(42){3}");
}
#[test]
fn test_repeat_pattern_display_with_reluctance() {
use dcbor_pattern::{Quantifier, Reluctance};
let lazy_pattern = Pattern::repeat(
Pattern::text("test"),
Quantifier::new(0..=1, Reluctance::Lazy),
);
assert_eq!(lazy_pattern.to_string(), r#"("test")??"#);
let possessive_pattern = Pattern::repeat(
Pattern::text("test"),
Quantifier::new(1.., Reluctance::Possessive),
);
assert_eq!(possessive_pattern.to_string(), r#"("test")++"#);
}
#[test]
fn test_search_pattern_basic() {
let pattern = Pattern::search(Pattern::number(42));
let forty_two_cbor = cbor("42");
let paths = pattern.paths(&forty_two_cbor);
let expected = "42";
assert_actual_expected!(format_paths(&paths), expected);
assert!(!pattern.matches(&cbor("43")));
let array_cbor = cbor("[1, 42, 3]");
let paths = pattern.paths(&array_cbor);
#[rustfmt::skip]
let expected = indoc! {r#"
[1, 42, 3]
42
"#}.trim();
assert_actual_expected!(format_paths(&paths), expected);
let map_cbor = cbor("{1: 42}");
let paths = pattern.paths(&map_cbor);
#[rustfmt::skip]
let expected = indoc! {r#"
{1: 42}
42
"#}.trim();
assert_actual_expected!(format_paths(&paths), expected);
let nested_cbor = cbor("{\"key\": [1, 2, 42]}");
let paths = pattern.paths(&nested_cbor);
#[rustfmt::skip]
let expected = r#"{"key": [1, 2, 42]}
[1, 2, 42]
42"#;
assert_actual_expected!(format_paths(&paths), expected);
assert!(!pattern.matches(&cbor("[1, 2, 3]")));
assert!(!pattern.matches(&cbor("{1: 2}")));
assert_eq!(pattern.to_string(), "search(42)");
}
#[test]
fn test_search_pattern_text() {
let pattern = Pattern::search(Pattern::text("hello"));
let array_cbor = cbor(r#"["hello", "world"]"#);
let paths = pattern.paths(&array_cbor);
#[rustfmt::skip]
let expected = indoc! {r#"
["hello", "world"]
"hello"
"#}.trim();
assert_actual_expected!(format_paths(&paths), expected);
let map_cbor = cbor(r#"{"greeting": "hello"}"#);
let paths = pattern.paths(&map_cbor);
#[rustfmt::skip]
let expected = indoc! {r#"
{"greeting": "hello"}
"hello"
"#}.trim();
assert_actual_expected!(format_paths(&paths), expected);
let nested_cbor = cbor(r#"[{"nested": ["hello"]}]"#);
let paths = pattern.paths(&nested_cbor);
#[rustfmt::skip]
let expected = r#"[{"nested": ["hello"]}]
{"nested": ["hello"]}
["hello"]
"hello""#;
assert_actual_expected!(format_paths(&paths), expected);
assert!(!pattern.matches(&cbor(r#"["goodbye", "world"]"#)));
assert_eq!(pattern.to_string(), r#"search("hello")"#);
}
#[test]
fn test_search_pattern_any() {
let pattern = Pattern::search(Pattern::any());
let forty_two_cbor = cbor("42");
let paths = pattern.paths(&forty_two_cbor);
let expected = "42";
assert_actual_expected!(format_paths(&paths), expected);
let hello_cbor = cbor(r#""hello""#);
let paths = pattern.paths(&hello_cbor);
let expected = r#""hello""#;
assert_actual_expected!(format_paths(&paths), expected);
let array_cbor = cbor("[1, 2, 3]");
let paths = pattern.paths(&array_cbor);
#[rustfmt::skip]
let expected = indoc! {r#"
[1, 2, 3]
[1, 2, 3]
1
[1, 2, 3]
2
[1, 2, 3]
3
"#}.trim();
assert_actual_expected!(format_paths(&paths), expected);
let empty_map_cbor = cbor("{}");
let paths = pattern.paths(&empty_map_cbor);
let expected = "{}";
assert_actual_expected!(format_paths(&paths), expected);
assert_eq!(pattern.to_string(), "search(*)");
}
#[test]
fn test_search_pattern_complex() {
let pattern = Pattern::search(Pattern::number(5));
let test_data = cbor(
r#"
{
"data": [
{"values": [1, 2, 3]},
{"values": [4, 5, 6]},
{"other": "text"}
],
"meta": {
"count": 5,
"items": [7, 8, 9]
}
}
"#,
);
let paths = pattern.paths(&test_data);
#[rustfmt::skip]
let expected = r#"{"data": [{"values": [1, 2, 3]}, {"values": [4, 5, 6]}, {"other": "text"}], "meta": {"count": 5, "items": [7, 8, 9]}}
[{"values": [1, 2, 3]}, {"values": [4, 5, 6]}, {"other": "text"}]
{"values": [4, 5, 6]}
[4, 5, 6]
5
{"data": [{"values": [1, 2, 3]}, {"values": [4, 5, 6]}, {"other": "text"}], "meta": {"count": 5, "items": [7, 8, 9]}}
{"count": 5, "items": [7, 8, 9]}
5"#;
assert_actual_expected!(format_paths(&paths), expected);
assert!(!paths.is_empty());
}
#[test]
fn test_search_pattern_with_captures() {
let inner_pattern = Pattern::capture("found", Pattern::number(42));
let pattern = Pattern::search(inner_pattern);
let data = cbor(r#"[1, {"key": 42}, 3]"#);
let paths = pattern.paths(&data);
#[rustfmt::skip]
let expected = indoc! {r#"
[1, {"key": 42}, 3]
{"key": 42}
42
"#}.trim();
assert_actual_expected!(format_paths(&paths), expected);
assert_eq!(pattern.to_string(), "search(@found(42))");
}
#[test]
fn test_search_pattern_paths() {
let pattern = Pattern::search(Pattern::text("target"));
#[rustfmt::skip]
let data = cbor(r#"
{
"level1": {
"level2": ["target", "other"]
},
"another": "target"
}
"#);
let paths = pattern.paths(&data);
assert!(paths.len() >= 2);
for path in &paths {
assert!(!path.is_empty());
}
}
#[test]
fn test_search_pattern_edge_cases() {
let pattern = Pattern::search(Pattern::number(1));
assert!(!pattern.matches(&cbor("[]")));
assert!(!pattern.matches(&cbor("{}")));
assert!(!pattern.matches(&cbor("null")));
assert!(pattern.matches(&cbor("[[[[1]]]]")));
}
#[test]
fn test_search_pattern_with_structure_pattern() {
let pattern = Pattern::search(Pattern::parse("array").unwrap());
#[rustfmt::skip]
let data = cbor(r#"
{
"arrays": [[1, 2], [3, 4]],
"not_array": 42
}
"#);
let paths = pattern.paths(&data);
#[rustfmt::skip]
let expected = indoc! {r#"
{"arrays": [[1, 2], [3, 4]], "not_array": 42}
[[1, 2], [3, 4]]
{"arrays": [[1, 2], [3, 4]], "not_array": 42}
[[1, 2], [3, 4]]
[1, 2]
{"arrays": [[1, 2], [3, 4]], "not_array": 42}
[[1, 2], [3, 4]]
[3, 4]
"#}.trim();
assert_actual_expected!(format_paths(&paths), expected);
assert!(paths.len() >= 3); }
#[test]
fn test_search_array_order() {
let data = cbor(r#"[[1, 2, 3], [4, 5, 6]]"#);
let pattern = Pattern::parse("search(array)").unwrap();
let paths = pattern.paths(&data);
#[rustfmt::skip]
let expected = indoc! {r#"
[[1, 2, 3], [4, 5, 6]]
[[1, 2, 3], [4, 5, 6]]
[1, 2, 3]
[[1, 2, 3], [4, 5, 6]]
[4, 5, 6]
"#}.trim();
assert_actual_expected!(format_paths(&paths), expected);
let pattern = Pattern::parse("search(number)").unwrap();
let paths = pattern.paths(&data);
#[rustfmt::skip]
let expected = indoc! {r#"
[[1, 2, 3], [4, 5, 6]]
[1, 2, 3]
1
[[1, 2, 3], [4, 5, 6]]
[1, 2, 3]
2
[[1, 2, 3], [4, 5, 6]]
[1, 2, 3]
3
[[1, 2, 3], [4, 5, 6]]
[4, 5, 6]
4
[[1, 2, 3], [4, 5, 6]]
[4, 5, 6]
5
[[1, 2, 3], [4, 5, 6]]
[4, 5, 6]
6
"#}.trim();
assert_actual_expected!(format_paths(&paths), expected);
}