diff --git a/hcledit.go b/hcledit.go index 92727ac..24ab693 100644 --- a/hcledit.go +++ b/hcledit.go @@ -73,7 +73,7 @@ func (h *hclEditImpl) Create(queryStr string, value interface{}, opts ...Option) return err } - hdlr, err := handler.New(value, opt.comment, opt.afterKey) + hdlr, err := handler.New(value, opt.comment, opt.afterKey, opt.beforeNewline) if err != nil { return err } @@ -133,6 +133,8 @@ func (h *hclEditImpl) Update(queryStr string, value interface{}, opts ...Option) // the position of target attribute is also changed... if opt.comment != "" { return fmt.Errorf("WithComment is not supported for Update") + } else if opt.beforeNewline { + return fmt.Errorf("WithNewLine is not supported for Update") } queries, err := query.Build(queryStr) @@ -140,7 +142,7 @@ func (h *hclEditImpl) Update(queryStr string, value interface{}, opts ...Option) return err } - hdlr, err := handler.New(value, opt.comment, opt.afterKey) + hdlr, err := handler.New(value, opt.comment, opt.afterKey, opt.beforeNewline) if err != nil { return err } diff --git a/internal/ast/object.go b/internal/ast/object.go index e24c305..3393cf6 100644 --- a/internal/ast/object.go +++ b/internal/ast/object.go @@ -46,12 +46,12 @@ func (o *Object) GetObjectAttribute(name string) *ObjectAtrribute { return nil } -func (o *Object) SetObjectAttributeRaw(name string, exprTokens, commentTokens hclwrite.Tokens) *ObjectAtrribute { +func (o *Object) SetObjectAttributeRaw(name string, exprTokens, beforeTokens hclwrite.Tokens) *ObjectAtrribute { objAttr := o.GetObjectAttribute(name) if objAttr != nil { objAttr.exprTokens = exprTokens } else { - objAttr = newObjectAttribute(name, exprTokens, commentTokens) + objAttr = newObjectAttribute(name, exprTokens, beforeTokens) o.objectAtrributes = append(o.objectAtrributes, objAttr) } return objAttr diff --git a/internal/ast/object_attribute.go b/internal/ast/object_attribute.go index 8e217b9..4b308d4 100644 --- a/internal/ast/object_attribute.go +++ b/internal/ast/object_attribute.go @@ -22,11 +22,7 @@ func (oa *ObjectAtrribute) BuildTokens() hclwrite.Tokens { return tokens } -func newObjectAttribute(name string, exprTokens, commentTokens hclwrite.Tokens) *ObjectAtrribute { - beforeTokens := hclwrite.Tokens{} - if len(commentTokens) != 0 { - beforeTokens = append(beforeTokens, commentTokens...) - } +func newObjectAttribute(name string, exprTokens, beforeTokens hclwrite.Tokens) *ObjectAtrribute { beforeTokens = append(beforeTokens, hclwrite.Tokens{ { Type: hclsyntax.TokenIdent, diff --git a/internal/handler/cty.go b/internal/handler/cty.go index f9b9daa..b8b59be 100644 --- a/internal/handler/cty.go +++ b/internal/handler/cty.go @@ -8,23 +8,23 @@ import ( ) type ctyValueHandler struct { - exprTokens hclwrite.Tokens - commentTokens hclwrite.Tokens + exprTokens hclwrite.Tokens + beforeTokens hclwrite.Tokens afterKey string } -func newCtyValueHandler(value cty.Value, comment, afterKey string) (Handler, error) { +func newCtyValueHandler(value cty.Value, comment, afterKey string, beforeNewline bool) (Handler, error) { return &ctyValueHandler{ - exprTokens: hclwrite.NewExpressionLiteral(value).BuildTokens(nil), - commentTokens: commentTokens(comment), + exprTokens: hclwrite.NewExpressionLiteral(value).BuildTokens(nil), + beforeTokens: beforeTokens(comment, beforeNewline), afterKey: afterKey, }, nil } func (h *ctyValueHandler) HandleObject(object *ast.Object, name string, _ []string) error { - object.SetObjectAttributeRaw(name, h.exprTokens, h.commentTokens) + object.SetObjectAttributeRaw(name, h.exprTokens, h.beforeTokens) if h.afterKey != "" { object.UpdateObjectAttributeOrder(name, h.afterKey) } @@ -34,8 +34,8 @@ func (h *ctyValueHandler) HandleObject(object *ast.Object, name string, _ []stri func (h *ctyValueHandler) HandleBody(body *hclwrite.Body, name string, _ []string) error { body.SetAttributeRaw(name, h.exprTokens) - if len(h.commentTokens) > 0 { - tokens := body.GetAttribute(name).BuildTokens(h.commentTokens) + if len(h.beforeTokens) > 0 { + tokens := body.GetAttribute(name).BuildTokens(h.beforeTokens) body.RemoveAttribute(name) body.AppendUnstructuredTokens(tokens) } diff --git a/internal/handler/handler.go b/internal/handler/handler.go index d3c4587..ec2efee 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -15,7 +15,7 @@ type Handler interface { HandleObject(object *ast.Object, name string, keyTrail []string) error } -func New(input interface{}, comment, afterKey string) (Handler, error) { +func New(input interface{}, comment, afterKey string, beforeNewline bool) (Handler, error) { switch v := input.(type) { case *BlockVal: return newBlockHandler(v.Labels) @@ -33,22 +33,28 @@ func New(input interface{}, comment, afterKey string) (Handler, error) { return nil, fmt.Errorf("failed to convert cty value from go value: %s", err) } - return newCtyValueHandler(ctyVal, comment, afterKey) + return newCtyValueHandler(ctyVal, comment, afterKey, beforeNewline) } -func commentTokens(comment string) hclwrite.Tokens { - if comment == "" { - return hclwrite.Tokens{} +func beforeTokens(comment string, beforeNewline bool) hclwrite.Tokens { + result := hclwrite.Tokens{} + if beforeNewline { + result = append(result, &hclwrite.Token{ + Type: hclsyntax.TokenNewline, + Bytes: []byte{'\n'}, + }) } - return hclwrite.Tokens{ - { + if comment != "" { + result = append(result, &hclwrite.Token{ Type: hclsyntax.TokenComment, Bytes: []byte(comment), - }, - { + }) + result = append(result, &hclwrite.Token{ Type: hclsyntax.TokenNewline, Bytes: []byte{'\n'}, - }, + }) } + + return result } diff --git a/internal/query/query.go b/internal/query/query.go index 852353d..d8db363 100644 --- a/internal/query/query.go +++ b/internal/query/query.go @@ -12,8 +12,7 @@ type queryKey struct { key string } -type queryWildcard struct { -} +type queryWildcard struct{} func (c *queryKey) Match(target string) bool { return target == c.key diff --git a/option.go b/option.go index 869f288..e04beaf 100644 --- a/option.go +++ b/option.go @@ -1,8 +1,9 @@ package hcledit type option struct { - comment string - afterKey string + comment string + afterKey string + beforeNewline bool } type Option func(*option) @@ -20,3 +21,10 @@ func WithAfter(key string) Option { opt.afterKey = key } } + +// WithNewLine +func WithNewLine() Option { + return func(opt *option) { + opt.beforeNewline = true + } +} diff --git a/option_test.go b/option_test.go index 95bfa9a..033a627 100644 --- a/option_test.go +++ b/option_test.go @@ -173,3 +173,90 @@ block { }) } } + +func TestWithNewLine(t *testing.T) { + cases := map[string]struct { + input string + exec func(editor hcledit.HCLEditor) error + want string + }{ + "CreateAttribute": { + input: ` +`, + exec: func(editor hcledit.HCLEditor) error { + return editor.Create("attribute", "str", hcledit.WithNewLine()) + }, + want: ` + +attribute = "str" +`, + }, + + "CreateAttributeInBlock": { + input: ` +block "label1" { +} +`, + exec: func(editor hcledit.HCLEditor) error { + return editor.Create("block.label1.attribute", "str", hcledit.WithNewLine()) + }, + want: ` +block "label1" { + + attribute = "str" +} +`, + }, + + "CreateAttributeInObject": { + input: ` +object = { +} +`, + exec: func(editor hcledit.HCLEditor) error { + return editor.Create("object.attribute", "str", hcledit.WithNewLine()) + }, + want: ` +object = { + + attribute = "str" +} +`, + }, + "CreateWithComment": { + input: ` +object = { +} +`, + exec: func(editor hcledit.HCLEditor) error { + return editor.Create("object.attribute", "str", hcledit.WithComment("// Comment"), hcledit.WithNewLine()) + }, + want: ` +object = { + + // Comment + attribute = "str" +} +`, + }, + } + + for name, tc := range cases { + tc := tc + t.Run(name, func(t *testing.T) { + editor, err := hcledit.Read(strings.NewReader(tc.input), "") + if err != nil { + t.Fatal(err) + } + + if err := tc.exec(editor); err != nil { + t.Fatal(err) + } + + got := string(editor.Bytes()) + if got != tc.want { + t.Errorf("Update() mismatch:\ngot:%s\nwant:%s\n", got, tc.want) + } + }) + } +} diff --git a/write_test.go b/write_test.go index e2c5c8f..0aa2db8 100644 --- a/write_test.go +++ b/write_test.go @@ -52,7 +52,7 @@ func TestOverWriteFile(t *testing.T) { tempFile := filepath.Join(tempDir, "test.hcl") if err := ioutil.WriteFile(tempFile, []byte(` attribute1 = "str1" -`), 0600); err != nil { +`), 0o600); err != nil { t.Fatal(err) }