use std::sync::Arc;
use super::green::{GreenElement, GreenNode, GreenToken};
use crate::types::{FieldId, SyntaxKind};
pub struct GreenBuilder {
kind: SyntaxKind,
children: Vec<(Option<FieldId>, GreenElement)>,
}
impl GreenBuilder {
#[inline]
#[must_use]
pub const fn new(kind: SyntaxKind) -> Self {
Self {
kind,
children: Vec::new(),
}
}
#[inline]
pub fn token(&mut self, kind: SyntaxKind, text: &str, is_trivia: bool) -> &mut Self {
self.children.push((
None,
GreenElement::Token(GreenToken::new(kind, text, is_trivia)),
));
self
}
#[inline]
pub fn child_node(&mut self, node: Arc<GreenNode>) -> &mut Self {
self.children.push((None, GreenElement::Node(node)));
self
}
#[inline]
pub fn node(&mut self, kind: SyntaxKind, f: impl FnOnce(&mut Self)) -> &mut Self {
let child = Self::node_standalone(kind, f);
self.children.push((None, GreenElement::Node(child)));
self
}
#[inline]
pub fn node_with_field(
&mut self,
kind: SyntaxKind,
field_id: FieldId,
f: impl FnOnce(&mut Self),
) -> &mut Self {
let child = Self::node_standalone(kind, f);
self.children
.push((Some(field_id), GreenElement::Node(child)));
self
}
#[inline]
#[must_use]
pub fn finish(mut self) -> Arc<GreenNode> {
let children_with_fields = std::mem::take(&mut self.children);
let has_any_field = children_with_fields.iter().any(|(f, _)| f.is_some());
if has_any_field {
GreenNode::new_with_fields(self.kind, children_with_fields)
} else {
let children: Vec<GreenElement> =
children_with_fields.into_iter().map(|(_, e)| e).collect();
GreenNode::new(self.kind, children)
}
}
pub fn node_standalone(kind: SyntaxKind, f: impl FnOnce(&mut Self)) -> Arc<GreenNode> {
let mut b = Self::new(kind);
f(&mut b);
b.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn build_simple_node() {
let root = GreenBuilder::node_standalone(1, |b| {
b.token(2, "x", false)
.token(3, "+", false)
.token(4, "1", false);
});
assert_eq!(root.kind, 1);
assert_eq!(root.children.len(), 3);
assert_eq!(root.collect_text(), "x+1");
}
#[test]
fn build_nested() {
let root = GreenBuilder::node_standalone(10, |b| {
b.node(11, |b| {
b.token(2, "a", false);
})
.token(3, " ", true)
.node(11, |b| {
b.token(2, "b", false);
});
});
assert_eq!(root.kind, 10);
assert_eq!(root.children.len(), 3);
assert_eq!(root.collect_text(), "a b");
}
}