use rowan::ast::AstNode;
use smol_str::SmolStr;
use super::syntax::{RdLanguage, SyntaxKind, SyntaxNode, SyntaxToken};
macro_rules! wrapper {
($name:ident, $kind:ident) => {
#[doc = concat!("A typed view of an Rd `", stringify!($kind), "` node.")]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct $name(SyntaxNode);
impl AstNode for $name {
type Language = RdLanguage;
fn can_cast(kind: SyntaxKind) -> bool {
kind == SyntaxKind::$kind
}
fn cast(node: SyntaxNode) -> std::option::Option<Self> {
Self::can_cast(node.kind()).then_some(Self(node))
}
fn syntax(&self) -> &SyntaxNode {
&self.0
}
}
};
}
wrapper!(Document, ROOT);
wrapper!(Macro, MACRO);
wrapper!(Argument, ARGUMENT);
wrapper!(Group, GROUP);
wrapper!(Option, OPTION);
wrapper!(Conditional, CONDITIONAL);
fn token(node: &SyntaxNode, kind: SyntaxKind) -> std::option::Option<SyntaxToken> {
node.children_with_tokens()
.filter_map(|element| element.into_token())
.find(|token| token.kind() == kind)
}
impl Document {
pub fn macros(&self) -> impl Iterator<Item = Macro> + use<> {
self.0.children().filter_map(Macro::cast)
}
}
impl Macro {
pub fn name_token(&self) -> std::option::Option<SyntaxToken> {
token(&self.0, SyntaxKind::MACRO_NAME)
}
pub fn name(&self) -> SmolStr {
self.name_token()
.map(|token| SmolStr::new(token.text().trim_start_matches('\\')))
.unwrap_or_default()
}
pub fn arguments(&self) -> impl Iterator<Item = Argument> + use<> {
self.0.children().filter_map(Argument::cast)
}
pub fn option(&self) -> std::option::Option<Option> {
self.0.children().find_map(Option::cast)
}
}
impl Option {
pub fn text(&self) -> String {
self.0
.children_with_tokens()
.filter(|element| {
!matches!(element.kind(), SyntaxKind::LBRACKET | SyntaxKind::RBRACKET)
})
.map(|element| element.to_string())
.collect()
}
}
impl Conditional {
pub fn directive(&self) -> std::option::Option<SyntaxToken> {
token(&self.0, SyntaxKind::DIRECTIVE)
}
pub fn target(&self) -> std::option::Option<SyntaxToken> {
token(&self.0, SyntaxKind::TARGET)
}
}