diff --git a/gohcl/decode.go b/gohcl/decode.go index f0d589d..3525383 100644 --- a/gohcl/decode.go +++ b/gohcl/decode.go @@ -65,6 +65,19 @@ func decodeBodyToStruct(body hcl.Body, ctx *hcl.EvalContext, val reflect.Value) tags := getFieldTags(val.Type()) + if tags.Body != nil { + fieldIdx := *tags.Body + field := val.Type().Field(fieldIdx) + fieldV := val.Field(fieldIdx) + switch { + case bodyType.AssignableTo(field.Type): + fieldV.Set(reflect.ValueOf(body)) + + default: + diags = append(diags, decodeBodyToValue(body, ctx, fieldV)...) + } + } + if tags.Remain != nil { fieldIdx := *tags.Remain field := val.Type().Field(fieldIdx) diff --git a/gohcl/decode_test.go b/gohcl/decode_test.go index 8f065c4..0927490 100644 --- a/gohcl/decode_test.go +++ b/gohcl/decode_test.go @@ -217,6 +217,30 @@ func TestDecodeBody(t *testing.T) { }), 0, }, + { + map[string]interface{}{ + "name": "Ermintrude", + "age": 50, + }, + makeInstantiateType(struct { + Name string `hcl:"name"` + Body hcl.Body `hcl:",body"` + Remain hcl.Body `hcl:",remain"` + }{}), + func(gotI interface{}) bool { + got := gotI.(struct { + Name string `hcl:"name"` + Body hcl.Body `hcl:",body"` + Remain hcl.Body `hcl:",remain"` + }) + + attrs, _ := got.Body.JustAttributes() + + return got.Name == "Ermintrude" && len(attrs) == 2 && + attrs["name"] != nil && attrs["age"] != nil + }, + 0, + }, { map[string]interface{}{ "noodle": map[string]interface{}{}, diff --git a/gohcl/doc.go b/gohcl/doc.go index c1921cb..419efb0 100644 --- a/gohcl/doc.go +++ b/gohcl/doc.go @@ -30,6 +30,13 @@ // in which case multiple blocks of the corresponding type are decoded into // the slice. // +// "body" can be placed on a single field of type hcl.Body to capture +// the full hcl.Body that was decoded for a block. This does not allow leftover +// values like "remain", so a decoding error will still be returned if leftover +// fields are given. If you want to capture the decoding body PLUS leftover +// fields, you must specify a "remain" field as well to prevent errors. The +// body field and the remain field will both contain the leftover fields. +// // "label" fields are considered only in a struct used as the type of a field // marked as "block", and are used sequentially to capture the labels of // the blocks being decoded. In this case, the name token is used only as diff --git a/gohcl/schema.go b/gohcl/schema.go index ecf6ddb..df21cc4 100644 --- a/gohcl/schema.go +++ b/gohcl/schema.go @@ -113,6 +113,7 @@ type fieldTags struct { Blocks map[string]int Labels []labelField Remain *int + Body *int Optional map[string]bool } @@ -162,6 +163,12 @@ func getFieldTags(ty reflect.Type) *fieldTags { } idx := i // copy, because this loop will continue assigning to i ret.Remain = &idx + case "body": + if ret.Body != nil { + panic("only one 'body' tag is permitted") + } + idx := i // copy, because this loop will continue assigning to i + ret.Body = &idx case "optional": ret.Attributes[name] = i ret.Optional[name] = true