use std::{fmt, marker::PhantomData, slice::Iter};
use rowan::{GreenNode, GreenNodeBuilder};
use crate::{
cst::{self, CstNode},
Error, SyntaxElement, SyntaxKind, SyntaxNode,
};
use super::LimitTracker;
pub(crate) enum SyntaxTreeWrapper {
Document(SyntaxTree<cst::Document>),
FieldSet(SyntaxTree<cst::SelectionSet>),
Type(SyntaxTree<cst::Type>),
}
#[derive(PartialEq, Eq, Clone)]
pub struct SyntaxTree<T: CstNode = cst::Document> {
pub(crate) green: GreenNode,
pub(crate) errors: Vec<crate::Error>,
pub(crate) recursion_limit: LimitTracker,
pub(crate) token_limit: LimitTracker,
_phantom: PhantomData<fn() -> T>,
}
const _: () = {
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
let _ = assert_send::<SyntaxTree>;
let _ = assert_sync::<SyntaxTree>;
};
impl<T: CstNode> SyntaxTree<T> {
pub fn errors(&self) -> Iter<'_, crate::Error> {
self.errors.iter()
}
pub fn recursion_limit(&self) -> LimitTracker {
self.recursion_limit
}
pub fn token_limit(&self) -> LimitTracker {
self.token_limit
}
pub fn green(&self) -> GreenNode {
self.green.clone()
}
pub(crate) fn syntax_node(&self) -> SyntaxNode {
rowan::SyntaxNode::new_root(self.green.clone())
}
}
impl SyntaxTree<cst::Document> {
pub fn document(&self) -> cst::Document {
cst::Document {
syntax: self.syntax_node(),
}
}
}
impl SyntaxTree<cst::SelectionSet> {
pub fn field_set(&self) -> cst::SelectionSet {
cst::SelectionSet {
syntax: self.syntax_node(),
}
}
}
impl SyntaxTree<cst::Type> {
pub fn ty(&self) -> cst::Type {
match self.syntax_node().kind() {
SyntaxKind::NAMED_TYPE => cst::Type::NamedType(cst::NamedType {
syntax: self.syntax_node(),
}),
SyntaxKind::LIST_TYPE => cst::Type::ListType(cst::ListType {
syntax: self.syntax_node(),
}),
SyntaxKind::NON_NULL_TYPE => cst::Type::NonNullType(cst::NonNullType {
syntax: self.syntax_node(),
}),
_ => unreachable!("this should only return Type node"),
}
}
}
impl<T: CstNode> fmt::Debug for SyntaxTree<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn print(f: &mut fmt::Formatter<'_>, indent: usize, element: SyntaxElement) -> fmt::Result {
let kind: SyntaxKind = element.kind();
write!(f, "{:indent$}", "", indent = indent)?;
match element {
rowan::NodeOrToken::Node(node) => {
writeln!(f, "- {:?}@{:?}", kind, node.text_range())?;
for child in node.children_with_tokens() {
print(f, indent + 4, child)?;
}
Ok(())
}
rowan::NodeOrToken::Token(token) => {
writeln!(
f,
"- {:?}@{:?} {:?}",
kind,
token.text_range(),
token.text()
)
}
}
}
fn print_err(f: &mut fmt::Formatter<'_>, errors: Vec<Error>) -> fmt::Result {
for err in errors {
writeln!(f, "- {err:?}")?;
}
write!(f, "")
}
fn print_recursion_limit(
f: &mut fmt::Formatter<'_>,
recursion_limit: LimitTracker,
) -> fmt::Result {
write!(f, "{recursion_limit:?}")
}
print(f, 0, self.syntax_node().into())?;
print_err(f, self.errors.clone())?;
print_recursion_limit(f, self.recursion_limit)
}
}
#[derive(Debug)]
pub(crate) struct SyntaxTreeBuilder {
builder: GreenNodeBuilder<'static>,
}
impl SyntaxTreeBuilder {
pub(crate) fn new() -> Self {
Self {
builder: GreenNodeBuilder::new(),
}
}
pub(crate) fn checkpoint(&self) -> rowan::Checkpoint {
self.builder.checkpoint()
}
pub(crate) fn start_node(&mut self, kind: SyntaxKind) {
self.builder.start_node(rowan::SyntaxKind(kind as u16));
}
pub(crate) fn finish_node(&mut self) {
self.builder.finish_node();
}
pub(crate) fn wrap_node(&mut self, checkpoint: rowan::Checkpoint, kind: SyntaxKind) {
self.builder
.start_node_at(checkpoint, rowan::SyntaxKind(kind as u16));
}
pub(crate) fn token(&mut self, kind: SyntaxKind, text: &str) {
self.builder.token(rowan::SyntaxKind(kind as u16), text);
}
pub(crate) fn finish_document(
self,
errors: Vec<Error>,
recursion_limit: LimitTracker,
token_limit: LimitTracker,
) -> SyntaxTreeWrapper {
SyntaxTreeWrapper::Document(SyntaxTree {
green: self.builder.finish(),
errors,
recursion_limit,
token_limit,
_phantom: PhantomData,
})
}
pub(crate) fn finish_selection_set(
self,
errors: Vec<Error>,
recursion_limit: LimitTracker,
token_limit: LimitTracker,
) -> SyntaxTreeWrapper {
SyntaxTreeWrapper::FieldSet(SyntaxTree {
green: self.builder.finish(),
errors,
recursion_limit,
token_limit,
_phantom: PhantomData,
})
}
pub(crate) fn finish_type(
self,
errors: Vec<Error>,
recursion_limit: LimitTracker,
token_limit: LimitTracker,
) -> SyntaxTreeWrapper {
SyntaxTreeWrapper::Type(SyntaxTree {
green: self.builder.finish(),
errors,
recursion_limit,
token_limit,
_phantom: PhantomData,
})
}
}
#[cfg(test)]
mod test {
use crate::cst::Definition;
use crate::Parser;
#[test]
fn directive_name() {
let input = "directive @example(isTreat: Boolean, treatKind: String) on FIELD | MUTATION";
let parser = Parser::new(input);
let cst = parser.parse();
let doc = cst.document();
for def in doc.definitions() {
if let Definition::DirectiveDefinition(directive) = def {
assert_eq!(directive.name().unwrap().text(), "example");
}
}
}
#[test]
fn object_type_definition() {
let input = "
type ProductDimension {
size: String
weight: Float @tag(name: \"hi from inventory value type field\")
}
";
let parser = Parser::new(input);
let cst = parser.parse();
assert_eq!(0, cst.errors().len());
let doc = cst.document();
for def in doc.definitions() {
if let Definition::ObjectTypeDefinition(object_type) = def {
assert_eq!(object_type.name().unwrap().text(), "ProductDimension");
for field_def in object_type.fields_definition().unwrap().field_definitions() {
println!("{}", field_def.name().unwrap().text()); }
}
}
}
}