use mlua::prelude::*;
pub(super) fn register_fuzzy(_lua: &Lua, alc_table: &LuaTable) -> LuaResult<()> {
let match_enum = _lua.create_function(
|_, (text, candidates, opts): (String, Vec<String>, Option<LuaTable>)| {
let threshold = opts
.as_ref()
.and_then(|t| t.get::<f64>("threshold").ok())
.unwrap_or(0.7);
let text_lower = text.to_lowercase();
let mut best: Option<(usize, &str)> = None; for c in &candidates {
let c_lower = c.to_lowercase();
if let Some(pos) = text_lower.rfind(&c_lower) {
match best {
Some((prev_pos, _)) if pos > prev_pos => best = Some((pos, c)),
None => best = Some((pos, c)),
_ => {}
}
}
}
if let Some((_, matched)) = best {
return Ok(Some(matched.to_string()));
}
let candidates_lower: Vec<String> =
candidates.iter().map(|c| c.to_lowercase()).collect();
let mut best_match: Option<(f64, usize)> = None; for token in text_lower.split_whitespace() {
let token = token.trim_matches(|c: char| !c.is_alphanumeric());
if token.is_empty() {
continue;
}
for (i, cl) in candidates_lower.iter().enumerate() {
let sim = fuzzy_parser::distance::similarity(
token,
cl,
fuzzy_parser::distance::Algorithm::JaroWinkler,
);
if sim >= threshold {
match best_match {
Some((prev_sim, _)) if sim > prev_sim => {
best_match = Some((sim, i));
}
None => best_match = Some((sim, i)),
_ => {}
}
}
}
}
if let Some((_, idx)) = best_match {
return Ok(Some(candidates[idx].clone()));
}
Ok(None)
},
)?;
alc_table.set("match_enum", match_enum)?;
let match_bool = _lua.create_function(|_, text: String| {
const TRUE_WORDS: &[&str] = &[
"approved", "yes", "ok", "accept", "pass", "confirm", "agree", "true", "lgtm",
];
const FALSE_WORDS: &[&str] = &[
"rejected", "no", "deny", "block", "fail", "refuse", "disagree", "false",
];
let text_lower = text.to_lowercase();
let bytes = text_lower.as_bytes();
let is_boundary =
|pos: usize| -> bool { pos >= bytes.len() || !bytes[pos].is_ascii_alphanumeric() };
let mut last_pos: Option<(usize, bool)> = None; for word in TRUE_WORDS {
let w = word.as_bytes();
let mut start = 0;
while let Some(rel) = text_lower[start..].find(word) {
let pos = start + rel;
let before_ok = pos == 0 || is_boundary(pos - 1);
let after_ok = is_boundary(pos + w.len());
if before_ok && after_ok {
match last_pos {
Some((prev, _)) if pos > prev => last_pos = Some((pos, true)),
None => last_pos = Some((pos, true)),
_ => {}
}
}
start = pos + 1;
}
}
for word in FALSE_WORDS {
let w = word.as_bytes();
let mut start = 0;
while let Some(rel) = text_lower[start..].find(word) {
let pos = start + rel;
let before_ok = pos == 0 || is_boundary(pos - 1);
let after_ok = is_boundary(pos + w.len());
if before_ok && after_ok {
match last_pos {
Some((prev, _)) if pos > prev => last_pos = Some((pos, false)),
None => last_pos = Some((pos, false)),
_ => {}
}
}
start = pos + 1;
}
}
Ok(last_pos.map(|(_, v)| v))
})?;
alc_table.set("match_bool", match_bool)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use algocline_core::ExecutionMetrics;
fn test_config() -> crate::bridge::BridgeConfig {
let metrics = ExecutionMetrics::new();
crate::bridge::BridgeConfig {
llm_tx: None,
ns: "default".into(),
custom_metrics: metrics.custom_metrics_handle(),
budget: metrics.budget_handle(),
progress: metrics.progress_handle(),
lib_paths: vec![],
variant_pkgs: vec![],
}
}
#[test]
fn match_enum_exact_substring() {
let lua = Lua::new();
let t = lua.create_table().unwrap();
crate::bridge::register(&lua, &t, test_config()).unwrap();
lua.globals().set("alc", t).unwrap();
let result: String = lua
.load(r#"return alc.match_enum("Verdict: BLOCKED. Fix the issues.", {"PASS", "BLOCKED"})"#)
.eval()
.unwrap();
assert_eq!(result, "BLOCKED");
}
#[test]
fn match_enum_case_insensitive() {
let lua = Lua::new();
let t = lua.create_table().unwrap();
crate::bridge::register(&lua, &t, test_config()).unwrap();
lua.globals().set("alc", t).unwrap();
let result: String = lua
.load(r#"return alc.match_enum("verdict: pass. all good.", {"PASS", "BLOCKED"})"#)
.eval()
.unwrap();
assert_eq!(result, "PASS");
}
#[test]
fn match_enum_last_wins() {
let lua = Lua::new();
let t = lua.create_table().unwrap();
crate::bridge::register(&lua, &t, test_config()).unwrap();
lua.globals().set("alc", t).unwrap();
let result: String = lua
.load(r#"return alc.match_enum("Initially BLOCKED, but after review: PASS", {"PASS", "BLOCKED"})"#)
.eval()
.unwrap();
assert_eq!(result, "PASS");
}
#[test]
fn match_enum_no_match_returns_nil() {
let lua = Lua::new();
let t = lua.create_table().unwrap();
crate::bridge::register(&lua, &t, test_config()).unwrap();
lua.globals().set("alc", t).unwrap();
let result: LuaValue = lua
.load(r#"return alc.match_enum("something unrelated", {"PASS", "BLOCKED"})"#)
.eval()
.unwrap();
assert!(result.is_nil());
}
#[test]
fn match_enum_fuzzy_typo_in_short_response() {
let lua = Lua::new();
let t = lua.create_table().unwrap();
crate::bridge::register(&lua, &t, test_config()).unwrap();
lua.globals().set("alc", t).unwrap();
let result: String = lua
.load(r#"return alc.match_enum("BLOKED", {"PASS", "BLOCKED"})"#)
.eval()
.unwrap();
assert_eq!(result, "BLOCKED");
}
#[test]
fn match_enum_fuzzy_works_in_long_text() {
let lua = Lua::new();
let t = lua.create_table().unwrap();
crate::bridge::register(&lua, &t, test_config()).unwrap();
lua.globals().set("alc", t).unwrap();
let result: String = lua
.load(r#"return alc.match_enum("After careful review of all the evidence and considering multiple factors, the final verdict is BLCKED due to missing tests.", {"PASS", "BLOCKED"})"#)
.eval()
.unwrap();
assert_eq!(result, "BLOCKED");
}
#[test]
fn match_enum_fuzzy_nil_when_no_close_word() {
let lua = Lua::new();
let t = lua.create_table().unwrap();
crate::bridge::register(&lua, &t, test_config()).unwrap();
lua.globals().set("alc", t).unwrap();
let result: LuaValue = lua
.load(r#"return alc.match_enum("The weather is nice today", {"PASS", "BLOCKED"})"#)
.eval()
.unwrap();
assert!(result.is_nil());
}
#[test]
fn match_bool_approved() {
let lua = Lua::new();
let t = lua.create_table().unwrap();
crate::bridge::register(&lua, &t, test_config()).unwrap();
lua.globals().set("alc", t).unwrap();
let result: bool = lua
.load(r#"return alc.match_bool("Approved. The plan looks good.")"#)
.eval()
.unwrap();
assert!(result);
}
#[test]
fn match_bool_rejected() {
let lua = Lua::new();
let t = lua.create_table().unwrap();
crate::bridge::register(&lua, &t, test_config()).unwrap();
lua.globals().set("alc", t).unwrap();
let result: bool = lua
.load(r#"return alc.match_bool("rejected: missing test coverage")"#)
.eval()
.unwrap();
assert!(!result);
}
#[test]
fn match_bool_nil_on_ambiguous() {
let lua = Lua::new();
let t = lua.create_table().unwrap();
crate::bridge::register(&lua, &t, test_config()).unwrap();
lua.globals().set("alc", t).unwrap();
let result: LuaValue = lua
.load(r#"return alc.match_bool("I need more information about the design")"#)
.eval()
.unwrap();
assert!(result.is_nil());
}
#[test]
fn match_bool_last_keyword_wins() {
let lua = Lua::new();
let t = lua.create_table().unwrap();
crate::bridge::register(&lua, &t, test_config()).unwrap();
lua.globals().set("alc", t).unwrap();
let result: bool = lua
.load(r#"return alc.match_bool("No issues found. Approved.")"#)
.eval()
.unwrap();
assert!(result);
}
#[test]
fn match_bool_rejects_partial_word_ok_in_bypass() {
let lua = Lua::new();
let t = lua.create_table().unwrap();
crate::bridge::register(&lua, &t, test_config()).unwrap();
lua.globals().set("alc", t).unwrap();
let result: LuaValue = lua
.load(r#"return alc.match_bool("This is a broken token")"#)
.eval()
.unwrap();
assert!(result.is_nil());
}
#[test]
fn match_bool_rejects_pass_in_bypass() {
let lua = Lua::new();
let t = lua.create_table().unwrap();
crate::bridge::register(&lua, &t, test_config()).unwrap();
lua.globals().set("alc", t).unwrap();
let result: LuaValue = lua
.load(r#"return alc.match_bool("We need to bypass the filter")"#)
.eval()
.unwrap();
assert!(result.is_nil());
}
#[test]
fn match_bool_rejects_no_in_innovation() {
let lua = Lua::new();
let t = lua.create_table().unwrap();
crate::bridge::register(&lua, &t, test_config()).unwrap();
lua.globals().set("alc", t).unwrap();
let result: LuaValue = lua
.load(r#"return alc.match_bool("Great innovation in technology")"#)
.eval()
.unwrap();
assert!(result.is_nil());
}
#[test]
fn match_bool_word_boundary_with_punctuation() {
let lua = Lua::new();
let t = lua.create_table().unwrap();
crate::bridge::register(&lua, &t, test_config()).unwrap();
lua.globals().set("alc", t).unwrap();
let result: bool = lua
.load(r#"return alc.match_bool("yes, that works")"#)
.eval()
.unwrap();
assert!(result);
}
#[test]
fn match_bool_fail_in_failed_matches() {
let lua = Lua::new();
let t = lua.create_table().unwrap();
crate::bridge::register(&lua, &t, test_config()).unwrap();
lua.globals().set("alc", t).unwrap();
let result: LuaValue = lua
.load(r#"return alc.match_bool("The process failed gracefully")"#)
.eval()
.unwrap();
assert!(result.is_nil());
}
}