1
Fork 0
mirror of https://github.com/RGBCube/cstree synced 2025-07-28 09:37:44 +00:00

Serializable trees (#11)

Clean up #4. Now everything is moved to the new design.
This commit is contained in:
Stu 2021-01-27 11:44:46 +01:00 committed by GitHub
parent 91b68c9a23
commit 3dbfe22bed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 711 additions and 93 deletions

91
Cargo.lock generated
View file

@ -36,6 +36,8 @@ dependencies = [
"m_lexer", "m_lexer",
"parking_lot", "parking_lot",
"serde", "serde",
"serde_json",
"serde_test",
"servo_arc", "servo_arc",
"text-size", "text-size",
] ]
@ -67,6 +69,12 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "itoa"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]] [[package]]
name = "lasso" name = "lasso"
version = "0.4.1" version = "0.4.1"
@ -143,6 +151,24 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "proc-macro2"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
dependencies = [
"proc-macro2",
]
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.1.57" version = "0.1.57"
@ -167,6 +193,12 @@ version = "0.6.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
[[package]]
name = "ryu"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.1.0" version = "1.1.0"
@ -175,15 +207,50 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.119" version = "1.0.120"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bdd36f49e35b61d49efd8aa7fc068fd295961fd2286d0b2ee9a4c7a14e99cc3" checksum = "166b2349061381baf54a58e4b13c89369feb0ef2eaa57198899e2312aac30aab"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca2a8cb5805ce9e3b95435e3765b7b553cecc762d938d409434338386cb5775"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serde_test"
version = "1.0.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dd7d96489b14fa2f4a89be299ac117c8023d1ead9aaee963a2dde72dad4d14b"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "servo_arc" name = "servo_arc"
version = "0.1.1" version = "0.1.1"
dependencies = [ dependencies = [
"nodrop", "nodrop",
"serde",
"stable_deref_trait", "stable_deref_trait",
] ]
@ -199,14 +266,22 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "syn"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]] [[package]]
name = "text-size" name = "text-size"
version = "1.1.0" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "288cb548dbe72b652243ea797201f3d481a0609a967980fcc5b2315ea811560a" checksum = "288cb548dbe72b652243ea797201f3d481a0609a967980fcc5b2315ea811560a"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "thread_local" name = "thread_local"
@ -217,6 +292,12 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "unicode-xid"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"

View file

@ -8,15 +8,26 @@ license = "MIT OR Apache-2.0"
repository = "https://github.com/domenicquirl/cstree" repository = "https://github.com/domenicquirl/cstree"
[dependencies] [dependencies]
serde = { version = "1.0.89", optional = true, default-features = false }
lasso = "0.4.1" lasso = "0.4.1"
text-size = "1.0.0" text-size = "1.0.0"
fxhash= "0.2.1" fxhash= "0.2.1"
servo_arc = { path = "vendor/servo_arc" } servo_arc = { path = "vendor/servo_arc" }
parking_lot= "0.11.1" parking_lot= "0.11.1"
[dependencies.serde]
version = "1.0"
optional = true
default-features = false
features = ["derive"]
[dev-dependencies] [dev-dependencies]
m_lexer = "0.0.4" m_lexer = "0.0.4"
serde_json = "1.0.61"
serde_test = "1.0.119"
[features] [features]
serde1 = ["serde", "text-size/serde"] default = []
serde1 = ["serde", "servo_arc/servo"]
[package.metadata.docs.rs]
features = ["serde1"]

View file

