use std::{
ops::{ControlFlow, Range},
sync::Arc,
};
use javelin_sys as sys;
use crate::{Error, ffi::Code};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Match<'s> {
subject: &'s [u8],
start: usize,
end: usize,
}
impl<'s> Match<'s> {
pub(crate) fn new(subject: &'s [u8], start: usize, end: usize) -> Self {
assert!(
start <= end && end <= subject.len(),
"invalid native match range"
);
Self {
subject,
start,
end,
}
}
pub fn start(&self) -> usize {
self.start
}
pub fn end(&self) -> usize {
self.end
}
pub fn range(&self) -> Range<usize> {
self.start..self.end
}
pub fn as_bytes(&self) -> &'s [u8] {
&self.subject[self.start..self.end]
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Captures<'s> {
pub(crate) subject: &'s [u8],
pub(crate) ranges: Vec<Option<(usize, usize)>>,
}
impl<'s> Captures<'s> {
pub fn get(&self, index: usize) -> Option<Match<'s>> {
let (start, end) = self.ranges.get(index).copied().flatten()?;
Some(Match::new(self.subject, start, end))
}
pub fn len(&self) -> usize {
self.ranges.len()
}
pub fn is_empty(&self) -> bool {
self.ranges.is_empty()
}
pub fn iter(&self) -> impl ExactSizeIterator<Item = Option<Match<'s>>> + '_ {
(0..self.len()).map(|index| self.get(index))
}
}
#[derive(Clone, Debug, Default)]
pub struct RegexBuilder {
options: i32,
}
impl RegexBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn build(&self, pattern: &str) -> Result<Regex, Error> {
Ok(Regex {
code: Arc::new(Code::new(pattern, self.options)?),
pattern: Arc::from(pattern),
})
}
fn flag(&mut self, flag: i32, enabled: bool) -> &mut Self {
if enabled {
self.options |= flag;
} else {
self.options &= !flag;
}
self
}
pub fn caseless(&mut self, yes: bool) -> &mut Self {
self.flag(sys::JP_OPTION_IGNORE_CASE, yes)
}
pub fn multi_line(&mut self, yes: bool) -> &mut Self {
self.flag(sys::JP_OPTION_MULTILINE, yes)
}
pub fn dotall(&mut self, yes: bool) -> &mut Self {
self.flag(sys::JP_OPTION_DOTALL, yes)
}
pub fn utf(&mut self, yes: bool) -> &mut Self {
self.flag(sys::JP_OPTION_UTF8, yes)
}
pub fn ucp(&mut self, yes: bool) -> &mut Self {
self.flag(sys::JP_OPTION_UCP, yes)
}
pub fn unicode_case(&mut self, yes: bool) -> &mut Self {
self.flag(sys::JP_OPTION_UNICODE_CASE, yes)
}
pub fn ungreedy(&mut self, yes: bool) -> &mut Self {
self.flag(sys::JP_OPTION_UNGREEDY, yes)
}
pub fn anchored(&mut self, yes: bool) -> &mut Self {
self.flag(sys::JP_OPTION_ANCHORED, yes)
}
pub fn glob(&mut self, yes: bool) -> &mut Self {
self.flag(sys::JP_OPTION_GLOB_SYNTAX, yes)
}
}
#[derive(Clone, Debug)]
pub struct Regex {
code: Arc<Code>,
pattern: Arc<str>,
}
impl Regex {
pub fn new(pattern: &str) -> Result<Self, Error> {
RegexBuilder::new().build(pattern)
}
pub fn as_str(&self) -> &str {
&self.pattern
}
pub fn captures_len(&self) -> usize {
self.code.capture_count
}
pub fn is_match(&self, subject: &[u8]) -> bool {
self.is_match_at(subject, 0)
}
pub fn is_match_at(&self, subject: &[u8], start: usize) -> bool {
self.code.is_match(subject, start)
}
pub fn is_full_match(&self, subject: &[u8]) -> bool {
self.code.is_full_match(subject)
}
pub fn find<'s>(&self, subject: &'s [u8]) -> Option<Match<'s>> {
self.find_at(subject, 0)
}
pub fn find_at<'s>(&self, subject: &'s [u8], start: usize) -> Option<Match<'s>> {
match self.scan_at(subject, start, ControlFlow::Break) {
ControlFlow::Break(matched) => Some(matched),
ControlFlow::Continue(()) => None,
}
}
pub fn captures<'s>(&self, subject: &'s [u8]) -> Option<Captures<'s>> {
self.captures_at(subject, 0)
}
pub fn captures_at<'s>(&self, subject: &'s [u8], start: usize) -> Option<Captures<'s>> {
self.code.captures(subject, start)
}
pub fn scan<'s, F, B>(&self, subject: &'s [u8], callback: F) -> ControlFlow<B>
where
F: FnMut(Match<'s>) -> ControlFlow<B>,
{
self.scan_at(subject, 0, callback)
}
pub fn scan_at<'s, F, B>(&self, subject: &'s [u8], start: usize, callback: F) -> ControlFlow<B>
where
F: FnMut(Match<'s>) -> ControlFlow<B>,
{
self.code.scan(subject, start, callback)
}
pub fn scan_captures<'s, F, B>(&self, subject: &'s [u8], callback: F) -> ControlFlow<B>
where
F: FnMut(Captures<'s>) -> ControlFlow<B>,
{
self.scan_captures_at(subject, 0, callback)
}
pub fn scan_captures_at<'s, F, B>(
&self,
subject: &'s [u8],
start: usize,
callback: F,
) -> ControlFlow<B>
where
F: FnMut(Captures<'s>) -> ControlFlow<B>,
{
self.code.scan_captures(subject, start, callback)
}
}