Skip to main content

dioxus_core/
nodes.rs

1use crate::{
2    DynamicAnchor, DynamicNodeSlot, Element, Event, Properties, ScopeId, Template, VirtualDom,
3    arena::ElementId,
4    events::ListenerCallback,
5    innerlude::{BoxedAnyProps, MountId, ScopeState, VProps},
6    properties::ComponentFunction,
7    view::ViewExt,
8};
9use dioxus_core_types::DioxusFormattable;
10
11use std::ops::Deref;
12use std::rc::Rc;
13use std::{
14    any::{Any, TypeId},
15    fmt::{Arguments, Debug},
16};
17
18/// Runtime node and attribute values that hydrate a static [`Template`].
19pub struct DynamicValues {
20    /// Root key for this render.
21    pub(crate) key: Option<String>,
22
23    /// Dynamic node values in template order.
24    pub(crate) dynamic_nodes: Vec<DynamicNode>,
25
26    /// Dynamic attribute values in template order.
27    pub(crate) dynamic_attrs: Vec<Box<[Attribute]>>,
28}
29
30impl Debug for DynamicValues {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        f.debug_struct("DynamicValues").finish_non_exhaustive()
33    }
34}
35
36impl DynamicValues {
37    /// Create a dynamic node/attribute payload.
38    ///
39    /// Each dynamic attribute slot is normalized so its attributes are sorted by
40    /// `(name, namespace)`. Duplicate keys keep their relative input order, giving
41    /// last-wins semantics for spread attributes like `..props.attributes`.
42    #[inline]
43    pub fn from_parts(
44        key: Option<String>,
45        dynamic_nodes: Box<[DynamicNode]>,
46        dynamic_attrs: Box<[Box<[Attribute]>]>,
47    ) -> Self {
48        let mut values = Self {
49            key,
50            dynamic_nodes: dynamic_nodes.into_vec(),
51            dynamic_attrs: dynamic_attrs.into_vec(),
52        };
53        values.normalize();
54        values
55    }
56
57    /// Create an empty dynamic node/attribute payload.
58    pub(crate) fn new() -> Self {
59        Self {
60            key: None,
61            dynamic_nodes: Vec::new(),
62            dynamic_attrs: Vec::new(),
63        }
64    }
65
66    /// Set the root key for this render.
67    pub(crate) fn set_key(&mut self, key: Option<String>) {
68        self.key = key;
69    }
70
71    /// Push a dynamic node slot.
72    pub(crate) fn push_node(&mut self, value: DynamicNode) {
73        self.dynamic_nodes.push(value);
74    }
75
76    /// Push a dynamic attribute slot.
77    ///
78    /// Dynamic attribute slots are normalized by [`Self::normalize`] before the values are stored
79    /// on a [`VNode`].
80    pub(crate) fn push_attrs(&mut self, value: Box<[Attribute]>) {
81        self.dynamic_attrs.push(value);
82    }
83
84    /// Normalize dynamic attribute slots for diffing.
85    pub(crate) fn normalize(&mut self) {
86        for slot in self.dynamic_attrs.iter_mut() {
87            slot.sort_by_key(|attribute| (attribute.name, attribute.namespace));
88        }
89    }
90
91    /// The root key for this render.
92    #[inline]
93    pub fn key(&self) -> Option<&str> {
94        self.key.as_deref()
95    }
96
97    /// The dynamic node values in template order.
98    #[inline]
99    pub fn dynamic_node_values(&self) -> &[DynamicNode] {
100        &self.dynamic_nodes
101    }
102
103    /// The dynamic attribute values in template order.
104    #[inline]
105    pub fn dynamic_attr_values(&self) -> &[Box<[Attribute]>] {
106        &self.dynamic_attrs
107    }
108}
109
110/// A static template with the values rendered for it.
111pub(crate) struct VNodeInner {
112    /// The static template.
113    pub template: Template,
114
115    /// The rendered dynamic node/attribute values.
116    pub view: DynamicValues,
117}
118
119impl Debug for VNodeInner {
120    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121        f.debug_struct("VNodeInner").finish_non_exhaustive()
122    }
123}
124
125impl Deref for VNodeInner {
126    type Target = DynamicValues;
127
128    fn deref(&self) -> &Self::Target {
129        &self.view
130    }
131}
132
133/// A reference to a template along with any context needed to hydrate it
134///
135/// The dynamic parts of the template are stored separately from the static parts. This allows faster diffing by skipping
136/// static parts of the template.
137#[derive(Clone)]
138pub struct VNode {
139    vnode: Rc<VNodeInner>,
140}
141
142impl Debug for VNode {
143    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144        f.debug_struct("VNode").finish_non_exhaustive()
145    }
146}
147
148impl Default for VNode {
149    fn default() -> Self {
150        Self::placeholder()
151    }
152}
153
154impl PartialEq for VNode {
155    fn eq(&self, other: &Self) -> bool {
156        Rc::ptr_eq(&self.vnode, &other.vnode)
157    }
158}
159
160impl VNode {
161    /// The static template for this rendered node.
162    pub fn template(&self) -> &Template {
163        &self.vnode.template
164    }
165
166    /// The rendered dynamic node/attribute values.
167    pub fn dynamic_values(&self) -> &DynamicValues {
168        &self.vnode.view
169    }
170
171    /// The dynamic node values in template order.
172    pub fn dynamic_node_values(&self) -> &[DynamicNode] {
173        self.dynamic_values().dynamic_node_values()
174    }
175
176    /// The dynamic attribute values in template order.
177    pub fn dynamic_attr_values(&self) -> &[Box<[Attribute]>] {
178        self.dynamic_values().dynamic_attr_values()
179    }
180
181    /// The root key for this render.
182    pub fn key(&self) -> Option<&str> {
183        self.dynamic_values().key()
184    }
185
186    /// Create a template with no nodes that will be skipped over during diffing
187    pub fn empty() -> Element {
188        Ok(Self::default())
189    }
190
191    /// Create an empty VNode that produces no DOM nodes
192    pub fn placeholder() -> Self {
193        use std::cell::OnceCell;
194        // We can reuse this empty vnode across the same thread to save memory
195        thread_local! {
196            static EMPTY_VNODE: OnceCell<VNode> = const { OnceCell::new() };
197        }
198        EMPTY_VNODE.with(|cell| {
199            cell.get_or_init(move || crate::view::fragment().into_vnode())
200                .clone()
201        })
202    }
203
204    /// Create a new VNode from a static template and dynamic node/attribute payload.
205    pub fn new(template: Template, mut values: DynamicValues) -> Self {
206        values.normalize();
207
208        debug_assert!(
209            values.dynamic_nodes.len()
210                == template
211                    .anchors()
212                    .iter()
213                    .map(|anchor| anchor.nodes().end)
214                    .max()
215                    .unwrap_or_default(),
216            "bad dynamic node count"
217        );
218        debug_assert!(
219            values.dynamic_attrs.len()
220                == template
221                    .anchors()
222                    .iter()
223                    .map(|anchor| anchor.attributes().end)
224                    .max()
225                    .unwrap_or_default(),
226            "bad dynamic attribute count"
227        );
228
229        Self {
230            vnode: Rc::new(VNodeInner {
231                template,
232                view: values,
233            }),
234        }
235    }
236}
237
238#[derive(Clone, Copy, Debug)]
239/// A [`VNode`] paired with the live mount that renders it.
240pub struct MountedVNode<'a> {
241    vnode: &'a VNode,
242    mount: MountId,
243}
244
245impl<'a> MountedVNode<'a> {
246    pub(crate) const fn new(vnode: &'a VNode, mount: MountId) -> Self {
247        Self { vnode, mount }
248    }
249
250    pub(crate) const fn mount(self) -> MountId {
251        self.mount
252    }
253
254    /// Return the underlying vnode.
255    pub const fn vnode(self) -> &'a VNode {
256        self.vnode
257    }
258
259    /// Get the mounted id for a dynamic node.
260    pub fn mounted_dynamic_node(
261        self,
262        slot: DynamicNodeSlot<'a>,
263        dom: &VirtualDom,
264    ) -> Option<ElementId> {
265        let dynamic_node_idx = slot.index();
266        match &self.vnode.dynamic_node_values()[dynamic_node_idx] {
267            DynamicNode::Text(_) => dom
268                .mounted_dynamic_text_node(self.mount, dynamic_node_idx)
269                .map(|id| id.element_id()),
270            _ => None,
271        }
272    }
273
274    /// Get the mounted id for the static template node addressed by a dynamic anchor.
275    pub fn mounted_anchor_node(
276        self,
277        anchor: DynamicAnchor<'a>,
278        dom: &VirtualDom,
279    ) -> Option<ElementId> {
280        dom.mounted_anchor_node(self.mount, anchor.anchor_index())
281            .map(|id| id.element_id())
282    }
283
284    /// Get the mounted id for a static template node by anchor index.
285    pub fn mounted_static_anchor(self, anchor_idx: usize, dom: &VirtualDom) -> Option<ElementId> {
286        dom.mounted_anchor_node(self.mount, anchor_idx)
287            .map(|id| id.element_id())
288    }
289
290    /// Get mounted children for a dynamic fragment.
291    pub fn mounted_fragment_children(
292        self,
293        slot: DynamicNodeSlot<'a>,
294        dom: &VirtualDom,
295    ) -> Vec<MountedVNode<'a>> {
296        let dynamic_node_idx = slot.index();
297        let DynamicNode::Fragment(children) = &self.vnode.dynamic_node_values()[dynamic_node_idx]
298        else {
299            return Vec::new();
300        };
301
302        children
303            .iter()
304            .zip(dom.mounted_fragment_children(self.mount, dynamic_node_idx, children.len()))
305            .map(|(vnode, mount)| MountedVNode::new(vnode, mount))
306            .collect()
307    }
308}
309
310impl Deref for MountedVNode<'_> {
311    type Target = VNode;
312
313    fn deref(&self) -> &Self::Target {
314        self.vnode
315    }
316}
317
318/// A node created at runtime
319///
320/// This node's index in the DynamicNode list on VNode should match its respective `Dynamic` index
321#[derive(Debug, Clone)]
322pub enum DynamicNode {
323    /// A component node
324    ///
325    /// Most of the time, Dioxus will actually know which component this is as compile time, but the props and
326    /// assigned scope are dynamic.
327    ///
328    /// The actual VComponent can be dynamic between two VNodes, though, allowing implementations to swap
329    /// the render function at runtime
330    Component(VComponent),
331
332    /// A text node
333    Text(VText),
334
335    /// A list of VNodes.
336    ///
337    /// Note that this is not a list of dynamic nodes. These must be VNodes and created through conditional rendering
338    /// or iterators.
339    Fragment(Vec<VNode>),
340}
341
342impl DynamicNode {
343    /// Convert any item that implements [`IntoDynNode`] into a [`DynamicNode`]
344    pub fn make_node<'c, I>(into: impl IntoDynNode<I> + 'c) -> DynamicNode {
345        into.into_dyn_node()
346    }
347}
348
349impl Default for DynamicNode {
350    fn default() -> Self {
351        Self::Fragment(Vec::new())
352    }
353}
354
355/// An instance of a child component
356pub struct VComponent {
357    /// The name of this component
358    pub name: &'static str,
359
360    /// The raw pointer to the render function.
361    pub(crate) render_fn: usize,
362
363    /// The rendering lifecycle for this component's scope.
364    pub(crate) driver: Rc<dyn crate::render_driver::RenderDriver>,
365
366    /// The props this component renders from.
367    pub(crate) props: BoxedAnyProps,
368}
369
370impl Clone for VComponent {
371    fn clone(&self) -> Self {
372        Self {
373            name: self.name,
374            render_fn: self.render_fn,
375            driver: self.driver.clone(),
376            props: self.props.duplicate(),
377        }
378    }
379}
380
381impl VComponent {
382    /// Create a new [`VComponent`] variant
383    pub fn new<P, M: 'static>(
384        component: impl ComponentFunction<P, M>,
385        props: P,
386        fn_name: &'static str,
387    ) -> Self
388    where
389        P: Properties + 'static,
390    {
391        let render_fn = component.fn_ptr();
392        let props = Box::new(VProps::new(
393            component,
394            <P as Properties>::memoize,
395            props,
396            fn_name,
397        ));
398        Self::new_with_driver(
399            fn_name,
400            render_fn,
401            Rc::new(crate::render_driver::BodyDriver),
402            props,
403        )
404    }
405
406    /// Create a new [`VComponent`] whose scope is rendered by `driver`.
407    pub(crate) fn new_with_driver(
408        fn_name: &'static str,
409        render_fn: usize,
410        driver: Rc<dyn crate::render_driver::RenderDriver>,
411        props: BoxedAnyProps,
412    ) -> Self {
413        VComponent {
414            name: fn_name,
415            render_fn,
416            driver,
417            props,
418        }
419    }
420
421    /// Get the [`ScopeId`] this node is mounted to if it's mounted
422    ///
423    /// This is useful for rendering nodes outside of the VirtualDom, such as in SSR
424    ///
425    /// Returns [`None`] if the node is not mounted
426    pub fn mounted_scope_id(
427        &self,
428        slot: DynamicNodeSlot<'_>,
429        vnode: MountedVNode<'_>,
430        dom: &VirtualDom,
431    ) -> Option<ScopeId> {
432        dom.mounted_dynamic_component_scope(vnode.mount(), slot.index())
433    }
434
435    /// Get the scope this node is mounted to if it's mounted
436    ///
437    /// This is useful for rendering nodes outside of the VirtualDom, such as in SSR
438    ///
439    /// Returns [`None`] if the node is not mounted
440    pub fn mounted_scope<'a>(
441        &self,
442        slot: DynamicNodeSlot<'_>,
443        vnode: MountedVNode<'_>,
444        dom: &'a VirtualDom,
445    ) -> Option<&'a ScopeState> {
446        let scope_id = dom.mounted_dynamic_component_scope(vnode.mount(), slot.index())?;
447
448        dom.scopes.get(scope_id.index())
449    }
450}
451
452impl std::fmt::Debug for VComponent {
453    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
454        f.debug_struct("VComponent")
455            .field("name", &self.name)
456            .finish()
457    }
458}
459
460/// A text node
461#[derive(Clone, Debug)]
462pub struct VText {
463    /// The actual text itself
464    pub value: String,
465}
466
467impl VText {
468    /// Create a new VText
469    #[inline]
470    pub fn new(value: impl ToString) -> Self {
471        Self {
472            value: value.to_string(),
473        }
474    }
475}
476
477impl From<Arguments<'_>> for VText {
478    fn from(args: Arguments) -> Self {
479        Self::new(args.to_string())
480    }
481}
482
483/// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`
484#[derive(Debug, Clone, PartialEq)]
485pub struct Attribute {
486    /// The name of the attribute.
487    pub name: &'static str,
488
489    /// The value of the attribute
490    pub value: AttributeValue,
491
492    /// The namespace of the attribute.
493    ///
494    /// Doesn’t exist in the html spec. Used in Dioxus to denote “style” tags and other attribute groups.
495    pub namespace: Option<&'static str>,
496
497    /// An indication of we should always try and set the attribute. Used in controlled components to ensure changes are propagated
498    pub volatile: bool,
499}
500
501impl Attribute {
502    /// Create a new [`Attribute`] from a name, value, namespace, and volatile bool
503    ///
504    /// "Volatile" refers to whether or not Dioxus should always override the value. This helps prevent the UI in
505    /// some renderers stay in sync with the VirtualDom's understanding of the world
506    pub fn new<T>(
507        name: &'static str,
508        value: impl IntoAttributeValue<T>,
509        namespace: Option<&'static str>,
510        volatile: bool,
511    ) -> Attribute {
512        Attribute {
513            name,
514            namespace,
515            volatile,
516            value: value.into_value(),
517        }
518    }
519}
520
521/// Any of the built-in values that the Dioxus VirtualDom supports as dynamic attributes on elements
522///
523/// These are built-in to be faster during the diffing process. To use a custom value, use the [`AttributeValue::Any`]
524/// variant.
525#[derive(Clone)]
526pub enum AttributeValue {
527    /// Text attribute
528    Text(String),
529
530    /// A float
531    Float(f64),
532
533    /// Signed integer
534    Int(i64),
535
536    /// Boolean
537    Bool(bool),
538
539    /// A listener, like "onclick"
540    Listener(ListenerCallback),
541
542    /// An arbitrary value that implements PartialEq and is static
543    Any(Rc<dyn AnyValue>),
544
545    /// A "none" value, resulting in the removal of an attribute from the dom
546    None,
547}
548
549impl AttributeValue {
550    /// Create a new [`AttributeValue`] with the listener variant from a callback
551    ///
552    /// The callback must be confined to the lifetime of the ScopeState
553    pub fn listener<T: 'static>(callback: impl FnMut(Event<T>) + 'static) -> AttributeValue {
554        AttributeValue::Listener(ListenerCallback::new(callback).erase())
555    }
556
557    /// Create a new [`AttributeValue`] with a value that implements [`AnyValue`]
558    pub fn any_value<T: AnyValue>(value: T) -> AttributeValue {
559        AttributeValue::Any(Rc::new(value))
560    }
561}
562
563impl std::fmt::Debug for AttributeValue {
564    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
565        match self {
566            Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
567            Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
568            Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
569            Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
570            Self::Listener(_) => f.debug_tuple("Listener").finish(),
571            Self::Any(_) => f.debug_tuple("Any").finish(),
572            Self::None => write!(f, "None"),
573        }
574    }
575}
576
577impl PartialEq for AttributeValue {
578    fn eq(&self, other: &Self) -> bool {
579        match (self, other) {
580            (Self::Text(l0), Self::Text(r0)) => l0 == r0,
581            (Self::Float(l0), Self::Float(r0)) => l0 == r0,
582            (Self::Int(l0), Self::Int(r0)) => l0 == r0,
583            (Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
584            (Self::Listener(l0), Self::Listener(r0)) => l0 == r0,
585            (Self::Any(l0), Self::Any(r0)) => l0.as_ref().any_cmp(r0.as_ref()),
586            (Self::None, Self::None) => true,
587            _ => false,
588        }
589    }
590}
591
592#[doc(hidden)]
593pub trait AnyValue: 'static {
594    /// Compare this value with another erased value.
595    fn any_cmp(&self, other: &dyn AnyValue) -> bool;
596
597    /// Return this value as [`Any`] for downcasting.
598    fn as_any(&self) -> &dyn Any;
599
600    /// Return the underlying value's [`TypeId`].
601    fn type_id(&self) -> TypeId {
602        self.as_any().type_id()
603    }
604}
605
606impl<T: Any + PartialEq + 'static> AnyValue for T {
607    fn any_cmp(&self, other: &dyn AnyValue) -> bool {
608        if let Some(other) = other.as_any().downcast_ref() {
609            self == other
610        } else {
611            false
612        }
613    }
614
615    fn as_any(&self) -> &dyn Any {
616        self
617    }
618}
619
620/// A trait that allows various items to be converted into a dynamic node for the rsx macro
621pub trait IntoDynNode<A = ()> {
622    /// Consume this item and produce a DynamicNode
623    fn into_dyn_node(self) -> DynamicNode;
624}
625
626impl IntoDynNode for () {
627    #[inline]
628    fn into_dyn_node(self) -> DynamicNode {
629        DynamicNode::default()
630    }
631}
632impl IntoDynNode for VNode {
633    #[inline]
634    fn into_dyn_node(self) -> DynamicNode {
635        DynamicNode::Fragment(vec![self])
636    }
637}
638impl IntoDynNode for DynamicNode {
639    #[inline]
640    fn into_dyn_node(self) -> DynamicNode {
641        self
642    }
643}
644impl<T: IntoDynNode> IntoDynNode for Option<T> {
645    #[inline]
646    fn into_dyn_node(self) -> DynamicNode {
647        match self {
648            Some(val) => val.into_dyn_node(),
649            None => DynamicNode::default(),
650        }
651    }
652}
653impl IntoDynNode for &Element {
654    #[inline]
655    fn into_dyn_node(self) -> DynamicNode {
656        match self.as_ref() {
657            Ok(val) => val.into_dyn_node(),
658            _ => DynamicNode::default(),
659        }
660    }
661}
662impl IntoDynNode for Element {
663    #[inline]
664    fn into_dyn_node(self) -> DynamicNode {
665        match self {
666            Ok(val) => val.into_dyn_node(),
667            _ => DynamicNode::default(),
668        }
669    }
670}
671impl IntoDynNode for &Option<VNode> {
672    #[inline]
673    fn into_dyn_node(self) -> DynamicNode {
674        match self.as_ref() {
675            Some(val) => val.clone().into_dyn_node(),
676            _ => DynamicNode::default(),
677        }
678    }
679}
680impl IntoDynNode for &str {
681    #[inline]
682    fn into_dyn_node(self) -> DynamicNode {
683        DynamicNode::Text(VText {
684            value: self.to_string(),
685        })
686    }
687}
688impl IntoDynNode for String {
689    #[inline]
690    fn into_dyn_node(self) -> DynamicNode {
691        DynamicNode::Text(VText { value: self })
692    }
693}
694impl IntoDynNode for Arguments<'_> {
695    #[inline]
696    fn into_dyn_node(self) -> DynamicNode {
697        DynamicNode::Text(VText {
698            value: self.to_string(),
699        })
700    }
701}
702impl IntoDynNode for &VNode {
703    #[inline]
704    fn into_dyn_node(self) -> DynamicNode {
705        DynamicNode::Fragment(vec![self.clone()])
706    }
707}
708
709/// Convert a value into a [`VNode`].
710pub trait IntoVNode {
711    /// Convert this value into a [`VNode`].
712    fn into_vnode(self) -> VNode;
713}
714impl IntoVNode for VNode {
715    #[inline]
716    fn into_vnode(self) -> VNode {
717        self
718    }
719}
720impl IntoVNode for &VNode {
721    #[inline]
722    fn into_vnode(self) -> VNode {
723        self.clone()
724    }
725}
726impl IntoVNode for Element {
727    #[inline]
728    fn into_vnode(self) -> VNode {
729        match self {
730            Ok(val) => val.into_vnode(),
731            _ => VNode::default(),
732        }
733    }
734}
735impl IntoVNode for &Element {
736    #[inline]
737    fn into_vnode(self) -> VNode {
738        match self {
739            Ok(val) => val.into_vnode(),
740            _ => VNode::default(),
741        }
742    }
743}
744impl IntoVNode for Option<VNode> {
745    #[inline]
746    fn into_vnode(self) -> VNode {
747        match self {
748            Some(val) => val.into_vnode(),
749            _ => VNode::default(),
750        }
751    }
752}
753impl IntoVNode for &Option<VNode> {
754    #[inline]
755    fn into_vnode(self) -> VNode {
756        match self.as_ref() {
757            Some(val) => val.clone().into_vnode(),
758            _ => VNode::default(),
759        }
760    }
761}
762impl IntoVNode for Option<Element> {
763    #[inline]
764    fn into_vnode(self) -> VNode {
765        match self {
766            Some(val) => val.into_vnode(),
767            _ => VNode::default(),
768        }
769    }
770}
771impl IntoVNode for &Option<Element> {
772    #[inline]
773    fn into_vnode(self) -> VNode {
774        match self.as_ref() {
775            Some(val) => val.clone().into_vnode(),
776            _ => VNode::default(),
777        }
778    }
779}
780
781// Note that we're using the E as a generic but this is never crafted anyways.
782pub struct FromNodeIterator;
783impl<T, I> IntoDynNode<FromNodeIterator> for T
784where
785    T: Iterator<Item = I>,
786    I: IntoVNode,
787{
788    #[inline]
789    fn into_dyn_node(self) -> DynamicNode {
790        DynamicNode::Fragment(self.into_iter().map(|node| node.into_vnode()).collect())
791    }
792}
793
794/// A value that can be converted into an attribute value
795pub trait IntoAttributeValue<T = ()> {
796    /// Convert into an attribute value
797    fn into_value(self) -> AttributeValue;
798}
799
800impl IntoAttributeValue for AttributeValue {
801    fn into_value(self) -> AttributeValue {
802        self
803    }
804}
805
806impl IntoAttributeValue for &str {
807    fn into_value(self) -> AttributeValue {
808        AttributeValue::Text(self.to_string())
809    }
810}
811
812impl IntoAttributeValue for String {
813    fn into_value(self) -> AttributeValue {
814        AttributeValue::Text(self)
815    }
816}
817
818macro_rules! impl_float_attribute_value {
819    ($($ty:ty),* $(,)?) => {
820        $(
821            impl IntoAttributeValue for $ty {
822                fn into_value(self) -> AttributeValue {
823                    AttributeValue::Float(self as _)
824                }
825            }
826        )*
827    };
828}
829
830macro_rules! impl_int_attribute_value {
831    ($($ty:ty),* $(,)?) => {
832        $(
833            impl IntoAttributeValue for $ty {
834                fn into_value(self) -> AttributeValue {
835                    AttributeValue::Int(self as _)
836                }
837            }
838        )*
839    };
840}
841
842impl_float_attribute_value!(f32, f64);
843impl_int_attribute_value!(i8, i16, i32, i64, isize, i128);
844impl_int_attribute_value!(u8, u16, u32, u64, usize, u128);
845
846impl IntoAttributeValue for bool {
847    fn into_value(self) -> AttributeValue {
848        AttributeValue::Bool(self)
849    }
850}
851
852impl IntoAttributeValue for Arguments<'_> {
853    fn into_value(self) -> AttributeValue {
854        AttributeValue::Text(self.to_string())
855    }
856}
857
858impl IntoAttributeValue for Rc<dyn AnyValue> {
859    fn into_value(self) -> AttributeValue {
860        AttributeValue::Any(self)
861    }
862}
863
864impl<T> IntoAttributeValue for ListenerCallback<T> {
865    fn into_value(self) -> AttributeValue {
866        AttributeValue::Listener(self.erase())
867    }
868}
869
870impl<T: IntoAttributeValue> IntoAttributeValue for Option<T> {
871    fn into_value(self) -> AttributeValue {
872        match self {
873            Some(val) => val.into_value(),
874            None => AttributeValue::None,
875        }
876    }
877}
878
879impl<T: ToOwned<Owned = R>, R: IntoAttributeValue> IntoAttributeValue for &T {
880    fn into_value(self) -> AttributeValue {
881        self.to_owned().into_value()
882    }
883}
884
885#[doc(hidden)]
886pub struct AnyFmtMarker;
887impl<T> IntoAttributeValue<AnyFmtMarker> for T
888where
889    T: DioxusFormattable,
890{
891    fn into_value(self) -> AttributeValue {
892        AttributeValue::Text(self.format().to_string())
893    }
894}
895
896/// A trait for anything that has a dynamic list of attributes
897pub trait HasAttributes {
898    /// Push an attribute onto the list of attributes
899    fn push_attribute<T>(
900        self,
901        name: &'static str,
902        ns: Option<&'static str>,
903        attr: impl IntoAttributeValue<T>,
904        volatile: bool,
905    ) -> Self;
906}