use crate::encoding::{Encoder, Encoding};
use mlua::{Lua, Result as LuaResult, String as LuaString, Table, Value};
const VERSION: &str = "0.1.0";
const DESCRIPTION: &str = "Encoding conversion (UTF-8 <-> ANSI)";
pub(crate) fn register(lua: &Lua) -> LuaResult<Table> {
let enc = lua.create_table()?;
enc.set("_VERSION", VERSION)?;
enc.set("_DESCRIPTION", DESCRIPTION)?;
enc.set("to_ansi", lua.create_function(to_ansi_impl)?)?;
enc.set("to_utf8", lua.create_function(to_utf8_impl)?)?;
Ok(enc)
}
fn to_ansi_impl(lua: &Lua, value: Value) -> LuaResult<(Option<LuaString>, Option<String>)> {
let utf8_str = match value {
Value::String(s) => s,
_ => {
let err_msg = format!("expected string, got {}", value.type_name());
tracing::warn!(error = %err_msg, "enc.to_ansi type error");
return Ok((None, Some(err_msg)));
}
};
let utf8_bytes = utf8_str.as_bytes();
let utf8_str = match std::str::from_utf8(&utf8_bytes) {
Ok(s) => s,
Err(e) => {
let err_msg = format!("invalid UTF-8 input: {}", e);
tracing::warn!(error = %err_msg, "enc.to_ansi encoding error");
return Ok((None, Some(err_msg)));
}
};
match Encoding::ANSI.to_bytes(utf8_str) {
Ok(ansi_bytes) => {
let lua_string = lua.create_string(&ansi_bytes)?;
Ok((Some(lua_string), None))
}
Err(e) => {
let err_msg = format!("ANSI conversion failed: {}", e);
tracing::warn!(error = %err_msg, "enc.to_ansi conversion error");
Ok((None, Some(err_msg)))
}
}
}
fn to_utf8_impl(lua: &Lua, value: Value) -> LuaResult<(Option<LuaString>, Option<String>)> {
let ansi_str = match value {
Value::String(s) => s,
_ => {
let err_msg = format!("expected string, got {}", value.type_name());
tracing::warn!(error = %err_msg, "enc.to_utf8 type error");
return Ok((None, Some(err_msg)));
}
};
let ansi_bytes = ansi_str.as_bytes();
match Encoding::ANSI.to_string(&ansi_bytes) {
Ok(utf8_string) => {
let lua_string = lua.create_string(&utf8_string)?;
Ok((Some(lua_string), None))
}
Err(e) => {
let err_msg = format!("UTF-8 conversion failed: {}", e);
tracing::warn!(error = %err_msg, "enc.to_utf8 conversion error");
Ok((None, Some(err_msg)))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use mlua::StdLib;
fn create_test_lua() -> Lua {
unsafe { Lua::unsafe_new_with(StdLib::ALL_SAFE, mlua::LuaOptions::default()) }
}
#[test]
fn test_register_creates_table() {
let lua = create_test_lua();
let enc = register(&lua).unwrap();
let version: String = enc.get("_VERSION").unwrap();
assert_eq!(version, VERSION);
let desc: String = enc.get("_DESCRIPTION").unwrap();
assert_eq!(desc, DESCRIPTION);
}
#[test]
fn test_to_ansi_ascii() {
let lua = create_test_lua();
let enc = register(&lua).unwrap();
let globals = lua.globals();
globals.set("enc", enc).unwrap();
let result: (Option<LuaString>, Option<String>) =
lua.load(r#"return enc.to_ansi("hello")"#).eval().unwrap();
assert!(result.0.is_some());
assert!(result.1.is_none());
assert_eq!(result.0.unwrap().as_bytes(), b"hello");
}
#[test]
fn test_to_ansi_type_error() {
let lua = create_test_lua();
let enc = register(&lua).unwrap();
let globals = lua.globals();
globals.set("enc", enc).unwrap();
let result: (Option<LuaString>, Option<String>) =
lua.load(r#"return enc.to_ansi(123)"#).eval().unwrap();
assert!(result.0.is_none());
assert!(result.1.is_some());
assert!(result.1.unwrap().contains("expected string"));
}
#[test]
fn test_to_utf8_ascii() {
let lua = create_test_lua();
let enc = register(&lua).unwrap();
let globals = lua.globals();
globals.set("enc", enc).unwrap();
let result: (Option<LuaString>, Option<String>) =
lua.load(r#"return enc.to_utf8("hello")"#).eval().unwrap();
assert!(result.0.is_some());
assert!(result.1.is_none());
assert_eq!(result.0.unwrap().as_bytes(), b"hello");
}
#[test]
fn test_to_utf8_type_error() {
let lua = create_test_lua();
let enc = register(&lua).unwrap();
let globals = lua.globals();
globals.set("enc", enc).unwrap();
let result: (Option<LuaString>, Option<String>) =
lua.load(r#"return enc.to_utf8(nil)"#).eval().unwrap();
assert!(result.0.is_none());
assert!(result.1.is_some());
assert!(result.1.unwrap().contains("expected string"));
}
#[cfg(windows)]
#[test]
fn test_roundtrip_japanese() {
let lua = create_test_lua();
let enc = register(&lua).unwrap();
let globals = lua.globals();
globals.set("enc", enc).unwrap();
let script = r#"
local original = "日本語テスト"
local ansi, err1 = enc.to_ansi(original)
if not ansi then
return nil, err1
end
local utf8, err2 = enc.to_utf8(ansi)
if not utf8 then
return nil, err2
end
return utf8, original
"#;
let result: (Option<String>, Option<String>) = lua.load(script).eval().unwrap();
assert!(result.0.is_some());
assert!(result.1.is_some());
assert_eq!(result.0.unwrap(), result.1.unwrap());
}
}