Evan Almloff %!s(int64=3) %!d(string=hai) anos
pai
achega
92f48169e9

+ 2 - 1
Cargo.toml

@@ -29,6 +29,7 @@ dioxus-tui = { path = "./packages/tui", version = "^0.2.0", optional = true }
 dioxus-liveview = { path = "./packages/liveview", optional = true }
 
 dioxus-native-core = { path = "./packages/native-core", optional = true }
+dioxus-native-core-macro = { path = "./packages/native-core-macro", optional = true }
 
 # dioxus-mobile = { path = "./packages/mobile", version = "^0.2.0", optional = true }
 # dioxus-rsx = { path = "./packages/rsx", optional = true }
@@ -47,7 +48,7 @@ ayatana = ["dioxus-desktop/ayatana"]
 router = ["dioxus-router"]
 tui = ["dioxus-tui"]
 liveview = ["dioxus-liveview"]
-native-core = ["dioxus-native-core"]
+native-core = ["dioxus-native-core", "dioxus-native-core-macro"]
 
 
 [workspace]

+ 12 - 0
packages/native-core-macro/Cargo.toml

@@ -0,0 +1,12 @@
+[package]
+name = "dioxus-native-core-macro"
+version = "0.2.0"
+edition = "2021"
+
+[lib]
+proc-macro = true
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+[dependencies]
+syn = { version = "1.0.11", features = ["extra-traits"] }
+quote = "1.0"

+ 285 - 0
packages/native-core-macro/src/lib.rs

