Skip to content
This repository has been archived by the owner on Mar 20, 2024. It is now read-only.

Use the hcl:,optional tag to omit empty values from the encoded output #24

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package hclencoder
import (
"errors"
"fmt"
"log"
"reflect"
"sort"
"strconv"
"strings"
"unsafe"

"github.com/hashicorp/hcl/hcl/ast"
"github.com/hashicorp/hcl/hcl/token"
Expand All @@ -27,6 +29,11 @@ const (
// the key for the value.
SquashTag string = "squash"

// Optional tag is attached to fields of a struct that are not optional
// when being read by the HCL Parser. If the field is empty, these fields
// can be omitted from the resulting HCL output
OptionalTag string = "optional"

// UnusedKeysTag is a flag that indicates any unused keys found by the
// decoder are stored in this field of type []string. This has the same
// behavior as the OmitTag and is not encoded.
Expand Down Expand Up @@ -241,9 +248,17 @@ func encodeStruct(in reflect.Value) (ast.Node, []*ast.ObjectKey, error) {

// if the OmitEmptyTag is provided, check if the value is its zero value.
rawVal := in.Field(i)
//can only determine emptiness if the field is settable from the current context
if meta.omitEmpty {
var rawOut interface{}
zeroVal := reflect.Zero(rawVal.Type()).Interface()
if reflect.DeepEqual(rawVal.Interface(), zeroVal) {
if rawVal.CanInterface() {
rawOut = rawVal.Interface()
} else if rawVal.CanAddr() {
rvAddr := reflect.NewAt(rawVal.Type(), unsafe.Pointer(rawVal.UnsafeAddr()))
rawOut = rvAddr.Elem().Interface()
}
if rawOut != nil && reflect.DeepEqual(rawOut, zeroVal) {
continue
}
}
Expand Down Expand Up @@ -375,6 +390,9 @@ func extractFieldMeta(f reflect.StructField) (meta fieldMeta) {
meta.decodedFields = true
case UnusedKeysTag:
meta.unusedKeys = true
case OptionalTag:
log.Printf("Setting field to empty for field %s %s %s", f.PkgPath, f.Type, f.Name)
meta.omitEmpty = true
}
}
}
Expand Down
19 changes: 19 additions & 0 deletions nodes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,21 @@ func TestEncodeStruct(t *testing.T) {
},
}}},
},
{
ID: "optional field - empty",
Input: reflect.ValueOf(OptionalStruct{}),
Expected: &ast.ObjectType{List: &ast.ObjectList{Items: []*ast.ObjectItem{}}},
},
{
ID: "optional field - not empty",
Input: reflect.ValueOf(OptionalStruct{"foo"}),
Expected: &ast.ObjectType{List: &ast.ObjectList{Items: []*ast.ObjectItem{
&ast.ObjectItem{
Keys: []*ast.ObjectKey{{Token: token.Token{Type: token.IDENT, Text: "Bar"}}},
Val: &ast.LiteralType{Token: token.Token{Type: token.STRING, Text: `"foo"`}},
},
}}},
},
{
ID: "nil field",
Input: reflect.ValueOf(NillableStruct{}),
Expand Down Expand Up @@ -695,6 +710,10 @@ type OmitEmptyStruct struct {
Bar string `hcle:"omitempty"`
}

type OptionalStruct struct {
Bar string `hcl:",optional"`
}

type InvalidStruct struct {
Chan chan struct{}
}
Expand Down