cedar_policy_core/parser/
node.rs1use std::cmp::Ordering;
18use std::error::Error;
19use std::fmt::{self, Debug, Display};
20use std::hash::{Hash, Hasher};
21use std::ops::Range;
22
23use miette::{Diagnostic, LabeledSpan, Severity, SourceCode};
24use serde::{Deserialize, Serialize};
25
26#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
29pub struct SourceInfo(pub Range<usize>);
30
31impl SourceInfo {
32 pub const fn new(start: usize, len: usize) -> Self {
35 SourceInfo(start..(start + len))
36 }
37
38 pub const fn from_offset(offset: usize) -> Self {
41 SourceInfo(offset..offset)
42 }
43
44 pub const fn range_start(&self) -> usize {
46 self.0.start
47 }
48
49 pub const fn range_end(&self) -> usize {
51 self.0.end
52 }
53
54 pub const fn len(&self) -> usize {
60 assert!(self.range_start() <= self.range_end());
61 self.range_end() - self.range_start()
62 }
63
64 pub const fn is_empty(&self) -> bool {
66 self.len() == 0
67 }
68}
69
70impl Display for SourceInfo {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 if self.is_empty() {
73 write!(f, "{}", self.range_start())
74 } else {
75 write!(f, "[{}, {})", self.range_start(), self.range_end())
76 }
77 }
78}
79
80impl Ord for SourceInfo {
81 fn cmp(&self, other: &Self) -> Ordering {
82 self.range_start()
83 .cmp(&other.range_start())
84 .then_with(|| self.len().cmp(&other.len()))
85 }
86}
87
88impl PartialOrd for SourceInfo {
89 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
90 Some(self.cmp(other))
91 }
92}
93
94impl From<usize> for SourceInfo {
95 fn from(offset: usize) -> Self {
96 SourceInfo::from_offset(offset)
97 }
98}
99
100impl From<Range<usize>> for SourceInfo {
101 fn from(range: Range<usize>) -> Self {
102 SourceInfo(range)
103 }
104}
105
106impl From<SourceInfo> for Range<usize> {
107 fn from(info: SourceInfo) -> Self {
108 info.0
109 }
110}
111
112#[derive(Clone, Deserialize, Serialize)]
114pub struct ASTNode<N> {
115 pub node: N,
117
118 pub info: SourceInfo,
120}
121
122impl<N> ASTNode<N> {
123 pub fn new(node: N, left: usize, right: usize) -> Self {
125 ASTNode::from_source(left..right, node)
126 }
127
128 pub fn from_source(info: impl Into<SourceInfo>, node: N) -> Self {
130 ASTNode {
131 node,
132 info: info.into(),
133 }
134 }
135
136 pub fn map<M>(self, f: impl FnOnce(N) -> M) -> ASTNode<M> {
138 ASTNode {
139 node: f(self.node),
140 info: self.info,
141 }
142 }
143
144 pub fn as_ref(&self) -> ASTNode<&N> {
146 ASTNode {
147 node: &self.node,
148 info: self.info.clone(),
149 }
150 }
151
152 pub fn as_mut(&mut self) -> ASTNode<&mut N> {
154 ASTNode {
155 node: &mut self.node,
156 info: self.info.clone(),
157 }
158 }
159
160 pub fn into_inner(self) -> (N, SourceInfo) {
162 (self.node, self.info)
163 }
164}
165
166impl<N: Clone> ASTNode<&N> {
167 pub fn cloned(self) -> ASTNode<N> {
169 self.map(|value| value.clone())
170 }
171}
172
173impl<N: Copy> ASTNode<&N> {
174 pub fn copied(self) -> ASTNode<N> {
176 self.map(|value| *value)
177 }
178}
179
180impl<N: Debug> Debug for ASTNode<N> {
181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182 Debug::fmt(&self.node, f)?;
183 write!(f, " @ {}", self.info)
184 }
185}
186
187impl<N: Display> Display for ASTNode<N> {
188 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189 Display::fmt(&self.node, f)
190 }
191}
192
193impl<N: Error> Error for ASTNode<N> {
194 fn source(&self) -> Option<&(dyn Error + 'static)> {
195 self.node.source()
196 }
197
198 #[allow(deprecated)]
199 fn description(&self) -> &str {
200 self.node.description()
201 }
202
203 fn cause(&self) -> Option<&dyn Error> {
204 #[allow(deprecated)]
205 self.node.cause()
206 }
207}
208
209impl<N: Diagnostic> Diagnostic for ASTNode<N> {
210 fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
211 self.node.code()
212 }
213
214 fn severity(&self) -> Option<Severity> {
215 self.node.severity()
216 }
217
218 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
219 self.node.help()
220 }
221
222 fn url<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
223 self.node.url()
224 }
225
226 fn source_code(&self) -> Option<&dyn SourceCode> {
227 self.node.source_code()
228 }
229
230 fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
231 self.node.labels()
232 }
233
234 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
235 self.node.related()
236 }
237
238 fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
239 self.node.diagnostic_source()
240 }
241}
242
243impl<N: PartialEq> PartialEq for ASTNode<N> {
245 fn eq(&self, other: &Self) -> bool {
247 self.node == other.node
248 }
249}
250impl<N: Eq> Eq for ASTNode<N> {}
251impl<N: Hash> Hash for ASTNode<N> {
252 fn hash<H: Hasher>(&self, state: &mut H) {
254 self.node.hash(state);
255 }
256}
257
258impl<N> ASTNode<Option<N>> {
260 pub fn as_inner_pair(&self) -> (&SourceInfo, Option<&N>) {
262 (&self.info, self.node.as_ref())
263 }
264
265 pub fn as_inner(&self) -> Option<&N> {
267 self.node.as_ref()
268 }
269
270 pub fn collapse(&self) -> Option<ASTNode<&N>> {
272 self.node.as_ref().map(|node| ASTNode {
273 node,
274 info: self.info.clone(),
275 })
276 }
277
278 pub fn apply<F, R>(&self, f: F) -> Option<R>
281 where
282 F: FnOnce(&N, &SourceInfo) -> Option<R>,
283 {
284 f(self.node.as_ref()?, &self.info)
285 }
286
287 pub fn into_apply<F, R>(self, f: F) -> Option<R>
290 where
291 F: FnOnce(N, SourceInfo) -> Option<R>,
292 {
293 f(self.node?, self.info)
294 }
295}