Skip to main content

telex/
async_state.rs

1//! Async state management for RTE.
2//!
3//! Provides the `Async<T>` enum and `use_async` hook for loading data.
4
5use std::cell::RefCell;
6use std::rc::Rc;
7use std::sync::mpsc::{self, Receiver, Sender};
8use std::thread;
9
10/// Type alias for the async result channel.
11type AsyncChannel<T> = (Sender<Result<T, String>>, Receiver<Result<T, String>>);
12
13/// Represents the state of an async operation.
14#[derive(Clone)]
15pub enum Async<T: Clone> {
16    /// The operation is still loading.
17    Loading,
18    /// The operation completed successfully.
19    Ready(T),
20    /// The operation failed with an error.
21    Error(String),
22}
23
24impl<T: Clone> Async<T> {
25    /// Returns true if the async operation is still loading.
26    pub fn is_loading(&self) -> bool {
27        matches!(self, Async::Loading)
28    }
29
30    /// Returns true if the async operation completed successfully.
31    pub fn is_ready(&self) -> bool {
32        matches!(self, Async::Ready(_))
33    }
34
35    /// Returns true if the async operation failed.
36    pub fn is_error(&self) -> bool {
37        matches!(self, Async::Error(_))
38    }
39
40    /// Returns the value if ready, or None otherwise.
41    pub fn get(&self) -> Option<&T> {
42        match self {
43            Async::Ready(v) => Some(v),
44            _ => None,
45        }
46    }
47
48    /// Returns the value if ready, or a default.
49    pub fn unwrap_or(&self, default: T) -> T {
50        match self {
51            Async::Ready(v) => v.clone(),
52            _ => default,
53        }
54    }
55
56    /// Returns the value if ready, or computes a default.
57    pub fn unwrap_or_else(&self, f: impl FnOnce() -> T) -> T {
58        match self {
59            Async::Ready(v) => v.clone(),
60            _ => f(),
61        }
62    }
63}
64
65/// Internal state for tracking async operations.
66pub(crate) struct AsyncInner<T> {
67    /// The current result (None if still loading).
68    result: Option<Result<T, String>>,
69    /// Whether the async operation has been started.
70    started: bool,
71    /// Receiver for the async result.
72    receiver: Option<Receiver<Result<T, String>>>,
73}
74
75/// Handle for async state that can be stored and cloned.
76pub struct AsyncHandle<T> {
77    inner: Rc<RefCell<AsyncInner<T>>>,
78}
79
80impl<T> Clone for AsyncHandle<T> {
81    fn clone(&self) -> Self {
82        Self {
83            inner: Rc::clone(&self.inner),
84        }
85    }
86}
87
88impl<T: Clone + Send + 'static> AsyncHandle<T> {
89    /// Create a new async handle.
90    pub fn new() -> Self {
91        Self {
92            inner: Rc::new(RefCell::new(AsyncInner {
93                result: None,
94                started: false,
95                receiver: None,
96            })),
97        }
98    }
99
100    /// Start the async operation if not already started.
101    pub fn start<F>(&self, f: F)
102    where
103        F: FnOnce() -> Result<T, String> + Send + 'static,
104    {
105        let mut inner = self.inner.borrow_mut();
106        if inner.started {
107            return;
108        }
109
110        inner.started = true;
111
112        // Create channel for result
113        let (tx, rx): AsyncChannel<T> = mpsc::channel();
114        inner.receiver = Some(rx);
115
116        // Spawn thread to run the function
117        thread::spawn(move || {
118            let result = f();
119            let _ = tx.send(result);
120        });
121    }
122
123    /// Poll for completion and return current state.
124    pub fn poll(&self) -> Async<T>
125    where
126        T: Clone,
127    {
128        let mut inner = self.inner.borrow_mut();
129
130        // If we already have a result, return it
131        if let Some(ref result) = inner.result {
132            return match result {
133                Ok(v) => Async::Ready(v.clone()),
134                Err(e) => Async::Error(e.clone()),
135            };
136        }
137
138        // Try to receive from channel
139        if let Some(ref receiver) = inner.receiver {
140            match receiver.try_recv() {
141                Ok(result) => {
142                    let async_result = match &result {
143                        Ok(v) => Async::Ready(v.clone()),
144                        Err(e) => Async::Error(e.clone()),
145                    };
146                    inner.result = Some(result);
147                    return async_result;
148                }
149                Err(mpsc::TryRecvError::Empty) => {
150                    // Still loading
151                    return Async::Loading;
152                }
153                Err(mpsc::TryRecvError::Disconnected) => {
154                    // Thread panicked or channel closed
155                    let error = "Async operation failed: channel disconnected".to_string();
156                    inner.result = Some(Err(error.clone()));
157                    return Async::Error(error);
158                }
159            }
160        }
161
162        Async::Loading
163    }
164
165    /// Reset the async state to allow refetching.
166    #[allow(dead_code)]
167    pub fn reset(&self) {
168        let mut inner = self.inner.borrow_mut();
169        inner.result = None;
170        inner.started = false;
171        inner.receiver = None;
172    }
173}
174
175impl<T: Clone + Send + 'static> Default for AsyncHandle<T> {
176    fn default() -> Self {
177        Self::new()
178    }
179}