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}