cedar_policy_validator/cedar_schema/
ast.rs1use std::{collections::BTreeMap, iter::once};
18
19use cedar_policy_core::{
20 ast::{Annotation, Annotations, AnyId, Id, InternalName},
21 parser::{Loc, Node},
22};
23use itertools::{Either, Itertools};
24use nonempty::NonEmpty;
25use smol_str::SmolStr;
26#[allow(unused_imports)]
28use smol_str::ToSmolStr;
29
30use crate::json_schema;
31
32use super::err::UserError;
33
34pub const BUILTIN_TYPES: [&str; 3] = ["Long", "String", "Bool"];
35
36pub(super) const CEDAR_NAMESPACE: &str = "__cedar";
37
38#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
40pub struct Annotated<T> {
41 pub data: T,
43 pub annotations: Annotations,
45}
46
47pub type Schema = Vec<Annotated<Namespace>>;
48
49#[allow(clippy::type_complexity)]
50pub fn deduplicate_annotations<T>(
51 data: T,
52 annotations: Vec<Node<(Node<AnyId>, Option<Node<SmolStr>>)>>,
53) -> Result<Annotated<T>, UserError> {
54 let mut unique_annotations: BTreeMap<Node<AnyId>, Option<Node<SmolStr>>> = BTreeMap::new();
55 for annotation in annotations {
56 let (key, value) = annotation.node;
57 if let Some((old_key, _)) = unique_annotations.get_key_value(&key) {
58 return Err(UserError::DuplicateAnnotations(
59 key.node,
60 Node::with_source_loc((), old_key.loc.clone()),
61 Node::with_source_loc((), key.loc),
62 ));
63 } else {
64 unique_annotations.insert(key, value);
65 }
66 }
67 Ok(Annotated {
68 data,
69 annotations: unique_annotations
70 .into_iter()
71 .map(|(key, value)| {
72 let (val, loc) = match value {
73 Some(n) => (Some(n.node), Some(n.loc)),
74 None => (None, None),
75 };
76 (key.node, Annotation::with_optional_value(val, loc))
77 })
78 .collect(),
79 })
80}
81
82#[derive(Debug, Clone, PartialEq, Eq, Hash)]
84pub struct Path(Node<PathInternal>);
85impl Path {
86 pub fn single(basename: Id, loc: Loc) -> Self {
88 Self(Node::with_source_loc(
89 PathInternal {
90 basename,
91 namespace: vec![],
92 },
93 loc,
94 ))
95 }
96
97 pub fn new(basename: Id, namespace: impl IntoIterator<Item = Id>, loc: Loc) -> Self {
99 let namespace = namespace.into_iter().collect();
100 Self(Node::with_source_loc(
101 PathInternal {
102 basename,
103 namespace,
104 },
105 loc,
106 ))
107 }
108
109 pub fn iter(&self) -> impl Iterator<Item = &Id> {
111 self.0.node.iter()
112 }
113
114 pub fn loc(&self) -> &Loc {
116 &self.0.loc
117 }
118
119 #[allow(clippy::should_implement_trait)] pub fn into_iter(self) -> impl Iterator<Item = Node<Id>> {
122 let loc = self.0.loc;
123 self.0
124 .node
125 .into_iter()
126 .map(move |x| Node::with_source_loc(x, loc.clone()))
127 }
128
129 pub fn split_last(self) -> (Vec<Id>, Id) {
131 (self.0.node.namespace, self.0.node.basename)
132 }
133
134 pub fn is_in_cedar(&self) -> bool {
136 self.0.node.is_in_cedar()
137 }
138}
139
140impl From<Path> for InternalName {
141 fn from(value: Path) -> Self {
142 InternalName::new(
143 value.0.node.basename,
144 value.0.node.namespace,
145 Some(value.0.loc),
146 )
147 }
148}
149
150impl std::fmt::Display for Path {
151 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152 write!(f, "{}", self.0.node)
153 }
154}
155
156#[derive(Debug, Clone, PartialEq, Eq, Hash)]
157struct PathInternal {
158 basename: Id,
159 namespace: Vec<Id>,
160}
161
162impl PathInternal {
163 fn iter(&self) -> impl Iterator<Item = &Id> {
164 self.namespace.iter().chain(once(&self.basename))
165 }
166
167 fn is_in_cedar(&self) -> bool {
169 match self.namespace.as_slice() {
170 [id] => id.as_ref() == CEDAR_NAMESPACE,
171 _ => false,
172 }
173 }
174}
175
176impl IntoIterator for PathInternal {
177 type Item = Id;
178 type IntoIter = std::iter::Chain<<Vec<Id> as IntoIterator>::IntoIter, std::iter::Once<Id>>;
179
180 fn into_iter(self) -> Self::IntoIter {
181 self.namespace.into_iter().chain(once(self.basename))
182 }
183}
184
185impl std::fmt::Display for PathInternal {
186 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187 if self.namespace.is_empty() {
188 write!(f, "{}", self.basename)
189 } else {
190 let namespace = self.namespace.iter().map(|id| id.as_ref()).join("::");
191 write!(f, "{namespace}::{}", self.basename)
192 }
193 }
194}
195
196#[derive(Debug, Clone)]
198pub struct QualName {
199 pub path: Option<Path>,
200 pub eid: SmolStr,
201}
202
203impl QualName {
204 pub fn unqualified(eid: SmolStr) -> Self {
205 Self { path: None, eid }
206 }
207
208 pub fn qualified(path: Path, eid: SmolStr) -> Self {
209 Self {
210 path: Some(path),
211 eid,
212 }
213 }
214}
215
216#[derive(Debug, Clone)]
220pub struct Namespace {
221 pub name: Option<Path>,
223 pub decls: Vec<Annotated<Node<Declaration>>>,
225 pub loc: Option<Loc>,
226}
227
228impl Namespace {
229 pub fn is_unqualified(&self) -> bool {
231 self.name.is_none()
232 }
233}
234
235pub trait Decl {
236 fn names(&self) -> Vec<Node<SmolStr>>;
237}
238
239#[derive(Debug, Clone)]
242pub enum Declaration {
243 Entity(EntityDecl),
244 Action(ActionDecl),
245 Type(TypeDecl),
246}
247
248#[derive(Debug, Clone)]
249pub struct TypeDecl {
250 pub name: Node<Id>,
251 pub def: Node<Type>,
252}
253
254impl Decl for TypeDecl {
255 fn names(&self) -> Vec<Node<SmolStr>> {
256 vec![self.name.clone().map(|id| id.to_smolstr())]
257 }
258}
259
260#[derive(Debug, Clone)]
261pub enum EntityDecl {
262 Standard(StandardEntityDecl),
263 Enum(EnumEntityDecl),
264}
265
266impl EntityDecl {
267 pub fn names(&self) -> impl Iterator<Item = &Node<Id>> + '_ {
268 match self {
269 Self::Enum(d) => d.names.iter(),
270 Self::Standard(d) => d.names.iter(),
271 }
272 }
273}
274
275#[derive(Debug, Clone)]
277pub struct StandardEntityDecl {
278 pub names: NonEmpty<Node<Id>>,
281 pub member_of_types: Vec<Path>,
283 pub attrs: Node<Vec<Node<Annotated<AttrDecl>>>>,
285 pub tags: Option<Node<Type>>,
287}
288
289#[derive(Debug, Clone)]
291pub struct EnumEntityDecl {
292 pub names: NonEmpty<Node<Id>>,
293 pub choices: NonEmpty<Node<SmolStr>>,
294}
295
296#[derive(Debug, Clone)]
298pub enum Type {
299 Set(Box<Node<Type>>),
301 Ident(Path),
303 Record(Vec<Node<Annotated<AttrDecl>>>),
305}
306
307#[derive(Debug, Clone)]
309pub enum PrimitiveType {
310 Long,
312 String,
314 Bool,
316}
317
318impl<N> From<PrimitiveType> for json_schema::TypeVariant<N> {
319 fn from(value: PrimitiveType) -> Self {
320 match value {
321 PrimitiveType::Long => json_schema::TypeVariant::Long,
322 PrimitiveType::String => json_schema::TypeVariant::String,
323 PrimitiveType::Bool => json_schema::TypeVariant::Boolean,
324 }
325 }
326}
327
328#[derive(Debug, Clone)]
331pub struct AttrDecl {
332 pub name: Node<SmolStr>,
334 pub required: bool,
336 pub ty: Node<Type>,
338}
339
340#[derive(Debug, Clone, Copy, PartialEq, Eq)]
342pub enum PR {
343 Principal,
345 Resource,
347}
348
349impl std::fmt::Display for PR {
350 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
351 match self {
352 PR::Principal => write!(f, "principal"),
353 PR::Resource => write!(f, "resource"),
354 }
355 }
356}
357
358#[derive(Debug, Clone)]
360pub struct PRAppDecl {
361 pub kind: Node<PR>,
363 pub entity_tys: Option<NonEmpty<Path>>,
365}
366
367#[derive(Debug, Clone)]
369pub enum AppDecl {
370 PR(PRAppDecl),
372 Context(Either<Path, Node<Vec<Node<Annotated<AttrDecl>>>>>),
374}
375
376#[derive(Debug, Clone)]
378pub struct ActionDecl {
379 pub names: NonEmpty<Node<SmolStr>>,
382 pub parents: Option<NonEmpty<Node<QualName>>>,
384 pub app_decls: Option<Node<NonEmpty<Node<AppDecl>>>>,
386}
387
388impl Decl for ActionDecl {
389 fn names(&self) -> Vec<Node<SmolStr>> {
390 self.names.iter().cloned().collect()
391 }
392}
393
394#[cfg(test)]
395mod test {
396 use std::sync::Arc;
397
398 use super::*;
399
400 fn loc() -> Loc {
401 Loc::new((1, 1), Arc::from("foo"))
402 }
403
404 #[test]
406 fn path_iter() {
407 let p = Path::new(
408 "baz".parse().unwrap(),
409 ["foo".parse().unwrap(), "bar".parse().unwrap()],
410 loc(),
411 );
412
413 let expected: Vec<Id> = vec![
414 "foo".parse().unwrap(),
415 "bar".parse().unwrap(),
416 "baz".parse().unwrap(),
417 ];
418
419 let expected_borrowed = expected.iter().collect::<Vec<_>>();
420
421 let borrowed = p.iter().collect::<Vec<_>>();
422 assert_eq!(borrowed, expected_borrowed);
423 let moved = p.into_iter().map(|n| n.node).collect::<Vec<_>>();
424 assert_eq!(moved, expected);
425 }
426}