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