From d7b43bbc260711d922a8f941eeba52d9a6cd540c Mon Sep 17 00:00:00 2001 From: WorldSEnder Date: Wed, 11 May 2022 12:37:39 +0200 Subject: [PATCH] Change access to VList children to a wrapper (#2673) this should avoid having to manually call recheck_fully_keyed * add mutable accessor to children * fix workflow * fix markdown example * remove recheck_fully_keyed --- examples/futures/src/markdown.rs | 41 +++++++------- packages/yew/src/virtual_dom/vlist.rs | 81 +++++++++++++++++++++++---- tools/changelog/Cargo.toml | 2 +- 3 files changed, 93 insertions(+), 31 deletions(-) diff --git a/examples/futures/src/markdown.rs b/examples/futures/src/markdown.rs index 415a0891b3e..74bb30b7bbd 100644 --- a/examples/futures/src/markdown.rs +++ b/examples/futures/src/markdown.rs @@ -53,31 +53,34 @@ pub fn render_markdown(src: &str) -> Html { pre.add_child(top.into()); top = pre; } else if let Tag::Table(aligns) = tag { - for r in top.children_mut().iter_mut().flat_map(|ch| ch.iter_mut()) { - if let VNode::VTag(ref mut vtag) = r { - for (i, c) in vtag - .children_mut() - .iter_mut() - .flat_map(|ch| ch.iter_mut()) - .enumerate() - { - if let VNode::VTag(ref mut vtag) = c { - match aligns[i] { - Alignment::None => {} - Alignment::Left => add_class(vtag, "text-left"), - Alignment::Center => add_class(vtag, "text-center"), - Alignment::Right => add_class(vtag, "text-right"), + if let Some(top_children) = top.children_mut() { + for r in top_children.children_mut().iter_mut() { + if let VNode::VTag(ref mut vtag) = r { + if let Some(vtag_children) = vtag.children_mut() { + for (i, c) in + vtag_children.children_mut().iter_mut().enumerate() + { + if let VNode::VTag(ref mut vtag) = c { + match aligns[i] { + Alignment::None => {} + Alignment::Left => add_class(vtag, "text-left"), + Alignment::Center => add_class(vtag, "text-center"), + Alignment::Right => add_class(vtag, "text-right"), + } + } } } } } } } else if let Tag::TableHead = tag { - for c in top.children_mut().iter_mut().flat_map(|ch| ch.iter_mut()) { - if let VNode::VTag(ref mut vtag) = c { - // TODO - // vtag.tag = "th".into(); - vtag.add_attribute("scope", "col"); + if let Some(top_children) = top.children_mut() { + for c in top_children.children_mut().iter_mut() { + if let VNode::VTag(ref mut vtag) = c { + // TODO + // vtag.tag = "th".into(); + vtag.add_attribute("scope", "col"); + } } } } diff --git a/packages/yew/src/virtual_dom/vlist.rs b/packages/yew/src/virtual_dom/vlist.rs index f9ab588ad76..4717246e72c 100644 --- a/packages/yew/src/virtual_dom/vlist.rs +++ b/packages/yew/src/virtual_dom/vlist.rs @@ -29,13 +29,42 @@ impl Deref for VList { } } -impl DerefMut for VList { +/// Mutable children of a [VList]. +/// +/// This struct has a `DerefMut` implementations into [`Vec`](std::vec::Vec). +/// Prefer to use immutable access, since this re-checks if all nodes have keys when dropped. +pub struct ChildrenMut<'a> { + children: &'a mut Vec, + fully_keyed: &'a mut bool, +} + +impl<'a> Deref for ChildrenMut<'a> { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + self.children + } +} + +impl<'a> DerefMut for ChildrenMut<'a> { fn deref_mut(&mut self) -> &mut Self::Target { - // Caller might change the keys of the VList or add unkeyed children. - // Defensively assume they will. - self.fully_keyed = false; + *self.fully_keyed = false; + self.children + } +} - &mut self.children +impl<'a> Drop for ChildrenMut<'a> { + fn drop(&mut self) { + if !*self.fully_keyed { + // Caller might have changed the keys of the VList or add unkeyed children. + *self.fully_keyed = self.children.iter().all(|ch| ch.has_key()); + } + } +} + +impl<'a> std::fmt::Debug for ChildrenMut<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("ChildrenMut").field(&self.children).finish() } } @@ -76,12 +105,42 @@ impl VList { } } - /// Recheck, if the all the children have keys. - /// - /// Run this, after modifying the child list that contained only keyed children prior to the - /// mutable dereference. - pub fn recheck_fully_keyed(&mut self) { - self.fully_keyed = self.children.iter().all(|ch| ch.has_key()); + /// Get a mutable list of children in this vlist. + pub fn children_mut(&mut self) -> ChildrenMut<'_> { + ChildrenMut { + children: &mut self.children, + fully_keyed: &mut self.fully_keyed, + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::virtual_dom::{VTag, VText}; + + #[test] + fn mutably_change_children() { + let mut vlist = VList::new(); + assert!(vlist.fully_keyed, "should start fully keyed"); + // add a child that is keyed + let mut children = vlist.children_mut(); + children.push(VNode::VTag({ + let mut tag = VTag::new("a"); + tag.key = Some(42u32.into()); + Box::new(tag) + })); + drop(children); + assert!(vlist.fully_keyed, "should still be fully keyed"); + assert_eq!(vlist.len(), 1, "should contain 1 child"); + // now add a child that is not keyed + let mut children = vlist.children_mut(); + children.push(VNode::VText(VText::new("lorem ipsum"))); + drop(children); + assert!( + !vlist.fully_keyed, + "should not be fully keyed, text tags have no key" + ); } } diff --git a/tools/changelog/Cargo.toml b/tools/changelog/Cargo.toml index 0bed984d901..e114ad5fe69 100644 --- a/tools/changelog/Cargo.toml +++ b/tools/changelog/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" [dependencies] anyhow = "1" chrono = "0.4" -git2 = "0.14" +git2 = "=0.14.2" # see https://github.com/rust-lang/git2-rs/issues/838 fixed with MSRV 1.60 regex = "1" reqwest = { version = "0.11", features = ["blocking", "json"] } serde = { version = "1", features = ["derive"] }