Skip to main content

euv_core/vdom/cast/
impl.rs

1use crate::*;
2
3/// Converts a `VirtualNode` reference into an owned node.
4impl AsNode for VirtualNode {
5    /// Clones this virtual node into an owned `Option<VirtualNode>`.
6    ///
7    /// # Returns
8    ///
9    /// - `Option<VirtualNode>` - Always `Some` with the cloned node.
10    fn as_node(&self) -> Option<VirtualNode> {
11        Some(self.clone())
12    }
13}
14
15/// Converts a `VirtualNode` reference into an owned node.
16impl AsNode for &VirtualNode {
17    /// Clones this virtual node reference into an owned `Option<VirtualNode>`.
18    ///
19    /// # Returns
20    ///
21    /// - `Option<VirtualNode>` - Always `Some` with the cloned node.
22    fn as_node(&self) -> Option<VirtualNode> {
23        Some((*self).clone())
24    }
25}
26
27/// Converts a `String` into a text virtual node.
28impl AsNode for String {
29    /// Converts this string into a text virtual node.
30    ///
31    /// # Returns
32    ///
33    /// - `Option<VirtualNode>` - Always `Some` with a text node.
34    fn as_node(&self) -> Option<VirtualNode> {
35        Some(VirtualNode::Text(TextNode::new(self.clone(), None)))
36    }
37}
38
39/// Converts a string slice into a text virtual node.
40impl AsNode for &str {
41    /// Converts this string slice into a text virtual node.
42    ///
43    /// # Returns
44    ///
45    /// - `Option<VirtualNode>` - Always `Some` with a text node.
46    fn as_node(&self) -> Option<VirtualNode> {
47        Some(VirtualNode::Text(TextNode::new(self.to_string(), None)))
48    }
49}
50
51/// Converts an `i32` into a text virtual node.
52impl AsNode for i32 {
53    /// Converts this integer into a text virtual node.
54    ///
55    /// # Returns
56    ///
57    /// - `Option<VirtualNode>` - Always `Some` with a text node.
58    fn as_node(&self) -> Option<VirtualNode> {
59        Some(VirtualNode::Text(TextNode::new(self.to_string(), None)))
60    }
61}
62
63/// Converts an `i64` into a text virtual node.
64impl AsNode for i64 {
65    /// Converts this integer into a text virtual node.
66    ///
67    /// # Returns
68    ///
69    /// - `Option<VirtualNode>` - Always `Some` with a text node.
70    fn as_node(&self) -> Option<VirtualNode> {
71        Some(VirtualNode::Text(TextNode::new(self.to_string(), None)))
72    }
73}
74
75/// Converts a `usize` into a text virtual node.
76impl AsNode for usize {
77    /// Converts this unsigned integer into a text virtual node.
78    ///
79    /// # Returns
80    ///
81    /// - `Option<VirtualNode>` - Always `Some` with a text node.
82    fn as_node(&self) -> Option<VirtualNode> {
83        Some(VirtualNode::Text(TextNode::new(self.to_string(), None)))
84    }
85}
86
87/// Converts an `f32` into a text virtual node.
88impl AsNode for f32 {
89    /// Converts this float into a text virtual node.
90    ///
91    /// # Returns
92    ///
93    /// - `Option<VirtualNode>` - Always `Some` with a text node.
94    fn as_node(&self) -> Option<VirtualNode> {
95        Some(VirtualNode::Text(TextNode::new(self.to_string(), None)))
96    }
97}
98
99/// Converts an `f64` into a text virtual node.
100impl AsNode for f64 {
101    /// Converts this float into a text virtual node.
102    ///
103    /// # Returns
104    ///
105    /// - `Option<VirtualNode>` - Always `Some` with a text node.
106    fn as_node(&self) -> Option<VirtualNode> {
107        Some(VirtualNode::Text(TextNode::new(self.to_string(), None)))
108    }
109}
110
111/// Converts a `bool` into a text virtual node.
112impl AsNode for bool {
113    /// Converts this boolean into a text virtual node.
114    ///
115    /// # Returns
116    ///
117    /// - `Option<VirtualNode>` - Always `Some` with a text node.
118    fn as_node(&self) -> Option<VirtualNode> {
119        Some(VirtualNode::Text(TextNode::new(self.to_string(), None)))
120    }
121}
122
123/// Converts a signal into a reactive text virtual node.
124impl<T> AsNode for Signal<T>
125where
126    T: Clone + PartialEq + std::fmt::Display + 'static,
127{
128    /// Converts this signal into a reactive text virtual node.
129    ///
130    /// # Returns
131    ///
132    /// - `Option<VirtualNode>` - Always `Some` with a reactive text node.
133    fn as_node(&self) -> Option<VirtualNode> {
134        Some(self.as_reactive_text())
135    }
136}
137
138/// Converts a `VirtualNode` into itself via `IntoNode`.
139impl IntoNode for VirtualNode {
140    /// Returns this virtual node as-is.
141    ///
142    /// # Returns
143    ///
144    /// - `VirtualNode` - This same virtual node.
145    fn into_node(self) -> VirtualNode {
146        self
147    }
148}
149
150/// Wraps a `FnMut() -> VirtualNode` closure into a `DynamicNode` via `IntoNode`.
151///
152/// This enables writing `{move || html! { ... }}` directly in HTML markup
153/// without explicit `DynamicNode` construction.
154impl<F> IntoNode for F
155where
156    F: FnMut() -> VirtualNode + 'static,
157{
158    /// Wraps this closure into a `VirtualNode::Dynamic` with a fresh hook context.
159    ///
160    /// # Returns
161    ///
162    /// - `VirtualNode` - A dynamic virtual node wrapping this closure.
163    fn into_node(self) -> VirtualNode {
164        let inner: Box<RenderFnInner> = Box::new(RenderFnInner {
165            render_fn: Box::new(self),
166        });
167        VirtualNode::Dynamic(DynamicNode {
168            render_fn: Box::leak(inner) as *mut RenderFnInner,
169            hook_context: crate::reactive::create_hook_context(),
170        })
171    }
172}
173
174/// Converts a `String` into a text virtual node via `IntoNode`.
175impl IntoNode for String {
176    /// Converts this string into a text virtual node.
177    ///
178    /// # Returns
179    ///
180    /// - `VirtualNode` - A text virtual node.
181    fn into_node(self) -> VirtualNode {
182        VirtualNode::Text(TextNode::new(self, None))
183    }
184}
185
186/// Converts a `&str` into a text virtual node via `IntoNode`.
187impl IntoNode for &str {
188    /// Converts this string slice into a text virtual node.
189    ///
190    /// # Returns
191    ///
192    /// - `VirtualNode` - A text virtual node.
193    fn into_node(self) -> VirtualNode {
194        VirtualNode::Text(TextNode::new(self.to_string(), None))
195    }
196}
197
198/// Converts an `i32` into a text virtual node via `IntoNode`.
199impl IntoNode for i32 {
200    /// Converts this integer into a text virtual node.
201    ///
202    /// # Returns
203    ///
204    /// - `VirtualNode` - A text virtual node.
205    fn into_node(self) -> VirtualNode {
206        VirtualNode::Text(TextNode::new(self.to_string(), None))
207    }
208}
209
210/// Converts a `usize` into a text virtual node via `IntoNode`.
211impl IntoNode for usize {
212    /// Converts this unsigned integer into a text virtual node.
213    ///
214    /// # Returns
215    ///
216    /// - `VirtualNode` - A text virtual node.
217    fn into_node(self) -> VirtualNode {
218        VirtualNode::Text(TextNode::new(self.to_string(), None))
219    }
220}
221
222/// Converts a `bool` into a text virtual node via `IntoNode`.
223impl IntoNode for bool {
224    /// Converts this boolean into a text virtual node.
225    ///
226    /// # Returns
227    ///
228    /// - `VirtualNode` - A text virtual node.
229    fn into_node(self) -> VirtualNode {
230        VirtualNode::Text(TextNode::new(self.to_string(), None))
231    }
232}
233
234/// Converts a signal into a reactive text virtual node via `IntoNode`.
235impl<T> IntoNode for Signal<T>
236where
237    T: Clone + PartialEq + std::fmt::Display + 'static,
238{
239    /// Converts this signal into a reactive text virtual node.
240    ///
241    /// # Returns
242    ///
243    /// - `VirtualNode` - A reactive text virtual node.
244    fn into_node(self) -> VirtualNode {
245        self.as_reactive_text()
246    }
247}
248
249/// Converts a signal into a reactive text node with listener wiring.
250impl<T> AsReactiveText for Signal<T>
251where
252    T: Clone + PartialEq + std::fmt::Display + 'static,
253{
254    /// Creates a reactive text node that auto-updates when the signal changes.
255    ///
256    /// Internally creates a bridge `Signal<String>` that subscribes to the
257    /// source signal and updates the text content on every change.
258    ///
259    /// # Returns
260    ///
261    /// - `VirtualNode` - A text virtual node with reactive signal binding.
262    fn as_reactive_text(&self) -> VirtualNode {
263        let signal: Signal<T> = *self;
264        let initial: String = signal.get().to_string();
265        let string_signal: Signal<String> = {
266            let boxed: Box<SignalInner<String>> = Box::new(SignalInner::new(initial.clone()));
267            let ptr: *mut SignalInner<String> = Box::leak(boxed) as *mut SignalInner<String>;
268            let address: usize = ptr as usize;
269            address.into()
270        };
271        let source_signal: Signal<T> = *self;
272        let string_signal_clone: Signal<String> = string_signal;
273        source_signal.subscribe({
274            let source_signal: Signal<T> = source_signal;
275            move || {
276                let new_value: String = source_signal.get().to_string();
277                string_signal_clone.set(new_value);
278            }
279        });
280        VirtualNode::Text(TextNode::new(initial, Some(string_signal)))
281    }
282}
283
284/// Constructs an `EventAdapter` that wraps any event-compatible value.
285impl<T> EventAdapter<T> {
286    /// Creates a new `EventAdapter` wrapping the given value.
287    ///
288    /// # Arguments
289    ///
290    /// - `T` - The value to wrap for event attribute adaptation.
291    ///
292    /// # Returns
293    ///
294    /// - `EventAdapter<T>` - A new adapter wrapping the value.
295    pub fn new(inner: T) -> Self {
296        EventAdapter { inner }
297    }
298
299    /// Returns the inner wrapped value, consuming the adapter.
300    ///
301    /// # Returns
302    ///
303    /// - `T` - The inner value.
304    pub(crate) fn into_inner(self) -> T {
305        self.inner
306    }
307}
308
309/// Adapts a `FnMut(NativeEvent)` closure into an `AttributeValue::Event`.
310///
311/// Wraps the closure into a `NativeEventHandler` and returns it as an
312/// event attribute value. This replaces the `__EventWrapper<F>` type
313/// that was previously generated inline by the `html!` macro.
314impl<F> EventAdapter<F>
315where
316    F: FnMut(NativeEvent) + 'static,
317{
318    /// Converts the wrapped closure into an event `AttributeValue`.
319    ///
320    /// # Arguments
321    ///
322    /// - `NativeEventName` - The event name enum variant to associate with the handler.
323    ///
324    /// # Returns
325    ///
326    /// - `AttributeValue` - An `AttributeValue::Event` wrapping the handler.
327    pub fn into_attribute(self, event_name: NativeEventName) -> AttributeValue {
328        AttributeValue::Event(NativeEventHandler::new(event_name, self.into_inner()))
329    }
330}
331
332/// Adapts an owned `NativeEventHandler` into an `AttributeValue::Event` directly.
333///
334/// When the user already provides a `NativeEventHandler`, the handler is
335/// re-wrapped with the given `event_name` to ensure the DOM event listener
336/// is bound to the correct event type (e.g., "click" rather than "onclick").
337/// This replaces the `impl __EventWrapper<NativeEventHandler>` that was
338/// previously generated inline.
339impl EventAdapter<NativeEventHandler> {
340    /// Converts the wrapped handler into an event `AttributeValue`.
341    ///
342    /// Re-wraps the handler with the provided `event_name` so that the
343    /// DOM event listener uses the correct event type string.
344    ///
345    /// # Arguments
346    ///
347    /// - `NativeEventName` - The event name to bind the handler to.
348    ///
349    /// # Returns
350    ///
351    /// - `AttributeValue` - An `AttributeValue::Event` containing the re-wrapped handler.
352    pub fn into_attribute(self, event_name: NativeEventName) -> AttributeValue {
353        let handler: NativeEventHandler = self.into_inner();
354        AttributeValue::Event(NativeEventHandler::new(
355            event_name,
356            move |event: NativeEvent| {
357                handler.handle(event);
358            },
359        ))
360    }
361}
362
363/// Adapts an `Option<NativeEventHandler>` into an `AttributeValue`.
364///
365/// `Some(handler)` becomes `AttributeValue::Event(handler)` re-wrapped with the
366/// given event name, and `None` becomes `AttributeValue::Text(String::new())`.
367/// This replaces the `impl __EventWrapper<Option<NativeEventHandler>>` that was
368/// previously generated inline by the `html!` macro.
369impl EventAdapter<Option<NativeEventHandler>> {
370    /// Converts the wrapped optional handler into an attribute value.
371    ///
372    /// Re-wraps a `Some` handler with the provided `event_name` so that the
373    /// DOM event listener uses the correct event type string.
374    ///
375    /// # Arguments
376    ///
377    /// - `NativeEventName` - The event name to bind the handler to.
378    ///
379    /// # Returns
380    ///
381    /// - `AttributeValue` - An event attribute if `Some`, otherwise an empty text attribute.
382    pub fn into_attribute(self, event_name: NativeEventName) -> AttributeValue {
383        match self.into_inner() {
384            Some(handler) => EventAdapter::new(handler).into_attribute(event_name),
385            None => AttributeValue::Text(String::new()),
386        }
387    }
388}
389
390/// Constructs an `AttrValueAdapter` that wraps any attribute-compatible value.
391impl<T> AttrValueAdapter<T> {
392    /// Creates a new `AttrValueAdapter` wrapping the given value.
393    ///
394    /// # Arguments
395    ///
396    /// - `T` - The value to wrap for attribute adaptation.
397    ///
398    /// # Returns
399    ///
400    /// - `AttrValueAdapter<T>` - A new adapter wrapping the value.
401    pub fn new(inner: T) -> Self {
402        AttrValueAdapter { inner }
403    }
404
405    /// Returns the inner wrapped value, consuming the adapter.
406    ///
407    /// # Returns
408    ///
409    /// - `T` - The inner value.
410    pub(crate) fn into_inner(self) -> T {
411        self.inner
412    }
413}
414
415/// Adapts a `FnMut(NativeEvent)` closure into a callback `AttributeValue`.
416///
417/// This handles the case where a closure is used as a component callback prop.
418/// The closure is converted via `IntoCallbackAttribute::into_callback_attribute()`.
419/// This replaces the `__IsClosure for F` impl that was previously generated inline.
420impl<F> AttrValueAdapter<F>
421where
422    F: FnMut(NativeEvent) + 'static,
423{
424    /// Converts the wrapped closure into a callback `AttributeValue`.
425    ///
426    /// # Returns
427    ///
428    /// - `AttributeValue` - An event attribute value wrapping the adapted closure.
429    pub fn into_callback_attribute_value(self) -> AttributeValue {
430        self.into_inner().into_callback_attribute()
431    }
432
433    /// Converts the wrapped closure into a callback `AttributeValue` with a
434    /// custom event name for component props.
435    ///
436    /// # Arguments
437    ///
438    /// - `String` - The custom attribute name (e.g., "on-increment", "on-change").
439    ///
440    /// # Returns
441    ///
442    /// - `AttributeValue` - An event attribute value with the custom name.
443    pub fn into_callback_attribute_value_with_name(self, name: String) -> AttributeValue {
444        AttributeValue::Event(NativeEventHandler::new(
445            NativeEventName::Other(name),
446            self.into_inner(),
447        ))
448    }
449}
450
451/// Adapts an owned `NativeEventHandler` into an `AttributeValue::Event` directly.
452///
453/// When the user already provides a `NativeEventHandler`, the handler is
454/// re-wrapped with a generic "callback" event name so that subsequent
455/// `EventAdapter::into_attribute` calls can override it with the correct
456/// DOM event type. This replaces the `__IsClosure for NativeEventHandler`
457/// impl that was previously generated inline by the `html!` macro.
458impl AttrValueAdapter<NativeEventHandler> {
459    /// Converts the wrapped handler into an event `AttributeValue`.
460    ///
461    /// Re-wraps the handler with a generic callback event name so that the
462    /// DOM event type can be correctly resolved when the handler is later
463    /// bound to a real element via `EventAdapter::into_attribute`.
464    ///
465    /// # Returns
466    ///
467    /// - `AttributeValue` - An `AttributeValue::Event` containing the re-wrapped handler.
468    pub fn into_callback_attribute_value(self) -> AttributeValue {
469        let handler: NativeEventHandler = self.into_inner();
470        AttributeValue::Event(NativeEventHandler::new(
471            NativeEventName::Other("callback".to_string()),
472            move |event: NativeEvent| {
473                handler.handle(event);
474            },
475        ))
476    }
477
478    /// Converts the wrapped handler into a callback `AttributeValue` with a
479    /// custom event name for component props.
480    ///
481    /// # Arguments
482    ///
483    /// - `String` - The custom attribute name.
484    ///
485    /// # Returns
486    ///
487    /// - `AttributeValue` - An event attribute value with the custom name.
488    pub fn into_callback_attribute_value_with_name(self, name: String) -> AttributeValue {
489        let handler: NativeEventHandler = self.into_inner();
490        AttributeValue::Event(NativeEventHandler::new(
491            NativeEventName::Other(name),
492            move |event: NativeEvent| {
493                handler.handle(event);
494            },
495        ))
496    }
497}
498
499/// Adapts an `Option<NativeEventHandler>` into an `AttributeValue`.
500///
501/// `Some(handler)` becomes `AttributeValue::Event(handler)` re-wrapped with
502/// a generic callback event name, and `None` becomes `AttributeValue::Text(String::new())`.
503/// This replaces the `__IsClosure for Option<NativeEventHandler>` impl that was
504/// previously generated inline by the `html!` macro.
505impl AttrValueAdapter<Option<NativeEventHandler>> {
506    /// Converts the wrapped optional handler into an attribute value.
507    ///
508    /// Re-wraps a `Some` handler with a generic callback event name so that the
509    /// DOM event type can be correctly resolved when the handler is later bound
510    /// to a real element via `EventAdapter::into_attribute`.
511    ///
512    /// # Returns
513    ///
514    /// - `AttributeValue` - An event attribute if `Some`, otherwise an empty text attribute.
515    pub fn into_callback_attribute_value(self) -> AttributeValue {
516        match self.into_inner() {
517            Some(handler) => AttrValueAdapter::new(handler).into_callback_attribute_value(),
518            None => AttributeValue::Text(String::new()),
519        }
520    }
521
522    /// Converts this optional handler into a callback `AttributeValue` with a
523    /// custom event name for component props.
524    ///
525    /// # Arguments
526    ///
527    /// - `String` - The custom attribute name.
528    ///
529    /// # Returns
530    ///
531    /// - `AttributeValue` - An event attribute with the custom name if `Some`,
532    ///   otherwise an empty text attribute.
533    pub fn into_callback_attribute_value_with_name(self, name: String) -> AttributeValue {
534        match self.into_inner() {
535            Some(handler) => {
536                AttrValueAdapter::new(handler).into_callback_attribute_value_with_name(name)
537            }
538            None => AttributeValue::Text(String::new()),
539        }
540    }
541}
542
543/// Adapts any `IntoReactiveValue` type into an `AttributeValue`.
544///
545/// This is the fallback path for non-closure attribute values (strings, signals,
546/// CSS classes, etc.). The value is converted via `IntoReactiveValue::into_reactive_value()`.
547/// This replaces the `__ValuePicker` / `__FallbackHelper` hierarchy that was previously
548/// generated inline by the `html!` macro.
549impl<T> AttrValueAdapter<T>
550where
551    T: IntoReactiveValue,
552{
553    /// Converts the wrapped value into an `AttributeValue` via reactive value adaptation.
554    ///
555    /// # Returns
556    ///
557    /// - `AttributeValue` - The reactive attribute value.
558    pub fn into_reactive_attribute_value(self) -> AttributeValue {
559        self.into_inner().into_reactive_value()
560    }
561}