hcl/format/
mod.rs

1//! Format data structures as HCL.
2//!
3//! This module provides the [`Formatter`] type and the convienince functions [`to_string`],
4//! [`to_vec`] and [`to_writer`] for formatting the data structures provided by this crate as HCL.
5//!
6//! For serialization of other Rust data structures implementing [`serde::Serialize`] refer to the
7//! documentation of the [`ser`](crate::ser) module.
8//!
9//! # Examples
10//!
11//! Format an HCL block as string:
12//!
13//! ```
14//! # use std::error::Error;
15//! #
16//! # fn main() -> Result<(), Box<dyn Error>> {
17//! let block = hcl::Block::builder("user")
18//!     .add_label("johndoe")
19//!     .add_attribute(("age", 34))
20//!     .add_attribute(("email", "johndoe@example.com"))
21//!     .build();
22//!
23//! let expected = r#"
24//! user "johndoe" {
25//!   age = 34
26//!   email = "johndoe@example.com"
27//! }
28//! "#.trim_start();
29//!
30//! let formatted = hcl::format::to_string(&block)?;
31//!
32//! assert_eq!(formatted, expected);
33//! #   Ok(())
34//! # }
35//! ```
36
37mod escape;
38mod impls;
39
40use self::escape::{CharEscape, ESCAPE};
41use crate::Result;
42use hcl_primitives::template::escape_markers;
43use std::io;
44
45mod private {
46    pub trait Sealed {}
47}
48
49/// A trait to format data structures as HCL.
50///
51/// This trait is sealed to prevent implementation outside of this crate.
52pub trait Format: private::Sealed {
53    /// Formats a HCL structure using a formatter and writes the result to the provided writer.
54    ///
55    /// # Errors
56    ///
57    /// Formatting the data structure or writing to the writer may fail with an `Error`.
58    fn format<W>(&self, fmt: &mut Formatter<W>) -> Result<()>
59    where
60        W: io::Write;
61
62    /// Formats a HCL structure using a formatter and returns the result as a `Vec<u8>`.
63    ///
64    /// # Errors
65    ///
66    /// Formatting the data structure or writing to the writer may fail with an `Error`.
67    fn format_vec<W>(&self, fmt: &mut Formatter<W>) -> Result<Vec<u8>>
68    where
69        W: io::Write + AsMut<Vec<u8>>,
70    {
71        self.format(fmt)?;
72        // "Drain" the buffer by splitting off all bytes, leaving the formatter's buffer empty
73        // ready for reuse.
74        Ok(fmt.writer.as_mut().split_off(0))
75    }
76
77    /// Formats a HCL structure using a formatter and returns the result as a `String`.
78    ///
79    /// # Errors
80    ///
81    /// Formatting the data structure or writing to the writer may fail with an `Error`.
82    fn format_string<W>(&self, fmt: &mut Formatter<W>) -> Result<String>
83    where
84        W: io::Write + AsMut<Vec<u8>>,
85    {
86        let bytes = self.format_vec(fmt)?;
87        // SAFETY: The `Formatter` never emits invalid UTF-8.
88        Ok(unsafe { String::from_utf8_unchecked(bytes) })
89    }
90}
91
92#[derive(PartialEq)]
93enum FormatState {
94    Initial,
95    AttributeStart,
96    AttributeEnd,
97    BlockStart,
98    BlockEnd,
99    BlockBodyStart,
100}
101
102struct FormatConfig<'a> {
103    indent: &'a [u8],
104    dense: bool,
105    compact_arrays: bool,
106    compact_objects: bool,
107    prefer_ident_keys: bool,
108}
109
110impl Default for FormatConfig<'_> {
111    fn default() -> Self {
112        FormatConfig {
113            indent: b"  ",
114            dense: false,
115            compact_arrays: false,
116            compact_objects: false,
117            prefer_ident_keys: false,
118        }
119    }
120}
121
122/// A pretty printing HCL formatter.
123///
124/// # Examples
125///
126/// Format an HCL block as string:
127///
128/// ```
129/// # use std::error::Error;
130/// #
131/// # fn main() -> Result<(), Box<dyn Error>> {
132/// use hcl::format::{Format, Formatter};
133///
134/// let mut buf = Vec::new();
135/// let mut formatter = Formatter::new(&mut buf);
136///
137/// let block = hcl::Block::builder("user")
138///     .add_label("johndoe")
139///     .add_attribute(("age", 34))
140///     .add_attribute(("email", "johndoe@example.com"))
141///     .build();
142///
143/// block.format(&mut formatter)?;
144///
145/// let expected = r#"
146/// user "johndoe" {
147///   age = 34
148///   email = "johndoe@example.com"
149/// }
150/// "#.trim_start();
151///
152/// let formatted = String::from_utf8(buf)?;
153///
154/// assert_eq!(formatted, expected);
155/// #   Ok(())
156/// # }
157/// ```
158///
159/// The [`builder()`](Formatter::builder) method can be used to construct a custom `Formatter` for
160/// use with a [`Serializer`][Serializer]:
161///
162/// ```
163/// use hcl::{format::Formatter, ser::Serializer};
164/// # let mut writer = Vec::new();
165///
166/// let formatter = Formatter::builder()
167///     .indent(b"  ")
168///     .dense(false)
169///     .build(&mut writer);
170///
171/// let ser = Serializer::with_formatter(formatter);
172/// ```
173///
174/// [Serializer]: ../ser/struct.Serializer.html
175pub struct Formatter<'a, W> {
176    writer: W,
177    config: FormatConfig<'a>,
178    state: FormatState,
179    first_element: bool,
180    current_indent: usize,
181    has_value: bool,
182    compact_mode_level: u64,
183}
184
185/// A builder to create a `Formatter`.
186///
187/// See the documentation of [`Formatter`] for a usage example.
188pub struct FormatterBuilder<'a> {
189    config: FormatConfig<'a>,
190}
191
192impl<'a> FormatterBuilder<'a> {
193    /// Set the indent for indenting nested HCL structures.
194    ///
195    /// The default indentation is two spaces.
196    pub fn indent(mut self, indent: &'a [u8]) -> Self {
197        self.config.indent = indent;
198        self
199    }
200
201    /// If set, blocks are not visually separated by empty lines from attributes and adjacent
202    /// blocks.
203    ///
204    /// Default formatting:
205    ///
206    /// ```hcl
207    /// attr1 = "value1"
208    /// attr2 = "value2"
209    ///
210    /// block1 {}
211    ///
212    /// block2 {}
213    /// ```
214    ///
215    /// Dense formatting:
216    ///
217    /// ```hcl
218    /// attr1 = "value1"
219    /// attr2 = "value2"
220    /// block1 {}
221    /// block2 {}
222    /// ```
223    pub fn dense(mut self, yes: bool) -> Self {
224        self.config.dense = yes;
225        self
226    }
227
228    /// If set, arrays and objects are formatted in a more compact way.
229    ///
230    /// See the method documation of [`compact_arrays`][FormatterBuilder::compact_arrays] and
231    /// [`compact_objects`][FormatterBuilder::compact_objects].
232    pub fn compact(self, yes: bool) -> Self {
233        self.compact_arrays(yes).compact_objects(yes)
234    }
235
236    /// Controls the array formatting.
237    ///
238    /// By default, array elements are separated by newlines:
239    ///
240    /// ```hcl
241    /// array = [
242    ///   1,
243    ///   2,
244    ///   3,
245    /// ]
246    /// ```
247    ///
248    /// When compact array formatting is enabled no newlines are inserted between elements:
249    ///
250    /// ```hcl
251    /// array = [1, 2, 3]
252    /// ```
253    pub fn compact_arrays(mut self, yes: bool) -> Self {
254        self.config.compact_arrays = yes;
255        self
256    }
257
258    /// Controls the object formatting.
259    ///
260    /// By default, object items are separated by newlines:
261    ///
262    /// ```hcl
263    /// object = {
264    ///   one = "foo"
265    ///   two = "bar"
266    ///   three = "baz"
267    /// }
268    /// ```
269    ///
270    /// When compact object formatting is enabled no newlines are inserted between items:
271    ///
272    /// ```hcl
273    /// object = { one = "foo", two = "bar", three = "baz" }
274    /// ```
275    pub fn compact_objects(mut self, yes: bool) -> Self {
276        self.config.compact_objects = yes;
277        self
278    }
279
280    /// Controls the object key quoting.
281    ///
282    /// By default, object keys are formatted as quoted strings (unless they are of variant
283    /// [`ObjectKey::Identifier`][ident-variant]).
284    ///
285    /// ```hcl
286    /// object = {
287    ///   "foo" = 1
288    ///   "bar baz" = 2
289    /// }
290    /// ```
291    ///
292    /// When identifier keys are preferred, object keys that are also valid HCL identifiers are
293    /// not quoted:
294    ///
295    /// ```hcl
296    /// object = {
297    ///   foo = 1
298    ///   "bar baz" = 2
299    /// }
300    /// ```
301    ///
302    /// [ident-variant]: crate::expr::ObjectKey::Identifier
303    pub fn prefer_ident_keys(mut self, yes: bool) -> Self {
304        self.config.prefer_ident_keys = yes;
305        self
306    }
307
308    /// Consumes the `FormatterBuilder` and turns it into a `Formatter` which writes HCL to the
309    /// provided writer.
310    pub fn build<W>(self, writer: W) -> Formatter<'a, W>
311    where
312        W: io::Write,
313    {
314        Formatter {
315            writer,
316            config: self.config,
317            state: FormatState::Initial,
318            first_element: false,
319            current_indent: 0,
320            has_value: false,
321            compact_mode_level: 0,
322        }
323    }
324
325    /// Consumes the `FormatterBuilder` and turns it into a `Formatter` which is specialized to use
326    /// a pre-allocated `Vec<u8>` as internal buffer.
327    ///
328    /// The returned formatter can be passed to the [`format_string`][Format::format_string] or
329    /// [`format_vec`][Format::format_vec] method of types implementing [`Format`].
330    ///
331    /// Alternatively, the internal buffer can be obtained by calling
332    /// [`into_inner`][Formatter::into_inner] on the returned `Formatter` after passing it to the
333    /// [`format`][Format::format] method of a type implementing [`Format`].
334    ///
335    /// # Examples
336    ///
337    /// ```
338    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
339    /// use hcl::format::{Format, Formatter};
340    /// use hcl::structure::Attribute;
341    ///
342    /// let mut formatter = Formatter::builder()
343    ///     .compact_arrays(true)
344    ///     .build_vec();
345    ///
346    /// let attr = Attribute::new("foo", vec![1, 2, 3]);
347    ///
348    /// assert_eq!(attr.format_string(&mut formatter)?, "foo = [1, 2, 3]\n");
349    /// #    Ok(())
350    /// # }
351    /// ```
352    pub fn build_vec(self) -> Formatter<'a, Vec<u8>> {
353        let vec = Vec::with_capacity(128);
354        self.build(vec)
355    }
356}
357
358impl Default for Formatter<'_, Vec<u8>> {
359    /// Creates the default `Formatter` which is specialized to use a pre-allocated `Vec<u8>` as
360    /// internal buffer.
361    ///
362    /// The formatter can be passed to the [`format_string`][Format::format_string] or
363    /// [`format_vec`][Format::format_vec] method of types implementing [`Format`].
364    ///
365    /// Alternatively, the internal buffer can be obtained by calling
366    /// [`into_inner`][Formatter::into_inner] after passing it to the [`format`][Format::format]
367    /// method of a type implementing [`Format`].
368    fn default() -> Self {
369        Formatter::builder().build_vec()
370    }
371}
372
373// Public API.
374impl<'a> Formatter<'a, ()> {
375    /// Creates a new [`FormatterBuilder`] to start building a new `Formatter`.
376    pub fn builder() -> FormatterBuilder<'a> {
377        FormatterBuilder {
378            config: FormatConfig::default(),
379        }
380    }
381}
382
383// Public API.
384impl<'a, W> Formatter<'a, W>
385where
386    W: io::Write,
387{
388    /// Creates a new `Formatter` which writes HCL to the provided writer.
389    pub fn new(writer: W) -> Formatter<'a, W> {
390        Formatter::builder().build(writer)
391    }
392
393    /// Takes ownership of the `Formatter` and returns the underlying writer.
394    pub fn into_inner(self) -> W {
395        self.writer
396    }
397}
398
399// Internal formatter API.
400impl<W> Formatter<'_, W>
401where
402    W: io::Write,
403{
404    /// Writes `null` to the writer.
405    fn write_null(&mut self) -> Result<()> {
406        self.write_bytes(b"null")
407    }
408
409    /// Writes a boolean value to the writer.
410    fn write_bool(&mut self, value: bool) -> Result<()> {
411        let s = if value {
412            b"true" as &[u8]
413        } else {
414            b"false" as &[u8]
415        };
416        self.write_bytes(s)
417    }
418
419    /// Writes an integer value to the writer.
420    fn write_int<T>(&mut self, value: T) -> Result<()>
421    where
422        T: itoa::Integer,
423    {
424        let mut buffer = itoa::Buffer::new();
425        let s = buffer.format(value);
426        self.write_bytes(s.as_bytes())
427    }
428
429    /// Writes a quoted string to the writer.
430    fn write_quoted_string(&mut self, s: &str) -> Result<()> {
431        self.write_bytes(b"\"")?;
432        self.write_string_fragment(s)?;
433        self.write_bytes(b"\"")
434    }
435
436    /// Writes a quoted string to the writer after escaping it.
437    fn write_quoted_string_escaped(&mut self, s: &str) -> Result<()> {
438        self.write_bytes(b"\"")?;
439        self.write_escaped_string(s)?;
440        self.write_bytes(b"\"")
441    }
442
443    /// Writes a string fragment to the writer. No escaping occurs.
444    fn write_string_fragment(&mut self, s: &str) -> Result<()> {
445        self.write_bytes(s.as_bytes())
446    }
447
448    /// Writes a string to the writer and escapes control characters and quotes that might be
449    /// contained in it.
450    fn write_escaped_string(&mut self, value: &str) -> Result<()> {
451        let value = escape_markers(value);
452        let bytes = value.as_bytes();
453
454        let mut start = 0;
455
456        for (i, &byte) in bytes.iter().enumerate() {
457            let escape = ESCAPE[byte as usize];
458            if escape == 0 {
459                continue;
460            }
461
462            if start < i {
463                self.write_string_fragment(&value[start..i])?;
464            }
465
466            let char_escape = CharEscape::from_escape_table(escape, byte);
467            char_escape.write_escaped(&mut self.writer)?;
468
469            start = i + 1;
470        }
471
472        if start != bytes.len() {
473            self.write_string_fragment(&value[start..])?;
474        }
475
476        Ok(())
477    }
478
479    /// Signals the start of an array to the formatter.
480    fn begin_array(&mut self) -> Result<()> {
481        if !self.compact_arrays() {
482            self.current_indent += 1;
483        }
484        self.has_value = false;
485        self.first_element = true;
486        self.write_bytes(b"[")
487    }
488
489    /// Signals the start of an array value to the formatter.
490    fn begin_array_value(&mut self) -> Result<()> {
491        if self.first_element {
492            self.first_element = false;
493            if !self.compact_arrays() {
494                self.write_bytes(b"\n")?;
495                self.write_indent(self.current_indent)?;
496            }
497        } else if self.compact_arrays() {
498            self.write_bytes(b", ")?;
499        } else {
500            self.write_bytes(b",\n")?;
501            self.write_indent(self.current_indent)?;
502        }
503
504        Ok(())
505    }
506
507    /// Signals the end of an array value to the formatter.
508    fn end_array_value(&mut self) -> Result<()> {
509        self.has_value = true;
510        Ok(())
511    }
512
513    /// Signals the end of an array to the formatter.
514    fn end_array(&mut self) -> Result<()> {
515        if !self.compact_arrays() {
516            self.current_indent -= 1;
517
518            if self.has_value {
519                self.write_bytes(b"\n")?;
520                self.write_indent(self.current_indent)?;
521            }
522        }
523
524        self.write_bytes(b"]")
525    }
526
527    /// Signals the start of an object to the formatter.
528    fn begin_object(&mut self) -> Result<()> {
529        if !self.compact_objects() {
530            self.current_indent += 1;
531        }
532        self.has_value = false;
533        self.first_element = true;
534        self.write_bytes(b"{")
535    }
536
537    /// Signals the start of an object key to the formatter.
538    fn begin_object_key(&mut self) -> Result<()> {
539        if self.first_element {
540            self.first_element = false;
541            if self.compact_objects() {
542                self.write_bytes(b" ")?;
543            } else {
544                self.write_bytes(b"\n")?;
545                self.write_indent(self.current_indent)?;
546            }
547        } else if self.compact_objects() {
548            self.write_bytes(b", ")?;
549        } else {
550            self.write_bytes(b"\n")?;
551            self.write_indent(self.current_indent)?;
552        }
553
554        Ok(())
555    }
556
557    /// Signals the start of an object value to the formatter.
558    fn begin_object_value(&mut self) -> Result<()> {
559        self.write_bytes(b" = ")
560    }
561
562    /// Signals the end of an object value to the formatter.
563    fn end_object_value(&mut self) -> Result<()> {
564        self.end_array_value()
565    }
566
567    /// Signals the end of an object to the formatter.
568    fn end_object(&mut self) -> Result<()> {
569        if self.compact_objects() {
570            if self.has_value {
571                self.write_bytes(b" ")?;
572            }
573        } else {
574            self.current_indent -= 1;
575
576            if self.has_value {
577                self.write_bytes(b"\n")?;
578                self.write_indent(self.current_indent)?;
579            }
580        }
581
582        self.write_bytes(b"}")
583    }
584
585    /// Signals the start of an attribute to the formatter.
586    fn begin_attribute(&mut self) -> Result<()> {
587        self.maybe_write_newline(FormatState::AttributeStart)?;
588        self.write_indent(self.current_indent)
589    }
590
591    /// Signals the start of an attribute value to the formatter.
592    fn begin_attribute_value(&mut self) -> Result<()> {
593        self.write_bytes(b" = ")
594    }
595
596    /// Signals the end of an attribute to the formatter.
597    fn end_attribute(&mut self) -> Result<()> {
598        self.state = FormatState::AttributeEnd;
599        self.write_bytes(b"\n")
600    }
601
602    /// Signals the start of a block to the formatter.
603    fn begin_block(&mut self) -> Result<()> {
604        self.maybe_write_newline(FormatState::BlockStart)?;
605        self.write_indent(self.current_indent)
606    }
607
608    /// Signals the start of a block body to the formatter.
609    fn begin_block_body(&mut self) -> Result<()> {
610        self.current_indent += 1;
611        self.state = FormatState::BlockBodyStart;
612        self.write_bytes(b" {")
613    }
614
615    /// Signals the end of a block to the formatter.
616    fn end_block(&mut self) -> Result<()> {
617        self.state = FormatState::BlockEnd;
618        self.current_indent -= 1;
619        self.write_indent(self.current_indent)?;
620        self.write_bytes(b"}\n")
621    }
622
623    // Conditionally writes a newline character depending on the formatter configuration and the
624    // current and next state. Updates the state to `next_state`.
625    fn maybe_write_newline(&mut self, next_state: FormatState) -> Result<()> {
626        let newline = match &self.state {
627            FormatState::AttributeEnd if !self.config.dense => {
628                matches!(next_state, FormatState::BlockStart)
629            }
630            FormatState::BlockEnd if !self.config.dense => {
631                matches!(
632                    next_state,
633                    FormatState::BlockStart | FormatState::AttributeStart
634                )
635            }
636            other => matches!(other, FormatState::BlockBodyStart),
637        };
638
639        if newline {
640            self.write_bytes(b"\n")?;
641        }
642
643        self.state = next_state;
644        Ok(())
645    }
646
647    fn write_indent(&mut self, n: usize) -> Result<()> {
648        for _ in 0..n {
649            self.write_bytes(self.config.indent)?;
650        }
651
652        Ok(())
653    }
654
655    fn write_indented(&mut self, n: usize, s: &str) -> Result<()> {
656        for (i, line) in s.lines().enumerate() {
657            if i > 0 {
658                self.write_bytes(b"\n")?;
659            }
660
661            self.write_indent(n)?;
662            self.write_string_fragment(line)?;
663        }
664
665        if s.ends_with('\n') {
666            self.write_bytes(b"\n")?;
667        }
668
669        Ok(())
670    }
671
672    fn write_bytes(&mut self, buf: &[u8]) -> Result<()> {
673        self.writer.write_all(buf)?;
674        Ok(())
675    }
676
677    /// Enables compact mode, runs the closure and disables compact mode again unless it's enabled
678    /// via another call to `with_compact_mode`.
679    ///
680    /// This is mostly used for serializing array and object function arguments.
681    fn with_compact_mode<F>(&mut self, f: F) -> Result<()>
682    where
683        F: FnOnce(&mut Self) -> Result<()>,
684    {
685        self.compact_mode_level += 1;
686        let result = f(self);
687        self.compact_mode_level -= 1;
688        result
689    }
690
691    fn compact_arrays(&self) -> bool {
692        self.config.compact_arrays || self.in_compact_mode()
693    }
694
695    fn compact_objects(&self) -> bool {
696        self.config.compact_objects || self.in_compact_mode()
697    }
698
699    fn in_compact_mode(&self) -> bool {
700        self.compact_mode_level > 0
701    }
702}
703
704/// Format the given value as an HCL byte vector.
705///
706/// If you need to serialize custom data structures implementing [`serde::Serialize`] use
707/// [`hcl::to_vec`](crate::to_vec) instead.
708///
709/// # Errors
710///
711/// Formatting a value as byte vector cannot fail.
712pub fn to_vec<T>(value: &T) -> Result<Vec<u8>>
713where
714    T: ?Sized + Format,
715{
716    let mut formatter = Formatter::default();
717    value.format_vec(&mut formatter)
718}
719
720/// Format the given value as an HCL string.
721///
722/// If you need to serialize custom data structures implementing [`serde::Serialize`] use
723/// [`hcl::to_string`](crate::to_string) instead.
724///
725/// # Errors
726///
727/// Formatting a value as string cannot fail.
728pub fn to_string<T>(value: &T) -> Result<String>
729where
730    T: ?Sized + Format,
731{
732    let mut formatter = Formatter::default();
733    value.format_string(&mut formatter)
734}
735
736/// Format the given value as HCL into the IO stream.
737///
738/// If you need to serialize custom data structures implementing [`serde::Serialize`] use
739/// [`hcl::to_writer`](crate::to_writer) instead.
740///
741/// # Errors
742///
743/// Formatting fails if any operation on the writer fails.
744pub fn to_writer<W, T>(writer: W, value: &T) -> Result<()>
745where
746    W: io::Write,
747    T: ?Sized + Format,
748{
749    let mut formatter = Formatter::new(writer);
750    value.format(&mut formatter)
751}
752
753/// Format the given value as an interpolated HCL string.
754///
755/// It is the callers responsiblity to ensure that the value is not an HCL structure (i.e. `Body`,
756/// `Structure`, `Block` or `Attribute`). Otherwise this will produce invalid HCL.
757///
758/// # Errors
759///
760/// Formatting a value as string cannot fail.
761pub(crate) fn to_interpolated_string<T>(value: &T) -> Result<String>
762where
763    T: ?Sized + Format,
764{
765    let mut formatter = Formatter::builder().compact(true).build_vec();
766    formatter.writer.extend([b'$', b'{']);
767    let mut string = value.format_string(&mut formatter)?;
768    string.push('}');
769    Ok(string)
770}
771
772#[cfg(test)]
773mod tests {
774    use super::to_interpolated_string;
775    use crate::expr::{BinaryOp, BinaryOperator, FuncCall};
776    use pretty_assertions::assert_eq;
777
778    #[test]
779    fn format_interpolated_string() {
780        let binop = BinaryOp::new(1, BinaryOperator::Plus, 1);
781        assert_eq!(to_interpolated_string(&binop).unwrap(), "${1 + 1}");
782
783        let expr = FuncCall::builder("add").arg(1).arg(1).build();
784        assert_eq!(to_interpolated_string(&expr).unwrap(), "${add(1, 1)}");
785    }
786}