Skip to content

Commit b3a9be1

Browse files
authored
Merge pull request #34 from rust-scraper/serde
Implement serde traits for ego-tree
2 parents da8049b + 2520ba0 commit b3a9be1

11 files changed

+302
-49
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
target
22
Cargo.lock
3-
.vscode
3+
.vscode
4+
.idea

Cargo.toml

+10
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,13 @@ authors = [
1111
license = "ISC"
1212
repository = "https://github.com/rust-scraper/ego-tree"
1313
readme = "README.md"
14+
15+
[features]
16+
serde = ["dep:serde"]
17+
18+
[dependencies]
19+
serde = { version = "1.0.209", optional = true }
20+
21+
[dev-dependencies]
22+
serde = "1.0.209"
23+
serde_test = "1.0.177"

src/lib.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@
3838
use std::fmt::{self, Debug, Display, Formatter};
3939
use std::num::NonZeroUsize;
4040

41+
#[cfg(feature = "serde")]
42+
pub mod serde;
43+
4144
/// Vec-backed ID-tree.
4245
///
4346
/// Always contains at least a root node.
@@ -55,7 +58,7 @@ pub struct NodeId(NonZeroUsize);
5558
impl NodeId {
5659
// Safety: `n` must not equal `usize::MAX`.
5760
// (This is never the case for `Vec::len()`, that would mean it owns
58-
// the entire address space without leaving space for even the its pointer.)
61+
// the entire address space without leaving space even for its pointer.)
5962
unsafe fn from_index(n: usize) -> Self {
6063
NodeId(NonZeroUsize::new_unchecked(n + 1))
6164
}
@@ -75,7 +78,7 @@ struct Node<T> {
7578
}
7679

7780
fn _static_assert_size_of_node() {
78-
// "Instanciating" the generic `transmute` function without calling it
81+
// "Instantiating" the generic `transmute` function without calling it
7982
// still triggers the magic compile-time check
8083
// that input and output types have the same `size_of()`.
8184
let _ = std::mem::transmute::<Node<()>, [usize; 5]>;

src/serde.rs

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
//! Implement `serde::Serialize` and `serde::Deserialize` traits for Tree
2+
//!
3+
//! # Warning
4+
//! Serialize and Deserialize implementations are recursive. They require an amount of stack memory
5+
//! proportional to the depth of the tree.
6+
7+
use std::{fmt, marker::PhantomData};
8+
9+
use serde::{
10+
de::{self, MapAccess, Visitor},
11+
ser::{Serialize, SerializeStruct},
12+
Deserialize, Deserializer,
13+
};
14+
15+
use crate::{NodeMut, NodeRef, Tree};
16+
17+
#[derive(Debug)]
18+
struct SerNode<'a, T> {
19+
value: &'a T,
20+
children: Vec<SerNode<'a, T>>,
21+
}
22+
23+
impl<'a, T> From<NodeRef<'a, T>> for SerNode<'a, T> {
24+
fn from(node: NodeRef<'a, T>) -> Self {
25+
let value = node.value();
26+
let children = node.children().map(SerNode::from).collect();
27+
Self { value, children }
28+
}
29+
}
30+
31+
impl<'a, T: Serialize> Serialize for SerNode<'a, T> {
32+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
33+
where
34+
S: serde::Serializer,
35+
{
36+
let mut state = serializer.serialize_struct("Node", 2)?;
37+
state.serialize_field("value", &self.value)?;
38+
state.serialize_field("children", &self.children)?;
39+
state.end()
40+
}
41+
}
42+
43+
impl<T: Serialize> Serialize for Tree<T> {
44+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
45+
where
46+
S: serde::Serializer,
47+
{
48+
SerNode::from(self.root()).serialize(serializer)
49+
}
50+
}
51+
52+
#[derive(Debug)]
53+
struct DeserNode<T> {
54+
value: T,
55+
children: Vec<DeserNode<T>>,
56+
}
57+
58+
impl<T> DeserNode<T> {
59+
fn into_tree_node(self, parent: &mut NodeMut<T>) {
60+
let mut node = parent.append(self.value);
61+
62+
for child in self.children {
63+
child.into_tree_node(&mut node);
64+
}
65+
}
66+
}
67+
68+
impl<T> From<DeserNode<T>> for Tree<T> {
69+
fn from(root: DeserNode<T>) -> Self {
70+
let mut tree: Tree<T> = Tree::new(root.value);
71+
let mut tree_root = tree.root_mut();
72+
73+
for child in root.children {
74+
child.into_tree_node(&mut tree_root);
75+
}
76+
77+
tree
78+
}
79+
}
80+
81+
struct DeserNodeVisitor<T> {
82+
marker: PhantomData<fn() -> DeserNode<T>>,
83+
}
84+
85+
impl<T> DeserNodeVisitor<T> {
86+
fn new() -> Self {
87+
DeserNodeVisitor {
88+
marker: PhantomData,
89+
}
90+
}
91+
}
92+
93+
impl<'de, T> Visitor<'de> for DeserNodeVisitor<T>
94+
where
95+
T: Deserialize<'de>,
96+
{
97+
type Value = DeserNode<T>;
98+
99+
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
100+
formatter.write_str("struct Node")
101+
}
102+
103+
fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
104+
where
105+
M: MapAccess<'de>,
106+
{
107+
let mut value = None;
108+
let mut children = None;
109+
110+
while let Some(key) = map.next_key()? {
111+
match key {
112+
"value" => {
113+
if value.is_some() {
114+
return Err(de::Error::duplicate_field("value"));
115+
}
116+
value = Some(map.next_value()?);
117+
}
118+
"children" => {
119+
if children.is_some() {
120+
return Err(de::Error::duplicate_field("children"));
121+
}
122+
children = Some(map.next_value()?);
123+
}
124+
_ => {
125+
return Err(de::Error::unknown_field(key, &["value", "children"]));
126+
}
127+
}
128+
}
129+
130+
let value = value.ok_or_else(|| de::Error::missing_field("value"))?;
131+
let children = children.ok_or_else(|| de::Error::missing_field("children"))?;
132+
133+
Ok(DeserNode { value, children })
134+
}
135+
}
136+
137+
impl<'de, T> Deserialize<'de> for DeserNode<T>
138+
where
139+
T: Deserialize<'de>,
140+
{
141+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
142+
where
143+
D: Deserializer<'de>,
144+
{
145+
deserializer.deserialize_struct("Node", &["value", "children"], DeserNodeVisitor::new())
146+
}
147+
}
148+
149+
impl<'de, T: Deserialize<'de>> Deserialize<'de> for Tree<T> {
150+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
151+
where
152+
D: serde::Deserializer<'de>,
153+
{
154+
let deser = DeserNode::<T>::deserialize(deserializer)?;
155+
Ok(deser.into())
156+
}
157+
}

