d6367b5f96
This will allow for use-cases such as renaming a variable (changing the content of the first token) and replacing variable references with constant values that they evaluate to for debug purposes.
206 lines
4.9 KiB
Go
206 lines
4.9 KiB
Go
package hclwrite
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
|
|
"github.com/hashicorp/hcl2/hcl"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
type Node interface {
|
|
walkChildNodes(w internalWalkFunc)
|
|
Tokens() *TokenSeq
|
|
}
|
|
|
|
type internalWalkFunc func(Node)
|
|
|
|
type File struct {
|
|
Name string
|
|
SrcBytes []byte
|
|
|
|
Body *Body
|
|
AllTokens *TokenSeq
|
|
}
|
|
|
|
// WriteTo writes the tokens underlying the receiving file to the given writer.
|
|
func (f *File) WriteTo(wr io.Writer) (int, error) {
|
|
return f.AllTokens.WriteTo(wr)
|
|
}
|
|
|
|
// 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 (f *File) Bytes() []byte {
|
|
buf := &bytes.Buffer{}
|
|
f.WriteTo(buf)
|
|
return buf.Bytes()
|
|
}
|
|
|
|
// Format makes in-place modifications to the tokens underlying the receiving
|
|
// file in order to change the whitespace to be in canonical form.
|
|
func (f *File) Format() {
|
|
format(f.Body.AllTokens.Tokens())
|
|
}
|
|
|
|
type Body struct {
|
|
// Items may contain Attribute, Block and Unstructured instances.
|
|
// Items and AllTokens should be updated only by methods of this type,
|
|
// since they must be kept synchronized for correct operation.
|
|
Items []Node
|
|
AllTokens *TokenSeq
|
|
|
|
// IndentLevel is the number of spaces that should appear at the start
|
|
// of lines added within this body.
|
|
IndentLevel int
|
|
}
|
|
|
|
func (n *Body) walkChildNodes(w internalWalkFunc) {
|
|
for _, item := range n.Items {
|
|
w(item)
|
|
}
|
|
}
|
|
|
|
func (n *Body) Tokens() *TokenSeq {
|
|
return n.AllTokens
|
|
}
|
|
|
|
func (n *Body) AppendItem(node Node) {
|
|
n.Items = append(n.Items, node)
|
|
n.AppendUnstructuredTokens(node.Tokens())
|
|
}
|
|
|
|
func (n *Body) AppendUnstructuredTokens(seq *TokenSeq) {
|
|
if n.AllTokens == nil {
|
|
new := make(TokenSeq, 0, 1)
|
|
n.AllTokens = &new
|
|
}
|
|
*(n.AllTokens) = append(*(n.AllTokens), seq)
|
|
}
|
|
|
|
// FindAttribute returns the first attribute item from the body that has the
|
|
// given name, or returns nil if there is currently no matching attribute.
|
|
//
|
|
// A valid AST has only one definition of each attribute, but that constraint
|
|
// is not enforced in the hclwrite AST, so a tree that has been mutated by
|
|
// other calls may contain additional matching attributes that cannot be seen
|
|
// by this method.
|
|
func (n *Body) FindAttribute(name string) *Attribute {
|
|
nameBytes := []byte(name)
|
|
for _, item := range n.Items {
|
|
if attr, ok := item.(*Attribute); ok {
|
|
if attr.NameTokens.IsIdent(nameBytes) {
|
|
return attr
|
|
}
|
|
}
|
|
}
|
|
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.
|
|
//
|
|
// 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.
|
|
func (n *Body) SetAttributeValue(name string, val cty.Value) *Attribute {
|
|
panic("Body.SetAttributeValue not yet implemented")
|
|
}
|
|
|
|
// SetAttributeTraversal 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 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 (n *Body) SetAttributeTraversal(name string, traversal hcl.Traversal) *Attribute {
|
|
panic("Body.SetAttributeTraversal not yet implemented")
|
|
}
|
|
|
|
type Attribute struct {
|
|
AllTokens *TokenSeq
|
|
|
|
LeadCommentTokens *TokenSeq
|
|
NameTokens *TokenSeq
|
|
EqualsTokens *TokenSeq
|
|
Expr *Expression
|
|
LineCommentTokens *TokenSeq
|
|
EOLTokens *TokenSeq
|
|
}
|
|
|
|
func (a *Attribute) walkChildNodes(w internalWalkFunc) {
|
|
w(a.Expr)
|
|
}
|
|
|
|
func (n *Attribute) Tokens() *TokenSeq {
|
|
return n.AllTokens
|
|
}
|
|
|
|
type Block struct {
|
|
AllTokens *TokenSeq
|
|
|
|
LeadCommentTokens *TokenSeq
|
|
TypeTokens *TokenSeq
|
|
LabelTokens []*TokenSeq
|
|
LabelTokensFlat *TokenSeq
|
|
OBraceTokens *TokenSeq
|
|
Body *Body
|
|
CBraceTokens *TokenSeq
|
|
EOLTokens *TokenSeq
|
|
}
|
|
|
|
func (n *Block) walkChildNodes(w internalWalkFunc) {
|
|
w(n.Body)
|
|
}
|
|
|
|
func (n *Block) Tokens() *TokenSeq {
|
|
return n.AllTokens
|
|
}
|
|
|
|
type Expression struct {
|
|
AllTokens *TokenSeq
|
|
AbsTraversals []*Traversal
|
|
}
|
|
|
|
func (n *Expression) walkChildNodes(w internalWalkFunc) {
|
|
for _, name := range n.AbsTraversals {
|
|
w(name)
|
|
}
|
|
}
|
|
|
|
func (n *Expression) Tokens() *TokenSeq {
|
|
return n.AllTokens
|
|
}
|
|
|
|
type Traversal struct {
|
|
AllTokens *TokenSeq
|
|
Steps []*Traverser
|
|
}
|
|
|
|
func (n *Traversal) walkChildNodes(w internalWalkFunc) {
|
|
for _, step := range n.Steps {
|
|
w(step)
|
|
}
|
|
}
|
|
|
|
func (n *Traversal) Tokens() *TokenSeq {
|
|
return n.AllTokens
|
|
}
|
|
|
|
type Traverser struct {
|
|
AllTokens *TokenSeq
|
|
Logical hcl.Traverser
|
|
}
|
|
|
|
func (n *Traverser) Tokens() *TokenSeq {
|
|
return n.AllTokens
|
|
}
|
|
|
|
func (n *Traverser) walkChildNodes(w internalWalkFunc) {
|
|
// No child nodes for a traversal step
|
|
}
|