podman
326 строк · 7.9 Кб
1// Copyright 2015 go-swagger maintainers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package strfmt16
17import (18"encoding"19"fmt"20"reflect"21"strings"22"sync"23"time"24
25"github.com/go-openapi/errors"26"github.com/mitchellh/mapstructure"27)
28
29// Default is the default formats registry
30var Default = NewSeededFormats(nil, nil)31
32// Validator represents a validator for a string format.
33type Validator func(string) bool34
35// Format represents a string format.
36//
37// All implementations of Format provide a string representation and text
38// marshaling/unmarshaling interface to be used by encoders (e.g. encoding/json).
39type Format interface {40String() string41encoding.TextMarshaler42encoding.TextUnmarshaler43}
44
45// Registry is a registry of string formats, with a validation method.
46type Registry interface {47Add(string, Format, Validator) bool48DelByName(string) bool49GetType(string) (reflect.Type, bool)50ContainsName(string) bool51Validates(string, string) bool52Parse(string, string) (interface{}, error)53MapStructureHookFunc() mapstructure.DecodeHookFunc54}
55
56type knownFormat struct {57Name string58OrigName string59Type reflect.Type60Validator Validator
61}
62
63// NameNormalizer is a function that normalizes a format name.
64type NameNormalizer func(string) string65
66// DefaultNameNormalizer removes all dashes
67func DefaultNameNormalizer(name string) string {68return strings.ReplaceAll(name, "-", "")69}
70
71type defaultFormats struct {72sync.Mutex73data []knownFormat74normalizeName NameNormalizer
75}
76
77// NewFormats creates a new formats registry seeded with the values from the default
78func NewFormats() Registry {79//nolint:forcetypeassert80return NewSeededFormats(Default.(*defaultFormats).data, nil)81}
82
83// NewSeededFormats creates a new formats registry
84func NewSeededFormats(seeds []knownFormat, normalizer NameNormalizer) Registry {85if normalizer == nil {86normalizer = DefaultNameNormalizer87}88// copy here, don't modify original89d := append([]knownFormat(nil), seeds...)90return &defaultFormats{91data: d,92normalizeName: normalizer,93}94}
95
96// MapStructureHookFunc is a decode hook function for mapstructure
97func (f *defaultFormats) MapStructureHookFunc() mapstructure.DecodeHookFunc { //nolint:gocyclo,cyclop98return func(from reflect.Type, to reflect.Type, obj interface{}) (interface{}, error) {99if from.Kind() != reflect.String {100return obj, nil101}102data, ok := obj.(string)103if !ok {104return nil, fmt.Errorf("failed to cast %+v to string", obj)105}106
107for _, v := range f.data {108tpe, _ := f.GetType(v.Name)109if to == tpe {110switch v.Name {111case "date":112d, err := time.ParseInLocation(RFC3339FullDate, data, DefaultTimeLocation)113if err != nil {114return nil, err115}116return Date(d), nil117case "datetime":118input := data119if len(input) == 0 {120return nil, fmt.Errorf("empty string is an invalid datetime format")121}122return ParseDateTime(input)123case "duration":124dur, err := ParseDuration(data)125if err != nil {126return nil, err127}128return Duration(dur), nil129case "uri":130return URI(data), nil131case "email":132return Email(data), nil133case "uuid":134return UUID(data), nil135case "uuid3":136return UUID3(data), nil137case "uuid4":138return UUID4(data), nil139case "uuid5":140return UUID5(data), nil141case "hostname":142return Hostname(data), nil143case "ipv4":144return IPv4(data), nil145case "ipv6":146return IPv6(data), nil147case "cidr":148return CIDR(data), nil149case "mac":150return MAC(data), nil151case "isbn":152return ISBN(data), nil153case "isbn10":154return ISBN10(data), nil155case "isbn13":156return ISBN13(data), nil157case "creditcard":158return CreditCard(data), nil159case "ssn":160return SSN(data), nil161case "hexcolor":162return HexColor(data), nil163case "rgbcolor":164return RGBColor(data), nil165case "byte":166return Base64(data), nil167case "password":168return Password(data), nil169case "ulid":170ulid, err := ParseULID(data)171if err != nil {172return nil, err173}174return ulid, nil175default:176return nil, errors.InvalidTypeName(v.Name)177}178}179}180return data, nil181}182}
183
184// Add adds a new format, return true if this was a new item instead of a replacement
185func (f *defaultFormats) Add(name string, strfmt Format, validator Validator) bool {186f.Lock()187defer f.Unlock()188
189nme := f.normalizeName(name)190
191tpe := reflect.TypeOf(strfmt)192if tpe.Kind() == reflect.Ptr {193tpe = tpe.Elem()194}195
196for i := range f.data {197v := &f.data[i]198if v.Name == nme {199v.Type = tpe200v.Validator = validator201return false202}203}204
205// turns out it's new after all206f.data = append(f.data, knownFormat{Name: nme, OrigName: name, Type: tpe, Validator: validator})207return true208}
209
210// GetType gets the type for the specified name
211func (f *defaultFormats) GetType(name string) (reflect.Type, bool) {212f.Lock()213defer f.Unlock()214nme := f.normalizeName(name)215for _, v := range f.data {216if v.Name == nme {217return v.Type, true218}219}220return nil, false221}
222
223// DelByName removes the format by the specified name, returns true when an item was actually removed
224func (f *defaultFormats) DelByName(name string) bool {225f.Lock()226defer f.Unlock()227
228nme := f.normalizeName(name)229
230for i, v := range f.data {231if v.Name == nme {232f.data[i] = knownFormat{} // release233f.data = append(f.data[:i], f.data[i+1:]...)234return true235}236}237return false238}
239
240// DelByFormat removes the specified format, returns true when an item was actually removed
241func (f *defaultFormats) DelByFormat(strfmt Format) bool {242f.Lock()243defer f.Unlock()244
245tpe := reflect.TypeOf(strfmt)246if tpe.Kind() == reflect.Ptr {247tpe = tpe.Elem()248}249
250for i, v := range f.data {251if v.Type == tpe {252f.data[i] = knownFormat{} // release253f.data = append(f.data[:i], f.data[i+1:]...)254return true255}256}257return false258}
259
260// ContainsName returns true if this registry contains the specified name
261func (f *defaultFormats) ContainsName(name string) bool {262f.Lock()263defer f.Unlock()264nme := f.normalizeName(name)265for _, v := range f.data {266if v.Name == nme {267return true268}269}270return false271}
272
273// ContainsFormat returns true if this registry contains the specified format
274func (f *defaultFormats) ContainsFormat(strfmt Format) bool {275f.Lock()276defer f.Unlock()277tpe := reflect.TypeOf(strfmt)278if tpe.Kind() == reflect.Ptr {279tpe = tpe.Elem()280}281
282for _, v := range f.data {283if v.Type == tpe {284return true285}286}287return false288}
289
290// Validates passed data against format.
291//
292// Note that the format name is automatically normalized, e.g. one may
293// use "date-time" to use the "datetime" format validator.
294func (f *defaultFormats) Validates(name, data string) bool {295f.Lock()296defer f.Unlock()297nme := f.normalizeName(name)298for _, v := range f.data {299if v.Name == nme {300return v.Validator(data)301}302}303return false304}
305
306// Parse a string into the appropriate format representation type.
307//
308// E.g. parsing a string a "date" will return a Date type.
309func (f *defaultFormats) Parse(name, data string) (interface{}, error) {310f.Lock()311defer f.Unlock()312nme := f.normalizeName(name)313for _, v := range f.data {314if v.Name == nme {315nw := reflect.New(v.Type).Interface()316if dec, ok := nw.(encoding.TextUnmarshaler); ok {317if err := dec.UnmarshalText([]byte(data)); err != nil {318return nil, err319}320return nw, nil321}322return nil, errors.InvalidTypeName(name)323}324}325return nil, errors.InvalidTypeName(name)326}
327