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 & 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;