@@ -0,0 +1,285 @@
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+use quote::{quote, ToTokens};
+use syn::{
+    self,
+    parse::{Parse, ParseStream},
+    Field, Ident, Token, Type,
+};
+
+#[derive(PartialEq)]
+enum DepKind {
+    NodeDepState,
+    ChildDepState,
+    ParentDepState,
+}
+
+// macro that streams data from the State for any attributes that end with _
+#[proc_macro_derive(State, attributes(node_dep_state, child_dep_state, parent_dep_state))]
+pub fn state_macro_derive(input: TokenStream) -> TokenStream {
+    let ast = syn::parse(input).unwrap();
+    impl_derive_macro(&ast)
+}
+
+fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
+    let type_name = &ast.ident;
+    let fields: Vec<_> = match &ast.data {
+        syn::Data::Struct(data) => match &data.fields {
+            syn::Fields::Named(e) => &e.named,
+            syn::Fields::Unnamed(_) => todo!("unnamed fields"),
+            syn::Fields::Unit => todo!("unit fields"),
+        }
+        .iter()
+        .collect(),
+        _ => unimplemented!(),
+    };
+    let strct = Struct::parse(&fields);
+    let state_strct = StateStruct::parse(&fields, &strct);
+    let node_dep_state_fields = quote::__private::TokenStream::from_iter(
+        state_strct
+            .state_members
+            .iter()
+            .filter(|f| f.dep_kind == DepKind::NodeDepState)
+            .map(|f| {
+                let ty_id = &f.type_id();
+                let reduce = &f.reduce_self();
+                quote! {
+                    else if ty == #ty_id {
+                        #reduce
+                    }
+                }
+            }),
+    );
+    let child_dep_state_fields = quote::__private::TokenStream::from_iter(
+        state_strct
+            .state_members
+            .iter()
+            .filter(|f| f.dep_kind == DepKind::ChildDepState)
+            .map(|f| {
+                let ty_id = &f.type_id();
+                let reduce = &f.reduce_self();
+                quote! {
+                    else if ty == #ty_id {
+                        #reduce
+                    }
+                }
+            }),
+    );
+    let parent_dep_state_fields = quote::__private::TokenStream::from_iter(
+        state_strct
+            .state_members
+            .iter()
+            .filter(|f| f.dep_kind == DepKind::ParentDepState)
+            .map(|f| {
+                let ty_id = &f.type_id();
+                let reduce = &f.reduce_self();
+                quote! {
+                    else if ty == #ty_id {
+                        #reduce
+                    }
+                }
+            }),
+    );
+
+    let node_types = state_strct
+        .state_members
+        .iter()
+        .filter(|f| f.dep_kind == DepKind::NodeDepState)
+        .map(|f| f.type_id());
+    let child_types = state_strct
+        .state_members
+        .iter()
+        .filter(|f| f.dep_kind == DepKind::ChildDepState)
+        .map(|f| f.type_id());
+    let parent_types = state_strct
+        .state_members
+        .iter()
+        .filter(|f| f.dep_kind == DepKind::ParentDepState)
+        .map(|f| f.type_id());
+
+    let type_name_str = type_name.to_string();
+
+    let gen = quote! {
+        impl State for #type_name{
+            fn update_node_dep_state(&mut self, ty: std::any::TypeId, node: dioxus_native_core::real_dom_new_api::NodeRef, ctx: &anymap::AnyMap){
+                use dioxus_native_core::real_dom_new_api::NodeDepState;
+                if false {}
+                #node_dep_state_fields
+                else{
+                    panic!("{:?} not in {}", ty, #type_name_str);
+                }
+            }
+
+            fn update_parent_dep_state(&mut self, ty: std::any::TypeId, node: dioxus_native_core::real_dom_new_api::NodeRef, parent: &Self, ctx: &anymap::AnyMap){
+                use dioxus_native_core::real_dom_new_api::ParentDepState;
+                if false {}
+                #parent_dep_state_fields
+                else{
+                    panic!("{:?} not in {}", ty, #type_name_str);
+                }
+            }
+
+            fn update_child_dep_state(&mut self, ty: std::any::TypeId, node: dioxus_native_core::real_dom_new_api::NodeRef, children: Vec<&Self>, ctx: &anymap::AnyMap){
+                use dioxus_native_core::real_dom_new_api::ChildDepState;
+                if false {}
+                #child_dep_state_fields
+                else{
+                    panic!("{:?} not in {}", ty, #type_name_str);
+                }
+            }
+
+            fn child_dep_types(&self) -> Vec<std::any::TypeId>{
+                // todo: order should depend on order of dependencies
+                vec![
+                    #(#child_types,)*
+                ]
+            }
+
+            fn parent_dep_types(&self) -> Vec<std::any::TypeId>{
+                // todo: order should depend on order of dependencies
+                vec![
+                    #(#parent_types,)*
+                ]
+            }
+
+            fn node_dep_types(&self) -> Vec<std::any::TypeId>{
+                vec![
+                    #(#node_types,)*
+                ]
+            }
+        }
+    };
+    gen.into()
+}
+
+struct Struct {
+    members: Vec<Member>,
+}
+
+impl Struct {
+    fn parse(fields: &[&Field]) -> Self {
+        let members = fields.iter().filter_map(|f| Member::parse(f)).collect();
+        Self { members }
+    }
+}
+
+struct StateStruct<'a> {
+    state_members: Vec<StateMember<'a>>,
+}
+
+impl<'a> StateStruct<'a> {
+    fn parse(fields: &[&'a Field], strct: &'a Struct) -> Self {
+        let state_members = strct
+            .members
+            .iter()
+            .zip(fields.iter())
+            .filter_map(|(m, f)| StateMember::parse(f, m, &strct))
+            .collect();
+        Self { state_members }
+    }
+}
+
+struct DepTypes {
+    ctx_ty: Option<Type>,
+    dep_ty: Option<Type>,
+}
+
+impl Parse for DepTypes {
+    fn parse(input: ParseStream) -> Result<DepTypes, syn::Error> {
+        let ctx_ty = input.parse::<Type>().ok();
+        let comma = input.parse::<Token![,]>().ok();
+        let dep_ty = input.parse::<Type>().ok();
+        let dep_ty = comma.and(dep_ty);
+        Ok(DepTypes { ctx_ty, dep_ty })
+    }
+}
+
+struct Member {
+    ty: Type,
+    ident: Ident,
+}
+
+impl Member {
+    fn parse(field: &Field) -> Option<Self> {
+        Some(Self {
+            ty: field.ty.clone(),
+            ident: field.ident.as_ref()?.clone(),
+        })
+    }
+}
+
+struct StateMember<'a> {
+    mem: &'a Member,
+    dep_kind: DepKind,
+    dep_mem: Option<&'a Member>,
+    ctx_ty: Option<Type>,
+}
+
+impl<'a> StateMember<'a> {
+    fn parse(field: &Field, mem: &'a Member, parent: &'a Struct) -> Option<StateMember<'a>> {
+        field.attrs.iter().find_map(|a| {
+            let dep_kind = a
+                .path
+                .get_ident()
+                .map(|i| match i.to_string().as_str() {
+                    "node_dep_state" => Some(DepKind::NodeDepState),
+                    "child_dep_state" => Some(DepKind::ChildDepState),
+                    "parent_dep_state" => Some(DepKind::ParentDepState),
+                    _ => None,
+                })
+                .flatten()?;
+            let deps: DepTypes = a.parse_args().ok()?;
+
+            Some(Self {
+                mem,
+                dep_kind,
+                dep_mem: deps
+                    .dep_ty
+                    .map(|ty| parent.members.iter().find(|m| m.ty == ty))
+                    .flatten(),
+                ctx_ty: deps.ctx_ty,
+            })
+        })
+    }
+
+    fn reduce_self(&self) -> quote::__private::TokenStream {
+        let ident = &self.mem.ident;
+        let get_ctx = if let Some(ctx_ty) = &self.ctx_ty {
+            let msg = ctx_ty.to_token_stream().to_string() + " not found in context";
+            quote! {ctx.get().expect(#msg)}
+        } else {
+            quote! {&()}
+        };
+        if let Some(dep_ident) = &self.dep_mem.map(|m| &m.ident) {
+            match self.dep_kind {
+                DepKind::NodeDepState => {
+                    quote!(self.#ident.reduce(node, #get_ctx);)
+                }
+                DepKind::ChildDepState => {
+                    quote!(self.#ident.reduce(node, children.iter().map(|s| &s.#dep_ident).collect(), #get_ctx);)
+                }
+                DepKind::ParentDepState => {
+                    quote!(self.#ident.reduce(node, &parent.#dep_ident, #get_ctx);)
+                }
+            }
+        } else {
+            match self.dep_kind {
+                DepKind::NodeDepState => {
+                    quote!(self.#ident.reduce(node, #get_ctx);)
+                }
+                DepKind::ChildDepState => {
+                    quote!(self.#ident.reduce(node, &(), #get_ctx);)
+                }
+                DepKind::ParentDepState => {
+                    quote!(self.#ident.reduce(node, &(), #get_ctx);)
+                }
+            }
+        }
+    }
+
+    fn type_id(&self) -> quote::__private::TokenStream {
+        let ty = &self.mem.ty;
+        quote!(std::any::TypeId::of::<#ty>())
+    }
+}

+ 2 - 0
packages/native-core/Cargo.toml

@@ -10,10 +10,12 @@ homepage = "https://dioxuslabs.com"
 dioxus-core = { path = "../core", version = "^0.2.0" }
 dioxus-html = { path = "../html", version = "^0.2.0" }
 dioxus-core-macro = { path = "../core-macro", version = "^0.2.0" }
+dioxus-native-core-macro = { path = "../native-core-macro", version = "^0.2.0" }
 
 stretch2 = { git = "https://github.com/DioxusLabs/stretch" }
 smallvec = "1.6"
 fxhash = "0.2"
+anymap = "0.12.1"
 
 [dev-dependencies]
 rand = "0.8.5"

+ 1 - 0
packages/native-core/src/lib.rs

@@ -1,2 +1,3 @@
 pub mod layout_attributes;
 pub mod real_dom;
+pub mod real_dom_new_api;

+ 58 - 0
packages/native-core/src/real_dom_new_api.rs

@@ -0,0 +1,58 @@
+use std::any::TypeId;
+
+use anymap::AnyMap;
+use dioxus_core::{Attribute, VElement};
+
+#[repr(transparent)]
+pub struct NodeRef<'a>(&'a VElement<'a>);
+impl<'a> NodeRef<'a> {
+    pub fn new(velement: &'a VElement<'a>) -> Self {
+        Self(velement)
+    }
+
+    pub fn tag(&self) -> &'a str {
+        self.0.tag
+    }
+
+    pub fn namespace(&self) -> Option<&'a str> {
+        self.0.namespace
+    }
+
+    pub fn attributes(&self) -> &'a [Attribute<'a>] {
+        self.0.attributes
+    }
+}
+
+pub trait ChildDepState: PartialEq {
+    type Ctx;
+    type DepState: ChildDepState;
+    fn reduce(&mut self, node: NodeRef, children: Vec<&Self::DepState>, ctx: &Self::Ctx);
+}
+
+pub trait ParentDepState: PartialEq {
+    type Ctx;
+    type DepState: ParentDepState;
+    fn reduce(&mut self, node: NodeRef, parent: &Self::DepState, ctx: &Self::Ctx);
+}
+
+pub trait NodeDepState: PartialEq {
+    type Ctx;
+    fn reduce(&mut self, node: NodeRef, ctx: &Self::Ctx);
+}
+
+pub trait State {
+    fn update_node_dep_state(&mut self, ty: TypeId, node: NodeRef, ctx: &AnyMap);
+    fn child_dep_types(&self) -> Vec<TypeId>;
+
+    fn update_parent_dep_state(&mut self, ty: TypeId, node: NodeRef, parent: &Self, ctx: &AnyMap);
+    fn parent_dep_types(&self) -> Vec<TypeId>;
+
+    fn update_child_dep_state(
+        &mut self,
+        ty: TypeId,
+        node: NodeRef,
+        children: Vec<&Self>,
+        ctx: &AnyMap,
+    );
+    fn node_dep_types(&self) -> Vec<TypeId>;
+}

+ 62 - 0
packages/native-core/tests/parse.rs

@@ -0,0 +1,62 @@
+use dioxus_native_core::real_dom_new_api::*;
+use dioxus_native_core_macro::*;
+
+#[derive(State)]
+struct Z {
+    // depends on just attributes and no context
+    #[node_dep_state()]
+    x: A,
+    // depends on attributes, the B component of children and i32 context
+    #[child_dep_state(i32, B)]
+    y: B,
+    // depends on attributes, the C component of it's parent and a u8 context
+    #[parent_dep_state(u8, C)]
+    z: C,
+}
+
+// struct Z {
+//     x: A,
+//     y: B,
+//     z: C,
+// }
+
+use dioxus_native_core::real_dom_new_api::NodeDepState;
+
+#[derive(PartialEq)]
+struct A;
+impl NodeDepState for A {
+    type Ctx = ();
+    fn reduce(&mut self, _: NodeRef, _: &()) {
+        todo!()
+    }
+}
+
+#[derive(PartialEq)]
+struct B;
+impl ChildDepState for B {
+    type Ctx = i32;
+    type DepState = Self;
+    fn reduce(
+        &mut self,
+        _: dioxus_native_core::real_dom_new_api::NodeRef,
+        _: Vec<&Self::DepState>,
+        _: &i32,
+    ) {
+        todo!()
+    }
+}
+
+#[derive(PartialEq)]
+struct C;
+impl ParentDepState for C {
+    type Ctx = u8;
+    type DepState = Self;
+    fn reduce(
+        &mut self,
+        _: dioxus_native_core::real_dom_new_api::NodeRef,
+        _: &Self::DepState,
+        _: &u8,
+    ) {
+        todo!()
+    }
+}