1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use core::future::Future;
use core::panic::Location;

use alloc::boxed::Box;

#[cfg(not(feature = "sync"))]
use crate::Mutexish as _;
use crate::{
    basic_yield_now, BoxFuture, MaRc, ProgressInfo, StateCell, YieldInfo, YieldProgress,
    YieldState, Yielding,
};

/// Builder for creating root [`YieldProgress`] instances.
#[derive(Clone)]
#[must_use]
pub struct Builder {
    yielding: MaRc<Yielding<crate::YieldFn>>,
    /// public to allow overriding the API `Sync` requirement
    pub(crate) progressor: MaRc<crate::ProgressFn>,
}

impl Builder {
    /// Constructs a [`Builder`].
    ///
    /// The default values are:
    ///
    /// * The yield function is [`basic_yield_now`].
    /// * The progress function does nothing.
    ///
    /// The call site of this function is considered to be the yield point preceding the first actual
    /// yield point.
    #[track_caller]
    pub fn new() -> Builder {
        Builder {
            yielding: MaRc::new(Yielding {
                yielder: move |_info: &YieldInfo<'_>| -> BoxFuture<'static, ()> {
                    Box::pin(basic_yield_now())
                },
                state: StateCell::new(YieldState {
                    #[cfg(feature = "log_hiccups")]
                    last_finished_yielding: web_time::Instant::now(),
                    last_yield_location: Location::caller(),
                    last_yield_label: None,
                }),
            }),
            progressor: MaRc::new(|_| {}),
        }
    }

    /// Construct a new [`YieldProgress`] using the behaviors specified by this builder.
    pub fn build(self) -> YieldProgress {
        let Self {
            yielding,
            progressor,
        } = self;

        YieldProgress {
            start: 0.0,
            end: 1.0,
            label: None,
            yielding,
            progressor,
        }
    }

    /// Set the function which will be called in order to yield.
    ///
    /// See [`basic_yield_now()`] for the default implementation used if this is not not set, and
    /// some information about what you might want to pass here instead.
    #[allow(clippy::missing_panics_doc)] // internal, can't happen
    pub fn yield_using<Y, YFut>(mut self, function: Y) -> Self
    where
        Y: for<'a> Fn(&'a YieldInfo<'a>) -> YFut + Send + Sync + 'static,
        YFut: Future<Output = ()> + Send + 'static,
    {
        let new_yielding = MaRc::new(Yielding {
            yielder: move |info: &YieldInfo<'_>| -> BoxFuture<'static, ()> {
                Box::pin(function(info))
            },
            state: StateCell::new(self.yielding.state.lock().unwrap().clone()),
        });
        self.yielding = new_yielding;
        self
    }

    /// Internal version of `yield_using` which takes an already boxed function and yielding state.
    pub(crate) fn yielding_internal(mut self, new_yielding: crate::BoxYielding) -> Self {
        self.yielding = new_yielding;
        self
    }

    /// Set the function which will be called in order to report progress.
    ///
    /// If this is called more than once, the previous function will be discarded (there is no list
    /// of callbacks). If this is not called, the default function used does nothing.
    pub fn progress_using<P>(mut self, function: P) -> Self
    where
        P: for<'a> Fn(&'a ProgressInfo<'a>) + Send + Sync + 'static,
    {
        self.progressor = MaRc::new(function);
        self
    }
}

impl Default for Builder {
    fn default() -> Self {
        Self::new()
    }
}