Skip to content

Commit

Permalink
hclwrite: Allow selecting blocks for updating
Browse files Browse the repository at this point in the history
  • Loading branch information
minamijoyo authored and apparentlymart committed Oct 1, 2019
1 parent ed70541 commit 9d1235a
Show file tree
Hide file tree
Showing 6 changed files with 402 additions and 5 deletions.
8 changes: 4 additions & 4 deletions hclsyntax/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -1661,7 +1661,7 @@ Token:
break Token

case TokenQuotedLit:
s, sDiags := p.decodeStringLit(tok)
s, sDiags := ParseStringLiteralToken(tok)
diags = append(diags, sDiags...)
ret.WriteString(s)

Expand Down Expand Up @@ -1721,21 +1721,21 @@ Token:
return ret.String(), hcl.RangeBetween(oQuote.Range, cQuote.Range), diags
}

// decodeStringLit processes the given token, which must be either a
// ParseStringLiteralToken processes the given token, which must be either a
// TokenQuotedLit or a TokenStringLit, returning the string resulting from
// resolving any escape sequences.
//
// If any error diagnostics are returned, the returned string may be incomplete
// or otherwise invalid.
func (p *parser) decodeStringLit(tok Token) (string, hcl.Diagnostics) {
func ParseStringLiteralToken(tok Token) (string, hcl.Diagnostics) {
var quoted bool
switch tok.Type {
case TokenQuotedLit:
quoted = true
case TokenStringLit:
quoted = false
default:
panic("decodeQuotedLit can only be used with TokenStringLit and TokenQuotedLit tokens")
panic("ParseStringLiteralToken can only be used with TokenStringLit and TokenQuotedLit tokens")
}
var diags hcl.Diagnostics

Expand Down
2 changes: 1 addition & 1 deletion hclsyntax/parser_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ Token:

switch next.Type {
case TokenStringLit, TokenQuotedLit:
str, strDiags := p.decodeStringLit(next)
str, strDiags := ParseStringLiteralToken(next)
diags = append(diags, strDiags...)

if ltrim {
Expand Down
44 changes: 44 additions & 0 deletions hclwrite/ast_block.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,47 @@ func (b *Block) init(typeName string, labels []string) {
func (b *Block) Body() *Body {
return b.body.content.(*Body)
}

// Type returns the type name of the block.
func (b *Block) Type() string {
typeNameObj := b.typeName.content.(*identifier)
return string(typeNameObj.token.Bytes)
}

// Labels returns the labels of the block.
func (b *Block) Labels() []string {
labelNames := make([]string, 0, len(b.labels))
list := b.labels.List()

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
}
105 changes: 105 additions & 0 deletions hclwrite/ast_block_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package hclwrite

import (
"fmt"
"reflect"
"strings"
"testing"

"github.com/hashicorp/hcl/v2"
)

func TestBlockType(t *testing.T) {
tests := []struct {
src string
want string
}{
{
`
service {
attr0 = "val0"
}
`,
"service",
},
}

for _, test := range tests {
t.Run(fmt.Sprintf("%s", test.want), func(t *testing.T) {
f, diags := ParseConfig([]byte(test.src), "", hcl.Pos{Line: 1, Column: 1})
if len(diags) != 0 {
for _, diag := range diags {
t.Logf("- %s", diag.Error())
}
t.Fatalf("unexpected diagnostics")
}

block := f.Body().Blocks()[0]
got := string(block.Type())
if got != test.want {
t.Errorf("wrong result\ngot: %s\nwant: %s", got, test.want)
}
})
}
}

func TestBlockLabels(t *testing.T) {
tests := []struct {
src string
want []string
}{
{
`
nolabel {
}
`,
[]string{},
},
{
`
quoted "label1" {
}
`,
[]string{"label1"},
},
{
`
quoted "label1" "label2" {
}
`,
[]string{"label1", "label2"},
},
{
`
unquoted label1 {
}
`,
[]string{"label1"},
},
{
`
escape "\u0041" {
}
`,
[]string{"\u0041"},
},
}

for _, test := range tests {
t.Run(fmt.Sprintf("%s", strings.Join(test.want, " ")), func(t *testing.T) {
f, diags := ParseConfig([]byte(test.src), "", hcl.Pos{Line: 1, Column: 1})
if len(diags) != 0 {
for _, diag := range diags {
t.Logf("- %s", diag.Error())
}
t.Fatalf("unexpected diagnostics")
}

block := f.Body().Blocks()[0]
got := block.Labels()
if !reflect.DeepEqual(got, test.want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.want)
}
})
}
}
19 changes: 19 additions & 0 deletions hclwrite/ast_body.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package hclwrite

import (
"reflect"

"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/zclconf/go-cty/cty"
Expand Down Expand Up @@ -82,6 +84,23 @@ func (b *Body) GetAttribute(name string) *Attribute {
return nil
}

// FirstMatchingBlock returns a first matching block from the body that has the
// given name and labels or returns nil if there is currently no matching
// block.
func (b *Body) FirstMatchingBlock(typeName string, labels []string) *Block {
for _, block := range b.Blocks() {
if typeName == block.Type() {
labelNames := block.Labels()
if reflect.DeepEqual(labels, labelNames) {
// We've found it!
return block
}
}
}

return nil
}

// SetAttributeValue either replaces the expression of an existing attribute
// of the given name or adds a new attribute definition to the end of the block.
//
Expand Down
Loading

0 comments on commit 9d1235a

Please sign in to comment.