1use std::{
39 hash::{Hash, Hasher},
40 marker::PhantomData,
41};
42
43use rowan::TextRange;
44
45use crate::{AstNode, SyntaxNode, syntax_node::Sql};
46
47pub type SyntaxNodePtr = rowan::ast::SyntaxNodePtr<Sql>;
49
50pub struct AstPtr<N: AstNode> {
52 raw: SyntaxNodePtr,
53 _ty: PhantomData<fn() -> N>,
54}
55
56impl<N: AstNode> std::fmt::Debug for AstPtr<N> {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 f.debug_tuple("AstPtr").field(&self.raw).finish()
59 }
60}
61
62impl<N: AstNode> Copy for AstPtr<N> {}
63impl<N: AstNode> Clone for AstPtr<N> {
64 fn clone(&self) -> AstPtr<N> {
65 *self
66 }
67}
68
69impl<N: AstNode> Eq for AstPtr<N> {}
70
71impl<N: AstNode> PartialEq for AstPtr<N> {
72 fn eq(&self, other: &AstPtr<N>) -> bool {
73 self.raw == other.raw
74 }
75}
76
77impl<N: AstNode> Hash for AstPtr<N> {
78 fn hash<H: Hasher>(&self, state: &mut H) {
79 self.raw.hash(state);
80 }
81}
82
83impl<N: AstNode> AstPtr<N> {
84 pub fn new(node: &N) -> AstPtr<N> {
85 AstPtr {
86 raw: SyntaxNodePtr::new(node.syntax()),
87 _ty: PhantomData,
88 }
89 }
90
91 pub fn to_node(&self, root: &SyntaxNode) -> N {
92 let syntax_node = self.raw.to_node(root);
93 N::cast(syntax_node).unwrap()
94 }
95
96 pub fn syntax_node_ptr(&self) -> SyntaxNodePtr {
97 self.raw
98 }
99
100 pub fn text_range(&self) -> TextRange {
101 self.raw.text_range()
102 }
103
104 pub fn cast<U: AstNode>(self) -> Option<AstPtr<U>> {
105 if !U::can_cast(self.raw.kind()) {
106 return None;
107 }
108 Some(AstPtr {
109 raw: self.raw,
110 _ty: PhantomData,
111 })
112 }
113
114 pub fn kind(&self) -> squawk_parser::SyntaxKind {
115 self.raw.kind()
116 }
117
118 pub fn upcast<M: AstNode>(self) -> AstPtr<M>
119 where
120 N: Into<M>,
121 {
122 AstPtr {
123 raw: self.raw,
124 _ty: PhantomData,
125 }
126 }
127
128 pub fn try_from_raw(raw: SyntaxNodePtr) -> Option<AstPtr<N>> {
130 N::can_cast(raw.kind()).then_some(AstPtr {
131 raw,
132 _ty: PhantomData,
133 })
134 }
135
136 pub fn wrap_left<R>(self) -> AstPtr<either::Either<N, R>>
137 where
138 either::Either<N, R>: AstNode,
139 {
140 AstPtr {
141 raw: self.raw,
142 _ty: PhantomData,
143 }
144 }
145
146 pub fn wrap_right<L>(self) -> AstPtr<either::Either<L, N>>
147 where
148 either::Either<L, N>: AstNode,
149 {
150 AstPtr {
151 raw: self.raw,
152 _ty: PhantomData,
153 }
154 }
155}
156
157impl<N: AstNode> From<AstPtr<N>> for SyntaxNodePtr {
158 fn from(ptr: AstPtr<N>) -> SyntaxNodePtr {
159 ptr.raw
160 }
161}
162
163#[test]
164fn test_local_syntax_ptr() {
165 use crate::{AstNode, ast};
166
167 let file = ast::SourceFile::parse("create table t(c int8);")
168 .ok()
169 .unwrap();
170 let field = file
171 .syntax()
172 .descendants()
173 .find_map(ast::Column::cast)
174 .unwrap();
175 let ptr = SyntaxNodePtr::new(field.syntax());
176 let field_syntax = ptr.to_node(file.syntax());
177 assert_eq!(field.syntax(), &field_syntax);
178}