1use std::{
32 ffi::{c_void, CString},
33 future::Future,
34 panic::AssertUnwindSafe,
35 path::{Path, PathBuf},
36 pin::Pin,
37 task::{Context, Poll},
38};
39
40use doom_fish_utils::completion::{error_from_cstr, AsyncCompletion, AsyncCompletionFuture};
41use doom_fish_utils::panic_safe::log_callback_panic;
42
43use crate::{error::VisionError, ffi};
44
45#[cfg(feature = "coreml")]
46use crate::classify::Classification;
47#[cfg(feature = "coreml")]
48use crate::coreml::{CoreMLFeatureValueObservation, CoreMLRequest};
49#[cfg(feature = "detect_barcodes")]
50use crate::detect_barcodes::DetectedBarcode;
51#[cfg(feature = "detect_faces")]
52use crate::detect_faces::DetectedFace;
53use crate::human_body_pose_3d::HumanBodyPose3DObservation;
54#[cfg(feature = "recognize_text")]
55use crate::recognize_text::{RecognitionLevel, RecognizedText};
56#[cfg(feature = "segmentation")]
57use crate::segmentation::{SegmentationMask, SegmentationQuality};
58use crate::trajectories::Trajectory;
59
60enum FutureState<T> {
61 Ready(Option<Result<T, VisionError>>),
62 Pending(AsyncCompletionFuture<T>),
63}
64
65impl<T> FutureState<T> {
66 const fn ready_err(error: VisionError) -> Self {
67 Self::Ready(Some(Err(error)))
68 }
69
70 const fn pending(future: AsyncCompletionFuture<T>) -> Self {
71 Self::Pending(future)
72 }
73}
74
75impl<T: Unpin> Future for FutureState<T> {
76 type Output = Result<T, VisionError>;
77
78 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
79 match self.as_mut().get_mut() {
80 Self::Ready(result) => Poll::Ready(
81 result
82 .take()
83 .expect("async Vision future polled after completion"),
84 ),
85 Self::Pending(future) => Pin::new(future)
86 .poll(cx)
87 .map(|result| result.map_err(VisionError::RequestFailed)),
88 }
89 }
90}
91
92fn path_to_cstring(path: impl AsRef<Path>) -> Result<CString, VisionError> {
93 let path_str = path
94 .as_ref()
95 .to_str()
96 .ok_or_else(|| VisionError::InvalidArgument("non-UTF-8 path".into()))?;
97 CString::new(path_str)
98 .map_err(|error| VisionError::InvalidArgument(format!("path NUL byte: {error}")))
99}
100
101struct WorkerFuture<T> {
102 inner: AsyncCompletionFuture<Result<T, VisionError>>,
103}
104
105impl<T> std::fmt::Debug for WorkerFuture<T> {
106 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107 f.debug_struct("WorkerFuture").finish_non_exhaustive()
108 }
109}
110
111impl<T> Future for WorkerFuture<T> {
112 type Output = Result<T, VisionError>;
113
114 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
115 Pin::new(&mut self.inner).poll(cx).map(|result| {
116 result.unwrap_or_else(|message| {
117 Err(VisionError::Unknown {
118 code: ffi::status::UNKNOWN,
119 message,
120 })
121 })
122 })
123 }
124}
125
126fn run_sync_on_worker<T, F>(work: F) -> WorkerFuture<T>
127where
128 T: Send + 'static,
129 F: FnOnce() -> Result<T, VisionError> + Send + 'static,
130{
131 let (future, ctx) = AsyncCompletion::<Result<T, VisionError>>::create();
132 let ctx = ctx as usize;
133 std::thread::spawn(move || unsafe {
134 AsyncCompletion::complete_ok(ctx as *mut c_void, work());
135 });
136 WorkerFuture { inner: future }
137}
138
139#[cfg(feature = "coreml")]
140pub struct CoreMLClassifyFuture {
141 inner: WorkerFuture<Vec<Classification>>,
142}
143
144#[cfg(feature = "coreml")]
145impl std::fmt::Debug for CoreMLClassifyFuture {
146 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147 f.debug_struct("CoreMLClassifyFuture")
148 .finish_non_exhaustive()
149 }
150}
151
152#[cfg(feature = "coreml")]
153impl Future for CoreMLClassifyFuture {
154 type Output = Result<Vec<Classification>, VisionError>;
155
156 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
157 Pin::new(&mut self.inner).poll(cx)
158 }
159}
160
161#[cfg(feature = "coreml")]
162pub struct CoreMLFeatureValueFuture {
163 inner: WorkerFuture<Option<CoreMLFeatureValueObservation>>,
164}
165
166#[cfg(feature = "coreml")]
167impl std::fmt::Debug for CoreMLFeatureValueFuture {
168 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169 f.debug_struct("CoreMLFeatureValueFuture")
170 .finish_non_exhaustive()
171 }
172}
173
174#[cfg(feature = "coreml")]
175impl Future for CoreMLFeatureValueFuture {
176 type Output = Result<Option<CoreMLFeatureValueObservation>, VisionError>;
177
178 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
179 Pin::new(&mut self.inner).poll(cx)
180 }
181}
182
183#[cfg(feature = "coreml")]
184#[derive(Debug, Clone)]
185pub struct AsyncCoreMLRequest {
186 request: CoreMLRequest,
187}
188
189#[cfg(feature = "coreml")]
190impl AsyncCoreMLRequest {
191 #[must_use]
192 pub const fn new(request: CoreMLRequest) -> Self {
193 Self { request }
194 }
195
196 #[must_use]
197 pub fn classify_in_path(&self, path: impl AsRef<Path>) -> CoreMLClassifyFuture {
198 let request = self.request.clone();
199 let path = path.as_ref().to_path_buf();
200 CoreMLClassifyFuture {
201 inner: run_sync_on_worker(move || request.classify(path.as_path())),
202 }
203 }
204
205 #[must_use]
206 pub fn feature_value_in_path(&self, path: impl AsRef<Path>) -> CoreMLFeatureValueFuture {
207 let request = self.request.clone();
208 let path = path.as_ref().to_path_buf();
209 CoreMLFeatureValueFuture {
210 inner: run_sync_on_worker(move || request.feature_value(path.as_path())),
211 }
212 }
213}
214
215pub struct DetectHumanBodyPose3DFuture {
216 inner: WorkerFuture<Vec<HumanBodyPose3DObservation>>,
217}
218
219impl std::fmt::Debug for DetectHumanBodyPose3DFuture {
220 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
221 f.debug_struct("DetectHumanBodyPose3DFuture")
222 .finish_non_exhaustive()
223 }
224}
225
226impl Future for DetectHumanBodyPose3DFuture {
227 type Output = Result<Vec<HumanBodyPose3DObservation>, VisionError>;
228
229 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
230 Pin::new(&mut self.inner).poll(cx)
231 }
232}
233
234#[derive(Debug, Clone, Copy, Default)]
235pub struct AsyncDetectHumanBodyPose3D;
236
237impl AsyncDetectHumanBodyPose3D {
238 #[must_use]
239 pub const fn new() -> Self {
240 Self
241 }
242
243 #[must_use]
244 pub fn detect_in_path(&self, path: impl AsRef<Path>) -> DetectHumanBodyPose3DFuture {
245 let path = path.as_ref().to_path_buf();
246 DetectHumanBodyPose3DFuture {
247 inner: run_sync_on_worker(move || {
248 crate::human_body_pose_3d::detect_human_body_pose_3d_observations(path.as_path())
249 }),
250 }
251 }
252}
253
254pub struct DetectTrajectoriesFuture {
255 inner: WorkerFuture<Vec<Trajectory>>,
256}
257
258impl std::fmt::Debug for DetectTrajectoriesFuture {
259 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
260 f.debug_struct("DetectTrajectoriesFuture")
261 .finish_non_exhaustive()
262 }
263}
264
265impl Future for DetectTrajectoriesFuture {
266 type Output = Result<Vec<Trajectory>, VisionError>;
267
268 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
269 Pin::new(&mut self.inner).poll(cx)
270 }
271}
272
273#[derive(Debug, Clone)]
274pub struct AsyncDetectTrajectories {
275 trajectory_length: usize,
276}
277
278impl AsyncDetectTrajectories {
279 #[must_use]
280 pub const fn new(trajectory_length: usize) -> Self {
281 Self { trajectory_length }
282 }
283
284 #[must_use]
285 pub fn detect_in_path(&self, path: impl AsRef<Path>) -> DetectTrajectoriesFuture {
286 let path: PathBuf = path.as_ref().to_path_buf();
287 let trajectory_length = self.trajectory_length;
288 DetectTrajectoriesFuture {
289 inner: run_sync_on_worker(move || {
290 crate::trajectories::detect_trajectories(path.as_path(), trajectory_length)
291 }),
292 }
293 }
294}
295
296#[cfg(feature = "recognize_text")]
312unsafe fn parse_text_result(
313 result: *const c_void,
314 error: *const i8,
315) -> Result<Vec<RecognizedText>, String> {
316 if !error.is_null() {
317 return Err(unsafe { error_from_cstr(error) });
319 }
320 if result.is_null() {
321 return Err("text recognition returned null".into());
322 }
323
324 let raw = unsafe { &*(result.cast::<ffi::AsyncArrayResultRaw>()) };
326 let texts = if raw.array.is_null() || raw.count == 0 {
327 Vec::new()
328 } else {
329 let typed = raw.array.cast::<ffi::RecognizedTextRaw>();
330 let mut out = Vec::with_capacity(raw.count);
331 for index in 0..raw.count {
332 let entry = unsafe { &*typed.add(index) };
334 let text = if entry.text.is_null() {
335 String::new()
336 } else {
337 unsafe { std::ffi::CStr::from_ptr(entry.text) }
339 .to_string_lossy()
340 .into_owned()
341 };
342 out.push(RecognizedText {
343 text,
344 confidence: entry.confidence,
345 bounding_box: crate::recognize_text::BoundingBox {
346 x: entry.bbox_x,
347 y: entry.bbox_y,
348 width: entry.bbox_w,
349 height: entry.bbox_h,
350 },
351 });
352 }
353 unsafe { ffi::vn_recognized_text_free(raw.array, raw.count) };
356 out
357 };
358
359 unsafe { ffi::vn_async_array_result_free(result.cast_mut()) };
362 Ok(texts)
363}
364
365#[cfg(feature = "recognize_text")]
375extern "C" fn text_result_cb(result: *const c_void, error: *const i8, ctx: *mut c_void) {
376 let outcome =
380 std::panic::catch_unwind(AssertUnwindSafe(|| unsafe { parse_text_result(result, error) }));
381 match outcome {
382 Ok(Ok(texts)) => {
383 unsafe { AsyncCompletion::complete_ok(ctx, texts) };
386 }
387 Ok(Err(msg)) => {
388 unsafe { AsyncCompletion::<Vec<RecognizedText>>::complete_err(ctx, msg) };
390 }
391 Err(payload) => {
392 log_callback_panic("text_result_cb", payload.as_ref());
393 unsafe {
395 AsyncCompletion::<Vec<RecognizedText>>::complete_err(
396 ctx,
397 "panic in Vision text_result_cb".into(),
398 );
399 };
400 }
401 }
402}
403
404#[cfg(feature = "recognize_text")]
406pub struct RecognizeTextFuture {
407 inner: FutureState<Vec<RecognizedText>>,
408}
409
410#[cfg(feature = "recognize_text")]
411impl std::fmt::Debug for RecognizeTextFuture {
412 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
413 f.debug_struct("RecognizeTextFuture")
414 .finish_non_exhaustive()
415 }
416}
417
418#[cfg(feature = "recognize_text")]
419impl Future for RecognizeTextFuture {
420 type Output = Result<Vec<RecognizedText>, VisionError>;
421
422 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
423 Pin::new(&mut self.inner).poll(cx)
424 }
425}
426
427#[cfg(feature = "recognize_text")]
432#[derive(Debug, Clone)]
433pub struct AsyncRecognizeText {
434 recognition_level: RecognitionLevel,
435 uses_language_correction: bool,
436}
437
438#[cfg(feature = "recognize_text")]
439impl Default for AsyncRecognizeText {
440 fn default() -> Self {
441 Self::new(RecognitionLevel::Accurate, true)
442 }
443}
444
445#[cfg(feature = "recognize_text")]
446impl AsyncRecognizeText {
447 #[must_use]
448 pub const fn new(recognition_level: RecognitionLevel, uses_language_correction: bool) -> Self {
449 Self {
450 recognition_level,
451 uses_language_correction,
452 }
453 }
454
455 pub fn recognize_in_path(&self, path: impl AsRef<Path>) -> RecognizeTextFuture {
462 match path_to_cstring(path) {
463 Err(error) => RecognizeTextFuture {
464 inner: FutureState::ready_err(error),
465 },
466 Ok(path_c) => {
467 let (future, ctx) = AsyncCompletion::create();
468 unsafe {
473 ffi::vn_recognize_text_in_path_async(
474 path_c.as_ptr(),
475 self.recognition_level.as_raw(),
476 self.uses_language_correction,
477 text_result_cb,
478 ctx,
479 );
480 };
481 RecognizeTextFuture {
482 inner: FutureState::pending(future),
483 }
484 }
485 }
486 }
487}
488
489#[cfg(feature = "detect_faces")]
501unsafe fn parse_face_result(
502 result: *const c_void,
503 error: *const i8,
504) -> Result<Vec<DetectedFace>, String> {
505 if !error.is_null() {
506 return Err(unsafe { error_from_cstr(error) });
508 }
509 if result.is_null() {
510 return Err("face detection returned null".into());
511 }
512
513 let raw = unsafe { &*(result.cast::<ffi::AsyncArrayResultRaw>()) };
515 let faces = if raw.array.is_null() || raw.count == 0 {
516 Vec::new()
517 } else {
518 let typed = raw.array.cast::<ffi::DetectedFaceRaw>();
519 let mut out = Vec::with_capacity(raw.count);
520 let nan_to_none = |value: f32| if value.is_nan() { None } else { Some(value) };
521 for index in 0..raw.count {
522 let entry = unsafe { &*typed.add(index) };
524 out.push(DetectedFace {
525 bounding_box: crate::recognize_text::BoundingBox {
526 x: entry.bbox_x,
527 y: entry.bbox_y,
528 width: entry.bbox_w,
529 height: entry.bbox_h,
530 },
531 confidence: entry.confidence,
532 roll: nan_to_none(entry.roll),
533 yaw: nan_to_none(entry.yaw),
534 pitch: nan_to_none(entry.pitch),
535 });
536 }
537 unsafe { ffi::vn_detected_faces_free(raw.array, raw.count) };
540 out
541 };
542
543 unsafe { ffi::vn_async_array_result_free(result.cast_mut()) };
545 Ok(faces)
546}
547
548#[cfg(feature = "detect_faces")]
552extern "C" fn face_result_cb(result: *const c_void, error: *const i8, ctx: *mut c_void) {
553 let outcome =
556 std::panic::catch_unwind(AssertUnwindSafe(|| unsafe { parse_face_result(result, error) }));
557 match outcome {
558 Ok(Ok(faces)) => {
559 unsafe { AsyncCompletion::complete_ok(ctx, faces) };
561 }
562 Ok(Err(msg)) => {
563 unsafe { AsyncCompletion::<Vec<DetectedFace>>::complete_err(ctx, msg) };
565 }
566 Err(payload) => {
567 log_callback_panic("face_result_cb", payload.as_ref());
568 unsafe {
570 AsyncCompletion::<Vec<DetectedFace>>::complete_err(
571 ctx,
572 "panic in Vision face_result_cb".into(),
573 );
574 };
575 }
576 }
577}
578
579#[cfg(feature = "detect_faces")]
581pub struct DetectFacesFuture {
582 inner: FutureState<Vec<DetectedFace>>,
583}
584
585#[cfg(feature = "detect_faces")]
586impl std::fmt::Debug for DetectFacesFuture {
587 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
588 f.debug_struct("DetectFacesFuture").finish_non_exhaustive()
589 }
590}
591
592#[cfg(feature = "detect_faces")]
593impl Future for DetectFacesFuture {
594 type Output = Result<Vec<DetectedFace>, VisionError>;
595
596 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
597 Pin::new(&mut self.inner).poll(cx)
598 }
599}
600
601#[cfg(feature = "detect_faces")]
603#[derive(Debug, Clone, Copy, Default)]
604pub struct AsyncDetectFaces;
605
606#[cfg(feature = "detect_faces")]
607impl AsyncDetectFaces {
608 #[must_use]
609 pub const fn new() -> Self {
610 Self
611 }
612
613 pub fn detect_in_path(&self, path: impl AsRef<Path>) -> DetectFacesFuture {
619 match path_to_cstring(path) {
620 Err(error) => DetectFacesFuture {
621 inner: FutureState::ready_err(error),
622 },
623 Ok(path_c) => {
624 let (future, ctx) = AsyncCompletion::create();
625 unsafe {
628 ffi::vn_detect_faces_in_path_async(path_c.as_ptr(), face_result_cb, ctx);
629 };
630 DetectFacesFuture {
631 inner: FutureState::pending(future),
632 }
633 }
634 }
635 }
636}
637
638#[cfg(feature = "detect_barcodes")]
650unsafe fn parse_barcode_result(
651 result: *const c_void,
652 error: *const i8,
653) -> Result<Vec<DetectedBarcode>, String> {
654 if !error.is_null() {
655 return Err(unsafe { error_from_cstr(error) });
657 }
658 if result.is_null() {
659 return Err("barcode detection returned null".into());
660 }
661
662 let raw = unsafe { &*(result.cast::<ffi::AsyncArrayResultRaw>()) };
664 let barcodes = if raw.array.is_null() || raw.count == 0 {
665 Vec::new()
666 } else {
667 let typed = raw.array.cast::<ffi::DetectedBarcodeRaw>();
668 let mut out = Vec::with_capacity(raw.count);
669 for index in 0..raw.count {
670 let entry = unsafe { &*typed.add(index) };
672 let payload = if entry.payload.is_null() {
673 String::new()
674 } else {
675 unsafe { std::ffi::CStr::from_ptr(entry.payload) }
677 .to_string_lossy()
678 .into_owned()
679 };
680 let symbology = if entry.symbology.is_null() {
681 String::new()
682 } else {
683 unsafe { std::ffi::CStr::from_ptr(entry.symbology) }
685 .to_string_lossy()
686 .into_owned()
687 };
688 out.push(DetectedBarcode {
689 payload,
690 symbology,
691 confidence: entry.confidence,
692 bounding_box: crate::recognize_text::BoundingBox {
693 x: entry.bbox_x,
694 y: entry.bbox_y,
695 width: entry.bbox_w,
696 height: entry.bbox_h,
697 },
698 });
699 }
700 unsafe { ffi::vn_detected_barcodes_free(raw.array, raw.count) };
702 out
703 };
704
705 unsafe { ffi::vn_async_array_result_free(result.cast_mut()) };
707 Ok(barcodes)
708}
709
710#[cfg(feature = "detect_barcodes")]
714extern "C" fn barcode_result_cb(result: *const c_void, error: *const i8, ctx: *mut c_void) {
715 let outcome = std::panic::catch_unwind(AssertUnwindSafe(|| unsafe {
718 parse_barcode_result(result, error)
719 }));
720 match outcome {
721 Ok(Ok(barcodes)) => {
722 unsafe { AsyncCompletion::complete_ok(ctx, barcodes) };
724 }
725 Ok(Err(msg)) => {
726 unsafe { AsyncCompletion::<Vec<DetectedBarcode>>::complete_err(ctx, msg) };
728 }
729 Err(payload) => {
730 log_callback_panic("barcode_result_cb", payload.as_ref());
731 unsafe {
733 AsyncCompletion::<Vec<DetectedBarcode>>::complete_err(
734 ctx,
735 "panic in Vision barcode_result_cb".into(),
736 );
737 };
738 }
739 }
740}
741
742#[cfg(feature = "detect_barcodes")]
744pub struct DetectBarcodesFuture {
745 inner: FutureState<Vec<DetectedBarcode>>,
746}
747
748#[cfg(feature = "detect_barcodes")]
749impl std::fmt::Debug for DetectBarcodesFuture {
750 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
751 f.debug_struct("DetectBarcodesFuture")
752 .finish_non_exhaustive()
753 }
754}
755
756#[cfg(feature = "detect_barcodes")]
757impl Future for DetectBarcodesFuture {
758 type Output = Result<Vec<DetectedBarcode>, VisionError>;
759
760 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
761 Pin::new(&mut self.inner).poll(cx)
762 }
763}
764
765#[cfg(feature = "detect_barcodes")]
767#[derive(Debug, Clone, Copy, Default)]
768pub struct AsyncDetectBarcodes;
769
770#[cfg(feature = "detect_barcodes")]
771impl AsyncDetectBarcodes {
772 #[must_use]
773 pub const fn new() -> Self {
774 Self
775 }
776
777 pub fn detect_in_path(&self, path: impl AsRef<Path>) -> DetectBarcodesFuture {
783 match path_to_cstring(path) {
784 Err(error) => DetectBarcodesFuture {
785 inner: FutureState::ready_err(error),
786 },
787 Ok(path_c) => {
788 let (future, ctx) = AsyncCompletion::create();
789 unsafe {
792 ffi::vn_detect_barcodes_in_path_async(path_c.as_ptr(), barcode_result_cb, ctx);
793 };
794 DetectBarcodesFuture {
795 inner: FutureState::pending(future),
796 }
797 }
798 }
799 }
800}
801
802#[cfg(feature = "segmentation")]
814unsafe fn parse_seg_result(
815 result: *const c_void,
816 error: *const i8,
817) -> Result<SegmentationMask, String> {
818 if !error.is_null() {
819 return Err(unsafe { error_from_cstr(error) });
821 }
822 if result.is_null() {
823 return Err("segmentation returned null".into());
824 }
825
826 let raw = unsafe { &*(result.cast::<ffi::AsyncSegResultRaw>()) };
828 if raw.bytes.is_null() {
829 unsafe { ffi::vn_async_seg_result_free(result.cast_mut()) };
831 return Err("segmentation bytes were null".into());
832 }
833
834 let len = raw.height.saturating_mul(raw.bytes_per_row);
835 let bytes = unsafe { core::slice::from_raw_parts(raw.bytes, len) }.to_vec();
837 let mask = SegmentationMask {
838 width: raw.width,
839 height: raw.height,
840 bytes_per_row: raw.bytes_per_row,
841 bytes,
842 };
843
844 unsafe { ffi::vn_async_seg_result_free(result.cast_mut()) };
847 Ok(mask)
848}
849
850#[cfg(feature = "segmentation")]
854extern "C" fn seg_result_cb(result: *const c_void, error: *const i8, ctx: *mut c_void) {
855 let outcome =
858 std::panic::catch_unwind(AssertUnwindSafe(|| unsafe { parse_seg_result(result, error) }));
859 match outcome {
860 Ok(Ok(mask)) => {
861 unsafe { AsyncCompletion::complete_ok(ctx, mask) };
863 }
864 Ok(Err(msg)) => {
865 unsafe { AsyncCompletion::<SegmentationMask>::complete_err(ctx, msg) };
867 }
868 Err(payload) => {
869 log_callback_panic("seg_result_cb", payload.as_ref());
870 unsafe {
872 AsyncCompletion::<SegmentationMask>::complete_err(
873 ctx,
874 "panic in Vision seg_result_cb".into(),
875 );
876 };
877 }
878 }
879}
880
881#[cfg(feature = "segmentation")]
883pub struct PersonSegmentationFuture {
884 inner: FutureState<SegmentationMask>,
885}
886
887#[cfg(feature = "segmentation")]
888impl std::fmt::Debug for PersonSegmentationFuture {
889 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
890 f.debug_struct("PersonSegmentationFuture")
891 .finish_non_exhaustive()
892 }
893}
894
895#[cfg(feature = "segmentation")]
896impl Future for PersonSegmentationFuture {
897 type Output = Result<SegmentationMask, VisionError>;
898
899 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
900 Pin::new(&mut self.inner).poll(cx)
901 }
902}
903
904#[cfg(feature = "segmentation")]
906#[derive(Debug, Clone, Copy)]
907pub struct AsyncPersonSegmentation {
908 quality: SegmentationQuality,
909}
910
911#[cfg(feature = "segmentation")]
912impl Default for AsyncPersonSegmentation {
913 fn default() -> Self {
914 Self::new(SegmentationQuality::Balanced)
915 }
916}
917
918#[cfg(feature = "segmentation")]
919impl AsyncPersonSegmentation {
920 #[must_use]
921 pub const fn new(quality: SegmentationQuality) -> Self {
922 Self { quality }
923 }
924
925 pub fn generate_in_path(&self, path: impl AsRef<Path>) -> PersonSegmentationFuture {
931 match path_to_cstring(path) {
932 Err(error) => PersonSegmentationFuture {
933 inner: FutureState::ready_err(error),
934 },
935 Ok(path_c) => {
936 let (future, ctx) = AsyncCompletion::create();
937 unsafe {
941 ffi::vn_generate_person_segmentation_async(
942 path_c.as_ptr(),
943 self.quality as i32,
944 seg_result_cb,
945 ctx,
946 );
947 };
948 PersonSegmentationFuture {
949 inner: FutureState::pending(future),
950 }
951 }
952 }
953 }
954}