Skip to main content

sql_dialect_fmt_parser/
ast.rs

1//! A thin typed layer over the untyped CST: zero-cost newtypes around `SyntaxNode` with
2//! `cast`/`syntax` plus a few accessors. This bootstraps the pattern; coverage grows with the
3//! grammar (and can later be code-generated from an ungrammar).
4
5use sql_dialect_fmt_syntax::{SyntaxKind, SyntaxNode};
6
7/// A typed view of a CST node of a particular kind.
8pub trait AstNode {
9    fn can_cast(kind: SyntaxKind) -> bool
10    where
11        Self: Sized;
12    fn cast(node: SyntaxNode) -> Option<Self>
13    where
14        Self: Sized;
15    fn syntax(&self) -> &SyntaxNode;
16}
17
18macro_rules! ast_node {
19    ($name:ident, $kind:ident) => {
20        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
21        pub struct $name {
22            syntax: SyntaxNode,
23        }
24        impl AstNode for $name {
25            fn can_cast(kind: SyntaxKind) -> bool {
26                kind == SyntaxKind::$kind
27            }
28            fn cast(node: SyntaxNode) -> Option<Self> {
29                if node.kind() == SyntaxKind::$kind {
30                    Some(Self { syntax: node })
31                } else {
32                    None
33                }
34            }
35            fn syntax(&self) -> &SyntaxNode {
36                &self.syntax
37            }
38        }
39    };
40}
41
42ast_node!(SourceFile, SOURCE_FILE);
43ast_node!(SelectStmt, SELECT_STMT);
44ast_node!(SelectList, SELECT_LIST);
45ast_node!(SelectItem, SELECT_ITEM);
46ast_node!(FromClause, FROM_CLAUSE);
47ast_node!(WhereClause, WHERE_CLAUSE);
48
49impl SourceFile {
50    /// All top-level statement nodes (e.g. `SELECT_STMT`, `EXPR_STMT`).
51    pub fn statements(&self) -> impl Iterator<Item = SyntaxNode> + '_ {
52        self.syntax.children()
53    }
54}
55
56impl SelectStmt {
57    pub fn select_list(&self) -> Option<SelectList> {
58        self.syntax.children().find_map(SelectList::cast)
59    }
60    pub fn from_clause(&self) -> Option<FromClause> {
61        self.syntax.children().find_map(FromClause::cast)
62    }
63    pub fn where_clause(&self) -> Option<WhereClause> {
64        self.syntax.children().find_map(WhereClause::cast)
65    }
66}
67
68impl SelectList {
69    pub fn items(&self) -> impl Iterator<Item = SelectItem> + '_ {
70        self.syntax.children().filter_map(SelectItem::cast)
71    }
72}