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