use smallvec::SmallVec;
use std::collections::HashMap;
use std::sync::{Arc, OnceLock};
type SlotVec = SmallVec<[Option<(usize, usize)>; 8]>;
static EMPTY_NAMES: OnceLock<Arc<HashMap<String, usize>>> = OnceLock::new();
fn empty_names() -> Arc<HashMap<String, usize>> {
EMPTY_NAMES.get_or_init(|| Arc::new(HashMap::new())).clone()
}
#[derive(Debug, Clone)]
pub struct CaptureState {
slots: SlotVec,
names: Arc<HashMap<String, usize>>,
}
impl CaptureState {
#[must_use]
pub fn new(group_count: usize) -> Self {
let mut slots = SlotVec::new();
slots.resize(group_count + 1, None); CaptureState {
slots,
names: empty_names(),
}
}
pub fn register_name(&mut self, name: String, index: usize) {
Arc::make_mut(&mut self.names).insert(name, index);
}
pub fn start_capture(&mut self, group: usize, pos: usize) {
if group < self.slots.len() {
self.slots[group] = Some((pos, pos));
}
}
pub fn end_capture(&mut self, group: usize, pos: usize) {
if group < self.slots.len()
&& let Some((start, _)) = self.slots[group]
{
self.slots[group] = Some((start, pos));
}
}
#[must_use]
pub fn get(&self, index: usize) -> Option<(usize, usize)> {
self.slots.get(index).copied().flatten()
}
#[must_use]
pub fn get_named(&self, name: &str) -> Option<(usize, usize)> {
self.names.get(name).and_then(|&idx| self.get(idx))
}
#[must_use]
pub fn get_text<'a>(&self, index: usize, text: &'a str) -> Option<&'a str> {
self.get(index).map(|(start, end)| &text[start..end])
}
pub fn set_full_match(&mut self, start: usize, end: usize) {
self.slots[0] = Some((start, end));
}
#[must_use]
pub fn slots(&self) -> &[Option<(usize, usize)>] {
&self.slots
}
#[must_use]
pub fn names(&self) -> &HashMap<String, usize> {
&self.names
}
pub fn clear(&mut self) {
for slot in &mut self.slots {
*slot = None;
}
}
#[must_use]
pub fn with_offset(&self, offset: usize) -> Self {
CaptureState {
slots: self
.slots
.iter()
.map(|s| s.map(|(start, end)| (start + offset, end + offset)))
.collect(),
names: Arc::clone(&self.names),
}
}
}
pub struct CaptureStateBuilder {
group_count: usize,
names: HashMap<String, usize>,
}
impl CaptureStateBuilder {
#[must_use]
pub fn new(group_count: usize) -> Self {
CaptureStateBuilder {
group_count,
names: HashMap::new(),
}
}
#[must_use]
pub fn with_name(mut self, name: String, index: usize) -> Self {
self.names.insert(name, index);
self
}
#[must_use]
pub fn build(self) -> CaptureState {
let mut slots = SlotVec::new();
slots.resize(self.group_count + 1, None);
CaptureState {
slots,
names: Arc::new(self.names),
}
}
}