use rlua::prelude::*;use regex;pub struct LuaRegex(regex::Regex);pub struct LuaCapture<'a>(regex::Captures<'a>);impl LuaUserData for LuaRegex { fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { methods.add_method("match", |_, this: &LuaRegex, val: String| { Ok(this.0.is_match(&val)) }); methods.add_method("replace_all", |_, this: &LuaRegex, (val, patt): (String, String)| { Ok(this.0.replace_all(&val, patt.as_str()).into_owned()) }); methods.add_method("capture", |_, this: &LuaRegex, val: String| { Ok(this.0.captures(Box::leak(val.into_boxed_str())).map(LuaCapture)) }); } }impl<'a> LuaUserData for LuaCapture<'a> { fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { methods.add_method("get", |_, this: &LuaCapture, index: usize| { Ok(this.0.get(index).map(|s| s.as_str().to_string())) }); methods.add_method("name", |_, this: &LuaCapture, name: String| { Ok(this.0.name(&name).map(|s| s.as_str().to_string())) }); methods.add_method("to_table", |_, this: &LuaCapture, _: ()| { Ok(this.0.iter().filter_map(|s| s).map(|s| s.as_str().to_string()).collect::<Vec<String>>()) }) }}pub fn init(lua: &Lua) -> crate::Result<()> { let module = lua.create_table()?; module.set("new", lua.create_function(|_, expr: String| { regex::Regex::new(&expr).map(LuaRegex).map_err(LuaError::external) })?)?; //TODO: Move into method //TODO: Further investigation locally// module.set("capture_all", lua.create_function(|lua: &Lua, (expr, val): (String, String) | {// let regex = regex::Regex::new(&expr).map_err(LuaError::external)?;// let mut re = regex.captures_iter(Box::leak(val.into_boxed_str()));// let mut arc_iter = Arc::new(Some(re));// let mut f = move |_, _: ()| {// let result = match Arc::get_mut(&mut arc_iter).expect("entries iterator is mutably borrowed") {// Some(iter) => iter.next().map(LuaCapture),// None => None// };// if result.is_none() { *Arc::get_mut(&mut arc_iter).unwrap() = None; }// Ok(result)// };// Ok(lua.create_function_mut(f)?)// })?)?; lua.globals().set("regex", module)?; Ok(())}#[cfg(test)]mod tests { use super::*; #[test] fn lua_regex_replace_all () { let lua = Lua::new(); init(&lua).unwrap(); lua.exec::<_, ()>(r#" local regex = regex.new([[(?P<y>\d{4})-(?P<m>\d{2})-(?P<d>\d{2})]]) local before = "2012-03-14, 2013-01-01 and 2014-07-05" local result = regex:replace_all(before, "$m/$d/$y") assert(result == "03/14/2012, 01/01/2013 and 07/05/2014") "#, None).unwrap(); } #[test] fn lua_regex_match () { let lua = Lua::new(); init(&lua).unwrap(); lua.exec::<_, ()>(r#" local regex = regex.new([[^\d{4}-\d{2}-\d{2}$]]) local date = "2014-01-01" local result = regex:match(date) assert(result == true) "#, None).unwrap(); } #[test] fn lua_regex_captures () { let lua = Lua::new(); init(&lua).unwrap(); lua.exec::<_, ()>(r#" local regex = regex.new([['([^']+)'\s+\((\d{4})\)]]) local val = "Not my favorite movie: 'Citizen Kane' (1941)" local result = regex:capture(val):to_table() assert(result ~= nil) assert(result[1] == "'Citizen Kane' (1941)") assert(result[2] == "Citizen Kane") assert(result[3] == "1941") "#, None).unwrap(); }}