Skip to content

Commit d3e3031

Browse files
authored
Merge pull request #91 from compio-rs/dev/boxed-components
feat(elm): boxed component
2 parents 8625193 + 60561a5 commit d3e3031

File tree

4 files changed

+173
-2
lines changed

4 files changed

+173
-2
lines changed

winio-elm/src/boxed.rs

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
use std::{convert::Infallible, fmt::Debug, pin::Pin};
2+
3+
use futures_util::FutureExt;
4+
5+
use crate::{Component, ComponentMessage, ComponentSender, channel::Channel};
6+
7+
type DynComponentSender<T> =
8+
Channel<ComponentMessage<<T as DynComponent>::Message, <T as DynComponent>::Event>>;
9+
10+
trait DynComponent {
11+
type Message;
12+
type Event;
13+
type Error: Debug;
14+
15+
fn start<'a>(
16+
&'a mut self,
17+
sender: &'a DynComponentSender<Self>,
18+
) -> Pin<Box<dyn Future<Output = Infallible> + 'a>>;
19+
20+
fn update_children(&mut self) -> Pin<Box<dyn Future<Output = Result<bool, Self::Error>> + '_>>;
21+
22+
fn update<'a>(
23+
&'a mut self,
24+
message: Self::Message,
25+
sender: &'a DynComponentSender<Self>,
26+
) -> Pin<Box<dyn Future<Output = Result<bool, Self::Error>> + 'a>>;
27+
28+
fn render(&mut self, sender: &DynComponentSender<Self>) -> Result<(), Self::Error>;
29+
30+
fn render_children(&mut self) -> Result<(), Self::Error>;
31+
}
32+
33+
impl<T: Component + 'static> DynComponent for T {
34+
type Error = <T as Component>::Error;
35+
type Event = <T as Component>::Event;
36+
type Message = <T as Component>::Message;
37+
38+
fn start<'a>(
39+
&'a mut self,
40+
sender: &'a DynComponentSender<Self>,
41+
) -> Pin<Box<dyn Future<Output = Infallible> + 'a>> {
42+
let sender = ComponentSender::<T>::from_ref(sender);
43+
Box::pin(Component::start(self, sender).map(|a| a))
44+
}
45+
46+
fn update_children(&mut self) -> Pin<Box<dyn Future<Output = Result<bool, Self::Error>> + '_>> {
47+
Box::pin(Component::update_children(self))
48+
}
49+
50+
fn update<'a>(
51+
&'a mut self,
52+
message: Self::Message,
53+
sender: &'a DynComponentSender<Self>,
54+
) -> Pin<Box<dyn Future<Output = Result<bool, Self::Error>> + 'a>> {
55+
let sender = ComponentSender::<T>::from_ref(sender);
56+
Box::pin(Component::update(self, message, sender))
57+
}
58+
59+
fn render(&mut self, sender: &DynComponentSender<Self>) -> Result<(), Self::Error> {
60+
let sender = ComponentSender::<T>::from_ref(sender);
61+
Component::render(self, sender)
62+
}
63+
64+
fn render_children(&mut self) -> Result<(), Self::Error> {
65+
Component::render_children(self)
66+
}
67+
}
68+
69+
/// A boxed component. It is not initialized directly, but constructed by
70+
/// [`Child::into_boxed`] or [`Root::into_boxed`].
71+
///
72+
/// [`Child::into_boxed`]: crate::Child::into_boxed
73+
/// [`Root::into_boxed`]: crate::Root::into_boxed
74+
pub struct BoxComponent<M, Ev, Err>(Box<dyn DynComponent<Message = M, Event = Ev, Error = Err>>);
75+
76+
impl<M, Ev, Err> BoxComponent<M, Ev, Err> {
77+
pub(crate) fn new<T: Component<Message = M, Event = Ev, Error = Err> + 'static>(
78+
component: T,
79+
) -> Self {
80+
Self(Box::new(component))
81+
}
82+
}
83+
84+
impl<M, Ev, Err: Debug> Component for BoxComponent<M, Ev, Err> {
85+
type Error = Err;
86+
type Event = Ev;
87+
type Init<'a> = Infallible;
88+
type Message = M;
89+
90+
async fn init(
91+
init: Self::Init<'_>,
92+
_sender: &ComponentSender<Self>,
93+
) -> Result<Self, Self::Error> {
94+
match init {}
95+
}
96+
97+
async fn start(&mut self, sender: &ComponentSender<Self>) -> ! {
98+
let sender = &sender.0;
99+
match self.0.start(sender).await {}
100+
}
101+
102+
async fn update_children(&mut self) -> Result<bool, Self::Error> {
103+
self.0.update_children().await
104+
}
105+
106+
async fn update(
107+
&mut self,
108+
message: Self::Message,
109+
sender: &ComponentSender<Self>,
110+
) -> Result<bool, Self::Error> {
111+
let sender = &sender.0;
112+
self.0.update(message, sender).await
113+
}
114+
115+
fn render(&mut self, sender: &ComponentSender<Self>) -> Result<(), Self::Error> {
116+
let sender = &sender.0;
117+
self.0.render(sender)
118+
}
119+
120+
fn render_children(&mut self) -> Result<(), Self::Error> {
121+
self.0.render_children()
122+
}
123+
}
124+
125+
impl<M, Ev, Err> Debug for BoxComponent<M, Ev, Err> {
126+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127+
f.debug_struct("BoxComponent").finish_non_exhaustive()
128+
}
129+
}