@ -1,63 +1,255 @@
use serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer}; //! Serialization and Deserialization for syntax trees.
use std::fmt;
use crate::{Language, NodeOrToken, SyntaxNode, SyntaxToken}; use crate::{interning::Resolver, GreenNodeBuilder, Language, NodeOrToken, SyntaxKind, SyntaxNode, WalkEvent};
use serde::{
de::{Error, SeqAccess, Visitor},
ser::SerializeTuple,
Deserialize, Serialize,
};
use std::{collections::VecDeque, fmt, marker::PhantomData};
struct SerDisplay<T>(T); type Rodeo = lasso::Rodeo<lasso::Spur, fxhash::FxBuildHasher>;
impl<T: fmt::Display> Serialize for SerDisplay<T> { type RodeoResolver = lasso::RodeoResolver<lasso::Spur>;
/// Expands to the first expression, if there's
/// no expression following, otherwise return the second expression.
///
/// Required for having two different values if the argument is `$(...)?`.
macro_rules! data_list {
($_:expr, $list:expr) => {
$list
};
($list:expr,) => {
$list
};
}
/// Generate the code that should be put inside the [`Serialize`] implementation
/// of a [`SyntaxNode`]-like type.
///
/// It serializes a [`SyntaxNode`] into a tuple with 2 elements.
/// The first element is the serialized event stream that was generated
/// by [`SyntaxNode::preorder_with_tokens()`].
/// The second element is a list of `D`s, where `D` is the data of the nodes.
/// The data may only be serialized if it's `Some(data)`. Each `EnterNode` event
/// contains a boolean which indicates if this node has a data. If it has one,
/// the deserializer should pop the first element from the data list and continue.
///
/// Takes the `Language` (`$l`), `SyntaxNode` (`$node`), `Resolver` (`$resolver`),
/// `Serializer` (`$serializer`), and an optional `data_list` which must be a `mut Vec<D>`.
macro_rules! gen_serialize {
($l:ident, $node:expr, $resolver:expr, $ser:ident, $($data_list:ident)?) => {{
#[allow(unused_variables)]
let events = $node.preorder_with_tokens().filter_map(|event| match event {
WalkEvent::Enter(NodeOrToken::Node(node)) => {
let has_data = false;
$(let has_data = node
.get_data()
.map(|data| {
$data_list.push(data);
true
})
.unwrap_or(false);)?
Some(Event::EnterNode($l::kind_to_raw(node.kind()), has_data))
}
WalkEvent::Enter(NodeOrToken::Token(tok)) => Some(Event::Token($l::kind_to_raw(tok.kind()), tok.resolve_text($resolver))),
WalkEvent::Leave(NodeOrToken::Node(_)) => Some(Event::LeaveNode),
WalkEvent::Leave(NodeOrToken::Token(_)) => None,
});
let mut tuple = $ser.serialize_tuple(2)?;
// TODO(Stupremee): We can easily avoid this allocation but it would
// require more weird and annoying-to-write code, so I'll skip it for now.
tuple.serialize_element(&events.collect::<Vec<_>>())?;
tuple.serialize_element(&data_list!(Vec::<()>::new(), $($data_list)?))?;
tuple.end()
}};
}
#[derive(Deserialize, Serialize)]
#[serde(tag = "t", content = "c")]
enum Event<'text> {
/// The second parameter indicates if this node needs data.
/// If the boolean is true, the next element inside the data list
/// must be attached to this node.
EnterNode(SyntaxKind, bool),
Token(SyntaxKind, &'text str),
LeaveNode,
}
/// Make a `SyntaxNode` serializable but without serializing the data.
pub(crate) struct SerializeWithResolver<'node, 'resolver, L: Language, D: 'static, RN: 'static, R> {
pub(crate) node: &'node SyntaxNode<L, D, RN>,
pub(crate) resolver: &'resolver R,
}
/// Make a `SyntaxNode` serializable which will include the data for serialization.
pub(crate) struct SerializeWithData<'node, 'resolver, L: Language, D: 'static, RN: 'static, R> {
pub(crate) node: &'node SyntaxNode<L, D, RN>,
pub(crate) resolver: &'resolver R,
}
impl<L, D, RN, R> Serialize for SerializeWithData<'_, '_, L, D, RN, R>
where
L: Language,
R: Resolver,
D: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where where
S: Serializer, S: serde::Serializer,
{ {
serializer.collect_str(&self.0) let mut data_list = Vec::new();
gen_serialize!(L, self.node, self.resolver, serializer, data_list)
} }
} }
struct DisplayDebug<T>(T); impl<L, D, RN, R> Serialize for SerializeWithResolver<'_, '_, L, D, RN, R>
impl<T: fmt::Debug> fmt::Display for DisplayDebug<T> { where
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { L: Language,
fmt::Debug::fmt(&self.0, f) R: Resolver,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
gen_serialize!(L, self.node, self.resolver, serializer,)
} }
} }
impl<L: Language> Serialize for SyntaxNode<L> { impl<L, D, R> Serialize for SyntaxNode<L, D, R>
where
L: Language,
D: Serialize,
R: Resolver,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where where
S: Serializer, S: serde::Serializer,
{ {
let mut state = serializer.serialize_map(Some(3))?; let node = SerializeWithResolver {
state.serialize_entry("kind", &SerDisplay(DisplayDebug(self.kind())))?; node: self,
state.serialize_entry("text_range", &self.text_range())?; resolver: self.resolver().as_ref(),
state.serialize_entry("children", &Children(self))?; };
state.end() node.serialize(serializer)
} }
} }
impl<L: Language> Serialize for SyntaxToken<L> { impl<'de, L, D> Deserialize<'de> for SyntaxNode<L, D, RodeoResolver>
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where
L: Language,
D: Deserialize<'de>,
{
// Deserialization is done by walking down the deserialized event stream,
// which is the first element inside the tuple. The events
// are then passed to a `GreenNodeBuilder` which will do all
// the hard work for use.
//
// While walking the event stream, we also store a list of booleans,
// which indicate which node needs to set data. After creating the tree,
// we walk down the nodes, check if the bool at `data_list[idx]` is true,
// and if so, pop the first element of the data list and attach the data
// to the current node.
fn deserialize<DE>(deserializer: DE) -> Result<Self, DE::Error>
where where
S: Serializer, DE: serde::Deserializer<'de>,
{ {
let mut state = serializer.serialize_map(Some(3))?; struct EventVisitor<L: Language, D: 'static> {
state.serialize_entry("kind", &SerDisplay(DisplayDebug(self.kind())))?; _marker: PhantomData<SyntaxNode<L, D, Rodeo>>,
state.serialize_entry("text_range", &self.text_range())?; }
state.serialize_entry("text", &self.text().as_str())?;
state.end()
}
}
struct Children<T>(T); impl<'de, L, D> Visitor<'de> for EventVisitor<L, D>
where
L: Language,
D: Deserialize<'de>,
{
type Value = (SyntaxNode<L, D, RodeoResolver>, VecDeque<bool>);
impl<L: Language> Serialize for Children<&'_ SyntaxNode<L>> { fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> formatter.write_str("a list of tree events")
where }
S: Serializer,
{ fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
let mut state = serializer.serialize_seq(None)?; where
self.0.children_with_tokens().try_for_each(|element| match element { A: SeqAccess<'de>,
NodeOrToken::Node(it) => state.serialize_element(&it), {
NodeOrToken::Token(it) => state.serialize_element(&it), let mut builder = GreenNodeBuilder::new();
let mut data_indices = VecDeque::new();
while let Some(next) = seq.next_element::<Event<'_>>()? {
match next {
Event::EnterNode(kind, has_data) => {
builder.start_node(kind);
data_indices.push_back(has_data);
}
Event::Token(kind, text) => builder.token(kind, text),
Event::LeaveNode => builder.finish_node(),
}
}
let (tree, resolver) = builder.finish();
let tree = SyntaxNode::new_root_with_resolver(tree, resolver.unwrap().into_resolver());
Ok((tree, data_indices))
}
}
struct ProcessedEvents<L: Language, D: 'static>(SyntaxNode<L, D, RodeoResolver>, VecDeque<bool>);
impl<'de, L, D> Deserialize<'de> for ProcessedEvents<L, D>
where
L: Language,
D: Deserialize<'de>,
{
fn deserialize<DE>(deserializer: DE) -> Result<Self, DE::Error>
where
DE: serde::Deserializer<'de>,
{
let (tree, ids) = deserializer.deserialize_seq(EventVisitor { _marker: PhantomData })?;
Ok(Self(tree, ids))
}
}
let (ProcessedEvents(tree, data_indices), mut data) =
<(ProcessedEvents<L, D>, VecDeque<D>)>::deserialize(deserializer)?;
tree.descendants().zip(data_indices).try_for_each(|(node, has_data)| {
if has_data {
let data = data
.pop_front()
.ok_or_else(|| DE::Error::custom("invalid serialized tree"))?;
node.set_data(data);
}
<Result<(), DE::Error>>::Ok(())
})?; })?;
state.end()
if !data.is_empty() {
Err(DE::Error::custom(
"serialized SyntaxNode contained too many data elements",
))
} else {
Ok(tree)
}
}
}
impl Serialize for SyntaxKind {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_u16(self.0)
}
}
impl<'de> Deserialize<'de> for SyntaxKind {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
Ok(Self(u16::deserialize(deserializer)?))
} }
} }

