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