Selaa lähdekoodia

Merge pull request #711 from Demonthos/realdom-generic-over-custom-types

make the real dom generic over custom types
Jon Kelley 2 vuotta sitten
vanhempi
commit
3733ce7332

+ 53 - 11
packages/native-core-macro/src/lib.rs

@@ -124,6 +124,17 @@ enum DependencyKind {
 /// # The state attribute
 /// # The state attribute
 /// The state macro declares a member that implements the State trait. This allows you to organize your state into multiple isolated components.
 /// The state macro declares a member that implements the State trait. This allows you to organize your state into multiple isolated components.
 /// Unlike the other attributes, the state attribute does not accept any arguments, because a nested state cannot depend on any other part of the state.
 /// Unlike the other attributes, the state attribute does not accept any arguments, because a nested state cannot depend on any other part of the state.
+///
+/// # Custom values
+///
+/// If your state has a custom value type you can specify it with the state attribute.
+///
+/// ```rust, ignore
+/// #[derive(State)]
+/// #[state(custom_value = MyCustomType)]
+/// struct MyStruct {
+///     // ...
+/// }
 #[proc_macro_derive(
 #[proc_macro_derive(
     State,
     State,
     attributes(node_dep_state, child_dep_state, parent_dep_state, state)
     attributes(node_dep_state, child_dep_state, parent_dep_state, state)
@@ -134,6 +145,33 @@ pub fn state_macro_derive(input: TokenStream) -> TokenStream {
 }
 }
 
 
 fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
 fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
