diff --git a/src/util.rs b/src/util.rs index e13526a..71dd21e 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,8 +1,46 @@ use crate::cbor::AuthenticatorData; use crate::{FidoAssertionRequest, FidoCredential, FidoDevice, FidoErrorKind, FidoResult}; -use std::sync::mpsc::channel; #[cfg(feature = "assert_devices")] use crossbeam::thread; +use std::sync::mpsc::channel; + +#[cfg(feature = "assert_devices")] +pub fn request_multiple_devices< + 'a, + T: Send + 'a, + F: Fn(&mut FidoDevice) -> FidoResult + 'a + Sync, +>( + devices: impl Iterator, +) -> FidoResult { + thread::scope(|scope| -> FidoResult { + let (tx, rx) = channel(); + let handles = devices + .map(|(device, fn_)| { + let cancel = device.cancel_handle()?; + let tx = tx.clone(); + let thread_handle = scope.spawn(move |_| tx.send(fn_(device))); + Ok((cancel, thread_handle)) + }) + .collect::>>()?; + + let mut err = None; + for res in rx.iter().take(handles.len()) { + match res { + Ok(_) => { + for (mut cancel, join) in handles { + // Canceling out of courtesy don't care if it fails + let _ = cancel.cancel(); + let _ = join.join(); + } + return res; + } + e => err = Some(e), + } + } + err.unwrap_or(Err(FidoErrorKind::DeviceUnsupported.into())) + }) + .unwrap() +} /// Will send the `assertion` to all supplied `devices` and return either the first successful assertion or the last error #[cfg(feature = "assert_devices")] @@ -10,35 +48,11 @@ pub fn get_assertion_devices<'a>( assertion: &'a FidoAssertionRequest, devices: impl Iterator, ) -> FidoResult<(&'a FidoCredential, AuthenticatorData)> { - thread::scope( - |scope| -> FidoResult<(&'a FidoCredential, AuthenticatorData)> { - let (tx, rx) = channel(); - let handles = devices - .map(|device| { - let cancel = device.cancel_handle()?; - let tx = tx.clone(); - let thread_handle = - scope.spawn(move |_| tx.send(device.get_assertion(assertion))); - Ok((cancel, thread_handle)) - }) - .collect::>>()?; - - let mut err = None; - for res in rx.iter().take(handles.len()) { - match res { - Ok(_) => { - for (mut cancel, join) in handles { - // Canceling out of courtesy don't care if it fails - let _ = cancel.cancel(); - let _ = join.join(); - } - return res; - } - e => err = Some(e), - } - } - err.unwrap_or(Err(FidoErrorKind::DeviceUnsupported.into())) - }, - ) - .unwrap() + let get_assertion = |device: &mut FidoDevice| device.get_assertion(assertion); + request_multiple_devices(devices.map(|device| { + ( + device, + &get_assertion, + ) + })) }