use crate::la_context::LAContext;
use crate::la_error::Result;
use crate::la_policy::LAPolicy;
use doom_fish_utils::completion::{error_from_cstr, AsyncCompletion, AsyncCompletionFuture};
use doom_fish_utils::panic_safe::catch_user_panic;
use std::ffi::c_void;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
extern "C" fn evaluate_policy_callback(
success: u8,
error: *const i8,
user_data: *mut c_void,
) {
catch_user_panic("evaluate_policy_callback", || {
if error.is_null() {
unsafe { AsyncCompletion::complete_ok(user_data, success != 0) };
} else {
let error_msg = unsafe { error_from_cstr(error) };
unsafe { AsyncCompletion::<bool>::complete_err(user_data, error_msg) };
}
});
}
extern "C" fn evaluate_access_control_callback(
success: u8,
error: *const i8,
user_data: *mut c_void,
) {
catch_user_panic("evaluate_access_control_callback", || {
if error.is_null() {
unsafe { AsyncCompletion::complete_ok(user_data, success != 0) };
} else {
let error_msg = unsafe { error_from_cstr(error) };
unsafe { AsyncCompletion::<bool>::complete_err(user_data, error_msg) };
}
});
}
pub struct AsyncPolicyEvaluation {
inner: AsyncCompletionFuture<bool>,
}
impl std::fmt::Debug for AsyncPolicyEvaluation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AsyncPolicyEvaluation")
.finish_non_exhaustive()
}
}
impl Future for AsyncPolicyEvaluation {
type Output = Result<bool>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
Pin::new(&mut self.inner)
.poll(cx)
.map(|r| r.map_err(crate::la_error::LAError::BridgeFailed))
}
}
pub struct AsyncAccessControlEvaluation {
inner: AsyncCompletionFuture<bool>,
}
impl std::fmt::Debug for AsyncAccessControlEvaluation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AsyncAccessControlEvaluation")
.finish_non_exhaustive()
}
}
impl Future for AsyncAccessControlEvaluation {
type Output = Result<bool>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
Pin::new(&mut self.inner)
.poll(cx)
.map(|r| r.map_err(crate::la_error::LAError::BridgeFailed))
}
}
pub trait AsyncContextExt {
fn evaluate_policy_async(
&self,
policy: LAPolicy,
localized_reason: &str,
) -> Result<AsyncPolicyEvaluation>;
unsafe fn evaluate_access_control_async(
&self,
access_control: *const c_void,
operation: crate::la_context::LAAccessControlOperation,
localized_reason: &str,
) -> Result<AsyncAccessControlEvaluation>;
}
impl AsyncContextExt for LAContext {
fn evaluate_policy_async(
&self,
policy: LAPolicy,
localized_reason: &str,
) -> Result<AsyncPolicyEvaluation> {
if localized_reason.is_empty() {
return Err(crate::la_error::LAError::InvalidArgument(
"localized reason must not be empty".to_owned(),
));
}
let (future, ctx) = AsyncCompletion::create();
let reason_cstring = std::ffi::CString::new(localized_reason).map_err(|_| {
crate::la_error::LAError::InvalidArgument("localized reason contains null byte".to_owned())
})?;
let context_ptr = self.as_ptr();
unsafe {
crate::ffi::la_context::la_context_evaluate_policy_async(
context_ptr,
policy.as_ffi(),
reason_cstring.as_ptr(),
evaluate_policy_callback,
ctx,
);
}
Ok(AsyncPolicyEvaluation { inner: future })
}
unsafe fn evaluate_access_control_async(
&self,
access_control: *const c_void,
operation: crate::la_context::LAAccessControlOperation,
localized_reason: &str,
) -> Result<AsyncAccessControlEvaluation> {
if access_control.is_null() {
return Err(crate::la_error::LAError::InvalidArgument(
"access control pointer must not be null".to_owned(),
));
}
if localized_reason.is_empty() {
return Err(crate::la_error::LAError::InvalidArgument(
"localized reason must not be empty".to_owned(),
));
}
let (future, ctx) = AsyncCompletion::create();
let reason_cstring = std::ffi::CString::new(localized_reason).map_err(|_| {
crate::la_error::LAError::InvalidArgument("localized reason contains null byte".to_owned())
})?;
let context_ptr = self.as_ptr();
unsafe {
crate::ffi::la_context::la_context_evaluate_access_control_async(
context_ptr,
access_control,
operation.raw_value(),
reason_cstring.as_ptr(),
evaluate_access_control_callback,
ctx,
);
}
Ok(AsyncAccessControlEvaluation { inner: future })
}
}