localauthentication/
async_api.rs1use crate::la_context::LAContext;
7use crate::la_error::Result;
8use crate::la_policy::LAPolicy;
9use doom_fish_utils::completion::{error_from_cstr, AsyncCompletion, AsyncCompletionFuture};
10use doom_fish_utils::panic_safe::catch_user_panic;
11use std::ffi::c_void;
12use std::future::Future;
13use std::pin::Pin;
14use std::task::{Context, Poll};
15
16extern "C" fn evaluate_policy_callback(
21 success: u8,
22 error: *const i8,
23 user_data: *mut c_void,
24) {
25 catch_user_panic("evaluate_policy_callback", || {
26 if error.is_null() {
27 unsafe { AsyncCompletion::complete_ok(user_data, success != 0) };
29 } else {
30 let error_msg = unsafe { error_from_cstr(error) };
32 unsafe { AsyncCompletion::<bool>::complete_err(user_data, error_msg) };
34 }
35 });
36}
37
38extern "C" fn evaluate_access_control_callback(
39 success: u8,
40 error: *const i8,
41 user_data: *mut c_void,
42) {
43 catch_user_panic("evaluate_access_control_callback", || {
44 if error.is_null() {
45 unsafe { AsyncCompletion::complete_ok(user_data, success != 0) };
47 } else {
48 let error_msg = unsafe { error_from_cstr(error) };
50 unsafe { AsyncCompletion::<bool>::complete_err(user_data, error_msg) };
52 }
53 });
54}
55
56pub struct AsyncPolicyEvaluation {
62 inner: AsyncCompletionFuture<bool>,
63}
64
65impl std::fmt::Debug for AsyncPolicyEvaluation {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 f.debug_struct("AsyncPolicyEvaluation")
68 .finish_non_exhaustive()
69 }
70}
71
72impl Future for AsyncPolicyEvaluation {
73 type Output = Result<bool>;
74
75 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
76 Pin::new(&mut self.inner)
77 .poll(cx)
78 .map(|r| r.map_err(crate::la_error::LAError::BridgeFailed))
79 }
80}
81
82pub struct AsyncAccessControlEvaluation {
84 inner: AsyncCompletionFuture<bool>,
85}
86
87impl std::fmt::Debug for AsyncAccessControlEvaluation {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 f.debug_struct("AsyncAccessControlEvaluation")
90 .finish_non_exhaustive()
91 }
92}
93
94impl Future for AsyncAccessControlEvaluation {
95 type Output = Result<bool>;
96
97 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
98 Pin::new(&mut self.inner)
99 .poll(cx)
100 .map(|r| r.map_err(crate::la_error::LAError::BridgeFailed))
101 }
102}
103
104pub trait AsyncContextExt {
110 fn evaluate_policy_async(
116 &self,
117 policy: LAPolicy,
118 localized_reason: &str,
119 ) -> Result<AsyncPolicyEvaluation>;
120
121 unsafe fn evaluate_access_control_async(
131 &self,
132 access_control: *const c_void,
133 operation: crate::la_context::LAAccessControlOperation,
134 localized_reason: &str,
135 ) -> Result<AsyncAccessControlEvaluation>;
136}
137
138impl AsyncContextExt for LAContext {
139 fn evaluate_policy_async(
148 &self,
149 policy: LAPolicy,
150 localized_reason: &str,
151 ) -> Result<AsyncPolicyEvaluation> {
152 if localized_reason.is_empty() {
153 return Err(crate::la_error::LAError::InvalidArgument(
154 "localized reason must not be empty".to_owned(),
155 ));
156 }
157
158 let (future, ctx) = AsyncCompletion::create();
159 let reason_cstring = std::ffi::CString::new(localized_reason).map_err(|_| {
160 crate::la_error::LAError::InvalidArgument("localized reason contains null byte".to_owned())
161 })?;
162
163 let context_ptr = self.as_ptr();
164
165 unsafe {
166 crate::ffi::la_context::la_context_evaluate_policy_async(
167 context_ptr,
168 policy.as_ffi(),
169 reason_cstring.as_ptr(),
170 evaluate_policy_callback,
171 ctx,
172 );
173 }
174
175 Ok(AsyncPolicyEvaluation { inner: future })
176 }
177
178 unsafe fn evaluate_access_control_async(
192 &self,
193 access_control: *const c_void,
194 operation: crate::la_context::LAAccessControlOperation,
195 localized_reason: &str,
196 ) -> Result<AsyncAccessControlEvaluation> {
197 if access_control.is_null() {
198 return Err(crate::la_error::LAError::InvalidArgument(
199 "access control pointer must not be null".to_owned(),
200 ));
201 }
202 if localized_reason.is_empty() {
203 return Err(crate::la_error::LAError::InvalidArgument(
204 "localized reason must not be empty".to_owned(),
205 ));
206 }
207
208 let (future, ctx) = AsyncCompletion::create();
209 let reason_cstring = std::ffi::CString::new(localized_reason).map_err(|_| {
210 crate::la_error::LAError::InvalidArgument("localized reason contains null byte".to_owned())
211 })?;
212
213 let context_ptr = self.as_ptr();
214
215 unsafe {
216 crate::ffi::la_context::la_context_evaluate_access_control_async(
217 context_ptr,
218 access_control,
219 operation.raw_value(),
220 reason_cstring.as_ptr(),
221 evaluate_access_control_callback,
222 ctx,
223 );
224 }
225
226 Ok(AsyncAccessControlEvaluation { inner: future })
227 }
228}