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