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 }