cedar_policy_core/parser/
node.rs1use std::fmt::{self, Debug, Display};
18
19use educe::Educe;
20use miette::Diagnostic;
21
22use super::err::{ToASTError, ToASTErrorKind};
23use super::Loc;
24
25#[derive(Educe, Debug, Clone)]
27#[educe(PartialEq, Eq, Hash, Ord, PartialOrd)]
28pub struct Node<T> {
29 pub node: T,
31
32 #[educe(PartialEq(ignore))]
34 #[educe(PartialOrd(ignore))]
35 #[educe(Hash(ignore))]
36 pub loc: Option<Loc>,
37}
38
39impl<T> Node<T> {
40 pub fn with_source_loc(node: T, loc: Loc) -> Self {
42 Node {
43 node,
44 loc: Some(loc),
45 }
46 }
47
48 pub fn with_maybe_source_loc(node: T, loc: Option<Loc>) -> Self {
50 Node { node, loc }
51 }
52
53 pub fn new(node: T) -> Self {
55 Node { node, loc: None }
56 }
57
58 pub fn map<R>(self, f: impl FnOnce(T) -> R) -> Node<R> {
60 Node {
61 node: f(self.node),
62 loc: self.loc,
63 }
64 }
65
66 pub fn as_ref(&self) -> Node<&T> {
68 Node {
69 node: &self.node,
70 loc: self.loc.clone(),
71 }
72 }
73
74 pub fn as_mut(&mut self) -> Node<&mut T> {
76 Node {
77 node: &mut self.node,
78 loc: self.loc.clone(),
79 }
80 }
81
82 pub fn into_inner(self) -> (T, Option<Loc>) {
84 (self.node, self.loc)
85 }
86
87 pub fn to_ast_err(&self, error_kind: impl Into<ToASTErrorKind>) -> ToASTError {
89 ToASTError::new(error_kind.into(), self.loc.clone())
90 }
91
92 pub fn loc(&self) -> Option<&Loc> {
94 self.loc.as_ref()
95 }
96}
97
98impl<T: Clone> Node<&T> {
99 pub fn cloned(self) -> Node<T> {
101 self.map(|value| value.clone())
102 }
103}
104
105impl<T: Copy> Node<&T> {
106 pub fn copied(self) -> Node<T> {
108 self.map(|value| *value)
109 }
110}
111
112impl<T: Display> Display for Node<T> {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 Display::fmt(&self.node, f)
115 }
116}
117
118impl<T: std::error::Error> std::error::Error for Node<T> {
119 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
120 self.node.source()
121 }
122
123 fn description(&self) -> &str {
124 #[expect(
125 deprecated,
126 reason = "description() is deprecated but we still want to forward it"
127 )]
128 self.node.description()
129 }
130
131 fn cause(&self) -> Option<&dyn std::error::Error> {
132 #[expect(
133 deprecated,
134 reason = "cause() is deprecated but we still want to forward it"
135 )]
136 self.node.cause()
137 }
138}
139
140impl<T: Diagnostic> Diagnostic for Node<T> {
142 impl_diagnostic_from_source_loc_opt_field!(loc);
143
144 fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
145 self.node.code()
146 }
147
148 fn severity(&self) -> Option<miette::Severity> {
149 self.node.severity()
150 }
151
152 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
153 self.node.help()
154 }
155
156 fn url<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
157 self.node.url()
158 }
159
160 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
161 self.node.related()
162 }
163
164 fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
165 self.node.diagnostic_source()
166 }
167}
168
169impl<T> Node<Option<T>> {
171 pub fn as_inner(&self) -> Option<&T> {
173 self.node.as_ref()
174 }
175
176 pub fn collapse(&self) -> Option<Node<&T>> {
178 self.node.as_ref().map(|node| Node {
179 node,
180 loc: self.loc.clone(),
181 })
182 }
183
184 pub fn apply<F, R>(&self, f: F) -> Option<R>
187 where
188 F: FnOnce(&T, Option<&Loc>) -> Option<R>,
189 {
190 f(self.node.as_ref()?, self.loc.as_ref())
191 }
192
193 pub fn into_apply<F, R>(self, f: F) -> Option<R>
196 where
197 F: FnOnce(T, Option<Loc>) -> Option<R>,
198 {
199 f(self.node?, self.loc)
200 }
201
202 pub fn try_as_inner(&self) -> Result<&T, ToASTError> {
204 self.node
205 .as_ref()
206 .ok_or_else(|| self.to_ast_err(ToASTErrorKind::EmptyNodeInvariantViolation))
207 }
208
209 pub fn try_into_inner(self) -> Result<T, ToASTError> {
211 self.node
212 .ok_or_else(|| ToASTError::new(ToASTErrorKind::EmptyNodeInvariantViolation, self.loc))
213 }
214}