View file

@ -6,6 +6,8 @@ use std::{
sync::atomic::{AtomicU32, Ordering}, sync::atomic::{AtomicU32, Ordering},
}; };
#[cfg(feature = "serde1")]
use crate::serde_impls::{SerializeWithData, SerializeWithResolver};
use parking_lot::RwLock; use parking_lot::RwLock;
use servo_arc::Arc; use servo_arc::Arc;
@ -872,6 +874,46 @@ where
} }
} }
#[cfg(feature = "serde1")]
impl<L, D, R> SyntaxNode<L, D, R>
where
L: Language,
{
/// Return an anonymous object that can be used to serialize this node,
/// including the data for each node.
pub fn as_serialize_with_data(&self) -> impl serde::Serialize + '_
where
R: Resolver,
D: serde::Serialize,
{
SerializeWithData {
node: self,
resolver: self.resolver().as_ref(),
}
}
/// Return an anonymous object that can be used to serialize this node,
/// including the data and by using an external resolver.
pub fn as_serialize_with_data_with_resolver<'node>(
&'node self,
resolver: &'node impl Resolver,
) -> impl serde::Serialize + 'node
where
D: serde::Serialize,
{
SerializeWithData { node: self, resolver }
}
/// Return an anonymous object that can be used to serialize this node,
/// which uses the given resolver instead of the resolver inside the tree.
pub fn as_serialize_with_resolver<'node>(
&'node self,
resolver: &'node impl Resolver,
) -> impl serde::Serialize + 'node {
SerializeWithResolver { node: self, resolver }
}
}
impl<L: Language, D, R> fmt::Debug for SyntaxNode<L, D, R> impl<L: Language, D, R> fmt::Debug for SyntaxNode<L, D, R>
where where
R: Resolver, R: Resolver,

