cedar_policy_core/parser/
node.rs

1/*
2 * Copyright Cedar Contributors
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::fmt::{self, Debug, Display};
18
19use educe::Educe;
20use miette::Diagnostic;
21
22use super::err::{ToASTError, ToASTErrorKind};
23use super::loc::Loc;
24
25/// Metadata for our syntax trees
26#[derive(Educe, Debug, Clone)]
27#[educe(PartialEq, Eq, Hash, Ord, PartialOrd)]
28pub struct Node<T> {
29    /// Main data represented
30    pub node: T,
31
32    /// Source location
33    #[educe(PartialEq(ignore))]
34    #[educe(PartialOrd(ignore))]
35    #[educe(Hash(ignore))]
36    pub loc: Loc,
37}
38
39impl<T> Node<T> {
40    /// Create a new Node with the given source location
41    pub fn with_source_loc(node: T, loc: Loc) -> Self {
42        Node { node, loc }
43    }
44
45    /// Transform the inner value while retaining the attached source info.
46    pub fn map<R>(self, f: impl FnOnce(T) -> R) -> Node<R> {
47        Node {
48            node: f(self.node),
49            loc: self.loc,
50        }
51    }
52
53    /// Converts from `&Node<T>` to `Node<&T>`.
54    pub fn as_ref(&self) -> Node<&T> {
55        Node {
56            node: &self.node,
57            loc: self.loc.clone(),
58        }
59    }
60
61    /// Converts from `&mut Node<T>` to `Node<&mut T>`.
62    pub fn as_mut(&mut self) -> Node<&mut T> {
63        Node {
64            node: &mut self.node,
65            loc: self.loc.clone(),
66        }
67    }
68
69    /// Consume the `Node`, yielding the node and attached source info.
70    pub fn into_inner(self) -> (T, Loc) {
71        (self.node, self.loc)
72    }
73
74    /// Utility to construct a `ToAstError` with the source location taken from this node.
75    pub fn to_ast_err(&self, error_kind: impl Into<ToASTErrorKind>) -> ToASTError {
76        ToASTError::new(error_kind.into(), self.loc.clone())
77    }
78}
79
80impl<T: Clone> Node<&T> {
81    /// Converts a `Node<&T>` to a `Node<T>` by cloning the inner value.
82    pub fn cloned(self) -> Node<T> {
83        self.map(|value| value.clone())
84    }
85}
86
87impl<T: Copy> Node<&T> {
88    /// Converts a `Node<&T>` to a `Node<T>` by copying the inner value.
89    pub fn copied(self) -> Node<T> {
90        self.map(|value| *value)
91    }
92}
93
94impl<T: Display> Display for Node<T> {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        Display::fmt(&self.node, f)
97    }
98}
99
100impl<T: std::error::Error> std::error::Error for Node<T> {
101    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
102        self.node.source()
103    }
104
105    #[allow(deprecated)]
106    fn description(&self) -> &str {
107        self.node.description()
108    }
109
110    fn cause(&self) -> Option<&dyn std::error::Error> {
111        #[allow(deprecated)]
112        self.node.cause()
113    }
114}
115
116// impl Diagnostic by taking `labels()` and `source_code()` from .loc and everything else from .node
117impl<T: Diagnostic> Diagnostic for Node<T> {
118    impl_diagnostic_from_source_loc_field!(loc);
119
120    fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
121        self.node.code()
122    }
123
124    fn severity(&self) -> Option<miette::Severity> {
125        self.node.severity()
126    }
127
128    fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
129        self.node.help()
130    }
131
132    fn url<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
133        self.node.url()
134    }
135
136    fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
137        self.node.related()
138    }
139
140    fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
141        self.node.diagnostic_source()
142    }
143}
144
145/// Convenience methods on `Node<Option<T>>`
146impl<T> Node<Option<T>> {
147    /// Get the inner data as `&T`, if it exists
148    pub fn as_inner(&self) -> Option<&T> {
149        self.node.as_ref()
150    }
151
152    /// `None` if the node is empty, otherwise a node without the `Option`
153    pub fn collapse(&self) -> Option<Node<&T>> {
154        self.node.as_ref().map(|node| Node {
155            node,
156            loc: self.loc.clone(),
157        })
158    }
159
160    /// Apply the function `f` to the main data and source info. Returns `None`
161    /// if no main data or if `f` returns `None`.
162    pub fn apply<F, R>(&self, f: F) -> Option<R>
163    where
164        F: FnOnce(&T, &Loc) -> Option<R>,
165    {
166        f(self.node.as_ref()?, &self.loc)
167    }
168
169    /// Apply the function `f` to the main data and `Loc`, consuming them.
170    /// Returns `None` if no main data or if `f` returns `None`.
171    pub fn into_apply<F, R>(self, f: F) -> Option<R>
172    where
173        F: FnOnce(T, Loc) -> Option<R>,
174    {
175        f(self.node?, self.loc)
176    }
177
178    /// Get node data if present or return the error `EmptyNodeInvariantViolation`
179    pub fn try_as_inner(&self) -> Result<&T, ToASTError> {
180        self.node
181            .as_ref()
182            .ok_or_else(|| self.to_ast_err(ToASTErrorKind::EmptyNodeInvariantViolation))
183    }
184
185    /// Get node data if present or return the error `EmptyNodeInvariantViolation`
186    pub fn try_into_inner(self) -> Result<T, ToASTError> {
187        self.node.ok_or_else(|| {
188            ToASTError::new(
189                ToASTErrorKind::EmptyNodeInvariantViolation,
190                self.loc.clone(),
191            )
192        })
193    }
194}