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