sauron_core/dom/
effects.rs

1use crate::dom::Cmd;
2use std::future::ready;
3
4/// Effects is a convenient way to group Msg for component to execute subsequent updates based on certain conditions.
5/// This can be used for doing animation and incremental changes to the view to provide an effect
6/// of transition or animation.
7///
8/// Effects contains 2 types of Messages. The local messages which will be executed in its
9/// own component on the next update loop. The other type is the external effects which are Messages
10/// that are sent to the parent Component in response to an event that has been triggerred.
11pub struct Effects<MSG, XMSG> {
12    /// Messages that will be executed locally in the Component
13    pub local: Vec<Cmd<MSG>>,
14    /// effects that will be executed on the parent Component which instantiate
15    /// this component
16    pub external: Vec<Cmd<XMSG>>,
17}
18
19impl<MSG, XMSG> Effects<MSG, XMSG>
20where
21    MSG: 'static,
22{
23    /// create a new Effects with local and external expects respectively
24    pub fn new(
25        local: impl IntoIterator<Item = MSG>,
26        external: impl IntoIterator<Item = XMSG>,
27    ) -> Self
28    where
29        XMSG: 'static,
30    {
31        Self {
32            local: local.into_iter().map(|l| Cmd::once(ready(l))).collect(),
33            external: external.into_iter().map(|x| Cmd::once(ready(x))).collect(),
34        }
35    }
36
37    /// Create an Effects with  local messages that will be executed on the next update loop on this Component
38    pub fn with_local(local: impl IntoIterator<Item = MSG>) -> Self {
39        Self {
40            local: local.into_iter().map(|l| Cmd::once(ready(l))).collect(),
41            external: vec![],
42        }
43    }
44
45    /// Create an Effects with extern messages that will be executed on the parent Component
46    pub fn with_external(external: impl IntoIterator<Item = XMSG>) -> Self
47    where
48        XMSG: 'static,
49    {
50        Self {
51            local: vec![],
52            external: external.into_iter().map(|x| Cmd::once(ready(x))).collect(),
53        }
54    }
55
56    /// Create and empty Effects
57    pub fn none() -> Self {
58        Self {
59            local: vec![],
60            external: vec![],
61        }
62    }
63
64    /// Map the local messages of this Effects such that MSG will be transposed into
65    /// MSG2 with the use of the mapping function `f`.
66    ///
67    /// The external messages stays the same.
68    pub fn map_msg<F, MSG2>(self, f: F) -> Effects<MSG2, XMSG>
69    where
70        F: Fn(MSG) -> MSG2 + Clone + 'static,
71        MSG2: 'static,
72    {
73        let Effects { local, external } = self;
74        Effects {
75            local: local.into_iter().map(|l| l.map_msg(f.clone())).collect(),
76            external,
77        }
78    }
79
80    /// Map the external messages of this Effects such that XMSG will be transposed into XMSG2
81    /// with the use of the mapping function `f`
82    pub fn map_external<F, XMSG2>(self, f: F) -> Effects<MSG, XMSG2>
83    where
84        F: Fn(XMSG) -> XMSG2 + Clone + 'static,
85        XMSG: 'static,
86        XMSG2: 'static,
87    {
88        let Effects { local, external } = self;
89        Effects {
90            local,
91            external: external.into_iter().map(|l| l.map_msg(f.clone())).collect(),
92        }
93    }
94
95    /// derives an Effects which contains only local effects by transforming the external messages
96    /// and mapping them with function `f` such that they can be of the same type as local effects
97    /// them merge them together into local effects.
98    ///
99    pub fn localize<F, XMSG2>(self, f: F) -> Effects<XMSG, XMSG2>
100    where
101        F: Fn(MSG) -> XMSG + Clone + 'static,
102        XMSG: 'static,
103        XMSG2: 'static,
104    {
105        let Effects { local, external } = self;
106
107        Effects {
108            local: external
109                .into_iter()
110                .chain(local.into_iter().map(|x| x.map_msg(f.clone())))
111                .collect(),
112            external: vec![],
113        }
114    }
115
116    /// Append this msgs to the local effects
117    pub fn append_local(mut self, local: impl IntoIterator<Item = MSG>) -> Self {
118        self.local
119            .extend(local.into_iter().map(|l| Cmd::once(ready(l))));
120        self
121    }
122
123    /// Merge all the internal objects of this Vec of Effects to produce only one.
124    pub fn batch(all_effects: impl IntoIterator<Item = Self>) -> Self {
125        let mut local = vec![];
126        let mut external = vec![];
127        for effect in all_effects {
128            local.extend(effect.local);
129            external.extend(effect.external);
130        }
131        Effects { local, external }
132    }
133
134    /// Extern the local and external MSG of this Effect
135    pub fn extend(
136        mut self,
137        local: impl IntoIterator<Item = MSG>,
138        external: impl IntoIterator<Item = XMSG>,
139    ) -> Self
140    where
141        XMSG: 'static,
142    {
143        self.local
144            .extend(local.into_iter().map(|l| Cmd::once(ready(l))));
145        self.external
146            .extend(external.into_iter().map(|x| Cmd::once(ready(x))));
147        self
148    }
149}
150
151impl<MSG, XMSG> From<Cmd<MSG>> for Effects<MSG, XMSG> {
152    fn from(task: Cmd<MSG>) -> Effects<MSG, XMSG> {
153        Effects {
154            local: vec![task],
155            external: vec![],
156        }
157    }
158}
159
160impl<MSG> Effects<MSG, MSG>
161where
162    MSG: 'static,
163{
164    /// merge external msg into local msg, if they are of the same type
165    pub fn merge(self) -> Effects<MSG, ()> {
166        let Effects { local, external } = self;
167
168        Effects {
169            local: local.into_iter().chain(external).collect(),
170            external: vec![],
171        }
172    }
173}