// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 package protohcl import ( "fmt" "github.com/hashicorp/hcl/v2" "google.golang.org/protobuf/reflect/protoreflect" ) func (u UnmarshalOptions) decodeBlocks(ctx *UnmarshalContext, blocks hcl.Blocks, msg protoreflect.Message, f protoreflect.FieldDescriptor) (protoreflect.Value, error) { if f.Kind() != protoreflect.MessageKind { return protoreflect.Value{}, fmt.Errorf("only protobuf message kinds can use HCL block syntax") } if f.IsMap() { return u.decodeBlocksToMap(ctx, blocks, msg, f) } if f.IsList() { return u.decodeBlocksToList(ctx, blocks, msg, f) } return u.decodeBlocksToMessage(ctx, blocks, msg, f) } func (u UnmarshalOptions) decodeBlocksToMap(ctx *UnmarshalContext, blocks hcl.Blocks, msg protoreflect.Message, f protoreflect.FieldDescriptor) (protoreflect.Value, error) { val := msg.NewField(f) mapVal := val.Map() for _, block := range blocks { if len(block.Labels) != 1 { return protoreflect.Value{}, fmt.Errorf("protobuf map fields must have 1 HCL block label") } key := protoreflect.ValueOfString(block.Labels[0]) value := mapVal.NewValue() msgVal := value.Message() err := u.decodeMessage( &UnmarshalContext{ Parent: ctx, Name: fmt.Sprintf("%s[%q]", u.FieldNamer.NameField(f), block.Labels[0]), Message: msgVal, Range: block.DefRange, }, u.bodyDecoder(block.Body), msgVal, ) if err != nil { return protoreflect.Value{}, err } mapVal.Set(key.MapKey(), value) } return val, nil } func (u UnmarshalOptions) decodeBlocksToList(ctx *UnmarshalContext, blocks hcl.Blocks, msg protoreflect.Message, f protoreflect.FieldDescriptor) (protoreflect.Value, error) { val := msg.NewField(f) listVal := val.List() var err error for idx, block := range blocks { if len(block.Labels) > 0 { return protoreflect.Value{}, fmt.Errorf("repeated protobuf fields must not have HCL block labels") } elem := listVal.NewElement() elemMsg := elem.Message() err = u.decodeMessage( &UnmarshalContext{ Parent: ctx, Name: fmt.Sprintf("%s[%d]", u.FieldNamer.NameField(f), idx), Message: elemMsg, Range: block.DefRange, }, u.bodyDecoder(block.Body), elemMsg, ) if err != nil { return protoreflect.Value{}, err } listVal.Append(elem) } return val, nil } func (u UnmarshalOptions) decodeBlocksToMessage(ctx *UnmarshalContext, blocks hcl.Blocks, msg protoreflect.Message, f protoreflect.FieldDescriptor) (protoreflect.Value, error) { if len(blocks) > 1 { return protoreflect.Value{}, fmt.Errorf("only one HCL block may be specified for a non-repeated protobuf Message") } val := msg.NewField(f) valMsg := val.Message() err := u.decodeMessage( &UnmarshalContext{ Parent: ctx, Name: blocks[0].Type, Message: valMsg, Range: blocks[0].DefRange, }, u.bodyDecoder(blocks[0].Body), valMsg, ) if err != nil { return protoreflect.Value{}, err } return val, nil }