+    let custom_type = ast
+        .attrs
+        .iter()
+        .find(|a| a.path.is_ident("state"))
+        .and_then(|attr| {
+            // parse custom_type = "MyType"
+            let assignment = attr.parse_args::<syn::Expr>().unwrap();
+            if let syn::Expr::Assign(assign) = assignment {
+                let (left, right) = (&*assign.left, &*assign.right);
+                if let syn::Expr::Path(e) = left {
+                    let path = &e.path;
+                    if let Some(ident) = path.get_ident() {
+                        if ident == "custom_value" {
+                            return match right {
+                                syn::Expr::Path(e) => {
+                                    let path = &e.path;
+                                    Some(quote! {#path})
+                                }
+                                _ => None,
+                            };
+                        }
+                    }
+                }
+            }
+            None
+        })
+        .unwrap_or(quote! {()});
     let type_name = &ast.ident;
     let type_name = &ast.ident;
     let fields: Vec<_> = match &ast.data {
     let fields: Vec<_> = match &ast.data {
         syn::Data::Struct(data) => match &data.fields {
         syn::Data::Struct(data) => match &data.fields {
@@ -162,12 +200,12 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
             let impl_members = state_strct
             let impl_members = state_strct
                 .state_members
                 .state_members
                 .iter()
                 .iter()
-                .map(|m| m.impl_pass(state_strct.ty));
+                .map(|m| m.impl_pass(state_strct.ty, &custom_type));
 
 
             let gen = quote! {
             let gen = quote! {
                 #(#impl_members)*
                 #(#impl_members)*
-                impl State for #type_name {
-                    const PASSES: &'static [dioxus_native_core::AnyPass<dioxus_native_core::node::Node<Self>>] = &[
+                impl State<#custom_type> for #type_name {
+                    const PASSES: &'static [dioxus_native_core::AnyPass<dioxus_native_core::node::Node<Self, #custom_type>>] = &[
                         #(#passes),*
                         #(#passes),*
                     ];
                     ];
                     const MASKS: &'static [dioxus_native_core::NodeMask] = &[#(#member_types::NODE_MASK),*];
                     const MASKS: &'static [dioxus_native_core::NodeMask] = &[#(#member_types::NODE_MASK),*];
@@ -378,7 +416,11 @@ impl<'a> StateMember<'a> {
     }
     }
 
 
     /// generate code to call the resolve function for the state. This does not handle checking if resolving the state is necessary, or marking the states that depend on this state as dirty.
     /// generate code to call the resolve function for the state. This does not handle checking if resolving the state is necessary, or marking the states that depend on this state as dirty.
-    fn impl_pass(&self, parent_type: &Ident) -> quote::__private::TokenStream {
+    fn impl_pass(
+        &self,
+        parent_type: &Ident,
+        custom_type: impl ToTokens,
+    ) -> quote::__private::TokenStream {
         let ident = &self.mem.ident;
         let ident = &self.mem.ident;
         let get_ctx = if let Some(ctx_ty) = &self.ctx_ty {
         let get_ctx = if let Some(ctx_ty) = &self.ctx_ty {
             if ctx_ty == &parse_quote!(()) {
             if ctx_ty == &parse_quote!(()) {
@@ -399,8 +441,8 @@ impl<'a> StateMember<'a> {
         let impl_specific = match self.dep_kind {
         let impl_specific = match self.dep_kind {
             DependencyKind::Node => {
             DependencyKind::Node => {
                 quote! {
                 quote! {
-                    impl dioxus_native_core::NodePass<dioxus_native_core::node::Node<#parent_type>> for #unit_type {
-                        fn pass(&self, node: &mut dioxus_native_core::node::Node<#parent_type>, ctx: &dioxus_native_core::SendAnyMap) -> bool {
+                    impl dioxus_native_core::NodePass<dioxus_native_core::node::Node<#parent_type, #custom_type>> for #unit_type {
+                        fn pass(&self, node: &mut dioxus_native_core::node::Node<#parent_type, #custom_type>, ctx: &dioxus_native_core::SendAnyMap) -> bool {
                             node.state.#ident.reduce(#node_view, (#(&node.state.#dep_idents,)*), #get_ctx)
                             node.state.#ident.reduce(#node_view, (#(&node.state.#dep_idents,)*), #get_ctx)
                         }
                         }
                     }
                     }
@@ -437,11 +479,11 @@ impl<'a> StateMember<'a> {
                     }
                     }
                 };
                 };
                 quote!(
                 quote!(
-                    impl dioxus_native_core::UpwardPass<dioxus_native_core::node::Node<#parent_type>> for #unit_type{
+                    impl dioxus_native_core::UpwardPass<dioxus_native_core::node::Node<#parent_type, #custom_type>> for #unit_type{
                         fn pass<'a>(
                         fn pass<'a>(
                             &self,
                             &self,
-                            node: &mut dioxus_native_core::node::Node<#parent_type>,
-                            children: &mut dyn Iterator<Item = &'a mut dioxus_native_core::node::Node<#parent_type>>,
+                            node: &mut dioxus_native_core::node::Node<#parent_type, #custom_type>,
+                            children: &mut dyn Iterator<Item = &'a mut dioxus_native_core::node::Node<#parent_type, #custom_type>>,
                             ctx: &dioxus_native_core::SendAnyMap,
                             ctx: &dioxus_native_core::SendAnyMap,
                         ) -> dioxus_native_core::PassReturn {
                         ) -> dioxus_native_core::PassReturn {
                             let update = node.state.#ident.reduce(#node_view, children.map(|c| (#(&c.state.#dep_idents,)*)), #get_ctx);
                             let update = node.state.#ident.reduce(#node_view, children.map(|c| (#(&c.state.#dep_idents,)*)), #get_ctx);
@@ -481,8 +523,8 @@ impl<'a> StateMember<'a> {
                     }
                     }
                 };
                 };
                 quote!(
                 quote!(
-                    impl dioxus_native_core::DownwardPass<dioxus_native_core::node::Node<#parent_type>> for #unit_type {
-                        fn pass(&self, node: &mut dioxus_native_core::node::Node<#parent_type>, parent: Option<&mut dioxus_native_core::node::Node<#parent_type>>, ctx: &dioxus_native_core::SendAnyMap) -> dioxus_native_core::PassReturn{
+                    impl dioxus_native_core::DownwardPass<dioxus_native_core::node::Node<#parent_type, #custom_type>> for #unit_type {
+                        fn pass(&self, node: &mut dioxus_native_core::node::Node<#parent_type, #custom_type>, parent: Option<&mut dioxus_native_core::node::Node<#parent_type, #custom_type>>, ctx: &dioxus_native_core::SendAnyMap) -> dioxus_native_core::PassReturn{
                             let update = node.state.#ident.reduce(#node_view, parent.as_ref().map(|p| (#(&p.state.#dep_idents,)*)), #get_ctx);
                             let update = node.state.#ident.reduce(#node_view, parent.as_ref().map(|p| (#(&p.state.#dep_idents,)*)), #get_ctx);
                             #update
                             #update
                         }
                         }

+ 3 - 3
packages/native-core/src/node.rs

@@ -5,7 +5,7 @@ use std::fmt::Debug;
 
 
 /// The node is stored client side and stores only basic data about the node.
 /// The node is stored client side and stores only basic data about the node.
 #[derive(Debug, Clone)]
 #[derive(Debug, Clone)]
-pub struct Node<S: State, V: FromAnyValue = ()> {
+pub struct Node<S: State<V>, V: FromAnyValue + 'static = ()> {
     /// The transformed state of the node.
     /// The transformed state of the node.
     pub state: S,
     pub state: S,
     /// The raw data for the node
     /// The raw data for the node
@@ -37,7 +37,7 @@ pub enum NodeType<V: FromAnyValue = ()> {
     Placeholder,
     Placeholder,
 }
 }
 
 
-impl<S: State, V: FromAnyValue> Node<S, V> {
+impl<S: State<V>, V: FromAnyValue> Node<S, V> {
     pub(crate) fn new(node_type: NodeType<V>) -> Self {
     pub(crate) fn new(node_type: NodeType<V>) -> Self {
         Node {
         Node {
             state: S::default(),
             state: S::default(),
@@ -82,7 +82,7 @@ pub enum OwnedAttributeValue<V: FromAnyValue = ()> {
     Custom(V),
     Custom(V),
 }
 }
 
 
-pub trait FromAnyValue {
+pub trait FromAnyValue: Clone {
     fn from_any_value(value: &dyn AnyValue) -> Self;
     fn from_any_value(value: &dyn AnyValue) -> Self;
 }
 }
 
 

+ 22 - 19
packages/native-core/src/real_dom.rs

@@ -3,7 +3,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
 use std::fmt::Debug;
 use std::fmt::Debug;
 use std::ops::{Deref, DerefMut, Index, IndexMut};
 use std::ops::{Deref, DerefMut, Index, IndexMut};
 
 
-use crate::node::{Node, NodeType, OwnedAttributeDiscription, OwnedAttributeValue};
+use crate::node::{FromAnyValue, Node, NodeType, OwnedAttributeDiscription, OwnedAttributeValue};
 use crate::node_ref::{AttributeMask, NodeMask};
 use crate::node_ref::{AttributeMask, NodeMask};
 use crate::passes::DirtyNodeStates;
 use crate::passes::DirtyNodeStates;
 use crate::state::State;
 use crate::state::State;
@@ -25,9 +25,12 @@ fn mark_dirty(
 /// A Dom that can sync with the VirtualDom mutations intended for use in lazy renderers.
 /// A Dom that can sync with the VirtualDom mutations intended for use in lazy renderers.
 /// The render state passes from parent to children and or accumulates state from children to parents.
 /// The render state passes from parent to children and or accumulates state from children to parents.
 /// To get started implement [crate::state::ParentDepState], [crate::state::NodeDepState], or [crate::state::ChildDepState] and call [RealDom::apply_mutations] to update the dom and [RealDom::update_state] to update the state of the nodes.
 /// To get started implement [crate::state::ParentDepState], [crate::state::NodeDepState], or [crate::state::ChildDepState] and call [RealDom::apply_mutations] to update the dom and [RealDom::update_state] to update the state of the nodes.
+///
+/// # Custom values
+/// To allow custom values to be passed into attributes implement FromAnyValue on a type that can represent your custom value and specify the V generic to be that type. If you have many different custom values, it can be useful to use a enum type to represent the varients.
 #[derive(Debug)]
 #[derive(Debug)]
-pub struct RealDom<S: State> {
-    pub tree: Tree<Node<S>>,
+pub struct RealDom<S: State<V>, V: FromAnyValue + 'static = ()> {
+    pub tree: Tree<Node<S, V>>,
     /// a map from element id to real node id
     /// a map from element id to real node id
     node_id_mapping: Vec<Option<RealNodeId>>,
     node_id_mapping: Vec<Option<RealNodeId>>,
     nodes_listening: FxHashMap<String, FxHashSet<RealNodeId>>,
     nodes_listening: FxHashMap<String, FxHashSet<RealNodeId>>,
@@ -36,14 +39,14 @@ pub struct RealDom<S: State> {
     root_initialized: bool,
     root_initialized: bool,
 }
 }
 
 
-impl<S: State> Default for RealDom<S> {
+impl<S: State<V>, V: FromAnyValue> Default for RealDom<S, V> {
     fn default() -> Self {
     fn default() -> Self {
         Self::new()
         Self::new()
     }
     }
 }
 }
 
 
-impl<S: State> RealDom<S> {
-    pub fn new() -> RealDom<S> {
+impl<S: State<V>, V: FromAnyValue> RealDom<S, V> {
+    pub fn new() -> RealDom<S, V> {
         let mut root = Node::new(NodeType::Element {
         let mut root = Node::new(NodeType::Element {
             tag: "Root".to_string(),
             tag: "Root".to_string(),
             namespace: Some("Root".to_string()),
             namespace: Some("Root".to_string()),
@@ -91,7 +94,7 @@ impl<S: State> RealDom<S> {
         current
         current
     }
     }
 
 
-    fn create_node(&mut self, node: Node<S>) -> RealNodeId {
+    fn create_node(&mut self, node: Node<S, V>) -> RealNodeId {
         let node_id = self.tree.create_node(node);
         let node_id = self.tree.create_node(node);
         let node = self.tree.get_mut(node_id).unwrap();
         let node = self.tree.get_mut(node_id).unwrap();
         node.node_data.node_id = node_id;
         node.node_data.node_id = node_id;
@@ -362,7 +365,7 @@ impl<S: State> RealDom<S> {
 
 
     /// Find all nodes that are listening for an event, sorted by there height in the dom progressing starting at the bottom and progressing up.
     /// Find all nodes that are listening for an event, sorted by there height in the dom progressing starting at the bottom and progressing up.
     /// This can be useful to avoid creating duplicate events.
     /// This can be useful to avoid creating duplicate events.
-    pub fn get_listening_sorted(&self, event: &'static str) -> Vec<&Node<S>> {
+    pub fn get_listening_sorted(&self, event: &'static str) -> Vec<&Node<S, V>> {
         if let Some(nodes) = self.nodes_listening.get(event) {
         if let Some(nodes) = self.nodes_listening.get(event) {
             let mut listening: Vec<_> = nodes.iter().map(|id| &self[*id]).collect();
             let mut listening: Vec<_> = nodes.iter().map(|id| &self[*id]).collect();
             listening.sort_by(|n1, n2| {
             listening.sort_by(|n1, n2| {
@@ -409,9 +412,9 @@ impl<S: State> RealDom<S> {
     }
     }
 }
 }
 
 
-impl<S: State + Sync> RealDom<S>
+impl<S: State<V> + Sync, V: FromAnyValue> RealDom<S, V>
 where
 where
-    Tree<Node<S>>: Sync + Send,
+    Tree<Node<S, V>>: Sync + Send,
 {
 {
     /// Update the state of the dom, after appling some mutations. This will keep the nodes in the dom up to date with their VNode counterparts.
     /// Update the state of the dom, after appling some mutations. This will keep the nodes in the dom up to date with their VNode counterparts.
     /// This will resolve the state in parallel
     /// This will resolve the state in parallel
@@ -424,43 +427,43 @@ where
     }
     }
 }
 }
 
 
-impl<S: State> Deref for RealDom<S> {
-    type Target = Tree<Node<S>>;
+impl<S: State<V>, V: FromAnyValue> Deref for RealDom<S, V> {
+    type Target = Tree<Node<S, V>>;
 
 
     fn deref(&self) -> &Self::Target {
     fn deref(&self) -> &Self::Target {
         &self.tree
         &self.tree
     }
     }
 }
 }
 
 
-impl<S: State> DerefMut for RealDom<S> {
+impl<S: State<V>, V: FromAnyValue> DerefMut for RealDom<S, V> {
     fn deref_mut(&mut self) -> &mut Self::Target {
     fn deref_mut(&mut self) -> &mut Self::Target {
         &mut self.tree
         &mut self.tree
     }
     }
 }
 }
 
 
-impl<S: State> Index<ElementId> for RealDom<S> {
-    type Output = Node<S>;
+impl<S: State<V>, V: FromAnyValue> Index<ElementId> for RealDom<S, V> {
+    type Output = Node<S, V>;
 
 
     fn index(&self, id: ElementId) -> &Self::Output {
     fn index(&self, id: ElementId) -> &Self::Output {
         self.tree.get(self.element_to_node_id(id)).unwrap()
         self.tree.get(self.element_to_node_id(id)).unwrap()
     }
     }
 }
 }
 
 
-impl<S: State> Index<RealNodeId> for RealDom<S> {
-    type Output = Node<S>;
+impl<S: State<V>, V: FromAnyValue> Index<RealNodeId> for RealDom<S, V> {
+    type Output = Node<S, V>;
 
 
     fn index(&self, idx: RealNodeId) -> &Self::Output {
     fn index(&self, idx: RealNodeId) -> &Self::Output {
         self.tree.get(idx).unwrap()
         self.tree.get(idx).unwrap()
     }
     }
 }
 }
 
 
-impl<S: State> IndexMut<ElementId> for RealDom<S> {
+impl<S: State<V>, V: FromAnyValue> IndexMut<ElementId> for RealDom<S, V> {
     fn index_mut(&mut self, id: ElementId) -> &mut Self::Output {
     fn index_mut(&mut self, id: ElementId) -> &mut Self::Output {
         self.tree.get_mut(self.element_to_node_id(id)).unwrap()
         self.tree.get_mut(self.element_to_node_id(id)).unwrap()
     }
     }
 }
 }
 
 
-impl<S: State> IndexMut<RealNodeId> for RealDom<S> {
+impl<S: State<V>, V: FromAnyValue> IndexMut<RealNodeId> for RealDom<S, V> {
     fn index_mut(&mut self, idx: RealNodeId) -> &mut Self::Output {
     fn index_mut(&mut self, idx: RealNodeId) -> &mut Self::Output {
         self.tree.get_mut(idx).unwrap()
         self.tree.get_mut(idx).unwrap()
     }
     }

+ 4 - 4
packages/native-core/src/state.rs

@@ -210,14 +210,14 @@ pub trait NodeDepState<V: FromAnyValue = ()> {
 }
 }
 
 
 /// Do not implement this trait. It is only meant to be derived and used through [crate::real_dom::RealDom].
 /// Do not implement this trait. It is only meant to be derived and used through [crate::real_dom::RealDom].
-pub trait State: Default + Clone + 'static {
+pub trait State<V: FromAnyValue + 'static>: Default + Clone + 'static {
     #[doc(hidden)]
     #[doc(hidden)]
-    const PASSES: &'static [AnyPass<Node<Self>>];
+    const PASSES: &'static [AnyPass<Node<Self, V>>];
     #[doc(hidden)]
     #[doc(hidden)]
     const MASKS: &'static [NodeMask];
     const MASKS: &'static [NodeMask];
 
 
     #[doc(hidden)]
     #[doc(hidden)]
-    fn update<T: TreeView<Node<Self>> + Sync + Send>(
+    fn update<T: TreeView<Node<Self, V>> + Sync + Send>(
         dirty: DirtyNodeStates,
         dirty: DirtyNodeStates,
         tree: &mut T,
         tree: &mut T,
         ctx: SendAnyMap,
         ctx: SendAnyMap,
@@ -227,7 +227,7 @@ pub trait State: Default + Clone + 'static {
     }
     }
 
 
     #[doc(hidden)]
     #[doc(hidden)]
-    fn update_single_threaded<T: TreeView<Node<Self>>>(
+    fn update_single_threaded<T: TreeView<Node<Self, V>>>(
         dirty: DirtyNodeStates,
         dirty: DirtyNodeStates,
         tree: &mut T,
         tree: &mut T,
         ctx: SendAnyMap,
         ctx: SendAnyMap,

+ 17 - 7
packages/native-core/src/utils/persistant_iterator.rs

@@ -1,4 +1,10 @@
-use crate::{node::NodeType, real_dom::RealDom, state::State, tree::TreeView, NodeId, RealNodeId};
+use crate::{
+    node::{FromAnyValue, NodeType},
+    real_dom::RealDom,
+    state::State,
+    tree::TreeView,
+    NodeId, RealNodeId,
+};
 use dioxus_core::{Mutation, Mutations};
 use dioxus_core::{Mutation, Mutations};
 use std::fmt::Debug;
 use std::fmt::Debug;
 
 
@@ -67,7 +73,11 @@ impl PersistantElementIter {
 
 
     /// remove stale element refreneces
     /// remove stale element refreneces
     /// returns true if the focused element is removed
     /// returns true if the focused element is removed
-    pub fn prune<S: State>(&mut self, mutations: &Mutations, rdom: &RealDom<S>) -> bool {
+    pub fn prune<S: State<V>, V: FromAnyValue>(
+        &mut self,
+        mutations: &Mutations,
+        rdom: &RealDom<S, V>,
+    ) -> bool {
         let mut changed = false;
         let mut changed = false;
         let ids_removed: Vec<_> = mutations
         let ids_removed: Vec<_> = mutations
             .edits
             .edits
@@ -124,7 +134,7 @@ impl PersistantElementIter {
     }
     }
 
 
     /// get the next element
     /// get the next element
-    pub fn next<S: State>(&mut self, rdom: &RealDom<S>) -> ElementProduced {
+    pub fn next<S: State<V>, V: FromAnyValue>(&mut self, rdom: &RealDom<S, V>) -> ElementProduced {
         if self.stack.is_empty() {
         if self.stack.is_empty() {
             let id = NodeId(0);
             let id = NodeId(0);
             let new = (id, NodePosition::AtNode);
             let new = (id, NodePosition::AtNode);
@@ -160,12 +170,12 @@ impl PersistantElementIter {
     }
     }
 
 
     /// get the previous element
     /// get the previous element
-    pub fn prev<S: State>(&mut self, rdom: &RealDom<S>) -> ElementProduced {
+    pub fn prev<S: State<V>, V: FromAnyValue>(&mut self, rdom: &RealDom<S, V>) -> ElementProduced {
         // recursively add the last child element to the stack
         // recursively add the last child element to the stack
-        fn push_back<S: State>(
+        fn push_back<S: State<V>, V: FromAnyValue>(
             stack: &mut smallvec::SmallVec<[(RealNodeId, NodePosition); 5]>,
             stack: &mut smallvec::SmallVec<[(RealNodeId, NodePosition); 5]>,
             new_node: RealNodeId,
             new_node: RealNodeId,
-            rdom: &RealDom<S>,
+            rdom: &RealDom<S, V>,
         ) -> RealNodeId {
         ) -> RealNodeId {
             match &rdom[new_node].node_data.node_type {
             match &rdom[new_node].node_data.node_type {
                 NodeType::Element { .. } => {
                 NodeType::Element { .. } => {
@@ -228,7 +238,7 @@ impl PersistantElementIter {
 
 
 #[derive(Default, Clone, Debug)]
 #[derive(Default, Clone, Debug)]
 struct Empty {}
 struct Empty {}
-impl State for Empty {
+impl State<()> for Empty {
     const PASSES: &'static [crate::AnyPass<crate::node::Node<Self, ()>>] = &[];
     const PASSES: &'static [crate::AnyPass<crate::node::Node<Self, ()>>] = &[];
 
 
     const MASKS: &'static [crate::NodeMask] = &[];
     const MASKS: &'static [crate::NodeMask] = &[];