View file

@ -1,15 +1,14 @@
mod common; mod common;
use common::TestLang; use common::{build_recursive, build_tree_with_cache, Element, SyntaxNode};
use cstree::{GreenNode, GreenNodeBuilder, NodeCache, SyntaxKind, TextRange}; use cstree::{GreenNodeBuilder, NodeCache, SyntaxKind, TextRange};
use lasso::{Interner, Resolver, Rodeo}; use lasso::{Resolver, Rodeo};
type SyntaxNode<D = (), R = ()> = cstree::SyntaxNode<TestLang, D, R>; fn build_tree<D>(root: &Element<'_>) -> (SyntaxNode<D>, impl Resolver) {
let mut builder = GreenNodeBuilder::new();
#[derive(Debug)] build_recursive(root, &mut builder, 0);
enum Element<'s> { let (node, interner) = builder.finish();
Node(Vec<Element<'s>>), (SyntaxNode::new_root(node), interner.unwrap())
Token(&'s str),
} }
fn two_level_tree() -> Element<'static> { fn two_level_tree() -> Element<'static> {
@ -21,43 +20,6 @@ fn two_level_tree() -> Element<'static> {
]) ])
} }
fn build_tree<D>(root: &Element<'_>) -> (SyntaxNode<D>, impl Resolver) {
let mut builder = GreenNodeBuilder::new();
build_recursive(root, &mut builder, 0);
let (node, interner) = builder.finish();
(SyntaxNode::new_root(node), interner.unwrap())
}
fn build_tree_with_cache<'c, 'i, I>(root: &Element<'_>, cache: &'c mut NodeCache<'i, I>) -> GreenNode
where
I: Interner,
{
let mut builder = GreenNodeBuilder::with_cache(cache);
build_recursive(root, &mut builder, 0);
let (node, interner) = builder.finish();
assert!(interner.is_none());
node
}
fn build_recursive<'c, 'i, I>(root: &Element<'_>, builder: &mut GreenNodeBuilder<'c, 'i, I>, mut from: u16) -> u16
where
I: Interner,
{
match root {
Element::Node(children) => {
builder.start_node(SyntaxKind(from));
for child in children {
from = build_recursive(child, builder, from + 1);
}
builder.finish_node();
}
Element::Token(text) => {
builder.token(SyntaxKind(from), *text);
}
}
from
}
#[test] #[test]
fn create() { fn create() {
let tree = two_level_tree(); let tree = two_level_tree();

View file

@ -1,4 +1,13 @@
use cstree::{Language, SyntaxKind}; use cstree::{GreenNode, GreenNodeBuilder, Language, NodeCache, SyntaxKind};
use lasso::Interner;
pub type SyntaxNode<D = (), R = ()> = cstree::SyntaxNode<TestLang, D, R>;
#[derive(Debug)]
pub enum Element<'s> {
Node(Vec<Element<'s>>),
Token(&'s str),
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum TestLang {} pub enum TestLang {}
@ -13,3 +22,33 @@ impl Language for TestLang {
kind kind
} }
} }
pub fn build_tree_with_cache<'c, 'i, I>(root: &Element<'_>, cache: &'c mut NodeCache<'i, I>) -> GreenNode
where
I: Interner,
{
let mut builder = GreenNodeBuilder::with_cache(cache);
build_recursive(root, &mut builder, 0);
let (node, interner) = builder.finish();
assert!(interner.is_none());
node
}
pub fn build_recursive<'c, 'i, I>(root: &Element<'_>, builder: &mut GreenNodeBuilder<'c, 'i, I>, mut from: u16) -> u16
where
I: Interner,
{
match root {
Element::Node(children) => {
builder.start_node(SyntaxKind(from));
for child in children {
from = build_recursive(child, builder, from + 1);
}
builder.finish_node();
}
Element::Token(text) => {
builder.token(SyntaxKind(from), *text);
}
}
from
}

291
tests/serde.rs Normal file
View file

@ -0,0 +1,291 @@
#![cfg(feature = "serde1")]
mod common;
use common::{Element, SyntaxNode};
use cstree::{GreenNodeBuilder, NodeCache, NodeOrToken};
use lasso::Resolver;
use serde_test::Token;
use std::fmt;
type Rodeo = lasso::Rodeo<lasso::Spur, fxhash::FxBuildHasher>;
type RodeoResolver = lasso::RodeoResolver<lasso::Spur>;
/// Macro for generating a list of `serde_test::Token`s using a simpler DSL.
macro_rules! event_tokens {
($($name:ident($($token:tt)*)),*) => {
[
$(
event_tokens!(@token, $name($($token)*))
),*
].concat()
};
(@token, token($kind:expr, $str:expr)) => {
[
Token::Struct { name: "Event", len: 2 },
Token::BorrowedStr("t"),
Token::BorrowedStr("Token"),
Token::BorrowedStr("c"),
Token::Tuple { len: 2 },
Token::U16($kind),
Token::BorrowedStr($str),
Token::TupleEnd,
Token::StructEnd,
].as_ref()
};
(@token, node($kind:expr, $data:expr)) => {
[
Token::Struct { name: "Event", len: 2 },
Token::BorrowedStr("t"),
Token::BorrowedStr("EnterNode"),
Token::BorrowedStr("c"),
Token::Tuple { len: 2 },
Token::U16($kind),
Token::Bool($data),
Token::TupleEnd,
Token::StructEnd,
].as_ref()
};
(@token, leave_node()) => {
[
Token::Struct { name: "Event", len: 1 },
Token::BorrowedStr("t"),
Token::BorrowedStr("LeaveNode"),
Token::StructEnd,
].as_ref()
};
(@token, data($data:expr)) => {
[Token::Str($data)].as_ref()
};
(@token, seq($len:expr)) => {
[Token::Seq { len: Option::Some($len) }].as_ref()
};
(@token, seq_end()) => {
[Token::SeqEnd].as_ref()
};
(@token, tuple($len:expr)) => {
[Token::Tuple { len: $len }].as_ref()
};
(@token, tuple_end()) => {
[Token::TupleEnd].as_ref()
};
(@token,) => {};
}
fn three_level_tree_with_data_tokens() -> Vec<Token> {
event_tokens!(
tuple(2),
seq(14),
node(0, true),
node(1, true),
node(2, true),
token(3, "foo"),
token(4, "bar"),
leave_node(),
token(5, "baz"),
leave_node(),
node(6, true),
token(7, "pub"),
token(8, "fn"),
token(9, "tree"),
leave_node(),
leave_node(),
seq_end(),
seq(4),
data("1"),
data("2"),
data("3"),
data("4"),
seq_end(),
tuple_end()
)
}
fn three_level_tree_tokens() -> Vec<Token> {
event_tokens!(
tuple(2),
seq(14),
node(0, false),
node(1, false),
node(2, false),
token(3, "foo"),
token(4, "bar"),
leave_node(),
token(5, "baz"),
leave_node(),
node(6, false),
token(7, "pub"),
token(8, "fn"),
token(9, "tree"),
leave_node(),
leave_node(),
seq_end(),
seq(0),
seq_end(),
tuple_end()
)
}
struct NonSerializable;
/// Serializable SyntaxNode that doesn't have a identity `PartialEq` implementation,
/// but checks if both trees have equal nodes and tokens.
struct TestNode<R: 'static> {
node: SyntaxNode<String, R>,
with_data: bool,
}
impl<R> TestNode<R> {
fn new(node: SyntaxNode<String, R>) -> Self {
Self { node, with_data: false }
}
fn with_data(node: SyntaxNode<String, R>) -> Self {
Self { node, with_data: true }
}
}
impl<R: Resolver> fmt::Debug for TestNode<R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.node, f)
}
}
impl<R: Resolver> serde::Serialize for TestNode<R> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if self.with_data {
self.node.as_serialize_with_data().serialize(serializer)
} else {
self.node.serialize(serializer)
}
}
}
impl<'de> serde::Deserialize<'de> for TestNode<RodeoResolver> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
Ok(Self {
node: SyntaxNode::deserialize(deserializer)?,
with_data: true,
})
}
}
impl<R1: Resolver, R2: Resolver> PartialEq<TestNode<R2>> for TestNode<R1> {
fn eq(&self, other: &TestNode<R2>) -> bool {
self.node.kind() == other.node.kind()
&& self.node.get_data() == other.node.get_data()
&& self.node.text_range() == other.node.text_range()
&& self
.node
.children_with_tokens()
.zip(other.node.children_with_tokens())
.all(|(this, other)| match (this, other) {
(NodeOrToken::Node(this), NodeOrToken::Node(other)) => {
TestNode::new(this.clone()) == TestNode::new(other.clone())
}
(NodeOrToken::Token(this), NodeOrToken::Token(other)) => {
this.kind() == other.kind() && this.text_range() == other.text_range()
}
_ => unreachable!(),
})
}
}
#[rustfmt::skip]
fn three_level_tree() -> Element<'static> {
use Element::*;
Node(vec![
Node(vec![
Node(vec![
Token("foo"),
Token("bar")
]),
Token("baz")
]),
Node(vec![
Token("pub"),
Token("fn"),
Token("tree")
]),
])
}
fn build_tree(root: Element<'_>) -> SyntaxNode<String, RodeoResolver> {
let mut builder = GreenNodeBuilder::new();
common::build_recursive(&root, &mut builder, 0);
let (node, interner) = builder.finish();
SyntaxNode::new_root_with_resolver(node, interner.unwrap().into_resolver())
}
fn attach_data<R>(node: &SyntaxNode<String, R>) {
node.descendants().enumerate().for_each(|(idx, node)| {
node.set_data(format!("{}", idx + 1));
});
}
#[test]
fn serialize_tree_with_data_with_resolver() {
let mut interner = Rodeo::with_hasher(Default::default());
let mut cache = NodeCache::with_interner(&mut interner);
let root = three_level_tree();
let root = common::build_tree_with_cache(&root, &mut cache);
let tree = SyntaxNode::<String, ()>::new_root(root.clone());
attach_data(&tree);
let serialized = serde_json::to_string(&tree.as_serialize_with_data_with_resolver(&interner)).unwrap();
let deserialized: TestNode<_> = serde_json::from_str(&serialized).unwrap();
let expected = SyntaxNode::new_root_with_resolver(root, interner);
attach_data(&expected);
assert_eq!(TestNode::new(expected), deserialized);
}
#[test]
fn serialize_tree_with_resolver() {
let mut interner = Rodeo::with_hasher(Default::default());
let mut cache = NodeCache::with_interner(&mut interner);
let root = three_level_tree();
let root = common::build_tree_with_cache(&root, &mut cache);
let tree = SyntaxNode::<String, NonSerializable>::new_root(root.clone());
let serialized = serde_json::to_string(&tree.as_serialize_with_resolver(&interner)).unwrap();
let deserialized: TestNode<_> = serde_json::from_str(&serialized).unwrap();
let expected = SyntaxNode::new_root_with_resolver(root, interner);
assert_eq!(TestNode::new(expected), deserialized);
}
#[test]
fn serialize_tree_with_data() {
let tree = build_tree(three_level_tree());
let tree = TestNode::with_data(tree);
attach_data(&tree.node);
serde_test::assert_tokens(&tree, three_level_tree_with_data_tokens().as_slice());
}
#[test]
fn serialize_tree_without_data() {
let tree = build_tree(three_level_tree());
let tree = TestNode::new(tree);
serde_test::assert_tokens(&tree, three_level_tree_tokens().as_slice());
}