1 // Package strictyaml provides a strict YAML unmarshaller based on `go-yaml/yaml` 2 package strictyaml 3 4 import ( 5 "bytes" 6 "errors" 7 "fmt" 8 "io" 9 10 "gopkg.in/yaml.v3" 11 ) 12 13 // Unmarshal takes a byte array and an interface passed by reference. The 14 // d.Decode will read the next YAML-encoded value from its input and store it in 15 // the value pointed to by yamlObj. Any config keys from the incoming YAML 16 // document which do not correspond to expected keys in the config struct will 17 // result in errors. 18 // 19 // TODO(https://github.com/go-yaml/yaml/issues/639): Replace this function with 20 // yaml.Unmarshal once a more ergonomic way to set unmarshal options is added 21 // upstream. 22 func Unmarshal(b []byte, yamlObj interface{}) error { 23 r := bytes.NewReader(b) 24 25 d := yaml.NewDecoder(r) 26 d.KnownFields(true) 27 28 // d.Decode will mutate yamlObj 29 err := d.Decode(yamlObj) 30 31 if err != nil { 32 // io.EOF is returned when the YAML document is empty. 33 if errors.Is(err, io.EOF) { 34 return fmt.Errorf("unmarshalling YAML, bytes cannot be nil: %w", err) 35 } 36 return fmt.Errorf("unmarshalling YAML: %w", err) 37 } 38 39 // As bytes are read by the decoder, the length of the byte buffer should 40 // decrease. If it doesn't, there's a problem. 41 if r.Len() != 0 { 42 return fmt.Errorf("yaml object of size %d bytes had %d bytes of unexpected unconsumed trailers", r.Size(), r.Len()) 43 } 44 45 return nil 46 } 47