situwaition/
lib.rs

1//! Situwaition
2//!
3//! <hr>
4//!
5//! This library makes it easy to wait for a situation (cutely named "situWAITion") to complete.
6//!
7//! Situwaition can be used in contexts with or without async runtimes, and generally does what you'd expect (tm):
8//!
9//! ```
10//! use situwaition::wait_for;
11//!
12//! fn main() -> Result<(), Box<dyn Error>> {
13//!     let value = 0;
14//!
15//!     let result: Result<&str> = wait_for(|| match value == 5 {
16//!         true => Ok("done!"),
17//!         false => {
18//!             value += 1; // NOTE: incrementing like this only works in a single threaded context!
19//!             Err("not yet")
20//!         },
21//!     });
22//! }
23//! ```
24//!
25//! The example above demonstrates the synchronous usage, but `tokio` and `async-std` and corresponding modules are available as well.
26
27use std::{result::Result, time::Duration};
28
29use derive_builder::Builder;
30use thiserror::Error;
31
32#[cfg(any(feature = "tokio", feature = "async-std"))]
33use async_trait::async_trait;
34
35pub mod runtime;
36pub mod sync;
37
38const DEFAULT_SITUWAITION_TIMEOUT_MS: u64 = 3_000;
39const DEFAULT_SITUWAITION_CHECK_INTERVAL_MS: u64 = 250;
40
41pub use sync::wait_for;
42
43/// The type of error that is thrown when
44#[derive(Debug, Error)]
45pub enum SituwaitionError<E> {
46    /// Timeout from repeated failure
47    #[error("failed repeatedly until the timeout: {0}")]
48    TimeoutError(E),
49
50    #[error("check fn run exceeded the timeout")]
51    CheckTimeoutError,
52
53    /// A single conditoin failure
54    #[error("condition check failed: {0}")]
55    ConditionFailed(E),
56
57    #[error("unexpected error: {0}")]
58    UnexpectedError(String),
59}
60
61/// Options for a given situwaition
62#[allow(dead_code)]
63#[derive(Debug, Clone, Builder)]
64pub struct SituwaitionOpts {
65    /// The maximum time to wait for a situwaition
66    pub timeout: Duration,
67
68    /// How often to check for a passing condition.
69    /// Note that in the synchronous case, this determines how quickly
70    /// you can return *before* a check actually completes (i.e. timing in 100ms when check_fn takes 500ms)
71    pub check_interval: Duration,
72
73    /// Time to wait after a check has been performed.
74    /// Use this to avoid running resource-intensive checks too frequently
75    pub check_cooldown: Option<Duration>,
76}
77
78impl Default for SituwaitionOpts {
79    fn default() -> Self {
80        SituwaitionOpts {
81            timeout: Duration::from_millis(DEFAULT_SITUWAITION_TIMEOUT_MS),
82            check_interval: Duration::from_millis(DEFAULT_SITUWAITION_CHECK_INTERVAL_MS),
83            check_cooldown: None,
84        }
85    }
86}
87
88/// The basic requirements of any situwaition
89pub trait SituwaitionBase {
90    type Result;
91    type Error;
92
93    /// Retrieve the options associated with this situwaition
94    fn options(&self) -> &SituwaitionOpts;
95
96    /// Change the options associated with this situwaition
97    fn set_options(
98        &mut self,
99        update_fn: impl Fn(&SituwaitionOpts) -> SituwaitionOpts,
100    ) -> Result<(), SituwaitionError<()>>;
101}
102
103/// Synchronously executed situwaitions
104pub trait SyncSituwaition: SituwaitionBase {
105    /// Execute the situwaition, and wait until it resolves
106    /// or fails with a timeout
107    fn exec(&mut self) -> Result<Self::Result, SituwaitionError<Self::Error>>;
108}
109
110/// This trait represents a "situwaition" that can be a"waited", with tokio.
111/// note that how the waiting is done can differ by platform
112#[cfg(feature = "tokio")]
113#[async_trait]
114pub trait TokioAsyncSituwaition: SituwaitionBase {
115    /// Execute the situwaition, and wait until it resolves
116    /// or fails with a timeout
117    async fn exec(&mut self) -> Result<Self::Result, SituwaitionError<Self::Error>>;
118}
119
120/// This trait represents a "situwaition" that can be a"waited", with async-std.
121/// note that how the waiting is done can differ by platform
122#[cfg(feature = "async-std")]
123#[async_trait]
124pub trait AsyncStdAsyncSituwaition: SituwaitionBase {
125    /// Execute the situwaition, and wait until it resolves
126    /// or fails with a timeout
127    async fn exec(&mut self) -> Result<Self::Result, SituwaitionError<Self::Error>>;
128}
129
130/// Errors that are thrown during waiter creation
131#[derive(Debug, Clone, Error)]
132pub enum WaiterCreationError {
133    #[error("invalid timeout: {0}")]
134    InvalidTimeout(String),
135
136    #[error("invalid interval: {0}")]
137    InvalidInterval(String),
138}