use crate::error::{Error, Result};
use crate::ffi;
pub struct PersistentRequest {
handle: i64,
active: bool, }
impl PersistentRequest {
pub(crate) fn new(handle: i64) -> Self {
PersistentRequest {
handle,
active: false,
}
}
pub fn raw_handle(&self) -> i64 {
self.handle
}
pub fn is_active(&self) -> bool {
self.active
}
pub fn start(&mut self) -> Result<()> {
if self.active {
return Err(Error::Internal("Request is already active".into()));
}
let ret = unsafe { ffi::ferrompi_start(self.handle) };
Error::check_with_op(ret, "start")?;
self.active = true;
Ok(())
}
pub fn wait(&mut self) -> Result<()> {
if !self.active {
return Ok(());
}
let ret = unsafe { ffi::ferrompi_wait(self.handle) };
Error::check_with_op(ret, "wait")?;
self.active = false;
Ok(())
}
pub fn test(&mut self) -> Result<bool> {
if !self.active {
return Ok(true);
}
let mut flag: i32 = 0;
let ret = unsafe { ffi::ferrompi_test(self.handle, &mut flag) };
Error::check_with_op(ret, "test")?;
if flag != 0 {
self.active = false;
}
Ok(flag != 0)
}
pub fn start_all(requests: &mut [PersistentRequest]) -> Result<()> {
if requests.is_empty() {
return Ok(());
}
for req in requests.iter() {
if req.active {
return Err(Error::Internal(
"One or more requests already active".into(),
));
}
}
let mut handles: Vec<i64> = requests.iter().map(|r| r.handle).collect();
let ret = unsafe { ffi::ferrompi_startall(handles.len() as i64, handles.as_mut_ptr()) };
Error::check_with_op(ret, "startall")?;
for req in requests.iter_mut() {
req.active = true;
}
Ok(())
}
pub fn wait_all(requests: &mut [PersistentRequest]) -> Result<()> {
if requests.is_empty() {
return Ok(());
}
let mut handles: Vec<i64> = requests.iter().map(|r| r.handle).collect();
let ret = unsafe { ffi::ferrompi_waitall(handles.len() as i64, handles.as_mut_ptr()) };
Error::check_with_op(ret, "waitall")?;
for req in requests.iter_mut() {
req.active = false;
}
Ok(())
}
}
impl Drop for PersistentRequest {
fn drop(&mut self) {
if self.active {
unsafe { ffi::ferrompi_wait(self.handle) };
}
unsafe { ffi::ferrompi_request_free(self.handle) };
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::mem::forget;
#[test]
fn new_request_is_inactive() {
let req = PersistentRequest::new(0);
assert!(!req.is_active());
assert_eq!(req.raw_handle(), 0);
forget(req);
}
#[test]
fn raw_handle_returns_constructor_value() {
let req = PersistentRequest::new(42);
assert_eq!(req.raw_handle(), 42);
forget(req);
}
#[test]
fn start_when_already_active_returns_error() {
let mut req = PersistentRequest {
handle: 0,
active: true,
};
let result = req.start();
assert!(result.is_err());
let err = result.unwrap_err();
assert!(
matches!(&err, Error::Internal(msg) if msg.contains("already active")),
"expected Error::Internal containing 'already active', got: {err}"
);
forget(req);
}
#[test]
fn test_when_inactive_returns_true() {
let mut req = PersistentRequest::new(0);
let result = req.test();
assert!(
matches!(result, Ok(true)),
"expected Ok(true), got: {result:?}"
);
forget(req);
}
#[test]
fn start_all_empty_slice_returns_ok() {
let result = PersistentRequest::start_all(&mut []);
assert!(result.is_ok());
}
#[test]
fn start_all_with_active_request_returns_error() {
let mut req = PersistentRequest {
handle: 0,
active: true,
};
let result = PersistentRequest::start_all(std::slice::from_mut(&mut req));
assert!(result.is_err());
let err = result.unwrap_err();
assert!(
matches!(&err, Error::Internal(msg) if msg.contains("already active")),
"expected Error::Internal containing 'already active', got: {err}"
);
forget(req);
}
#[test]
fn wait_all_empty_slice_returns_ok() {
let result = PersistentRequest::wait_all(&mut []);
assert!(result.is_ok());
}
}