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}