diff options
Diffstat (limited to 'vendor/github.com/go-openapi/strfmt/format.go')
-rw-r--r-- | vendor/github.com/go-openapi/strfmt/format.go | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/vendor/github.com/go-openapi/strfmt/format.go b/vendor/github.com/go-openapi/strfmt/format.go new file mode 100644 index 000000000..ad3b3c355 --- /dev/null +++ b/vendor/github.com/go-openapi/strfmt/format.go @@ -0,0 +1,326 @@ +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package strfmt + +import ( + "encoding" + "fmt" + "reflect" + "strings" + "sync" + "time" + + "github.com/go-openapi/errors" + "github.com/mitchellh/mapstructure" +) + +// Default is the default formats registry +var Default = NewSeededFormats(nil, nil) + +// Validator represents a validator for a string format. +type Validator func(string) bool + +// Format represents a string format. +// +// All implementations of Format provide a string representation and text +// marshaling/unmarshaling interface to be used by encoders (e.g. encoding/json). +type Format interface { + String() string + encoding.TextMarshaler + encoding.TextUnmarshaler +} + +// Registry is a registry of string formats, with a validation method. +type Registry interface { + Add(string, Format, Validator) bool + DelByName(string) bool + GetType(string) (reflect.Type, bool) + ContainsName(string) bool + Validates(string, string) bool + Parse(string, string) (interface{}, error) + MapStructureHookFunc() mapstructure.DecodeHookFunc +} + +type knownFormat struct { + Name string + OrigName string + Type reflect.Type + Validator Validator +} + +// NameNormalizer is a function that normalizes a format name. +type NameNormalizer func(string) string + +// DefaultNameNormalizer removes all dashes +func DefaultNameNormalizer(name string) string { + return strings.ReplaceAll(name, "-", "") +} + +type defaultFormats struct { + sync.Mutex + data []knownFormat + normalizeName NameNormalizer +} + +// NewFormats creates a new formats registry seeded with the values from the default +func NewFormats() Registry { + //nolint:forcetypeassert + return NewSeededFormats(Default.(*defaultFormats).data, nil) +} + +// NewSeededFormats creates a new formats registry +func NewSeededFormats(seeds []knownFormat, normalizer NameNormalizer) Registry { + if normalizer == nil { + normalizer = DefaultNameNormalizer + } + // copy here, don't modify original + d := append([]knownFormat(nil), seeds...) + return &defaultFormats{ + data: d, + normalizeName: normalizer, + } +} + +// MapStructureHookFunc is a decode hook function for mapstructure +func (f *defaultFormats) MapStructureHookFunc() mapstructure.DecodeHookFunc { //nolint:gocyclo,cyclop + return func(from reflect.Type, to reflect.Type, obj interface{}) (interface{}, 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", obj) + } + + for _, v := range f.data { + tpe, _ := f.GetType(v.Name) + if to == tpe { + switch v.Name { + case "date": + d, err := time.ParseInLocation(RFC3339FullDate, data, DefaultTimeLocation) + if err != nil { + return nil, err + } + return Date(d), nil + case "datetime": + input := data + if len(input) == 0 { + return nil, fmt.Errorf("empty string is an invalid datetime format") + } + return ParseDateTime(input) + case "duration": + dur, err := ParseDuration(data) + if err != nil { + return nil, err + } + return Duration(dur), nil + case "uri": + return URI(data), nil + case "email": + return Email(data), nil + case "uuid": + return UUID(data), nil + case "uuid3": + return UUID3(data), nil + case "uuid4": + return UUID4(data), nil + case "uuid5": + return UUID5(data), nil + case "hostname": + return Hostname(data), nil + case "ipv4": + return IPv4(data), nil + case "ipv6": + return IPv6(data), nil + case "cidr": + return CIDR(data), nil + case "mac": + return MAC(data), nil + case "isbn": + return ISBN(data), nil + case "isbn10": + return ISBN10(data), nil + case "isbn13": + return ISBN13(data), nil + case "creditcard": + return CreditCard(data), nil + case "ssn": + return SSN(data), nil + case "hexcolor": + return HexColor(data), nil + case "rgbcolor": + return RGBColor(data), nil + case "byte": + return Base64(data), nil + case "password": + return Password(data), nil + case "ulid": + ulid, err := ParseULID(data) + if err != nil { + return nil, err + } + return ulid, nil + default: + return nil, errors.InvalidTypeName(v.Name) + } + } + } + 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.Ptr { + 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.Ptr { + 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.Ptr { + 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) (interface{}, 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) +} |