github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/codegen/hcl2/model/body.go (about) 1 // Copyright 2016-2020, Pulumi Corporation. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package model 16 17 import ( 18 "fmt" 19 "io" 20 21 "github.com/hashicorp/hcl/v2" 22 "github.com/hashicorp/hcl/v2/hclsyntax" 23 "github.com/pulumi/pulumi/pkg/v3/codegen/hcl2/syntax" 24 "github.com/pulumi/pulumi/sdk/v3/go/common/util/contract" 25 ) 26 27 // BodyItem represents either an *Attribute or a *Block that is part of an HCL2 Body. 28 type BodyItem interface { 29 printable 30 31 // SyntaxNode returns syntax node of the item. 32 SyntaxNode() hclsyntax.Node 33 34 isBodyItem() 35 } 36 37 // Body represents an HCL2 body. A Body may be the root of an HCL2 file or the contents of an HCL2 block. 38 type Body struct { 39 // The syntax node for the body, if any. 40 Syntax *hclsyntax.Body 41 // The tokens for the body. 42 Tokens *syntax.BodyTokens 43 44 // The items that make up the body's contents. 45 Items []BodyItem 46 } 47 48 // SyntaxNode returns the syntax node of the body, and will either return an *hclsyntax.Body or syntax.None. 49 func (b *Body) SyntaxNode() hclsyntax.Node { 50 return syntaxOrNone(b.Syntax) 51 } 52 53 func (b *Body) HasLeadingTrivia() bool { 54 return len(b.Items) > 0 && b.Items[0].HasLeadingTrivia() 55 } 56 57 func (b *Body) HasTrailingTrivia() bool { 58 if eof := b.Tokens.GetEndOfFile(); eof != nil { 59 return true 60 } 61 return len(b.Items) > 0 && b.Items[len(b.Items)-1].HasTrailingTrivia() 62 } 63 64 func (b *Body) GetLeadingTrivia() syntax.TriviaList { 65 if len(b.Items) == 0 { 66 return nil 67 } 68 return b.Items[0].GetLeadingTrivia() 69 } 70 71 func (b *Body) GetTrailingTrivia() syntax.TriviaList { 72 if eof := b.Tokens.GetEndOfFile(); eof != nil { 73 return eof.TrailingTrivia 74 } 75 if len(b.Items) == 0 { 76 return nil 77 } 78 return b.Items[len(b.Items)-1].GetTrailingTrivia() 79 } 80 81 func (b *Body) Format(f fmt.State, c rune) { 82 b.print(f, &printer{}) 83 } 84 85 func (b *Body) print(w io.Writer, p *printer) { 86 // Print the items, separated by newlines. 87 for _, item := range b.Items { 88 p.fprintf(w, "% v", item) 89 if !item.GetTrailingTrivia().EndsOnNewLine() { 90 p.fprintf(w, "\n") 91 } 92 } 93 94 // If the body has an end-of-file token, print it. 95 if b.Tokens.GetEndOfFile() != nil { 96 p.fprintf(w, "%v", b.Tokens.EndOfFile) 97 } 98 } 99 100 // Attribute returns the attribute with the givne in the body if any exists. 101 func (b *Body) Attribute(name string) (*Attribute, bool) { 102 for _, item := range b.Items { 103 if attr, ok := item.(*Attribute); ok && attr.Name == name { 104 return attr, true 105 } 106 } 107 return nil, false 108 } 109 110 // Blocks returns all blocks in the body with the given type. 111 func (b *Body) Blocks(typ string) []*Block { 112 var blocks []*Block 113 for _, item := range b.Items { 114 if block, ok := item.(*Block); ok && block.Type == typ { 115 blocks = append(blocks, block) 116 } 117 } 118 return blocks 119 } 120 121 // BindBody binds an HCL2 body using the given scopes and token map. 122 func BindBody(body *hclsyntax.Body, scopes Scopes, tokens syntax.TokenMap, 123 opts ...BindOption) (*Body, hcl.Diagnostics) { 124 125 var diagnostics hcl.Diagnostics 126 127 syntaxItems := SourceOrderBody(body) 128 items := make([]BodyItem, len(syntaxItems)) 129 for i, syntaxItem := range syntaxItems { 130 var itemDiags hcl.Diagnostics 131 switch syntaxItem := syntaxItem.(type) { 132 case *hclsyntax.Attribute: 133 scope, scopeDiags := scopes.GetScopeForAttribute(syntaxItem) 134 diagnostics = append(diagnostics, scopeDiags...) 135 136 items[i], itemDiags = BindAttribute(syntaxItem, scope, tokens, opts...) 137 case *hclsyntax.Block: 138 scopes, scopesDiags := scopes.GetScopesForBlock(syntaxItem) 139 diagnostics = append(diagnostics, scopesDiags...) 140 141 items[i], itemDiags = BindBlock(syntaxItem, scopes, tokens, opts...) 142 default: 143 contract.Failf("unexpected syntax item of type %T (%v)", syntaxItem, syntaxItem.Range()) 144 } 145 diagnostics = append(diagnostics, itemDiags...) 146 } 147 148 bodyTokens, _ := tokens.ForNode(body).(*syntax.BodyTokens) 149 return &Body{ 150 Syntax: body, 151 Tokens: bodyTokens, 152 Items: items, 153 }, diagnostics 154 }