pub mod engine;
pub mod parser;
pub mod matcher;
pub mod compat;
pub use engine::{Nfa, NfaEngine, EngineError};
pub use parser::{Pattern, PatternParser, PatternError};
pub use matcher::{Match, MatchResult, Matcher};
pub use compat::{LightRegex, RegexBuilder, Error as RegexError};
pub type Result<T> = std::result::Result<T, RegexError>;
#[derive(Debug, Clone)]
pub struct Regex {
engine: NfaEngine,
pattern: String,
}
impl Regex {
pub fn new(pattern: &str) -> Result<Self> {
let parsed = PatternParser::new(pattern).parse()?;
let engine = NfaEngine::from_pattern(&parsed)?;
Ok(Self {
engine,
pattern: pattern.to_string(),
})
}
pub fn is_match(&self, text: &str) -> bool {
let mut matcher = Matcher::new(&self.engine);
matcher.find(text).is_some()
}
pub fn find(&self, text: &str) -> Option<Match> {
let mut matcher = Matcher::new(&self.engine);
matcher.find(text)
}
pub fn find_iter<'t>(&'t self, text: &'t str) -> impl Iterator<Item = Match> + 't {
FindIter::new(&self.engine, text)
}
pub fn as_str(&self) -> &str {
&self.pattern
}
}
struct FindIter<'t> {
matcher: Matcher<'t>,
text: &'t str,
pos: usize,
}
impl<'t> FindIter<'t> {
fn new(engine: &'t NfaEngine, text: &'t str) -> Self {
Self {
matcher: Matcher::new(engine),
text,
pos: 0,
}
}
}
impl<'t> Iterator for FindIter<'t> {
type Item = Match;
fn next(&mut self) -> Option<Self::Item> {
if self.pos >= self.text.len() {
return None;
}
let remaining = &self.text[self.pos..];
if let Some(mut m) = self.matcher.find(remaining) {
m.start += self.pos;
m.end += self.pos;
self.pos = m.end.max(self.pos + 1); Some(m)
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic_matching() {
let regex = Regex::new(r"hello").unwrap();
assert!(regex.is_match("hello world"));
assert!(!regex.is_match("goodbye world"));
}
#[test]
fn test_pattern_storage() {
let regex = Regex::new(r"\d+").unwrap();
assert_eq!(regex.as_str(), r"\d+");
}
#[test]
fn test_find_match() {
let regex = Regex::new(r"\d+").unwrap();
let m = regex.find("abc123def").unwrap();
assert_eq!(m.start, 3);
assert_eq!(m.end, 6);
assert_eq!(m.as_str(), "123");
}
#[test]
fn test_find_all() {
let regex = Regex::new(r"\d+").unwrap();
let matches: Vec<_> = regex.find_iter("a1b2c3").collect();
assert_eq!(matches.len(), 3);
assert_eq!(matches[0].as_str(), "1");
assert_eq!(matches[1].as_str(), "2");
assert_eq!(matches[2].as_str(), "3");
}
}