mod common;
use bc_envelope::prelude::*;
use bc_envelope_pattern::{
FormatPathsOpts, Matcher, Pattern, format_paths_with_captures_opt,
};
use indoc::indoc;
#[test]
fn test_simple_dcbor_capture() {
let envelope = Envelope::new(42);
let pattern = Pattern::parse("cbor(/@num(42)/)").unwrap();
let (paths, captures) = pattern.paths_with_captures(&envelope);
assert_eq!(paths.len(), 1, "Should find exactly one path");
assert_eq!(captures.len(), 1, "Should have exactly one capture group");
assert!(captures.contains_key("num"), "Should have 'num' capture");
assert_eq!(captures["num"].len(), 1, "Should capture one instance");
let actual = format_paths_with_captures_opt(
&paths,
&captures,
FormatPathsOpts::default(),
);
#[rustfmt::skip]
let expected = indoc! {r#"
@num
7f83f7bd LEAF 42
7f83f7bd LEAF 42
"#}.trim();
assert_actual_expected!(
actual,
expected,
"`cbor` pattern should capture and format dcbor captures correctly"
);
}
#[test]
fn test_dcbor_capture_with_search() {
let envelope = Envelope::new(vec![1, 2, 3]);
let pattern = Pattern::parse("cbor(/@values(search(number))/)").unwrap();
let (paths, captures) = pattern.paths_with_captures(&envelope);
assert_eq!(paths.len(), 3, "Should find exactly three paths");
assert_eq!(captures.len(), 1, "Should have exactly one capture group");
assert!(
captures.contains_key("values"),
"Should have 'values' capture"
);
assert_eq!(
captures["values"].len(),
3,
"Should capture three instances (one per number)"
);
let actual = format_paths_with_captures_opt(
&paths,
&captures,
FormatPathsOpts::default(),
);
#[rustfmt::skip]
let expected = indoc! {r#"
@values
4abc3113 LEAF [1, 2, 3]
4bf5122f LEAF 1
4abc3113 LEAF [1, 2, 3]
dbc1b4c9 LEAF 2
4abc3113 LEAF [1, 2, 3]
084fed08 LEAF 3
4abc3113 LEAF [1, 2, 3]
4bf5122f LEAF 1
4abc3113 LEAF [1, 2, 3]
dbc1b4c9 LEAF 2
4abc3113 LEAF [1, 2, 3]
084fed08 LEAF 3
"#}
.trim();
assert_actual_expected!(
actual,
expected,
"`cbor` search pattern should capture all number matches"
);
}
#[test]
fn test_multiple_dcbor_captures() {
let envelope = Envelope::new(vec!["name", "Alice", "age", "30"]);
let pattern = Pattern::parse("cbor(/@names(search(text))/)").unwrap();
let (paths, captures) = pattern.paths_with_captures(&envelope);
assert_eq!(
paths.len(),
4,
"Should find exactly four paths (one per text string)"
);
assert_eq!(captures.len(), 1, "Should have exactly one capture group");
assert!(
captures.contains_key("names"),
"Should have 'names' capture"
);
assert_eq!(
captures["names"].len(),
4,
"Should capture four instances (one per text string)"
);
let actual = format_paths_with_captures_opt(
&paths,
&captures,
FormatPathsOpts::default(),
);
#[rustfmt::skip]
let expected = indoc! {r#"
@names
ce1042d4 LEAF ["name", "Alice", "age", "30"]
800a0588 LEAF "name"
ce1042d4 LEAF ["name", "Alice", "age", "30"]
13941b48 LEAF "Alice"
ce1042d4 LEAF ["name", "Alice", "age", "30"]
5943be12 LEAF "age"
ce1042d4 LEAF ["name", "Alice", "age", "30"]
08e52634 LEAF "30"
ce1042d4 LEAF ["name", "Alice", "age", "30"]
800a0588 LEAF "name"
ce1042d4 LEAF ["name", "Alice", "age", "30"]
13941b48 LEAF "Alice"
ce1042d4 LEAF ["name", "Alice", "age", "30"]
5943be12 LEAF "age"
ce1042d4 LEAF ["name", "Alice", "age", "30"]
08e52634 LEAF "30"
"#}
.trim();
assert_actual_expected!(
actual,
expected,
"`cbor` pattern should capture all text values found"
);
}
#[test]
fn test_nested_dcbor_captures() {
let envelope = Envelope::new(vec![vec!["Alice", "95"], vec!["Bob", "85"]]);
let pattern =
Pattern::parse("cbor(/@users(search([@name(text), @score(text)]))/)")
.unwrap();
let (paths, captures) = pattern.paths_with_captures(&envelope);
assert_eq!(
paths.len(),
2,
"Should find exactly two paths (one per nested array)"
);
assert_eq!(
captures.len(),
3,
"Should have exactly three capture groups"
);
assert!(
captures.contains_key("users"),
"Should have 'users' capture"
);
assert!(captures.contains_key("name"), "Should have 'name' capture");
assert!(
captures.contains_key("score"),
"Should have 'score' capture"
);
assert_eq!(
captures["users"].len(),
2,
"Should capture two user instances (one per nested array)"
);
assert_eq!(
captures["name"].len(),
2,
"Should capture two name instances (one per nested array)"
);
assert_eq!(
captures["score"].len(),
2,
"Should capture two score instances (one per nested array)"
);
let actual = format_paths_with_captures_opt(
&paths,
&captures,
FormatPathsOpts::default(),
);
#[rustfmt::skip]
let expected = indoc! {r#"
@name
7dfc2858 LEAF [["Alice", "95"], ["Bob", "85"]]
6daf5539 LEAF ["Alice", "95"]
7dfc2858 LEAF [["Alice", "95"], ["Bob", "85"]]
43a6ef66 LEAF ["Bob", "85"]
@score
7dfc2858 LEAF [["Alice", "95"], ["Bob", "85"]]
6daf5539 LEAF ["Alice", "95"]
7dfc2858 LEAF [["Alice", "95"], ["Bob", "85"]]
43a6ef66 LEAF ["Bob", "85"]
@users
7dfc2858 LEAF [["Alice", "95"], ["Bob", "85"]]
6daf5539 LEAF ["Alice", "95"]
7dfc2858 LEAF [["Alice", "95"], ["Bob", "85"]]
43a6ef66 LEAF ["Bob", "85"]
7dfc2858 LEAF [["Alice", "95"], ["Bob", "85"]]
6daf5539 LEAF ["Alice", "95"]
7dfc2858 LEAF [["Alice", "95"], ["Bob", "85"]]
43a6ef66 LEAF ["Bob", "85"]
"#}
.trim();
assert_actual_expected!(
actual,
expected,
"`cbor` pattern should handle nested captures correctly"
);
}
#[test]
fn test_mixed_envelope_and_dcbor_captures() {
let envelope = Envelope::new(42);
let cbor_pattern = Pattern::parse("cbor(/@dcbor_level(42)/)").unwrap();
let pattern = Pattern::capture("envelope_level", cbor_pattern);
let (paths, captures) = pattern.paths_with_captures(&envelope);
assert_eq!(paths.len(), 1, "Should find exactly one path");
assert_eq!(captures.len(), 2, "Should have exactly two capture groups");
assert!(
captures.contains_key("envelope_level"),
"Should have 'envelope_level' capture"
);
assert!(
captures.contains_key("dcbor_level"),
"Should have 'dcbor_level' capture"
);
assert_eq!(
captures["envelope_level"].len(),
1,
"Should capture one envelope instance"
);
assert_eq!(
captures["dcbor_level"].len(),
1,
"Should capture one dcbor instance"
);
let actual = format_paths_with_captures_opt(
&paths,
&captures,
FormatPathsOpts::default(),
);
#[rustfmt::skip]
let expected = indoc! {r#"
@dcbor_level
7f83f7bd LEAF 42
@envelope_level
7f83f7bd LEAF 42
7f83f7bd LEAF 42
"#}
.trim();
assert_actual_expected!(
actual,
expected,
"Pattern should capture at both envelope and dcbor levels"
);
}
#[test]
fn test_capture_name_conflicts() {
let envelope = Envelope::new(42);
let cbor_pattern = Pattern::parse("cbor(/@same_name(42)/)").unwrap();
let pattern = Pattern::capture("same_name", cbor_pattern);
let (paths, captures) = pattern.paths_with_captures(&envelope);
assert_eq!(paths.len(), 1, "Should find exactly one path");
assert_eq!(captures.len(), 1, "Should have exactly one capture group");
assert!(
captures.contains_key("same_name"),
"Should have 'same_name' capture"
);
assert_eq!(
captures["same_name"].len(),
2,
"Should capture from both levels"
);
let actual = format_paths_with_captures_opt(
&paths,
&captures,
FormatPathsOpts::default(),
);
#[rustfmt::skip]
let expected = indoc! {r#"
@same_name
7f83f7bd LEAF 42
7f83f7bd LEAF 42
7f83f7bd LEAF 42
"#}
.trim();
assert_actual_expected!(
actual,
expected,
"Pattern should merge captures with same name from different levels"
);
}
#[test]
fn test_array_traversal_captures() {
let envelope = Envelope::new(vec!["hello", "42", "world", "123"]);
let pattern = Pattern::parse("cbor(/@text(search(text))/)").unwrap();
let (paths, captures) = pattern.paths_with_captures(&envelope);
assert_eq!(
paths.len(),
4,
"Should find exactly four paths (one per text element)"
);
assert_eq!(captures.len(), 1, "Should have exactly one capture group");
assert!(captures.contains_key("text"), "Should have 'text' capture");
assert_eq!(
captures["text"].len(),
4,
"Should capture four instances (one per text element)"
);
let actual = format_paths_with_captures_opt(
&paths,
&captures,
FormatPathsOpts::default(),
);
#[rustfmt::skip]
let expected = indoc! {r#"
@text
162867a4 LEAF ["hello", "42", "world", "123"]
cb835593 LEAF "hello"
162867a4 LEAF ["hello", "42", "world", "123"]
9fa6eb00 LEAF "42"
162867a4 LEAF ["hello", "42", "world", "123"]
29651e19 LEAF "world"
162867a4 LEAF ["hello", "42", "world", "123"]
9bf5bb3e LEAF "123"
162867a4 LEAF ["hello", "42", "world", "123"]
cb835593 LEAF "hello"
162867a4 LEAF ["hello", "42", "world", "123"]
9fa6eb00 LEAF "42"
162867a4 LEAF ["hello", "42", "world", "123"]
29651e19 LEAF "world"
162867a4 LEAF ["hello", "42", "world", "123"]
9bf5bb3e LEAF "123"
"#}
.trim();
assert_actual_expected!(
actual,
expected,
"`cbor` pattern should capture all text elements via `search`"
);
}
#[test]
fn test_cbor_captures_no_match() {
let envelope = Envelope::new("hello");
let pattern = Pattern::parse("cbor(/@num(number)/)").unwrap();
let (paths, captures) = pattern.paths_with_captures(&envelope);
assert_eq!(paths.len(), 0, "Should find no paths");
assert_eq!(captures.len(), 0, "Should have no captures");
let actual = format_paths_with_captures_opt(
&paths,
&captures,
FormatPathsOpts::default(),
);
#[rustfmt::skip]
let expected = "";
assert_actual_expected!(
actual,
expected,
"`cbor` pattern with no matches should return empty formatted output"
);
}
#[test]
fn test_cbor_captures_performance() {
let numbers: Vec<i32> = (1..=3).collect(); let envelope = Envelope::new(numbers);
let pattern = Pattern::parse("cbor(/@nums(search(number))/)").unwrap();
let start = std::time::Instant::now();
let (paths, captures) = pattern.paths_with_captures(&envelope);
let duration = start.elapsed();
assert_eq!(
paths.len(),
3,
"Should find exactly three paths (one per number)"
);
assert_eq!(captures.len(), 1, "Should have exactly one capture group");
assert!(captures.contains_key("nums"), "Should have 'nums' capture");
assert_eq!(
captures["nums"].len(),
3,
"Should capture three instances (one per number)"
);
let actual = format_paths_with_captures_opt(
&paths,
&captures,
FormatPathsOpts::default(),
);
#[rustfmt::skip]
let expected = indoc! {r#"
@nums
4abc3113 LEAF [1, 2, 3]
4bf5122f LEAF 1
4abc3113 LEAF [1, 2, 3]
dbc1b4c9 LEAF 2
4abc3113 LEAF [1, 2, 3]
084fed08 LEAF 3
4abc3113 LEAF [1, 2, 3]
4bf5122f LEAF 1
4abc3113 LEAF [1, 2, 3]
dbc1b4c9 LEAF 2
4abc3113 LEAF [1, 2, 3]
084fed08 LEAF 3
"#}
.trim();
assert_actual_expected!(
actual,
expected,
"`cbor` pattern should capture all numbers with `search` behavior"
);
println!(
"✅ Performance test completed in {}ms",
duration.as_millis()
);
}
#[test]
fn test_comprehensive_cbor_captures() {
let envelope = Envelope::new(vec!["Alice", "Bob", "Charlie"]);
let cbor_pattern = Pattern::parse("cbor(/@people(search(text))/)").unwrap();
let pattern = Pattern::capture("data", cbor_pattern);
let (paths, captures) = pattern.paths_with_captures(&envelope);
assert_eq!(
paths.len(),
3,
"Should find exactly three paths (one per person)"
);
assert_eq!(captures.len(), 2, "Should have exactly two capture groups");
assert!(captures.contains_key("data"), "Should have 'data' capture");
assert!(
captures.contains_key("people"),
"Should have 'people' capture"
);
assert_eq!(
captures["data"].len(),
3,
"Should capture three data instances (one per path)"
);
assert_eq!(
captures["people"].len(),
3,
"Should capture three people instances (one per person)"
);
let actual = format_paths_with_captures_opt(
&paths,
&captures,
FormatPathsOpts::default(),
);
#[rustfmt::skip]
let expected = indoc! {r#"
@data
aea55aad LEAF ["Alice", "Bob", "Charlie"]
13941b48 LEAF "Alice"
aea55aad LEAF ["Alice", "Bob", "Charlie"]
13b74194 LEAF "Bob"
aea55aad LEAF ["Alice", "Bob", "Charlie"]
ee8e3b02 LEAF "Charlie"
@people
aea55aad LEAF ["Alice", "Bob", "Charlie"]
13941b48 LEAF "Alice"
aea55aad LEAF ["Alice", "Bob", "Charlie"]
13b74194 LEAF "Bob"
aea55aad LEAF ["Alice", "Bob", "Charlie"]
ee8e3b02 LEAF "Charlie"
aea55aad LEAF ["Alice", "Bob", "Charlie"]
13941b48 LEAF "Alice"
aea55aad LEAF ["Alice", "Bob", "Charlie"]
13b74194 LEAF "Bob"
aea55aad LEAF ["Alice", "Bob", "Charlie"]
ee8e3b02 LEAF "Charlie"
"#}
.trim();
assert_actual_expected!(
actual,
expected,
"Comprehensive `cbor` pattern should capture via `search` at multiple levels"
);
}