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