github.com/hashicorp/hcl/v2@v2.20.0/ext/transform/transform.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package transform
     5  
     6  import (
     7  	"github.com/hashicorp/hcl/v2"
     8  )
     9  
    10  // Shallow is equivalent to calling transformer.TransformBody(body), and
    11  // is provided only for completeness of the top-level API.
    12  func Shallow(body hcl.Body, transformer Transformer) hcl.Body {
    13  	return transformer.TransformBody(body)
    14  }
    15  
    16  // Deep applies the given transform to the given body and then
    17  // wraps the result such that any descendent blocks that are decoded will
    18  // also have the transform applied to their bodies.
    19  //
    20  // This allows for language extensions that define a particular block type
    21  // for a particular body and all nested blocks within it.
    22  //
    23  // Due to the wrapping behavior, the body resulting from this function
    24  // will not be of the type returned by the transformer. Callers may call
    25  // only the methods defined for interface hcl.Body, and may not type-assert
    26  // to access other methods.
    27  func Deep(body hcl.Body, transformer Transformer) hcl.Body {
    28  	return deepWrapper{
    29  		Transformed: transformer.TransformBody(body),
    30  		Transformer: transformer,
    31  	}
    32  }
    33  
    34  // deepWrapper is a hcl.Body implementation that ensures that a given
    35  // transformer is applied to another given body when content is extracted,
    36  // and that it recursively applies to any child blocks that are extracted.
    37  type deepWrapper struct {
    38  	Transformed hcl.Body
    39  	Transformer Transformer
    40  }
    41  
    42  func (w deepWrapper) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) {
    43  	content, diags := w.Transformed.Content(schema)
    44  	content = w.transformContent(content)
    45  	return content, diags
    46  }
    47  
    48  func (w deepWrapper) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) {
    49  	content, remain, diags := w.Transformed.PartialContent(schema)
    50  	content = w.transformContent(content)
    51  	return content, remain, diags
    52  }
    53  
    54  func (w deepWrapper) transformContent(content *hcl.BodyContent) *hcl.BodyContent {
    55  	if len(content.Blocks) == 0 {
    56  		// Easy path: if there are no blocks then there are no child bodies to wrap
    57  		return content
    58  	}
    59  
    60  	// Since we're going to change things here, we'll be polite and clone the
    61  	// structure so that we don't risk impacting any internal state of the
    62  	// original body.
    63  	ret := &hcl.BodyContent{
    64  		Attributes:       content.Attributes,
    65  		MissingItemRange: content.MissingItemRange,
    66  		Blocks:           make(hcl.Blocks, len(content.Blocks)),
    67  	}
    68  
    69  	for i, givenBlock := range content.Blocks {
    70  		// Shallow-copy the block so we can mutate it
    71  		newBlock := *givenBlock
    72  		newBlock.Body = Deep(newBlock.Body, w.Transformer)
    73  		ret.Blocks[i] = &newBlock
    74  	}
    75  
    76  	return ret
    77  }
    78  
    79  func (w deepWrapper) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
    80  	// Attributes can't have bodies or nested blocks, so this is just a thin wrapper.
    81  	return w.Transformed.JustAttributes()
    82  }
    83  
    84  func (w deepWrapper) MissingItemRange() hcl.Range {
    85  	return w.Transformed.MissingItemRange()
    86  }