1use dioxus_core_types::DioxusFormattable;
2
3use crate::events::ListenerCallback;
4use crate::innerlude::VProps;
5use crate::prelude::RenderError;
6use crate::{any_props::BoxedAnyProps, innerlude::ScopeState};
7use crate::{arena::ElementId, Element, Event};
8use crate::{
9 innerlude::{ElementRef, MountId},
10 properties::ComponentFunction,
11};
12use crate::{Properties, ScopeId, VirtualDom};
13use std::ops::Deref;
14use std::rc::Rc;
15use std::vec;
16use std::{
17 any::{Any, TypeId},
18 cell::Cell,
19 fmt::{Arguments, Debug},
20};
21
22#[derive(Debug)]
24pub(crate) struct VNodeMount {
25 pub parent: Option<ElementRef>,
27
28 pub node: VNode,
30
31 pub root_ids: Box<[ElementId]>,
34
35 pub(crate) mounted_attributes: Box<[ElementId]>,
37
38 pub(crate) mounted_dynamic_nodes: Box<[usize]>,
41}
42
43#[derive(Debug)]
48pub struct VNodeInner {
49 pub key: Option<String>,
53
54 pub template: Template,
56
57 pub dynamic_nodes: Box<[DynamicNode]>,
59
60 pub dynamic_attrs: Box<[Box<[Attribute]>]>,
92}
93
94#[derive(Debug, Clone)]
99pub struct VNode {
100 vnode: Rc<VNodeInner>,
101
102 pub(crate) mount: Cell<MountId>,
104}
105
106impl AsRef<VNode> for Element {
107 fn as_ref(&self) -> &VNode {
108 match self {
109 Element::Ok(node) => node,
110 Element::Err(RenderError::Aborted(err)) => &err.render,
111 Element::Err(RenderError::Suspended(fut)) => &fut.placeholder,
112 }
113 }
114}
115
116impl From<&Element> for VNode {
117 fn from(val: &Element) -> Self {
118 AsRef::as_ref(val).clone()
119 }
120}
121
122impl From<Element> for VNode {
123 fn from(val: Element) -> Self {
124 match val {
125 Element::Ok(node) => node,
126 Element::Err(RenderError::Aborted(err)) => err.render,
127 Element::Err(RenderError::Suspended(fut)) => fut.placeholder,
128 }
129 }
130}
131
132pub(crate) trait AsVNode {
134 fn as_vnode(&self) -> &VNode;
136
137 fn deep_clone(&self) -> Self;
139}
140
141impl AsVNode for Element {
142 fn as_vnode(&self) -> &VNode {
143 AsRef::as_ref(self)
144 }
145
146 fn deep_clone(&self) -> Self {
147 match self {
148 Ok(node) => Ok(node.deep_clone()),
149 Err(RenderError::Aborted(err)) => Err(RenderError::Aborted(err.deep_clone())),
150 Err(RenderError::Suspended(fut)) => Err(RenderError::Suspended(fut.deep_clone())),
151 }
152 }
153}
154
155impl Default for VNode {
156 fn default() -> Self {
157 Self::placeholder()
158 }
159}
160
161impl PartialEq for VNode {
162 fn eq(&self, other: &Self) -> bool {
163 Rc::ptr_eq(&self.vnode, &other.vnode)
164 }
165}
166
167impl Deref for VNode {
168 type Target = VNodeInner;
169
170 fn deref(&self) -> &Self::Target {
171 &self.vnode
172 }
173}
174
175impl VNode {
176 pub fn empty() -> Element {
178 Ok(Self::default())
179 }
180
181 pub fn placeholder() -> Self {
183 use std::cell::OnceCell;
184 thread_local! {
186 static PLACEHOLDER_VNODE: OnceCell<Rc<VNodeInner>> = const { OnceCell::new() };
187 }
188 let vnode = PLACEHOLDER_VNODE.with(|cell| {
189 cell.get_or_init(move || {
190 Rc::new(VNodeInner {
191 key: None,
192 dynamic_nodes: Box::new([DynamicNode::Placeholder(Default::default())]),
193 dynamic_attrs: Box::new([]),
194 template: Template {
195 roots: &[TemplateNode::Dynamic { id: 0 }],
196 node_paths: &[&[0]],
197 attr_paths: &[],
198 },
199 })
200 })
201 .clone()
202 });
203 Self {
204 vnode,
205 mount: Default::default(),
206 }
207 }
208
209 pub fn new(
211 key: Option<String>,
212 template: Template,
213 dynamic_nodes: Box<[DynamicNode]>,
214 dynamic_attrs: Box<[Box<[Attribute]>]>,
215 ) -> Self {
216 Self {
217 vnode: Rc::new(VNodeInner {
218 key,
219 template,
220 dynamic_nodes,
221 dynamic_attrs,
222 }),
223 mount: Default::default(),
224 }
225 }
226
227 pub fn dynamic_root(&self, idx: usize) -> Option<&DynamicNode> {
231 self.template.roots[idx]
232 .dynamic_id()
233 .map(|id| &self.dynamic_nodes[id])
234 }
235
236 pub fn mounted_dynamic_node(
238 &self,
239 dynamic_node_idx: usize,
240 dom: &VirtualDom,
241 ) -> Option<ElementId> {
242 let mount = self.mount.get().as_usize()?;
243
244 match &self.dynamic_nodes[dynamic_node_idx] {
245 DynamicNode::Text(_) | DynamicNode::Placeholder(_) => {
246 let mounts = dom.runtime.mounts.borrow();
247 mounts
248 .get(mount)?
249 .mounted_dynamic_nodes
250 .get(dynamic_node_idx)
251 .map(|id| ElementId(*id))
252 }
253 _ => None,
254 }
255 }
256
257 pub fn mounted_root(&self, root_idx: usize, dom: &VirtualDom) -> Option<ElementId> {
259 let mount = self.mount.get().as_usize()?;
260
261 let mounts = dom.runtime.mounts.borrow();
262 mounts.get(mount)?.root_ids.get(root_idx).copied()
263 }
264
265 pub fn mounted_dynamic_attribute(
267 &self,
268 dynamic_attribute_idx: usize,
269 dom: &VirtualDom,
270 ) -> Option<ElementId> {
271 let mount = self.mount.get().as_usize()?;
272
273 let mounts = dom.runtime.mounts.borrow();
274 mounts
275 .get(mount)?
276 .mounted_attributes
277 .get(dynamic_attribute_idx)
278 .copied()
279 }
280
281 pub(crate) fn deep_clone(&self) -> Self {
283 Self {
284 vnode: Rc::new(VNodeInner {
285 key: self.vnode.key.clone(),
286 template: self.vnode.template,
287 dynamic_nodes: self
288 .vnode
289 .dynamic_nodes
290 .iter()
291 .map(|node| match node {
292 DynamicNode::Fragment(nodes) => DynamicNode::Fragment(
293 nodes.iter().map(|node| node.deep_clone()).collect(),
294 ),
295 other => other.clone(),
296 })
297 .collect(),
298 dynamic_attrs: self
299 .vnode
300 .dynamic_attrs
301 .iter()
302 .map(|attr| {
303 attr.iter()
304 .map(|attribute| attribute.deep_clone())
305 .collect()
306 })
307 .collect(),
308 }),
309 mount: Default::default(),
310 }
311 }
312}
313
314type StaticStr = &'static str;
315type StaticPathArray = &'static [&'static [u8]];
316type StaticTemplateArray = &'static [TemplateNode];
317type StaticTemplateAttributeArray = &'static [TemplateAttribute];
318
319#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
325#[derive(Debug, Clone, Copy, Eq, PartialOrd, Ord)]
326pub struct Template {
327 #[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
331 pub roots: StaticTemplateArray,
332
333 #[cfg_attr(
338 feature = "serialize",
339 serde(deserialize_with = "deserialize_bytes_leaky")
340 )]
341 pub node_paths: StaticPathArray,
342
343 #[cfg_attr(
348 feature = "serialize",
349 serde(deserialize_with = "deserialize_bytes_leaky", bound = "")
350 )]
351 pub attr_paths: StaticPathArray,
352}
353
354#[allow(unpredictable_function_pointer_comparisons)] fn static_items_merged() -> bool {
358 fn a() {}
359 fn b() {}
360 a as fn() == b as fn()
361 }
363
364impl std::hash::Hash for Template {
365 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
366 if static_items_merged() {
368 std::ptr::hash(self.roots as *const _, state);
369 std::ptr::hash(self.node_paths as *const _, state);
370 std::ptr::hash(self.attr_paths as *const _, state);
371 }
372 else {
374 self.roots.hash(state);
375 self.node_paths.hash(state);
376 self.attr_paths.hash(state);
377 }
378 }
379}
380
381impl PartialEq for Template {
382 fn eq(&self, other: &Self) -> bool {
383 if static_items_merged() {
385 std::ptr::eq(self.roots as *const _, other.roots as *const _)
386 && std::ptr::eq(self.node_paths as *const _, other.node_paths as *const _)
387 && std::ptr::eq(self.attr_paths as *const _, other.attr_paths as *const _)
388 }
389 else {
391 self.roots == other.roots
392 && self.node_paths == other.node_paths
393 && self.attr_paths == other.attr_paths
394 }
395 }
396}
397
398#[cfg(feature = "serialize")]
399pub(crate) fn deserialize_string_leaky<'a, 'de, D>(
400 deserializer: D,
401) -> Result<&'static str, D::Error>
402where
403 D: serde::Deserializer<'de>,
404{
405 use serde::Deserialize;
406
407 let deserialized = String::deserialize(deserializer)?;
408 Ok(&*Box::leak(deserialized.into_boxed_str()))
409}
410
411#[cfg(feature = "serialize")]
412fn deserialize_bytes_leaky<'a, 'de, D>(
413 deserializer: D,
414) -> Result<&'static [&'static [u8]], D::Error>
415where
416 D: serde::Deserializer<'de>,
417{
418 use serde::Deserialize;
419
420 let deserialized = Vec::<Vec<u8>>::deserialize(deserializer)?;
421 let deserialized = deserialized
422 .into_iter()
423 .map(|v| &*Box::leak(v.into_boxed_slice()))
424 .collect::<Vec<_>>();
425 Ok(&*Box::leak(deserialized.into_boxed_slice()))
426}
427
428#[cfg(feature = "serialize")]
429pub(crate) fn deserialize_leaky<'a, 'de, T, D>(deserializer: D) -> Result<&'static [T], D::Error>
430where
431 T: serde::Deserialize<'de>,
432 D: serde::Deserializer<'de>,
433{
434 use serde::Deserialize;
435
436 let deserialized = Box::<[T]>::deserialize(deserializer)?;
437 Ok(&*Box::leak(deserialized))
438}
439
440#[cfg(feature = "serialize")]
441pub(crate) fn deserialize_option_leaky<'a, 'de, D>(
442 deserializer: D,
443) -> Result<Option<&'static str>, D::Error>
444where
445 D: serde::Deserializer<'de>,
446{
447 use serde::Deserialize;
448
449 let deserialized = Option::<String>::deserialize(deserializer)?;
450 Ok(deserialized.map(|deserialized| &*Box::leak(deserialized.into_boxed_str())))
451}
452
453impl Template {
454 pub fn is_completely_dynamic(&self) -> bool {
458 use TemplateNode::*;
459 self.roots.iter().all(|root| matches!(root, Dynamic { .. }))
460 }
461}
462
463#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord)]
467#[cfg_attr(
468 feature = "serialize",
469 derive(serde::Serialize, serde::Deserialize),
470 serde(tag = "type")
471)]
472pub enum TemplateNode {
473 Element {
477 #[cfg_attr(
481 feature = "serialize",
482 serde(deserialize_with = "deserialize_string_leaky")
483 )]
484 tag: StaticStr,
485
486 #[cfg_attr(
491 feature = "serialize",
492 serde(deserialize_with = "deserialize_option_leaky")
493 )]
494 namespace: Option<StaticStr>,
495
496 #[cfg_attr(
500 feature = "serialize",
501 serde(deserialize_with = "deserialize_leaky", bound = "")
502 )]
503 attrs: StaticTemplateAttributeArray,
504
505 #[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
507 children: StaticTemplateArray,
508 },
509
510 Text {
512 #[cfg_attr(
514 feature = "serialize",
515 serde(deserialize_with = "deserialize_string_leaky", bound = "")
516 )]
517 text: StaticStr,
518 },
519
520 Dynamic {
522 id: usize,
524 },
525}
526
527impl TemplateNode {
528 pub fn dynamic_id(&self) -> Option<usize> {
530 use TemplateNode::*;
531 match self {
532 Dynamic { id } => Some(*id),
533 _ => None,
534 }
535 }
536}
537
538#[derive(Debug, Clone)]
542pub enum DynamicNode {
543 Component(VComponent),
551
552 Text(VText),
554
555 Placeholder(VPlaceholder),
561
562 Fragment(Vec<VNode>),
567}
568
569impl DynamicNode {
570 pub fn make_node<'c, I>(into: impl IntoDynNode<I> + 'c) -> DynamicNode {
572 into.into_dyn_node()
573 }
574}
575
576impl Default for DynamicNode {
577 fn default() -> Self {
578 Self::Placeholder(Default::default())
579 }
580}
581
582pub struct VComponent {
584 pub name: &'static str,
586
587 pub(crate) render_fn: usize,
589
590 pub(crate) props: BoxedAnyProps,
592}
593
594impl Clone for VComponent {
595 fn clone(&self) -> Self {
596 Self {
597 name: self.name,
598 props: self.props.duplicate(),
599 render_fn: self.render_fn,
600 }
601 }
602}
603
604impl VComponent {
605 pub fn new<P, M: 'static>(
607 component: impl ComponentFunction<P, M>,
608 props: P,
609 fn_name: &'static str,
610 ) -> Self
611 where
612 P: Properties + 'static,
613 {
614 let render_fn = component.fn_ptr();
615 let props = Box::new(VProps::new(
616 component,
617 <P as Properties>::memoize,
618 props,
619 fn_name,
620 ));
621
622 VComponent {
623 render_fn,
624 name: fn_name,
625 props,
626 }
627 }
628
629 pub fn mounted_scope_id(
635 &self,
636 dynamic_node_index: usize,
637 vnode: &VNode,
638 dom: &VirtualDom,
639 ) -> Option<ScopeId> {
640 let mount = vnode.mount.get().as_usize()?;
641
642 let mounts = dom.runtime.mounts.borrow();
643 let scope_id = mounts.get(mount)?.mounted_dynamic_nodes[dynamic_node_index];
644
645 Some(ScopeId(scope_id))
646 }
647
648 pub fn mounted_scope<'a>(
654 &self,
655 dynamic_node_index: usize,
656 vnode: &VNode,
657 dom: &'a VirtualDom,
658 ) -> Option<&'a ScopeState> {
659 let mount = vnode.mount.get().as_usize()?;
660
661 let mounts = dom.runtime.mounts.borrow();
662 let scope_id = mounts.get(mount)?.mounted_dynamic_nodes[dynamic_node_index];
663
664 dom.scopes.get(scope_id)
665 }
666}
667
668impl std::fmt::Debug for VComponent {
669 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
670 f.debug_struct("VComponent")
671 .field("name", &self.name)
672 .finish()
673 }
674}
675
676#[derive(Clone, Debug)]
678pub struct VText {
679 pub value: String,
681}
682
683impl VText {
684 pub fn new(value: impl ToString) -> Self {
686 Self {
687 value: value.to_string(),
688 }
689 }
690}
691
692impl From<Arguments<'_>> for VText {
693 fn from(args: Arguments) -> Self {
694 Self::new(args.to_string())
695 }
696}
697
698#[derive(Clone, Debug, Default)]
700#[non_exhaustive]
701pub struct VPlaceholder {}
702
703#[derive(Debug, PartialEq, Hash, Eq, PartialOrd, Ord)]
705#[cfg_attr(
706 feature = "serialize",
707 derive(serde::Serialize, serde::Deserialize),
708 serde(tag = "type")
709)]
710pub enum TemplateAttribute {
711 Static {
713 #[cfg_attr(
717 feature = "serialize",
718 serde(deserialize_with = "deserialize_string_leaky", bound = "")
719 )]
720 name: StaticStr,
721
722 #[cfg_attr(
726 feature = "serialize",
727 serde(deserialize_with = "deserialize_string_leaky", bound = "")
728 )]
729 value: StaticStr,
730
731 #[cfg_attr(
733 feature = "serialize",
734 serde(deserialize_with = "deserialize_option_leaky", bound = "")
735 )]
736 namespace: Option<StaticStr>,
737 },
738
739 Dynamic {
743 id: usize,
745 },
746}
747
748#[derive(Debug, Clone, PartialEq)]
750pub struct Attribute {
751 pub name: &'static str,
753
754 pub value: AttributeValue,
756
757 pub namespace: Option<&'static str>,
761
762 pub volatile: bool,
764}
765
766impl Attribute {
767 pub fn new<T>(
772 name: &'static str,
773 value: impl IntoAttributeValue<T>,
774 namespace: Option<&'static str>,
775 volatile: bool,
776 ) -> Attribute {
777 Attribute {
778 name,
779 namespace,
780 volatile,
781 value: value.into_value(),
782 }
783 }
784
785 pub(crate) fn deep_clone(&self) -> Self {
787 Attribute {
788 name: self.name,
789 namespace: self.namespace,
790 volatile: self.volatile,
791 value: self.value.clone(),
792 }
793 }
794}
795
796#[derive(Clone)]
801pub enum AttributeValue {
802 Text(String),
804
805 Float(f64),
807
808 Int(i64),
810
811 Bool(bool),
813
814 Listener(ListenerCallback),
816
817 Any(Rc<dyn AnyValue>),
819
820 None,
822}
823
824impl AttributeValue {
825 pub fn listener<T: 'static>(callback: impl FnMut(Event<T>) + 'static) -> AttributeValue {
829 AttributeValue::Listener(ListenerCallback::new(callback).erase())
830 }
831
832 pub fn any_value<T: AnyValue>(value: T) -> AttributeValue {
834 AttributeValue::Any(Rc::new(value))
835 }
836}
837
838impl std::fmt::Debug for AttributeValue {
839 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
840 match self {
841 Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
842 Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
843 Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
844 Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
845 Self::Listener(_) => f.debug_tuple("Listener").finish(),
846 Self::Any(_) => f.debug_tuple("Any").finish(),
847 Self::None => write!(f, "None"),
848 }
849 }
850}
851
852impl PartialEq for AttributeValue {
853 fn eq(&self, other: &Self) -> bool {
854 match (self, other) {
855 (Self::Text(l0), Self::Text(r0)) => l0 == r0,
856 (Self::Float(l0), Self::Float(r0)) => l0 == r0,
857 (Self::Int(l0), Self::Int(r0)) => l0 == r0,
858 (Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
859 (Self::Listener(l0), Self::Listener(r0)) => l0 == r0,
860 (Self::Any(l0), Self::Any(r0)) => l0.as_ref().any_cmp(r0.as_ref()),
861 (Self::None, Self::None) => true,
862 _ => false,
863 }
864 }
865}
866
867#[doc(hidden)]
868pub trait AnyValue: 'static {
869 fn any_cmp(&self, other: &dyn AnyValue) -> bool;
870 fn as_any(&self) -> &dyn Any;
871 fn type_id(&self) -> TypeId {
872 self.as_any().type_id()
873 }
874}
875
876impl<T: Any + PartialEq + 'static> AnyValue for T {
877 fn any_cmp(&self, other: &dyn AnyValue) -> bool {
878 if let Some(other) = other.as_any().downcast_ref() {
879 self == other
880 } else {
881 false
882 }
883 }
884
885 fn as_any(&self) -> &dyn Any {
886 self
887 }
888}
889
890pub trait IntoDynNode<A = ()> {
892 fn into_dyn_node(self) -> DynamicNode;
894}
895
896impl IntoDynNode for () {
897 fn into_dyn_node(self) -> DynamicNode {
898 DynamicNode::default()
899 }
900}
901impl IntoDynNode for VNode {
902 fn into_dyn_node(self) -> DynamicNode {
903 DynamicNode::Fragment(vec![self])
904 }
905}
906impl IntoDynNode for DynamicNode {
907 fn into_dyn_node(self) -> DynamicNode {
908 self
909 }
910}
911impl<T: IntoDynNode> IntoDynNode for Option<T> {
912 fn into_dyn_node(self) -> DynamicNode {
913 match self {
914 Some(val) => val.into_dyn_node(),
915 None => DynamicNode::default(),
916 }
917 }
918}
919impl IntoDynNode for &Element {
920 fn into_dyn_node(self) -> DynamicNode {
921 match self.as_ref() {
922 Ok(val) => val.into_dyn_node(),
923 _ => DynamicNode::default(),
924 }
925 }
926}
927impl IntoDynNode for Element {
928 fn into_dyn_node(self) -> DynamicNode {
929 match self {
930 Ok(val) => val.into_dyn_node(),
931 _ => DynamicNode::default(),
932 }
933 }
934}
935impl IntoDynNode for &Option<VNode> {
936 fn into_dyn_node(self) -> DynamicNode {
937 match self.as_ref() {
938 Some(val) => val.clone().into_dyn_node(),
939 _ => DynamicNode::default(),
940 }
941 }
942}
943impl IntoDynNode for &str {
944 fn into_dyn_node(self) -> DynamicNode {
945 DynamicNode::Text(VText {
946 value: self.to_string(),
947 })
948 }
949}
950impl IntoDynNode for String {
951 fn into_dyn_node(self) -> DynamicNode {
952 DynamicNode::Text(VText { value: self })
953 }
954}
955impl IntoDynNode for Arguments<'_> {
956 fn into_dyn_node(self) -> DynamicNode {
957 DynamicNode::Text(VText {
958 value: self.to_string(),
959 })
960 }
961}
962impl IntoDynNode for &VNode {
963 fn into_dyn_node(self) -> DynamicNode {
964 DynamicNode::Fragment(vec![self.clone()])
965 }
966}
967
968pub trait IntoVNode {
969 fn into_vnode(self) -> VNode;
970}
971impl IntoVNode for VNode {
972 fn into_vnode(self) -> VNode {
973 self
974 }
975}
976impl IntoVNode for &VNode {
977 fn into_vnode(self) -> VNode {
978 self.clone()
979 }
980}
981impl IntoVNode for Element {
982 fn into_vnode(self) -> VNode {
983 match self {
984 Ok(val) => val.into_vnode(),
985 _ => VNode::empty().unwrap(),
986 }
987 }
988}
989impl IntoVNode for &Element {
990 fn into_vnode(self) -> VNode {
991 match self {
992 Ok(val) => val.into_vnode(),
993 _ => VNode::empty().unwrap(),
994 }
995 }
996}
997impl IntoVNode for Option<VNode> {
998 fn into_vnode(self) -> VNode {
999 match self {
1000 Some(val) => val.into_vnode(),
1001 _ => VNode::empty().unwrap(),
1002 }
1003 }
1004}
1005impl IntoVNode for &Option<VNode> {
1006 fn into_vnode(self) -> VNode {
1007 match self.as_ref() {
1008 Some(val) => val.clone().into_vnode(),
1009 _ => VNode::empty().unwrap(),
1010 }
1011 }
1012}
1013impl IntoVNode for Option<Element> {
1014 fn into_vnode(self) -> VNode {
1015 match self {
1016 Some(val) => val.into_vnode(),
1017 _ => VNode::empty().unwrap(),
1018 }
1019 }
1020}
1021impl IntoVNode for &Option<Element> {
1022 fn into_vnode(self) -> VNode {
1023 match self.as_ref() {
1024 Some(val) => val.clone().into_vnode(),
1025 _ => VNode::empty().unwrap(),
1026 }
1027 }
1028}
1029
1030pub struct FromNodeIterator;
1032impl<T, I> IntoDynNode<FromNodeIterator> for T
1033where
1034 T: Iterator<Item = I>,
1035 I: IntoVNode,
1036{
1037 fn into_dyn_node(self) -> DynamicNode {
1038 let children: Vec<_> = self.into_iter().map(|node| node.into_vnode()).collect();
1039
1040 if children.is_empty() {
1041 DynamicNode::default()
1042 } else {
1043 DynamicNode::Fragment(children)
1044 }
1045 }
1046}
1047
1048pub trait IntoAttributeValue<T = ()> {
1050 fn into_value(self) -> AttributeValue;
1052}
1053
1054impl IntoAttributeValue for AttributeValue {
1055 fn into_value(self) -> AttributeValue {
1056 self
1057 }
1058}
1059
1060impl IntoAttributeValue for &str {
1061 fn into_value(self) -> AttributeValue {
1062 AttributeValue::Text(self.to_string())
1063 }
1064}
1065
1066impl IntoAttributeValue for String {
1067 fn into_value(self) -> AttributeValue {
1068 AttributeValue::Text(self)
1069 }
1070}
1071
1072impl IntoAttributeValue for f32 {
1073 fn into_value(self) -> AttributeValue {
1074 AttributeValue::Float(self as _)
1075 }
1076}
1077impl IntoAttributeValue for f64 {
1078 fn into_value(self) -> AttributeValue {
1079 AttributeValue::Float(self)
1080 }
1081}
1082
1083impl IntoAttributeValue for i8 {
1084 fn into_value(self) -> AttributeValue {
1085 AttributeValue::Int(self as _)
1086 }
1087}
1088impl IntoAttributeValue for i16 {
1089 fn into_value(self) -> AttributeValue {
1090 AttributeValue::Int(self as _)
1091 }
1092}
1093impl IntoAttributeValue for i32 {
1094 fn into_value(self) -> AttributeValue {
1095 AttributeValue::Int(self as _)
1096 }
1097}
1098impl IntoAttributeValue for i64 {
1099 fn into_value(self) -> AttributeValue {
1100 AttributeValue::Int(self)
1101 }
1102}
1103impl IntoAttributeValue for isize {
1104 fn into_value(self) -> AttributeValue {
1105 AttributeValue::Int(self as _)
1106 }
1107}
1108impl IntoAttributeValue for i128 {
1109 fn into_value(self) -> AttributeValue {
1110 AttributeValue::Int(self as _)
1111 }
1112}
1113
1114impl IntoAttributeValue for u8 {
1115 fn into_value(self) -> AttributeValue {
1116 AttributeValue::Int(self as _)
1117 }
1118}
1119impl IntoAttributeValue for u16 {
1120 fn into_value(self) -> AttributeValue {
1121 AttributeValue::Int(self as _)
1122 }
1123}
1124impl IntoAttributeValue for u32 {
1125 fn into_value(self) -> AttributeValue {
1126 AttributeValue::Int(self as _)
1127 }
1128}
1129impl IntoAttributeValue for u64 {
1130 fn into_value(self) -> AttributeValue {
1131 AttributeValue::Int(self as _)
1132 }
1133}
1134impl IntoAttributeValue for usize {
1135 fn into_value(self) -> AttributeValue {
1136 AttributeValue::Int(self as _)
1137 }
1138}
1139impl IntoAttributeValue for u128 {
1140 fn into_value(self) -> AttributeValue {
1141 AttributeValue::Int(self as _)
1142 }
1143}
1144
1145impl IntoAttributeValue for bool {
1146 fn into_value(self) -> AttributeValue {
1147 AttributeValue::Bool(self)
1148 }
1149}
1150
1151impl IntoAttributeValue for Arguments<'_> {
1152 fn into_value(self) -> AttributeValue {
1153 AttributeValue::Text(self.to_string())
1154 }
1155}
1156
1157impl IntoAttributeValue for Rc<dyn AnyValue> {
1158 fn into_value(self) -> AttributeValue {
1159 AttributeValue::Any(self)
1160 }
1161}
1162
1163impl<T> IntoAttributeValue for ListenerCallback<T> {
1164 fn into_value(self) -> AttributeValue {
1165 AttributeValue::Listener(self.erase())
1166 }
1167}
1168
1169impl<T: IntoAttributeValue> IntoAttributeValue for Option<T> {
1170 fn into_value(self) -> AttributeValue {
1171 match self {
1172 Some(val) => val.into_value(),
1173 None => AttributeValue::None,
1174 }
1175 }
1176}
1177
1178pub struct AnyFmtMarker;
1179impl<T> IntoAttributeValue<AnyFmtMarker> for T
1180where
1181 T: DioxusFormattable,
1182{
1183 fn into_value(self) -> AttributeValue {
1184 AttributeValue::Text(self.format().to_string())
1185 }
1186}
1187
1188pub trait HasAttributes {
1190 fn push_attribute<T>(
1192 self,
1193 name: &'static str,
1194 ns: Option<&'static str>,
1195 attr: impl IntoAttributeValue<T>,
1196 volatile: bool,
1197 ) -> Self;
1198}