cedar_policy_core/parser/
fmt.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, Write};
18
19use super::cst::*;
20use super::node::Node;
21
22/// Helper struct to handle non-existent nodes
23struct View<'a, T>(&'a Node<Option<T>>);
24impl<T: fmt::Display> fmt::Display for View<'_, T> {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        if let Some(n) = &self.0.as_inner() {
27            if f.alternate() {
28                write!(f, "{:#}", n)
29            } else {
30                write!(f, "{}", n)
31            }
32        } else {
33            write!(f, "[error]")
34        }
35    }
36}
37
38impl fmt::Display for Policies {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        let mut ps = self.0.iter();
41        if f.alternate() {
42            if let Some(p) = ps.next() {
43                write!(f, "{:#}", View(p))?;
44            }
45            for p in ps {
46                write!(f, "\n\n{:#}", View(p))?;
47            }
48        } else {
49            if let Some(p) = ps.next() {
50                write!(f, "{}", View(p))?;
51            }
52            for p in ps {
53                write!(f, " {}", View(p))?;
54            }
55        }
56        Ok(())
57    }
58}
59impl fmt::Display for Policy {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        let policy = match self {
62            Policy::Policy(p) => p,
63            #[cfg(feature = "tolerant-ast")]
64            Policy::PolicyError => {
65                writeln!(f, "Policy::PolicyError")?;
66                return Ok(());
67            }
68        };
69        // start with annotations
70        for anno in policy.annotations.iter() {
71            if f.alternate() {
72                // each annotation on a new line
73                writeln!(f, "{:#}", View(anno))?;
74            } else {
75                write!(f, "{} ", View(anno))?;
76            }
77        }
78        // main policy body
79        if f.alternate() {
80            write!(f, "{:#}(", View(&policy.effect))?;
81            let mut vars = policy.variables.iter();
82            // if at least one var ...
83            if let Some(v) = vars.next() {
84                // write out the first one ...
85                write!(f, "\n  {:#}", View(v))?;
86                // ... and write out the others after commas
87                for v in vars {
88                    write!(f, ",\n  {:#}", View(v))?;
89                }
90                // close up the vars
91                write!(f, "\n)")?;
92            } else {
93                // no vars: stay on the same line
94                write!(f, ")")?;
95            }
96            // include conditions on their own lines
97            for c in policy.conds.iter() {
98                write!(f, "\n{:#}", View(c))?;
99            }
100            write!(f, ";")?;
101        } else {
102            write!(f, "{}(", View(&policy.effect))?;
103            let mut vars = policy.variables.iter();
104            // if at least one var ...
105            if let Some(v) = vars.next() {
106                // write out the first one ...
107                write!(f, "{}", View(v))?;
108                // ... and write out the others after commas
109                for v in vars {
110                    write!(f, ",  {}", View(v))?;
111                }
112            }
113            write!(f, ")")?;
114
115            for c in policy.conds.iter() {
116                write!(f, " {}", View(c))?;
117            }
118            write!(f, ";")?;
119        }
120        Ok(())
121    }
122}
123
124impl fmt::Display for Annotation {
125    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126        match self.value.as_ref() {
127            Some(value) => write!(f, "@{}({})", View(&self.key), View(value)),
128            None => write!(f, "@{}", View(&self.key)),
129        }
130    }
131}
132
133impl fmt::Display for VariableDef {
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135        write!(f, "{}", View(&self.variable))?;
136        if let Some(name) = &self.unused_type_name {
137            write!(f, ": {}", View(name))?;
138        }
139        if let Some((op, expr)) = &self.ineq {
140            write!(f, " {} {}", op, View(expr))?;
141        }
142        Ok(())
143    }
144}
145impl fmt::Display for Cond {
146    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147        match self.expr.as_ref() {
148            Some(expr_ref) => {
149                if f.alternate() {
150                    write!(f, "{} {{\n  {:#}\n}}", View(&self.cond), View(expr_ref))
151                } else {
152                    write!(f, "{} {{{}}}", View(&self.cond), View(expr_ref))
153                }
154            }
155            None => write!(f, "{} {{ }}", View(&self.cond)),
156        }
157    }
158}
159impl fmt::Display for Expr {
160    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161        // let expr_opt: &_ = &*self.expr;
162        let expr = match self {
163            Expr::Expr(expr_impl) => &*expr_impl.expr,
164            #[cfg(feature = "tolerant-ast")]
165            Expr::ErrorExpr => return write!(f, "Expr::Error"),
166        };
167        match expr {
168            ExprData::Or(or) => write!(f, "{}", View(or)),
169            ExprData::If(ex1, ex2, ex3) => {
170                write!(f, "if {} then {} else {}", View(ex1), View(ex2), View(ex3))
171            }
172        }
173    }
174}
175impl fmt::Display for Or {
176    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177        write!(f, "{}", View(&self.initial))?;
178        for or in self.extended.iter() {
179            write!(f, " || {}", View(or))?;
180        }
181        Ok(())
182    }
183}
184impl fmt::Display for And {
185    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186        write!(f, "{}", View(&self.initial))?;
187        for and in self.extended.iter() {
188            write!(f, " && {}", View(and))?;
189        }
190        Ok(())
191    }
192}
193impl fmt::Display for Relation {
194    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195        match self {
196            Relation::Common { initial, extended } => {
197                write!(f, "{}", View(initial))?;
198                for (op, add) in extended.iter() {
199                    write!(f, " {} {}", op, View(add))?;
200                }
201            }
202            Relation::Has { target, field } => {
203                write!(f, "{} has {}", View(target), View(field))?;
204            }
205            Relation::Like { target, pattern } => {
206                write!(f, "{} like {}", View(target), View(pattern))?;
207            }
208            Relation::IsIn {
209                target,
210                entity_type,
211                in_entity: None,
212            } => {
213                write!(f, "{} is {}", View(target), View(entity_type))?;
214            }
215            Relation::IsIn {
216                target,
217                entity_type,
218                in_entity: Some(in_entity),
219            } => {
220                write!(
221                    f,
222                    "{} is {} in {}",
223                    View(target),
224                    View(entity_type),
225                    View(in_entity)
226                )?;
227            }
228        }
229        Ok(())
230    }
231}
232impl fmt::Display for RelOp {
233    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234        match self {
235            RelOp::Less => write!(f, "<"),
236            RelOp::LessEq => write!(f, "<="),
237            RelOp::GreaterEq => write!(f, ">="),
238            RelOp::Greater => write!(f, ">"),
239            RelOp::NotEq => write!(f, "!="),
240            RelOp::Eq => write!(f, "=="),
241            RelOp::In => write!(f, "in"),
242            RelOp::InvalidSingleEq => write!(f, "="),
243        }
244    }
245}
246impl fmt::Display for AddOp {
247    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248        match self {
249            AddOp::Plus => write!(f, "+"),
250            AddOp::Minus => write!(f, "-"),
251        }
252    }
253}
254impl fmt::Display for MultOp {
255    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256        match self {
257            MultOp::Times => write!(f, "*"),
258            MultOp::Divide => write!(f, "/"),
259            MultOp::Mod => write!(f, "%"),
260        }
261    }
262}
263impl fmt::Display for NegOp {
264    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265        match self {
266            NegOp::Bang(cnt) => {
267                for _ in 0..*cnt {
268                    write!(f, "!")?;
269                }
270            }
271            // represents too many, current parser accepts a max of 4
272            NegOp::OverBang => write!(f, "!!!!!!!!!!")?,
273            NegOp::Dash(cnt) => {
274                for _ in 0..*cnt {
275                    write!(f, "-")?;
276                }
277            }
278            // represents too many, current parser accepts a max of 4
279            NegOp::OverDash => write!(f, "----------")?,
280        }
281        Ok(())
282    }
283}
284impl fmt::Display for Add {
285    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
286        write!(f, "{}", View(&self.initial))?;
287        for (op, mult) in self.extended.iter() {
288            write!(f, " {} {}", op, View(mult))?;
289        }
290        Ok(())
291    }
292}
293impl fmt::Display for Mult {
294    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295        write!(f, "{}", View(&self.initial))?;
296        for (op, un) in self.extended.iter() {
297            write!(f, " {} {}", op, View(un))?;
298        }
299        Ok(())
300    }
301}
302impl fmt::Display for Unary {
303    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304        if let Some(op) = &self.op {
305            write!(f, "{}{}", op, View(&self.item))
306        } else {
307            write!(f, "{}", View(&self.item))
308        }
309    }
310}
311impl fmt::Display for Member {
312    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
313        write!(f, "{}", View(&self.item))?;
314        for m in self.access.iter() {
315            write!(f, "{}", View(m))?;
316        }
317        Ok(())
318    }
319}
320impl fmt::Display for MemAccess {
321    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
322        match self {
323            MemAccess::Field(id) => write!(f, ".{}", View(id))?,
324            MemAccess::Call(exprs) => {
325                write!(f, "(")?;
326                let mut es = exprs.iter();
327                if let Some(ex) = es.next() {
328                    write!(f, "{}", View(ex))?;
329                }
330                for e in es {
331                    write!(f, ", {}", View(e))?;
332                }
333                write!(f, ")")?;
334            }
335            MemAccess::Index(e) => write!(f, "[{}]", View(e))?,
336        }
337        Ok(())
338    }
339}
340impl fmt::Display for Primary {
341    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
342        match self {
343            Primary::Literal(lit) => write!(f, "{}", View(lit)),
344            Primary::Ref(rf) => write!(f, "{}", View(rf)),
345            Primary::Name(nm) => write!(f, "{}", View(nm)),
346            Primary::Expr(expr) => write!(f, "({})", View(expr)),
347            Primary::EList(exs) => {
348                write!(f, "[")?;
349                let mut es = exs.iter();
350                if let Some(ex) = es.next() {
351                    write!(f, "{}", View(ex))?;
352                }
353                for e in es {
354                    write!(f, ", {}", View(e))?;
355                }
356                write!(f, "]")
357            }
358            Primary::RInits(mis) => {
359                write!(f, "{{")?;
360                let mut ms = mis.iter();
361                if let Some(i) = ms.next() {
362                    write!(f, "{}", View(i))?;
363                }
364                for i in ms {
365                    write!(f, ", {}", View(i))?;
366                }
367                write!(f, "}}")
368            }
369            Primary::Slot(s) => write!(f, "{}", View(s)),
370        }
371    }
372}
373impl fmt::Display for Name {
374    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375        for n in self.path.iter() {
376            write!(f, "{}::", View(n))?;
377        }
378        write!(f, "{}", View(&self.name))?;
379        Ok(())
380    }
381}
382impl fmt::Display for Ref {
383    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
384        match self {
385            Ref::Uid { path, eid } => {
386                write!(f, "{}::{}", View(path), View(eid))?;
387            }
388            Ref::Ref { path, rinits } => {
389                write!(f, "{}::{{", View(path))?;
390                let mut ris = rinits.iter();
391                if let Some(r) = ris.next() {
392                    write!(f, "{}", View(r))?;
393                }
394                for r in ris {
395                    write!(f, ", {}", View(r))?;
396                }
397                write!(f, "}}")?;
398            }
399        }
400        Ok(())
401    }
402}
403impl fmt::Display for RefInit {
404    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
405        write!(f, "{}: {}", View(&self.0), View(&self.1))
406    }
407}
408impl fmt::Display for RecInit {
409    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
410        write!(f, "{}: {}", View(&self.0), View(&self.1))
411    }
412}
413impl fmt::Display for Ident {
414    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
415        match self {
416            Ident::Principal => write!(f, "principal"),
417            Ident::Action => write!(f, "action"),
418            Ident::Resource => write!(f, "resource"),
419            Ident::Context => write!(f, "context"),
420            Ident::True => write!(f, "true"),
421            Ident::False => write!(f, "false"),
422            Ident::Permit => write!(f, "permit"),
423            Ident::Forbid => write!(f, "forbid"),
424            Ident::When => write!(f, "when"),
425            Ident::Unless => write!(f, "unless"),
426            Ident::In => write!(f, "in"),
427            Ident::Has => write!(f, "has"),
428            Ident::Like => write!(f, "like"),
429            Ident::Is => write!(f, "is"),
430            Ident::If => write!(f, "if"),
431            Ident::Then => write!(f, "then"),
432            Ident::Else => write!(f, "else"),
433            Ident::Ident(s) => write!(f, "{}", s),
434            Ident::Invalid(s) => write!(f, "{}", s),
435        }
436    }
437}
438impl fmt::Display for Literal {
439    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
440        match self {
441            Literal::True => write!(f, "true"),
442            Literal::False => write!(f, "false"),
443            Literal::Num(n) => write!(f, "{}", n),
444            Literal::Str(s) => write!(f, "{}", View(s)),
445        }
446    }
447}
448impl fmt::Display for Str {
449    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
450        match self {
451            Str::String(s) | Str::Invalid(s) => {
452                write!(f, "\"{}\"", s)
453            }
454        }
455    }
456}
457
458impl std::fmt::Display for Slot {
459    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
460        let src = match self {
461            Slot::Principal => "?principal",
462            Slot::Resource => "?resource",
463            Slot::Other(slot) => slot.as_ref(),
464        };
465        write!(f, "{src}")
466    }
467}
468
469/// Format an iterator as a natural-language string, separating items with
470/// commas and a conjunction (e.g., "and", "or") between the last two items.
471pub fn join_with_conjunction<T, W: Write>(
472    f: &mut W,
473    conjunction: &str,
474    items: impl IntoIterator<Item = T>,
475    fmt_item: impl Fn(&mut W, T) -> fmt::Result,
476) -> fmt::Result {
477    let mut iter = items.into_iter().peekable();
478
479    if let Some(first_item) = iter.next() {
480        fmt_item(f, first_item)?;
481
482        if let Some(second_item) = iter.next() {
483            match iter.peek() {
484                Some(_) => write!(f, ", "),
485                None => write!(f, " {conjunction} "),
486            }?;
487
488            fmt_item(f, second_item)?;
489
490            while let Some(item) = iter.next() {
491                match iter.peek() {
492                    Some(_) => write!(f, ", "),
493                    None => write!(f, ", {conjunction} "),
494                }?;
495
496                fmt_item(f, item)?;
497            }
498        }
499    }
500
501    Ok(())
502}
503
504#[cfg(test)]
505mod test {
506    use crate::parser::*;
507
508    // Currently, hese tests supplement the ones in the main test
509    // directory, rather than testing everything themselves
510
511    #[test]
512    fn idempotent1() {
513        // Note: the context field in the scope is no longer supported and
514        // will produce an error during CST -> AST conversion. But it is
515        // still correctly parsed & displayed by the CST code.
516        let cstnode1 = text_to_cst::parse_policies(
517            r#"
518
519        permit(principal,action,resource,context)
520        when {
521            -3 != !!2
522        };
523
524        "#,
525        )
526        .expect("parse fail");
527        let cst1 = cstnode1.as_inner().expect("no data");
528        let revert = format!("{}", cst1);
529        let cstnode2 = text_to_cst::parse_policies(&revert).expect("parse fail");
530        let cst2 = cstnode2.as_inner().expect("no data");
531        println!("{:#}", cst2);
532        assert!(cst1 == cst2);
533    }
534    #[test]
535    fn idempotent2() {
536        let cstnode1 = text_to_cst::parse_policies(
537            r#"
538
539        permit(principal,action,resource,context)
540        when {
541            context.contains(3,"four",five(6,7))
542        };
543
544        "#,
545        )
546        .expect("parse fail");
547        let cst1 = cstnode1.as_inner().expect("no data");
548        let revert = format!("{}", cst1);
549        let cstnode2 = text_to_cst::parse_policies(&revert).expect("parse fail");
550        let cst2 = cstnode2.as_inner().expect("no data");
551        assert!(cst1 == cst2);
552    }
553    #[test]
554    fn idempotent3() {
555        let cstnode1 = text_to_cst::parse_policies(
556            r#"
557
558        permit(principal,action,resource,context)
559        when {
560            context == {3: 14, "true": false || true }
561        };
562
563        "#,
564        )
565        .expect("parse fail");
566        let cst1 = cstnode1.as_inner().expect("no data");
567        let revert = format!("{}", cst1);
568        let cstnode2 = text_to_cst::parse_policies(&revert).expect("parse fail");
569        let cst2 = cstnode2.as_inner().expect("no data");
570        assert!(cst1 == cst2);
571    }
572    #[test]
573    fn idempotent4() {
574        let cstnode1 = text_to_cst::parse_policies(
575            r#"
576
577        permit(principal,action,resource,context)
578        when {
579            contains() ||
580            containsAll() ||
581            containsAny() ||
582            "sometext" like "some*" ||
583            Random::naming::of::foo()
584        };
585
586        "#,
587        )
588        .expect("parse fail");
589        let cst1 = cstnode1.as_inner().expect("no data");
590        let revert = format!("{}", cst1);
591        println!("{:#}", cst1);
592        let cstnode2 = text_to_cst::parse_policies(&revert).expect("parse fail");
593        let cst2 = cstnode2.as_inner().expect("no data");
594        assert!(cst1 == cst2);
595    }
596
597    #[test]
598    fn idempotent5() {
599        let cstnode1 = text_to_cst::parse_policies(
600            r#"
601
602        permit(principal,action,resource,context)
603        when {
604            principle == Group::{uid:"ajn34-3qg3-g5"}
605        };
606
607        "#,
608        )
609        .expect("parse fail");
610        let cst1 = cstnode1.as_inner().expect("no data");
611        let revert = format!("{}", cst1);
612        let cstnode2 = text_to_cst::parse_policies(&revert).expect("parse fail");
613        let cst2 = cstnode2.as_inner().expect("no data");
614        assert!(cst1 == cst2);
615    }
616}