use std::ptr;
use super::{
Error, OperationType, Result, gpiod,
request::{Event, Request},
};
pub struct Events<'a> {
buffer: &'a mut Buffer,
read_index: usize,
len: usize,
}
impl<'a> Events<'a> {
pub fn new(buffer: &'a mut Buffer, len: usize) -> Self {
Self {
buffer,
read_index: 0,
len,
}
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
}
impl<'a> Iterator for Events<'a> {
type Item = Result<&'a Event>;
fn nth(&mut self, n: usize) -> Option<Self::Item> {
if self.read_index + n >= self.len {
return None;
}
self.read_index += n + 1;
Some(self.buffer.event(self.read_index - 1))
}
fn next(&mut self) -> Option<Self::Item> {
#[allow(clippy::iter_nth_zero)]
self.nth(0)
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct Buffer {
pub(crate) buffer: *mut gpiod::gpiod_edge_event_buffer,
events: Vec<*mut gpiod::gpiod_edge_event>,
}
unsafe impl Send for Buffer {}
impl Buffer {
pub fn new(capacity: usize) -> Result<Self> {
let buffer = unsafe { gpiod::gpiod_edge_event_buffer_new(capacity) };
if buffer.is_null() {
return Err(Error::OperationFailed(
OperationType::EdgeEventBufferNew,
errno::errno(),
));
}
let capacity = unsafe { gpiod::gpiod_edge_event_buffer_get_capacity(buffer) };
Ok(Self {
buffer,
events: vec![ptr::null_mut(); capacity],
})
}
pub fn capacity(&self) -> usize {
self.events.len()
}
pub fn read_edge_events<'a>(&'a mut self, request: &Request) -> Result<Events<'a>> {
for i in 0..self.events.len() {
self.events[i] = ptr::null_mut();
}
let ret = unsafe {
gpiod::gpiod_line_request_read_edge_events(
request.request,
self.buffer,
self.events.len(),
)
};
if ret == -1 {
Err(Error::OperationFailed(
OperationType::LineRequestReadEdgeEvent,
errno::errno(),
))
} else {
let ret = ret as usize;
if ret > self.events.len() {
Err(Error::TooManyEvents(ret, self.events.len()))
} else {
Ok(Events::new(self, ret))
}
}
}
fn event<'a>(&mut self, index: usize) -> Result<&'a Event> {
if self.events[index].is_null() {
let event = unsafe {
gpiod::gpiod_edge_event_buffer_get_event(self.buffer, index.try_into().unwrap())
};
if event.is_null() {
return Err(Error::OperationFailed(
OperationType::EdgeEventBufferGetEvent,
errno::errno(),
));
}
self.events[index] = event;
}
Ok(unsafe {
(self.events.as_ptr().add(index) as *const Event)
.as_ref()
.unwrap()
})
}
}
impl Drop for Buffer {
fn drop(&mut self) {
unsafe { gpiod::gpiod_edge_event_buffer_free(self.buffer) };
}
}