Skip to content
48 changes: 47 additions & 1 deletion hclwrite/ast_expression.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright IBM Corp. 2014, 2025
// Copyright IBM Corp. 2014, 2026
// SPDX-License-Identifier: MPL-2.0

package hclwrite
Expand All @@ -15,6 +15,11 @@ type Expression struct {
inTree

absTraversals nodeSet
wrapped *node
}

func (e *Expression) unwrap() *node {
return e.wrapped
}

func newExpression() *Expression {
Expand All @@ -24,6 +29,15 @@ func newExpression() *Expression {
}
}

func (e *Expression) wrap(c nodeContent) {
if e.wrapped != nil {
panic("this is a problem")
}

e.wrapped = newNode(c)
e.children.AppendNode(e.wrapped)
}

// NewExpressionRaw constructs an expression containing the given raw tokens.
//
// There is no automatic validation that the given tokens produce a valid
Expand Down Expand Up @@ -187,6 +201,38 @@ Traversals:
}
}

func (e *Expression) AsObjectConsExpr() *ObjectConsExpr {
if e.wrapped == nil {
return nil
}

if object, ok := e.wrapped.content.(*ObjectConsExpr); ok {
return object
}

return nil
}

func (e *Expression) asQuoted() *quoted {
if e.wrapped == nil {
return nil
}

if quoted, ok := e.wrapped.content.(*quoted); ok {
return quoted
}

return nil
}

func (e *Expression) AsQuotedLiteral() Tokens {
if quoted := e.asQuoted(); quoted == nil {
return Tokens{}
} else {
return quoted.tokens
}
}

// Traversal represents a sequence of variable, attribute, and/or index
// operations.
type Traversal struct {
Expand Down
160 changes: 160 additions & 0 deletions hclwrite/ast_object_cons_expr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// Copyright IBM Corp. 2014, 2026
// SPDX-License-Identifier: MPL-2.0

package hclwrite

import (
"reflect"
"strings"
)

type ObjectConsExpr struct {
inTree

items nodeSet
}

func newObjectConsExpr() *ObjectConsExpr {
return &ObjectConsExpr{
inTree: newInTree(),
items: newNodeSet(),
}
}

func (o *ObjectConsExpr) Items() []*ObjectConsItem {
list := o.items.List()
items := make([]*ObjectConsItem, 0, len(list))

for _, n := range list {
items = append(items, n.content.(*ObjectConsItem))
}

return items
}

func (o *ObjectConsExpr) ItemFor(key string) *ObjectConsItem {
for _, n := range o.items.List() {
if item, ok := n.content.(*ObjectConsItem); ok {
k := item.key.content.(*ObjectConsKeyExpr)

maybeKey := k.String()
if maybeKey == key {
return item
}
}
}

return nil
}

func (o *ObjectConsExpr) ValueFor(key string) *ObjectConsValue {
if item := o.ItemFor(key); item == nil {
return nil
} else {
return item.value.content.(*ObjectConsValue)
}
}

// SetItemRaw either replaces the expression of an existing item of the given
// name or adds a new item definition to the end of the object, 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 SetItemValue or SetItemTraversal.
func (o *ObjectConsExpr) SetItemRaw(key string, tokens Tokens) (k *ObjectConsKeyExpr, v *ObjectConsValue) {
item := o.ItemFor(key)
expr := NewExpressionRaw(tokens)
if item != nil {
k = item.key.content.(*ObjectConsKeyExpr)
v = item.value.content.(*ObjectConsValue)

v.expr.list.Clear()
v.expr = v.expr.ReplaceWith(expr)
v.children.AppendNode(v.expr)

} else {
item = newObjectConsItem()

ident := newIdentifier(TokensForIdentifier(key)[0])
k = newObjectConsKeyExpr(newNode(ident))
item.key = item.children.Append(k)

v = newObjectConsValue() // TODO: expr in constructor
v.expr = v.children.Append(expr)
item.value = item.children.Append(v)

node := newNode(item)
o.children.AppendNode(node)
o.items.Add(node)
}
return
}

type ObjectConsItem struct {
inTree
key *node
value *node
}

func newObjectConsItem() *ObjectConsItem {
return &ObjectConsItem{
inTree: newInTree(),
}
}

type ObjectConsKeyExpr struct {
inTree

wrapped *node
}

func (k *ObjectConsKeyExpr) unwrap() *node {
return k.wrapped
}

func newObjectConsKeyExpr(wrapped *node) *ObjectConsKeyExpr {
expr := &ObjectConsKeyExpr{
inTree: newInTree(),
wrapped: wrapped,
}
expr.children.AppendNode(wrapped)

return expr
}

func (k *ObjectConsKeyExpr) String() string {
if k.wrapped == nil {
return ""
}

unwrapped := unwrapUntilType(k.wrapped, reflect.TypeFor[*identifier]())
if unwrapped == nil {
return ""
}

if ident, ok := unwrapped.content.(*identifier); ok {
var b strings.Builder
tok := ident.BuildTokens(nil)
format(tok) // removes any SpacesBefore
tok.WriteTo(&b)

Check failure on line 139 in hclwrite/ast_object_cons_expr.go

View workflow job for this annotation

GitHub Actions / linting

Error return value of `tok.WriteTo` is not checked (errcheck)

Check failure on line 139 in hclwrite/ast_object_cons_expr.go

View workflow job for this annotation

GitHub Actions / linting

Error return value of `tok.WriteTo` is not checked (errcheck)

return b.String()
}

return ""
}

type ObjectConsValue struct {
inTree
expr *node
}

func newObjectConsValue() *ObjectConsValue {
return &ObjectConsValue{
inTree: newInTree(),
}
}

func (o *ObjectConsValue) Expr() *Expression {
return o.expr.content.(*Expression)
}
Loading
Loading