garm/vendor/github.com/go-openapi/swag/yamlutils/yaml.go
Gabriel Adrian Samfira 47537fb8b6 Update all dependencies
Update all deps.

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
2026-01-06 01:13:34 +02:00

211 lines
5.6 KiB
Go

// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
// SPDX-License-Identifier: Apache-2.0
package yamlutils
import (
json "encoding/json"
"fmt"
"strconv"
"github.com/go-openapi/swag/jsonutils"
yaml "go.yaml.in/yaml/v3"
)
// YAMLToJSON converts a YAML document into JSON bytes.
//
// Note: a YAML document is the output from a [yaml.Marshaler], e.g a pointer to a [yaml.Node].
//
// [YAMLToJSON] is typically called after [BytesToYAMLDoc].
func YAMLToJSON(value any) (json.RawMessage, error) {
jm, err := transformData(value)
if err != nil {
return nil, err
}
b, err := jsonutils.WriteJSON(jm)
return json.RawMessage(b), err
}
// BytesToYAMLDoc converts a byte slice into a YAML document.
//
// This function only supports root documents that are objects.
//
// A YAML document is a pointer to a [yaml.Node].
func BytesToYAMLDoc(data []byte) (any, error) {
var document yaml.Node // preserve order that is present in the document
if err := yaml.Unmarshal(data, &document); err != nil {
return nil, err
}
if document.Kind != yaml.DocumentNode || len(document.Content) != 1 || document.Content[0].Kind != yaml.MappingNode {
return nil, fmt.Errorf("only YAML documents that are objects are supported: %w", ErrYAML)
}
return &document, nil
}
func yamlNode(root *yaml.Node) (any, error) {
switch root.Kind {
case yaml.DocumentNode:
return yamlDocument(root)
case yaml.SequenceNode:
return yamlSequence(root)
case yaml.MappingNode:
return yamlMapping(root)
case yaml.ScalarNode:
return yamlScalar(root)
case yaml.AliasNode:
return yamlNode(root.Alias)
default:
return nil, fmt.Errorf("unsupported YAML node type: %v: %w", root.Kind, ErrYAML)
}
}
func yamlDocument(node *yaml.Node) (any, error) {
if len(node.Content) != 1 {
return nil, fmt.Errorf("unexpected YAML Document node content length: %d: %w", len(node.Content), ErrYAML)
}
return yamlNode(node.Content[0])
}
func yamlMapping(node *yaml.Node) (any, error) {
const sensibleAllocDivider = 2 // nodes concatenate (key,value) sequences
m := make(YAMLMapSlice, len(node.Content)/sensibleAllocDivider)
if err := m.UnmarshalYAML(node); err != nil {
return nil, err
}
return m, nil
}
func yamlSequence(node *yaml.Node) (any, error) {
s := make([]any, 0)
for i := range len(node.Content) {
v, err := yamlNode(node.Content[i])
if err != nil {
return nil, fmt.Errorf("unable to decode YAML sequence value: %w: %w", err, ErrYAML)
}
s = append(s, v)
}
return s, nil
}
const ( // See https://yaml.org/type/
yamlStringScalar = "tag:yaml.org,2002:str"
yamlIntScalar = "tag:yaml.org,2002:int"
yamlBoolScalar = "tag:yaml.org,2002:bool"
yamlFloatScalar = "tag:yaml.org,2002:float"
yamlTimestamp = "tag:yaml.org,2002:timestamp"
yamlNull = "tag:yaml.org,2002:null"
)
func yamlScalar(node *yaml.Node) (any, error) {
switch node.LongTag() {
case yamlStringScalar:
return node.Value, nil
case yamlBoolScalar:
b, err := strconv.ParseBool(node.Value)
if err != nil {
return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting bool content: %w: %w", node.Value, err, ErrYAML)
}
return b, nil
case yamlIntScalar:
i, err := strconv.ParseInt(node.Value, 10, 64)
if err != nil {
return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting integer content: %w: %w", node.Value, err, ErrYAML)
}
return i, nil
case yamlFloatScalar:
f, err := strconv.ParseFloat(node.Value, 64)
if err != nil {
return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting float content: %w: %w", node.Value, err, ErrYAML)
}
return f, nil
case yamlTimestamp:
// YAML timestamp is marshaled as string, not time
return node.Value, nil
case yamlNull:
return nil, nil //nolint:nilnil
default:
return nil, fmt.Errorf("YAML tag %q is not supported: %w", node.LongTag(), ErrYAML)
}
}
func yamlStringScalarC(node *yaml.Node) (string, error) {
if node.Kind != yaml.ScalarNode {
return "", fmt.Errorf("expecting a string scalar but got %q: %w", node.Kind, ErrYAML)
}
switch node.LongTag() {
case yamlStringScalar, yamlIntScalar, yamlFloatScalar:
return node.Value, nil
default:
return "", fmt.Errorf("YAML tag %q is not supported as map key: %w", node.LongTag(), ErrYAML)
}
}
func format(t any) (string, error) {
switch k := t.(type) {
case string:
return k, nil
case uint:
return strconv.FormatUint(uint64(k), 10), nil
case uint8:
return strconv.FormatUint(uint64(k), 10), nil
case uint16:
return strconv.FormatUint(uint64(k), 10), nil
case uint32:
return strconv.FormatUint(uint64(k), 10), nil
case uint64:
return strconv.FormatUint(k, 10), nil
case int:
return strconv.Itoa(k), nil
case int8:
return strconv.FormatInt(int64(k), 10), nil
case int16:
return strconv.FormatInt(int64(k), 10), nil
case int32:
return strconv.FormatInt(int64(k), 10), nil
case int64:
return strconv.FormatInt(k, 10), nil
default:
return "", fmt.Errorf("unexpected map key type, got: %T: %w", k, ErrYAML)
}
}
func transformData(input any) (out any, err error) {
switch in := input.(type) {
case yaml.Node:
return yamlNode(&in)
case *yaml.Node:
return yamlNode(in)
case map[any]any:
o := make(YAMLMapSlice, 0, len(in))
for ke, va := range in {
var nmi YAMLMapItem
if nmi.Key, err = format(ke); err != nil {
return nil, err
}
v, ert := transformData(va)
if ert != nil {
return nil, ert
}
nmi.Value = v
o = append(o, nmi)
}
return o, nil
case []any:
len1 := len(in)
o := make([]any, len1)
for i := range len1 {
o[i], err = transformData(in[i])
if err != nil {
return nil, err
}
}
return o, nil
}
return input, nil
}