@@ -37,6 +37,11 @@ pub enum CanMakeProgress {
37
37
/// Used to represent data that is pending being available
38
38
#[ derive( Debug ) ]
39
39
pub struct Awaiting < T , E : ErrorBounds > ( pub oneshot:: Receiver < Result < T , E > > ) ;
40
+ impl < T , E : ErrorBounds > From < oneshot:: Receiver < Result < T , E > > > for Awaiting < T , E > {
41
+ fn from ( value : oneshot:: Receiver < Result < T , E > > ) -> Self {
42
+ Self ( value)
43
+ }
44
+ }
40
45
41
46
/// Used to store a type that is not always available and we need to keep
42
47
/// polling it to get it ready
@@ -55,80 +60,89 @@ pub enum DataState<T, E: ErrorBounds = anyhow::Error> {
55
60
56
61
impl < T , E : ErrorBounds > DataState < T , E > {
57
62
#[ cfg( feature = "egui" ) ]
58
- /// Attempts to load the data and displays appropriate UI if applicable.
59
- /// Some branches lead to no UI being displayed, in particular when the data
60
- /// or an error is received (On the expectation it will show next frame).
61
- /// When in an error state the error messages will show as applicable.
62
- /// If called an already has data present this function does nothing and
63
- /// returns [CanMakeProgress::UnableToMakeProgress]
63
+ /// Calls [Self::start_request] and adds a spinner if progress can be made
64
+ pub fn egui_start_request < F , R > ( & mut self , ui : & mut egui:: Ui , fetch_fn : F ) -> CanMakeProgress
65
+ where
66
+ F : FnOnce ( ) -> R ,
67
+ R : Into < Awaiting < T , E > > ,
68
+ {
69
+ let result = self . start_request ( fetch_fn) ;
70
+ if result. is_able_to_make_progress ( ) {
71
+ ui. spinner ( ) ;
72
+ }
73
+ result
74
+ }
75
+
76
+ /// Starts a new request. Only intended to be on [Self::None] and if state
77
+ /// is any other value it returns [CanMakeProgress::UnableToMakeProgress]
78
+ #[ must_use]
79
+ pub fn start_request < F , R > ( & mut self , fetch_fn : F ) -> CanMakeProgress
80
+ where
81
+ F : FnOnce ( ) -> R ,
82
+ R : Into < Awaiting < T , E > > ,
83
+ {
84
+ if self . is_none ( ) {
85
+ * self = DataState :: AwaitingResponse ( fetch_fn ( ) . into ( ) ) ;
86
+ CanMakeProgress :: AbleToMakeProgress
87
+ } else {
88
+ debug_assert ! (
89
+ false ,
90
+ "No known good reason this path should be hit other than logic error"
91
+ ) ;
92
+ CanMakeProgress :: UnableToMakeProgress
93
+ }
94
+ }
95
+
96
+ /// Convenience method that will try to make progress if in
97
+ /// [Self::AwaitingResponse] and does nothing otherwise. Returns a reference
98
+ /// to self for chaining
99
+ pub fn poll ( & mut self ) -> & mut Self {
100
+ if let DataState :: AwaitingResponse ( rx) = self {
101
+ if let Some ( new_state) = Self :: await_data ( rx) {
102
+ * self = new_state;
103
+ }
104
+ }
105
+ self
106
+ }
107
+
108
+ #[ cfg( feature = "egui" ) ]
109
+ /// Meant to be a simple method to just provide the data if it's ready or
110
+ /// help with UI and polling to get it ready if it's not.
64
111
///
65
- /// If a `retry_msg ` is provided then it overrides the default
112
+ /// WARNING: Does nothing if `self ` is [Self::None]
66
113
///
67
- /// Note see [`Self::get`] for more info.
68
- #[ must_use]
69
- pub fn egui_get < F > (
114
+ /// If a `error_btn_text` is provided then it overrides the default
115
+ pub fn egui_poll_mut (
70
116
& mut self ,
71
117
ui : & mut egui:: Ui ,
72
- retry_msg : Option < & str > ,
73
- fetch_fn : F ,
74
- ) -> CanMakeProgress
75
- where
76
- F : FnOnce ( ) -> Awaiting < T , E > ,
77
- {
118
+ error_btn_text : Option < & str > ,
119
+ ) -> Option < & mut T > {
78
120
match self {
79
- DataState :: None => {
80
- ui. spinner ( ) ;
81
- self . get ( fetch_fn)
82
- }
121
+ DataState :: None => { }
83
122
DataState :: AwaitingResponse ( _) => {
84
123
ui. spinner ( ) ;
85
- self . get ( fetch_fn )
124
+ self . poll ( ) ;
86
125
}
87
- DataState :: Present ( _data) => {
88
- // Does nothing as data is already present
89
- CanMakeProgress :: UnableToMakeProgress
126
+ DataState :: Present ( data) => {
127
+ return Some ( data) ;
90
128
}
91
129
DataState :: Failed ( e) => {
92
130
ui. colored_label ( ui. visuals ( ) . error_fg_color , e. to_string ( ) ) ;
93
- if ui. button ( retry_msg. unwrap_or ( "Retry Request" ) ) . clicked ( ) {
131
+ if ui
132
+ . button ( error_btn_text. unwrap_or ( "Clear Error Status" ) )
133
+ . clicked ( )
134
+ {
94
135
* self = DataState :: default ( ) ;
95
136
}
96
- CanMakeProgress :: AbleToMakeProgress
97
137
}
98
138
}
139
+ None
99
140
}
100
141
101
- /// Attempts to load the data and returns if it is able to make progress.
102
- ///
103
- /// Note: F needs to return `AwaitingType<T>` and not T because it needs to
104
- /// be able to be pending if T is not ready.
105
- #[ must_use]
106
- pub fn get < F > ( & mut self , fetch_fn : F ) -> CanMakeProgress
107
- where
108
- F : FnOnce ( ) -> Awaiting < T , E > ,
109
- {
110
- match self {
111
- DataState :: None => {
112
- let rx = fetch_fn ( ) ;
113
- * self = DataState :: AwaitingResponse ( rx) ;
114
- CanMakeProgress :: AbleToMakeProgress
115
- }
116
- DataState :: AwaitingResponse ( rx) => {
117
- if let Some ( new_state) = Self :: await_data ( rx) {
118
- * self = new_state;
119
- }
120
- CanMakeProgress :: AbleToMakeProgress
121
- }
122
- DataState :: Present ( _data) => {
123
- // Does nothing data is already present
124
- CanMakeProgress :: UnableToMakeProgress
125
- }
126
- DataState :: Failed ( _e) => {
127
- // Have no way to let the user know there is an error nothing we
128
- // can do here
129
- CanMakeProgress :: UnableToMakeProgress
130
- }
131
- }
142
+ #[ cfg( feature = "egui" ) ]
143
+ /// Wraps [Self::egui_poll_mut] and returns an immutable reference
144
+ pub fn egui_poll ( & mut self , ui : & mut egui:: Ui , error_btn_text : Option < & str > ) -> Option < & T > {
145
+ self . egui_poll_mut ( ui, error_btn_text) . map ( |x| & * x)
132
146
}
133
147
134
148
/// Checks to see if the data is ready and if it is returns a new [`Self`]
@@ -154,6 +168,31 @@ impl<T, E: ErrorBounds> DataState<T, E> {
154
168
} )
155
169
}
156
170
171
+ /// Returns a reference to the inner data if available otherwise None.
172
+ ///
173
+ /// NOTE: This function does not poll to get the data ready if the state is
174
+ /// still awaiting
175
+ pub fn present ( & self ) -> Option < & T > {
176
+ if let Self :: Present ( data) = self {
177
+ Some ( data)
178
+ } else {
179
+ None
180
+ }
181
+ }
182
+
183
+ /// Returns a mutable reference to the inner data if available otherwise
184
+ /// None
185
+ ///
186
+ /// NOTE: This function does not poll to get the data ready if the state is
187
+ /// still awaiting
188
+ pub fn present_mut ( & mut self ) -> Option < & mut T > {
189
+ if let Self :: Present ( data) = self {
190
+ Some ( data)
191
+ } else {
192
+ None
193
+ }
194
+ }
195
+
157
196
/// Returns `true` if the data state is [`Present`].
158
197
///
159
198
/// [`Present`]: DataState::Present
0 commit comments