mlua-periphery 1.2.4

A Rust-native implementation of lua-periphery for mlua.
#[cfg(test)]
pub mod integration_tests;

use crate::periphery;
use mlua::Error::RuntimeError;
use mlua::{Error, ExternalError, Lua, Table, UserData, UserDataFields, UserDataMethods};

#[derive(Clone)]
struct Led {
    name: String,
}

impl UserData for Led {
    fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
        methods.add_method("close", |_, _led, ()| Ok(()));
        methods.add_method("read", |_, led, ()| {
            periphery::led::read(&led.name).map_err(|err| RuntimeError(err.to_string()))
        });
        methods.add_method("write", |_, led, value: bool| {
            periphery::led::write(&led.name, value).map_err(|err| RuntimeError(err.to_string()))
        });
    }
    fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
        fields.add_field_method_get("name", |_, led| Ok(led.name.clone()));
        fields.add_field_method_get("brightness", |_, led| {
            periphery::led::get_brightness(&led.name).map_err(|err| err.to_string().into_lua_err())
        });
        fields.add_field_method_get("max_brightness", |_, led| {
            periphery::led::get_max_brightness(&led.name).map_err(|err| err.to_string().into_lua_err())
        });
    }
}

pub fn preload(lua: &Lua) -> Result<(), Error> {
    // Configure module table
    let table = lua.create_table()?;

    // Configure module metatable
    let metatable = lua.create_table()?;
    metatable.raw_set(
        "__call",
        lua.create_function(|_, args: mlua::MultiValue| {
            let name = args[1].to_string()?;

            // Verify led exists
            if let Err(e) = periphery::led::get_max_brightness(&name) {
                Err(e.to_string().into_lua_err())
            } else {
                Ok(Led { name })
            }
        })?,
    )?;
    table.set_metatable(Some(metatable))?;

    // Preload module
    let globals = lua.globals();
    let package: Table = globals.get("package")?;
    let loaded: Table = package.get("loaded")?;
    loaded.set("periphery.LED", table)?;
    Ok(())
}

#[cfg(test)]
mod tests {
    use mlua::Lua;
    use std::error::Error;

    #[test]
    fn preload() -> Result<(), Box<dyn Error>> {
        let lua = Lua::new();
        super::preload(&lua)?;
        lua.load("local LED = require('periphery.LED')").exec()?;
        Ok(())
    }

    #[test]
    fn constructor_for_non_existent_led() -> Result<(), Box<dyn Error>> {
        let lua = Lua::new();
        super::preload(&lua)?;
        let result = lua
            .load(
                r#"
                local LED = require('periphery.LED')
                local led = LED('ledFOO9999')
            "#,
            )
            .exec();
        assert!(result.is_err());
        Ok(())
    }
}