lune/src/lune/builtins/regex/captures.rs
2024-04-20 23:19:21 +02:00

88 lines
2.5 KiB
Rust

use std::sync::Arc;
use mlua::prelude::*;
use regex::{Captures, Regex};
use self_cell::self_cell;
use super::matches::LuaMatch;
type OptionalCaptures<'a> = Option<Captures<'a>>;
self_cell! {
struct LuaCapturesInner {
owner: Arc<String>,
#[covariant]
dependent: OptionalCaptures,
}
}
/**
A wrapper over the `regex::Captures` struct that can be used from Lua.
*/
pub struct LuaCaptures {
inner: LuaCapturesInner,
}
impl LuaCaptures {
/**
Create a new `LuaCaptures` instance from a `Regex` pattern and a `String` text.
Returns `Some(_)` if captures were found, `None` if no captures were found.
*/
pub fn new(pattern: &Regex, text: String) -> Option<Self> {
let inner =
LuaCapturesInner::new(Arc::from(text), |owned| pattern.captures(owned.as_str()));
if inner.borrow_dependent().is_some() {
Some(Self { inner })
} else {
None
}
}
fn captures(&self) -> &Captures {
self.inner
.borrow_dependent()
.as_ref()
.expect("None captures should not be used")
}
fn num_captures(&self) -> usize {
// NOTE: Here we exclude the match for the entire regex
// pattern, only counting the named and numbered captures
self.captures().len() - 1
}
fn text(&self) -> Arc<String> {
Arc::clone(self.inner.borrow_owner())
}
}
impl LuaUserData for LuaCaptures {
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("get", |_, this, index: usize| {
Ok(this
.captures()
.get(index)
.map(|m| LuaMatch::new(this.text(), m)))
});
methods.add_method("group", |_, this, group: String| {
Ok(this
.captures()
.name(&group)
.map(|m| LuaMatch::new(this.text(), m)))
});
methods.add_method("format", |_, this, format: String| {
let mut new = String::new();
this.captures().expand(&format, &mut new);
Ok(new)
});
methods.add_meta_method(LuaMetaMethod::Type, |_, _, ()| Ok("RegexCaptures"));
methods.add_meta_method(LuaMetaMethod::Len, |_, this, ()| Ok(this.num_captures()));
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| {
Ok(format!("RegexCaptures({})", this.num_captures()))
});
}
}