#[cfg(test)]
pub mod integration_tests;
mod close;
mod configure;
mod constructor;
mod poll;
mod read;
mod read_event;
mod write;
use gpio_cdev::{Chip, EventType, Line, LineEventHandle};
use mlua::Error::RuntimeError;
use mlua::{Error, Lua, MultiValue, Table, UserData, UserDataFields, UserDataMethods};
use std::os::raw;
use std::sync::{Arc, Mutex};
use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Clone)]
struct Gpio {
line_num: u32,
bias: Arc<Mutex<String>>,
chip: Arc<Mutex<Chip>>,
direction: Arc<Mutex<String>>,
drive: Arc<Mutex<String>>,
edge: Arc<Mutex<String>>,
inverted: Arc<Mutex<bool>>,
label: Arc<Mutex<String>>,
line: Arc<Mutex<Line>>,
line_events: Arc<Mutex<Option<LineEventHandle>>>,
}
impl UserData for Gpio {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("close", close::handle);
methods.add_method("poll", poll::handle);
methods.add_method("read", read::handle);
methods.add_method(
"read_event",
|lua: &Lua, gpio: &Gpio, args: MultiValue| match read_event::handle(lua, gpio, args) {
Err(e) => Err(e),
Ok(line_event) => {
let table = lua.create_table()?;
match line_event.event_type() {
EventType::RisingEdge => table.raw_set("edge", "rising")?,
EventType::FallingEdge => table.raw_set("edge", "falling")?,
};
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_err(|err| RuntimeError(err.to_string()))?;
table.raw_set("timestamp", mlua::Value::Number(now.as_nanos() as f64 / 1e9_f64))?;
Ok(table)
}
},
);
methods.add_method("write", write::handle);
}
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("bias", |_, gpio| -> Result<String, mlua::Error> {
Ok(gpio.bias.lock().map_err(|err| RuntimeError(err.to_string()))?.clone())
});
fields.add_field_method_get("chip_fd", |_, _gpio| -> Result<raw::c_int, mlua::Error> {
Err(RuntimeError("NIY".to_string())) });
fields.add_field_method_get("chip_label", |_, gpio| -> Result<String, mlua::Error> {
let locked_chip = gpio.chip.lock().map_err(|err| RuntimeError(err.to_string()))?;
Ok(locked_chip.label().to_string())
});
fields.add_field_method_get("chip_name", |_, gpio| -> Result<String, mlua::Error> {
let locked_chip = gpio.chip.lock().map_err(|err| RuntimeError(err.to_string()))?;
Ok(locked_chip.name().to_string())
});
fields.add_field_method_get("direction", |_, gpio| -> Result<String, mlua::Error> {
Ok(gpio
.direction
.lock()
.map_err(|err| RuntimeError(err.to_string()))?
.clone())
});
fields.add_field_method_get("drive", |_, gpio| -> Result<String, mlua::Error> {
Ok(gpio.drive.lock().map_err(|err| RuntimeError(err.to_string()))?.clone())
});
fields.add_field_method_get("edge", |_, gpio| -> Result<String, mlua::Error> {
Ok(gpio.edge.lock().map_err(|err| RuntimeError(err.to_string()))?.clone())
});
fields.add_field_method_get("fd", |_, _gpio| -> Result<raw::c_int, mlua::Error> {
Err(RuntimeError("NIY".to_string())) });
fields.add_field_method_get("inverted", |_, gpio| -> Result<bool, mlua::Error> {
Ok(*gpio.inverted.lock().map_err(|err| RuntimeError(err.to_string()))?)
});
fields.add_field_method_get("label", |_, gpio| -> Result<String, mlua::Error> {
Ok(gpio.label.lock().map_err(|err| RuntimeError(err.to_string()))?.clone())
});
fields.add_field_method_get("line", |_, gpio| -> Result<u32, mlua::Error> { Ok(gpio.line_num) });
fields.add_field_method_get("name", |_, _gpio| -> Result<String, mlua::Error> {
Err(RuntimeError("NIY".to_string())) });
fields.add_field_method_set("bias", |_, gpio, value: String| {
let mut locked_bias = gpio.bias.lock().map_err(|err| RuntimeError(err.to_string()))?;
*locked_bias = value;
Ok(())
});
fields.add_field_method_set("direction", |_, gpio, value: String| {
let mut locked_direction = gpio.direction.lock().map_err(|err| RuntimeError(err.to_string()))?;
*locked_direction = value;
Ok(())
});
fields.add_field_method_set("drive", |_, gpio, value: String| {
let mut locked_drive = gpio.drive.lock().map_err(|err| RuntimeError(err.to_string()))?;
*locked_drive = value;
Ok(())
});
fields.add_field_method_set("edge", |_, gpio, value: String| {
let mut locked_edge = gpio.edge.lock().map_err(|err| RuntimeError(err.to_string()))?;
*locked_edge = value;
Ok(())
});
fields.add_field_method_set("inverted", |_, gpio, value: bool| {
let mut locked_inverted = gpio.inverted.lock().map_err(|err| RuntimeError(err.to_string()))?;
*locked_inverted = value;
Ok(())
});
}
}
pub fn preload(lua: &Lua) -> Result<(), Error> {
let module = lua.create_table()?;
let metatable = lua.create_table()?;
metatable.raw_set("__call", lua.create_function(constructor::handle)?)?;
module.set_metatable(Some(metatable))?;
let globals = lua.globals();
let package: Table = globals.get("package")?;
let loaded: Table = package.get("loaded")?;
loaded.set("periphery.GPIO", module)?;
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 GPIO = require('periphery.GPIO')").exec()?;
Ok(())
}
}