hclwrite

package
v2.23.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 15, 2024 License: MPL-2.0 Imports: 12 Imported by: 80,068

Documentation

Overview

Package hclwrite deals with the problem of generating HCL configuration and of making specific surgical changes to existing HCL configurations.

It operates at a different level of abstraction than the main HCL parser and AST, since details such as the placement of comments and newlines are preserved when unchanged.

The hclwrite API follows a similar principle to XML/HTML DOM, allowing nodes to be read out, created and inserted, etc. Nodes represent syntax constructs rather than semantic concepts.

Example (GenerateFromScratch)
f := hclwrite.NewEmptyFile()
rootBody := f.Body()
rootBody.SetAttributeValue("string", cty.StringVal("bar")) // this is overwritten later
rootBody.AppendNewline()
rootBody.SetAttributeValue("object", cty.ObjectVal(map[string]cty.Value{
	"foo": cty.StringVal("foo"),
	"bar": cty.NumberIntVal(5),
	"baz": cty.True,
}))
rootBody.SetAttributeValue("string", cty.StringVal("foo"))
rootBody.SetAttributeValue("bool", cty.False)
rootBody.SetAttributeTraversal("path", hcl.Traversal{
	hcl.TraverseRoot{
		Name: "env",
	},
	hcl.TraverseAttr{
		Name: "PATH",
	},
})
rootBody.AppendNewline()
fooBlock := rootBody.AppendNewBlock("foo", nil)
fooBody := fooBlock.Body()
rootBody.AppendNewBlock("empty", nil)
rootBody.AppendNewline()
barBlock := rootBody.AppendNewBlock("bar", []string{"a", "b"})
barBody := barBlock.Body()

fooBody.SetAttributeValue("hello", cty.StringVal("world"))

bazBlock := barBody.AppendNewBlock("baz", nil)
bazBody := bazBlock.Body()
bazBody.SetAttributeValue("foo", cty.NumberIntVal(10))
bazBody.SetAttributeValue("beep", cty.StringVal("boop"))
bazBody.SetAttributeValue("baz", cty.ListValEmpty(cty.String))

fmt.Printf("%s", f.Bytes())
Output:

string = "foo"

object = {
  bar = 5
  baz = true
  foo = "foo"
}
bool = false
path = env.PATH

foo {
  hello = "world"
}
empty {
}

