bloom_core/component.rs
1use std::{any::Any, sync::Arc};
2
3use crate::Element;
4use async_trait::async_trait;
5
6/// The component trait is the core of the bloom library.
7/// It represents the basic unit of a bloom application.
8#[async_trait]
9pub trait Component: PartialEq<Self> + Send + Sync {
10 /// The node type is a representation of the native UI elements of the target renderer.
11 type Node: From<String>;
12 /// The error type for the entire application.
13 type Error;
14 /// Components are roughly equivalent to React components.
15 /// The struct itself represents the props of the component.
16 /// The trait has a render method which contains the component logic.
17 /// It should return the [Element] type, which is easily generated
18 /// using the rsx macro from the bloom-rsx crate.
19 /// Within the render function, hooks can be used such as [bloom_core::use_state] and [bloom_core::use_effect].
20 /// ```
21 /// use bloom_core::Component;
22 ///
23 /// #[derive(PartialEq, Debug)]
24 /// struct Counter {
25 /// initial_count: i32
26 /// }
27 ///
28 /// #[async_trait]
29 /// impl Component for Counter {
30 /// type Node = HtmlNode;
31 /// type Error = ();
32 ///
33 /// async fn render(self: Arc<Self>) -> Result<Element<Self::Node, Self::Error>, Self::Error> {
34 /// let count = use_state(|| self.initial_count);
35 ///
36 /// rsx!(
37 /// <div>{count}</div>
38 /// <button on_click={move |_| count.update(|count| *count + 1)}>Increment</button>
39 /// )
40 /// ```
41 ///
42 /// Components should usually implement a builder pattern for construction using the bloom-rsx macro./// Components are roughly equivalent to React components.
43 /// The struct itself represents the props of the component.
44 /// The trait has a render method which contains the component logic.
45 /// It should return the [Element] type, which is easily generated
46 /// using the rsx macro from the bloom-rsx crate.
47 /// Within the render function, hooks can be used such as [use_state] and [use_effect].
48 /// ```
49 /// use bloom_core::Component;
50 ///
51 /// #[derive(PartialEq, Debug)]
52 /// struct Counter {
53 /// initial_count: i32
54 /// }
55 ///
56 /// #[async_trait]
57 /// impl Component for Counter {
58 /// type Node = HtmlNode;
59 /// type Error = ();
60 ///
61 /// async fn render(self: Arc<Self>) -> Result<Element<Self::Node, Self::Error>, Self::Error> {
62 /// let count = use_state(|| self.initial_count);
63 ///
64 /// rsx!(
65 /// <div>{count}</div>
66 /// <button on_click={move |_| count.update(|count| *count + 1)}>Increment</button>
67 /// )
68 /// ```
69 ///
70 /// Components should usually implement a builder pattern for construction using the bloom-rsx macro.
71 async fn render(self: Arc<Self>) -> Result<Element<Self::Node, Self::Error>, Self::Error>;
72}
73
74impl<N, E, C> From<C> for Element<N, E>
75where
76 N: From<String>,
77 C: Component<Node = N, Error = E> + Sized + 'static,
78{
79 fn from(component: C) -> Self {
80 Element::Component(Arc::new(component))
81 }
82}
83
84#[derive(PartialEq)]
85pub enum ComponentDiff {
86 NewType,
87 NewProps,
88 Equal,
89}
90
91#[async_trait]
92pub trait AnyComponent {
93 type Node: From<String>;
94 type Error;
95
96 fn compare(&self, other: &dyn Any) -> ComponentDiff;
97 fn as_any(&self) -> &dyn Any;
98 async fn render(self: Arc<Self>) -> Result<Element<Self::Node, Self::Error>, Self::Error>;
99}
100
101#[async_trait]
102impl<C> AnyComponent for C
103where
104 C: Component + 'static,
105 Self: Sized,
106{
107 type Node = C::Node;
108 type Error = C::Error;
109
110 fn compare(&self, other: &dyn Any) -> ComponentDiff {
111 let this = self;
112 if let Some(other) = other.downcast_ref::<C>() {
113 if this == other {
114 return ComponentDiff::Equal;
115 } else {
116 return ComponentDiff::NewProps;
117 }
118 }
119 ComponentDiff::NewType
120 }
121
122 fn as_any(&self) -> &dyn Any {
123 self
124 }
125
126 async fn render(self: Arc<Self>) -> Result<Element<Self::Node, Self::Error>, Self::Error> {
127 Component::render(self).await
128 }
129}
130
131impl<N, E> PartialEq for &(dyn AnyComponent<Node = N, Error = E> + 'static)
132where
133 N: From<String>,
134{
135 fn eq(&self, other: &Self) -> bool {
136 self.compare(other.as_any()) == ComponentDiff::Equal
137 }
138}