relm4/component/async/traits.rs
1// Copyright 2021-2022 Aaron Erhardt <aaron.erhardt@t-online.de>
2// Copyright 2022 System76 <info@system76.com>
3// SPDX-License-Identifier: MIT or Apache-2.0
4
5// Prevent false positive in nightly
6#![allow(unused_qualifications)]
7
8use std::fmt::Debug;
9
10use crate::channel::{AsyncComponentSender, Sender};
11use crate::loading_widgets::LoadingWidgets;
12
13use super::{AsyncComponentBuilder, AsyncComponentParts};
14
15/// Asynchronous variant of [`Component`](crate::Component).
16///
17/// `AsyncComponent` is powerful and flexible, but for many use-cases the [`SimpleAsyncComponent`]
18/// convenience trait will suffice.
19pub trait AsyncComponent: Sized + 'static {
20 /// Messages which are received from commands executing in the background.
21 type CommandOutput: Debug + Send + 'static;
22
23 /// The message type that the component accepts as inputs.
24 type Input: Debug + 'static;
25
26 /// The message type that the component provides as outputs.
27 type Output: Debug + 'static;
28
29 /// The parameter used to initialize the component.
30 type Init;
31
32 /// The top-level widget of the component.
33 type Root: Debug + Clone;
34
35 /// The type that's used for storing widgets created for this component.
36 type Widgets: 'static;
37
38 /// Create a builder for this component.
39 #[must_use]
40 fn builder() -> AsyncComponentBuilder<Self> {
41 AsyncComponentBuilder::<Self>::default()
42 }
43
44 /// Initializes the root widget
45 #[must_use]
46 fn init_root() -> Self::Root;
47
48 /// Allows you to initialize the root widget with a temporary value
49 /// as a placeholder until the [`init()`](AsyncComponent::init)
50 /// future completes.
51 ///
52 /// This method does nothing by default.
53 #[must_use]
54 fn init_loading_widgets(_root: Self::Root) -> Option<LoadingWidgets> {
55 None
56 }
57
58 /// Creates the initial model and view, docking it into the component.
59 fn init(
60 init: Self::Init,
61 root: Self::Root,
62 sender: AsyncComponentSender<Self>,
63 ) -> impl std::future::Future<Output = AsyncComponentParts<Self>>;
64
65 /// Processes inputs received by the component.
66 #[allow(unused)]
67 fn update(
68 &mut self,
69 message: Self::Input,
70 sender: AsyncComponentSender<Self>,
71 root: &Self::Root,
72 ) -> impl std::future::Future<Output = ()> {
73 async {}
74 }
75
76 /// Defines how the component should respond to command updates.
77 #[allow(unused)]
78 fn update_cmd(
79 &mut self,
80 message: Self::CommandOutput,
81 sender: AsyncComponentSender<Self>,
82 root: &Self::Root,
83 ) -> impl std::future::Future<Output = ()> {
84 async {}
85 }
86
87 /// Updates the model and view upon completion of a command.
88 ///
89 /// Overriding this method is helpful if you need access to the widgets while processing a
90 /// command output.
91 ///
92 /// The default implementation of this method calls [`update_cmd`] followed by [`update_view`].
93 /// If you override this method while using the [`component`] macro, you must remember to call
94 /// [`update_view`] in your implementation. Otherwise, the view will not reflect the updated
95 /// model.
96 ///
97 /// [`update_cmd`]: Self::update_cmd
98 /// [`update_view`]: Self::update_view
99 /// [`component`]: relm4_macros::component
100 fn update_cmd_with_view(
101 &mut self,
102 widgets: &mut Self::Widgets,
103 message: Self::CommandOutput,
104 sender: AsyncComponentSender<Self>,
105 root: &Self::Root,
106 ) -> impl std::future::Future<Output = ()> {
107 async {
108 self.update_cmd(message, sender.clone(), root).await;
109 self.update_view(widgets, sender);
110 }
111 }
112
113 /// Updates the view after the model has been updated.
114 #[allow(unused)]
115 fn update_view(&self, widgets: &mut Self::Widgets, sender: AsyncComponentSender<Self>) {}
116
117 /// Updates the model and view when a new input is received.
118 ///
119 /// Overriding this method is helpful if you need access to the widgets while processing an
120 /// input.
121 ///
122 /// The default implementation of this method calls [`update`] followed by [`update_view`]. If
123 /// you override this method while using the [`component`] macro, you must remember to
124 /// call [`update_view`] in your implementation. Otherwise, the view will not reflect the
125 /// updated model.
126 ///
127 /// [`update`]: Self::update
128 /// [`update_view`]: Self::update_view
129 /// [`component`]: relm4_macros::component
130 fn update_with_view(
131 &mut self,
132 widgets: &mut Self::Widgets,
133 message: Self::Input,
134 sender: AsyncComponentSender<Self>,
135 root: &Self::Root,
136 ) -> impl std::future::Future<Output = ()> {
137 async {
138 self.update(message, sender.clone(), root).await;
139 self.update_view(widgets, sender);
140 }
141 }
142
143 /// Last method called before a component is shut down.
144 ///
145 /// This method is guaranteed to be called even when the entire application is shut down.
146 #[allow(unused)]
147 fn shutdown(&mut self, widgets: &mut Self::Widgets, output: Sender<Self::Output>) {}
148
149 /// An identifier for the component used for debug logging.
150 ///
151 /// The default implementation of this method uses the address of the component, but
152 /// implementations are free to provide more meaningful identifiers.
153 #[must_use]
154 fn id(&self) -> String {
155 format!("{:p}", &self)
156 }
157}
158
159/// Asynchronous variant of [`SimpleComponent`](crate::SimpleComponent).
160pub trait SimpleAsyncComponent: Sized + 'static {
161 /// The message type that the component accepts as inputs.
162 type Input: Debug + 'static;
163
164 /// The message type that the component provides as outputs.
165 type Output: Debug + 'static;
166
167 /// The parameter used to initialize the component.
168 type Init;
169
170 /// The top-level widget of the component.
171 type Root: Debug + Clone;
172
173 /// The type that's used for storing widgets created for this component.
174 type Widgets: 'static;
175
176 /// Initializes the root widget
177 #[must_use]
178 fn init_root() -> Self::Root;
179
180 /// Allows you to initialize the root widget with a temporary value
181 /// as a placeholder until the [`init()`](AsyncComponent::init)
182 /// future completes.
183 ///
184 /// This method does nothing by default.
185 #[must_use]
186 fn init_loading_widgets(_root: Self::Root) -> Option<LoadingWidgets> {
187 None
188 }
189
190 /// Creates the initial model and view, docking it into the component.
191 fn init(
192 init: Self::Init,
193 root: Self::Root,
194 sender: AsyncComponentSender<Self>,
195 ) -> impl std::future::Future<Output = AsyncComponentParts<Self>>;
196
197 /// Processes inputs received by the component.
198 #[allow(unused)]
199 fn update(
200 &mut self,
201 message: Self::Input,
202 sender: AsyncComponentSender<Self>,
203 ) -> impl std::future::Future<Output = ()> {
204 async {}
205 }
206
207 /// Defines how the component should respond to command updates.
208 #[allow(unused)]
209 fn update_cmd(
210 &mut self,
211 input: &Sender<Self::Input>,
212 output: Sender<Self::Output>,
213 ) -> impl std::future::Future<Output = ()> {
214 async {}
215 }
216
217 /// Updates the view after the model has been updated.
218 #[allow(unused)]
219 fn update_view(&self, widgets: &mut Self::Widgets, sender: AsyncComponentSender<Self>) {}
220
221 /// Last method called before a component is shut down.
222 ///
223 /// This method is guaranteed to be called even when the entire application is shut down.
224 #[allow(unused)]
225 fn shutdown(&mut self, widgets: &mut Self::Widgets, output: Sender<Self::Output>) {}
226}
227
228impl<C> AsyncComponent for C
229where
230 C: SimpleAsyncComponent,
231{
232 type Init = C::Init;
233 type Input = C::Input;
234 type Output = C::Output;
235 type Root = C::Root;
236 type Widgets = C::Widgets;
237
238 type CommandOutput = ();
239
240 fn init_root() -> Self::Root {
241 C::init_root()
242 }
243
244 fn init_loading_widgets(root: Self::Root) -> Option<LoadingWidgets> {
245 C::init_loading_widgets(root)
246 }
247
248 async fn init(
249 init: Self::Init,
250 root: Self::Root,
251 sender: AsyncComponentSender<Self>,
252 ) -> AsyncComponentParts<Self> {
253 C::init(init, root, sender).await
254 }
255
256 async fn update(
257 &mut self,
258 message: Self::Input,
259 sender: AsyncComponentSender<Self>,
260 _root: &Self::Root,
261 ) {
262 C::update(self, message, sender).await;
263 }
264
265 fn update_view(&self, widgets: &mut Self::Widgets, sender: AsyncComponentSender<Self>) {
266 C::update_view(self, widgets, sender);
267 }
268
269 fn shutdown(&mut self, widgets: &mut Self::Widgets, output: Sender<Self::Output>) {
270 self.shutdown(widgets, output);
271 }
272}
273
274/// An empty, non-interactive component as a placeholder for tests.
275impl SimpleAsyncComponent for () {
276 type Input = ();
277 type Output = ();
278 type Init = ();
279 type Root = ();
280 type Widgets = ();
281
282 fn init_root() -> Self::Root {}
283
284 async fn init(
285 _init: Self::Init,
286 _root: Self::Root,
287 _sender: AsyncComponentSender<Self>,
288 ) -> AsyncComponentParts<Self> {
289 AsyncComponentParts {
290 model: (),
291 widgets: (),
292 }
293 }
294}