Files
buildkit/vendor/github.com/go-openapi/strfmt/format.go
T
Tonis Tiigi 7281e97ffb vendor: update deps to match moby
- github.com/Microsoft/hcsshim v0.15.0-rc.4 -> v0.15.0
- google.golang.org/grpc v1.84.0 -> v1.85.0
- golang.org/x/net v0.61.0 -> v0.62.0
- go.opentelemetry.io/otel v1.46.0 -> v1.47.0
- go.opentelemetry.io/contrib v0.71.0 -> v0.72.0
- github.com/prometheus/client_golang v1.24.1 -> v1.25.0
- github.com/prometheus/procfs v0.21.1 -> v0.22.0
- github.com/go-openapi/* to moby versions

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2026-11-04 00:44:46 -08:00

260 lines
6.6 KiB
Go

// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
// SPDX-License-Identifier: Apache-2.0
package strfmt
import (
"encoding"
"fmt"
"reflect"
"slices"
"strings"
"sync"
"github.com/go-openapi/errors"
"github.com/go-viper/mapstructure/v2"
)
// Validator represents a validator for a string format.
type Validator func(string) bool
// NewFormats creates a new formats registry seeded with the values from the default.
func NewFormats() Registry { //nolint:ireturn // factory function returns the Registry interface by design
//nolint:forcetypeassert
return NewSeededFormats(Default.(*defaultFormats).data, nil)
}
// NewSeededFormats creates a new formats registry.
func NewSeededFormats(seeds []knownFormat, normalizer NameNormalizer) Registry { //nolint:ireturn // factory function returns the Registry interface by design
if normalizer == nil {
normalizer = DefaultNameNormalizer
}
// copy here, don't modify the original
return &defaultFormats{
data: slices.Clone(seeds),
normalizeName: normalizer,
}
}
type knownFormat struct {
Name string
OrigName string
Type reflect.Type
Validator Validator
}
// NameNormalizer is a function that normalizes a format name.
//
// The default duration format corresponds to "duration-human".
type NameNormalizer func(string) string
var dashReplacer = strings.NewReplacer("-", "") //nolint:gochecknoglobals // it's okay to use a global private replacer
// DefaultNameNormalizer removes all dashes.
func DefaultNameNormalizer(name string) string {
if name == "duration" {
name = "duration-human"
}
return dashReplacer.Replace(name)
}
// JSONSchema2020Normalizer is like [NameNormalizer] but adopts "duration-iso8601" as the default "duration" format.
func JSONSchema2020Normalizer(name string) string {
if name == "duration" {
name = "duration-iso8601"
}
return dashReplacer.Replace(name)
}
type defaultFormats struct {
sync.Mutex
data []knownFormat
normalizeName NameNormalizer
}
// MapStructureHookFunc is a decode hook function for mapstructure.
//
// A registered format is decoded by delegating to the destination type's own
// [encoding.TextUnmarshaler], so the mapstructure path is identical to the JSON
// and [defaultFormats.Parse] paths. New formats are picked up automatically as
// soon as they are registered — no per-type wiring here.
func (f *defaultFormats) MapStructureHookFunc() mapstructure.DecodeHookFunc { //nolint:ireturn // returns interface required by mapstructure
return func(from reflect.Type, to reflect.Type, obj any) (any, error) {
if from.Kind() != reflect.String {
return obj, nil
}
data, ok := obj.(string)
if !ok {
return nil, fmt.Errorf("failed to cast %+v to string: %w", obj, ErrFormat)
}
for _, v := range f.data {
if to != v.Type {
continue
}
// reflect.New yields an addressable *T so the (pointer-receiver)
// TextUnmarshaler can hydrate it; we hand back the dereferenced
// value to match the destination field's (value) type.
nw := reflect.New(v.Type).Interface()
dec, isText := nw.(encoding.TextUnmarshaler)
if !isText {
return nil, errors.InvalidTypeName(v.Name)
}
if err := dec.UnmarshalText([]byte(data)); err != nil {
return nil, err
}
return reflect.ValueOf(nw).Elem().Interface(), nil
}
return data, nil
}
}
// Add adds a new format, return true if this was a new item instead of a replacement.
func (f *defaultFormats) Add(name string, strfmt Format, validator Validator) bool {
f.Lock()
defer f.Unlock()
nme := f.normalizeName(name)
tpe := reflect.TypeOf(strfmt)
if tpe.Kind() == reflect.Pointer {
tpe = tpe.Elem()
}
for i := range f.data {
v := &f.data[i]
if v.Name == nme {
v.Type = tpe
v.Validator = validator
return false
}
}
// turns out it's new after all
f.data = append(f.data, knownFormat{Name: nme, OrigName: name, Type: tpe, Validator: validator})
return true
}
// GetType gets the type for the specified name.
func (f *defaultFormats) GetType(name string) (reflect.Type, bool) {
f.Lock()
defer f.Unlock()
nme := f.normalizeName(name)
for _, v := range f.data {
if v.Name == nme {
return v.Type, true
}
}
return nil, false
}
// DelByName removes the format by the specified name, returns true when an item was actually removed.
func (f *defaultFormats) DelByName(name string) bool {
f.Lock()
defer f.Unlock()
nme := f.normalizeName(name)
for i, v := range f.data {
if v.Name == nme {
f.data[i] = knownFormat{} // release
f.data = append(f.data[:i], f.data[i+1:]...)
return true
}
}
return false
}
// DelByFormat removes the specified format, returns true when an item was actually removed.
func (f *defaultFormats) DelByFormat(strfmt Format) bool {
f.Lock()
defer f.Unlock()
tpe := reflect.TypeOf(strfmt)
if tpe.Kind() == reflect.Pointer {
tpe = tpe.Elem()
}
for i, v := range f.data {
if v.Type == tpe {
f.data[i] = knownFormat{} // release
f.data = append(f.data[:i], f.data[i+1:]...)
return true
}
}
return false
}
// ContainsName returns true if this registry contains the specified name.
func (f *defaultFormats) ContainsName(name string) bool {
f.Lock()
defer f.Unlock()
nme := f.normalizeName(name)
for _, v := range f.data {
if v.Name == nme {
return true
}
}
return false
}
// ContainsFormat returns true if this registry contains the specified format.
func (f *defaultFormats) ContainsFormat(strfmt Format) bool {
f.Lock()
defer f.Unlock()
tpe := reflect.TypeOf(strfmt)
if tpe.Kind() == reflect.Pointer {
tpe = tpe.Elem()
}
for _, v := range f.data {
if v.Type == tpe {
return true
}
}
return false
}
// Validates passed data against format.
//
// Note that the format name is automatically normalized, e.g. one may
// use "date-time" to use the "datetime" format validator.
func (f *defaultFormats) Validates(name, data string) bool {
f.Lock()
defer f.Unlock()
nme := f.normalizeName(name)
for _, v := range f.data {
if v.Name == nme {
return v.Validator(data)
}
}
return false
}
// Parse a string into the appropriate format representation type.
//
// E.g. parsing a string a "date" will return a Date type.
func (f *defaultFormats) Parse(name, data string) (any, error) {
f.Lock()
defer f.Unlock()
nme := f.normalizeName(name)
for _, v := range f.data {
if v.Name == nme {
nw := reflect.New(v.Type).Interface()
if dec, ok := nw.(encoding.TextUnmarshaler); ok {
if err := dec.UnmarshalText([]byte(data)); err != nil {
return nil, err
}
return nw, nil
}
return nil, errors.InvalidTypeName(name)
}
}
return nil, errors.InvalidTypeName(name)
}