remove native-core-macro

This commit is contained in:
Evan Almloff committed 2023-01-22 16:41:15 -06:00
1 parent fb0c6c1b62
commit 4738352955
13 files changed
+118 -1081

No files matched your search

-1
View File
@@ -17,7 +17,6 @@ members = [
"packages/rsx",
"packages/tui",
"packages/native-core",
"packages/native-core-macro",
"packages/rsx-rosetta",
"packages/signals",
"docs/guide",
-2
View File
@@ -1,2 +0,0 @@
/target
Cargo.lock
-26
View File
@@ -1,26 +0,0 @@
[package]
name = "dioxus-native-core-macro"
version = "0.2.0"
edition = "2021"
description = "Build natively rendered apps with Dioxus"
license = "MIT/Apache-2.0"
repository = "https://github.com/DioxusLabs/dioxus/"
homepage = "https://dioxuslabs.com"
documentation = "https://dioxuslabs.com"
keywords = ["dom", "ui", "gui", "react"]
[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"
dioxus-native-core = { path = "../native-core", version = "^0.2.0" }
dioxus = { path = "../dioxus" }
[dev-dependencies]
smallvec = "1.6"
rustc-hash = "1.1.0"
anymap = "0.12.1"
-43
View File
@@ -1,43 +0,0 @@
# Dioxus Native Core Macro
[![Crates.io][crates-badge]][crates-url]
[![MIT licensed][mit-badge]][mit-url]
[![Build Status][actions-badge]][actions-url]
[![Discord chat][discord-badge]][discord-url]
[crates-badge]: https://img.shields.io/crates/v/dioxus-native-core-macro.svg
[crates-url]: https://crates.io/crates/dioxus-native-core-macro
[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
[mit-url]: https://github.com/dioxuslabs/dioxus/blob/master/LICENSE
[actions-badge]: https://github.com/dioxuslabs/dioxus/actions/workflows/main.yml/badge.svg
[actions-url]: https://github.com/dioxuslabs/dioxus/actions?query=workflow%3ACI+branch%3Amaster
[discord-badge]: https://img.shields.io/discord/899851952891002890.svg?logo=discord&style=flat-square
[discord-url]: https://discord.gg/XgGxMSkvUM
[Website](https://dioxuslabs.com) |
[Guides](https://dioxuslabs.com/guide/) |
[API Docs](https://docs.rs/dioxus-native-core-macro/latest/dioxus_native_core_macro) |
[Chat](https://discord.gg/XgGxMSkvUM)
## Overview
`dioxus-native-core-macro` provides a handful of macros used by native-core for native renderers like TUI, Blitz, and Freya to derive their state.
## Contributing
- Report issues on our [issue tracker](https://github.com/dioxuslabs/dioxus/issues).
- Join the discord and ask questions!
## License
This project is licensed under the [MIT license].
[mit license]: https://github.com/DioxusLabs/dioxus/blob/master/LICENSE-MIT
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in Dioxus by you shall be licensed as MIT without any additional
terms or conditions.
-134
View File
@@ -1,134 +0,0 @@
extern crate proc_macro;
mod sorted_slice;
use proc_macro::TokenStream;
use quote::quote;
use sorted_slice::StrSlice;
use syn::{self, parse_macro_input};
/// Sorts a slice of string literals at compile time.
#[proc_macro]
pub fn sorted_str_slice(input: TokenStream) -> TokenStream {
let slice: StrSlice = parse_macro_input!(input as StrSlice);
let strings = slice.map.values();
quote!([#(#strings, )*]).into()
}
/// Derive's the state from any members that implement the Pass trait
#[proc_macro_derive(State, attributes(skip, skip_clone))]
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 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 {
syn::Fields::Named(e) => &e.named,
syn::Fields::Unnamed(_) => todo!("unnamed fields"),
syn::Fields::Unit => todo!("unit structs"),
}
.iter()
.collect(),
_ => unimplemented!(),
};
let types = fields
.iter()
.filter(|field| !field.attrs.iter().any(|attr| attr.path.is_ident("skip")))
.map(|field| &field.ty);
let gen = quote! {
impl dioxus_native_core::State<#custom_type> for #type_name {
fn create_passes() -> Box<[dioxus_native_core::TypeErasedPass<Self>]> {
Box::new([
#(
<#types as dioxus_native_core::Pass>::to_type_erased()
),*
])
}
}
};
gen.into()
}
/// Derive's the state from any elements that have a node_dep_state, child_dep_state, parent_dep_state, or state attribute.
#[proc_macro_derive(AnyMapLike)]
pub fn anymap_like_macro_derive(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
impl_anymap_like_derive_macro(&ast)
}
fn impl_anymap_like_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 structs"),
}
.iter()
.collect(),
_ => unimplemented!(),
};
let names: Vec<_> = fields
.iter()
.map(|field| field.ident.as_ref().unwrap())
.collect();
let types: Vec<_> = fields.iter().map(|field| &field.ty).collect();
let gen = quote! {
impl dioxus_native_core::AnyMapLike for #type_name {
fn get<T: std::any::Any>(&self) -> Option<&T> {
#(
if std::any::TypeId::of::<T>() == std::any::TypeId::of::<#types>() {
return unsafe { Some(&*(&self.#names as *const _ as *const T)) }
}
)*
None
}
fn get_mut<T: std::any::Any>(&mut self) -> Option<&mut T> {
#(
if std::any::TypeId::of::<T>() == std::any::TypeId::of::<#types>() {
return unsafe { Some(&mut *(&mut self.#names as *mut _ as *mut T)) }
}
)*
None
}
}
};
gen.into()
}
@@ -1,29 +0,0 @@
extern crate proc_macro;
use std::collections::BTreeMap;
use syn::{
self, bracketed,
parse::{Parse, ParseStream, Result},
LitStr, Token,
};
pub struct StrSlice {
pub map: BTreeMap<String, LitStr>,
}
impl Parse for StrSlice {
fn parse(input: ParseStream) -> Result<Self> {
let content;
bracketed!(content in input);
let mut map = BTreeMap::new();
while let Ok(s) = content.parse::<LitStr>() {
map.insert(s.value(), s);
#[allow(unused_must_use)]
{
content.parse::<Token![,]>();
}
}
Ok(StrSlice { map })
}
}
@@ -1,244 +0,0 @@
use dioxus::prelude::*;
use dioxus_native_core::node_ref::*;
use dioxus_native_core::real_dom::*;
use dioxus_native_core::state::{
ChildDepState, ElementBorrowable, NodeDepState, ParentDepState, State,
};
use dioxus_native_core::tree::*;
use dioxus_native_core::SendAnyMap;
use dioxus_native_core_macro::State;
macro_rules! dep {
( child( $name:ty, $dep:ty ) ) => {
impl ChildDepState for $name {
type Ctx = ();
type DepState = $dep;
const NODE_MASK: NodeMask = NodeMask::ALL;
fn reduce<'a>(
&mut self,
_: NodeView,
_: impl Iterator<Item = <Self::DepState as ElementBorrowable>::ElementBorrowed<'a>>,
_: &Self::Ctx,
) -> bool
where
Self::DepState: 'a,
{
self.0 += 1;
true
}
}
};
( parent( $name:ty, $dep:ty ) ) => {
impl ParentDepState for $name {
type Ctx = ();
type DepState = $dep;
const NODE_MASK: NodeMask = NodeMask::ALL;
fn reduce(
&mut self,
_: NodeView,
_: Option<<Self::DepState as ElementBorrowable>::ElementBorrowed<'_>>,
_: &Self::Ctx,
) -> bool {
self.0 += 1;
true
}
}
};
( node( $name:ty, $dep:ty ) ) => {
impl NodeDepState for $name {
type Ctx = ();
type DepState = $dep;
const NODE_MASK: NodeMask = NodeMask::ALL;
fn reduce(
&mut self,
_: NodeView,
_: <Self::DepState as ElementBorrowable>::ElementBorrowed<'_>,
_: &Self::Ctx,
) -> bool {
self.0 += 1;
true
}
}
};
}
macro_rules! test_state{
( $s:ty, child: ( $( $child:ident ),* ), node: ( $( $node:ident ),* ), parent: ( $( $parent:ident ),* ) ) => {
#[test]
fn state_reduce_initally_called_minimally() {
#[allow(non_snake_case)]
fn Base(cx: Scope) -> Element {
render!{
div {
div{
div{
p{}
}
p{
"hello"
}
div{
h1{}
}
p{
"world"
}
}
}
}
}
let mut vdom = VirtualDom::new(Base);
let mutations = vdom.rebuild();
let mut dom: RealDom<$s> = RealDom::new();
let (nodes_updated, _) = dom.apply_mutations(mutations);
let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new());
dom.traverse_depth_first(|n| {
$(
assert_eq!(n.state.$child.0, 1);
)*
$(
assert_eq!(n.state.$node.0, 1);
)*
$(
assert_eq!(n.state.$parent.0, 1);
)*
});
}
}
}
mod node_depends_on_child_and_parent {
use super::*;
#[derive(Debug, Clone, Default, PartialEq)]
struct Node(i32);
dep!(node(Node, (Child, Parent)));
#[derive(Debug, Clone, Default, PartialEq)]
struct Child(i32);
dep!(child(Child, (Child,)));
#[derive(Debug, Clone, Default, PartialEq)]
struct Parent(i32);
dep!(parent(Parent, (Parent,)));
#[derive(Debug, Clone, Default, State)]
struct StateTester {
#[node_dep_state((child, parent))]
node: Node,
#[child_dep_state(child)]
child: Child,
#[parent_dep_state(parent)]
parent: Parent,
}
test_state!(StateTester, child: (child), node: (node), parent: (parent));
}
mod child_depends_on_node_that_depends_on_parent {
use super::*;
#[derive(Debug, Clone, Default, PartialEq)]
struct Node(i32);
dep!(node(Node, (Parent,)));
#[derive(Debug, Clone, Default, PartialEq)]
struct Child(i32);
dep!(child(Child, (Node,)));
#[derive(Debug, Clone, Default, PartialEq)]
struct Parent(i32);
dep!(parent(Parent, (Parent,)));
#[derive(Debug, Clone, Default, State)]
struct StateTester {
#[node_dep_state(parent)]
node: Node,
#[child_dep_state(node)]
child: Child,
#[parent_dep_state(parent)]
parent: Parent,
}
test_state!(StateTester, child: (child), node: (node), parent: (parent));
}
mod parent_depends_on_node_that_depends_on_child {
use super::*;
#[derive(Debug, Clone, Default, PartialEq)]
struct Node(i32);
dep!(node(Node, (Child,)));
#[derive(Debug, Clone, Default, PartialEq)]
struct Child(i32);
dep!(child(Child, (Child,)));
#[derive(Debug, Clone, Default, PartialEq)]
struct Parent(i32);
dep!(parent(Parent, (Node,)));
#[derive(Debug, Clone, Default, State)]
struct StateTester {
#[node_dep_state(child)]
node: Node,
#[child_dep_state(child)]
child: Child,
#[parent_dep_state(node)]
parent: Parent,
}
test_state!(StateTester, child: (child), node: (node), parent: (parent));
}
mod node_depends_on_other_node_state {
use super::*;
#[derive(Debug, Clone, Default, PartialEq)]
struct Node1(i32);
dep!(node(Node1, (Node2,)));
#[derive(Debug, Clone, Default, PartialEq)]
struct Node2(i32);
dep!(node(Node2, ()));
#[derive(Debug, Clone, Default, State)]
struct StateTester {
#[node_dep_state((node2))]
node1: Node1,
#[node_dep_state()]
node2: Node2,
}
test_state!(StateTester, child: (), node: (node1, node2), parent: ());
}
mod node_child_and_parent_state_depends_on_self {
use super::*;
#[derive(Debug, Clone, Default, PartialEq)]
struct Node(i32);
dep!(node(Node, ()));
#[derive(Debug, Clone, Default, PartialEq)]
struct Child(i32);
dep!(child(Child, (Child,)));
#[derive(Debug, Clone, Default, PartialEq)]
struct Parent(i32);
dep!(parent(Parent, (Parent,)));
#[derive(Debug, Clone, Default, State)]
struct StateTester {
#[node_dep_state()]
node: Node,
#[child_dep_state(child)]
child: Child,
#[parent_dep_state(parent)]
parent: Parent,
}
test_state!(StateTester, child: (child), node: (node), parent: (parent));
}
@@ -1,421 +0,0 @@
use dioxus::prelude::*;
use dioxus_native_core::real_dom::*;
use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State};
use dioxus_native_core::tree::TreeView;
use dioxus_native_core::{node_ref::*, NodeId, SendAnyMap};
use dioxus_native_core_macro::State;
#[derive(Debug, Clone, Default, State)]
struct CallCounterState {
#[child_dep_state(child_counter)]
child_counter: ChildDepCallCounter,
#[parent_dep_state(parent_counter)]
parent_counter: ParentDepCallCounter,
#[node_dep_state()]
node_counter: NodeDepCallCounter,
}
#[derive(Debug, Clone, Default)]
struct ChildDepCallCounter(u32);
impl ChildDepState for ChildDepCallCounter {
type Ctx = ();
type DepState = (Self,);
const NODE_MASK: NodeMask = NodeMask::ALL;
fn reduce<'a>(
&mut self,
_: NodeView,
_: impl Iterator<Item = (&'a Self,)>,
_: &Self::Ctx,
) -> bool
where
Self::DepState: 'a,
{
self.0 += 1;
true
}
}
#[derive(Debug, Clone, Default)]
struct ParentDepCallCounter(u32);
impl ParentDepState for ParentDepCallCounter {
type Ctx = ();
type DepState = (Self,);
const NODE_MASK: NodeMask = NodeMask::ALL;
fn reduce(&mut self, _node: NodeView, _parent: Option<(&Self,)>, _ctx: &Self::Ctx) -> bool {
self.0 += 1;
true
}
}
#[derive(Debug, Clone, Default)]
struct NodeDepCallCounter(u32);
impl NodeDepState for NodeDepCallCounter {
type Ctx = ();
type DepState = ();
const NODE_MASK: NodeMask = NodeMask::ALL;
fn reduce(&mut self, _node: NodeView, _sibling: (), _ctx: &Self::Ctx) -> bool {
self.0 += 1;
true
}
}
#[allow(clippy::vec_box)]
#[derive(Debug, Clone, PartialEq, Default)]
struct BubbledUpStateTester(Option<String>, Vec<Box<BubbledUpStateTester>>);
impl ChildDepState for BubbledUpStateTester {
type Ctx = u32;
type DepState = (Self,);
const NODE_MASK: NodeMask = NodeMask::new().with_tag();
fn reduce<'a>(
&mut self,
node: NodeView,
children: impl Iterator<Item = (&'a Self,)>,
ctx: &Self::Ctx,
) -> bool
where
Self::DepState: 'a,
{
assert_eq!(*ctx, 42);
*self = BubbledUpStateTester(
node.tag().map(|s| s.to_string()),
children
.into_iter()
.map(|(c,)| Box::new(c.clone()))
.collect(),
);
true
}
}
#[derive(Debug, Clone, PartialEq, Default)]
struct PushedDownStateTester(Option<String>, Option<Box<PushedDownStateTester>>);
impl ParentDepState for PushedDownStateTester {
type Ctx = u32;
type DepState = (Self,);
const NODE_MASK: NodeMask = NodeMask::new().with_tag();
fn reduce(&mut self, node: NodeView, parent: Option<(&Self,)>, ctx: &Self::Ctx) -> bool {
assert_eq!(*ctx, 42);
*self = PushedDownStateTester(
node.tag().map(|s| s.to_string()),
parent.map(|(c,)| Box::new(c.clone())),
);
true
}
}
#[derive(Debug, Clone, PartialEq, Default)]
struct NodeStateTester(Option<String>, Vec<(String, String)>);
impl NodeDepState for NodeStateTester {
type Ctx = u32;
type DepState = ();
const NODE_MASK: NodeMask = NodeMask::new_with_attrs(AttributeMask::All).with_tag();
fn reduce(&mut self, node: NodeView, _sibling: (), ctx: &Self::Ctx) -> bool {
assert_eq!(*ctx, 42);
*self = NodeStateTester(
node.tag().map(|s| s.to_string()),
node.attributes()
.map(|iter| {
iter.map(|a| {
(
a.attribute.name.to_string(),
a.value.as_text().unwrap().to_string(),
)
})
.collect()
})
.unwrap_or_default(),
);
true
}
}
#[derive(State, Clone, Default, Debug)]
struct StateTester {
#[child_dep_state(bubbled, u32)]
bubbled: BubbledUpStateTester,
#[parent_dep_state(pushed, u32)]
pushed: PushedDownStateTester,
#[node_dep_state(NONE, u32)]
node: NodeStateTester,
}
#[test]
fn state_initial() {
#[allow(non_snake_case)]
fn Base(cx: Scope) -> Element {
render! {
div {
p{
color: "red"
}
h1{}
}
}
}
let mut vdom = VirtualDom::new(Base);
let mutations = vdom.rebuild();
let mut dom: RealDom<StateTester> = RealDom::new();
let (nodes_updated, _) = dom.apply_mutations(mutations);
let mut ctx = SendAnyMap::new();
ctx.insert(42u32);
let _to_rerender = dom.update_state(nodes_updated, ctx);
let root_div_id = dom.children_ids(NodeId(0)).unwrap()[0];
let root_div = &dom.get(root_div_id).unwrap();
assert_eq!(root_div.state.bubbled.0, Some("div".to_string()));
assert_eq!(
root_div.state.bubbled.1,
vec![
Box::new(BubbledUpStateTester(Some("p".to_string()), Vec::new())),
Box::new(BubbledUpStateTester(Some("h1".to_string()), Vec::new()))
]
);
assert_eq!(root_div.state.pushed.0, Some("div".to_string()));
assert_eq!(
root_div.state.pushed.1,
Some(Box::new(PushedDownStateTester(
Some("Root".to_string()),
None
)))
);
assert_eq!(root_div.state.node.0, Some("div".to_string()));
assert_eq!(root_div.state.node.1, vec![]);
let child_p_id = dom.children_ids(root_div_id).unwrap()[0];
let child_p = &dom[child_p_id];
assert_eq!(child_p.state.bubbled.0, Some("p".to_string()));
assert_eq!(child_p.state.bubbled.1, Vec::new());
assert_eq!(child_p.state.pushed.0, Some("p".to_string()));
assert_eq!(
child_p.state.pushed.1,
Some(Box::new(PushedDownStateTester(
Some("div".to_string()),
Some(Box::new(PushedDownStateTester(
Some("Root".to_string()),
None
)))
)))
);
assert_eq!(child_p.state.node.0, Some("p".to_string()));
assert_eq!(
child_p.state.node.1,
vec![("color".to_string(), "red".to_string())]
);
let child_h1_id = dom.children_ids(root_div_id).unwrap()[1];
let child_h1 = &dom[child_h1_id];
assert_eq!(child_h1.state.bubbled.0, Some("h1".to_string()));
assert_eq!(child_h1.state.bubbled.1, Vec::new());
assert_eq!(child_h1.state.pushed.0, Some("h1".to_string()));
assert_eq!(
child_h1.state.pushed.1,
Some(Box::new(PushedDownStateTester(
Some("div".to_string()),
Some(Box::new(PushedDownStateTester(
Some("Root".to_string()),
None
)))
)))
);
assert_eq!(child_h1.state.node.0, Some("h1".to_string()));
assert_eq!(child_h1.state.node.1, vec![]);
}
#[test]
fn state_reduce_parent_called_minimally_on_update() {
#[allow(non_snake_case)]
fn Base(cx: Scope) -> Element {
let width = if cx.generation() == 0 { "100%" } else { "99%" };
cx.render(rsx! {
div {
width: "{width}",
div{
div{
p{}
}
p{
"hello"
}
div{
h1{}
}
p{
"world"
}
}
}
})
}
let mut vdom = VirtualDom::new(Base);
let mut dom: RealDom<CallCounterState> = RealDom::new();
let (nodes_updated, _) = dom.apply_mutations(vdom.rebuild());
let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new());
vdom.mark_dirty(ScopeId(0));
let (nodes_updated, _) = dom.apply_mutations(vdom.render_immediate());
let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new());
let mut is_root = true;
dom.traverse_depth_first(|n| {
if is_root {
is_root = false;
assert_eq!(n.state.parent_counter.0, 1);
} else {
assert_eq!(n.state.parent_counter.0, 2);
}
});
}
#[test]
fn state_reduce_child_called_minimally_on_update() {
#[allow(non_snake_case)]
fn Base(cx: Scope) -> Element {
let width = if cx.generation() == 0 { "100%" } else { "99%" };
cx.render(rsx! {
// updated: 2
div {
// updated: 2
div{
// updated: 2
div{
// updated: 2
p{
width: "{width}",
}
}
// updated: 1
p{
// updated: 1
"hello"
}
// updated: 1
div{
// updated: 1
h1{}
}
// updated: 1
p{
// updated: 1
"world"
}
}
}
})
}
let mut vdom = VirtualDom::new(Base);
let mut dom: RealDom<CallCounterState> = RealDom::new();
let (nodes_updated, _) = dom.apply_mutations(vdom.rebuild());
let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new());
vdom.mark_dirty(ScopeId(0));
let (nodes_updated, _) = dom.apply_mutations(vdom.render_immediate());
let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new());
let mut traverse_count = 0;
dom.traverse_depth_first(|n| {
assert_eq!(n.state.child_counter.0, {
if traverse_count > 4 {
1
} else {
2
}
});
traverse_count += 1;
});
}
#[derive(Debug, Clone, Default, State)]
struct UnorderedDependanciesState {
#[node_dep_state(c)]
b: BDepCallCounter,
#[node_dep_state()]
c: CDepCallCounter,
#[node_dep_state(b)]
a: ADepCallCounter,
}
#[derive(Debug, Clone, Default, PartialEq)]
struct ADepCallCounter(usize, BDepCallCounter);
impl NodeDepState for ADepCallCounter {
type Ctx = ();
type DepState = (BDepCallCounter,);
const NODE_MASK: NodeMask = NodeMask::NONE;
fn reduce(
&mut self,
_node: NodeView,
(sibling,): (&BDepCallCounter,),
_ctx: &Self::Ctx,
) -> bool {
self.0 += 1;
self.1 = sibling.clone();
true
}
}
#[derive(Debug, Clone, Default, PartialEq)]
struct BDepCallCounter(usize, CDepCallCounter);
impl NodeDepState for BDepCallCounter {
type Ctx = ();
type DepState = (CDepCallCounter,);
const NODE_MASK: NodeMask = NodeMask::NONE;
fn reduce(
&mut self,
_node: NodeView,
(sibling,): (&CDepCallCounter,),
_ctx: &Self::Ctx,
) -> bool {
self.0 += 1;
self.1 = sibling.clone();
true
}
}
#[derive(Debug, Clone, Default, PartialEq)]
struct CDepCallCounter(usize);
impl NodeDepState for CDepCallCounter {
type Ctx = ();
type DepState = ();
const NODE_MASK: NodeMask = NodeMask::ALL;
fn reduce(&mut self, _node: NodeView, _sibling: (), _ctx: &Self::Ctx) -> bool {
self.0 += 1;
true
}
}
#[test]
fn dependancies_order_independant() {
#[allow(non_snake_case)]
fn Base(cx: Scope) -> Element {
render!(div {
width: "100%",
p{
"hello"
}
})
}
let mut vdom = VirtualDom::new(Base);
let mut dom: RealDom<UnorderedDependanciesState> = RealDom::new();
let mutations = vdom.rebuild();
let (nodes_updated, _) = dom.apply_mutations(mutations);
let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new());
let c = CDepCallCounter(1);
let b = BDepCallCounter(1, c.clone());
let a = ADepCallCounter(1, b.clone());
dom.traverse_depth_first(|n| {
assert_eq!(&n.state.a, &a);
assert_eq!(&n.state.b, &b);
assert_eq!(&n.state.c, &c);
});
}
-1
View File
@@ -31,5 +31,4 @@ lightningcss = "1.0.0-alpha.39"
[dev-dependencies]
rand = "0.8.5"
dioxus = { path = "../dioxus", version = "^0.3.0" }
dioxus-native-core-macro = { path = "../native-core-macro" }
tokio = { version = "*", features = ["full"] }
+109 -171
View File
@@ -1,47 +1,11 @@
use std::cmp::Ordering;
use dioxus_core::ElementId;
use rustc_hash::FxHashSet;
use crate::{
node::{ElementNode, FromAnyValue, NodeData, NodeType, OwnedAttributeView},
NodeId,
};
/// Join two sorted iterators
pub(crate) fn union_ordered_iter<'a>(
s_iter: impl Iterator<Item = &'a str>,
o_iter: impl Iterator<Item = &'a str>,
new_len_guess: usize,
) -> Vec<String> {
let mut s_peekable = s_iter.peekable();
let mut o_peekable = o_iter.peekable();
let mut v = Vec::with_capacity(new_len_guess);
while let Some(s_i) = s_peekable.peek() {
while let Some(o_i) = o_peekable.peek() {
match o_i.cmp(s_i) {
Ordering::Greater => {
break;
}
Ordering::Less => {
v.push(o_peekable.next().unwrap().to_string());
}
Ordering::Equal => {
o_peekable.next();
break;
}
}
}
v.push(s_peekable.next().unwrap().to_string());
}
for o_i in o_peekable {
v.push(o_i.to_string());
}
for w in v.windows(2) {
debug_assert!(w[1] > w[0]);
}
v
}
/// A view into a [VNode] with limited access.
#[derive(Debug)]
pub struct NodeView<'a, V: FromAnyValue = ()> {
@@ -138,124 +102,50 @@ impl<'a, V: FromAnyValue> NodeView<'a, V> {
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum AttributeMask {
All,
/// A list of attribute names that are visible, this list must be sorted
Dynamic(Vec<String>),
/// A list of attribute names that are visible, this list must be sorted
Static(&'static [&'static str]),
Some(FxHashSet<Box<str>>),
}
impl AttributeMask {
/// A empty attribute mask
pub const NONE: Self = Self::Static(&[]);
fn contains_attribute(&self, attr: &str) -> bool {
match self {
AttributeMask::All => true,
AttributeMask::Dynamic(l) => l.binary_search_by_key(&attr, |s| s.as_str()).is_ok(),
AttributeMask::Static(l) => l.binary_search(&attr).is_ok(),
AttributeMask::Some(attrs) => attrs.contains(attr),
}
}
/// Create a new dynamic attribute mask with a single attribute
pub fn single(new: &str) -> Self {
Self::Dynamic(vec![new.to_string()])
}
/// Ensure the attribute list is sorted.
pub fn verify(&self) {
match &self {
AttributeMask::Static(attrs) => debug_assert!(
attrs.windows(2).all(|w| w[0] < w[1]),
"attritutes must be increasing"
),
AttributeMask::Dynamic(attrs) => debug_assert!(
attrs.windows(2).all(|w| w[0] < w[1]),
"attritutes must be increasing"
),
_ => (),
}
let mut set = FxHashSet::default();
set.insert(new.into());
Self::Some(set)
}
/// Combine two attribute masks
pub fn union(&self, other: &Self) -> Self {
let new = match (self, other) {
(AttributeMask::Dynamic(s), AttributeMask::Dynamic(o)) => {
AttributeMask::Dynamic(union_ordered_iter(
s.iter().map(|s| s.as_str()),
o.iter().map(|s| s.as_str()),
s.len() + o.len(),
))
match (self, other) {
(AttributeMask::Some(s), AttributeMask::Some(o)) => {
AttributeMask::Some(s.intersection(o).cloned().collect())
}
(AttributeMask::Static(s), AttributeMask::Dynamic(o)) => {
AttributeMask::Dynamic(union_ordered_iter(
s.iter().copied(),
o.iter().map(|s| s.as_str()),
s.len() + o.len(),
))
}
(AttributeMask::Dynamic(s), AttributeMask::Static(o)) => {
AttributeMask::Dynamic(union_ordered_iter(
s.iter().map(|s| s.as_str()),
o.iter().copied(),
s.len() + o.len(),
))
}
(AttributeMask::Static(s), AttributeMask::Static(o)) => AttributeMask::Dynamic(
union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()),
),
_ => AttributeMask::All,
};
new.verify();
new
}
}
/// Check if two attribute masks overlap
fn overlaps(&self, other: &Self) -> bool {
fn overlaps_iter<'a>(
self_iter: impl Iterator<Item = &'a str>,
mut other_iter: impl Iterator<Item = &'a str>,
) -> bool {
if let Some(mut other_attr) = other_iter.next() {
for self_attr in self_iter {
while other_attr < self_attr {
if let Some(attr) = other_iter.next() {
other_attr = attr;
} else {
return false;
}
}
if other_attr == self_attr {
return true;
}
}
}
false
}
match (self, other) {
(AttributeMask::All, AttributeMask::All) => true,
(AttributeMask::All, AttributeMask::Dynamic(v)) => !v.is_empty(),
(AttributeMask::All, AttributeMask::Static(s)) => !s.is_empty(),
(AttributeMask::Dynamic(v), AttributeMask::All) => !v.is_empty(),
(AttributeMask::Static(s), AttributeMask::All) => !s.is_empty(),
(AttributeMask::Dynamic(v1), AttributeMask::Dynamic(v2)) => {
overlaps_iter(v1.iter().map(|s| s.as_str()), v2.iter().map(|s| s.as_str()))
}
(AttributeMask::Dynamic(v), AttributeMask::Static(s)) => {
overlaps_iter(v.iter().map(|s| s.as_str()), s.iter().copied())
}
(AttributeMask::Static(s), AttributeMask::Dynamic(v)) => {
overlaps_iter(v.iter().map(|s| s.as_str()), s.iter().copied())
}
(AttributeMask::Static(s1), AttributeMask::Static(s2)) => {
overlaps_iter(s1.iter().copied(), s2.iter().copied())
(AttributeMask::All, AttributeMask::Some(attrs)) => !attrs.is_empty(),
(AttributeMask::Some(attrs), AttributeMask::All) => !attrs.is_empty(),
(AttributeMask::Some(attrs1), AttributeMask::Some(attrs2)) => {
!attrs1.is_disjoint(attrs2)
}
_ => true,
}
}
}
impl Default for AttributeMask {
fn default() -> Self {
AttributeMask::Static(&[])
AttributeMask::Some(FxHashSet::default())
}
}
@@ -270,33 +160,33 @@ pub struct NodeMask {
}
impl NodeMask {
/// A node mask with no parts visible.
pub const NONE: Self = Self::new();
/// A node mask with every part visible.
pub const ALL: Self = Self::new_with_attrs(AttributeMask::All)
.with_text()
.with_element()
.with_listeners();
pub fn from_node(node: &NodeType) -> Self {
match node {
NodeType::Text { .. } => Self::new().with_text(),
NodeType::Text { .. } => NodeMaskBuilder::new().with_text().build(),
NodeType::Element(ElementNode {
tag: _,
namespace: _,
attributes,
listeners,
}) => {
let mut mask = Self::new_with_attrs(AttributeMask::Dynamic(
attributes.keys().map(|key| key.name.clone()).collect(),
))
.with_element();
if !listeners.is_empty() {
mask = mask.with_listeners();
}
mask
}
NodeType::Placeholder => Self::new().with_element(),
}) => Self {
attritutes: AttributeMask::Some(
attributes
.keys()
.map(|key| key.name.as_str().into())
.collect(),
),
namespace: true,
tag: true,
text: false,
listeners: !listeners.is_empty(),
},
NodeType::Placeholder => Self {
attritutes: AttributeMask::default(),
tag: true,
namespace: true,
text: false,
listeners: false,
},
}
}
@@ -320,8 +210,65 @@ impl NodeMask {
}
}
/// Allow the mask to view the given attributes
pub fn add_attributes(&mut self, attributes: AttributeMask) {
self.attritutes = self.attritutes.union(&attributes);
}
/// Set the mask to view the tag
pub fn set_tag(&mut self) {
self.tag = true;
}
/// Set the mask to view the namespace
pub fn set_namespace(&mut self) {
self.namespace = true;
}
/// Set the mask to view the text
pub fn set_text(&mut self) {
self.text = true;
}
/// Set the mask to view the listeners
pub fn set_listeners(&mut self) {
self.listeners = true;
}
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum AttributeMaskBuilder {
All,
Some(&'static [&'static str]),
}
impl Default for AttributeMaskBuilder {
fn default() -> Self {
AttributeMaskBuilder::Some(&[])
}
}
/// A mask that limits what parts of a node a dependency can see.
#[derive(Default, PartialEq, Eq, Clone, Debug)]
pub struct NodeMaskBuilder {
attritutes: AttributeMaskBuilder,
tag: bool,
namespace: bool,
text: bool,
listeners: bool,
}
impl NodeMaskBuilder {
/// A node mask with no parts visible.
pub const NONE: Self = Self::new();
/// A node mask with every part visible.
pub const ALL: Self = Self::new_with_attrs(AttributeMaskBuilder::All)
.with_text()
.with_element()
.with_listeners();
/// Create a new node mask with the given attributes
pub const fn new_with_attrs(attritutes: AttributeMask) -> Self {
pub const fn new_with_attrs(attritutes: AttributeMaskBuilder) -> Self {
Self {
attritutes,
tag: false,
@@ -333,7 +280,7 @@ impl NodeMask {
/// Create a empty node mask
pub const fn new() -> Self {
Self::new_with_attrs(AttributeMask::NONE)
Self::new_with_attrs(AttributeMaskBuilder::Some(&[]))
}
/// Allow the mask to view the tag
@@ -342,22 +289,12 @@ impl NodeMask {
self
}
/// Set the mask to view the tag
pub fn set_tag(&mut self) {
self.tag = true;
}
/// Allow the mask to view the namespace
pub const fn with_namespace(mut self) -> Self {
self.namespace = true;
self
}
/// Set the mask to view the namespace
pub fn set_namespace(&mut self) {
self.namespace = true;
}
/// Allow the mask to view the namespace and tag
pub const fn with_element(self) -> Self {
self.with_namespace().with_tag()
@@ -369,24 +306,25 @@ impl NodeMask {
self
}
/// Set the mask to view the text
pub fn set_text(&mut self) {
self.text = true;
}
/// Allow the mask to view the listeners
pub const fn with_listeners(mut self) -> Self {
self.listeners = true;
self
}
/// Set the mask to view the listeners
pub fn set_listeners(&mut self) {
self.listeners = true;
}
/// Allow the mask to view the given attributes
pub fn add_attributes(&mut self, attributes: AttributeMask) {
self.attritutes = self.attritutes.union(&attributes);
/// Build the mask
pub fn build(self) -> NodeMask {
NodeMask {
attritutes: match self.attritutes {
AttributeMaskBuilder::All => AttributeMask::All,
AttributeMaskBuilder::Some(attrs) => {
AttributeMask::Some(attrs.iter().map(|s| (*s).into()).collect())
}
},
tag: self.tag,
namespace: self.namespace,
text: self.text,
listeners: self.listeners,
}
}
}
+5 -4
View File
@@ -7,7 +7,7 @@ use std::marker::PhantomData;
use std::sync::Arc;
use crate::node::{FromAnyValue, NodeData};
use crate::node_ref::NodeView;
use crate::node_ref::{NodeMaskBuilder, NodeView};
use crate::real_dom::RealDom;
use crate::tree::TreeStateView;
use crate::{FxDashMap, FxDashSet, SendAnyMap};
@@ -116,7 +116,7 @@ pub trait Pass<V: FromAnyValue + Send = ()>: Any {
type ChildDependencies: Dependancy;
/// This is a tuple of (T: Any, ..)
type NodeDependencies: Dependancy;
const NODE_MASK: NodeMask;
const NODE_MASK: NodeMaskBuilder;
fn pass<'a>(
&mut self,
@@ -158,13 +158,14 @@ pub trait Pass<V: FromAnyValue + Send = ()>: Any {
Self: Sized,
{
Self::validate();
let node_mask = Self::NODE_MASK.build();
TypeErasedPass {
this_type_id: TypeId::of::<Self>(),
combined_dependancy_type_ids: Self::all_dependanices().iter().copied().collect(),
parent_dependant: !Self::parent_type_ids().is_empty(),
child_dependant: !Self::child_type_ids().is_empty(),
dependants: FxHashSet::default(),
mask: Self::NODE_MASK,
mask: node_mask,
pass_direction: Self::pass_direction(),
pass: Box::new(
|node_id: NodeId, tree: &mut TreeStateView, context: &SendAnyMap| {
@@ -181,7 +182,7 @@ pub trait Pass<V: FromAnyValue + Send = ()>: Any {
let myself = unsafe { &mut *node_raw };
myself.pass(
NodeView::new(node_data, Self::NODE_MASK),
NodeView::new(node_data, Self::NODE_MASK.build()),
node,
parent,
children,
+4 -4
View File
@@ -7,7 +7,7 @@ use std::collections::VecDeque;
use crate::node::{
ElementNode, FromAnyValue, NodeData, NodeType, OwnedAttributeDiscription, OwnedAttributeValue,
};
use crate::node_ref::{AttributeMask, NodeMask};
use crate::node_ref::{AttributeMask, NodeMask, NodeMaskBuilder};
use crate::passes::{resolve_passes, DirtyNodeStates, TypeErasedPass};
use crate::tree::{EntryBuilder, NodeId, Tree};
use crate::{FxDashSet, SendAnyMap};
@@ -64,7 +64,7 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
let mut nodes_updated = FxHashMap::default();
let root_id = NodeId(0);
nodes_updated.insert(root_id, NodeMask::ALL);
nodes_updated.insert(root_id, NodeMaskBuilder::ALL.build());
RealDom {
tree,
@@ -389,7 +389,7 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
pub fn get_mut(&mut self, id: NodeId) -> Option<NodeMut<'_, V>> {
self.tree.contains(id).then(|| NodeMut {
id,
dirty: NodeMask::new(),
dirty: NodeMask::default(),
dom: self,
})
}
@@ -616,7 +616,7 @@ impl<'a, V: FromAnyValue + Send> NodeMut<'a, V> {
pub fn set_type(&mut self, new: NodeType<V>) {
self.node_data_mut().node_type = new;
self.dirty = NodeMask::ALL;
self.dirty = NodeMaskBuilder::ALL.build();
}
pub fn read<T: Any>(&self) -> Option<&T> {
-1
View File
@@ -17,7 +17,6 @@ dioxus = { path = "../dioxus", version = "^0.3.0" }
dioxus-core = { path = "../core", version = "^0.3.0" }
dioxus-html = { path = "../html", version = "^0.3.0" }
dioxus-native-core = { path = "../native-core", version = "^0.2.0" }
dioxus-native-core-macro = { path = "../native-core-macro", version = "^0.2.0" }
tui = "0.17.0"
crossterm = "0.23.0"