From 8ede098a9bf4d3b4d60149c30bbc7552863c7f43 Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Thu, 28 Nov 2024 15:10:55 +0100 Subject: [PATCH 1/6] =?UTF-8?q?Add=20`fn=20map=5Fvalues(=E2=80=A6)`=20meth?= =?UTF-8?q?od=20to=20`Tree`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib.rs | 22 ++++++++++++++++++++++ tests/tree.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index ee5636d..871a43f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -94,6 +94,16 @@ impl Node { value, } } + + fn map_value(self, transform: impl FnOnce(T) -> U) -> Node { + Node { + parent: self.parent, + prev_sibling: self.prev_sibling, + next_sibling: self.next_sibling, + children: self.children, + value: transform(self.value), + } + } } /// Node reference. @@ -232,6 +242,18 @@ impl Tree { self.vec.extend(other_tree.vec); unsafe { self.get_unchecked_mut(other_tree_root_id) } } + + /// Maps a `Tree` to `Tree` by applying a function to all node values, + /// without modifying the nodes' ids, or the tree's structure. + pub fn map_values(self, transform: impl Fn(T) -> U) -> Tree { + Tree { + vec: self + .vec + .into_iter() + .map(|node| node.map_value(&transform)) + .collect(), + } + } } impl<'a, T: 'a> NodeRef<'a, T> { diff --git a/tests/tree.rs b/tests/tree.rs index bd92b5b..16f5faf 100644 --- a/tests/tree.rs +++ b/tests/tree.rs @@ -155,6 +155,32 @@ fn insert_id_before() { assert_eq!(3, tree.root().children().count()); } +#[test] +fn test_map_values() { + let str_tree = tree! { + "root" => { + "a" => { + "child 1", + }, + "b" => { + "child 2", + }, + } + }; + + let identity_mapped_tree = str_tree.clone().map_values(|value| value); + + // If we pass the identity function to `.map_values()`, + // then we expect the tree to effectively remain untouched: + assert_eq!(str_tree, identity_mapped_tree); + + let string_tree = str_tree.clone().map_values(|value| value.to_owned()); + + // A `&str` will produce the same output for `.to_string()` as its equivalent `String`, + // so the output of `.to_string()` should match for corresponding trees as well: + assert_eq!(str_tree.to_string(), string_tree.to_string()); +} + #[test] fn test_display() { let tree = tree! { From 7690a51fb161eefe64f5c0a0212676ae10cf5f32 Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Thu, 28 Nov 2024 15:41:10 +0100 Subject: [PATCH 2/6] =?UTF-8?q?Add=20corresponding=20non-consuming=20`fn?= =?UTF-8?q?=20map=5Fvalue=5Frefs(=E2=80=A6)`=20method=20to=20`Tree`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib.rs | 24 +++++++++++++++++++++++- tests/tree.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 871a43f..ca3d5d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -104,6 +104,16 @@ impl Node { value: transform(self.value), } } + + fn map_value_ref(&self, transform: impl FnOnce(&T) -> U) -> Node { + Node { + parent: self.parent, + prev_sibling: self.prev_sibling, + next_sibling: self.next_sibling, + children: self.children, + value: transform(&self.value), + } + } } /// Node reference. @@ -244,7 +254,7 @@ impl Tree { } /// Maps a `Tree` to `Tree` by applying a function to all node values, - /// without modifying the nodes' ids, or the tree's structure. + /// copying over the tree's structure and node ids untouched, consuming `self`. pub fn map_values(self, transform: impl Fn(T) -> U) -> Tree { Tree { vec: self @@ -254,6 +264,18 @@ impl Tree { .collect(), } } + + /// Maps a `&Tree` to `Tree` by applying a function to all node values, + /// copying over the tree's structure and node ids untouched. + pub fn map_value_refs(&self, transform: impl Fn(&T) -> U) -> Tree { + Tree { + vec: self + .vec + .iter() + .map(|node| node.map_value_ref(&transform)) + .collect(), + } + } } impl<'a, T: 'a> NodeRef<'a, T> { diff --git a/tests/tree.rs b/tests/tree.rs index 16f5faf..81f876c 100644 --- a/tests/tree.rs +++ b/tests/tree.rs @@ -181,6 +181,32 @@ fn test_map_values() { assert_eq!(str_tree.to_string(), string_tree.to_string()); } +#[test] +fn test_map_value_refs() { + let str_tree = tree! { + "root" => { + "a" => { + "child 1", + }, + "b" => { + "child 2", + }, + } + }; + + let identity_mapped_tree = str_tree.map_value_refs(|&value| value); + + // If we pass the identity function to `.map_values()`, + // then we expect the tree to effectively remain untouched: + assert_eq!(str_tree, identity_mapped_tree); + + let string_tree = str_tree.map_value_refs(|&value| value.to_owned()); + + // A `&str` will produce the same output for `.to_string()` as its equivalent `String`, + // so the output of `.to_string()` should match for corresponding trees as well: + assert_eq!(str_tree.to_string(), string_tree.to_string()); +} + #[test] fn test_display() { let tree = tree! { From b26a1031bc9e55b9271bc8a3a9be392839b641c4 Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Thu, 28 Nov 2024 16:54:43 +0100 Subject: [PATCH 3/6] =?UTF-8?q?Make=20`map=5Fvalues()`/`map=5Fvalue=5Frefs?= =?UTF-8?q?()`/=E2=80=A6=20take=20a=20`FnMut`,=20rather=20than=20`FnOnce`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ca3d5d6..23bba03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,7 +95,7 @@ impl Node { } } - fn map_value(self, transform: impl FnOnce(T) -> U) -> Node { + fn map_value(self, mut transform: impl FnMut(T) -> U) -> Node { Node { parent: self.parent, prev_sibling: self.prev_sibling, @@ -105,7 +105,7 @@ impl Node { } } - fn map_value_ref(&self, transform: impl FnOnce(&T) -> U) -> Node { + fn map_value_ref(&self, mut transform: impl FnMut(&T) -> U) -> Node { Node { parent: self.parent, prev_sibling: self.prev_sibling, @@ -255,24 +255,24 @@ impl Tree { /// Maps a `Tree` to `Tree` by applying a function to all node values, /// copying over the tree's structure and node ids untouched, consuming `self`. - pub fn map_values(self, transform: impl Fn(T) -> U) -> Tree { + pub fn map_values(self, mut transform: impl FnMut(T) -> U) -> Tree { Tree { vec: self .vec .into_iter() - .map(|node| node.map_value(&transform)) + .map(|node| node.map_value(&mut transform)) .collect(), } } /// Maps a `&Tree` to `Tree` by applying a function to all node values, /// copying over the tree's structure and node ids untouched. - pub fn map_value_refs(&self, transform: impl Fn(&T) -> U) -> Tree { + pub fn map_value_refs(&self, mut transform: impl FnMut(&T) -> U) -> Tree { Tree { vec: self .vec .iter() - .map(|node| node.map_value_ref(&transform)) + .map(|node| node.map_value_ref(&mut transform)) .collect(), } } From c3dc0d8b55ab57a772b02fc976e20b754176a15f Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Thu, 28 Nov 2024 16:56:23 +0100 Subject: [PATCH 4/6] Rename `map_values()`/`map_value_refs()`/, dropping their `_values` infix --- src/lib.rs | 12 ++++++------ tests/tree.rs | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 23bba03..73bd624 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,7 +95,7 @@ impl Node { } } - fn map_value(self, mut transform: impl FnMut(T) -> U) -> Node { + fn map(self, mut transform: impl FnMut(T) -> U) -> Node { Node { parent: self.parent, prev_sibling: self.prev_sibling, @@ -105,7 +105,7 @@ impl Node { } } - fn map_value_ref(&self, mut transform: impl FnMut(&T) -> U) -> Node { + fn map_ref(&self, mut transform: impl FnMut(&T) -> U) -> Node { Node { parent: self.parent, prev_sibling: self.prev_sibling, @@ -255,24 +255,24 @@ impl Tree { /// Maps a `Tree` to `Tree` by applying a function to all node values, /// copying over the tree's structure and node ids untouched, consuming `self`. - pub fn map_values(self, mut transform: impl FnMut(T) -> U) -> Tree { + pub fn map(self, mut transform: impl FnMut(T) -> U) -> Tree { Tree { vec: self .vec .into_iter() - .map(|node| node.map_value(&mut transform)) + .map(|node| node.map(&mut transform)) .collect(), } } /// Maps a `&Tree` to `Tree` by applying a function to all node values, /// copying over the tree's structure and node ids untouched. - pub fn map_value_refs(&self, mut transform: impl FnMut(&T) -> U) -> Tree { + pub fn map_ref(&self, mut transform: impl FnMut(&T) -> U) -> Tree { Tree { vec: self .vec .iter() - .map(|node| node.map_value_ref(&mut transform)) + .map(|node| node.map_ref(&mut transform)) .collect(), } } diff --git a/tests/tree.rs b/tests/tree.rs index 81f876c..cb28780 100644 --- a/tests/tree.rs +++ b/tests/tree.rs @@ -168,13 +168,13 @@ fn test_map_values() { } }; - let identity_mapped_tree = str_tree.clone().map_values(|value| value); + let identity_mapped_tree = str_tree.clone().map(|value| value); // If we pass the identity function to `.map_values()`, // then we expect the tree to effectively remain untouched: assert_eq!(str_tree, identity_mapped_tree); - let string_tree = str_tree.clone().map_values(|value| value.to_owned()); + let string_tree = str_tree.clone().map(|value| value.to_owned()); // A `&str` will produce the same output for `.to_string()` as its equivalent `String`, // so the output of `.to_string()` should match for corresponding trees as well: @@ -194,13 +194,13 @@ fn test_map_value_refs() { } }; - let identity_mapped_tree = str_tree.map_value_refs(|&value| value); + let identity_mapped_tree = str_tree.map_ref(|&value| value); // If we pass the identity function to `.map_values()`, // then we expect the tree to effectively remain untouched: assert_eq!(str_tree, identity_mapped_tree); - let string_tree = str_tree.map_value_refs(|&value| value.to_owned()); + let string_tree = str_tree.map_ref(|&value| value.to_owned()); // A `&str` will produce the same output for `.to_string()` as its equivalent `String`, // so the output of `.to_string()` should match for corresponding trees as well: From 3903980da5793cf972b61767c6d9bf59e325d084 Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Thu, 28 Nov 2024 17:12:07 +0100 Subject: [PATCH 5/6] Replace `impl FnMut` with old-school generics --- src/lib.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 73bd624..0ceba47 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,7 +95,10 @@ impl Node { } } - fn map(self, mut transform: impl FnMut(T) -> U) -> Node { + pub fn map(self, mut transform: F) -> Node + where + F: FnMut(T) -> U, + { Node { parent: self.parent, prev_sibling: self.prev_sibling, @@ -105,7 +108,10 @@ impl Node { } } - fn map_ref(&self, mut transform: impl FnMut(&T) -> U) -> Node { + pub fn map_ref(&self, mut transform: F) -> Node + where + F: FnMut(&T) -> U, + { Node { parent: self.parent, prev_sibling: self.prev_sibling, @@ -255,7 +261,10 @@ impl Tree { /// Maps a `Tree` to `Tree` by applying a function to all node values, /// copying over the tree's structure and node ids untouched, consuming `self`. - pub fn map(self, mut transform: impl FnMut(T) -> U) -> Tree { + pub fn map(self, mut transform: F) -> Tree + where + F: FnMut(T) -> U, + { Tree { vec: self .vec @@ -267,7 +276,10 @@ impl Tree { /// Maps a `&Tree` to `Tree` by applying a function to all node values, /// copying over the tree's structure and node ids untouched. - pub fn map_ref(&self, mut transform: impl FnMut(&T) -> U) -> Tree { + pub fn map_ref(&self, mut transform: F) -> Tree + where + F: FnMut(&T) -> U, + { Tree { vec: self .vec From 5b81faf9b6aed18a27ee93fec5f0dc9efcf4f27b Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Thu, 28 Nov 2024 18:27:11 +0100 Subject: [PATCH 6/6] Fix unrelated clippy warning --- src/serde.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serde.rs b/src/serde.rs index 137c2ad..4fed0b3 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -28,7 +28,7 @@ impl<'a, T> From> for SerNode<'a, T> { } } -impl<'a, T: Serialize> Serialize for SerNode<'a, T> { +impl Serialize for SerNode<'_, T> { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer,