indent_display/indenter.rs
1/*a Copyright
2
3Licensed under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License.
5You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9Unless required by applicable law or agreed to in writing, software
10distributed under the License is distributed on an "AS IS" BASIS,
11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12See the License for the specific language governing permissions and
13limitations under the License.
14
15@file indent.rs
16@brief An indented display system
17 */
18
19//a Imports
20use std::cell::RefCell;
21use std::rc::Rc;
22
23use crate::IndentedOptions;
24
25//a Type aliases
26type IOResult = std::result::Result<(), std::io::Error>;
27type FmtResult = std::result::Result<(), std::fmt::Error>;
28type RrcRoot<'a, Opt> = Rc<RefCell<Root<'a, Opt>>>;
29type RrcInner<'a, Opt> = Rc<RefCell<Inner<'a, Opt>>>;
30
31//a Root
32//ti Root
33/// The root of the indenter - this is used as an Rc/RefCell
34/// so that it can be accessed by any depth of display node
35struct Root<'a, Opt: IndentedOptions<'a>> {
36 /// The underlying Write object that provides the output method
37 fmt: &'a mut (dyn std::io::Write + 'a),
38 /// The options the indenter was created with
39 options: &'a Opt,
40 /// Set if a newline is pending
41 pending_newline: bool,
42 /// Boolean set to true if at the start of a line - so if real
43 /// characters are to be output, the appropriate indentation must
44 /// be performed first
45 sol: bool,
46 /// The basic indentation string to be used per level, unless
47 /// explicit per-level indents are provided
48 ind: &'a str,
49 /// The current stack of indentation strings and the depth
50 /// associated with them; this is an empty vector if a single
51 /// indent string is used.
52 subind: Vec<(usize, &'a str)>,
53 /// The current depth of indentation
54 depth: usize,
55}
56
57//ii Root
58impl<'a, Opt: IndentedOptions<'a>> Root<'a, Opt> {
59 //fi new
60 /// Create a new [Root] of indentation, with a base indent string
61 fn new(fmt: &'a mut (dyn std::io::Write + 'a), ind: &'a str, options: &'a Opt) -> Self {
62 let subind = Vec::new();
63 Self {
64 fmt,
65 options,
66 pending_newline: false,
67 sol: true,
68 ind,
69 subind,
70 depth: 0,
71 }
72 }
73
74 //fi push_indent
75 /// Push a new indentation onto the stack - depth is presumably +1
76 /// on the current depth; if the indentation string provided is
77 /// Some then the indentation at this point will use this instead
78 /// of the base indentation
79 fn push_indent(&mut self, depth: usize, ind: Option<&'a str>) {
80 self.pending_newline = true;
81 if let Some(ind) = ind {
82 self.subind.push((self.depth, ind));
83 }
84 self.depth = depth;
85 }
86
87 //fi pop_indent
88 /// Pop the indent from the stack down to a new depth (which is
89 /// presumably self.depth-1)
90 ///
91 /// This may involve popping the top of subind, if that is for the
92 /// indentation depth being popped
93 fn pop_indent(&mut self, depth: usize) {
94 self.pending_newline = true;
95 if let Some((d, _)) = self.subind.last() {
96 if *d == depth {
97 self.subind.pop();
98 }
99 }
100 self.depth = depth;
101 }
102
103 //fi output_newline
104 /// Output a newline *if required*
105 ///
106 /// After the newline the output will be at the start of a line;
107 /// hence `sol` is set, and any characters to output afterwards
108 /// will require the appropriate indent
109 fn output_newline(&mut self) -> IOResult {
110 self.pending_newline = false;
111 if self.sol {
112 Ok(())
113 } else {
114 self.sol = true;
115 self.fmt.write_all(b"\n")
116 }
117 }
118
119 //fi output_indent
120 /// Output the current indentation
121 ///
122 /// After the newline the output will be at the start of a line;
123 /// hence `sol` is set, and any characters to output afterwards
124 /// will require the appropriate indent
125 fn output_indent(&mut self) -> IOResult {
126 let sublen = self.subind.len();
127 let mut s = 0;
128 for i in 0..self.depth {
129 if s < sublen {
130 if self.subind[s].0 == i {
131 self.fmt.write_all(self.subind[s].1.as_bytes())?;
132 s += 1;
133 } else {
134 self.fmt.write_all(self.ind.as_bytes())?;
135 }
136 } else {
137 self.fmt.write_all(self.ind.as_bytes())?;
138 }
139 }
140 Ok(())
141 }
142
143 //fi output_str
144 /// Output a string that contains no newlines
145 ///
146 /// An empty string requires no output
147 ///
148 /// If there is data to output and the last output left it at the
149 /// start of a line then indentation is required first to the
150 /// current depth
151 fn output_str(&mut self, s: &str) -> IOResult {
152 // If there is nothing to show then must not indent - it may
153 // be that the indent changes before there is something to
154 // output
155 if s.is_empty() {
156 return Ok(());
157 }
158 if self.pending_newline {
159 self.output_newline()?;
160 }
161 if self.sol {
162 self.output_indent()?;
163 }
164 self.sol = false;
165 self.fmt.write_all(s.as_bytes())
166 }
167
168 //fi complete
169 /// Invoked by the last stack frame being dropped; tidy up the
170 /// output
171 ///
172 /// No errors can be returned (this is in Drop)
173 fn complete(&mut self) {
174 if self.pending_newline {
175 let _ = self.output_newline();
176 }
177 }
178
179 //zz All done
180}
181
182//ii Debug for Root
183impl<'a, Opt: IndentedOptions<'a>> std::fmt::Debug for Root<'a, Opt> {
184 //fp fmt
185 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
186 write!(
187 f,
188 "Root[sol:{} depth: {}, ind:{:?}]",
189 self.sol, self.depth, self.ind
190 )
191 }
192}
193
194//ii Write for Root
195impl<'a, Opt: IndentedOptions<'a>> std::fmt::Write for Root<'a, Opt> {
196 //fp write_str
197 /// Perform the actual write operation, providing a write!
198 /// capability (etc) for Root
199 ///
200 /// The string to be written must be split into individual lines
201 /// which are then output
202 ///
203 /// output_newline is invoked *between* every line of output
204 /// i.e. for every newline character in the input string
205 fn write_str(&mut self, s: &str) -> FmtResult {
206 let mut output_newline = false;
207 for line in s.split('\n') {
208 if output_newline {
209 if self.output_newline().is_err() {
210 return Err(std::fmt::Error);
211 }
212 }
213 if self.output_str(line).is_err() {
214 return Err(std::fmt::Error);
215 }
216 output_newline = true;
217 }
218 Ok(())
219 }
220}
221
222//a Inner
223//ti Inner
224/// This represents an indentation 'stack frame', including the uppermost stack frame.
225///
226/// All the stack frames for the same indenter refer to the same
227/// [Root], which is created by the uppermost stack frame creation.
228///
229/// When a subframe is created it clones the [Root], and sets its parent appropriately
230///
231/// When the subframe is dropped it informs the [Root] and resets the
232/// state back to that of its parent.
233#[derive(Debug)]
234pub struct Inner<'a, Opt: IndentedOptions<'a>> {
235 root: RrcRoot<'a, Opt>,
236 parent: Option<RrcInner<'a, Opt>>,
237 depth: usize,
238}
239
240//ii Drop for Inner
241impl<'a, Opt: IndentedOptions<'a>> Drop for Inner<'a, Opt> {
242 //fi drop
243 /// Invoked automatically by Rust when the stack frame goes out of
244 /// scope allowing the indentation to revert to that prior to the
245 /// creation of this stack frame
246 fn drop(&mut self) {
247 if let Some(parent) = &self.parent {
248 let depth = parent.borrow().depth;
249 self.root.borrow_mut().pop_indent(depth);
250 } else {
251 self.root.borrow_mut().complete();
252 }
253 }
254}
255
256//ii Inner
257impl<'a, Opt: IndentedOptions<'a>> Inner<'a, Opt> {
258 //fi root
259 /// Create a root stack frame - which has no parent and is at depth 0
260 fn root(root: RrcRoot<'a, Opt>) -> RrcInner<'a, Opt> {
261 Rc::new(RefCell::new(Self {
262 root: root,
263 parent: None,
264 depth: 0,
265 }))
266 }
267
268 //fi subnode
269 /// Create a subnode of this stack frame, with an optional
270 /// depth-specific indentation string
271 fn subnode(s: &Rc<RefCell<Self>>, ind: Option<&'a str>) -> RrcInner<'a, Opt> {
272 let root = s.borrow().root.clone();
273 let parent = Some(s.clone());
274 let depth = s.borrow().depth + 1;
275 root.borrow_mut().push_indent(depth, ind);
276 Rc::new(RefCell::new(Self {
277 root,
278 parent,
279 depth,
280 }))
281 }
282
283 //fi pop
284 /// Create a subnode of this stack frame, with an optional
285 /// depth-specific indentation string
286 fn pop(self) -> Option<RrcInner<'a, Opt>> {
287 if let Some(parent) = &self.parent {
288 Some(parent.clone())
289 } else {
290 None
291 }
292 }
293
294 //fi take_parent
295 /// Provided this [Inner] has a parent (panic otherwise), return
296 /// that parent by deconstructing this
297 ///
298 /// This node must not be borrowed elsewhere for this to work,
299 /// as it cannot be deconstructed if so
300 fn take_parent(s: Rc<RefCell<Self>>) -> RrcInner<'a, Opt> {
301 assert!(s.borrow().parent.is_some());
302 match Rc::try_unwrap(s) {
303 Err(_) => {
304 panic!("Indent was multiply borrowed");
305 }
306 Ok(x) => x.into_inner().pop().unwrap(),
307 }
308 }
309
310 //zz All done
311}
312
313//a Indenter
314//tp Indenter
315/// The public face of the library, this is the type that must be
316/// created to use the [crate::IndentedDisplay] trait
317///
318/// This utilizes a [std::fmt::Write] formatter as its output, a base
319/// indent string that is used for all levels of indentation (unless
320/// overridden individually by indentation frames), and an options
321/// structure that contains options that may be interrogated by the
322/// implementation of [crate::IndentedDisplay]
323pub struct Indenter<'a, Opt: IndentedOptions<'a>> {
324 node: RrcInner<'a, Opt>,
325}
326
327//ip Indenter
328impl<'a, Opt: IndentedOptions<'a>> Indenter<'a, Opt> {
329 //fp new
330 /// Create a new [Indenter], to be used with types that implement
331 /// the [crate::IndentedDisplay] trait; this specifies the formatter, the
332 /// base indentation string, and the options for the indentation
333 pub fn new(fmt: &'a mut (dyn std::io::Write + 'a), s: &'a str, options: &'a Opt) -> Self {
334 let r = Rc::new(RefCell::new(Root::new(fmt, s, options)));
335 let node = Inner::root(r);
336 Self { node }
337 }
338
339 //fp sub
340 /// Create a new subframe of the [Indenter] using its base
341 /// indentation for this indentation level; this is invoked by the
342 /// `indent` function in an [crate::IndentedDisplay] trait implementation
343 /// to create subframes of indentation. The subframe is removed
344 /// from the indentation output stack when it is *dropped*, so it
345 /// must either go out of scope or be explicitly dropped.
346 pub fn sub(&self) -> Self {
347 let node = Inner::subnode(&self.node, None);
348 Self { node }
349 }
350
351 //fp push
352 /// Create a new subframe of the [Indenter] using a specific string,
353 /// indentation for this indentation level; this is invoked by the
354 /// `indent` function in an [crate::IndentedDisplay] trait implementation
355 /// to create subframes of indentation. The subframe is removed
356 /// from the indentation output stack when it is *dropped*, so it
357 /// must either go out of scope or be explicitly dropped.
358 ///
359 /// Currently the string must outlive the Indenter - usually this
360 /// means it is static.
361 pub fn push(&self, s: &'a str) -> Self {
362 let node = Inner::subnode(&self.node, Some(s));
363 Self { node }
364 }
365
366 //dp pop
367 /// Pop this subframe and return its parent
368 pub fn pop(self) -> Self {
369 let node = Inner::take_parent(self.node);
370 Self { node }
371 }
372
373 //fp options
374 /// Borrow the options used to invoke the [Indenter].
375 ///
376 /// This may be invoked by the
377 /// `indent` function in an [crate::IndentedDisplay] trait implementation
378 /// to determine the setting of indentation options that may affect its output.
379 pub fn options(&self) -> &Opt {
380 &self.node.borrow().root.borrow().options
381 }
382
383 //zz All done
384}
385
386//ip Write
387impl<'a, Opt: IndentedOptions<'a>> std::fmt::Write for Indenter<'a, Opt> {
388 fn write_str(&mut self, s: &str) -> FmtResult {
389 self.node.borrow().root.borrow_mut().write_str(s)
390 }
391}