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) ¶
package main import ( "fmt" "github.com/ryan-jan/hcl" "github.com/ryan-jan/hcl/hclwrite" "github.com/zclconf/go-cty/cty" ) func main() { 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 ¶
- func Format(src []byte) []byte
- type Attribute
- type Block
- type Body
- func (b *Body) AppendBlock(block *Block) *Block
- func (b *Body) AppendNewBlock(typeName string, labels []string) *Block
- func (b *Body) AppendNewline()
- func (b *Body) AppendUnstructuredTokens(ts Tokens)
- func (b *Body) Attributes() map[string]*Attribute
- func (b *Body) Blocks() []*Block
- func (it *Body) BuildTokens(to Tokens) Tokens
- func (b *Body) Clear()
- func (b *Body) FirstMatchingBlock(typeName string, labels []string) *Block
- func (b *Body) GetAttribute(name string) *Attribute
- func (b *Body) RemoveAttribute(name string) *Attribute
- func (b *Body) RemoveBlock(block *Block) bool
- func (b *Body) SetAttributeRaw(name string, tokens Tokens) *Attribute
- func (b *Body) SetAttributeTraversal(name string, traversal hcl.Traversal) *Attribute
- func (b *Body) SetAttributeValue(name string, val cty.Value) *Attribute
- type Expression
- type File
- type ObjectAttrTokens
- type Token
- type Tokens
- func TokensForFunctionCall(funcName string, args ...Tokens) Tokens
- func TokensForIdentifier(name string) Tokens
- func TokensForObject(attrs []ObjectAttrTokens) Tokens
- func TokensForTraversal(traversal hcl.Traversal) Tokens
- func TokensForTuple(elems []Tokens) Tokens
- func TokensForValue(val cty.Value) Tokens
- type Traversal
- type TraverseIndex
- type TraverseName
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Format ¶
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 (*Attribute) Expr ¶
func (a *Attribute) Expr() *Expression
type Block ¶
type Block struct {
// contains filtered or unexported fields
}
func (*Block) 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 (*Block) SetLabels ¶
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.
type Body ¶
type Body struct {
// contains filtered or unexported fields
}
func (*Body) AppendBlock ¶
AppendBlock appends an existing block (which must not be already attached to a body) to the end of the receiving body.
func (*Body) AppendNewBlock ¶
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 (*Body) Attributes ¶
Attributes returns a new map of all of the attributes in the body, with the attribute names as the keys.
func (*Body) BuildTokens ¶
func (*Body) Clear ¶
func (b *Body) Clear()
Clear removes all of the items from the body, making it empty.
func (*Body) FirstMatchingBlock ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 (*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 ¶
package main import ( "fmt" "github.com/ryan-jan/hcl" "github.com/ryan-jan/hcl/hclwrite" ) func main() { 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 ¶
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 ¶
Body returns the root body of the file, which contains the top-level attributes and blocks.
func (*File) BuildTokens ¶
type ObjectAttrTokens ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
type Traversal ¶
type Traversal struct {
// contains filtered or unexported fields
}
Traversal represents a sequence of variable, attribute, and/or index operations.
func (*Traversal) BuildTokens ¶
type TraverseIndex ¶
type TraverseIndex struct {
// contains filtered or unexported fields
}
func (*TraverseIndex) BuildTokens ¶
type TraverseName ¶
type TraverseName struct {
// contains filtered or unexported fields
}