Skip to main content

oxixml_xml/
writer.rs

1//! High-level interface for an events-based XML emitter.
2//!
3//! The central type is [`Writer`], which serializes XML [`Event`]s to any
4//! [`std::io::Write`] implementor, mirroring the quick-xml 0.41 `Writer`. Because
5//! the reader and the writer share the same [`Event`] vocabulary, a stream of
6//! events read by a [`Reader`](crate::reader::Reader) can be written back out
7//! verbatim (a round trip).
8//!
9//! For convenience, [`Writer::create_element`] returns an [`ElementWriter`]
10//! builder that makes writing a single element with attributes and content
11//! ergonomic, and [`Writer::new_with_indent`] adds pretty-printing.
12//!
13//! # Example
14//!
15//! ```
16//! use oxixml_xml::events::{BytesEnd, BytesStart, BytesText, Event};
17//! use oxixml_xml::writer::Writer;
18//!
19//! let mut writer = Writer::new(Vec::new());
20//! writer.write_event(Event::Start(BytesStart::new("root"))).expect("start");
21//! writer.write_event(Event::Text(BytesText::new("hello & bye"))).expect("text");
22//! writer.write_event(Event::End(BytesEnd::new("root"))).expect("end");
23//!
24//! let result = writer.into_inner();
25//! assert_eq!(result, br#"<root>hello &amp; bye</root>"#);
26//! ```
27
28use std::borrow::Cow;
29use std::io::{self, Write};
30
31use crate::encoding::UTF8_BOM;
32use crate::events::attributes::Attribute;
33use crate::events::{BytesCData, BytesPI, BytesStart, BytesText, Event};
34
35#[cfg(feature = "async-tokio")]
36mod async_tokio;
37
38/// A struct that holds a writer configuration.
39///
40/// The current writer configuration can be retrieved by calling
41/// [`Writer::config()`] and changed through [`Writer::config_mut()`].
42#[derive(Debug, Default, Clone, PartialEq, Eq)]
43#[cfg_attr(feature = "serde-types", derive(serde::Deserialize, serde::Serialize))]
44#[non_exhaustive]
45pub struct Config {
46    /// Whether to add a space before the closing slash in empty elements.
47    /// According to the [W3C guidelines], this is recommended for maximum
48    /// compatibility.
49    ///
50    /// When set to `true`, empty elements are terminated with "` />`". When set to
51    /// `false`, empty elements are terminated with "`/>`".
52    ///
53    /// Default: `false`.
54    ///
55    /// # Example
56    ///
57    /// ```
58    /// use oxixml_xml::writer::Writer;
59    ///
60    /// let mut writer = Writer::new(Vec::new());
61    /// writer.config_mut().add_space_before_slash_in_empty_elements = true;
62    ///
63    /// writer.create_element("tag")
64    ///     .with_attribute(("attr1", "value1"))
65    ///     .write_empty()
66    ///     .expect("write");
67    ///
68    /// assert_eq!(writer.into_inner(), br#"<tag attr1="value1" />"#);
69    /// ```
70    ///
71    /// [W3C guidelines]: https://www.w3.org/TR/xhtml1/#guidelines
72    pub add_space_before_slash_in_empty_elements: bool,
73}
74
75/// XML writer. Writes XML [`Event`]s to a [`std::io::Write`] implementor.
76///
77/// # Examples
78///
79/// This example reads an XML document event by event, renaming one element while
80/// keeping the rest verbatim, and writes it back out.
81///
82/// ```
83/// use oxixml_xml::events::{BytesEnd, BytesStart, Event};
84/// use oxixml_xml::reader::Reader;
85/// use oxixml_xml::writer::Writer;
86///
87/// let xml = r#"<this_tag k1="v1" k2="v2"><child>text</child></this_tag>"#;
88/// let mut reader = Reader::from_str(xml);
89/// let mut writer = Writer::new(Vec::new());
90/// loop {
91///     match reader.read_event().expect("valid event") {
92///         Event::Start(e) if e.name().as_ref() == b"this_tag" => {
93///             let mut elem = BytesStart::new("my_elem");
94///             elem.extend_attributes(e.attributes().map(|attr| attr.expect("attr")));
95///             elem.push_attribute(("my-key", "some value"));
96///             writer.write_event(Event::Start(elem)).expect("start");
97///         }
98///         Event::End(e) if e.name().as_ref() == b"this_tag" => {
99///             writer.write_event(Event::End(BytesEnd::new("my_elem"))).expect("end");
100///         }
101///         Event::Eof => break,
102///         e => writer.write_event(e).expect("event"),
103///     }
104/// }
105///
106/// let result = writer.into_inner();
107/// let expected = r#"<my_elem k1="v1" k2="v2" my-key="some value"><child>text</child></my_elem>"#;
108/// assert_eq!(result, expected.as_bytes());
109/// ```
110#[derive(Clone)]
111pub struct Writer<W> {
112    /// Underlying writer.
113    writer: W,
114    /// Writer configuration.
115    config: Config,
116    /// Indentation configuration and state; stored separately from other
117    /// configuration since it also tracks writer state.
118    indent: Option<Indentation>,
119}
120
121impl<W> Writer<W> {
122    /// Creates a `Writer` from a generic writer.
123    pub const fn new(inner: W) -> Writer<W> {
124        Writer {
125            writer: inner,
126            config: Config {
127                add_space_before_slash_in_empty_elements: false,
128            },
129            indent: None,
130        }
131    }
132
133    /// Creates a `Writer` with configured indentation from a generic writer.
134    ///
135    /// Each nesting level is indented by `indent_size` repetitions of the
136    /// `indent_char` byte (typically `b' '` or `b'\t'`).
137    pub fn new_with_indent(inner: W, indent_char: u8, indent_size: usize) -> Writer<W> {
138        Writer {
139            writer: inner,
140            config: Config {
141                add_space_before_slash_in_empty_elements: false,
142            },
143            indent: Some(Indentation::new(indent_char, indent_size)),
144        }
145    }
146
147    /// Consumes this `Writer`, returning the underlying writer.
148    pub fn into_inner(self) -> W {
149        self.writer
150    }
151
152    /// Gets a mutable reference to the underlying writer.
153    pub fn get_mut(&mut self) -> &mut W {
154        &mut self.writer
155    }
156
157    /// Gets a reference to the underlying writer.
158    pub const fn get_ref(&self) -> &W {
159        &self.writer
160    }
161
162    /// Returns a reference to the writer configuration.
163    pub const fn config(&self) -> &Config {
164        &self.config
165    }
166
167    /// Returns a mutable reference to the writer configuration.
168    pub fn config_mut(&mut self) -> &mut Config {
169        &mut self.config
170    }
171
172    /// Provides a simple, high-level API for writing an XML element.
173    ///
174    /// Returns an [`ElementWriter`] that simplifies setting attributes and writing
175    /// content inside the element.
176    ///
177    /// # Example
178    ///
179    /// ```
180    /// use oxixml_xml::events::BytesText;
181    /// use oxixml_xml::writer::Writer;
182    ///
183    /// let mut writer = Writer::new(Vec::new());
184    ///
185    /// // writes <tag attr1="value1"/>
186    /// writer.create_element("tag")
187    ///     .with_attribute(("attr1", "value1"))
188    ///     .write_empty()
189    ///     .expect("empty");
190    ///
191    /// // writes <tag attr1="value1" attr2="value2">with some text inside</tag>
192    /// writer.create_element("tag")
193    ///     .with_attributes(vec![("attr1", "value1"), ("attr2", "value2")])
194    ///     .write_text_content(BytesText::new("with some text inside"))
195    ///     .expect("text");
196    ///
197    /// // writes <tag><fruit quantity="0">apple</fruit><fruit quantity="1">orange</fruit></tag>
198    /// writer.create_element("tag")
199    ///     .write_inner_content(|writer| {
200    ///         let fruits = ["apple", "orange"];
201    ///         for (quant, item) in fruits.iter().enumerate() {
202    ///             writer
203    ///                 .create_element("fruit")
204    ///                 .with_attribute(("quantity", quant.to_string().as_str()))
205    ///                 .write_text_content(BytesText::new(item))?;
206    ///         }
207    ///         Ok(())
208    ///     })
209    ///     .expect("inner");
210    /// ```
211    #[must_use]
212    pub fn create_element<'a, N>(&'a mut self, name: N) -> ElementWriter<'a, W>
213    where
214        N: Into<Cow<'a, str>>,
215    {
216        ElementWriter {
217            writer: self,
218            start_tag: BytesStart::new(name),
219            state: AttributeIndent::NoneAttributesWritten,
220            spaces: Vec::new(),
221        }
222    }
223}
224
225impl<W: Write> Writer<W> {
226    /// Writes a [Byte-Order-Mark] character to the document.
227    ///
228    /// # Example
229    ///
230    /// ```
231    /// use oxixml_xml::writer::Writer;
232    ///
233    /// let mut writer = Writer::new_with_indent(Vec::new(), b' ', 4);
234    ///
235    /// writer.write_bom().expect("bom");
236    /// writer
237    ///     .create_element("empty")
238    ///     .with_attribute(("attr1", "value1"))
239    ///     .write_empty()
240    ///     .expect("empty");
241    ///
242    /// assert_eq!(
243    ///     std::str::from_utf8(&writer.into_inner()).expect("utf-8"),
244    ///     "\u{FEFF}<empty attr1=\"value1\"/>"
245    /// );
246    /// ```
247    ///
248    /// [Byte-Order-Mark]: https://unicode.org/faq/utf_bom.html#BOM
249    pub fn write_bom(&mut self) -> io::Result<()> {
250        self.write(UTF8_BOM)
251    }
252
253    /// Writes the given event to the underlying writer.
254    pub fn write_event<'a, E: Into<Event<'a>>>(&mut self, event: E) -> io::Result<()> {
255        let mut next_should_line_break = true;
256        let result = match event.into() {
257            Event::Start(e) => {
258                let result = self.write_wrapped(b"<", &e, b">");
259                if let Some(i) = self.indent.as_mut() {
260                    i.grow();
261                }
262                result
263            }
264            Event::End(e) => {
265                if let Some(i) = self.indent.as_mut() {
266                    i.shrink();
267                }
268                self.write_wrapped(b"</", &e, b">")
269            }
270            Event::Empty(e) => self.write_wrapped(
271                b"<",
272                &e,
273                if self.config.add_space_before_slash_in_empty_elements {
274                    b" />"
275                } else {
276                    b"/>"
277                },
278            ),
279            Event::Text(e) => {
280                next_should_line_break = false;
281                self.write(&e)
282            }
283            Event::Comment(e) => self.write_wrapped(b"<!--", &e, b"-->"),
284            Event::CData(e) => {
285                next_should_line_break = false;
286                self.write(b"<![CDATA[")?;
287                self.write(&e)?;
288                self.write(b"]]>")
289            }
290            Event::Decl(e) => self.write_wrapped(b"<?", &e, b"?>"),
291            Event::PI(e) => self.write_wrapped(b"<?", &e, b"?>"),
292            Event::DocType(e) => self.write_wrapped(b"<!DOCTYPE ", &e, b">"),
293            Event::GeneralRef(e) => self.write_wrapped(b"&", &e, b";"),
294            Event::Eof => Ok(()),
295        };
296        if let Some(i) = self.indent.as_mut() {
297            i.should_line_break = next_should_line_break;
298        }
299        result
300    }
301
302    /// Writes bytes directly to the underlying writer.
303    #[inline]
304    pub(crate) fn write(&mut self, value: &[u8]) -> io::Result<()> {
305        self.writer.write_all(value)
306    }
307
308    #[inline]
309    fn write_wrapped(&mut self, before: &[u8], value: &[u8], after: &[u8]) -> io::Result<()> {
310        if let Some(ref i) = self.indent {
311            if i.should_line_break {
312                self.writer.write_all(b"\n")?;
313                self.writer.write_all(i.current())?;
314            }
315        }
316        self.write(before)?;
317        self.write(value)?;
318        self.write(after)?;
319        Ok(())
320    }
321
322    /// Manually writes a newline and indentation at the proper level.
323    ///
324    /// This can be used when the heuristic to line break and indent after every
325    /// [`Event`] apart from [`Text`](Event::Text) fails, such as when a
326    /// [`Start`](Event::Start) occurs directly after a [`Text`](Event::Text).
327    ///
328    /// This method does nothing if `Writer` was not constructed with
329    /// [`new_with_indent`](Self::new_with_indent).
330    pub fn write_indent(&mut self) -> io::Result<()> {
331        if let Some(ref i) = self.indent {
332            self.writer.write_all(b"\n")?;
333            self.writer.write_all(i.current())?;
334        }
335        Ok(())
336    }
337
338    /// Writes a serde-serializable value as an XML element named `tag_name`.
339    ///
340    /// The value is serialized with [`crate::se::Serializer`], so the usual
341    /// mapping rules apply (`@field` becomes an attribute, `$text` becomes the
342    /// text content, and so on). If this `Writer` was created with
343    /// [`new_with_indent`](Self::new_with_indent), the produced markup shares the
344    /// writer's indentation state.
345    ///
346    /// # Examples
347    ///
348    /// ```
349    /// # use serde::Serialize;
350    /// # use oxixml_xml::events::{BytesStart, Event};
351    /// # use oxixml_xml::writer::Writer;
352    /// # use oxixml_xml::se::SeError;
353    /// # fn main() -> Result<(), SeError> {
354    /// #[derive(Debug, PartialEq, Serialize)]
355    /// struct MyData {
356    ///     question: String,
357    ///     answer: u32,
358    /// }
359    ///
360    /// let data = MyData {
361    ///     question: "The Ultimate Question of Life, the Universe, and Everything".into(),
362    ///     answer: 42,
363    /// };
364    ///
365    /// let mut buffer = Vec::new();
366    /// let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
367    ///
368    /// let start = BytesStart::new("root");
369    /// let end = start.to_end();
370    ///
371    /// writer.write_event(Event::Start(start.clone()))?;
372    /// writer.write_serializable("my_data", &data)?;
373    /// writer.write_event(Event::End(end))?;
374    ///
375    /// assert_eq!(
376    ///     std::str::from_utf8(&buffer)?,
377    ///     r#"<root>
378    ///     <my_data>
379    ///         <question>The Ultimate Question of Life, the Universe, and Everything</question>
380    ///         <answer>42</answer>
381    ///     </my_data>
382    /// </root>"#
383    /// );
384    /// # Ok(())
385    /// # }
386    /// ```
387    #[cfg(feature = "serialize")]
388    #[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
389    pub fn write_serializable<T: serde::Serialize>(
390        &mut self,
391        tag_name: &str,
392        content: &T,
393    ) -> std::result::Result<(), crate::errors::serialize::SeError> {
394        use crate::se::{Indent, Serializer};
395
396        self.write_indent()?;
397        let mut fmt = ToFmtWrite(&mut self.writer);
398        let mut serializer = Serializer::with_root(&mut fmt, Some(tag_name))?;
399
400        if let Some(indent) = &mut self.indent {
401            serializer.set_indent(Indent::Borrow(indent));
402        }
403
404        content.serialize(serializer)?;
405
406        Ok(())
407    }
408}
409
410/// Adapts an [`std::io::Write`] implementor to the [`std::fmt::Write`] trait
411/// required by the serde serializer.
412#[cfg(feature = "serialize")]
413pub(crate) struct ToFmtWrite<T>(pub T);
414
415#[cfg(feature = "serialize")]
416impl<T> std::fmt::Write for ToFmtWrite<T>
417where
418    T: std::io::Write,
419{
420    fn write_str(&mut self, s: &str) -> std::fmt::Result {
421        self.0.write_all(s.as_bytes()).map_err(|_| std::fmt::Error)
422    }
423}
424
425/// Tracks indent-inside-element state for [`ElementWriter`].
426///
427/// ```mermaid
428/// stateDiagram-v2
429///     [*] --> NoneAttributesWritten
430///     NoneAttributesWritten --> Spaces : .with_attribute()
431///     NoneAttributesWritten --> WriteConfigured : .new_line()
432///
433///     Spaces --> Spaces : .with_attribute()
434///     Spaces --> WriteSpaces : .new_line()
435///
436///     WriteSpaces --> Spaces : .with_attribute()
437///     WriteSpaces --> WriteSpaces : .new_line()
438///
439///     Configured --> Configured : .with_attribute()
440///     Configured --> WriteConfigured : .new_line()
441///
442///     WriteConfigured --> Configured : .with_attribute()
443///     WriteConfigured --> WriteConfigured : .new_line()
444/// ```
445#[derive(Debug)]
446enum AttributeIndent {
447    /// Initial state: `ElementWriter` was just created and no attributes are
448    /// written yet.
449    NoneAttributesWritten,
450    /// Write the specified count of spaces to indent before writing an attribute
451    /// in `with_attribute()`.
452    WriteSpaces(usize),
453    /// Keep the space indent that should be used if `new_line()` is called.
454    Spaces(usize),
455    /// Write the specified count of indent characters before writing an attribute
456    /// in `with_attribute()`.
457    WriteConfigured(usize),
458    /// Keep the indent that should be used if `new_line()` is called.
459    Configured(usize),
460}
461
462/// A builder to write a single element, returned by [`Writer::create_element`].
463///
464/// Contains methods to add attributes and inner content to the element.
465pub struct ElementWriter<'a, W> {
466    writer: &'a mut Writer<W>,
467    start_tag: BytesStart<'a>,
468    state: AttributeIndent,
469    /// Contains spaces used to write space indents of attributes.
470    spaces: Vec<u8>,
471}
472
473impl<'a, W> ElementWriter<'a, W> {
474    /// Adds an attribute to this element.
475    pub fn with_attribute<'b, I>(mut self, attr: I) -> Self
476    where
477        I: Into<Attribute<'b>>,
478    {
479        self.write_attr(attr.into());
480        self
481    }
482
483    /// Adds additional attributes to this element using an iterator.
484    ///
485    /// The yielded items must be convertible to [`Attribute`] via `Into`.
486    pub fn with_attributes<'b, I>(mut self, attributes: I) -> Self
487    where
488        I: IntoIterator,
489        I::Item: Into<Attribute<'b>>,
490    {
491        let mut iter = attributes.into_iter();
492        if let Some(attr) = iter.next() {
493            self.write_attr(attr.into());
494            self.start_tag.extend_attributes(iter);
495        }
496        self
497    }
498
499    /// Pushes a new line inside the element between attributes. Does nothing if
500    /// [`Writer`] was created without indentation support.
501    ///
502    /// # Examples
503    ///
504    /// ```
505    /// use oxixml_xml::writer::Writer;
506    ///
507    /// let mut writer = Writer::new_with_indent(Vec::new(), b' ', 2);
508    /// writer
509    ///   .create_element("element")
510    ///     .with_attribute(("first", "1"))
511    ///     .with_attribute(("second", "2"))
512    ///     .new_line()
513    ///     .with_attributes([("third", "3"), ("fourth", "4")])
514    ///     .write_empty()
515    ///     .expect("write");
516    ///
517    /// assert_eq!(
518    ///     std::str::from_utf8(&writer.into_inner()).expect("utf-8"),
519    ///     "<element first=\"1\" second=\"2\"\n         third=\"3\" fourth=\"4\"/>",
520    /// );
521    /// ```
522    pub fn new_line(mut self) -> Self {
523        if let Some(i) = self.writer.indent.as_mut() {
524            match self.state {
525                // `.new_line()` called just after `.create_element()`. Use element
526                // indent to additionally indent attributes.
527                AttributeIndent::NoneAttributesWritten => {
528                    self.state = AttributeIndent::WriteConfigured(i.indent_size)
529                }
530                AttributeIndent::WriteSpaces(_) => {}
531                // `.new_line()` called when `.with_attribute()` was called at least
532                // once. The spaces should be used to indent.
533                AttributeIndent::Spaces(indent) => {
534                    self.state = AttributeIndent::WriteSpaces(indent)
535                }
536                AttributeIndent::WriteConfigured(_) => {}
537                // `.new_line()` called when `.with_attribute()` was called at least
538                // once. The configured indent characters should be used to indent.
539                AttributeIndent::Configured(indent) => {
540                    self.state = AttributeIndent::WriteConfigured(indent)
541                }
542            }
543            self.start_tag.push_newline();
544        }
545        self
546    }
547
548    /// Writes an attribute and maintains indentation state.
549    fn write_attr<'b>(&mut self, attr: Attribute<'b>) {
550        if let Some(i) = self.writer.indent.as_mut() {
551            // Save the indent that we should use next time `.new_line()` is called.
552            self.state = match self.state {
553                // Neither `.new_line()` nor `.with_attribute()` called yet. If a
554                // newline inside attributes is later requested, indent them by the
555                // length of the tag name, +1 for `<` and +1 for one space.
556                AttributeIndent::NoneAttributesWritten => {
557                    self.start_tag.push_attribute(attr);
558                    AttributeIndent::Spaces(self.start_tag.name().as_ref().len() + 2)
559                }
560                // Indent was requested by a previous `.new_line()`; write it. The
561                // newline was already written.
562                AttributeIndent::WriteSpaces(indent) => {
563                    if self.spaces.len() < indent {
564                        self.spaces.resize(indent, b' ');
565                    }
566                    self.start_tag.push_indent(&self.spaces[..indent]);
567                    self.start_tag.push_attr(attr);
568                    AttributeIndent::Spaces(indent)
569                }
570                // `.new_line()` was not called, but `.with_attribute()` was. Use
571                // the previously calculated indent.
572                AttributeIndent::Spaces(indent) => {
573                    self.start_tag.push_attribute(attr);
574                    AttributeIndent::Spaces(indent)
575                }
576                // Indent was requested by a previous `.new_line()`; write it. The
577                // newline was already written.
578                AttributeIndent::WriteConfigured(indent) => {
579                    self.start_tag.push_indent(i.additional(indent));
580                    self.start_tag.push_attr(attr);
581                    AttributeIndent::Configured(indent)
582                }
583                // `.new_line()` was not called, but `.with_attribute()` was. Use
584                // the previously calculated indent.
585                AttributeIndent::Configured(indent) => {
586                    self.start_tag.push_attribute(attr);
587                    AttributeIndent::Configured(indent)
588                }
589            };
590        } else {
591            self.start_tag.push_attribute(attr);
592        }
593    }
594}
595
596impl<'a, W: Write> ElementWriter<'a, W> {
597    /// Writes some text inside the current element.
598    pub fn write_text_content(self, text: BytesText) -> io::Result<&'a mut Writer<W>> {
599        self.writer
600            .write_event(Event::Start(self.start_tag.borrow()))?;
601        self.writer.write_event(Event::Text(text))?;
602        self.writer
603            .write_event(Event::End(self.start_tag.to_end()))?;
604        Ok(self.writer)
605    }
606
607    /// Writes a CDATA event `<![CDATA[...]]>` inside the current element.
608    pub fn write_cdata_content(self, text: BytesCData) -> io::Result<&'a mut Writer<W>> {
609        self.writer
610            .write_event(Event::Start(self.start_tag.borrow()))?;
611        self.writer.write_event(Event::CData(text))?;
612        self.writer
613            .write_event(Event::End(self.start_tag.to_end()))?;
614        Ok(self.writer)
615    }
616
617    /// Writes a processing instruction `<?...?>` inside the current element.
618    pub fn write_pi_content(self, pi: BytesPI) -> io::Result<&'a mut Writer<W>> {
619        self.writer
620            .write_event(Event::Start(self.start_tag.borrow()))?;
621        self.writer.write_event(Event::PI(pi))?;
622        self.writer
623            .write_event(Event::End(self.start_tag.to_end()))?;
624        Ok(self.writer)
625    }
626
627    /// Writes an empty (self-closing) tag.
628    pub fn write_empty(self) -> io::Result<&'a mut Writer<W>> {
629        self.writer.write_event(Event::Empty(self.start_tag))?;
630        Ok(self.writer)
631    }
632
633    /// Creates a new scope for writing XML inside the current element.
634    pub fn write_inner_content<F>(self, closure: F) -> io::Result<&'a mut Writer<W>>
635    where
636        F: FnOnce(&mut Writer<W>) -> io::Result<()>,
637    {
638        self.writer
639            .write_event(Event::Start(self.start_tag.borrow()))?;
640        closure(self.writer)?;
641        self.writer
642            .write_event(Event::End(self.start_tag.to_end()))?;
643        Ok(self.writer)
644    }
645}
646
647/// Indentation configuration and state used by [`Writer`] when pretty-printing.
648#[derive(Debug, Clone)]
649pub(crate) struct Indentation {
650    /// Whether the next markup event should be preceded by a line break and
651    /// indentation. Toggled off after [`Text`](Event::Text) and
652    /// [`CData`](Event::CData) events so that text stays on the same line.
653    should_line_break: bool,
654    /// The byte used for indentation (e.g. `b' '` or `b'\t'`).
655    indent_char: u8,
656    /// How many `indent_char`s make up a single level of indentation.
657    indent_size: usize,
658    /// A cache of the bytes used for indentation.
659    indents: Vec<u8>,
660    /// The current amount of indentation, in bytes.
661    current_indent_len: usize,
662}
663
664impl Indentation {
665    pub(crate) fn new(indent_char: u8, indent_size: usize) -> Self {
666        Self {
667            should_line_break: false,
668            indent_char,
669            indent_size,
670            indents: vec![indent_char; 128],
671            current_indent_len: 0, // invariant: must remain <= indents.len()
672        }
673    }
674
675    /// Increases indentation by one level.
676    pub(crate) fn grow(&mut self) {
677        self.current_indent_len += self.indent_size;
678        self.ensure(self.current_indent_len);
679    }
680
681    /// Decreases indentation by one level. Does nothing if the level is already
682    /// zero.
683    pub(crate) fn shrink(&mut self) {
684        self.current_indent_len = self.current_indent_len.saturating_sub(self.indent_size);
685    }
686
687    /// Returns the indent string for the current level.
688    pub(crate) fn current(&self) -> &[u8] {
689        &self.indents[..self.current_indent_len]
690    }
691
692    /// Returns the current indent plus an additional indent.
693    pub(crate) fn additional(&mut self, additional_indent: usize) -> &[u8] {
694        let new_len = self.current_indent_len + additional_indent;
695        self.ensure(new_len);
696        &self.indents[..new_len]
697    }
698
699    fn ensure(&mut self, new_len: usize) {
700        if self.indents.len() < new_len {
701            self.indents.resize(new_len, self.indent_char);
702        }
703    }
704}
705
706#[cfg(test)]
707mod tests;