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}