cedar_policy_core/parser/
node.rs

1/*
2 * Copyright 2022-2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use 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/// Describes where in policy source code a node in the CST or expression AST
27/// occurs.
28#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
29pub struct SourceInfo(pub Range<usize>);
30
31impl SourceInfo {
32    /// Construct a new [`SourceInfo`] from a start offset and a length, in
33    /// bytes.
34    pub const fn new(start: usize, len: usize) -> Self {
35        SourceInfo(start..(start + len))
36    }
37
38    /// Construct a new zero-length [`SourceInfo`] pointing to a specific
39    /// offset.
40    pub const fn from_offset(offset: usize) -> Self {
41        SourceInfo(offset..offset)
42    }
43
44    /// Get the start of range, in bytes.
45    pub const fn range_start(&self) -> usize {
46        self.0.start
47    }
48
49    /// Get the end of range, in bytes.
50    pub const fn range_end(&self) -> usize {
51        self.0.end
52    }
53
54    /// Get the length of the source range, in bytes.
55    ///
56    /// # Panics
57    ///
58    /// Panics if the end of the range is before the start.
59    pub const fn len(&self) -> usize {
60        assert!(self.range_start() <= self.range_end());
61        self.range_end() - self.range_start()
62    }
63
64    /// Tests whether this [`SourceInfo`] range is a zero-length offset.
65    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/// Metadata for our syntax trees
113#[derive(Clone, Deserialize, Serialize)]
114pub struct ASTNode<N> {
115    /// Main data represented
116    pub node: N,
117
118    /// additional information
119    pub info: SourceInfo,
120}
121
122impl<N> ASTNode<N> {
123    /// Create a new Node from main data
124    pub fn new(node: N, left: usize, right: usize) -> Self {
125        ASTNode::from_source(left..right, node)
126    }
127
128    /// Create a new Node from main data
129    pub fn from_source(info: impl Into<SourceInfo>, node: N) -> Self {
130        ASTNode {
131            node,
132            info: info.into(),
133        }
134    }
135
136    /// Transform the inner value while retaining the attached source info.
137    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    /// Converts from `&ASTNode<N>` to `ASTNode<&N>`.
145    pub fn as_ref(&self) -> ASTNode<&N> {
146        ASTNode {
147            node: &self.node,
148            info: self.info.clone(),
149        }
150    }
151
152    /// Converts from `&mut ASTNode<N>` to `ASTNode<&mut N>`.
153    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    /// Consume the `ASTNode`, yielding the node and attached source info.
161    pub fn into_inner(self) -> (N, SourceInfo) {
162        (self.node, self.info)
163    }
164}
165
166impl<N: Clone> ASTNode<&N> {
167    /// Converts a `ASTNode<&N>` to a `ASTNode<N>` by cloning the inner value.
168    pub fn cloned(self) -> ASTNode<N> {
169        self.map(|value| value.clone())
170    }
171}
172
173impl<N: Copy> ASTNode<&N> {
174    /// Converts a `ASTNode<&N>` to a `ASTNode<N>` by copying the inner value.
175    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
243// Ignore the metadata this node contains
244impl<N: PartialEq> PartialEq for ASTNode<N> {
245    /// ignores metadata
246    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    /// ignores metadata
253    fn hash<H: Hasher>(&self, state: &mut H) {
254        self.node.hash(state);
255    }
256}
257
258/// Convenience methods on `ASTNode<Option<N>>`
259impl<N> ASTNode<Option<N>> {
260    /// Similar to `.as_inner()`, but also gives access to the `SourceInfo`
261    pub fn as_inner_pair(&self) -> (&SourceInfo, Option<&N>) {
262        (&self.info, self.node.as_ref())
263    }
264
265    /// Get the inner data as `&N`, if it exists
266    pub fn as_inner(&self) -> Option<&N> {
267        self.node.as_ref()
268    }
269
270    /// `None` if the node is empty, otherwise a node without the `Option`
271    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    /// Apply the function `f` to the main data and source info. Returns `None`
279    /// if no main data or if `f` returns `None`.
280    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    /// Apply the function `f` to the main data and source info, consuming them.
288    /// Returns `None` if no main data or if `f` returns `None`.
289    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}