1use relay_pattern::{Gas, Pattern};
4
5use crate::RelayStr;
6
7pub struct RelayPattern;
9
10pub struct RelayPatterns;
12
13#[unsafe(no_mangle)]
15#[relay_ffi::catch_unwind]
16pub unsafe extern "C" fn relay_pattern_new(
17 pattern: *const RelayStr,
18 case_insensitive: bool,
19 max_complexity: u64,
20) -> *mut RelayPattern {
21 let pattern = Pattern::builder(unsafe { (*pattern).as_str() })
22 .case_insensitive(case_insensitive)
23 .max_complexity(max_complexity)
24 .build()?;
25 Box::into_raw(Box::new(pattern)) as *mut RelayPattern
26}
27
28#[unsafe(no_mangle)]
30#[relay_ffi::catch_unwind]
31pub unsafe extern "C" fn relay_pattern_is_match(
32 pattern: *const RelayPattern,
33 value: *const RelayStr,
34 gas: u64,
35) -> bool {
36 let pattern = unsafe { &*(pattern as *const Pattern) };
37 pattern.is_match(unsafe { (*value).as_str() }, Gas::new(gas))?
38}
39
40#[unsafe(no_mangle)]
44#[relay_ffi::catch_unwind]
45pub unsafe extern "C" fn relay_pattern_to_string(pattern: *const RelayPattern) -> RelayStr {
46 let pattern = unsafe { &*(pattern as *const Pattern) };
47 RelayStr::from_string(pattern.to_string())
48}
49
50#[unsafe(no_mangle)]
52#[relay_ffi::catch_unwind]
53pub unsafe extern "C" fn relay_pattern_free(pattern: *mut RelayPattern) {
54 if !pattern.is_null() {
55 drop(unsafe { Box::from_raw(pattern as *mut Pattern) });
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use crate::{
62 RelayErrorCode, relay_err_clear, relay_err_get_last_code, relay_err_get_last_message,
63 };
64
65 use super::*;
66
67 macro_rules! test_pattern {
68 ($pattern:expr, $haystack:expr, $is_match:expr) => {{
69 test_pattern!($pattern, $haystack, $is_match, i:false)
70 }};
71 ($pattern:expr, $haystack:expr, $is_match:expr, i:$case_insensitive:expr) => {{
72 let pattern = unsafe { relay_pattern_new(&RelayStr::new($pattern), $case_insensitive, u64::MAX) };
73 assert!(!pattern.is_null());
75 assert_eq!(
76 unsafe { relay_pattern_is_match(pattern, &RelayStr::new($haystack), u64::MAX) },
77 $is_match,
78 );
79 unsafe { relay_pattern_free(pattern) };
80 }};
81 }
82
83 #[test]
84 fn test_pattern_case_sensitive() {
85 test_pattern!("*.{js,py}", "src/hello.py", true);
86 test_pattern!("*.{js,py}", "src/hello.rs", false);
87 test_pattern!("*.py", "hello.py.bak", false);
88 test_pattern!("h?llo", "héllo", true);
89 test_pattern!("[a-z]*", "hello", true);
90 test_pattern!("[a-z]*", "123", false);
91 test_pattern!("*", "hello\nworld", true);
92 test_pattern!("", "", false);
93 test_pattern!("*", "", true);
94 }
95
96 #[test]
97 fn test_pattern_case_insensitive() {
98 test_pattern!("*.{js,PY}", "src/hello.py", true, i:true);
99 test_pattern!("*.{js,PY}", "src/hello.PY", true, i:true);
100 test_pattern!("*.{js,PY}", "src/hello.JS", true, i:true);
101 test_pattern!("*.{js,PY}", "src/hello.js", true, i:true);
102 test_pattern!("", "", false, i:true);
103 test_pattern!("*", "", true, i:true);
104 }
105
106 #[test]
107 fn test_pattern_to_string() {
108 let pattern = unsafe { relay_pattern_new(&RelayStr::new("Foo**"), true, u64::MAX) };
109 let mut formatted = unsafe { relay_pattern_to_string(pattern) };
110 unsafe { relay_pattern_free(pattern) };
111
112 assert_eq!(unsafe { formatted.as_str() }, "foo*");
113 unsafe { formatted.free() };
114 }
115
116 #[test]
117 fn test_invalid_patterns() {
118 relay_err_clear();
119
120 let result = unsafe { relay_pattern_new(&RelayStr::new("["), false, u64::MAX) };
121 assert!(result.is_null());
122 assert!(matches!(
123 relay_err_get_last_code(),
124 RelayErrorCode::PatternError
125 ));
126
127 let mut message = relay_err_get_last_message();
128 assert!(unsafe { message.as_str() }.contains("Unbalanced character class"));
129 unsafe { message.free() };
130 relay_err_clear();
131 }
132}