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  }