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 VirtualNode::Dynamic(DynamicNode {
165 render_fn: Rc::new(RefCell::new(self)),
166 hook_context: crate::reactive::create_hook_context(),
167 })
168 }
169}
170
171/// Converts a `String` into a text virtual node via `IntoNode`.
172impl IntoNode for String {
173 /// Converts this string into a text virtual node.
174 ///
175 /// # Returns
176 ///
177 /// - `VirtualNode` - A text virtual node.
178 fn into_node(self) -> VirtualNode {
179 VirtualNode::Text(TextNode::new(self, None))
180 }
181}
182
183/// Converts a `&str` into a text virtual node via `IntoNode`.
184impl IntoNode for &str {
185 /// Converts this string slice into a text virtual node.
186 ///
187 /// # Returns
188 ///
189 /// - `VirtualNode` - A text virtual node.
190 fn into_node(self) -> VirtualNode {
191 VirtualNode::Text(TextNode::new(self.to_string(), None))
192 }
193}
194
195/// Converts an `i32` into a text virtual node via `IntoNode`.
196impl IntoNode for i32 {
197 /// Converts this integer into a text virtual node.
198 ///
199 /// # Returns
200 ///
201 /// - `VirtualNode` - A text virtual node.
202 fn into_node(self) -> VirtualNode {
203 VirtualNode::Text(TextNode::new(self.to_string(), None))
204 }
205}
206
207/// Converts a `usize` into a text virtual node via `IntoNode`.
208impl IntoNode for usize {
209 /// Converts this unsigned integer into a text virtual node.
210 ///
211 /// # Returns
212 ///
213 /// - `VirtualNode` - A text virtual node.
214 fn into_node(self) -> VirtualNode {
215 VirtualNode::Text(TextNode::new(self.to_string(), None))
216 }
217}
218
219/// Converts a `bool` into a text virtual node via `IntoNode`.
220impl IntoNode for bool {
221 /// Converts this boolean into a text virtual node.
222 ///
223 /// # Returns
224 ///
225 /// - `VirtualNode` - A text virtual node.
226 fn into_node(self) -> VirtualNode {
227 VirtualNode::Text(TextNode::new(self.to_string(), None))
228 }
229}
230
231/// Converts a signal into a reactive text virtual node via `IntoNode`.
232impl<T> IntoNode for Signal<T>
233where
234 T: Clone + PartialEq + std::fmt::Display + 'static,
235{
236 /// Converts this signal into a reactive text virtual node.
237 ///
238 /// # Returns
239 ///
240 /// - `VirtualNode` - A reactive text virtual node.
241 fn into_node(self) -> VirtualNode {
242 self.as_reactive_text()
243 }
244}
245
246/// Converts a signal into a reactive text node with listener wiring.
247impl<T> AsReactiveText for Signal<T>
248where
249 T: Clone + PartialEq + std::fmt::Display + 'static,
250{
251 /// Creates a reactive text node that auto-updates when the signal changes.
252 ///
253 /// Internally creates a bridge `Signal<String>` that subscribes to the
254 /// source signal and updates the text content on every change.
255 ///
256 /// # Returns
257 ///
258 /// - `VirtualNode` - A text virtual node with reactive signal binding.
259 fn as_reactive_text(&self) -> VirtualNode {
260 let signal: Signal<T> = *self;
261 let initial: String = signal.get().to_string();
262 let string_signal: Signal<String> = {
263 let boxed: Box<SignalInner<String>> = Box::new(SignalInner::new(initial.clone()));
264 Signal::from_inner(Box::leak(boxed) as *mut SignalInner<String>)
265 };
266 let source_signal: Signal<T> = *self;
267 let string_signal_clone: Signal<String> = string_signal;
268 source_signal.subscribe({
269 let source_signal: Signal<T> = source_signal;
270 move || {
271 let new_value: String = source_signal.get().to_string();
272 string_signal_clone.set(new_value);
273 }
274 });
275 VirtualNode::Text(TextNode::new(initial, Some(string_signal)))
276 }
277}
278
279/// Constructs an `EventAdapter` that wraps any event-compatible value.
280impl<T> EventAdapter<T> {
281 /// Creates a new `EventAdapter` wrapping the given value.
282 ///
283 /// # Arguments
284 ///
285 /// - `T` - The value to wrap for event attribute adaptation.
286 ///
287 /// # Returns
288 ///
289 /// - `EventAdapter<T>` - A new adapter wrapping the value.
290 pub fn new(inner: T) -> Self {
291 EventAdapter { inner }
292 }
293}
294
295/// Adapts a `FnMut(NativeEvent)` closure into an `AttributeValue::Event`.
296///
297/// Wraps the closure into a `NativeEventHandler` and returns it as an
298/// event attribute value. This replaces the `__EventWrapper<F>` type
299/// that was previously generated inline by the `html!` macro.
300impl<F> EventAdapter<F>
301where
302 F: FnMut(NativeEvent) + 'static,
303{
304 /// Converts the wrapped closure into an event `AttributeValue`.
305 ///
306 /// # Arguments
307 ///
308 /// - `NativeEventName` - The event name enum variant to associate with the handler.
309 ///
310 /// # Returns
311 ///
312 /// - `AttributeValue` - An `AttributeValue::Event` wrapping the handler.
313 pub fn into_attribute(self, event_name: NativeEventName) -> AttributeValue {
314 AttributeValue::Event(NativeEventHandler::new(event_name, self.inner))
315 }
316}
317
318/// Adapts an owned `NativeEventHandler` into an `AttributeValue::Event` directly.
319///
320/// When the user already provides a `NativeEventHandler`, no wrapping is needed;
321/// the handler is returned as-is. This replaces the `impl __EventWrapper<NativeEventHandler>`
322/// that was previously generated inline.
323impl EventAdapter<NativeEventHandler> {
324 /// Converts the wrapped handler into an event `AttributeValue`.
325 ///
326 /// # Arguments
327 ///
328 /// - `NativeEventName` - The event name (unused, since the handler already carries it).
329 ///
330 /// # Returns
331 ///
332 /// - `AttributeValue` - An `AttributeValue::Event` containing the handler.
333 pub fn into_attribute(self, _event_name: NativeEventName) -> AttributeValue {
334 AttributeValue::Event(self.inner)
335 }
336}
337
338/// Adapts an `Option<NativeEventHandler>` into an `AttributeValue`.
339///
340/// `Some(handler)` becomes `AttributeValue::Event(handler)`, and `None` becomes
341/// `AttributeValue::Text(String::new())`. This replaces the
342/// `impl __EventWrapper<Option<NativeEventHandler>>` that was previously
343/// generated inline by the `html!` macro.
344impl EventAdapter<Option<NativeEventHandler>> {
345 /// Converts the wrapped optional handler into an attribute value.
346 ///
347 /// # Arguments
348 ///
349 /// - `NativeEventName` - The event name (unused when handler is `None`).
350 ///
351 /// # Returns
352 ///
353 /// - `AttributeValue` - An event attribute if `Some`, otherwise an empty text attribute.
354 pub fn into_attribute(self, _event_name: NativeEventName) -> AttributeValue {
355 match self.inner {
356 Some(handler) => AttributeValue::Event(handler),
357 None => AttributeValue::Text(String::new()),
358 }
359 }
360}
361
362/// Constructs an `AttrValueAdapter` that wraps any attribute-compatible value.
363impl<T> AttrValueAdapter<T> {
364 /// Creates a new `AttrValueAdapter` wrapping the given value.
365 ///
366 /// # Arguments
367 ///
368 /// - `T` - The value to wrap for attribute adaptation.
369 ///
370 /// # Returns
371 ///
372 /// - `AttrValueAdapter<T>` - A new adapter wrapping the value.
373 pub fn new(inner: T) -> Self {
374 AttrValueAdapter { inner }
375 }
376}
377
378/// Adapts a `FnMut(NativeEvent)` closure into a callback `AttributeValue`.
379///
380/// This handles the case where a closure is used as a component callback prop.
381/// The closure is converted via `IntoCallbackAttribute::into_callback_attribute()`.
382/// This replaces the `__IsClosure for F` impl that was previously generated inline.
383impl<F> AttrValueAdapter<F>
384where
385 F: FnMut(NativeEvent) + 'static,
386{
387 /// Converts the wrapped closure into a callback `AttributeValue`.
388 ///
389 /// # Returns
390 ///
391 /// - `AttributeValue` - An event attribute value wrapping the adapted closure.
392 pub fn into_callback_attribute_value(self) -> AttributeValue {
393 self.inner.into_callback_attribute()
394 }
395}
396
397/// Adapts an owned `NativeEventHandler` into an `AttributeValue::Event` directly.
398///
399/// When the user already provides a `NativeEventHandler`, it is returned as-is.
400/// This replaces the `__IsClosure for NativeEventHandler` impl that was previously
401/// generated inline by the `html!` macro.
402impl AttrValueAdapter<NativeEventHandler> {
403 /// Converts the wrapped handler into an event `AttributeValue`.
404 ///
405 /// # Returns
406 ///
407 /// - `AttributeValue` - An `AttributeValue::Event` containing the handler.
408 pub fn into_callback_attribute_value(self) -> AttributeValue {
409 AttributeValue::Event(self.inner)
410 }
411}
412
413/// Adapts an `Option<NativeEventHandler>` into an `AttributeValue`.
414///
415/// `Some(handler)` becomes `AttributeValue::Event(handler)`, and `None` becomes
416/// `AttributeValue::Text(String::new())`. This replaces the
417/// `__IsClosure for Option<NativeEventHandler>` impl that was previously
418/// generated inline by the `html!` macro.
419impl AttrValueAdapter<Option<NativeEventHandler>> {
420 /// Converts the wrapped optional handler into an attribute value.
421 ///
422 /// # Returns
423 ///
424 /// - `AttributeValue` - An event attribute if `Some`, otherwise an empty text attribute.
425 pub fn into_callback_attribute_value(self) -> AttributeValue {
426 match self.inner {
427 Some(handler) => AttributeValue::Event(handler),
428 None => AttributeValue::Text(String::new()),
429 }
430 }
431}
432
433/// Adapts any `IntoReactiveValue` type into an `AttributeValue`.
434///
435/// This is the fallback path for non-closure attribute values (strings, signals,
436/// CSS classes, etc.). The value is converted via `IntoReactiveValue::into_reactive_value()`.
437/// This replaces the `__ValuePicker` / `__FallbackHelper` hierarchy that was previously
438/// generated inline by the `html!` macro.
439impl<T> AttrValueAdapter<T>
440where
441 T: IntoReactiveValue,
442{
443 /// Converts the wrapped value into an `AttributeValue` via reactive value adaptation.
444 ///
445 /// # Returns
446 ///
447 /// - `AttributeValue` - The reactive attribute value.
448 pub fn into_reactive_attribute_value(self) -> AttributeValue {
449 self.inner.into_reactive_value()
450 }
451}