tests/iter.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
#[macro_use]
2-
extern crate ego_tree;
1+
use ego_tree::tree;
32

43
#[test]
54
fn into_iter() {

tests/macro.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
#[macro_use]
2-
extern crate ego_tree;
3-
4-
use ego_tree::Tree;
1+
use ego_tree::{tree, Tree};
52

63
#[test]
74
fn root() {

tests/node_mut.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
#[macro_use]
2-
extern crate ego_tree;
3-
4-
use ego_tree::NodeRef;
1+
use ego_tree::{tree, NodeRef};
52

63
#[test]
74
fn value() {

tests/node_ref.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
#[macro_use]
2-
extern crate ego_tree;
1+
use ego_tree::tree;
32

43
#[test]
54
fn value() {

tests/serde.rs

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#![cfg(feature = "serde")]
2+
3+
use ego_tree::tree;
4+
use serde_test::{assert_tokens, Token};
5+
6+
#[test]
7+
fn test_internal_serde_repr_trivial() {
8+
let tree = tree!("a");
9+
10+
assert_tokens(
11+
&tree,
12+
&[
13+
Token::Struct {
14+
name: "Node",
15+
len: 2,
16+
},
17+
Token::BorrowedStr("value"),
18+
Token::BorrowedStr("a"),
19+
Token::BorrowedStr("children"),
20+
Token::Seq { len: Some(0) },
21+
Token::SeqEnd,
22+
Token::StructEnd,
23+
],
24+
);
25+
}
26+
27+
#[test]
28+
fn test_internal_serde_repr() {
29+
let tree = tree!("a" => {"b", "c" => {"d", "e"}, "f"});
30+
31+
assert_tokens(
32+
&tree,
33+
&[
34+
Token::Struct {
35+
name: "Node",
36+
len: 2,
37+
},
38+
Token::BorrowedStr("value"),
39+
Token::BorrowedStr("a"),
40+
Token::BorrowedStr("children"),
41+
Token::Seq { len: Some(3) },
42+
Token::Struct {
43+
name: "Node",
44+
len: 2,
45+
},
46+
Token::BorrowedStr("value"),
47+
Token::BorrowedStr("b"),
48+
Token::BorrowedStr("children"),
49+
Token::Seq { len: Some(0) },
50+
Token::SeqEnd,
51+
Token::StructEnd,
52+
Token::Struct {
53+
name: "Node",
54+
len: 2,
55+
},
56+
Token::BorrowedStr("value"),
57+
Token::BorrowedStr("c"),
58+
Token::BorrowedStr("children"),
59+
Token::Seq { len: Some(2) },
60+
Token::Struct {
61+
name: "Node",
62+
len: 2,
63+
},
64+
Token::BorrowedStr("value"),
65+
Token::BorrowedStr("d"),
66+
Token::BorrowedStr("children"),
67+
Token::Seq { len: Some(0) },
68+
Token::SeqEnd,
69+
Token::StructEnd,
70+
Token::Struct {
71+
name: "Node",
72+
len: 2,
73+
},
74+
Token::BorrowedStr("value"),
75+
Token::BorrowedStr("e"),
76+
Token::BorrowedStr("children"),
77+
Token::Seq { len: Some(0) },
78+
Token::SeqEnd,
79+
Token::StructEnd,
80+
Token::SeqEnd,
81+
Token::StructEnd,
82+
Token::Struct {
83+
name: "Node",
84+
len: 2,
85+
},
86+
Token::BorrowedStr("value"),
87+
Token::BorrowedStr("f"),
88+
Token::BorrowedStr("children"),
89+
Token::Seq { len: Some(0) },
90+
Token::SeqEnd,
91+
Token::StructEnd,
92+
Token::SeqEnd,
93+
Token::StructEnd,
94+
],
95+
);
96+
}

0 commit comments

Comments
 (0)