use crate::Result;
use crate::runtime::Runtime;
use crate::script::support::into_option_string; use base64::engine::{Engine as _, general_purpose};
use blake3::Hasher;
use mlua::{Lua, Table, Value}; use sha2::{Digest, Sha256, Sha512};
pub fn init_module(lua: &Lua, _runtime: &Runtime) -> Result<Table> {
let table = lua.create_table()?;
table.set("sha256", lua.create_function(lua_sha256)?)?;
table.set("sha256_b58", lua.create_function(lua_sha256_b58)?)?;
table.set("sha256_b64", lua.create_function(lua_sha256_b64)?)?;
table.set("sha256_b64u", lua.create_function(lua_sha256_b64u)?)?;
table.set("sha512", lua.create_function(lua_sha512)?)?;
table.set("sha512_b58", lua.create_function(lua_sha512_b58)?)?;
table.set("sha512_b64", lua.create_function(lua_sha512_b64)?)?;
table.set("sha512_b64u", lua.create_function(lua_sha512_b64u)?)?;
table.set("blake3", lua.create_function(lua_blake3)?)?;
table.set("blake3_b58", lua.create_function(lua_blake3_b58)?)?;
table.set("blake3_b64", lua.create_function(lua_blake3_b64)?)?;
table.set("blake3_b64u", lua.create_function(lua_blake3_b64u)?)?;
Ok(table)
}
fn lua_sha256(lua: &Lua, value: Value) -> mlua::Result<Value> {
let Some(input) = into_option_string(value, "aip.hash.sha256")? else {
return Ok(Value::Nil);
};
let mut hasher = Sha256::new();
hasher.update(input.as_bytes());
let result = hasher.finalize();
Ok(Value::String(lua.create_string(hex::encode(result))?))
}
fn lua_sha256_b58(lua: &Lua, value: Value) -> mlua::Result<Value> {
let Some(input) = into_option_string(value, "aip.hash.sha256_b58")? else {
return Ok(Value::Nil);
};
let mut hasher = Sha256::new();
hasher.update(input.as_bytes());
let result = hasher.finalize();
Ok(Value::String(lua.create_string(bs58::encode(result).into_string())?))
}
fn lua_sha256_b64(lua: &Lua, value: Value) -> mlua::Result<Value> {
let Some(input) = into_option_string(value, "aip.hash.sha256_b64")? else {
return Ok(Value::Nil);
};
let mut hasher = Sha256::new();
hasher.update(input.as_bytes());
let result = hasher.finalize();
Ok(Value::String(
lua.create_string(general_purpose::STANDARD.encode(result))?,
))
}
fn lua_sha256_b64u(lua: &Lua, value: Value) -> mlua::Result<Value> {
let Some(input) = into_option_string(value, "aip.hash.sha256_b64u")? else {
return Ok(Value::Nil);
};
let mut hasher = Sha256::new();
hasher.update(input.as_bytes());
let result = hasher.finalize();
Ok(Value::String(
lua.create_string(general_purpose::URL_SAFE_NO_PAD.encode(result))?,
))
}
fn lua_sha512(lua: &Lua, value: Value) -> mlua::Result<Value> {
let Some(input) = into_option_string(value, "aip.hash.sha512")? else {
return Ok(Value::Nil);
};
let mut hasher = Sha512::new();
hasher.update(input.as_bytes());
let result = hasher.finalize();
Ok(Value::String(lua.create_string(hex::encode(result))?))
}
fn lua_sha512_b58(lua: &Lua, value: Value) -> mlua::Result<Value> {
let Some(input) = into_option_string(value, "aip.hash.sha512_b58")? else {
return Ok(Value::Nil);
};
let mut hasher = Sha512::new();
hasher.update(input.as_bytes());
let result = hasher.finalize();
Ok(Value::String(lua.create_string(bs58::encode(result).into_string())?))
}
fn lua_sha512_b64(lua: &Lua, value: Value) -> mlua::Result<Value> {
let Some(input) = into_option_string(value, "aip.hash.sha512_b64")? else {
return Ok(Value::Nil);
};
let mut hasher = Sha512::new();
hasher.update(input.as_bytes());
let result = hasher.finalize();
Ok(Value::String(
lua.create_string(general_purpose::STANDARD.encode(result))?,
))
}
fn lua_sha512_b64u(lua: &Lua, value: Value) -> mlua::Result<Value> {
let Some(input) = into_option_string(value, "aip.hash.sha512_b64u")? else {
return Ok(Value::Nil);
};
let mut hasher = Sha512::new();
hasher.update(input.as_bytes());
let result = hasher.finalize();
Ok(Value::String(
lua.create_string(general_purpose::URL_SAFE_NO_PAD.encode(result))?,
))
}
fn lua_blake3(lua: &Lua, value: Value) -> mlua::Result<Value> {
let Some(input) = into_option_string(value, "aip.hash.blake3")? else {
return Ok(Value::Nil);
};
let mut hasher = Hasher::new();
hasher.update(input.as_bytes());
let hash_bytes = hasher.finalize();
Ok(Value::String(lua.create_string(hash_bytes.to_hex().to_string())?))
}
fn lua_blake3_b58(lua: &Lua, value: Value) -> mlua::Result<Value> {
let Some(input) = into_option_string(value, "aip.hash.blake3_b58")? else {
return Ok(Value::Nil);
};
let mut hasher = Hasher::new();
hasher.update(input.as_bytes());
let hash_bytes = hasher.finalize();
Ok(Value::String(
lua.create_string(bs58::encode(hash_bytes.as_bytes()).into_string())?,
))
}
fn lua_blake3_b64(lua: &Lua, value: Value) -> mlua::Result<Value> {
let Some(input) = into_option_string(value, "aip.hash.blake3_b64")? else {
return Ok(Value::Nil);
};
let mut hasher = Hasher::new();
hasher.update(input.as_bytes());
let result = hasher.finalize();
Ok(Value::String(
lua.create_string(general_purpose::STANDARD.encode(result.as_bytes()))?,
))
}
fn lua_blake3_b64u(lua: &Lua, value: Value) -> mlua::Result<Value> {
let Some(input) = into_option_string(value, "aip.hash.blake3_b64u")? else {
return Ok(Value::Nil);
};
let mut hasher = Hasher::new();
hasher.update(input.as_bytes());
let result = hasher.finalize();
Ok(Value::String(lua.create_string(
general_purpose::URL_SAFE_NO_PAD.encode(result.as_bytes()),
)?))
}
#[cfg(test)]
mod tests {
type Result<T> = core::result::Result<T, Box<dyn std::error::Error>>;
use crate::_test_support::{eval_lua, setup_lua};
use crate::script::aip_modules::aip_hash;
const TEST_INPUT: &str = "hello world";
#[tokio::test]
async fn test_lua_hash_sha256_hex() -> Result<()> {
let lua = setup_lua(aip_hash::init_module, "hash").await?;
let expected = "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9";
let lua_code = format!(r#"return aip.hash.sha256("{TEST_INPUT}")"#);
let res = eval_lua(&lua, &lua_code)?;
assert_eq!(res.as_str().ok_or("Result should be a string")?, expected);
Ok(())
}
#[tokio::test]
async fn test_lua_hash_sha256_hex_nil_input() -> Result<()> {
let lua = setup_lua(aip_hash::init_module, "hash").await?;
let script = "return aip.hash.sha256(nil)";
let result_val = eval_lua(&lua, script)?;
assert!(result_val.is_null(), "Expected nil for nil input to sha256");
Ok(())
}
#[tokio::test]
async fn test_lua_hash_sha256_b58() -> Result<()> {
let lua = setup_lua(aip_hash::init_module, "hash").await?;
let expected = "DULfJyE3WQqNxy3ymuhAChyNR3yufT88pmqvAazKFMG4";
let lua_code = format!(r#"return aip.hash.sha256_b58("{TEST_INPUT}")"#);
let res = eval_lua(&lua, &lua_code)?;
assert_eq!(res.as_str().ok_or("Result should be a string")?, expected);
Ok(())
}
#[tokio::test]
async fn test_lua_hash_sha256_b64() -> Result<()> {
let lua = setup_lua(aip_hash::init_module, "hash").await?;
let expected = "uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=";
let lua_code = format!(r#"return aip.hash.sha256_b64("{TEST_INPUT}")"#);
let res = eval_lua(&lua, &lua_code)?;
assert_eq!(res.as_str().ok_or("Result should be a string")?, expected);
Ok(())
}
#[tokio::test]
async fn test_lua_hash_sha256_b64u() -> Result<()> {
let lua = setup_lua(aip_hash::init_module, "hash").await?;
let expected = "uU0nuZNNPgilLlLX2n2r-sSE7-N6U4DukIj3rOLvzek";
let lua_code = format!(r#"return aip.hash.sha256_b64u("{TEST_INPUT}")"#);
let res = eval_lua(&lua, &lua_code)?;
assert_eq!(res.as_str().ok_or("Result should be a string")?, expected);
Ok(())
}
#[tokio::test]
async fn test_lua_hash_sha512_hex() -> Result<()> {
let lua = setup_lua(aip_hash::init_module, "hash").await?;
let expected = "309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f";
let lua_code = format!(r#"return aip.hash.sha512("{TEST_INPUT}")"#);
let res = eval_lua(&lua, &lua_code)?;
assert_eq!(res.as_str().ok_or("Result should be a string")?, expected);
Ok(())
}
#[tokio::test]
async fn test_lua_hash_sha512_hex_nil_input() -> Result<()> {
let lua = setup_lua(aip_hash::init_module, "hash").await?;
let script = "return aip.hash.sha512(nil)";
let result_val = eval_lua(&lua, script)?;
assert!(result_val.is_null(), "Expected nil for nil input to sha512");
Ok(())
}
#[tokio::test]
async fn test_lua_hash_sha512_b58() -> Result<()> {
let lua = setup_lua(aip_hash::init_module, "hash").await?;
let expected = "yP4cqy7jmaRDzC2bmcGNZkuQb3VdftMk6YH7ynQ2Qw4zktKsyA9fk52xghNQNAdkpF9iFmFkKh2bNVG4kDWhsok";
let lua_code = format!(r#"return aip.hash.sha512_b58("{TEST_INPUT}")"#);
let res = eval_lua(&lua, &lua_code)?;
assert_eq!(res.as_str().ok_or("Result should be a string")?, expected);
Ok(())
}
#[tokio::test]
async fn test_lua_hash_sha512_b64() -> Result<()> {
let lua = setup_lua(aip_hash::init_module, "hash").await?;
let expected = "MJ7MSJwS1utMxA9QyQLytNDtd+5RGnx6m808qG1M2G+YndNbxf9JlnDaNCVbRbDP2DDoH2Bdz33FVC6TrpzXbw==";
let lua_code = format!(r#"return aip.hash.sha512_b64("{TEST_INPUT}")"#);
let res = eval_lua(&lua, &lua_code)?;
assert_eq!(res.as_str().ok_or("Result should be a string")?, expected);
Ok(())
}
#[tokio::test]
async fn test_lua_hash_sha512_b64u() -> Result<()> {
let lua = setup_lua(aip_hash::init_module, "hash").await?;
let expected = "MJ7MSJwS1utMxA9QyQLytNDtd-5RGnx6m808qG1M2G-YndNbxf9JlnDaNCVbRbDP2DDoH2Bdz33FVC6TrpzXbw";
let lua_code = format!(r#"return aip.hash.sha512_b64u("{TEST_INPUT}")"#);
let res = eval_lua(&lua, &lua_code)?;
assert_eq!(res.as_str().ok_or("Result should be a string")?, expected);
Ok(())
}
#[tokio::test]
async fn test_lua_hash_blake3_hex() -> Result<()> {
let lua = setup_lua(aip_hash::init_module, "hash").await?;
let expected = "d74981efa70a0c880b8d8c1985d075dbcbf679b99a5f9914e5aaf96b831a9e24";
let lua_code = format!(r#"return aip.hash.blake3("{TEST_INPUT}")"#);
let res = eval_lua(&lua, &lua_code)?;
assert_eq!(res.as_str().ok_or("Result should be a string")?, expected);
Ok(())
}
#[tokio::test]
async fn test_lua_hash_blake3_hex_nil_input() -> Result<()> {
let lua = setup_lua(aip_hash::init_module, "hash").await?;
let script = "return aip.hash.blake3(nil)";
let result_val = eval_lua(&lua, script)?;
assert!(result_val.is_null(), "Expected nil for nil input to blake3");
Ok(())
}
#[tokio::test]
async fn test_lua_hash_blake3_b58() -> Result<()> {
let lua = setup_lua(aip_hash::init_module, "hash").await?;
let expected = "FVPfbg9bK7mj7jnaSRXhuVcVakkXcjMPgSwxmauUofYf";
let lua_code = format!(r#"return aip.hash.blake3_b58("{TEST_INPUT}")"#);
let res = eval_lua(&lua, &lua_code)?;
assert_eq!(res.as_str().ok_or("Result should be a string")?, expected);
Ok(())
}
#[tokio::test]
async fn test_lua_hash_blake3_b64() -> Result<()> {
let lua = setup_lua(aip_hash::init_module, "hash").await?;
let expected = "10mB76cKDIgLjYwZhdB128v2ebmaX5kU5ar5a4ManiQ=";
let lua_code = format!(r#"return aip.hash.blake3_b64("{TEST_INPUT}")"#);
let res = eval_lua(&lua, &lua_code)?;
assert_eq!(res.as_str().ok_or("Result should be a string")?, expected);
Ok(())
}
#[tokio::test]
async fn test_lua_hash_blake3_b64u() -> Result<()> {
let lua = setup_lua(aip_hash::init_module, "hash").await?;
let expected = "10mB76cKDIgLjYwZhdB128v2ebmaX5kU5ar5a4ManiQ";
let lua_code = format!(r#"return aip.hash.blake3_b64u("{TEST_INPUT}")"#);
let res = eval_lua(&lua, &lua_code)?;
assert_eq!(res.as_str().ok_or("Result should be a string")?, expected);
Ok(())
}
}