updated vendor

This commit is contained in:
2026-06-16 08:02:19 +02:00
parent 2f7f99d3f0
commit 77299d0c64
1283 changed files with 67302 additions and 208958 deletions
+250
View File
@@ -0,0 +1,250 @@
// Copyright 2011-2019 Canonical Ltd
// Copyright 2025 The go-yaml Project Contributors
// SPDX-License-Identifier: Apache-2.0
// Struct metadata extraction for YAML marshaling/unmarshaling.
//
// This file analyzes Go struct types to build mappings between YAML keys and
// struct fields. It parses struct tags like `yaml:"name,omitempty,flow,inline"`
// and caches the results for efficient repeated access.
//
// Used by:
// - Constructor: maps YAML keys to struct fields when unmarshaling
// - Representer: maps struct fields to YAML keys when marshaling
//
// Key types:
// - structInfo: cached metadata about a struct type
// - fieldInfo: metadata about a single struct field
// - getStructInfo(): analyzes a struct type and returns cached metadata
package libyaml
import (
"errors"
"fmt"
"reflect"
"strings"
"sync"
)
// structInfo holds cached information about a struct's YAML-relevant fields.
type structInfo struct {
FieldsMap map[string]fieldInfo
FieldsList []fieldInfo
// InlineMap is the number of the field in the struct that
// contains an ,inline map, or -1 if there's none.
InlineMap int
// InlineConstructors holds indexes to inlined fields that
// contain constructor values.
InlineConstructors [][]int
}
// fieldInfo holds information about a single struct field.
type fieldInfo struct {
Key string
Num int
OmitEmpty bool
Flow bool
// Id holds the unique field identifier, so we can cheaply
// check for field duplicates without maintaining an extra map.
Id int
// Inline holds the field index if the field is part of an inlined struct.
Inline []int
}
// structMap caches struct reflection information.
// fieldMapMutex protects access to structMap.
// constructorType holds the [reflect.Type] for the constructor interface.
var (
structMap = make(map[reflect.Type]*structInfo)
fieldMapMutex sync.RWMutex
constructorType reflect.Type
)
// constructor interface is defined here to detect types that implement
// UnmarshalYAML during struct reflection.
type constructor interface {
UnmarshalYAML(value *Node) error
}
// init initializes the constructorType variable with the [reflect.Type] of constructor interface.
func init() {
var v constructor
constructorType = reflect.ValueOf(&v).Elem().Type()
}
// hasConstructYAMLMethod checks if a type has an UnmarshalYAML method
// that takes a *Node from an allowlisted v3 yaml package. This detects
// v3 backward-compatible Unmarshaler implementations whose Node type
// can't be checked via interface assertion from this package.
func hasConstructYAMLMethod(t reflect.Type) bool {
method, found := t.MethodByName("UnmarshalYAML")
if !found {
return false
}
// Check signature: func(*T) UnmarshalYAML(*Node) error
mtype := method.Type
if mtype.NumIn() != 2 || mtype.NumOut() != 1 {
return false
}
// First param is receiver (already checked by MethodByName)
// Second param should be a pointer to a Node-like struct
paramType := mtype.In(1)
if paramType.Kind() != reflect.Ptr {
return false
}
elemType := paramType.Elem()
if elemType.Kind() != reflect.Struct || elemType.Name() != "Node" || !isYAMLNodePkg(elemType.PkgPath()) {
return false
}
// Return type should be error
retType := mtype.Out(0)
if retType.Kind() != reflect.Interface || retType.Name() != "error" {
return false
}
return true
}
func isYAMLNodePkg(pkg string) bool {
switch pkg {
case "gopkg.in/yaml.v3", "go.yaml.in/yaml/v3":
return true
}
return false
}
// getStructInfo returns cached information about a struct type's fields.
// It parses struct tags and builds a map of field names to field info.
func getStructInfo(st reflect.Type) (*structInfo, error) {
fieldMapMutex.RLock()
sinfo, found := structMap[st]
fieldMapMutex.RUnlock()
if found {
return sinfo, nil
}
n := st.NumField()
fieldsMap := make(map[string]fieldInfo)
fieldsList := make([]fieldInfo, 0, n)
inlineMap := -1
inlineConstructors := [][]int(nil)
for i := 0; i != n; i++ {
field := st.Field(i)
if field.PkgPath != "" && !field.Anonymous {
continue // Private field
}
info := fieldInfo{Num: i}
tag := field.Tag.Get("yaml")
if tag == "" && !strings.Contains(string(field.Tag), ":") {
tag = string(field.Tag)
}
if tag == "-" {
continue
}
inline := false
fields := strings.Split(tag, ",")
if len(fields) > 1 {
for _, flag := range fields[1:] {
switch flag {
case "omitempty":
info.OmitEmpty = true
case "flow":
info.Flow = true
case "inline":
inline = true
default:
return nil, fmt.Errorf("unsupported flag %q in tag %q of type %s", flag, tag, st)
}
}
tag = fields[0]
}
if inline {
switch field.Type.Kind() {
case reflect.Map:
if inlineMap >= 0 {
return nil, errors.New("multiple ,inline maps in struct " + st.String())
}
if field.Type.Key() != reflect.TypeOf("") {
return nil, errors.New("option ,inline needs a map with string keys in struct " + st.String())
}
inlineMap = info.Num
case reflect.Struct, reflect.Pointer:
ftype := field.Type
for ftype.Kind() == reflect.Pointer {
ftype = ftype.Elem()
}
if ftype.Kind() != reflect.Struct {
return nil, errors.New("option ,inline may only be used on a struct or map field")
}
// Check for both libyaml.constructor and yaml.Unmarshaler (by method name)
if reflect.PointerTo(ftype).Implements(constructorType) || hasConstructYAMLMethod(reflect.PointerTo(ftype)) {
inlineConstructors = append(inlineConstructors, []int{i})
} else {
sinfo, err := getStructInfo(ftype)
if err != nil {
return nil, err
}
for _, index := range sinfo.InlineConstructors {
inlineConstructors = append(inlineConstructors, append([]int{i}, index...))
}
for _, finfo := range sinfo.FieldsList {
if _, found := fieldsMap[finfo.Key]; found {
msg := "duplicated key '" + finfo.Key + "' in struct " + st.String()
return nil, errors.New(msg)
}
if finfo.Inline == nil {
finfo.Inline = []int{i, finfo.Num}
} else {
finfo.Inline = append([]int{i}, finfo.Inline...)
}
finfo.Id = len(fieldsList)
fieldsMap[finfo.Key] = finfo
fieldsList = append(fieldsList, finfo)
}
}
default:
return nil, errors.New("option ,inline may only be used on a struct or map field")
}
continue
}
if tag != "" {
info.Key = tag
} else {
info.Key = strings.ToLower(field.Name)
}
if _, found = fieldsMap[info.Key]; found {
msg := "duplicated key '" + info.Key + "' in struct " + st.String()
return nil, errors.New(msg)
}
info.Id = len(fieldsList)
fieldsList = append(fieldsList, info)
fieldsMap[info.Key] = info
}
sinfo = &structInfo{
FieldsMap: fieldsMap,
FieldsList: fieldsList,
InlineMap: inlineMap,
InlineConstructors: inlineConstructors,
}
fieldMapMutex.Lock()
structMap[st] = sinfo
fieldMapMutex.Unlock()
return sinfo, nil
}