github.com/wolfi-dev/wolfictl@v0.16.11/pkg/configs/advisory/v2/yaml.go (about)

     1  package v2
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  
     7  	"gopkg.in/yaml.v3"
     8  )
     9  
    10  // strictUnmarshal is a helper function that unmarshals a YAML node into a
    11  // struct and returns an error if the node contains unknown fields.
    12  //
    13  // This function is useful because while the yaml.v3 package has a KnownFields
    14  // option for its Decoder, this option doesn't cascade to nested nodes that have
    15  // custom unmarshaling logic, which means the safety guaranteed at the root YAML
    16  // node is not necessarily guaranteed at all children nodes. This is a known
    17  // deficiency in the yaml.v3 library:
    18  // https://github.com/go-yaml/yaml/issues/460.
    19  //
    20  // Note that this function will return an error if the node is empty. This is
    21  // because the yaml.v3 library favors other unmarshal errors over unknown
    22  // field errors, so there's no way to distinguish between an empty node and a
    23  // node that contains unknown fields.
    24  func strictUnmarshal[T any](n *yaml.Node) (*T, error) {
    25  	by, err := yaml.Marshal(n)
    26  	if err != nil {
    27  		return nil, fmt.Errorf("intermediate marshaling node to bytes failed: %w", err)
    28  	}
    29  
    30  	buf := bytes.NewBuffer(by)
    31  	dec := yaml.NewDecoder(buf)
    32  	dec.KnownFields(true)
    33  
    34  	data := new(T)
    35  	err = dec.Decode(data)
    36  	if err != nil {
    37  		return nil, fmt.Errorf(
    38  			"failed to decode to Go type %T: %w",
    39  			data,
    40  			err,
    41  		)
    42  	}
    43  
    44  	return data, nil
    45  }