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