mirror of
https://github.com/lune-org/lune.git
synced 2024-12-12 13:00:37 +00:00
Port over regex implementation from fork, do some cleanup
This commit is contained in:
parent
e11302766b
commit
12f5824da9
6 changed files with 215 additions and 7 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -1522,6 +1522,7 @@ dependencies = [
|
|||
"regex",
|
||||
"reqwest",
|
||||
"rustyline",
|
||||
"self_cell",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
|
@ -2423,6 +2424,12 @@ dependencies = [
|
|||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "self_cell"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
|
|
|
@ -24,7 +24,6 @@ cli = [
|
|||
"dep:env_logger",
|
||||
"dep:clap",
|
||||
"dep:include_dir",
|
||||
"dep:regex",
|
||||
"dep:rustyline",
|
||||
"dep:zip_next",
|
||||
]
|
||||
|
@ -76,7 +75,9 @@ path-clean = "1.0"
|
|||
pathdiff = "0.2"
|
||||
pin-project = "1.0"
|
||||
urlencoding = "2.1"
|
||||
bstr = "1.9.1"
|
||||
bstr = "1.9"
|
||||
regex = "1.10"
|
||||
self_cell = "1.0"
|
||||
|
||||
### RUNTIME
|
||||
|
||||
|
@ -133,10 +134,6 @@ env_logger = { optional = true, version = "0.11" }
|
|||
itertools = "0.12"
|
||||
clap = { optional = true, version = "4.1", features = ["derive"] }
|
||||
include_dir = { optional = true, version = "0.7", features = ["glob"] }
|
||||
regex = { optional = true, version = "1.7", default-features = false, features = [
|
||||
"std",
|
||||
"unicode-perl",
|
||||
] }
|
||||
rustyline = { optional = true, version = "14.0" }
|
||||
zip_next = { optional = true, version = "1.1" }
|
||||
|
||||
|
|
76
src/lune/builtins/regex/captures.rs
Normal file
76
src/lune/builtins/regex/captures.rs
Normal file
|
@ -0,0 +1,76 @@
|
|||
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,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LuaCaptures {
|
||||
inner: LuaCapturesInner,
|
||||
}
|
||||
|
||||
impl LuaCaptures {
|
||||
pub fn new(pattern: &Regex, text: String) -> Self {
|
||||
Self {
|
||||
inner: LuaCapturesInner::new(Arc::from(text), |owned| pattern.captures(owned.as_str())),
|
||||
}
|
||||
}
|
||||
|
||||
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, n: usize| {
|
||||
Ok(this
|
||||
.captures()
|
||||
.get(n)
|
||||
.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()))
|
||||
});
|
||||
}
|
||||
}
|
48
src/lune/builtins/regex/matches.rs
Normal file
48
src/lune/builtins/regex/matches.rs
Normal file
|
@ -0,0 +1,48 @@
|
|||
use std::{ops::Range, sync::Arc};
|
||||
|
||||
use mlua::prelude::*;
|
||||
use regex::Match;
|
||||
|
||||
pub struct LuaMatch {
|
||||
text: Arc<String>,
|
||||
start: usize,
|
||||
end: usize,
|
||||
}
|
||||
|
||||
impl LuaMatch {
|
||||
pub fn new(text: Arc<String>, matched: Match) -> Self {
|
||||
Self {
|
||||
text,
|
||||
start: matched.start(),
|
||||
end: matched.end(),
|
||||
}
|
||||
}
|
||||
|
||||
fn range(&self) -> Range<usize> {
|
||||
self.start..self.end
|
||||
}
|
||||
|
||||
fn slice(&self) -> &str {
|
||||
&self.text[self.range()]
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaUserData for LuaMatch {
|
||||
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
// NOTE: Strings are 0 based in Rust but 1 based in Luau, and end of range in Rust is exclusive
|
||||
fields.add_field_method_get("start", |_, this| Ok(this.start.saturating_add(1)));
|
||||
fields.add_field_method_get("finish", |_, this| Ok(this.end));
|
||||
fields.add_field_method_get("len", |_, this| Ok(this.range().len()));
|
||||
fields.add_field_method_get("text", |_, this| Ok(this.slice().to_string()));
|
||||
}
|
||||
|
||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("isEmpty", |_, this, ()| Ok(this.range().is_empty()));
|
||||
|
||||
methods.add_meta_method(LuaMetaMethod::Type, |_, _, ()| Ok("RegexMatch"));
|
||||
methods.add_meta_method(LuaMetaMethod::Len, |_, this, ()| Ok(this.range().len()));
|
||||
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| {
|
||||
Ok(format!("RegexMatch({})", this.slice()))
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,7 +1,21 @@
|
|||
#![allow(clippy::module_inception)]
|
||||
|
||||
use mlua::prelude::*;
|
||||
|
||||
use crate::lune::util::TableBuilder;
|
||||
|
||||
mod captures;
|
||||
mod matches;
|
||||
mod regex;
|
||||
|
||||
use self::regex::LuaRegex;
|
||||
|
||||
pub fn create(lua: &Lua) -> LuaResult<LuaTable> {
|
||||
TableBuilder::new(lua)?.build_readonly()
|
||||
TableBuilder::new(lua)?
|
||||
.with_function("new", new_regex)?
|
||||
.build_readonly()
|
||||
}
|
||||
|
||||
fn new_regex(_: &Lua, pattern: String) -> LuaResult<LuaRegex> {
|
||||
LuaRegex::new(pattern)
|
||||
}
|
||||
|
|
66
src/lune/builtins/regex/regex.rs
Normal file
66
src/lune/builtins/regex/regex.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use mlua::prelude::*;
|
||||
use regex::Regex;
|
||||
|
||||
use super::{captures::LuaCaptures, matches::LuaMatch};
|
||||
|
||||
pub struct LuaRegex {
|
||||
inner: Regex,
|
||||
}
|
||||
|
||||
impl LuaRegex {
|
||||
pub fn new(pattern: String) -> LuaResult<Self> {
|
||||
Regex::new(&pattern)
|
||||
.map(|inner| Self { inner })
|
||||
.map_err(LuaError::external)
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaUserData for LuaRegex {
|
||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("isMatch", |_, this, text: String| {
|
||||
Ok(this.inner.is_match(&text))
|
||||
});
|
||||
|
||||
methods.add_method("find", |_, this, text: String| {
|
||||
let arc = Arc::new(text);
|
||||
Ok(this
|
||||
.inner
|
||||
.find(&arc)
|
||||
.map(|m| LuaMatch::new(Arc::clone(&arc), m)))
|
||||
});
|
||||
|
||||
methods.add_method("captures", |_, this, text: String| {
|
||||
Ok(LuaCaptures::new(&this.inner, text))
|
||||
});
|
||||
|
||||
methods.add_method("split", |_, this, text: String| {
|
||||
Ok(this
|
||||
.inner
|
||||
.split(&text)
|
||||
.map(|s| s.to_string())
|
||||
.collect::<Vec<_>>())
|
||||
});
|
||||
|
||||
// TODO: Determine whether it's desirable and / or feasible to support
|
||||
// using a function or table for `replace` like in the lua string library
|
||||
methods.add_method(
|
||||
"replace",
|
||||
|_, this, (haystack, replacer): (String, String)| {
|
||||
Ok(this.inner.replace(&haystack, replacer).to_string())
|
||||
},
|
||||
);
|
||||
methods.add_method(
|
||||
"replaceAll",
|
||||
|_, this, (haystack, replacer): (String, String)| {
|
||||
Ok(this.inner.replace_all(&haystack, replacer).to_string())
|
||||
},
|
||||
);
|
||||
|
||||
methods.add_meta_method(LuaMetaMethod::Type, |_, _, ()| Ok("Regex"));
|
||||
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| {
|
||||
Ok(format!("Regex({})", this.inner.as_str()))
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue