mod common;
use dcbor::prelude::*;
use dcbor_parse::parse_dcbor_item;
use dcbor_pattern::{MapPattern, Matcher, Pattern, format_paths};
fn cbor(s: &str) -> CBOR { parse_dcbor_item(s).unwrap() }
#[test]
fn test_map_patterns_with_real_cbor() {
let empty_map = cbor("{}");
let single_item = cbor(r#"{"key": "hello"}"#);
let three_items = cbor(r#"{"a": 1, "b": 2, "c": 3}"#);
let large_map = cbor(
r#"{0: "item0", 1: "item1", 2: "item2", 3: "item3", 4: "item4", 5: "item5", 6: "item6", 7: "item7", 8: "item8", 9: "item9"}"#,
);
let any_map = Pattern::parse("map").unwrap();
let paths = any_map.paths(&empty_map);
let expected = "{}";
assert_actual_expected!(format_paths(&paths), expected);
let paths = any_map.paths(&single_item);
let expected = r#"{"key": "hello"}"#;
assert_actual_expected!(format_paths(&paths), expected);
let paths = any_map.paths(&three_items);
let expected = r#"{"a": 1, "b": 2, "c": 3}"#;
assert_actual_expected!(format_paths(&paths), expected);
let paths = any_map.paths(&large_map);
let expected = r#"{0: "item0", 1: "item1", 2: "item2", 3: "item3", 4: "item4", 5: "item5", 6: "item6", 7: "item7", 8: "item8", 9: "item9"}"#;
assert_actual_expected!(format_paths(&paths), expected);
assert!(!any_map.matches(&1.to_cbor()));
let empty_pattern = Pattern::parse("{{0}}").unwrap();
let paths = empty_pattern.paths(&empty_map);
let expected = "{}";
assert_actual_expected!(format_paths(&paths), expected);
assert!(!empty_pattern.matches(&single_item));
assert!(!empty_pattern.matches(&three_items));
let single_pattern = Pattern::parse("{{1}}").unwrap();
let paths = single_pattern.paths(&single_item);
let expected = r#"{"key": "hello"}"#;
assert_actual_expected!(format_paths(&paths), expected);
assert!(!single_pattern.matches(&empty_map));
assert!(!single_pattern.matches(&three_items));
let three_pattern = Pattern::parse("{{3}}").unwrap();
let paths = three_pattern.paths(&three_items);
let expected = r#"{"a": 1, "b": 2, "c": 3}"#;
assert_actual_expected!(format_paths(&paths), expected);
assert!(!three_pattern.matches(&empty_map));
assert!(!three_pattern.matches(&single_item));
assert!(!three_pattern.matches(&large_map));
let range_pattern = Pattern::parse("{{5,15}}").unwrap();
let paths = range_pattern.paths(&large_map);
let expected = r#"{0: "item0", 1: "item1", 2: "item2", 3: "item3", 4: "item4", 5: "item5", 6: "item6", 7: "item7", 8: "item8", 9: "item9"}"#;
assert_actual_expected!(format_paths(&paths), expected);
assert!(!range_pattern.matches(&empty_map));
assert!(!range_pattern.matches(&single_item));
assert!(!range_pattern.matches(&three_items));
let min_pattern = Pattern::parse("{{5,}}").unwrap();
let paths = min_pattern.paths(&large_map);
let expected = r#"{0: "item0", 1: "item1", 2: "item2", 3: "item3", 4: "item4", 5: "item5", 6: "item6", 7: "item7", 8: "item8", 9: "item9"}"#;
assert_actual_expected!(format_paths(&paths), expected);
assert!(!min_pattern.matches(&empty_map));
assert!(!min_pattern.matches(&single_item));
assert!(!min_pattern.matches(&three_items));
}
#[test]
fn test_map_pattern_display() {
assert_eq!(MapPattern::any().to_string(), "map");
assert_eq!(MapPattern::with_length(0).to_string(), "{{0}}");
assert_eq!(MapPattern::with_length(5).to_string(), "{{5}}");
assert_eq!(MapPattern::with_length_range(2..=8).to_string(), "{{2,8}}");
assert_eq!(MapPattern::with_length_range(3..).to_string(), "{{3,}}");
}
#[test]
fn test_map_pattern_round_trip() {
let patterns = ["map", "{{0}}", "{{1}}", "{{5}}", "{{2,8}}", "{{3,}}"];
let expected_displays =
["map", "{{0}}", "{{1}}", "{{5}}", "{{2,8}}", "{{3,}}"];
for (i, pattern_str) in patterns.iter().enumerate() {
let pattern = Pattern::parse(pattern_str).unwrap();
assert_eq!(pattern.to_string(), expected_displays[i]);
}
}
#[test]
fn test_map_pattern_paths() {
let test_map = cbor(r#"{"key1": "value1", "key2": 42}"#);
let any_map = MapPattern::any();
let paths = any_map.paths(&test_map);
let expected = r#"{"key1": "value1", "key2": 42}"#;
assert_actual_expected!(format_paths(&paths), expected);
let not_map = cbor(r#""not a map""#);
let paths = any_map.paths(¬_map);
assert_eq!(paths.len(), 0);
let exact_pattern = MapPattern::with_length(2);
let paths = exact_pattern.paths(&test_map);
let expected = r#"{"key1": "value1", "key2": 42}"#;
assert_actual_expected!(format_paths(&paths), expected);
let wrong_length = MapPattern::with_length(3);
let paths = wrong_length.paths(&test_map);
assert_eq!(paths.len(), 0);
}
#[test]
fn test_map_key_value_constraints_single() {
let test_map = cbor(r#"{"name": "Alice", "age": 30, "city": "New York"}"#);
let pattern = MapPattern::with_key_value_constraints(vec![(
Pattern::text("name"),
Pattern::any_text(),
)]);
let paths = pattern.paths(&test_map);
let expected = r#"{"age": 30, "city": "New York", "name": "Alice"}"#;
assert_actual_expected!(format_paths(&paths), expected);
let non_matching = MapPattern::with_key_value_constraints(vec![
(Pattern::text("name"), Pattern::any_number()),
]);
let paths = non_matching.paths(&test_map);
assert_eq!(paths.len(), 0);
}
#[test]
fn test_map_key_value_constraints_multiple() {
let test_map = cbor(r#"{"name": "Bob", "age": 25, "active": true}"#);
let pattern = MapPattern::with_key_value_constraints(vec![
(Pattern::text("name"), Pattern::any_text()),
(Pattern::text("age"), Pattern::any_number()),
(Pattern::text("active"), Pattern::any_bool()),
]);
let paths = pattern.paths(&test_map);
let expected = r#"{"age": 25, "name": "Bob", "active": true}"#;
assert_actual_expected!(format_paths(&paths), expected);
let partial_pattern = MapPattern::with_key_value_constraints(vec![
(Pattern::text("name"), Pattern::any_text()), (Pattern::text("age"), Pattern::any_text()),
(Pattern::text("active"), Pattern::any_bool()), ]);
let paths = partial_pattern.paths(&test_map);
assert_eq!(paths.len(), 0);
}
#[test]
fn test_map_key_value_constraints_any_key() {
let test_map = cbor(r#"{"key1": "hello", "key2": "world", "key3": 42}"#);
let pattern = MapPattern::with_key_value_constraints(vec![(
Pattern::any(),
Pattern::any_text(),
)]);
let paths = pattern.paths(&test_map);
let expected = r#"{"key1": "hello", "key2": "world", "key3": 42}"#;
assert_actual_expected!(format_paths(&paths), expected);
let number_pattern = MapPattern::with_key_value_constraints(vec![(
Pattern::any(),
Pattern::any_number(),
)]);
let paths = number_pattern.paths(&test_map);
assert_actual_expected!(format_paths(&paths), expected);
}
#[test]
fn test_map_key_value_constraints_specific_values() {
let test_map = cbor(r#"{"status": "active", "count": 42, "flag": true}"#);
let pattern = MapPattern::with_key_value_constraints(vec![
(Pattern::text("status"), Pattern::text("active")),
(Pattern::text("count"), Pattern::number(42.0)),
]);
let paths = pattern.paths(&test_map);
let expected = r#"{"flag": true, "count": 42, "status": "active"}"#;
assert_actual_expected!(format_paths(&paths), expected);
let wrong_values = MapPattern::with_key_value_constraints(vec![
(Pattern::text("status"), Pattern::text("inactive")), (Pattern::text("count"), Pattern::number(42.0)), ]);
let paths = wrong_values.paths(&test_map);
assert_eq!(paths.len(), 0);
}
#[test]
fn test_map_key_value_constraints_empty_map() {
let empty_map = cbor("{}");
let pattern = MapPattern::with_key_value_constraints(vec![(
Pattern::any(),
Pattern::any(),
)]);
let paths = pattern.paths(&empty_map);
assert_eq!(paths.len(), 0);
let multi_pattern = MapPattern::with_key_value_constraints(vec![
(Pattern::text("key1"), Pattern::any()),
(Pattern::text("key2"), Pattern::any()),
]);
let paths = multi_pattern.paths(&empty_map);
assert_eq!(paths.len(), 0);
}
#[test]
fn test_map_key_value_constraints_pattern_text_parsing() {
let pattern = Pattern::parse(r#"{"name":text, "age":number}"#).unwrap();
let matching_map =
cbor(r#"{"name": "Charlie", "age": 28, "extra": "data"}"#);
assert!(pattern.matches(&matching_map));
let non_matching_map = cbor(r#"{"name": 123, "age": 28}"#); assert!(!pattern.matches(&non_matching_map));
let missing_key_map = cbor(r#"{"name": "Charlie"}"#); assert!(!pattern.matches(&missing_key_map));
assert_eq!(pattern.to_string(), r#"{"name": text, "age": number}"#);
}
#[test]
fn test_map_key_value_constraints_complex_patterns() {
let pattern = Pattern::parse(r#"{*:"target", 42:true}"#).unwrap();
let matching_map =
cbor(r#"{"somekey": "target", 42: true, "other": "data"}"#);
assert!(pattern.matches(&matching_map));
let partial_match = cbor(r#"{"somekey": "target", 42: false}"#); assert!(!pattern.matches(&partial_match));
let no_match = cbor(r#"{"somekey": "other", 42: true}"#); assert!(!pattern.matches(&no_match));
}