use super::{
super::{Parser, Result, engine::find_first_match},
calculate_start_index,
};
pub fn find(
text: &str,
pattern: &str,
init: Option<isize>,
plain: bool,
) -> Result<Option<(usize, usize, Vec<String>)>> {
let text_bytes = text.as_bytes();
let byte_len = text_bytes.len();
let start_byte_index = calculate_start_index(byte_len, init);
if plain {
if pattern.is_empty() {
if cfg!(feature = "1-based") {
return Ok(Some((
start_byte_index.saturating_add(1),
start_byte_index,
vec![],
)));
} else {
return Ok(Some((start_byte_index, start_byte_index, vec![])));
}
}
if start_byte_index >= byte_len {
return Ok(None);
}
if let Some(rel_byte_pos) = text_bytes[start_byte_index..]
.windows(pattern.len())
.position(|window| window == pattern.as_bytes())
{
let zero_based_start_pos = start_byte_index + rel_byte_pos;
let zero_based_end_pos = zero_based_start_pos + pattern.len();
let start_pos = if cfg!(feature = "1-based") {
zero_based_start_pos.saturating_add(1)
} else {
zero_based_start_pos
};
let end_pos = zero_based_end_pos;
Ok(Some((start_pos, end_pos, vec![])))
} else {
Ok(None)
}
} else {
let mut parser = Parser::new(pattern)?;
let ast = parser.parse()?;
match find_first_match(&ast, text_bytes, start_byte_index)? {
Some((match_byte_range, captures_byte_ranges)) => {
let start_pos = if cfg!(feature = "1-based") {
match_byte_range.start.saturating_add(1)
} else {
match_byte_range.start
};
let end_pos = match_byte_range.end;
let captured_strings: Vec<String> = captures_byte_ranges
.into_iter()
.filter_map(|maybe_range| {
maybe_range
.map(|range| String::from_utf8_lossy(&text_bytes[range]).into_owned())
})
.collect();
Ok(Some((start_pos, end_pos, captured_strings)))
}
None => Ok(None),
}
}
}