hcl_edit/structure/
body.rs

1use crate::encode::{EncodeDecorated, EncodeState, NO_DECOR};
2use crate::structure::{Attribute, AttributeMut, Block, Structure, StructureMut};
3use crate::{parser, Decor};
4use std::fmt;
5use std::ops::Range;
6use std::str::FromStr;
7
8/// An owning iterator over the elements of a `Body`.
9///
10/// Values of this type are created by the [`into_iter`] method on [`Body`] (provided by the
11/// [`IntoIterator`] trait). See its documentation for more.
12///
13/// [`into_iter`]: IntoIterator::into_iter
14/// [`IntoIterator`]: core::iter::IntoIterator
15pub type IntoIter = Box<dyn Iterator<Item = Structure>>;
16
17/// An iterator over the elements of a `Body`.
18///
19/// Values of this type are created by the [`iter`] method on [`Body`]. See its documentation
20/// for more.
21///
22/// [`iter`]: Body::iter
23pub type Iter<'a> = Box<dyn Iterator<Item = &'a Structure> + 'a>;
24
25/// A mutable iterator over the elements of a `Body`.
26///
27/// Values of this type are created by the [`iter_mut`] method on [`Body`]. See its
28/// documentation for more.
29///
30/// [`iter_mut`]: Body::iter_mut
31pub type IterMut<'a> = Box<dyn Iterator<Item = StructureMut<'a>> + 'a>;
32
33/// An owning iterator over the `Attribute`s within a `Body`.
34///
35/// Values of this type are created by the [`into_attributes`] method on [`Body`]. See its
36/// documentation for more.
37///
38/// [`into_attributes`]: Body::into_attributes
39pub type IntoAttributes = Box<dyn Iterator<Item = Attribute>>;
40
41/// An iterator over the `Attribute`s within a `Body`.
42///
43/// Values of this type are created by the [`attributes`] method on [`Body`]. See its documentation
44/// for more.
45///
46/// [`attributes`]: Body::attributes
47pub type Attributes<'a> = Box<dyn Iterator<Item = &'a Attribute> + 'a>;
48
49/// A mutable iterator over the `Attribute`s within a `Body`.
50///
51/// Values of this type are created by the [`attributes_mut`] method on [`Body`]. See its
52/// documentation for more.
53///
54/// [`attributes_mut`]: Body::attributes_mut
55pub type AttributesMut<'a> = Box<dyn Iterator<Item = AttributeMut<'a>> + 'a>;
56
57/// An owning iterator over the `Block`s within a `Body`.
58///
59/// Values of this type are created by the [`into_blocks`] method on [`Body`]. See its
60/// documentation for more.
61///
62/// [`into_blocks`]: Body::into_blocks
63pub type IntoBlocks = Box<dyn Iterator<Item = Block>>;
64
65/// An iterator over the `Block`s within a `Body`.
66///
67/// Values of this type are created by the [`blocks`] method on [`Body`]. See its documentation
68/// for more.
69///
70/// [`blocks`]: Body::blocks
71pub type Blocks<'a> = Box<dyn Iterator<Item = &'a Block> + 'a>;
72
73/// A mutable iterator over the `Block`s within a `Body`.
74///
75/// Values of this type are created by the [`blocks_mut`] method on [`Body`]. See its
76/// documentation for more.
77///
78/// [`blocks_mut`]: Body::blocks_mut
79pub type BlocksMut<'a> = Box<dyn Iterator<Item = &'a mut Block> + 'a>;
80
81/// Represents an HCL config file body.
82///
83/// A `Body` consists of zero or more [`Attribute`] and [`Block`] HCL structures.
84#[derive(Debug, Clone, Default, Eq)]
85pub struct Body {
86    structures: Vec<Structure>,
87    prefer_oneline: bool,
88    prefer_omit_trailing_newline: bool,
89    decor: Decor,
90    span: Option<Range<usize>>,
91}
92
93impl Body {
94    /// Constructs a new, empty `Body`.
95    #[inline]
96    pub fn new() -> Self {
97        Body::default()
98    }
99
100    /// Constructs a new, empty `Body` with at least the specified capacity.
101    #[inline]
102    pub fn with_capacity(capacity: usize) -> Self {
103        Body {
104            structures: Vec::with_capacity(capacity),
105            ..Default::default()
106        }
107    }
108
109    /// Creates a new [`BodyBuilder`] to start building a new `Body`.
110    #[inline]
111    pub fn builder() -> BodyBuilder {
112        BodyBuilder::default()
113    }
114
115    /// Returns `true` if the body contains no structures.
116    #[inline]
117    pub fn is_empty(&self) -> bool {
118        self.structures.is_empty()
119    }
120
121    /// Returns the number of structures in the body, also referred to as its 'length'.
122    #[inline]
123    pub fn len(&self) -> usize {
124        self.structures.len()
125    }
126
127    /// Clears the body, removing all structures.
128    #[inline]
129    pub fn clear(&mut self) {
130        self.structures.clear();
131    }
132
133    /// Returns a reference to the structure at the given index, or `None` if the index is out of
134    /// bounds.
135    #[inline]
136    pub fn get(&self, index: usize) -> Option<&Structure> {
137        self.structures.get(index)
138    }
139
140    /// Returns a mutable reference to the structure at the given index, or `None` if the index is
141    /// out of bounds.
142    #[inline]
143    pub fn get_mut(&mut self, index: usize) -> Option<&mut Structure> {
144        self.structures.get_mut(index)
145    }
146
147    /// Returns `true` if the body contains an attribute with given key.
148    ///
149    /// # Example
150    ///
151    /// ```
152    /// use hcl_edit::structure::{Attribute, Body};
153    /// use hcl_edit::Ident;
154    ///
155    /// let body = Body::from_iter([Attribute::new(Ident::new("foo"), "bar")]);
156    /// assert!(body.has_attribute("foo"));
157    /// assert!(!body.has_attribute("bar"));
158    /// ```
159    #[inline]
160    pub fn has_attribute(&self, key: &str) -> bool {
161        self.get_attribute(key).is_some()
162    }
163
164    /// Returns `true` if the body contains blocks with given identifier.
165    ///
166    /// # Example
167    ///
168    /// ```
169    /// use hcl_edit::structure::{Block, Body};
170    /// use hcl_edit::Ident;
171    ///
172    /// let body = Body::from_iter([Block::new(Ident::new("foo"))]);
173    /// assert!(body.has_blocks("foo"));
174    /// assert!(!body.has_blocks("bar"));
175    /// ```
176    #[inline]
177    pub fn has_blocks(&self, ident: &str) -> bool {
178        self.get_blocks(ident).next().is_some()
179    }
180
181    /// Returns a reference to the `Attribute` with given key if it exists, otherwise `None`.
182    ///
183    /// # Example
184    ///
185    /// ```
186    /// use hcl_edit::structure::{Attribute, Body};
187    /// use hcl_edit::Ident;
188    ///
189    /// let mut body = Body::new();
190    ///
191    /// assert!(body.get_attribute("foo").is_none());
192    ///
193    /// let foo = Attribute::new(Ident::new("foo"), "bar");
194    ///
195    /// body.push(foo.clone());
196    ///
197    /// assert_eq!(body.get_attribute("foo"), Some(&foo));
198    /// ```
199    pub fn get_attribute(&self, key: &str) -> Option<&Attribute> {
200        self.structures
201            .iter()
202            .filter_map(Structure::as_attribute)
203            .find(|attr| attr.has_key(key))
204    }
205
206    /// Returns a mutable reference to the `Attribute` with given key if it exists, otherwise
207    /// `None`.
208    ///
209    /// # Example
210    ///
211    /// ```
212    /// use hcl_edit::expr::Expression;
213    /// use hcl_edit::structure::{Attribute, Body};
214    /// use hcl_edit::Ident;
215    ///
216    /// let mut body = Body::new();
217    ///
218    /// assert!(body.get_attribute("foo").is_none());
219    ///
220    /// let foo = Attribute::new(Ident::new("foo"), "bar");
221    ///
222    /// body.push(foo.clone());
223    ///
224    /// if let Some(mut attr) = body.get_attribute_mut("foo") {
225    ///     *attr.value_mut() = Expression::from("baz");
226    /// }
227    ///
228    /// assert_eq!(body.get_attribute("foo"), Some(&Attribute::new(Ident::new("foo"), "baz")));
229    /// ```
230    pub fn get_attribute_mut(&mut self, key: &str) -> Option<AttributeMut<'_>> {
231        self.structures
232            .iter_mut()
233            .filter_map(Structure::as_attribute_mut)
234            .find(|attr| attr.has_key(key))
235            .map(AttributeMut::new)
236    }
237
238    /// Returns an iterator visiting all `Block`s with the given identifier. The iterator element
239    /// type is `&'a Block`.
240    ///
241    /// # Example
242    ///
243    /// ```
244    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
245    /// use hcl_edit::structure::Body;
246    ///
247    /// let input = r#"
248    /// resource "aws_s3_bucket" "bucket" {}
249    ///
250    /// variable "name" {}
251    ///
252    /// resource "aws_instance" "instance" {}
253    /// "#;
254    ///
255    /// let body: Body = input.parse()?;
256    ///
257    /// let resources: Body = body.get_blocks("resource").cloned().collect();
258    ///
259    /// let expected = r#"
260    /// resource "aws_s3_bucket" "bucket" {}
261    ///
262    /// resource "aws_instance" "instance" {}
263    /// "#;
264    ///
265    /// assert_eq!(resources.to_string(), expected);
266    /// #   Ok(())
267    /// # }
268    /// ```
269    pub fn get_blocks<'a>(&'a self, ident: &'a str) -> Blocks<'a> {
270        Box::new(
271            self.structures
272                .iter()
273                .filter_map(Structure::as_block)
274                .filter(|block| block.has_ident(ident)),
275        )
276    }
277
278    /// Returns an iterator visiting all `Block`s with the given identifier. The iterator element
279    /// type is `&'a mut Block`.
280    ///
281    /// # Example
282    ///
283    /// ```
284    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
285    /// use hcl_edit::expr::{Traversal, TraversalOperator};
286    /// use hcl_edit::structure::{Attribute, Body};
287    /// use hcl_edit::Ident;
288    ///
289    /// let input = r#"
290    /// resource "aws_s3_bucket" "bucket" {}
291    ///
292    /// variable "name" {}
293    ///
294    /// resource "aws_db_instance" "db_instance" {}
295    /// "#;
296    ///
297    /// let mut body: Body = input.parse()?;
298    ///
299    /// for block in body.get_blocks_mut("resource") {
300    ///     let operators = vec![TraversalOperator::GetAttr(Ident::new("name").into()).into()];
301    ///     let value = Traversal::new(Ident::new("var"), operators);
302    ///     block.body.push(Attribute::new(Ident::new("name"), value));
303    /// }
304    ///
305    /// let expected = r#"
306    /// resource "aws_s3_bucket" "bucket" { name = var.name }
307    ///
308    /// variable "name" {}
309    ///
310    /// resource "aws_db_instance" "db_instance" { name = var.name }
311    /// "#;
312    ///
313    /// assert_eq!(body.to_string(), expected);
314    /// #   Ok(())
315    /// # }
316    /// ```
317    pub fn get_blocks_mut<'a>(&'a mut self, ident: &'a str) -> BlocksMut<'a> {
318        Box::new(
319            self.structures
320                .iter_mut()
321                .filter_map(Structure::as_block_mut)
322                .filter(|block| block.has_ident(ident)),
323        )
324    }
325
326    /// Inserts a structure at position `index` within the body, shifting all structures after it
327    /// to the right.
328    ///
329    /// If it is attempted to insert an `Attribute` which already exists in the body, it is ignored
330    /// and not inserted. For a fallible variant of this function see [`Body::try_insert`].
331    ///
332    /// # Panics
333    ///
334    /// Panics if `index > len`.
335    #[inline]
336    pub fn insert(&mut self, index: usize, structure: impl Into<Structure>) {
337        _ = self.try_insert(index, structure);
338    }
339
340    /// Inserts a structure at position `index` within the body, shifting all structures after it
341    /// to the right.
342    ///
343    /// # Errors
344    ///
345    /// If it is attempted to insert an `Attribute` which already exists in the body, it is not
346    /// inserted and returned as the `Result`'s `Err` variant instead.
347    ///
348    /// # Panics
349    ///
350    /// Panics if `index > len`.
351    ///
352    /// # Example
353    ///
354    /// ```
355    /// use hcl_edit::structure::{Attribute, Body};
356    /// use hcl_edit::Ident;
357    ///
358    /// let mut body = Body::new();
359    ///
360    /// body.push(Attribute::new(Ident::new("foo"), "bar"));
361    /// assert!(body.try_insert(0, Attribute::new(Ident::new("bar"), "baz")).is_ok());
362    /// assert_eq!(body.len(), 2);
363    ///
364    /// let duplicate_attr = Attribute::new(Ident::new("foo"), "baz");
365    ///
366    /// assert_eq!(body.try_insert(0, duplicate_attr.clone()), Err(duplicate_attr));
367    /// ```
368    #[inline]
369    pub fn try_insert(
370        &mut self,
371        index: usize,
372        structure: impl Into<Structure>,
373    ) -> Result<(), Attribute> {
374        match structure.into() {
375            Structure::Attribute(attr) if self.has_attribute(&attr.key) => Err(attr),
376            structure => {
377                self.structures.insert(index, structure);
378                Ok(())
379            }
380        }
381    }
382
383    /// Appends a structure to the back of the body.
384    ///
385    /// If it is attempted to append an `Attribute` which already exists in the body, it is ignored
386    /// and not appended. For a fallible variant of this function see [`Body::try_push`].
387    ///
388    /// # Panics
389    ///
390    /// Panics if the new capacity exceeds `isize::MAX` bytes.
391    #[inline]
392    pub fn push(&mut self, structure: impl Into<Structure>) {
393        _ = self.try_push(structure);
394    }
395
396    /// Appends a structure to the back of the body.
397    ///
398    /// # Errors
399    ///
400    /// If it is attempted to append an `Attribute` which already exists in the body, it is not
401    /// appended and returned as the `Result`'s `Err` variant instead.
402    ///
403    /// # Panics
404    ///
405    /// Panics if the new capacity exceeds `isize::MAX` bytes.
406    ///
407    /// # Example
408    ///
409    /// ```
410    /// use hcl_edit::structure::{Attribute, Body};
411    /// use hcl_edit::Ident;
412    ///
413    /// let mut body = Body::new();
414    ///
415    /// assert!(body.try_push(Attribute::new(Ident::new("foo"), "bar")).is_ok());
416    /// assert!(body.try_push(Attribute::new(Ident::new("bar"), "baz")).is_ok());
417    /// assert_eq!(body.len(), 2);
418    ///
419    /// let duplicate_attr = Attribute::new(Ident::new("foo"), "baz");
420    ///
421    /// assert_eq!(body.try_push(duplicate_attr.clone()), Err(duplicate_attr));
422    /// ```
423    #[inline]
424    pub fn try_push(&mut self, structure: impl Into<Structure>) -> Result<(), Attribute> {
425        match structure.into() {
426            Structure::Attribute(attr) if self.has_attribute(&attr.key) => Err(attr),
427            structure => {
428                self.structures.push(structure);
429                Ok(())
430            }
431        }
432    }
433
434    /// Removes the last structure from the body and returns it, or [`None`] if it is empty.
435    #[inline]
436    pub fn pop(&mut self) -> Option<Structure> {
437        self.structures.pop()
438    }
439
440    /// Removes and returns the structure at position `index` within the body, shifting all
441    /// elements after it to the left.
442    ///
443    /// Like `Vec::remove`, the structure is removed by shifting all of the structures that follow
444    /// it, preserving their relative order. **This perturbs the index of all of those elements!**
445    ///
446    /// # Panics
447    ///
448    /// Panics if `index` is out of bounds.
449    #[inline]
450    pub fn remove(&mut self, index: usize) -> Structure {
451        self.structures.remove(index)
452    }
453
454    /// Removes and returns the attribute with given `key`.
455    ///
456    /// # Example
457    ///
458    /// ```
459    /// use hcl_edit::structure::{Attribute, Block, Body};
460    /// use hcl_edit::Ident;
461    ///
462    /// let mut body = Body::new();
463    /// body.push(Block::new(Ident::new("block")));
464    ///
465    /// assert!(body.remove_attribute("foo").is_none());
466    ///
467    /// let foo = Attribute::new(Ident::new("foo"), "bar");
468    ///
469    /// body.push(foo.clone());
470    ///
471    /// assert_eq!(body.len(), 2);
472    /// assert_eq!(body.remove_attribute("foo"), Some(foo));
473    /// assert_eq!(body.len(), 1);
474    /// ```
475    pub fn remove_attribute(&mut self, key: &str) -> Option<Attribute> {
476        self.structures
477            .iter()
478            .position(|structure| {
479                structure
480                    .as_attribute()
481                    .is_some_and(|attr| attr.has_key(key))
482            })
483            .and_then(|index| self.remove(index).into_attribute().ok())
484    }
485
486    /// Removes and returns all blocks with given `ident`.
487    ///
488    /// # Example
489    ///
490    /// ```
491    /// use hcl_edit::structure::{Attribute, Block, Body};
492    /// use hcl_edit::Ident;
493    ///
494    /// let mut body = Body::builder()
495    ///     .attribute(Attribute::new(Ident::new("foo"), "bar"))
496    ///     .block(
497    ///         Block::builder(Ident::new("resource"))
498    ///             .labels(["aws_s3_bucket", "bucket"])
499    ///     )
500    ///     .block(Block::builder(Ident::new("variable")).label("name"))
501    ///     .block(
502    ///         Block::builder(Ident::new("resource"))
503    ///             .labels(["aws_db_instance", "db_instance"])
504    ///     )
505    ///     .build();
506    ///
507    /// let resources = body.remove_blocks("resource");
508    ///
509    /// assert_eq!(
510    ///     resources,
511    ///     vec![
512    ///         Block::builder(Ident::new("resource"))
513    ///             .labels(["aws_s3_bucket", "bucket"])
514    ///             .build(),
515    ///         Block::builder(Ident::new("resource"))
516    ///             .labels(["aws_db_instance", "db_instance"])
517    ///             .build()
518    ///     ]
519    /// );
520    ///
521    /// assert_eq!(
522    ///     body,
523    ///     Body::builder()
524    ///         .attribute(Attribute::new(Ident::new("foo"), "bar"))
525    ///         .block(Block::builder(Ident::new("variable")).label("name"))
526    ///         .build()
527    /// );
528    /// ```
529    pub fn remove_blocks(&mut self, ident: &str) -> Vec<Block> {
530        let mut removed = Vec::new();
531
532        while let Some(block) = self.remove_block(ident) {
533            removed.push(block);
534        }
535
536        removed
537    }
538
539    fn remove_block(&mut self, ident: &str) -> Option<Block> {
540        self.structures
541            .iter()
542            .position(|structure| {
543                structure
544                    .as_block()
545                    .is_some_and(|block| block.has_ident(ident))
546            })
547            .and_then(|index| self.remove(index).into_block().ok())
548    }
549
550    /// An iterator visiting all body structures in insertion order. The iterator element type is
551    /// `&'a Structure`.
552    #[inline]
553    pub fn iter(&self) -> Iter<'_> {
554        Box::new(self.structures.iter())
555    }
556
557    /// An iterator visiting all body structures in insertion order, with mutable references to the
558    /// values. The iterator element type is `StructureMut<'a>`.
559    #[inline]
560    pub fn iter_mut(&mut self) -> IterMut<'_> {
561        Box::new(self.structures.iter_mut().map(StructureMut::new))
562    }
563
564    /// An owning iterator visiting all `Attribute`s within the body in insertion order. The
565    /// iterator element type is `Attribute`.
566    #[inline]
567    pub fn into_attributes(self) -> IntoAttributes {
568        Box::new(
569            self.structures
570                .into_iter()
571                .filter_map(|s| s.into_attribute().ok()),
572        )
573    }
574
575    /// An iterator visiting all `Attribute`s within the body in insertion order. The iterator
576    /// element type is `&'a Attribute`.
577    #[inline]
578    pub fn attributes(&self) -> Attributes<'_> {
579        Box::new(self.structures.iter().filter_map(Structure::as_attribute))
580    }
581
582    /// An iterator visiting all `Attribute`s within the body in insertion order, with mutable
583    /// references to the values. The iterator element type is `AttributeMut<'a>`.
584    #[inline]
585    pub fn attributes_mut(&mut self) -> AttributesMut<'_> {
586        Box::new(
587            self.structures
588                .iter_mut()
589                .filter_map(Structure::as_attribute_mut)
590                .map(AttributeMut::new),
591        )
592    }
593
594    /// An owning iterator visiting all `Block`s within the body in insertion order. The iterator
595    /// element type is `Block`.
596    #[inline]
597    pub fn into_blocks(self) -> IntoBlocks {
598        Box::new(
599            self.structures
600                .into_iter()
601                .filter_map(|s| s.into_block().ok()),
602        )
603    }
604
605    /// An iterator visiting all `Block`s within the body in insertion order. The iterator element
606    /// type is `&'a Block`.
607    #[inline]
608    pub fn blocks(&self) -> Blocks<'_> {
609        Box::new(self.structures.iter().filter_map(Structure::as_block))
610    }
611
612    /// An iterator visiting all `Block`s within the body in insertion order, with mutable
613    /// references to the values. The iterator element type is `&'a mut Block`.
614    #[inline]
615    pub fn blocks_mut(&mut self) -> BlocksMut<'_> {
616        Box::new(
617            self.structures
618                .iter_mut()
619                .filter_map(Structure::as_block_mut),
620        )
621    }
622
623    /// Configures whether the body should be displayed on a single line.
624    ///
625    /// This is only a hint which will be applied if the `Body` is part of a `Block` (that is: not
626    /// the document root) and only if either of these conditions meet:
627    ///
628    /// - The body is empty. In this case, the opening (`{`) and closing (`}`) braces will be
629    ///   places on the same line.
630    /// - The body only consist of a single `Attribute`, which will be placed on the same
631    ///   line as the opening and closing braces.
632    ///
633    /// In all other cases this hint is ignored.
634    #[inline]
635    pub fn set_prefer_oneline(&mut self, yes: bool) {
636        self.prefer_oneline = yes;
637    }
638
639    /// Returns `true` if the body should be displayed on a single line.
640    ///
641    /// See the documentation of [`Body::set_prefer_oneline`] for more.
642    #[inline]
643    pub fn prefer_oneline(&self) -> bool {
644        self.prefer_oneline
645    }
646
647    /// Configures whether the trailing newline after the last structure in the body should be
648    /// omitted.
649    ///
650    /// This is only a hint which will be applied if this is the top-level `Body` of a HCL
651    /// document and is ignored if the `Body` is part of a [`Block`].
652    ///
653    /// The default is to always emit a trailing newline after the last body structure.
654    #[inline]
655    pub fn set_prefer_omit_trailing_newline(&mut self, yes: bool) {
656        self.prefer_omit_trailing_newline = yes;
657    }
658
659    /// Returns `true` if the trailing newline after the last structure in the body should be
660    /// omitted.
661    ///
662    /// See the documentation of [`Body::set_prefer_omit_trailing_newline`] for more.
663    #[inline]
664    pub fn prefer_omit_trailing_newline(&self) -> bool {
665        self.prefer_omit_trailing_newline
666    }
667
668    /// Returns `true` if the body only consist of a single `Attribute`.
669    #[inline]
670    pub(crate) fn has_single_attribute(&self) -> bool {
671        self.len() == 1 && self.get(0).is_some_and(Structure::is_attribute)
672    }
673
674    pub(crate) fn from_vec_unchecked(structures: Vec<Structure>) -> Self {
675        Body {
676            structures,
677            ..Default::default()
678        }
679    }
680
681    pub(crate) fn despan(&mut self, input: &str) {
682        self.decor.despan(input);
683        for structure in &mut self.structures {
684            structure.despan(input);
685        }
686    }
687}
688
689impl PartialEq for Body {
690    fn eq(&self, other: &Self) -> bool {
691        self.structures == other.structures
692    }
693}
694
695impl fmt::Display for Body {
696    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
697        let mut state = EncodeState::new(f);
698        self.encode_decorated(&mut state, NO_DECOR)
699    }
700}
701
702impl FromStr for Body {
703    type Err = parser::Error;
704
705    fn from_str(s: &str) -> Result<Self, Self::Err> {
706        parser::parse_body(s)
707    }
708}
709
710impl From<Vec<Structure>> for Body {
711    fn from(structures: Vec<Structure>) -> Self {
712        Body::from_iter(structures)
713    }
714}
715
716impl<T> Extend<T> for Body
717where
718    T: Into<Structure>,
719{
720    fn extend<I>(&mut self, iterable: I)
721    where
722        I: IntoIterator<Item = T>,
723    {
724        let iter = iterable.into_iter();
725        let reserve = if self.is_empty() {
726            iter.size_hint().0
727        } else {
728            (iter.size_hint().0 + 1) / 2
729        };
730        self.structures.reserve(reserve);
731        iter.for_each(|v| self.push(v));
732    }
733}
734
735impl<T> FromIterator<T> for Body
736where
737    T: Into<Structure>,
738{
739    fn from_iter<I>(iterable: I) -> Self
740    where
741        I: IntoIterator<Item = T>,
742    {
743        let iter = iterable.into_iter();
744        let lower = iter.size_hint().0;
745        let mut body = Body::with_capacity(lower);
746        body.extend(iter);
747        body
748    }
749}
750
751impl IntoIterator for Body {
752    type Item = Structure;
753    type IntoIter = IntoIter;
754
755    fn into_iter(self) -> Self::IntoIter {
756        Box::new(self.structures.into_iter())
757    }
758}
759
760impl<'a> IntoIterator for &'a Body {
761    type Item = &'a Structure;
762    type IntoIter = Iter<'a>;
763
764    fn into_iter(self) -> Self::IntoIter {
765        self.iter()
766    }
767}
768
769impl<'a> IntoIterator for &'a mut Body {
770    type Item = StructureMut<'a>;
771    type IntoIter = IterMut<'a>;
772
773    fn into_iter(self) -> Self::IntoIter {
774        self.iter_mut()
775    }
776}
777
778decorate_impl!(Body);
779span_impl!(Body);
780
781/// `BodyBuilder` builds a HCL [`Body`].
782///
783/// The builder allows to build the `Body` by adding attributes and other nested blocks via chained
784/// method calls. A call to [`.build()`](BodyBuilder::build) produces the final `Body`.
785///
786/// ## Example
787///
788/// ```
789/// use hcl_edit::structure::{Attribute, Block, Body};
790/// use hcl_edit::Ident;
791///
792/// let body = Body::builder()
793///     .block(
794///         Block::builder(Ident::new("resource"))
795///             .labels(["aws_s3_bucket", "mybucket"])
796///             .attribute(Attribute::new(Ident::new("name"), "mybucket"))
797///     )
798///     .build();
799/// ```
800#[derive(Debug, Default)]
801pub struct BodyBuilder {
802    body: Body,
803}
804
805impl BodyBuilder {
806    /// Adds an `Attribute` to the body.
807    ///
808    /// Consumes `self` and returns a new `BodyBuilder`.
809    #[inline]
810    pub fn attribute(self, attr: impl Into<Attribute>) -> BodyBuilder {
811        self.structure(attr.into())
812    }
813
814    /// Adds `Attribute`s to the body from an iterator.
815    ///
816    /// Consumes `self` and returns a new `BodyBuilder`.
817    #[inline]
818    pub fn attributes<I>(self, iter: I) -> BodyBuilder
819    where
820        I: IntoIterator,
821        I::Item: Into<Attribute>,
822    {
823        self.structures(iter.into_iter().map(Into::into))
824    }
825
826    /// Adds a `Block` to the body.
827    ///
828    /// Consumes `self` and returns a new `BodyBuilder`.
829    #[inline]
830    pub fn block(self, block: impl Into<Block>) -> BodyBuilder {
831        self.structure(block.into())
832    }
833
834    /// Adds `Block`s to the body from an iterator.
835    ///
836    /// Consumes `self` and returns a new `BodyBuilder`.
837    #[inline]
838    pub fn blocks<I>(self, iter: I) -> BodyBuilder
839    where
840        I: IntoIterator,
841        I::Item: Into<Block>,
842    {
843        self.structures(iter.into_iter().map(Into::into))
844    }
845
846    /// Adds a `Structure` to the body.
847    ///
848    /// Consumes `self` and returns a new `BodyBuilder`.
849    #[inline]
850    pub fn structure(mut self, structure: impl Into<Structure>) -> BodyBuilder {
851        self.body.push(structure.into());
852        self
853    }
854
855    /// Adds `Structure`s to the body from an iterator.
856    ///
857    /// Consumes `self` and returns a new `BodyBuilder`.
858    #[inline]
859    pub fn structures<I>(mut self, iter: I) -> BodyBuilder
860    where
861        I: IntoIterator,
862        I::Item: Into<Structure>,
863    {
864        self.body.extend(iter);
865        self
866    }
867
868    /// Consumes `self` and builds the [`Body`] from the structures added via the builder methods.
869    #[inline]
870    pub fn build(self) -> Body {
871        self.body
872    }
873}
874
875impl From<BodyBuilder> for Body {
876    #[inline]
877    fn from(builder: BodyBuilder) -> Self {
878        builder.build()
879    }
880}