2018-11-03 15:43:10 +00:00
|
|
|
package hclwrite
|
|
|
|
|
2018-11-03 16:21:31 +00:00
|
|
|
import (
|
2019-09-09 23:08:19 +00:00
|
|
|
"github.com/hashicorp/hcl/v2/hclsyntax"
|
2018-11-03 16:21:31 +00:00
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
)
|
|
|
|
|
2018-11-03 15:43:10 +00:00
|
|
|
type Block struct {
|
|
|
|
inTree
|
|
|
|
|
|
|
|
leadComments *node
|
|
|
|
typeName *node
|
2020-08-21 18:20:48 +00:00
|
|
|
labels *node
|
2018-11-03 15:43:10 +00:00
|
|
|
open *node
|
|
|
|
body *node
|
|
|
|
close *node
|
|
|
|
}
|
2018-11-03 16:21:31 +00:00
|
|
|
|
|
|
|
func newBlock() *Block {
|
|
|
|
return &Block{
|
|
|
|
inTree: newInTree(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-04 01:13:13 +00:00
|
|
|
// NewBlock constructs a new, empty block with the given type name and labels.
|
|
|
|
func NewBlock(typeName string, labels []string) *Block {
|
|
|
|
block := newBlock()
|
|
|
|
block.init(typeName, labels)
|
|
|
|
return block
|
|
|
|
}
|
|
|
|
|
2018-11-03 16:21:31 +00:00
|
|
|
func (b *Block) init(typeName string, labels []string) {
|
|
|
|
nameTok := newIdentToken(typeName)
|
|
|
|
nameObj := newIdentifier(nameTok)
|
|
|
|
b.leadComments = b.children.Append(newComments(nil))
|
|
|
|
b.typeName = b.children.Append(nameObj)
|
2020-08-21 18:20:48 +00:00
|
|
|
labelsObj := newBlockLabels(labels)
|
|
|
|
b.labels = b.children.Append(labelsObj)
|
2018-11-03 16:21:31 +00:00
|
|
|
b.open = b.children.AppendUnstructuredTokens(Tokens{
|
|
|
|
{
|
|
|
|
Type: hclsyntax.TokenOBrace,
|
|
|
|
Bytes: []byte{'{'},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: hclsyntax.TokenNewline,
|
|
|
|
Bytes: []byte{'\n'},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
body := newBody() // initially totally empty; caller can append to it subsequently
|
|
|
|
b.body = b.children.Append(body)
|
|
|
|
b.close = b.children.AppendUnstructuredTokens(Tokens{
|
|
|
|
{
|
|
|
|
Type: hclsyntax.TokenCBrace,
|
|
|
|
Bytes: []byte{'}'},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: hclsyntax.TokenNewline,
|
|
|
|
Bytes: []byte{'\n'},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Body returns the body that represents the content of the receiving block.
|
|
|
|
//
|
|
|
|
// Appending to or otherwise modifying this body will make changes to the
|
|
|
|
// tokens that are generated between the blocks open and close braces.
|
|
|
|
func (b *Block) Body() *Body {
|
|
|
|
return b.body.content.(*Body)
|
|
|
|
}
|
2019-07-20 15:23:02 +00:00
|
|
|
|
|
|
|
// Type returns the type name of the block.
|
|
|
|
func (b *Block) Type() string {
|
|
|
|
typeNameObj := b.typeName.content.(*identifier)
|
|
|
|
return string(typeNameObj.token.Bytes)
|
|
|
|
}
|
|
|
|
|
2020-01-29 05:21:48 +00:00
|
|
|
// SetType updates the type name of the block to a given name.
|
|
|
|
func (b *Block) SetType(typeName string) {
|
|
|
|
nameTok := newIdentToken(typeName)
|
|
|
|
nameObj := newIdentifier(nameTok)
|
|
|
|
b.typeName.ReplaceWith(nameObj)
|
|
|
|
}
|
|
|
|
|
2019-07-20 15:23:02 +00:00
|
|
|
// Labels returns the labels of the block.
|
|
|
|
func (b *Block) Labels() []string {
|
2020-08-21 18:20:48 +00:00
|
|
|
return b.labelsObj().Current()
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetLabels updates the labels of the block to given labels.
|
|
|
|
// Since we cannot assume that old and new labels are equal in length,
|
|
|
|
// remove old labels and insert new ones before TokenOBrace.
|
|
|
|
func (b *Block) SetLabels(labels []string) {
|
|
|
|
b.labelsObj().Replace(labels)
|
|
|
|
}
|
|
|
|
|
|
|
|
// labelsObj returns the internal node content representation of the block
|
|
|
|
// labels. This is not part of the public API because we're intentionally
|
|
|
|
// exposing only a limited API to get/set labels on the block itself in a
|
|
|
|
// manner similar to the main hcl.Block type, but our block accessors all
|
|
|
|
// use this to get the underlying node content to work with.
|
|
|
|
func (b *Block) labelsObj() *blockLabels {
|
|
|
|
return b.labels.content.(*blockLabels)
|
|
|
|
}
|
|
|
|
|
|
|
|
type blockLabels struct {
|
|
|
|
inTree
|
|
|
|
|
|
|
|
items nodeSet
|
|
|
|
}
|
|
|
|
|
|
|
|
func newBlockLabels(labels []string) *blockLabels {
|
|
|
|
ret := &blockLabels{
|
|
|
|
inTree: newInTree(),
|
|
|
|
items: newNodeSet(),
|
|
|
|
}
|
|
|
|
|
|
|
|
ret.Replace(labels)
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
func (bl *blockLabels) Replace(newLabels []string) {
|
|
|
|
bl.inTree.children.Clear()
|
|
|
|
bl.items.Clear()
|
|
|
|
|
|
|
|
for _, label := range newLabels {
|
|
|
|
labelToks := TokensForValue(cty.StringVal(label))
|
|
|
|
// Force a new label to use the quoted form, which is the idiomatic
|
|
|
|
// form. The unquoted form is supported in HCL 2 only for compatibility
|
|
|
|
// with historical use in HCL 1.
|
|
|
|
labelObj := newQuoted(labelToks)
|
|
|
|
labelNode := bl.children.Append(labelObj)
|
|
|
|
bl.items.Add(labelNode)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (bl *blockLabels) Current() []string {
|
|
|
|
labelNames := make([]string, 0, len(bl.items))
|
|
|
|
list := bl.items.List()
|
2019-07-20 15:23:02 +00:00
|
|
|
|
|
|
|
for _, label := range list {
|
|
|
|
switch labelObj := label.content.(type) {
|
|
|
|
case *identifier:
|
|
|
|
if labelObj.token.Type == hclsyntax.TokenIdent {
|
|
|
|
labelString := string(labelObj.token.Bytes)
|
|
|
|
labelNames = append(labelNames, labelString)
|
|
|
|
}
|
|
|
|
|
|
|
|
case *quoted:
|
|
|
|
tokens := labelObj.tokens
|
|
|
|
if len(tokens) == 3 &&
|
|
|
|
tokens[0].Type == hclsyntax.TokenOQuote &&
|
|
|
|
tokens[1].Type == hclsyntax.TokenQuotedLit &&
|
|
|
|
tokens[2].Type == hclsyntax.TokenCQuote {
|
|
|
|
// Note that TokenQuotedLit may contain escape sequences.
|
|
|
|
labelString, diags := hclsyntax.ParseStringLiteralToken(tokens[1].asHCLSyntax())
|
|
|
|
|
|
|
|
// If parsing the string literal returns error diagnostics
|
|
|
|
// then we can just assume the label doesn't match, because it's invalid in some way.
|
|
|
|
if !diags.HasErrors() {
|
|
|
|
labelNames = append(labelNames, labelString)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
// If neither of the previous cases are true (should be impossible)
|
|
|
|
// then we can just ignore it, because it's invalid too.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return labelNames
|
|
|
|
}
|