Skip to main content

euv_core/vdom/cast/
impl.rs

1use crate::*;
2
3/// Converts a `Vec<VirtualNode>` into a `VirtualNode::Fragment`.
4///
5/// This enables using a `Vec<VirtualNode>` directly in the `html!` macro
6/// without manually wrapping it in `VirtualNode::Fragment(...)`.
7///
8/// # Returns
9///
10/// - `VirtualNode` - A `VirtualNode::Fragment` containing the nodes, or
11///   `VirtualNode::Empty` if the vector is empty.
12impl From<Vec<VirtualNode>> for VirtualNode {
13    fn from(nodes: Vec<VirtualNode>) -> Self {
14        if nodes.is_empty() {
15            VirtualNode::Empty
16        } else {
17            VirtualNode::Fragment(nodes)
18        }
19    }
20}
21
22/// Converts an `Option<VirtualNode>` into a `VirtualNode`.
23///
24/// `Some(node)` returns the inner node, `None` returns `VirtualNode::Empty`.
25///
26/// # Returns
27///
28/// - `VirtualNode` - The inner node if `Some`, otherwise `VirtualNode::Empty`.
29impl From<Option<VirtualNode>> for VirtualNode {
30    fn from(node: Option<VirtualNode>) -> Self {
31        match node {
32            Some(node) => node,
33            None => VirtualNode::Empty,
34        }
35    }
36}
37
38/// Converts an `Option<Vec<VirtualNode>>` into a `VirtualNode`.
39///
40/// `Some(vec)` converts the vector into a `VirtualNode::Fragment` (or `Empty`
41/// if the vector is empty), `None` returns `VirtualNode::Empty`.
42///
43/// # Returns
44///
45/// - `VirtualNode` - A `VirtualNode::Fragment` if `Some` with nodes,
46///   `VirtualNode::Empty` if `None` or the vector is empty.
47impl From<Option<Vec<VirtualNode>>> for VirtualNode {
48    fn from(nodes: Option<Vec<VirtualNode>>) -> Self {
49        match nodes {
50            Some(nodes) => nodes.into(),
51            None => VirtualNode::Empty,
52        }
53    }
54}
55
56/// Wraps a `FnMut(&mut HookContext) -> VirtualNode` closure into a `DynamicNode`.
57///
58/// This enables writing `{move |_: &mut HookContext| html! { ... }}` directly in HTML markup
59/// without explicit `DynamicNode` construction.
60impl<F> From<F> for VirtualNode
61where
62    F: FnMut(&mut HookContext) -> VirtualNode + 'static,
63{
64    /// Wraps this closure into a `VirtualNode::Dynamic` with a fresh hook context.
65    ///
66    /// # Returns
67    ///
68    /// - `VirtualNode` - A dynamic virtual node wrapping this closure.
69    fn from(render_fn: F) -> Self {
70        VirtualNode::create_dynamic(render_fn)
71    }
72}
73
74/// Converts a `String` into a text virtual node.
75impl From<String> for VirtualNode {
76    /// Converts this string into a text virtual node.
77    ///
78    /// # Returns
79    ///
80    /// - `VirtualNode` - A text virtual node.
81    fn from(text: String) -> Self {
82        VirtualNode::Text(TextNode::new(text, None))
83    }
84}
85
86/// Converts a `&str` into a text virtual node.
87impl From<&str> for VirtualNode {
88    /// Converts this string slice into a text virtual node.
89    ///
90    /// # Returns
91    ///
92    /// - `VirtualNode` - A text virtual node.
93    fn from(text: &str) -> Self {
94        VirtualNode::Text(TextNode::new(text.to_string(), None))
95    }
96}
97
98/// Converts an `i32` into a text virtual node.
99impl From<i32> for VirtualNode {
100    /// Converts this integer into a text virtual node.
101    ///
102    /// # Returns
103    ///
104    /// - `VirtualNode` - A text virtual node.
105    fn from(value: i32) -> Self {
106        VirtualNode::Text(TextNode::new(value.to_string(), None))
107    }
108}
109
110/// Converts a `usize` into a text virtual node.
111impl From<usize> for VirtualNode {
112    /// Converts this unsigned integer into a text virtual node.
113    ///
114    /// # Returns
115    ///
116    /// - `VirtualNode` - A text virtual node.
117    fn from(value: usize) -> Self {
118        VirtualNode::Text(TextNode::new(value.to_string(), None))
119    }
120}
121
122/// Converts a `bool` into a text virtual node.
123impl From<bool> for VirtualNode {
124    /// Converts this boolean into a text virtual node.
125    ///
126    /// # Returns
127    ///
128    /// - `VirtualNode` - A text virtual node.
129    fn from(value: bool) -> Self {
130        VirtualNode::Text(TextNode::new(value.to_string(), None))
131    }
132}
133
134/// Converts a signal into a reactive text virtual node.
135impl<T> From<Signal<T>> for VirtualNode
136where
137    T: Clone + PartialEq + Display + 'static,
138{
139    /// Converts this signal into a reactive text virtual node.
140    ///
141    /// # Returns
142    ///
143    /// - `VirtualNode` - A reactive text virtual node.
144    fn from(signal: Signal<T>) -> Self {
145        signal.as_reactive_text()
146    }
147}
148
149/// Converts a signal into a reactive text node with listener wiring.
150impl<T> AsReactiveText for Signal<T>
151where
152    T: Clone + PartialEq + Display + 'static,
153{
154    /// Creates a reactive text node that auto-updates when the signal changes.
155    ///
156    /// Internally creates a bridge `Signal<String>` that subscribes to the
157    /// source signal and updates the text content on every change.
158    ///
159    /// # Returns
160    ///
161    /// - `VirtualNode` - A text virtual node with reactive signal binding.
162    fn as_reactive_text(&self) -> VirtualNode {
163        let source: Signal<T> = *self;
164        let string_signal: Signal<String> = Signal::create(source.get().to_string());
165        let string_signal_clone: Signal<String> = string_signal;
166        source.subscribe(move || {
167            string_signal_clone.set(source.get().to_string());
168        });
169        VirtualNode::Text(TextNode::new(string_signal.get(), Some(string_signal)))
170    }
171}
172
173/// Constructs an `EventAdapter` that wraps any event-compatible value.
174impl<T> EventAdapter<T> {
175    /// Returns the inner wrapped value, consuming the adapter.
176    ///
177    /// # Returns
178    ///
179    /// - `T` - The inner value.
180    pub(crate) fn into_inner(self) -> T {
181        self.inner
182    }
183}
184
185/// Adapts a `FnMut(Event)` closure into an `AttributeValue::Event`.
186///
187/// Wraps the closure into a `NativeEventHandler` and returns it as an
188/// event attribute value. This replaces the `__EventWrapper<F>` type
189/// that was previously generated inline by the `html!` macro.
190impl<F> EventAdapter<F>
191where
192    F: FnMut(Event) + 'static,
193{
194    /// Converts the wrapped closure into an event `AttributeValue`.
195    ///
196    /// # Arguments
197    ///
198    /// - `&'static str` - The event name string to associate with the handler.
199    ///
200    /// # Returns
201    ///
202    /// - `AttributeValue` - An `AttributeValue::Event` wrapping the handler.
203    pub fn into_attribute(self, event_name: &'static str) -> AttributeValue {
204        AttributeValue::Event(NativeEventHandler::create(event_name, self.into_inner()))
205    }
206}
207
208/// Converts an event with a specific event name into an `AttributeValue`.
209impl<F> From<EventNamedAdapter<F>> for AttributeValue
210where
211    F: FnMut(Event) + 'static,
212{
213    /// Converts the wrapped closure with event name into an event `AttributeValue`.
214    ///
215    /// # Returns
216    ///
217    /// - `AttributeValue` - An `AttributeValue::Event` wrapping the handler.
218    fn from(adapter: EventNamedAdapter<F>) -> Self {
219        AttributeValue::Event(NativeEventHandler::create(
220            adapter.get_event_name(),
221            adapter.inner,
222        ))
223    }
224}
225
226/// Converts an event named adapter with `NativeEventHandler` into an `AttributeValue`.
227impl From<EventNamedAdapter<NativeEventHandler>> for AttributeValue {
228    /// Converts the wrapped handler with event name into an event `AttributeValue`.
229    ///
230    /// # Returns
231    ///
232    /// - `AttributeValue` - An `AttributeValue::Event` wrapping the handler.
233    fn from(mut adapter: EventNamedAdapter<NativeEventHandler>) -> Self {
234        let event_name: &'static str = adapter.get_event_name();
235        adapter.get_mut_inner().set_event_name(event_name);
236        AttributeValue::Event(adapter.inner)
237    }
238}
239
240/// Converts an event named adapter with optional shared closure into an `AttributeValue`.
241///
242/// `Some(callback)` becomes `AttributeValue::Event` by wrapping the shared closure
243/// into a `NativeEventHandler` with the adapter's event name, and `None` becomes
244/// `AttributeValue::Text(String::new())`.
245impl From<EventNamedAdapter<Option<Rc<dyn Fn(Event)>>>> for AttributeValue {
246    /// Converts the wrapped optional shared closure with event name into an event `AttributeValue`.
247    ///
248    /// # Returns
249    ///
250    /// - `AttributeValue` - An event attribute if `Some`, otherwise an empty text attribute.
251    fn from(adapter: EventNamedAdapter<Option<Rc<dyn Fn(Event)>>>) -> Self {
252        let event_name: &'static str = adapter.get_event_name();
253        match adapter.inner {
254            Some(callback) => AttributeValue::Event(NativeEventHandler::create(
255                event_name,
256                move |event: Event| {
257                    callback(event);
258                },
259            )),
260            None => AttributeValue::Text(String::new()),
261        }
262    }
263}
264
265/// Adapts an owned `NativeEventHandler` into an `AttributeValue::Event` directly.
266///
267/// When the user already provides a `NativeEventHandler`, the handler is
268/// re-wrapped with the given `event_name` to ensure the DOM event listener
269/// is bound to the correct event type (e.g., "click" rather than "onclick").
270impl EventAdapter<NativeEventHandler> {
271    /// Converts the wrapped handler into an event `AttributeValue`.
272    ///
273    /// Re-wraps the handler with the provided `event_name` so that the
274    /// DOM event listener uses the correct event type string.
275    ///
276    /// # Arguments
277    ///
278    /// - `&'static str` - The event name to bind the handler to.
279    ///
280    /// # Returns
281    ///
282    /// - `AttributeValue` - An `AttributeValue::Event` containing the re-wrapped handler.
283    pub fn into_attribute(self, event_name: &'static str) -> AttributeValue {
284        let mut handler: NativeEventHandler = self.into_inner();
285        handler.set_event_name(event_name);
286        AttributeValue::Event(handler)
287    }
288}
289
290/// Adapts an `Option<NativeEventHandler>` into an `AttributeValue`.
291///
292/// `Some(handler)` becomes `AttributeValue::Event(handler)` re-wrapped with the
293/// given event name, and `None` becomes `AttributeValue::Text(String::new())`.
294impl EventAdapter<Option<NativeEventHandler>> {
295    /// Converts the wrapped optional handler into an attribute value.
296    ///
297    /// Re-wraps a `Some` handler with the provided `event_name` so that the
298    /// DOM event listener uses the correct event type string.
299    ///
300    /// # Arguments
301    ///
302    /// - `&'static str` - The event name to bind the handler to.
303    ///
304    /// # Returns
305    ///
306    /// - `AttributeValue` - An event attribute if `Some`, otherwise an empty text attribute.
307    pub fn into_attribute(self, event_name: &'static str) -> AttributeValue {
308        match self.into_inner() {
309            Some(handler) => EventNamedAdapter::new(handler, event_name).into(),
310            None => AttributeValue::Text(String::new()),
311        }
312    }
313}
314
315/// Adapts an `Option<Rc<dyn Fn(Event)>>` into an `AttributeValue`.
316///
317/// `Some(callback)` becomes `AttributeValue::Event` by wrapping the shared closure
318/// into a `NativeEventHandler`, and `None` becomes `AttributeValue::Text(String::new())`.
319/// This supports component Props that use `Option<Rc<dyn Fn(Event)>>` for event callbacks.
320impl EventAdapter<Option<Rc<dyn Fn(Event)>>> {
321    /// Converts the wrapped optional shared closure into an attribute value.
322    ///
323    /// # Arguments
324    ///
325    /// - `&'static str` - The event name to bind the handler to.
326    ///
327    /// # Returns
328    ///
329    /// - `AttributeValue` - An event attribute if `Some`, otherwise an empty text attribute.
330    pub fn into_attribute(self, event_name: &'static str) -> AttributeValue {
331        match self.into_inner() {
332            Some(callback) => AttributeValue::Event(NativeEventHandler::create(
333                event_name,
334                move |event: Event| {
335                    callback(event);
336                },
337            )),
338            None => AttributeValue::Text(String::new()),
339        }
340    }
341}
342
343/// Constructs an `AttrValueAdapter` that wraps any attribute-compatible value.
344impl<T> AttrValueAdapter<T> {
345    /// Returns the inner wrapped value, consuming the adapter.
346    ///
347    /// # Returns
348    ///
349    /// - `T` - The inner value.
350    pub(crate) fn into_inner(self) -> T {
351        self.inner
352    }
353}
354
355/// Adapts a `FnMut(Event)` closure into a callback `AttributeValue`.
356///
357/// This handles the case where a closure is used as a component callback prop.
358/// The closure is converted via `IntoCallbackAttribute::into_callback_attribute()`.
359impl<F> AttrValueAdapter<F>
360where
361    F: FnMut(Event) + 'static,
362{
363    /// Converts the wrapped closure into a callback `AttributeValue`.
364    ///
365    /// # Returns
366    ///
367    /// - `AttributeValue` - An event attribute value wrapping the adapted closure.
368    pub fn into_callback(self) -> AttributeValue {
369        self.into_inner().into()
370    }
371
372    /// Converts the wrapped closure into a callback `AttributeValue` with a
373    /// custom event name for component props.
374    ///
375    /// # Arguments
376    ///
377    /// - `&'static str` - The custom attribute name (e.g., "on-increment", "on-change").
378    ///
379    /// # Returns
380    ///
381    /// - `AttributeValue` - An event attribute value with the custom name.
382    pub fn into_callback_named(self, name: &'static str) -> AttributeValue {
383        AttributeValue::Event(NativeEventHandler::create(name, self.into_inner()))
384    }
385}
386
387/// Converts a named callback adapter into an `AttributeValue`.
388impl<F> From<CallbackNamedAdapter<F>> for AttributeValue
389where
390    F: FnMut(Event) + 'static,
391{
392    /// Converts the wrapped closure with custom name into a callback `AttributeValue`.
393    ///
394    /// # Returns
395    ///
396    /// - `AttributeValue` - An event attribute value with the custom name.
397    fn from(adapter: CallbackNamedAdapter<F>) -> Self {
398        AttributeValue::Event(NativeEventHandler::create(
399            adapter.get_name(),
400            adapter.inner,
401        ))
402    }
403}
404
405impl AttrValueAdapter<NativeEventHandler> {
406    /// Converts the wrapped handler into a callback `AttributeValue` with a
407    /// custom event name for component props.
408    ///
409    /// # Arguments
410    ///
411    /// - `&'static str` - The custom attribute name.
412    ///
413    /// # Returns
414    ///
415    /// - `AttributeValue` - An event attribute value with the custom name.
416    pub fn into_callback_named(self, name: &'static str) -> AttributeValue {
417        let mut handler: NativeEventHandler = self.into_inner();
418        handler.set_event_name(name);
419        AttributeValue::Event(handler)
420    }
421}
422
423/// Adapts an `Option<NativeEventHandler>` into an `AttributeValue`.
424impl AttrValueAdapter<Option<NativeEventHandler>> {
425    /// Converts the wrapped optional handler into an attribute value.
426    ///
427    /// # Returns
428    ///
429    /// - `AttributeValue` - An event attribute if `Some`, otherwise an empty text attribute.
430    pub fn into_callback(self) -> AttributeValue {
431        match self.into_inner() {
432            Some(handler) => AttrValueAdapter::new(handler).into(),
433            None => AttributeValue::Text(String::new()),
434        }
435    }
436
437    /// Converts this optional handler into a callback `AttributeValue` with a
438    /// custom event name for component props.
439    ///
440    /// # Arguments
441    ///
442    /// - `&'static str` - The custom attribute name.
443    ///
444    /// # Returns
445    ///
446    /// - `AttributeValue` - An event attribute with the custom name if `Some`,
447    ///   otherwise an empty text attribute.
448    pub fn into_callback_named(self, name: &'static str) -> AttributeValue {
449        match self.into_inner() {
450            Some(handler) => AttrValueAdapter::new(handler).into_callback_named(name),
451            None => AttributeValue::Text(String::new()),
452        }
453    }
454}
455
456/// Adapts any type that implements `Into<AttributeValue>` into an `AttributeValue`.
457///
458/// This is the fallback path for non-closure attribute values (strings, signals,
459/// CSS classes, etc.).
460impl<T> From<AttrValueAdapter<T>> for AttributeValue
461where
462    T: Into<AttributeValue>,
463{
464    /// Converts the wrapped value into an `AttributeValue`.
465    ///
466    /// # Returns
467    ///
468    /// - `AttributeValue` - The reactive attribute value.
469    fn from(adapter: AttrValueAdapter<T>) -> Self {
470        adapter.into_inner().into()
471    }
472}