bar "a" "b" {
  baz {
    foo  = 10
    beep = "boop"
    baz  = []
  }
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Format

func Format(src []byte) []byte

Format takes source code and performs simple whitespace changes to transform it to a canonical layout style.

Format skips constructing an AST and works directly with tokens, so it is less expensive than formatting via the AST for situations where no other changes will be made. It also ignores syntax errors and can thus be applied to partial source code, although the result in that case may not be desirable.

Types

type Attribute

type Attribute struct {
	// contains filtered or unexported fields
}

func (*Attribute) BuildTokens

func (it *Attribute) BuildTokens(to Tokens) Tokens

func (*Attribute) Expr

func (a *Attribute) Expr() *Expression

type Block

type Block struct {
	// contains filtered or unexported fields
}

func NewBlock

func NewBlock(typeName string, labels []string) *Block

NewBlock constructs a new, empty block with the given type name and labels.

func (*Block) Body

func (b *Block) Body() *Body

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 (*Block) BuildTokens

func (it *Block) BuildTokens(to Tokens) Tokens

func (*Block) Labels

func (b *Block) Labels() []string

Labels returns the labels of the block.

func (*Block) SetLabels added in v2.7.0

func (b *Block) SetLabels(labels []string)

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 (*Block) SetType added in v2.7.0

func (b *Block) SetType(typeName string)

SetType updates the type name of the block to a given name.

func (*Block) Type

func (b *Block) Type() string

Type returns the type name of the block.

type Body

type Body struct {
	// contains filtered or unexported fields
}

func (*Body) AppendBlock

func (b *Body) AppendBlock(block *Block) *Block

AppendBlock appends an existing block (which must not be already attached to a body) to the end of the receiving body.

func (*Body) AppendNewBlock

func (b *Body) AppendNewBlock(typeName string, labels []string) *Block

AppendNewBlock appends a new nested block to the end of the receiving body with the given type name and labels.

func (*Body) AppendNewline

func (b *Body) AppendNewline()

AppendNewline appends a newline token to th end of the receiving body, which generally serves as a separator between different sets of body contents.

func (*Body) AppendUnstructuredTokens

func (b *Body) AppendUnstructuredTokens(ts Tokens)

func (*Body) Attributes

func (b *Body) Attributes() map[string]*Attribute

Attributes returns a new map of all of the attributes in the body, with the attribute names as the keys.

func (*Body) Blocks

func (b *Body) Blocks() []*Block

Blocks returns a new slice of all the blocks in the body.

func (*Body) BuildTokens

func (it *Body) BuildTokens(to Tokens) Tokens

func (*Body) Clear

func (b *Body) Clear()

Clear removes all of the items from the body, making it empty.

func (*Body) FirstMatchingBlock

func (b *Body) FirstMatchingBlock(typeName string, labels []string) *Block

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 (*Body) GetAttribute

func (b *Body) GetAttribute(name string) *Attribute

GetAttribute returns the attribute from the body that has the given name, or returns nil if there is currently no matching attribute.

func (*Body) RemoveAttribute

func (b *Body) RemoveAttribute(name string) *Attribute

RemoveAttribute removes the attribute with the given name from the body.

The return value is the attribute that was removed, or nil if there was no such attribute (in which case the call was a no-op).

func (*Body) RemoveBlock

func (b *Body) RemoveBlock(block *Block) bool

RemoveBlock removes the given block from the body, if it's in that body. If it isn't present, this is a no-op.

Returns true if it removed something, or false otherwise.

func (*Body) SetAttributeRaw added in v2.1.0

func (b *Body) SetAttributeRaw(name string, tokens Tokens) *Attribute

SetAttributeRaw either replaces the expression of an existing attribute of the given name or adds a new attribute definition to the end of the block, using the given tokens verbatim as the expression.

The same caveats apply to this function as for NewExpressionRaw on which it is based. If possible, prefer to use SetAttributeValue or SetAttributeTraversal.

func (*Body) SetAttributeTraversal

func (b *Body) SetAttributeTraversal(name string, traversal hcl.Traversal) *Attribute

SetAttributeTraversal either replaces the expression of an existing attribute of the given name or adds a new attribute definition to the end of the body.

The new expression is given as a hcl.Traversal, which must be an absolute traversal. To set a literal value, use SetAttributeValue.

The return value is the attribute that was either modified in-place or created.

func (*Body) SetAttributeValue

func (b *Body) SetAttributeValue(name string, val cty.Value) *Attribute

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.

The value is given as a cty.Value, and must therefore be a literal. To set a variable reference or other traversal, use SetAttributeTraversal.

The return value is the attribute that was either modified in-place or created.

type Expression

type Expression struct {
	// contains filtered or unexported fields
}

func NewExpressionAbsTraversal

func NewExpressionAbsTraversal(traversal hcl.Traversal) *Expression

NewExpressionAbsTraversal constructs an expression that represents the given traversal, which must be absolute or this function will panic.

func NewExpressionLiteral

func NewExpressionLiteral(val cty.Value) *Expression

NewExpressionLiteral constructs an an expression that represents the given literal value.

Since an unknown value cannot be represented in source code, this function will panic if the given value is unknown or contains a nested unknown value. Use val.IsWhollyKnown before calling to be sure.

HCL native syntax does not directly represent lists, maps, and sets, and instead relies on the automatic conversions to those collection types from either list or tuple constructor syntax. Therefore converting collection values to source code and re-reading them will lose type information, and the reader must provide a suitable type at decode time to recover the original value.

func NewExpressionRaw added in v2.1.0

func NewExpressionRaw(tokens Tokens) *Expression

NewExpressionRaw constructs an expression containing the given raw tokens.

There is no automatic validation that the given tokens produce a valid expression. Callers of thus function must take care to produce invalid expression tokens. Where possible, use the higher-level functions NewExpressionLiteral or NewExpressionAbsTraversal instead.

Because NewExpressionRaw does not interpret the given tokens in any way, an expression created by NewExpressionRaw will produce an empty result for calls to its method Variables, even if the given token sequence contains a subslice that would normally be interpreted as a traversal under parsing.

func (*Expression) BuildTokens

func (it *Expression) BuildTokens(to Tokens) Tokens

func (*Expression) RenameVariablePrefix

func (e *Expression) RenameVariablePrefix(search, replacement []string)

RenameVariablePrefix examines each of the absolute traversals in the receiving expression to see if they have the given sequence of names as a prefix prefix. If so, they are updated in place to have the given replacement names instead of that prefix.

This can be used to implement symbol renaming. The calling application can visit all relevant expressions in its input and apply the same renaming to implement a global symbol rename.

The search and replacement traversals must be the same length, or this method will panic. Only attribute access operations can be matched and replaced. Index steps never match the prefix.

Example
src := []byte(
	"foo = a.x + a.y * b.c\n" +
		"bar = max(a.z, b.c)\n",
)
f, diags := hclwrite.ParseConfig(src, "", hcl.Pos{Line: 1, Column: 1})
if diags.HasErrors() {
	fmt.Printf("errors: %s", diags)
	return
}

// Rename references of variable "a" to "z"
for _, attr := range f.Body().Attributes() {
	attr.Expr().RenameVariablePrefix(
		[]string{"a"},
		[]string{"z"},
	)
}

fmt.Printf("%s", f.Bytes())
Output:

foo = z.x + z.y * b.c
bar = max(z.z, b.c)

func (*Expression) Variables

func (e *Expression) Variables() []*Traversal

Variables returns the absolute traversals that exist within the receiving expression.

type File

type File struct {
	// contains filtered or unexported fields
}

func NewEmptyFile

func NewEmptyFile() *File

NewEmptyFile constructs a new file with no content, ready to be mutated by other calls that append to its body.

func NewFile

func NewFile() *File

NewFile creates a new file object that is empty and ready to have constructs added t it.

func ParseConfig

func ParseConfig(src []byte, filename string, start hcl.Pos) (*File, hcl.Diagnostics)

ParseConfig interprets the given source bytes into a *hclwrite.File. The resulting AST can be used to perform surgical edits on the source code before turning it back into bytes again.

func (*File) Body

func (f *File) Body() *Body

Body returns the root body of the file, which contains the top-level attributes and blocks.

func (*File) BuildTokens

func (it *File) BuildTokens(to Tokens) Tokens

func (*File) Bytes

func (f *File) Bytes() []byte

Bytes returns a buffer containing the source code resulting from the tokens underlying the receiving file. If any updates have been made via the AST API, these will be reflected in the result.

func (*File) WriteTo

func (f *File) WriteTo(wr io.Writer) (int64, error)

WriteTo writes the tokens underlying the receiving file to the given writer.

The tokens first have a simple formatting pass applied that adjusts only the spaces between them.

type ObjectAttrTokens added in v2.12.0

type ObjectAttrTokens struct {
	Name  Tokens
	Value Tokens
}

ObjectAttrTokens represents the raw tokens for the name and value of one attribute in an object constructor expression.

This is defined primarily for use with function TokensForObject. See that function's documentation for more information.

type Token

type Token struct {
	Type  hclsyntax.TokenType
	Bytes []byte

	// We record the number of spaces before each token so that we can
	// reproduce the exact layout of the original file when we're making
	// surgical changes in-place. When _new_ code is created it will always
	// be in the canonical style, but we preserve layout of existing code.
	SpacesBefore int
}

Token is a single sequence of bytes annotated with a type. It is similar in purpose to hclsyntax.Token, but discards the source position information since that is not useful in code generation.

type Tokens

type Tokens []*Token

Tokens is a flat list of tokens.

func TokensForFunctionCall added in v2.12.0

func TokensForFunctionCall(funcName string, args ...Tokens) Tokens

TokensForFunctionCall returns a sequence of tokens that represents call to the function with the given name, using the argument tokens to populate the argument expressions.

TokensForFunctionCall includes the given argument tokens verbatim into the positions in the resulting call expression, without any validation to ensure that they represent valid expressions. Use TokensForValue or TokensForTraversal to generate valid leaf expression values, or use TokensForTuple, TokensForObject, and TokensForFunctionCall to generate other nested compound expressions.

This function doesn't include an explicit way to generate the expansion symbol "..." on the final argument. Currently, generating that requires manually appending a TokenEllipsis with the bytes "..." to the tokens for the final argument.

func TokensForIdentifier added in v2.12.0

func TokensForIdentifier(name string) Tokens

TokensForIdentifier returns a sequence of tokens representing just the given identifier.

In practice this function can only ever generate exactly one token, because an identifier is always a leaf token in the syntax tree.

This is similar to calling TokensForTraversal with a single-step absolute traversal, but avoids the need to construct a separate traversal object for this simple common case. If you need to generate a multi-step traversal, use TokensForTraversal instead.

func TokensForObject added in v2.12.0

func TokensForObject(attrs []ObjectAttrTokens) Tokens

TokensForObject returns a sequence of tokens that represents an object constructor, with attribute name/value pairs populated from the given list of attribute token objects.

TokensForObject includes the given tokens verbatim into the name and value positions in the resulting object expression, without any validation to ensure that they represent valid expressions. Use TokensForValue or TokensForTraversal to generate valid leaf expression values, or use TokensForTuple, TokensForObject, and TokensForFunctionCall to generate other nested compound expressions.

Note that HCL requires placing a traversal expression in parentheses if you intend to use it as an attribute name expression, because otherwise the parser will interpret it as a literal attribute name. TokensForObject does not handle that situation automatically, so a caller must add the necessary `TokenOParen` and TokenCParen` manually if needed.

func TokensForTraversal

func TokensForTraversal(traversal hcl.Traversal) Tokens

TokensForTraversal returns a sequence of tokens that represents the given traversal.

If the traversal is absolute then the result is a self-contained, valid reference expression. If the traversal is relative then the returned tokens could be appended to some other expression tokens to traverse into the represented expression.

func TokensForTuple added in v2.12.0

func TokensForTuple(elems []Tokens) Tokens

TokensForTuple returns a sequence of tokens that represents a tuple constructor, with element expressions populated from the given list of tokens.

TokensForTuple includes the given elements verbatim into the element positions in the resulting tuple expression, without any validation to ensure that they represent valid expressions. Use TokensForValue or TokensForTraversal to generate valid leaf expression values, or use TokensForTuple, TokensForObject, and TokensForFunctionCall to generate other nested compound expressions.

func TokensForValue

func TokensForValue(val cty.Value) Tokens

TokensForValue returns a sequence of tokens that represents the given constant value.

This function only supports types that are used by HCL. In particular, it does not support capsule types and will panic if given one.

It is not possible to express an unknown value in source code, so this function will panic if the given value is unknown or contains any unknown values. A caller can call the value's IsWhollyKnown method to verify that no unknown values are present before calling TokensForValue.

func (Tokens) BuildTokens

func (ts Tokens) BuildTokens(to Tokens) Tokens

func (Tokens) Bytes

func (ts Tokens) Bytes() []byte

func (Tokens) Columns

func (ts Tokens) Columns() int

Columns returns the number of columns (grapheme clusters) the token sequence occupies. The result is not meaningful if there are newline or single-line comment tokens in the sequence.

func (Tokens) WriteTo

func (ts Tokens) WriteTo(wr io.Writer) (int64, error)

WriteTo takes an io.Writer and writes the bytes for each token to it, along with the spacing that separates each token. In other words, this allows serializing the tokens to a file or other such byte stream.

type Traversal

type Traversal struct {
	// contains filtered or unexported fields
}

Traversal represents a sequence of variable, attribute, and/or index operations.

func (*Traversal) BuildTokens

func (it *Traversal) BuildTokens(to Tokens) Tokens

type TraverseIndex

type TraverseIndex struct {
	// contains filtered or unexported fields
}

func (*TraverseIndex) BuildTokens

func (it *TraverseIndex) BuildTokens(to Tokens) Tokens

type TraverseName

type TraverseName struct {
	// contains filtered or unexported fields
}

func (*TraverseName) BuildTokens

func (it *TraverseName) BuildTokens(to Tokens) Tokens

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL