Skip to main content

workflow_egui/runtime/
payload.rs

1use crate::imports::*;
2
3struct Inner<T>
4where
5    T: Send,
6{
7    #[allow(dead_code)]
8    id: String,
9    payload: Mutex<Option<T>>,
10    pending: AtomicBool,
11}
12
13/// A globally-registered, cheaply-cloneable slot for handing a value of type
14/// `T` between async tasks and the UI, with an associated pending flag to track
15/// in-flight operations. Instances sharing the same id share the same storage.
16pub struct Payload<T = ()>
17where
18    T: Send,
19{
20    inner: Arc<Inner<T>>,
21}
22
23impl<T> Clone for Payload<T>
24where
25    T: Send,
26{
27    fn clone(&self) -> Payload<T> {
28        Payload {
29            inner: self.inner.clone(),
30        }
31    }
32}
33
34impl<T> Payload<T>
35where
36    T: Send + 'static,
37{
38    /// Returns the payload registered under `id`, creating and registering a
39    /// new empty one if none exists yet, so callers sharing an id share state.
40    pub fn new<S: std::fmt::Display>(id: S) -> Self {
41        let id = id.to_string();
42
43        let mut registry = REGISTRY.lock().unwrap();
44
45        if let Some(payload) = registry.get(&id) {
46            if let Some(p) = payload.downcast_ref::<Payload<T>>() {
47                let inner = p.inner.clone();
48                Self { inner }
49            } else {
50                panic!("Unable to downcast Payload `{id}`");
51            }
52        } else {
53            let inner = Arc::new(Inner {
54                id: id.clone(),
55                payload: Mutex::new(None),
56                pending: AtomicBool::new(false),
57            });
58
59            registry.insert(
60                id,
61                Box::new(Payload {
62                    inner: inner.clone(),
63                }),
64            );
65            Self { inner }
66        }
67    }
68
69    /// Stores a value in the slot, replacing any previously stored value.
70    pub fn store(&self, data: T) {
71        *self.inner.payload.lock().unwrap() = Some(data);
72    }
73
74    /// Returns `true` if the pending flag is set.
75    pub fn is_pending(&self) -> bool {
76        self.inner.pending.load(Ordering::SeqCst)
77    }
78
79    /// Sets the pending flag, indicating an operation is in progress.
80    pub fn mark_pending(&self) {
81        self.inner.pending.store(true, Ordering::SeqCst);
82    }
83
84    /// Clears the pending flag, indicating no operation is in progress.
85    pub fn clear_pending(&self) {
86        self.inner.pending.store(false, Ordering::SeqCst);
87    }
88
89    /// Returns `true` if a value is currently stored in the slot.
90    pub fn is_some(&self) -> bool {
91        self.inner.payload.lock().unwrap().is_some()
92    }
93
94    /// Removes and returns the stored value, clearing the pending flag, or
95    /// returns `None` if the slot is empty.
96    pub fn take(&self) -> Option<T> {
97        match self.inner.payload.lock().unwrap().take() {
98            Some(result) => {
99                self.clear_pending();
100                Some(result)
101            }
102            _ => None,
103        }
104    }
105
106    /// Returns a clone of the stored value without removing it, or `None` if
107    /// the slot is empty.
108    pub fn inner_clone(&self) -> Option<T>
109    where
110        T: Clone,
111    {
112        self.inner.payload.lock().unwrap().clone()
113    }
114}
115
116static REGISTRY: LazyLock<Mutex<HashMap<String, Box<dyn Any + Sync + Send>>>> =
117    LazyLock::new(|| Mutex::new(HashMap::new()));