hirola_core/
effect.rs

1use std::fmt::Display;
2
3use futures_signals::signal::{DedupeMap, Mutable, Signal, SignalExt};
4
5use crate::prelude::GenericNode;
6
7/// Trait for defining side effects.
8///
9/// The `SideEffect` trait allows defining asynchronous side effects that are executed as futures.
10/// Implementations of this trait should represent tasks that need to be performed concurrently
11/// with the rendering process, such as making HTTP requests, updating global state, or scheduling
12/// timers.
13///
14/// When used in conjunction with the `Dom`, side effects can be attached to specific DOM nodes and
15/// executed during the rendering process, ensuring proper handling of asynchronous operations
16/// within the frontend application.
17pub trait SideEffect<Attr: EffectAttribute<Handler = Self>, Effect, Node> {
18    fn effect(&self, node: &Node, attr: Attr, effect: Effect);
19}
20
21pub trait EffectAttribute {
22    type Handler;
23    fn read_as_attr(&self) -> String;
24}
25
26pub struct DefaultAttributeEffect;
27
28pub struct DefaultAttrStr(pub &'static str);
29
30impl EffectAttribute for DefaultAttrStr {
31    type Handler = DefaultAttributeEffect;
32    fn read_as_attr(&self) -> String {
33        self.0.to_owned()
34    }
35}
36
37macro_rules! impl_simple_effect {
38    ($effect_type:ty) => {
39        impl<Node: GenericNode> SideEffect<DefaultAttrStr, $effect_type, Node>
40            for DefaultAttributeEffect
41        {
42            fn effect(&self, node: &Node, attr: DefaultAttrStr, effect: $effect_type) {
43                node.set_attribute(attr.0, &effect.to_string())
44            }
45        }
46    };
47}
48
49// Usage of the macro for different types
50impl_simple_effect!(&str);
51impl_simple_effect!(String);
52impl_simple_effect!(&String);
53impl_simple_effect!(bool);
54impl_simple_effect!(usize);
55impl_simple_effect!(i8);
56impl_simple_effect!(i64);
57impl_simple_effect!(i32);
58impl_simple_effect!(i128);
59impl_simple_effect!(u8);
60impl_simple_effect!(u16);
61impl_simple_effect!(u32);
62impl_simple_effect!(u64);
63impl_simple_effect!(u128);
64
65// macro_rules! impl_signal_effect {
66//     ($effect_type:ty) => {
67//         impl<Node: GenericNode, A: Display + 'static + Clone + PartialEq>
68//             SideEffect<DefaultAttrStr, $effect_type, Node> for DefaultAttributeEffect
69//         {
70//             fn effect(&self, node: &Node, attr: DefaultAttrStr, effect: $effect_type) {
71//                 let dom = node.clone();
72//                 let future = SignalExt::dedupe_map(effect, move |value| {
73//                     GenericNode::set_attribute(&dom, &attr.0, &value.to_string());
74//                 })
75//                 .to_future();
76//                 node.effect(future);
77//             }
78//         }
79//     };
80// }
81
82impl<Node: GenericNode, A: Display + 'static + Clone + PartialEq>
83    SideEffect<DefaultAttrStr, Mutable<A>, Node> for DefaultAttributeEffect
84{
85    fn effect(&self, node: &Node, attr: DefaultAttrStr, effect: Mutable<A>) {
86        let dom = node.clone();
87        let future = SignalExt::dedupe_map(effect.signal_cloned(), move |value| {
88            GenericNode::set_attribute(&dom, attr.0, &value.to_string());
89        })
90        .to_future();
91        node.effect(future);
92    }
93}
94
95impl<
96        Node: GenericNode,
97        F: FnMut(&mut <S as Signal>::Item) -> A + 'static,
98        S: Signal + 'static,
99        A: Display + 'static + Clone + PartialEq,
100    > SideEffect<DefaultAttrStr, DedupeMap<S, F>, Node> for DefaultAttributeEffect
101where
102    <S as Signal>::Item: PartialEq,
103{
104    fn effect(&self, node: &Node, attr: DefaultAttrStr, effect: DedupeMap<S, F>) {
105        let dom = node.clone();
106        let future = SignalExt::dedupe_map(effect, move |value| {
107            GenericNode::set_attribute(&dom, attr.0, &value.to_string());
108        })
109        .to_future();
110        node.effect(future);
111    }
112}
113
114use futures_signals::signal::Dedupe;
115
116impl<Node: GenericNode, S: Signal<Item = A> + 'static, A: Display + 'static + Copy + PartialEq>
117    SideEffect<DefaultAttrStr, Dedupe<S>, Node> for DefaultAttributeEffect
118where
119    <S as Signal>::Item: PartialEq,
120{
121    fn effect(&self, node: &Node, attr: DefaultAttrStr, effect: Dedupe<S>) {
122        let dom = node.clone();
123        let future = SignalExt::dedupe_map(effect, move |value| {
124            GenericNode::set_attribute(&dom, attr.0, &value.to_string());
125        })
126        .to_future();
127        node.effect(future);
128    }
129}
130
131use futures_signals::signal::DedupeCloned;
132
133impl<
134        Node: GenericNode,
135        S: Signal<Item = A> + 'static,
136        A: Display + 'static + Clone + PartialEq,
137    > SideEffect<DefaultAttrStr, DedupeCloned<S>, Node> for DefaultAttributeEffect
138where
139    <S as Signal>::Item: PartialEq,
140{
141    fn effect(&self, node: &Node, attr: DefaultAttrStr, effect: DedupeCloned<S>) {
142        let dom = node.clone();
143        let future = SignalExt::dedupe_map(effect, move |value| {
144            GenericNode::set_attribute(&dom, attr.0, &value.to_string());
145        })
146        .to_future();
147        node.effect(future);
148    }
149}
150
151use futures_signals::signal::Map;
152
153impl<
154        Node: GenericNode,
155        F: FnMut(<S as Signal>::Item) -> A + 'static,
156        S: Signal + 'static,
157        A: Display + 'static + Clone + PartialEq,
158    > SideEffect<DefaultAttrStr, Map<S, F>, Node> for DefaultAttributeEffect
159where
160    <S as Signal>::Item: PartialEq,
161{
162    fn effect(&self, node: &Node, attr: DefaultAttrStr, effect: Map<S, F>) {
163        let dom = node.clone();
164        let future = SignalExt::map(effect, move |value| {
165            GenericNode::set_attribute(&dom, attr.0, &value.to_string());
166        })
167        .to_future();
168        node.effect(future);
169    }
170}