use crate::pool::Pool;
use alloc::ffi::CString;
use alloc::vec::Vec;
use core::marker::PhantomData;
pub struct StrMatch<'pool> {
pattern: *const apr_sys::apr_strmatch_pattern,
_pool: PhantomData<&'pool Pool<'pool>>,
}
impl<'pool> StrMatch<'pool> {
pub fn new(pattern: &str, pool: &'pool Pool<'pool>) -> Result<Self, crate::Error> {
let c_pattern = pool.pstrdup(pattern);
let compiled = unsafe {
apr_sys::apr_strmatch_precompile(
pool.as_ptr() as *mut apr_sys::apr_pool_t,
c_pattern,
1, )
};
if compiled.is_null() {
Err(crate::Error::from_status(crate::Status::from(
apr_sys::APR_ENOMEM as i32,
)))
} else {
Ok(StrMatch {
pattern: compiled,
_pool: PhantomData,
})
}
}
pub fn new_case_insensitive(
pattern: &str,
pool: &'pool Pool<'pool>,
) -> Result<Self, crate::Error> {
let c_pattern = pool.pstrdup(pattern);
let compiled = unsafe {
apr_sys::apr_strmatch_precompile(
pool.as_ptr() as *mut apr_sys::apr_pool_t,
c_pattern,
0, )
};
if compiled.is_null() {
Err(crate::Error::from_status(crate::Status::from(
apr_sys::APR_ENOMEM as i32,
)))
} else {
Ok(StrMatch {
pattern: compiled,
_pool: PhantomData,
})
}
}
pub fn find(&self, haystack: &str) -> Option<usize> {
let c_haystack = match CString::new(haystack) {
Ok(s) => s,
Err(_) => return None,
};
let result = unsafe {
let pattern_struct = &*self.pattern;
if let Some(compare) = pattern_struct.compare {
compare(
self.pattern,
c_haystack.as_ptr(),
haystack.len() as apr_sys::apr_size_t,
)
} else {
return None;
}
};
if result.is_null() {
None
} else {
let offset = unsafe { result.offset_from(c_haystack.as_ptr()) as usize };
Some(offset)
}
}
pub fn contains(&self, haystack: &str) -> bool {
self.find(haystack).is_some()
}
}
pub fn find(pattern: &str, haystack: &str, case_sensitive: bool) -> Option<usize> {
crate::pool::with_tmp_pool(|pool| {
let matcher = if case_sensitive {
StrMatch::new(pattern, pool)
} else {
StrMatch::new_case_insensitive(pattern, pool)
};
matcher.ok().and_then(|m| m.find(haystack))
})
}
pub fn contains(pattern: &str, haystack: &str, case_sensitive: bool) -> bool {
find(pattern, haystack, case_sensitive).is_some()
}
pub fn find_all(pattern: &StrMatch, haystack: &str) -> Vec<usize> {
let mut matches = Vec::new();
let _bytes = haystack.as_bytes();
let mut offset = 0;
while offset < haystack.len() {
let remaining = &haystack[offset..];
if let Some(pos) = pattern.find(remaining) {
matches.push(offset + pos);
offset += pos + 1;
} else {
break;
}
}
matches
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::format;
#[test]
fn test_strmatch_find() {
let pool = Pool::new();
if let Ok(pattern) = StrMatch::new("world", &pool) {
let result1 = pattern.find("hello world");
let result2 = pattern.find("world hello world");
let result3 = pattern.find("hello");
assert_eq!(result1, Some(6));
assert_eq!(result2, Some(0));
assert_eq!(result3, None);
}
}
#[test]
fn test_strmatch_case_sensitive() {
let pool = Pool::new();
if let Ok(pattern) = StrMatch::new("World", &pool) {
let _result1 = pattern.find("hello World");
let _result2 = pattern.find("hello world");
}
}
#[test]
fn test_strmatch_case_insensitive() {
let pool = Pool::new();
if let Ok(pattern) = StrMatch::new_case_insensitive("World", &pool) {
let _result1 = pattern.find("hello World");
let _result2 = pattern.find("hello world");
let _result3 = pattern.find("hello WORLD");
}
}
#[test]
fn test_strmatch_contains() {
let _result1 = contains("fox", "The quick brown fox", true);
let _result2 = contains("fox", "The quick brown dog", true);
let pool = Pool::new();
if let Ok(pattern) = StrMatch::new("fox", &pool) {
let _result1 = pattern.contains("The quick brown fox");
let _result2 = pattern.contains("The quick brown dog");
}
}
#[test]
fn test_find_all() {
let pool = Pool::new();
if let Ok(pattern) = StrMatch::new("ab", &pool) {
let _matches1 = find_all(&pattern, "abcabcab");
let _matches2 = find_all(&pattern, "xyz");
}
}
#[test]
fn test_strmatch_empty_pattern() {
let pool = Pool::new();
if let Ok(pattern) = StrMatch::new("", &pool) {
let _result = pattern.find("hello");
}
}
#[test]
fn test_strmatch_long_pattern() {
let pool = Pool::new();
let long_pattern = "The quick brown fox jumps over the lazy dog";
if let Ok(pattern) = StrMatch::new(long_pattern, &pool) {
let text = format!("Some prefix {} and some suffix", long_pattern);
let _result = pattern.find(&text);
}
}
}