浏览代码

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

make the real dom generic over custom types
Jon Kelley 2 年之前
父节点
当前提交
3733ce7332

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

@@ -124,6 +124,17 @@ enum DependencyKind {
 /// # 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.
 /// 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(
     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 {
+    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 fields: Vec<_> = match &ast.data {
         syn::Data::Struct(data) => match &data.fields {
@@ -162,12 +200,12 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
             let impl_members = state_strct
                 .state_members
                 .iter()
-                .map(|m| m.impl_pass(state_strct.ty));
+                .map(|m| m.impl_pass(state_strct.ty, &custom_type));
 
             let gen = quote! {
                 #(#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),*
                     ];
                     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.
-    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 get_ctx = if let Some(ctx_ty) = &self.ctx_ty {
             if ctx_ty == &parse_quote!(()) {
@@ -399,8 +441,8 @@ impl<'a> StateMember<'a> {
         let impl_specific = match self.dep_kind {
             DependencyKind::Node => {
                 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)
                         }
                     }
@@ -437,11 +479,11 @@ impl<'a> StateMember<'a> {
                     }
                 };
                 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>(
                             &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,
                         ) -> dioxus_native_core::PassReturn {
                             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!(
-                    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);
                             #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.
 #[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.
     pub state: S,
     /// The raw data for the node
@@ -37,7 +37,7 @@ pub enum NodeType<V: FromAnyValue = ()> {
     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 {
         Node {
             state: S::default(),
@@ -82,7 +82,7 @@ pub enum OwnedAttributeValue<V: FromAnyValue = ()> {
     Custom(V),
 }
 
-pub trait FromAnyValue {
+pub trait FromAnyValue: Clone {
     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::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::passes::DirtyNodeStates;
 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.
 /// 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.
+///
+/// # 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)]
-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
     node_id_mapping: Vec<Option<RealNodeId>>,
     nodes_listening: FxHashMap<String, FxHashSet<RealNodeId>>,
@@ -36,14 +39,14 @@ pub struct RealDom<S: State> {
     root_initialized: bool,
 }
 
-impl<S: State> Default for RealDom<S> {
+impl<S: State<V>, V: FromAnyValue> Default for RealDom<S, V> {
     fn default() -> Self {
         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 {
             tag: "Root".to_string(),
             namespace: Some("Root".to_string()),
@@ -91,7 +94,7 @@ impl<S: State> RealDom<S> {
         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 = self.tree.get_mut(node_id).unwrap();
         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.
     /// 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) {
             let mut listening: Vec<_> = nodes.iter().map(|id| &self[*id]).collect();
             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
-    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.
     /// 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 {
         &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 {
         &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 {
         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 {
         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 {
         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 {
         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].
-pub trait State: Default + Clone + 'static {
+pub trait State<V: FromAnyValue + 'static>: Default + Clone + 'static {
     #[doc(hidden)]
-    const PASSES: &'static [AnyPass<Node<Self>>];
+    const PASSES: &'static [AnyPass<Node<Self, V>>];
     #[doc(hidden)]
     const MASKS: &'static [NodeMask];
 
     #[doc(hidden)]
-    fn update<T: TreeView<Node<Self>> + Sync + Send>(
+    fn update<T: TreeView<Node<Self, V>> + Sync + Send>(
         dirty: DirtyNodeStates,
         tree: &mut T,
         ctx: SendAnyMap,
@@ -227,7 +227,7 @@ pub trait State: Default + Clone + 'static {
     }
 
     #[doc(hidden)]
-    fn update_single_threaded<T: TreeView<Node<Self>>>(
+    fn update_single_threaded<T: TreeView<Node<Self, V>>>(
         dirty: DirtyNodeStates,
         tree: &mut T,
         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 std::fmt::Debug;
 
@@ -67,7 +73,11 @@ impl PersistantElementIter {
 
     /// remove stale element refreneces
     /// 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 ids_removed: Vec<_> = mutations
             .edits
@@ -124,7 +134,7 @@ impl PersistantElementIter {
     }
 
     /// 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() {
             let id = NodeId(0);
             let new = (id, NodePosition::AtNode);
@@ -160,12 +170,12 @@ impl PersistantElementIter {
     }
 
     /// 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
-        fn push_back<S: State>(
+        fn push_back<S: State<V>, V: FromAnyValue>(
             stack: &mut smallvec::SmallVec<[(RealNodeId, NodePosition); 5]>,
             new_node: RealNodeId,
-            rdom: &RealDom<S>,
+            rdom: &RealDom<S, V>,
         ) -> RealNodeId {
             match &rdom[new_node].node_data.node_type {
                 NodeType::Element { .. } => {
@@ -228,7 +238,7 @@ impl PersistantElementIter {
 
 #[derive(Default, Clone, Debug)]
 struct Empty {}
-impl State for Empty {
+impl State<()> for Empty {
     const PASSES: &'static [crate::AnyPass<crate::node::Node<Self, ()>>] = &[];
 
     const MASKS: &'static [crate::NodeMask] = &[];