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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//! Async state management for Telex.
//!
//! Provides the `Async<T>` enum and `use_async` hook for loading data.
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::mpsc::{self, Receiver, Sender};
use std::thread;
/// Type alias for the async result channel.
type AsyncChannel<T> = (Sender<Result<T, String>>, Receiver<Result<T, String>>);
/// Represents the state of an async operation.
#[derive(Clone)]
pub enum Async<T: Clone> {
/// The operation is still loading.
Loading,
/// The operation completed successfully.
Ready(T),
/// The operation failed with an error.
Error(String),
}
impl<T: Clone> Async<T> {
/// Returns true if the async operation is still loading.
pub fn is_loading(&self) -> bool {
matches!(self, Async::Loading)
}
/// Returns true if the async operation completed successfully.
pub fn is_ready(&self) -> bool {
matches!(self, Async::Ready(_))
}
/// Returns true if the async operation failed.
pub fn is_error(&self) -> bool {
matches!(self, Async::Error(_))
}
/// Returns the value if ready, or None otherwise.
pub fn get(&self) -> Option<&T> {
match self {
Async::Ready(v) => Some(v),
_ => None,
}
}
/// Returns the value if ready, or a default.
pub fn unwrap_or(&self, default: T) -> T {
match self {
Async::Ready(v) => v.clone(),
_ => default,
}
}
/// Returns the value if ready, or computes a default.
pub fn unwrap_or_else(&self, f: impl FnOnce() -> T) -> T {
match self {
Async::Ready(v) => v.clone(),
_ => f(),
}
}
}
/// Internal state for tracking async operations.
pub(crate) struct AsyncInner<T> {
/// The current result (None if still loading).
result: Option<Result<T, String>>,
/// Whether the async operation has been started.
started: bool,
/// Receiver for the async result.
receiver: Option<Receiver<Result<T, String>>>,
}
/// Handle for async state that can be stored and cloned.
pub struct AsyncHandle<T> {
inner: Rc<RefCell<AsyncInner<T>>>,
}
impl<T> Clone for AsyncHandle<T> {
fn clone(&self) -> Self {
Self {
inner: Rc::clone(&self.inner),
}
}
}
impl<T: Clone + Send + 'static> AsyncHandle<T> {
/// Create a new async handle.
pub fn new() -> Self {
Self {
inner: Rc::new(RefCell::new(AsyncInner {
result: None,
started: false,
receiver: None,
})),
}
}
/// Start the async operation if not already started.
pub fn start<F>(&self, f: F)
where
F: FnOnce() -> Result<T, String> + Send + 'static,
{
let mut inner = self.inner.borrow_mut();
if inner.started {
return;
}
inner.started = true;
// Create channel for result
let (tx, rx): AsyncChannel<T> = mpsc::channel();
inner.receiver = Some(rx);
// Spawn thread to run the function
thread::spawn(move || {
let result = f();
let _ = tx.send(result);
});
}
/// Poll for completion and return current state.
pub fn poll(&self) -> Async<T>
where
T: Clone,
{
let mut inner = self.inner.borrow_mut();
// If we already have a result, return it
if let Some(ref result) = inner.result {
return match result {
Ok(v) => Async::Ready(v.clone()),
Err(e) => Async::Error(e.clone()),
};
}
// Try to receive from channel
if let Some(ref receiver) = inner.receiver {
match receiver.try_recv() {
Ok(result) => {
let async_result = match &result {
Ok(v) => Async::Ready(v.clone()),
Err(e) => Async::Error(e.clone()),
};
inner.result = Some(result);
return async_result;
}
Err(mpsc::TryRecvError::Empty) => {
// Still loading
return Async::Loading;
}
Err(mpsc::TryRecvError::Disconnected) => {
// Thread panicked or channel closed
let error = "Async operation failed: channel disconnected".to_string();
inner.result = Some(Err(error.clone()));
return Async::Error(error);
}
}
}
Async::Loading
}
/// Reset the async state to allow refetching.
#[allow(dead_code)]
pub fn reset(&self) {
let mut inner = self.inner.borrow_mut();
inner.result = None;
inner.started = false;
inner.receiver = None;
}
}
impl<T: Clone + Send + 'static> Default for AsyncHandle<T> {
fn default() -> Self {
Self::new()
}
}