use crate::error::ErrorStack;
use crate::libc_types::{c_char, c_int, c_void};
use foreign_types::{ForeignType, ForeignTypeRef};
use std::any::Any;
use std::panic::{self, AssertUnwindSafe};
use std::slice;
pub struct CallbackState<F> {
cb: Option<F>,
panic: Option<Box<dyn Any + Send + 'static>>,
}
impl<F> CallbackState<F> {
pub fn new(callback: F) -> Self {
CallbackState {
cb: Some(callback),
panic: None,
}
}
}
impl<F> Drop for CallbackState<F> {
fn drop(&mut self) {
if let Some(panic) = self.panic.take() {
panic::resume_unwind(panic);
}
}
}
pub unsafe extern "C" fn invoke_passwd_cb<F>(
buf: *mut c_char,
size: c_int,
_rwflag: c_int,
cb_state: *mut c_void,
) -> c_int
where
F: FnOnce(&mut [u8]) -> Result<usize, ErrorStack>,
{
let callback = &mut *cb_state.cast::<CallbackState<F>>();
let result = panic::catch_unwind(AssertUnwindSafe(|| {
let pass_slice = slice::from_raw_parts_mut(buf.cast::<u8>(), size as usize);
callback.cb.take().unwrap()(pass_slice)
}));
match result {
Ok(Ok(len)) => len as c_int,
Ok(Err(err)) => {
err.put();
0
}
Err(err) => {
callback.panic = Some(err);
0
}
}
}
#[allow(dead_code)]
pub trait ForeignTypeExt: ForeignType {
unsafe fn from_ptr_opt(ptr: *mut Self::CType) -> Option<Self> {
if ptr.is_null() {
None
} else {
Some(Self::from_ptr(ptr))
}
}
}
impl<FT: ForeignType> ForeignTypeExt for FT {}
pub trait ForeignTypeRefExt: ForeignTypeRef {
unsafe fn from_const_ptr<'a>(ptr: *const Self::CType) -> &'a Self {
Self::from_ptr(ptr.cast_mut())
}
unsafe fn from_const_ptr_opt<'a>(ptr: *const Self::CType) -> Option<&'a Self> {
if ptr.is_null() {
None
} else {
Some(Self::from_const_ptr(ptr.cast_mut()))
}
}
}
impl<FT: ForeignTypeRef> ForeignTypeRefExt for FT {}