Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How does hclwrite remove objectelem inside a collectionValue of an attribute #695

Open
magodo opened this issue Aug 27, 2024 · 1 comment

Comments

@magodo
Copy link

magodo commented Aug 27, 2024

I have a piece of code using hclwrite to modify some Terraform code, in that it will conditionally remove attributes/blocks. The question raises when I encounter, e.g. the following HCL (regarded as a TF attribute):

foo = {
  bar = {
  }
  baz = "xxx"
}

(The above code is not prevalent in SDKv2 based providers, but will prevail for FW based ones as attributes are preferred to blocks)

I don't know how could I, e.g., remove the bar inside the foo attribute's expression (i.e. the collectionValue).

I've tried with something similar to below, but doesn't output what I expected:

package main

import (
	"fmt"
	"log"

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

func main() {
	f, diags := hclwrite.ParseConfig([]byte(`
foo = {
  bar = {
  }
  baz = "xxx"
}
`), "main.hcl", hcl.InitialPos)
	if diags.HasErrors() {
		log.Fatal(diags.Error())
	}
	foo := f.Body().Attributes()["foo"]
	expr := foo.Expr()

	tks := expr.BuildTokens(hclwrite.TokensForIdentifier("tmp"))
	ftmp, diags := hclwrite.ParseConfig(tks.Bytes(), "tmp", hcl.InitialPos)
	if diags.HasErrors() {
		log.Fatal(diags.Error())
	}

	body := ftmp.Body()
	body.Blocks()[0].Body().RemoveAttribute("bar")

	f.Body().SetAttributeRaw("foo", body.Blocks()[0].Body().BuildTokens(nil))
	fmt.Println(string(f.Bytes()))
}
@magodo
Copy link
Author

magodo commented Aug 27, 2024

Updated: I finally manage to do this via the following, though apparently not ideal:

package main

import (
	"fmt"
	"log"
	"slices"

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

func main() {
	f, diags := hclwrite.ParseConfig([]byte(`
foo = {
  bar = {
  }
  baz = "xxx"
}
`), "main.hcl", hcl.InitialPos)
	if diags.HasErrors() {
		log.Fatal(diags.Error())
	}
	foo := f.Body().Attributes()["foo"]

	tks, diags := removeExpressionAttributes(foo.Expr(), "bar")
	if diags.HasErrors() {
		log.Fatal(diags.Error())
	}
	f.Body().SetAttributeRaw("foo", tks)
	fmt.Println(string(f.Bytes()))
}

func removeExpressionAttributes(expr *hclwrite.Expression, attributes ...string) (hclwrite.Tokens, hcl.Diagnostics) {
	tks := expr.BuildTokens(hclwrite.TokensForIdentifier("tmp"))
	ftmp, diags := hclwrite.ParseConfig(tks.Bytes(), "tmp", hcl.InitialPos)
	if diags.HasErrors() {
		return nil, diags
	}

	body := ftmp.Body().Blocks()[0].Body()

	bodyAttributes := body.Attributes()
	var objectAttrTokens []hclwrite.ObjectAttrTokens
	for attrName, attr := range bodyAttributes {
		if slices.Contains(attributes, attrName) {
			continue
		}
		objectAttrTokens = append(objectAttrTokens, hclwrite.ObjectAttrTokens{
			Name:  hclwrite.TokensForIdentifier(attrName),
			Value: attr.Expr().BuildTokens(nil),
		})
	}
	return hclwrite.TokensForObject(objectAttrTokens), nil
}

Any idea about how to do this idiomatically would be appreciated!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant