yq/pkg/yqlib/format.go
Robin H. Johnson c6029376a5
feat: K8S KYAML output format support (#2560)
* feat: K8S KYAML output format support

Reference: https://github.com/kubernetes/enhancements/blob/master/keps/sig-cli/5295-kyaml/README.md
Co-authored-by: Codex <codex@openai.com>
Generated-with: OpenAI Codex CLI (partial)
Signed-off-by: Robin H. Johnson <rjohnson@coreweave.com>

* build: gomodcache/gocache should not be committed

Signed-off-by: Robin H. Johnson <rjohnson@coreweave.com>

* chore: fix spelling of behaviour

Signed-off-by: Robin H. Johnson <robbat2@gentoo.org>

* build: pass GOFLAGS to docker to support buildvcs=false

In trying to develop the KYAML support, various tests gave false
positive results because they made assumptions about Git functionality
Make it possible to avoid that by passing GOFLAGS='-buildvcs=false' to
to Makefile.

Signed-off-by: Robin H. Johnson <robbat2@gentoo.org>

* doc: cover documentScenarios for tests

Signed-off-by: Robin H. Johnson <rjohnson@coreweave.com>

* build: exclude go caches from gosec

Without tuning, gosec scans all of the vendor/gocache/gomodcache, taking
several minutes (3m35 here), whereas the core of the yq takes only 15
seconds to scan.

If we intend to remediate upstream issues in future; add a seperate
target to scan those.

Signed-off-by: Robin H. Johnson <rjohnson@coreweave.com>

---------

Signed-off-by: Robin H. Johnson <rjohnson@coreweave.com>
Signed-off-by: Robin H. Johnson <robbat2@gentoo.org>
Co-authored-by: Codex <codex@openai.com>
2026-01-01 15:14:53 +11:00

198 lines
5.1 KiB
Go

package yqlib
import (
"fmt"
"path/filepath"
"slices"
"strings"
)
type EncoderFactoryFunction func() Encoder
type DecoderFactoryFunction func() Decoder
type Format struct {
FormalName string
Names []string
EncoderFactory EncoderFactoryFunction
DecoderFactory DecoderFactoryFunction
}
var YamlFormat = &Format{"yaml", []string{"y", "yml"},
func() Encoder { return NewYamlEncoder(ConfiguredYamlPreferences) },
func() Decoder { return NewYamlDecoder(ConfiguredYamlPreferences) },
}
var KYamlFormat = &Format{"kyaml", []string{"ky"},
func() Encoder { return NewKYamlEncoder(ConfiguredKYamlPreferences) },
// KYaml is stricter YAML
func() Decoder { return NewYamlDecoder(ConfiguredYamlPreferences) },
}
var JSONFormat = &Format{"json", []string{"j"},
func() Encoder { return NewJSONEncoder(ConfiguredJSONPreferences) },
func() Decoder { return NewJSONDecoder() },
}
var PropertiesFormat = &Format{"props", []string{"p", "properties"},
func() Encoder { return NewPropertiesEncoder(ConfiguredPropertiesPreferences) },
func() Decoder { return NewPropertiesDecoder() },
}
var CSVFormat = &Format{"csv", []string{"c"},
func() Encoder { return NewCsvEncoder(ConfiguredCsvPreferences) },
func() Decoder { return NewCSVObjectDecoder(ConfiguredCsvPreferences) },
}
var TSVFormat = &Format{"tsv", []string{"t"},
func() Encoder { return NewCsvEncoder(ConfiguredTsvPreferences) },
func() Decoder { return NewCSVObjectDecoder(ConfiguredTsvPreferences) },
}
var XMLFormat = &Format{"xml", []string{"x"},
func() Encoder { return NewXMLEncoder(ConfiguredXMLPreferences) },
func() Decoder { return NewXMLDecoder(ConfiguredXMLPreferences) },
}
var Base64Format = &Format{"base64", []string{},
func() Encoder { return NewBase64Encoder() },
func() Decoder { return NewBase64Decoder() },
}
var UriFormat = &Format{"uri", []string{},
func() Encoder { return NewUriEncoder() },
func() Decoder { return NewUriDecoder() },
}
var ShFormat = &Format{"", nil,
func() Encoder { return NewShEncoder() },
nil,
}
var TomlFormat = &Format{"toml", []string{},
func() Encoder { return NewTomlEncoderWithPrefs(ConfiguredTomlPreferences) },
func() Decoder { return NewTomlDecoder() },
}
var HclFormat = &Format{"hcl", []string{"h", "tf"},
func() Encoder { return NewHclEncoder(ConfiguredHclPreferences) },
func() Decoder { return NewHclDecoder() },
}
var ShellVariablesFormat = &Format{"shell", []string{"s", "sh"},
func() Encoder { return NewShellVariablesEncoder() },
nil,
}
var LuaFormat = &Format{"lua", []string{"l"},
func() Encoder { return NewLuaEncoder(ConfiguredLuaPreferences) },
func() Decoder { return NewLuaDecoder(ConfiguredLuaPreferences) },
}
var INIFormat = &Format{"ini", []string{"i"},
func() Encoder { return NewINIEncoder() },
func() Decoder { return NewINIDecoder() },
}
var Formats = []*Format{
YamlFormat,
KYamlFormat,
JSONFormat,
PropertiesFormat,
CSVFormat,
TSVFormat,
XMLFormat,
Base64Format,
UriFormat,
ShFormat,
TomlFormat,
HclFormat,
ShellVariablesFormat,
LuaFormat,
INIFormat,
}
func (f *Format) MatchesName(name string) bool {
if f.FormalName == name {
return true
}
return slices.Contains(f.Names, name)
}
func (f *Format) GetConfiguredEncoder() Encoder {
return f.EncoderFactory()
}
func FormatStringFromFilename(filename string) string {
if filename != "" {
GetLogger().Debugf("checking filename '%s' for auto format detection", filename)
ext := filepath.Ext(filename)
if len(ext) >= 2 && ext[0] == '.' {
format := strings.ToLower(ext[1:])
GetLogger().Debugf("detected format '%s'", format)
return format
}
}
GetLogger().Debugf("using default inputFormat 'yaml'")
return "yaml"
}
func FormatFromString(format string) (*Format, error) {
if format != "" {
for _, printerFormat := range Formats {
if printerFormat.MatchesName(format) {
return printerFormat, nil
}
}
}
return nil, fmt.Errorf("unknown format '%v' please use [%v]", format, GetAvailableOutputFormatString())
}
func GetAvailableOutputFormats() []*Format {
var formats = []*Format{}
for _, printerFormat := range Formats {
if printerFormat.EncoderFactory != nil {
formats = append(formats, printerFormat)
}
}
return formats
}
func GetAvailableOutputFormatString() string {
var formats = []string{}
for _, printerFormat := range GetAvailableOutputFormats() {
if printerFormat.FormalName != "" {
formats = append(formats, printerFormat.FormalName)
}
if len(printerFormat.Names) >= 1 {
formats = append(formats, printerFormat.Names[0])
}
}
return strings.Join(formats, "|")
}
func GetAvailableInputFormats() []*Format {
var formats = []*Format{}
for _, printerFormat := range Formats {
if printerFormat.DecoderFactory != nil {
formats = append(formats, printerFormat)
}
}
return formats
}
func GetAvailableInputFormatString() string {
var formats = []string{}
for _, printerFormat := range GetAvailableInputFormats() {
if printerFormat.FormalName != "" {
formats = append(formats, printerFormat.FormalName)
}
if len(printerFormat.Names) >= 1 {
formats = append(formats, printerFormat.Names[0])
}
}
return strings.Join(formats, "|")
}