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}