1use std::{
7 collections::VecDeque,
8 ops::{AddAssign, Sub},
9};
10
11use crate::{render::Render, string::CowString};
12
13#[derive(Clone, Copy)]
14struct Position(pub usize);
15
16impl AddAssign<usize> for Position {
17 fn add_assign(&mut self, rhs: usize) {
18 self.0 += rhs
19 }
20}
21
22impl Sub<Position> for Position {
23 type Output = usize;
24
25 fn sub(self, rhs: Position) -> Self::Output {
26 debug_assert!(self.0 >= rhs.0);
27 self.0 - rhs.0
28 }
29}
30
31enum Token<'a, S: AsRef<str>> {
32 Text(CowString<'a, S>),
33 Break { indent: usize },
34 Group(OutGroup<'a, S>),
35}
36
37struct OutGroup<'a, S: AsRef<str>> {
38 tokens: Vec<(Token<'a, S>, usize)>,
39 consistent: bool,
40}
41
42#[derive(Clone, Copy)]
43enum RenderFrame {
44 Fits,
45 Break { consistent: bool },
46}
47
48pub struct Printer<'a, R: Render = String, S: AsRef<str> = String> {
51 line_width: usize,
53
54 position: Position,
56 indent: Vec<isize>,
57 dq: VecDeque<(Position, OutGroup<'a, S>)>,
58
59 renderer: R,
61 remaining: usize,
62 render_stack: Vec<RenderFrame>,
63 pending_indent: usize,
64}
65
66impl<R: Render> Printer<'_, R> {
67 pub fn new(renderer: R, line_width: usize) -> Self {
73 Self::new_with(renderer, line_width)
74 }
75}
76
77impl<'a, S: AsRef<str>, R: Render> Printer<'a, R, S> {
78 pub fn new_with(renderer: R, line_width: usize) -> Self {
84 assert!(
85 line_width > 0 && line_width <= Self::MAX_WIDTH,
86 "line width must be between 1 and {}",
87 Self::MAX_WIDTH
88 );
89 let mut pp = Self {
90 line_width,
91 position: Position(0),
92 indent: vec![0],
93 dq: VecDeque::new(),
94 renderer,
95 remaining: line_width,
96 render_stack: Vec::new(),
97 pending_indent: 0,
98 };
99 pp.scan_begin(0, false);
100 pp
101 }
102
103 pub const MAX_WIDTH: usize = 65536;
105
106 pub fn scan_text(&mut self, text: CowString<'a, S>, width: usize) -> Result<(), R::Error> {
108 self.scan(width, Token::Text(text))
109 }
110
111 pub fn scan_break(&mut self, size: usize, indent: isize) -> Result<(), R::Error> {
122 let indent = (self.indent() + indent)
123 .try_into()
124 .expect("indent must >= 0");
125 self.scan(size, Token::Break { indent })
126 }
127
128 pub fn scan_begin(&mut self, indent: isize, consistent: bool) {
130 self.indent.push(self.indent() + indent);
131 self.dq.push_back((
132 self.position,
133 OutGroup {
134 tokens: Vec::with_capacity(12),
135 consistent,
136 },
137 ));
138 }
139
140 pub fn scan_end(&mut self) -> Result<(), R::Error> {
142 self.indent.pop();
143 if let Some((s, grp1)) = self.dq.pop_back() {
144 let width = self.position - s;
145 if let Some((_, grp2)) = self.dq.back_mut() {
146 grp2.tokens.push((Token::Group(grp1), width));
147 } else {
148 self.render_begin(grp1, width)?;
149 self.render_end()?;
150 }
151 } else {
152 self.render_end()?;
153 }
154 Ok(())
155 }
156
157 pub fn finish(mut self) -> Result<R, R::Error> {
163 self.scan_end()?;
164 assert!(self.dq.is_empty(), "unclosed group");
165 Ok(self.renderer)
166 }
167
168 fn indent(&self) -> isize {
169 *self.indent.last().unwrap()
170 }
171
172 fn scan(&mut self, width: usize, out: Token<'a, S>) -> Result<(), R::Error> {
173 self.position += width;
174 if let Some((_, grp)) = self.dq.back_mut() {
175 grp.tokens.push((out, width));
176 self.prune()?;
177 } else {
178 self.render_token(out, width)?;
179 }
180 Ok(())
181 }
182
183 fn prune(&mut self) -> Result<(), R::Error> {
184 while self
185 .dq
186 .front()
187 .is_some_and(|&(s, _)| self.position - s > self.remaining)
188 {
189 let (_, grp) = self.dq.pop_front().unwrap();
190 self.render_begin(grp, Self::MAX_WIDTH)?;
191 }
192 Ok(())
193 }
194
195 fn render_token(&mut self, token: Token<'a, S>, width: usize) -> Result<(), R::Error> {
196 match token {
197 Token::Text(text) => self.render_text(text, width),
198 Token::Break { indent } => self.render_break(indent, width),
199 Token::Group(group) => {
200 self.render_begin(group, width)?;
201 self.render_end()
202 }
203 }
204 }
205
206 fn render_text(&mut self, text: CowString<'a, S>, width: usize) -> Result<(), R::Error> {
207 if self.pending_indent > 0 {
208 self.renderer.write_spaces(self.pending_indent)?;
209 self.pending_indent = 0;
210 }
211 self.renderer.write_str(&text)?;
212 self.remaining = self.remaining.saturating_sub(width);
213 Ok(())
214 }
215
216 fn render_break(&mut self, indent: usize, width: usize) -> Result<(), R::Error> {
217 let frame = self
218 .render_stack
219 .last()
220 .copied()
221 .unwrap_or(RenderFrame::Break { consistent: false });
222 let fits = match frame {
223 RenderFrame::Fits => true,
224 RenderFrame::Break { consistent, .. } => !consistent && width < self.remaining,
225 };
226 if fits {
227 self.renderer.write_spaces(width)?;
228 self.remaining = self.remaining.saturating_sub(width);
229 } else {
230 self.renderer.write_str("\n")?;
231 self.pending_indent = indent;
232 self.remaining = self.line_width.saturating_sub(indent);
233 }
234 Ok(())
235 }
236
237 fn render_begin(&mut self, group: OutGroup<'a, S>, width: usize) -> Result<(), R::Error> {
238 self.render_stack.push(if width <= self.remaining {
239 RenderFrame::Fits
240 } else {
241 RenderFrame::Break {
242 consistent: group.consistent,
243 }
244 });
245 for (out, width) in group.tokens {
246 self.render_token(out, width)?;
247 }
248 Ok(())
249 }
250
251 fn render_end(&mut self) -> Result<(), R::Error> {
252 self.render_stack.pop();
253 Ok(())
254 }
255}