winio-elm/src/child.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use winio_handle::{
1515
use winio_primitive::{Failable, Layoutable, Point, Rect, Size};
1616

1717
use super::ComponentMessage;
18-
use crate::{Component, ComponentSender, Root};
18+
use crate::{BoxComponent, Component, ComponentSender, Root};
1919

2020
/// Helper to embed one component into another. It handles different types of
2121
/// messages and events.
@@ -150,6 +150,18 @@ impl<T: Component> Child<T> {
150150
}
151151
}
152152

153+
impl<T: Component + 'static> Child<T> {
154+
/// Box the component.
155+
pub fn into_boxed(self) -> Child<BoxComponent<T::Message, T::Event, T::Error>> {
156+
let sender = ComponentSender(self.sender.0);
157+
Child {
158+
model: BoxComponent::new(self.model),
159+
sender,
160+
msg_cache: self.msg_cache,
161+
}
162+
}
163+
}
164+
153165
impl<T: Component> Deref for Child<T> {
154166
type Target = T;
155167

winio-elm/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,5 +128,8 @@ pub use macros::*;
128128
mod run;
129129
pub use run::*;
130130

131+
mod boxed;
132+
pub use boxed::*;
133+
131134
#[cfg(feature = "gen_blocks")]
132135
mod stream;

winio-elm/src/run.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use futures_util::{FutureExt, Stream};
66

77
#[cfg(feature = "gen_blocks")]
88
use crate::stream::stream;
9-
use crate::{Child, Component, ComponentMessage, ComponentSender};
9+
use crate::{BoxComponent, Child, Component, ComponentMessage, ComponentSender};
1010

1111
/// Events yielded by the [`run`].
1212
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -126,6 +126,14 @@ fn run_events_impl<'a, T: Component>(
126126
}
127127
}
128128

129+
impl<T: Component + 'static> Root<T> {
130+
/// Box the component.
131+
pub fn into_boxed(self) -> Root<BoxComponent<T::Message, T::Event, T::Error>> {
132+
let sender = ComponentSender(self.sender.0);
133+
Root::new(BoxComponent::new(self.model), sender)
134+
}
135+
}
136+
129137
#[cfg(test)]
130138
mod test {
131139
use async_stream::stream;
@@ -226,4 +234,23 @@ mod test {
226234
}
227235

228236
fn assert_send_sync<T: Send + Sync>(_: &T) {}
237+
238+
#[compio::test]
239+
async fn test_boxed_run() {
240+
let mut boxed_child = Root::<TestComponent>::init(vec![
241+
TestMessage::Msg1,
242+
TestMessage::Msg2,
243+
TestMessage::Msg1,
244+
])
245+
.await
246+
.expect("failed to init component")
247+
.into_boxed();
248+
let events = boxed_child.run();
249+
let expects = [TestEvent::Event1, TestEvent::Event2, TestEvent::Event1];
250+
let zip = events.zip(futures_util::stream::iter(expects.into_iter()));
251+
let mut zip = std::pin::pin!(zip);
252+
while let Some((e, ex)) = zip.next().await {
253+
assert_eq!(e, RunEvent::Event(ex));
254+
}
255+
}
229256
}

0 commit comments

Comments
 (0)