|
@@ -1,47 +1,11 @@
|
|
-use std::cmp::Ordering;
|
|
|
|
-
|
|
|
|
use dioxus_core::ElementId;
|
|
use dioxus_core::ElementId;
|
|
|
|
+use rustc_hash::FxHashSet;
|
|
|
|
|
|
use crate::{
|
|
use crate::{
|
|
node::{ElementNode, FromAnyValue, NodeData, NodeType, OwnedAttributeView},
|
|
node::{ElementNode, FromAnyValue, NodeData, NodeType, OwnedAttributeView},
|
|
NodeId,
|
|
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.
|
|
/// A view into a [VNode] with limited access.
|
|
#[derive(Debug)]
|
|
#[derive(Debug)]
|
|
pub struct NodeView<'a, V: FromAnyValue = ()> {
|
|
pub struct NodeView<'a, V: FromAnyValue = ()> {
|
|
@@ -138,124 +102,50 @@ impl<'a, V: FromAnyValue> NodeView<'a, V> {
|
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
|
pub enum AttributeMask {
|
|
pub enum AttributeMask {
|
|
All,
|
|
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 {
|
|
impl AttributeMask {
|
|
- /// A empty attribute mask
|
|
|
|
- pub const NONE: Self = Self::Static(&[]);
|
|
|
|
-
|
|
|
|
fn contains_attribute(&self, attr: &str) -> bool {
|
|
fn contains_attribute(&self, attr: &str) -> bool {
|
|
match self {
|
|
match self {
|
|
AttributeMask::All => true,
|
|
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
|
|
/// Create a new dynamic attribute mask with a single attribute
|
|
pub fn single(new: &str) -> Self {
|
|
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
|
|
/// Combine two attribute masks
|
|
pub fn union(&self, other: &Self) -> Self {
|
|
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(),
|
|
|
|
- ))
|
|
|
|
- }
|
|
|
|
- (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(),
|
|
|
|
- ))
|
|
|
|
|
|
+ match (self, other) {
|
|
|
|
+ (AttributeMask::Some(s), AttributeMask::Some(o)) => {
|
|
|
|
+ AttributeMask::Some(s.intersection(o).cloned().collect())
|
|
}
|
|
}
|
|
- (AttributeMask::Static(s), AttributeMask::Static(o)) => AttributeMask::Dynamic(
|
|
|
|
- union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()),
|
|
|
|
- ),
|
|
|
|
_ => AttributeMask::All,
|
|
_ => AttributeMask::All,
|
|
- };
|
|
|
|
- new.verify();
|
|
|
|
- new
|
|
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
/// Check if two attribute masks overlap
|
|
/// Check if two attribute masks overlap
|
|
fn overlaps(&self, other: &Self) -> bool {
|
|
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) {
|
|
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 {
|
|
impl Default for AttributeMask {
|
|
fn default() -> Self {
|
|
fn default() -> Self {
|
|
- AttributeMask::Static(&[])
|
|
|
|
|
|
+ AttributeMask::Some(FxHashSet::default())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -270,33 +160,33 @@ pub struct NodeMask {
|
|
}
|
|
}
|
|
|
|
|
|
impl 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 {
|
|
pub fn from_node(node: &NodeType) -> Self {
|
|
match node {
|
|
match node {
|
|
- NodeType::Text { .. } => Self::new().with_text(),
|
|
|
|
|
|
+ NodeType::Text { .. } => NodeMaskBuilder::new().with_text().build(),
|
|
NodeType::Element(ElementNode {
|
|
NodeType::Element(ElementNode {
|
|
tag: _,
|
|
tag: _,
|
|
namespace: _,
|
|
namespace: _,
|
|
attributes,
|
|
attributes,
|
|
listeners,
|
|
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
|
|
/// 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 {
|
|
Self {
|
|
attritutes,
|
|
attritutes,
|
|
tag: false,
|
|
tag: false,
|
|
@@ -333,7 +280,7 @@ impl NodeMask {
|
|
|
|
|
|
/// Create a empty node mask
|
|
/// Create a empty node mask
|
|
pub const fn new() -> Self {
|
|
pub const fn new() -> Self {
|
|
- Self::new_with_attrs(AttributeMask::NONE)
|
|
|
|
|
|
+ Self::new_with_attrs(AttributeMaskBuilder::Some(&[]))
|
|
}
|
|
}
|
|
|
|
|
|
/// Allow the mask to view the tag
|
|
/// Allow the mask to view the tag
|
|
@@ -342,22 +289,12 @@ impl NodeMask {
|
|
self
|
|
self
|
|
}
|
|
}
|
|
|
|
|
|
- /// Set the mask to view the tag
|
|
|
|
- pub fn set_tag(&mut self) {
|
|
|
|
- self.tag = true;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
/// Allow the mask to view the namespace
|
|
/// Allow the mask to view the namespace
|
|
pub const fn with_namespace(mut self) -> Self {
|
|
pub const fn with_namespace(mut self) -> Self {
|
|
self.namespace = true;
|
|
self.namespace = true;
|
|
self
|
|
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
|
|
/// Allow the mask to view the namespace and tag
|
|
pub const fn with_element(self) -> Self {
|
|
pub const fn with_element(self) -> Self {
|
|
self.with_namespace().with_tag()
|
|
self.with_namespace().with_tag()
|
|
@@ -369,24 +306,25 @@ impl NodeMask {
|
|
self
|
|
self
|
|
}
|
|
}
|
|
|
|
|
|
- /// Set the mask to view the text
|
|
|
|
- pub fn set_text(&mut self) {
|
|
|
|
- self.text = true;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
/// Allow the mask to view the listeners
|
|
/// Allow the mask to view the listeners
|
|
pub const fn with_listeners(mut self) -> Self {
|
|
pub const fn with_listeners(mut self) -> Self {
|
|
self.listeners = true;
|
|
self.listeners = true;
|
|
self
|
|
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,
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|