From 05edbe388122f749e95c391815fc525d17b0d041 Mon Sep 17 00:00:00 2001 From: LoZack19 Date: Wed, 28 Aug 2024 22:22:08 +0200 Subject: [PATCH 01/17] implement serde traits for ego-tree --- Cargo.toml | 10 ++++++ src/lib.rs | 4 +++ src/serde.rs | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++ tests/serde.rs | 16 ++++++++++ 4 files changed, 113 insertions(+) create mode 100644 src/serde.rs create mode 100644 tests/serde.rs diff --git a/Cargo.toml b/Cargo.toml index 39448df..d60e1af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,3 +11,13 @@ authors = [ license = "ISC" repository = "https://github.com/rust-scraper/ego-tree" readme = "README.md" + +[features] +serde = ["dep:serde"] + +[dependencies] +serde = { version = "1.0.209" , optional = true } + +[dev-dependencies] +serde = "1.0.209" +serde_json = "1.0.127" diff --git a/src/lib.rs b/src/lib.rs index 566d84d..ffe5d85 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,6 +38,10 @@ use std::fmt::{self, Debug, Display, Formatter}; use std::num::NonZeroUsize; +#[cfg(feature = "serde")] +/// Implement serde traits for Tree +pub mod serde; + /// Vec-backed ID-tree. /// /// Always contains at least a root node. diff --git a/src/serde.rs b/src/serde.rs new file mode 100644 index 0000000..72265cc --- /dev/null +++ b/src/serde.rs @@ -0,0 +1,83 @@ +#![cfg(feature = "serde")] +use std::num::NonZeroUsize; + +use serde::{Deserialize, Serialize}; + +use crate::{Node, NodeId, Tree}; + +impl Serialize for Tree { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.vec.serialize(serializer) + } +} + +impl<'de, T: Deserialize<'de>> Deserialize<'de> for Tree { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let vec = Vec::deserialize(deserializer)?; + Ok(Tree { vec }) + } +} + +impl Serialize for Node { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + ( + &self.parent, + &self.prev_sibling, + &self.next_sibling, + &self.children, + &self.value, + ) + .serialize(serializer) + } +} + +impl<'de, T: Deserialize<'de>> Deserialize<'de> for Node { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let (parent, prev_sibling, next_sibling, children, value) = + <( + Option, + Option, + Option, + Option<(NodeId, NodeId)>, + T, + )>::deserialize(deserializer)?; + Ok(Node { + parent, + prev_sibling, + next_sibling, + children, + value, + }) + } +} + +impl Serialize for NodeId { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.0.serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for NodeId { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let index = ::deserialize(deserializer)?; + Ok(NodeId(index)) + } +} diff --git a/tests/serde.rs b/tests/serde.rs new file mode 100644 index 0000000..88d8418 --- /dev/null +++ b/tests/serde.rs @@ -0,0 +1,16 @@ +#![cfg(feature = "serde")] +#[macro_use] +extern crate ego_tree; +use std::assert_eq; + +use ego_tree::Tree; + +#[test] +fn test_serialize() { + let tree = tree!("r" => {"a", "b" => { "d", "e" }, "c"}); + + let serialized = serde_json::to_string(&tree).unwrap(); + let deserialized: Tree<&str> = serde_json::from_str(&serialized).unwrap(); + + assert_eq!(tree, deserialized); +} From 2f7167bad617bae52811c81a645c441e412b5aa4 Mon Sep 17 00:00:00 2001 From: LoZack19 Date: Wed, 28 Aug 2024 22:27:11 +0200 Subject: [PATCH 02/17] fix clippy warnings for serde feature --- src/serde.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/serde.rs b/src/serde.rs index 72265cc..f757f66 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -1,4 +1,3 @@ -#![cfg(feature = "serde")] use std::num::NonZeroUsize; use serde::{Deserialize, Serialize}; From ea8c556b4c8e77354410210f19e420f78b20e3fc Mon Sep 17 00:00:00 2001 From: Giovanni Zaccaria <71092108+LoZack19@users.noreply.github.com> Date: Wed, 28 Aug 2024 22:34:16 +0200 Subject: [PATCH 03/17] Update Cargo.toml Remove disgusting whitespace --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d60e1af..71451ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ readme = "README.md" serde = ["dep:serde"] [dependencies] -serde = { version = "1.0.209" , optional = true } +serde = { version = "1.0.209", optional = true } [dev-dependencies] serde = "1.0.209" From 82514a5c57c74fa9767a5cc8bd4029195f575cd3 Mon Sep 17 00:00:00 2001 From: LoZack19 Date: Thu, 29 Aug 2024 00:07:47 +0200 Subject: [PATCH 04/17] updating to rust 2021: remove macro_use and extern crate from all tests --- tests/iter.rs | 3 +-- tests/macro.rs | 5 +--- tests/node_mut.rs | 5 +--- tests/node_ref.rs | 3 +-- tests/serde.rs | 6 ++--- tests/subtree.rs | 60 ++++++++++++++++++++++------------------------- tests/tree.rs | 2 -- 7 files changed, 35 insertions(+), 49 deletions(-) diff --git a/tests/iter.rs b/tests/iter.rs index 6ceffa8..5ce441a 100644 --- a/tests/iter.rs +++ b/tests/iter.rs @@ -1,5 +1,4 @@ -#[macro_use] -extern crate ego_tree; +use ego_tree::tree; #[test] fn into_iter() { diff --git a/tests/macro.rs b/tests/macro.rs index 14cf444..377fb8f 100644 --- a/tests/macro.rs +++ b/tests/macro.rs @@ -1,7 +1,4 @@ -#[macro_use] -extern crate ego_tree; - -use ego_tree::Tree; +use ego_tree::{tree, Tree}; #[test] fn root() { diff --git a/tests/node_mut.rs b/tests/node_mut.rs index 20aff6b..f2e8c63 100644 --- a/tests/node_mut.rs +++ b/tests/node_mut.rs @@ -1,7 +1,4 @@ -#[macro_use] -extern crate ego_tree; - -use ego_tree::NodeRef; +use ego_tree::{tree, NodeRef}; #[test] fn value() { diff --git a/tests/node_ref.rs b/tests/node_ref.rs index c46cb65..af4dff8 100644 --- a/tests/node_ref.rs +++ b/tests/node_ref.rs @@ -1,5 +1,4 @@ -#[macro_use] -extern crate ego_tree; +use ego_tree::tree; #[test] fn value() { diff --git a/tests/serde.rs b/tests/serde.rs index 88d8418..f22337d 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -1,15 +1,15 @@ #![cfg(feature = "serde")] -#[macro_use] -extern crate ego_tree; + use std::assert_eq; -use ego_tree::Tree; +use ego_tree::{tree, Tree}; #[test] fn test_serialize() { let tree = tree!("r" => {"a", "b" => { "d", "e" }, "c"}); let serialized = serde_json::to_string(&tree).unwrap(); + println!("{serialized}"); let deserialized: Tree<&str> = serde_json::from_str(&serialized).unwrap(); assert_eq!(tree, deserialized); diff --git a/tests/subtree.rs b/tests/subtree.rs index 6af769d..30a279d 100644 --- a/tests/subtree.rs +++ b/tests/subtree.rs @@ -1,39 +1,35 @@ -#[macro_use] -extern crate ego_tree; +use ego_tree::tree; -#[cfg(test)] -mod test { - #[test] - fn prepend_subtree() { - let mut tree = tree!('a' => { 'b', 'c' => { 'd', 'e' } }); - let node_id = tree.root().first_child().unwrap().id(); - let mut node = tree.get_mut(node_id).unwrap(); - assert_eq!(node.value(), &'b'); +#[test] +fn prepend_subtree() { + let mut tree = tree!('a' => { 'b', 'c' => { 'd', 'e' } }); + let node_id = tree.root().first_child().unwrap().id(); + let mut node = tree.get_mut(node_id).unwrap(); + assert_eq!(node.value(), &'b'); - let subtree = tree!('f' => { 'g', 'h' => { 'i', 'j' } }); - let mut root_subtree = node.prepend_subtree(subtree); - assert_eq!(root_subtree.parent().unwrap().value(), &'b'); - assert_eq!( - root_subtree.parent().unwrap().parent().unwrap().value(), - &'a' - ); + let subtree = tree!('f' => { 'g', 'h' => { 'i', 'j' } }); + let mut root_subtree = node.prepend_subtree(subtree); + assert_eq!(root_subtree.parent().unwrap().value(), &'b'); + assert_eq!( + root_subtree.parent().unwrap().parent().unwrap().value(), + &'a' + ); - let new_tree = - tree!('a' => { 'b' => { 'f' => { 'g', 'h' => { 'i', 'j' } } }, 'c' => { 'd', 'e' } }); - assert_eq!(format!("{:#?}", tree), format!("{:#?}", new_tree)); - } + let new_tree = + tree!('a' => { 'b' => { 'f' => { 'g', 'h' => { 'i', 'j' } } }, 'c' => { 'd', 'e' } }); + assert_eq!(format!("{:#?}", tree), format!("{:#?}", new_tree)); +} - #[test] - fn append_subtree() { - let mut tree = tree!('a' => { 'b', 'c' }); - let mut node = tree.root_mut(); - assert_eq!(node.value(), &'a'); +#[test] +fn append_subtree() { + let mut tree = tree!('a' => { 'b', 'c' }); + let mut node = tree.root_mut(); + assert_eq!(node.value(), &'a'); - let subtree = tree!('d' => { 'e', 'f' }); - let mut root_subtree = node.append_subtree(subtree); - assert_eq!(root_subtree.parent().unwrap().value(), &'a'); + let subtree = tree!('d' => { 'e', 'f' }); + let mut root_subtree = node.append_subtree(subtree); + assert_eq!(root_subtree.parent().unwrap().value(), &'a'); - let new_tree = tree!('a' => { 'b', 'c', 'd' => { 'e', 'f' } }); - assert_eq!(format!("{:#?}", tree), format!("{:#?}", new_tree)); - } + let new_tree = tree!('a' => { 'b', 'c', 'd' => { 'e', 'f' } }); + assert_eq!(format!("{:#?}", tree), format!("{:#?}", new_tree)); } diff --git a/tests/tree.rs b/tests/tree.rs index 78eee6a..bd92b5b 100644 --- a/tests/tree.rs +++ b/tests/tree.rs @@ -1,5 +1,3 @@ -extern crate ego_tree; - use ego_tree::{tree, Tree}; #[test] From 7fb9415492e5e1d3b57029c6553322619cc14e30 Mon Sep 17 00:00:00 2001 From: LoZack19 Date: Thu, 29 Aug 2024 10:40:01 +0200 Subject: [PATCH 05/17] remove useless line beacause already in prelude --- tests/serde.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/serde.rs b/tests/serde.rs index f22337d..c0cc7b1 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -1,7 +1,5 @@ #![cfg(feature = "serde")] -use std::assert_eq; - use ego_tree::{tree, Tree}; #[test] From b5c10292bfb740ca68173acc81d48eb2a2381def Mon Sep 17 00:00:00 2001 From: LoZack19 Date: Thu, 29 Aug 2024 12:18:26 +0200 Subject: [PATCH 06/17] implement serialize through intermediate representation --- src/serde.rs | 84 +++++++++++++++----------------------------------- tests/serde.rs | 3 -- 2 files changed, 25 insertions(+), 62 deletions(-) diff --git a/src/serde.rs b/src/serde.rs index f757f66..73ab1ee 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -1,82 +1,48 @@ -use std::num::NonZeroUsize; +use serde::ser::{Serialize, SerializeStruct}; -use serde::{Deserialize, Serialize}; +use crate::{NodeRef, Tree}; -use crate::{Node, NodeId, Tree}; - -impl Serialize for Tree { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - self.vec.serialize(serializer) - } +#[derive(Debug)] +struct SerNode<'a, T> { + value: &'a T, + children: Vec>, } -impl<'de, T: Deserialize<'de>> Deserialize<'de> for Tree { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let vec = Vec::deserialize(deserializer)?; - Ok(Tree { vec }) +impl<'a, T> From> for SerNode<'a, T> { + fn from(node: NodeRef<'a, T>) -> Self { + let value: &T = node.value(); + let children: Vec> = node.children().map(SerNode::<'a, T>::from).collect(); + SerNode { value, children } } } -impl Serialize for Node { +impl<'a, T: Serialize> Serialize for SerNode<'a, T> { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { - ( - &self.parent, - &self.prev_sibling, - &self.next_sibling, - &self.children, - &self.value, - ) - .serialize(serializer) + (&self.value, &self.children).serialize(serializer) } } -impl<'de, T: Deserialize<'de>> Deserialize<'de> for Node { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let (parent, prev_sibling, next_sibling, children, value) = - <( - Option, - Option, - Option, - Option<(NodeId, NodeId)>, - T, - )>::deserialize(deserializer)?; - Ok(Node { - parent, - prev_sibling, - next_sibling, - children, - value, - }) - } -} - -impl Serialize for NodeId { +impl Serialize for Tree { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { - self.0.serialize(serializer) + SerNode::from(self.root()).serialize(serializer) } } -impl<'de> Deserialize<'de> for NodeId { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let index = ::deserialize(deserializer)?; - Ok(NodeId(index)) +#[cfg(test)] +mod test { + use super::*; + use crate::tree; + + #[test] + fn test_ser_node_from() { + let tree = tree!("a" => {"b", "c" => {"d", "e"}, "f"}); + let repr = serde_json::to_string(&SerNode::from(tree.root())).unwrap(); + println!("{repr}"); } } diff --git a/tests/serde.rs b/tests/serde.rs index c0cc7b1..e11202b 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -8,7 +8,4 @@ fn test_serialize() { let serialized = serde_json::to_string(&tree).unwrap(); println!("{serialized}"); - let deserialized: Tree<&str> = serde_json::from_str(&serialized).unwrap(); - - assert_eq!(tree, deserialized); } From 9f3d03d08272689c804962638b9290e95f2a4363 Mon Sep 17 00:00:00 2001 From: LoZack19 Date: Thu, 29 Aug 2024 13:28:59 +0200 Subject: [PATCH 07/17] implement deserialize through intermediate representation --- src/serde.rs | 61 +++++++++++++++++++++++++++++++++++++++++--------- tests/serde.rs | 12 +++++----- 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/src/serde.rs b/src/serde.rs index 73ab1ee..db8ba2e 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -1,6 +1,6 @@ -use serde::ser::{Serialize, SerializeStruct}; +use serde::{ser::Serialize, Deserialize}; -use crate::{NodeRef, Tree}; +use crate::{NodeId, NodeRef, Tree}; #[derive(Debug)] struct SerNode<'a, T> { @@ -34,15 +34,54 @@ impl Serialize for Tree { } } -#[cfg(test)] -mod test { - use super::*; - use crate::tree; +#[derive(Debug)] +struct DeserNode { + value: T, + children: Vec>, +} + +impl DeserNode { + fn to_tree_node(self, tree: &mut Tree, parent: NodeId) -> NodeId { + let mut parent = tree.get_mut(parent).unwrap(); + let node = parent.append(self.value).id(); + + for child in self.children { + child.to_tree_node(tree, node); + } + + node + } +} + +impl From> for Tree { + fn from(root: DeserNode) -> Self { + let mut tree: Tree = Tree::new(root.value); + let root_id = tree.root().id; - #[test] - fn test_ser_node_from() { - let tree = tree!("a" => {"b", "c" => {"d", "e"}, "f"}); - let repr = serde_json::to_string(&SerNode::from(tree.root())).unwrap(); - println!("{repr}"); + for child in root.children { + child.to_tree_node(&mut tree, root_id); + } + + tree + } +} + +impl<'de, T: Deserialize<'de>> Deserialize<'de> for DeserNode { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let (value, children) = <(T, Vec>)>::deserialize(deserializer)?; + Ok(DeserNode { value, children }) + } +} + +impl<'de, T: Deserialize<'de>> Deserialize<'de> for Tree { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let deser = DeserNode::::deserialize(deserializer)?; + Ok(deser.into()) } } diff --git a/tests/serde.rs b/tests/serde.rs index e11202b..e6f8771 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -3,9 +3,11 @@ use ego_tree::{tree, Tree}; #[test] -fn test_serialize() { - let tree = tree!("r" => {"a", "b" => { "d", "e" }, "c"}); - - let serialized = serde_json::to_string(&tree).unwrap(); - println!("{serialized}"); +fn test_serde_round_trip() { + let tree = tree!("a" => {"b", "c" => {"d", "e"}, "f"}); + let repr = serde_json::to_string(&tree).unwrap(); + println!("{repr}"); + let re_tree: Tree<&str> = serde_json::from_str(&repr).unwrap(); + println!("{re_tree}"); + assert_eq!(tree, re_tree); } From 143afa60a66564cca4cfd1cc03a9f5ec795f1bcd Mon Sep 17 00:00:00 2001 From: LoZack19 Date: Thu, 29 Aug 2024 13:31:26 +0200 Subject: [PATCH 08/17] correct to_* into into_* for semantic correctness --- src/serde.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/serde.rs b/src/serde.rs index db8ba2e..17956c0 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -41,12 +41,12 @@ struct DeserNode { } impl DeserNode { - fn to_tree_node(self, tree: &mut Tree, parent: NodeId) -> NodeId { + fn into_tree_node(self, tree: &mut Tree, parent: NodeId) -> NodeId { let mut parent = tree.get_mut(parent).unwrap(); let node = parent.append(self.value).id(); for child in self.children { - child.to_tree_node(tree, node); + child.into_tree_node(tree, node); } node @@ -59,7 +59,7 @@ impl From> for Tree { let root_id = tree.root().id; for child in root.children { - child.to_tree_node(&mut tree, root_id); + child.into_tree_node(&mut tree, root_id); } tree From 1ff4288450a9443e47b6c053afaf2454707df081 Mon Sep 17 00:00:00 2001 From: LoZack19 Date: Thu, 29 Aug 2024 13:56:12 +0200 Subject: [PATCH 09/17] improve idiomacity of serialization and deserialization for structs --- src/serde.rs | 83 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 6 deletions(-) diff --git a/src/serde.rs b/src/serde.rs index 17956c0..4043bbb 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -1,4 +1,10 @@ -use serde::{ser::Serialize, Deserialize}; +use std::{fmt, marker::PhantomData}; + +use serde::{ + de::{self, MapAccess, Visitor}, + ser::{Serialize, SerializeStruct}, + Deserialize, Deserializer, +}; use crate::{NodeId, NodeRef, Tree}; @@ -21,7 +27,10 @@ impl<'a, T: Serialize> Serialize for SerNode<'a, T> { where S: serde::Serializer, { - (&self.value, &self.children).serialize(serializer) + let mut state = serializer.serialize_struct("SerNode", 2)?; + state.serialize_field("value", &self.value)?; + state.serialize_field("children", &self.children)?; + state.end() } } @@ -66,16 +75,78 @@ impl From> for Tree { } } -impl<'de, T: Deserialize<'de>> Deserialize<'de> for DeserNode { - fn deserialize(deserializer: D) -> Result +struct DeserNodeVisitor { + marker: PhantomData DeserNode>, +} + +impl DeserNodeVisitor { + fn new() -> Self { + DeserNodeVisitor { + marker: PhantomData, + } + } +} + +impl<'de, T> Visitor<'de> for DeserNodeVisitor +where + T: Deserialize<'de>, +{ + type Value = DeserNode; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("struct DeserNode") + } + + fn visit_map(self, mut map: M) -> Result where - D: serde::Deserializer<'de>, + M: MapAccess<'de>, { - let (value, children) = <(T, Vec>)>::deserialize(deserializer)?; + let mut value = None; + let mut children = None; + + while let Some(key) = map.next_key()? { + match key { + "value" => { + if value.is_some() { + return Err(de::Error::duplicate_field("value")); + } + value = Some(map.next_value()?); + } + "children" => { + if children.is_some() { + return Err(de::Error::duplicate_field("children")); + } + children = Some(map.next_value()?); + } + _ => { + return Err(de::Error::unknown_field(key, &["value", "children"])); + } + } + } + + let value = value.ok_or_else(|| de::Error::missing_field("value"))?; + let children = children.ok_or_else(|| de::Error::missing_field("children"))?; + Ok(DeserNode { value, children }) } } +impl<'de, T> Deserialize<'de> for DeserNode +where + T: Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_struct( + "DeserNode", + &["value", "children"], + DeserNodeVisitor::new(), + ) + } +} + impl<'de, T: Deserialize<'de>> Deserialize<'de> for Tree { fn deserialize(deserializer: D) -> Result where From b85b188bcf408a542ade8d423c6d15fa197418b8 Mon Sep 17 00:00:00 2001 From: LoZack19 Date: Thu, 29 Aug 2024 14:57:37 +0200 Subject: [PATCH 10/17] into_tree_node now takes parent as NodeMut --- src/serde.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/serde.rs b/src/serde.rs index 4043bbb..129fbcd 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -6,7 +6,7 @@ use serde::{ Deserialize, Deserializer, }; -use crate::{NodeId, NodeRef, Tree}; +use crate::{NodeId, NodeMut, NodeRef, Tree}; #[derive(Debug)] struct SerNode<'a, T> { @@ -50,25 +50,24 @@ struct DeserNode { } impl DeserNode { - fn into_tree_node(self, tree: &mut Tree, parent: NodeId) -> NodeId { - let mut parent = tree.get_mut(parent).unwrap(); - let node = parent.append(self.value).id(); + fn into_tree_node(self, parent: &mut NodeMut) -> NodeId { + let mut node = parent.append(self.value); for child in self.children { - child.into_tree_node(tree, node); + child.into_tree_node(&mut node); } - node + node.id } } impl From> for Tree { fn from(root: DeserNode) -> Self { let mut tree: Tree = Tree::new(root.value); - let root_id = tree.root().id; + let mut tree_root = tree.root_mut(); for child in root.children { - child.into_tree_node(&mut tree, root_id); + child.into_tree_node(&mut tree_root); } tree From be6bbea9b212bac21a53e3d4b10d21b3680be95c Mon Sep 17 00:00:00 2001 From: LoZack19 Date: Thu, 29 Aug 2024 15:17:11 +0200 Subject: [PATCH 11/17] add documentation for serde module --- src/lib.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index ffe5d85..f336af8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,8 +38,21 @@ use std::fmt::{self, Debug, Display, Formatter}; use std::num::NonZeroUsize; -#[cfg(feature = "serde")] /// Implement serde traits for Tree +/// +/// ``` +/// use ego_tree::{tree, Tree}; +/// +/// let tree = tree!("a" => {"b", "c" => {"d", "e"}, "f"}); +/// let repr = serde_json::to_string(&tree).unwrap(); +/// let re_tree: Tree<&str> = serde_json::from_str(&repr).unwrap(); +/// ``` +/// +/// +/// # Warning +/// Serialize and Deserialize implementations are recursive. They require an amount of stack memory +/// propotional to the tree depth. +#[cfg(feature = "serde")] pub mod serde; /// Vec-backed ID-tree. From f094f01e43789739d21d04fe31867b57f229e9c5 Mon Sep 17 00:00:00 2001 From: LoZack19 Date: Thu, 29 Aug 2024 16:12:18 +0200 Subject: [PATCH 12/17] wip: introduce serde_test token matching Co-authored-by: Carlo Federico Vescovo --- Cargo.toml | 1 + src/serde.rs | 10 +++------- tests/serde.rs | 22 ++++++++++++++++++++++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 71451ee..9b03d94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,4 @@ serde = { version = "1.0.209", optional = true } [dev-dependencies] serde = "1.0.209" serde_json = "1.0.127" +serde_test = "1.0.177" diff --git a/src/serde.rs b/src/serde.rs index 129fbcd..40f20d9 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -27,7 +27,7 @@ impl<'a, T: Serialize> Serialize for SerNode<'a, T> { where S: serde::Serializer, { - let mut state = serializer.serialize_struct("SerNode", 2)?; + let mut state = serializer.serialize_struct("Node", 2)?; state.serialize_field("value", &self.value)?; state.serialize_field("children", &self.children)?; state.end() @@ -93,7 +93,7 @@ where type Value = DeserNode; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("struct DeserNode") + formatter.write_str("struct Node") } fn visit_map(self, mut map: M) -> Result @@ -138,11 +138,7 @@ where where D: Deserializer<'de>, { - deserializer.deserialize_struct( - "DeserNode", - &["value", "children"], - DeserNodeVisitor::new(), - ) + deserializer.deserialize_struct("Node", &["value", "children"], DeserNodeVisitor::new()) } } diff --git a/tests/serde.rs b/tests/serde.rs index e6f8771..f6fd836 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -1,6 +1,7 @@ #![cfg(feature = "serde")] use ego_tree::{tree, Tree}; +use serde_test::{assert_tokens, Token}; #[test] fn test_serde_round_trip() { @@ -11,3 +12,24 @@ fn test_serde_round_trip() { println!("{re_tree}"); assert_eq!(tree, re_tree); } + +#[test] +fn test_internal_serde_repr() { + let tree = tree!("a"); + + assert_tokens( + &tree, + &[ + Token::Struct { + name: "Node", + len: 2, + }, + Token::Str("value"), + Token::Str("a"), + Token::Str("children"), + Token::Seq { len: Some(0) }, + Token::SeqEnd, + Token::StructEnd, + ], + ); +} From 08ae4d9390a559a4073462f70cbc376d35bf4ddc Mon Sep 17 00:00:00 2001 From: Carlo Federico Vescovo <26970569+cfvescovo@users.noreply.github.com> Date: Thu, 29 Aug 2024 16:43:21 +0200 Subject: [PATCH 13/17] Ignore JetBrains' .idea folder --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 110d010..5b99a65 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target Cargo.lock -.vscode \ No newline at end of file +.vscode +.idea \ No newline at end of file From e975c7904d2663d06307b6e71b072fb8ccd4a5b9 Mon Sep 17 00:00:00 2001 From: Carlo Federico Vescovo <26970569+cfvescovo@users.noreply.github.com> Date: Thu, 29 Aug 2024 16:55:29 +0200 Subject: [PATCH 14/17] Fix tests for serde feature Correct some typos too Co-authored-by: Giovanni Zaccaria <71092108+lozack19@users.noreply.github.com> --- Cargo.toml | 1 - src/lib.rs | 17 +++------- tests/serde.rs | 85 +++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 77 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9b03d94..f6deaa2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,5 +20,4 @@ serde = { version = "1.0.209", optional = true } [dev-dependencies] serde = "1.0.209" -serde_json = "1.0.127" serde_test = "1.0.177" diff --git a/src/lib.rs b/src/lib.rs index f336af8..9fa6767 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,20 +38,11 @@ use std::fmt::{self, Debug, Display, Formatter}; use std::num::NonZeroUsize; -/// Implement serde traits for Tree -/// -/// ``` -/// use ego_tree::{tree, Tree}; -/// -/// let tree = tree!("a" => {"b", "c" => {"d", "e"}, "f"}); -/// let repr = serde_json::to_string(&tree).unwrap(); -/// let re_tree: Tree<&str> = serde_json::from_str(&repr).unwrap(); -/// ``` -/// +/// Implement serde::Serialize and serde::Deserialize traits for Tree /// /// # Warning /// Serialize and Deserialize implementations are recursive. They require an amount of stack memory -/// propotional to the tree depth. +/// proportional to the tree depth. #[cfg(feature = "serde")] pub mod serde; @@ -72,7 +63,7 @@ pub struct NodeId(NonZeroUsize); impl NodeId { // Safety: `n` must not equal `usize::MAX`. // (This is never the case for `Vec::len()`, that would mean it owns - // the entire address space without leaving space for even the its pointer.) + // the entire address space without leaving space even for its pointer.) unsafe fn from_index(n: usize) -> Self { NodeId(NonZeroUsize::new_unchecked(n + 1)) } @@ -92,7 +83,7 @@ struct Node { } fn _static_assert_size_of_node() { - // "Instanciating" the generic `transmute` function without calling it + // "Instantiating" the generic `transmute` function without calling it // still triggers the magic compile-time check // that input and output types have the same `size_of()`. let _ = std::mem::transmute::, [usize; 5]>; diff --git a/tests/serde.rs b/tests/serde.rs index f6fd836..0e0deee 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -1,21 +1,32 @@ #![cfg(feature = "serde")] -use ego_tree::{tree, Tree}; +use ego_tree::tree; use serde_test::{assert_tokens, Token}; #[test] -fn test_serde_round_trip() { - let tree = tree!("a" => {"b", "c" => {"d", "e"}, "f"}); - let repr = serde_json::to_string(&tree).unwrap(); - println!("{repr}"); - let re_tree: Tree<&str> = serde_json::from_str(&repr).unwrap(); - println!("{re_tree}"); - assert_eq!(tree, re_tree); +fn test_internal_serde_repr_trivial() { + let tree = tree!("a"); + + assert_tokens( + &tree, + &[ + Token::Struct { + name: "Node", + len: 2, + }, + Token::BorrowedStr("value"), + Token::BorrowedStr("a"), + Token::BorrowedStr("children"), + Token::Seq { len: Some(0) }, + Token::SeqEnd, + Token::StructEnd, + ], + ); } #[test] fn test_internal_serde_repr() { - let tree = tree!("a"); + let tree = tree!("a" => {"b", "c" => {"d", "e"}, "f"}); assert_tokens( &tree, @@ -24,12 +35,62 @@ fn test_internal_serde_repr() { name: "Node", len: 2, }, - Token::Str("value"), - Token::Str("a"), - Token::Str("children"), + Token::BorrowedStr("value"), + Token::BorrowedStr("a"), + Token::BorrowedStr("children"), + Token::Seq { len: Some(3) }, + Token::Struct { + name: "Node", + len: 2, + }, + Token::BorrowedStr("value"), + Token::BorrowedStr("b"), + Token::BorrowedStr("children"), + Token::Seq { len: Some(0) }, + Token::SeqEnd, + Token::StructEnd, + Token::Struct { + name: "Node", + len: 2, + }, + Token::BorrowedStr("value"), + Token::BorrowedStr("c"), + Token::BorrowedStr("children"), + Token::Seq { len: Some(2) }, + Token::Struct { + name: "Node", + len: 2, + }, + Token::BorrowedStr("value"), + Token::BorrowedStr("d"), + Token::BorrowedStr("children"), Token::Seq { len: Some(0) }, Token::SeqEnd, Token::StructEnd, + Token::Struct { + name: "Node", + len: 2, + }, + Token::BorrowedStr("value"), + Token::BorrowedStr("e"), + Token::BorrowedStr("children"), + Token::Seq { len: Some(0) }, + Token::SeqEnd, + Token::StructEnd, + Token::SeqEnd, + Token::StructEnd, + Token::Struct { + name: "Node", + len: 2, + }, + Token::BorrowedStr("value"), + Token::BorrowedStr("f"), + Token::BorrowedStr("children"), + Token::Seq { len: Some(0) }, + Token::SeqEnd, + Token::StructEnd, + Token::SeqEnd, + Token::StructEnd, ], ); } From d082fec6f96cac6bff5a6fb62ba599d1069c7384 Mon Sep 17 00:00:00 2001 From: Carlo Federico Vescovo <26970569+cfvescovo@users.noreply.github.com> Date: Thu, 29 Aug 2024 18:00:08 +0200 Subject: [PATCH 15/17] Update src/serde.rs Co-authored-by: Adam Reichold --- src/serde.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/serde.rs b/src/serde.rs index 40f20d9..08338e9 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -16,9 +16,9 @@ struct SerNode<'a, T> { impl<'a, T> From> for SerNode<'a, T> { fn from(node: NodeRef<'a, T>) -> Self { - let value: &T = node.value(); - let children: Vec> = node.children().map(SerNode::<'a, T>::from).collect(); - SerNode { value, children } + let value = node.value(); + let children = node.children().map(SerNode::from).collect(); + Self { value, children } } } From 644828bb8d824556c6cce19fbbf12e2afb1bab69 Mon Sep 17 00:00:00 2001 From: LoZack19 Date: Thu, 29 Aug 2024 18:03:41 +0200 Subject: [PATCH 16/17] move serde module comments to serde.rs --- src/lib.rs | 5 ----- src/serde.rs | 6 ++++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9fa6767..ee5636d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,11 +38,6 @@ use std::fmt::{self, Debug, Display, Formatter}; use std::num::NonZeroUsize; -/// Implement serde::Serialize and serde::Deserialize traits for Tree -/// -/// # Warning -/// Serialize and Deserialize implementations are recursive. They require an amount of stack memory -/// proportional to the tree depth. #[cfg(feature = "serde")] pub mod serde; diff --git a/src/serde.rs b/src/serde.rs index 08338e9..f70cd82 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -1,3 +1,9 @@ +//! Implement `serde::Serialize` and `serde::Deserialize` traits for Tree +//! +//! # Warning +//! Serialize and Deserialize implementations are recursive. They require an amount of stack memory +//! proportional to the depth of the tree. + use std::{fmt, marker::PhantomData}; use serde::{ From 2520ba05c8e73fd93edc0e859273d6a64d85841c Mon Sep 17 00:00:00 2001 From: LoZack19 Date: Thu, 29 Aug 2024 18:06:36 +0200 Subject: [PATCH 17/17] remove unused return value in into_tree_node --- src/serde.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/serde.rs b/src/serde.rs index f70cd82..137c2ad 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -12,7 +12,7 @@ use serde::{ Deserialize, Deserializer, }; -use crate::{NodeId, NodeMut, NodeRef, Tree}; +use crate::{NodeMut, NodeRef, Tree}; #[derive(Debug)] struct SerNode<'a, T> { @@ -56,14 +56,12 @@ struct DeserNode { } impl DeserNode { - fn into_tree_node(self, parent: &mut NodeMut) -> NodeId { + fn into_tree_node(self, parent: &mut NodeMut) { let mut node = parent.append(self.value); for child in self.children { child.into_tree_node(&mut node); } - - node.id } }