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