1use core::fmt::{self, Debug, Formatter};
2
3use either::Either;
4
5use crate::utils::DebugFn;
6
7use super::Rule;
8
9pub struct PrintContext<'src> {
10 src: &'src str,
11 debug: bool,
12}
13
14enum IterSpecialCase<I: Iterator> {
15 Zero,
16 One(I::Item),
17 Many(I),
18}
19
20fn iter_special_case<T>(
21 iter: impl IntoIterator<Item = T>,
22) -> IterSpecialCase<impl Iterator<Item = T>> {
23 let mut iter = iter.into_iter();
24 match iter.size_hint() {
25 (_, Some(0)) => IterSpecialCase::Zero,
26 (_, Some(1)) => match iter.next() {
27 Some(item) => IterSpecialCase::One(item),
28 None => IterSpecialCase::Zero,
29 },
30 (2, _) => IterSpecialCase::Many(Either::Left(iter)),
31 _ => {
32 let Some(first) = iter.next() else {
33 return IterSpecialCase::Zero;
34 };
35
36 let Some(second) = iter.next() else {
37 return IterSpecialCase::One(first);
38 };
39
40 IterSpecialCase::Many(Either::Right([first, second].into_iter().chain(iter)))
41 }
42 }
43}
44
45impl<'src> PrintContext<'src> {
46 pub fn src(&self) -> &'src str {
47 self.src
48 }
49
50 pub fn new(src: &'src str) -> Self {
51 Self { src, debug: false }
52 }
53
54 pub fn debuggable<'lt, R: Rule + ?Sized>(&'lt self, ast: &'lt R) -> impl Debug + 'lt {
55 DebugFn(move |f| ast.print_tree(self, f))
56 }
57
58 pub fn filter_ignored<'short, 'item, R: Rule + ?Sized>(
59 &'short self,
60 items: impl IntoIterator<Item = &'item R> + 'short,
61 ) -> impl Iterator<Item = &'item R> + 'short {
62 items
63 .into_iter()
64 .filter(|item| match item.print_visibility(self) {
65 PrintVisibility::Never => false,
66 PrintVisibility::DebugOnly => self.debug,
67 PrintVisibility::Always => true,
68 })
69 }
70
71 pub fn fold_printable<'item, T>(
72 &self,
73 items: impl IntoIterator<Item = &'item dyn Rule>,
74 init: T,
75 mut f: impl FnMut(T, &dyn Debug) -> T,
76 ) -> T {
77 items
78 .into_iter()
79 .fold(init, move |acc, ast| f(acc, &self.debuggable(ast)))
80 }
81
82 pub fn debug_tuple<'item>(
83 &self,
84 name: &str,
85 f: &mut Formatter,
86 items: impl IntoIterator<Item = &'item dyn Rule>,
87 ) -> fmt::Result {
88 match iter_special_case(self.filter_ignored(items)) {
89 IterSpecialCase::Zero => f.write_str("()"),
90 IterSpecialCase::One(item) => item.print_tree(self, f),
91 IterSpecialCase::Many(items) => self
92 .fold_printable(items, &mut f.debug_tuple(name), |d, item| d.field(item))
93 .finish(),
94 }
95 }
96
97 pub fn debug_list<'item>(
98 &self,
99 f: &mut Formatter,
100 items: impl IntoIterator<Item = &'item dyn Rule>,
101 ) -> fmt::Result {
102 match iter_special_case(self.filter_ignored(items)) {
103 IterSpecialCase::Zero => f.write_str("[]"),
104 IterSpecialCase::One(item) => {
105 f.write_str("[")?;
106 item.print_tree(self, f)?;
107 f.write_str("]")
108 }
109 IterSpecialCase::Many(items) => self
110 .fold_printable(items, &mut f.debug_list(), |d, item| d.entry(item))
111 .finish(),
112 }
113 }
114
115 pub fn debug_rule<'item>(
116 &self,
117 f: &mut Formatter,
118 items: impl IntoIterator<Item = &'item dyn Rule>,
119 ) -> fmt::Result {
120 match iter_special_case(self.filter_ignored(items)) {
121 IterSpecialCase::Zero => f.write_str("{}"),
122 IterSpecialCase::One(item) => item.print_tree(self, f),
123 IterSpecialCase::Many(items) => self
124 .fold_printable(items, &mut f.debug_set(), |d, item| d.entry(item))
125 .finish(),
126 }
127 }
128
129 pub fn is_debug(&self) -> bool {
130 self.debug
131 }
132
133 pub fn set_debug(&mut self, debug: bool) -> &mut Self {
134 self.debug = debug;
135 self
136 }
137}
138
139#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
140pub enum PrintVisibility {
141 Never,
142 DebugOnly,
143 #[default]
144 Always,
145}
146
147impl PrintVisibility {
148 pub fn should_print(self, cx: &PrintContext) -> bool {
149 match self {
150 PrintVisibility::Never => false,
151 PrintVisibility::DebugOnly => cx.is_debug(),
152 PrintVisibility::Always => true,
153 }
154 }
155}