Skip to content
Draft
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
9 changes: 9 additions & 0 deletions gohcl/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ func decodeBodyToStruct(body hcl.Body, ctx *hcl.EvalContext, val reflect.Value)
return diags
}

return append(diags, decodeBodyToStructInner(body, content, leftovers, ctx, val)...)
}
func decodeBodyToStructInner(body hcl.Body, content *hcl.BodyContent, leftovers hcl.Body, ctx *hcl.EvalContext, val reflect.Value) hcl.Diagnostics {
var diags hcl.Diagnostics

tags := getFieldTags(val.Type())

if tags.Body != nil {
Expand Down Expand Up @@ -98,6 +103,10 @@ func decodeBodyToStruct(body hcl.Body, ctx *hcl.EvalContext, val reflect.Value)
diags = append(diags, decodeBodyToValue(leftovers, ctx, fieldV)...)
}
}
for _, embedded := range tags.Embedded {
fieldV := val.Field(embedded.FieldIndex)
diags = append(diags, decodeBodyToStructInner(body, content, leftovers, ctx, fieldV)...)
}

for name, fieldIdx := range tags.Attributes {
attr := content.Attributes[name]
Expand Down
49 changes: 38 additions & 11 deletions gohcl/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
package gohcl

import (
"cmp"
"fmt"
"reflect"
"sort"
"slices"
"strings"

"github.com/hashicorp/hcl/v2"
Expand All @@ -24,6 +25,10 @@ import (
// mapping is attempted, this function will panic.
func ImpliedBodySchema(val interface{}) (schema *hcl.BodySchema, partial bool) {
ty := reflect.TypeOf(val)
if nty, ok := val.(reflect.Value); ok {
// Recursion through embedded structs
ty = nty.Type()
}

if ty.Kind() == reflect.Ptr {
ty = ty.Elem()
Expand All @@ -38,12 +43,7 @@ func ImpliedBodySchema(val interface{}) (schema *hcl.BodySchema, partial bool) {

tags := getFieldTags(ty)

attrNames := make([]string, 0, len(tags.Attributes))
for n := range tags.Attributes {
attrNames = append(attrNames, n)
}
sort.Strings(attrNames)
for _, n := range attrNames {
idx := tags.Attributes[n]
optional := tags.Optional[n]
field := ty.Field(idx)
Expand All @@ -68,12 +68,7 @@ func ImpliedBodySchema(val interface{}) (schema *hcl.BodySchema, partial bool) {
})
}

blockNames := make([]string, 0, len(tags.Blocks))
for n := range tags.Blocks {
blockNames = append(blockNames, n)
}
sort.Strings(blockNames)
for _, n := range blockNames {
idx := tags.Blocks[n]
field := ty.Field(idx)
fty := field.Type
Expand Down Expand Up @@ -104,6 +99,25 @@ func ImpliedBodySchema(val interface{}) (schema *hcl.BodySchema, partial bool) {
}

partial = tags.Remain != nil

for _, embedded := range tags.Embedded {
nested, npartial := ImpliedBodySchema(reflect.New(embedded.Type))
if npartial && partial {
panic("only one 'remain' tag is permitted (nested)")
}

attrSchemas = append(attrSchemas, nested.Attributes...)
blockSchemas = append(blockSchemas, nested.Blocks...)
println(fmt.Sprintf("%v: %#v\n", embedded.Type, nested))
}

slices.SortStableFunc(attrSchemas, func(a, b hcl.AttributeSchema) int {
return cmp.Compare(a.Name, b.Name)
})
slices.SortStableFunc(blockSchemas, func(a, b hcl.BlockHeaderSchema) int {
return cmp.Compare(a.Type, b.Type)
})

schema = &hcl.BodySchema{
Attributes: attrSchemas,
Blocks: blockSchemas,
Expand All @@ -118,6 +132,12 @@ type fieldTags struct {
Remain *int
Body *int
Optional map[string]bool
Embedded []embeddedField
}

type embeddedField struct {
FieldIndex int
Type reflect.Type
}

type labelField struct {
Expand All @@ -135,8 +155,15 @@ func getFieldTags(ty reflect.Type) *fieldTags {
ct := ty.NumField()
for i := 0; i < ct; i++ {
field := ty.Field(i)

tag := field.Tag.Get("hcl")
if tag == "" {
if field.Type.Kind() == reflect.Struct && field.Anonymous {
ret.Embedded = append(ret.Embedded, embeddedField{
FieldIndex: i,
Type: field.Type,
})
}
continue
}

Expand Down