cedar_policy_core/parser/node.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
/*
* Copyright Cedar Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use std::fmt::{self, Debug, Display};
use std::hash::{Hash, Hasher};
use miette::Diagnostic;
use serde::{Deserialize, Serialize};
use super::err::{ToASTError, ToASTErrorKind};
use super::loc::Loc;
/// Metadata for our syntax trees
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Node<T> {
/// Main data represented
pub node: T,
/// Source location
pub loc: Loc,
}
impl<T> Node<T> {
/// Create a new Node with the given source location
pub fn with_source_loc(node: T, loc: Loc) -> Self {
Node { node, loc }
}
/// Transform the inner value while retaining the attached source info.
pub fn map<R>(self, f: impl FnOnce(T) -> R) -> Node<R> {
Node {
node: f(self.node),
loc: self.loc.clone(),
}
}
/// Converts from `&Node<T>` to `Node<&T>`.
pub fn as_ref(&self) -> Node<&T> {
Node {
node: &self.node,
loc: self.loc.clone(),
}
}
/// Converts from `&mut Node<T>` to `Node<&mut T>`.
pub fn as_mut(&mut self) -> Node<&mut T> {
Node {
node: &mut self.node,
loc: self.loc.clone(),
}
}
/// Consume the `Node`, yielding the node and attached source info.
pub fn into_inner(self) -> (T, Loc) {
(self.node, self.loc)
}
/// Utility to construct a `ToAstError` with the source location taken from this node.
pub fn to_ast_err(&self, error_kind: impl Into<ToASTErrorKind>) -> ToASTError {
ToASTError::new(error_kind.into(), self.loc.clone())
}
}
impl<T: Clone> Node<&T> {
/// Converts a `Node<&T>` to a `Node<T>` by cloning the inner value.
pub fn cloned(self) -> Node<T> {
self.map(|value| value.clone())
}
}
impl<T: Copy> Node<&T> {
/// Converts a `Node<&T>` to a `Node<T>` by copying the inner value.
pub fn copied(self) -> Node<T> {
self.map(|value| *value)
}
}
impl<T: Display> Display for Node<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.node, f)
}
}
impl<T: std::error::Error> std::error::Error for Node<T> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.node.source()
}
#[allow(deprecated)]
fn description(&self) -> &str {
self.node.description()
}
fn cause(&self) -> Option<&dyn std::error::Error> {
#[allow(deprecated)]
self.node.cause()
}
}
// impl Diagnostic by taking `labels()` and `source_code()` from .loc and everything else from .node
impl<T: Diagnostic> Diagnostic for Node<T> {
impl_diagnostic_from_source_loc_field!(loc);
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
self.node.code()
}
fn severity(&self) -> Option<miette::Severity> {
self.node.severity()
}
fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
self.node.help()
}
fn url<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
self.node.url()
}
fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
self.node.related()
}
fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
self.node.diagnostic_source()
}
}
// Ignore the metadata this node contains
impl<T: PartialEq> PartialEq for Node<T> {
/// ignores metadata
fn eq(&self, other: &Self) -> bool {
self.node == other.node
}
}
impl<T: Eq> Eq for Node<T> {}
impl<T: Hash> Hash for Node<T> {
/// ignores metadata
fn hash<H: Hasher>(&self, state: &mut H) {
self.node.hash(state);
}
}
/// Convenience methods on `Node<Option<T>>`
impl<T> Node<Option<T>> {
/// Get the inner data as `&T`, if it exists
pub fn as_inner(&self) -> Option<&T> {
self.node.as_ref()
}
/// `None` if the node is empty, otherwise a node without the `Option`
pub fn collapse(&self) -> Option<Node<&T>> {
self.node.as_ref().map(|node| Node {
node,
loc: self.loc.clone(),
})
}
/// Apply the function `f` to the main data and source info. Returns `None`
/// if no main data or if `f` returns `None`.
pub fn apply<F, R>(&self, f: F) -> Option<R>
where
F: FnOnce(&T, &Loc) -> Option<R>,
{
f(self.node.as_ref()?, &self.loc)
}
/// Apply the function `f` to the main data and `Loc`, consuming them.
/// Returns `None` if no main data or if `f` returns `None`.
pub fn into_apply<F, R>(self, f: F) -> Option<R>
where
F: FnOnce(T, Loc) -> Option<R>,
{
f(self.node?, self.loc)
}
/// Get node data if present or return the error `EmptyNodeInvariantViolation`
pub fn try_as_inner(&self) -> Result<&T, ToASTError> {
self.node
.as_ref()
.ok_or_else(|| self.to_ast_err(ToASTErrorKind::EmptyNodeInvariantViolation))
}
/// Get node data if present or return the error `EmptyNodeInvariantViolation`
pub fn try_into_inner(self) -> Result<T, ToASTError> {
self.node.ok_or_else(|| {
ToASTError::new(
ToASTErrorKind::EmptyNodeInvariantViolation,
self.loc.clone(),
)
})
}
}