toad_async/
lib.rs

1//! toad-async
2
3// docs
4#![doc(html_root_url = "https://docs.rs/toad-async/0.0.0")]
5#![cfg_attr(any(docsrs, feature = "docs"), feature(doc_cfg))]
6// -
7// style
8#![allow(clippy::unused_unit)]
9// -
10// deny
11#![deny(missing_docs)]
12#![deny(missing_debug_implementations)]
13#![deny(missing_copy_implementations)]
14#![cfg_attr(not(test), deny(unsafe_code))]
15// -
16// warnings
17#![cfg_attr(not(test), warn(unreachable_pub))]
18// -
19// features
20#![cfg_attr(not(feature = "std"), no_std)]
21
22#[cfg(feature = "alloc")]
23extern crate alloc as std_alloc;
24
25/// Crate glob-reexport for API compatibility with the `nb` crate
26pub mod nb {
27  pub use crate::{Error, Result};
28}
29
30/// Non-blocking `Result`
31pub type Result<T, E> = core::result::Result<T, Error<E>>;
32
33/// Either an error occurred or the operation would block
34#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
35pub enum Error<E> {
36  /// The operation would block
37  WouldBlock,
38  /// An error occurred
39  Other(E),
40}
41
42impl<E> Error<E> {
43  /// Map the error in [`Error::Other`]
44  pub fn map<F, R>(self, mut f: F) -> Error<R>
45    where F: FnMut(E) -> R
46  {
47    match self {
48      | Error::Other(e) => Error::Other(f(e)),
49      | Error::WouldBlock => Error::WouldBlock,
50    }
51  }
52}
53
54/// Poll the given function `f` until the result returned is not [`Error::WouldBlock`]
55///
56/// On `std` platforms, this will busy-wait with a small sleep (0.5 millis).
57///
58/// On `no-std` platforms, this will busy-wait.
59pub fn block<T, E, F>(f: F) -> core::result::Result<T, E>
60  where F: Fn() -> Result<T, E>
61{
62  loop {
63    match f() {
64      | Ok(ok) => break Ok(ok),
65      | Err(Error::Other(e)) => break Err(e),
66      | Err(Error::WouldBlock) => {
67        #[cfg(feature = "std")]
68        std::thread::sleep(std::time::Duration::from_nanos(500));
69        continue;
70      },
71    }
72  }
73}
74
75/// Helper methods for [`crate::Result`]
76pub trait AsyncResult<T, E>
77  where Self: Sized
78{
79  /// Map the error type contained in the result (if present)
80  /// preserving `Ok` and `Err(Error::WouldBlock)`
81  fn async_map_err<F, R>(self, f: F) -> Result<T, R>
82    where F: FnMut(E) -> R;
83}
84
85impl<T, E> AsyncResult<T, E> for Result<T, E> {
86  fn async_map_err<F, R>(self, mut f: F) -> Result<T, R>
87    where F: FnMut(E) -> R
88  {
89    self.map_err(|e| match e {
90          | Error::WouldBlock => Error::WouldBlock,
91          | Error::Other(e) => Error::Other(f(e)),
92        })
93  }
94}
95
96/// Helper methods for nullary closures returning [`crate::Result`]
97pub trait AsyncPollable<T, E> {
98  /// Convenience method for [`crate::block`]
99  ///
100  /// ```no_run
101  /// use std::fs::File;
102  /// use std::io;
103  ///
104  /// use toad_async::{nb, AsyncPollable};
105  ///
106  /// fn wait_for_file() -> nb::Result<File, io::Error> {
107  ///   match File::open("foo.txt") {
108  ///     | Ok(f) => Ok(f),
109  ///     | Err(e) if e.kind() == io::ErrorKind::NotFound => Err(nb::Error::WouldBlock),
110  ///     | Err(e) => Err(nb::Error::Other(e)),
111  ///   }
112  /// }
113  ///
114  /// let f: io::Result<File> = wait_for_file.block();
115  /// ```
116  fn block(self) -> core::result::Result<T, E>;
117}
118
119impl<F, T, E> AsyncPollable<T, E> for F where F: Fn() -> Result<T, E>
120{
121  fn block(self) -> core::result::Result<T, E> {
122    crate::block(self)
123  }
124}