...

Source file src/github.com/go-openapi/strfmt/format.go

Documentation: github.com/go-openapi/strfmt

     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  
    15  package strfmt
    16  
    17  import (
    18  	"encoding"
    19  	stderrors "errors"
    20  	"fmt"
    21  	"reflect"
    22  	"strings"
    23  	"sync"
    24  	"time"
    25  
    26  	"github.com/go-openapi/errors"
    27  	"github.com/mitchellh/mapstructure"
    28  )
    29  
    30  // Default is the default formats registry
    31  var Default = NewSeededFormats(nil, nil)
    32  
    33  // Validator represents a validator for a string format.
    34  type Validator func(string) bool
    35  
    36  // Format represents a string format.
    37  //
    38  // All implementations of Format provide a string representation and text
    39  // marshaling/unmarshaling interface to be used by encoders (e.g. encoding/json).
    40  type Format interface {
    41  	String() string
    42  	encoding.TextMarshaler
    43  	encoding.TextUnmarshaler
    44  }
    45  
    46  // Registry is a registry of string formats, with a validation method.
    47  type Registry interface {
    48  	Add(string, Format, Validator) bool
    49  	DelByName(string) bool
    50  	GetType(string) (reflect.Type, bool)
    51  	ContainsName(string) bool
    52  	Validates(string, string) bool
    53  	Parse(string, string) (interface{}, error)
    54  	MapStructureHookFunc() mapstructure.DecodeHookFunc
    55  }
    56  
    57  type knownFormat struct {
    58  	Name      string
    59  	OrigName  string
    60  	Type      reflect.Type
    61  	Validator Validator
    62  }
    63  
    64  // NameNormalizer is a function that normalizes a format name.
    65  type NameNormalizer func(string) string
    66  
    67  // DefaultNameNormalizer removes all dashes
    68  func DefaultNameNormalizer(name string) string {
    69  	return strings.ReplaceAll(name, "-", "")
    70  }
    71  
    72  type defaultFormats struct {
    73  	sync.Mutex
    74  	data          []knownFormat
    75  	normalizeName NameNormalizer
    76  }
    77  
    78  // NewFormats creates a new formats registry seeded with the values from the default
    79  func NewFormats() Registry {
    80  	//nolint:forcetypeassert
    81  	return NewSeededFormats(Default.(*defaultFormats).data, nil)
    82  }
    83  
    84  // NewSeededFormats creates a new formats registry
    85  func NewSeededFormats(seeds []knownFormat, normalizer NameNormalizer) Registry {
    86  	if normalizer == nil {
    87  		normalizer = DefaultNameNormalizer
    88  	}
    89  	// copy here, don't modify original
    90  	d := append([]knownFormat(nil), seeds...)
    91  	return &defaultFormats{
    92  		data:          d,
    93  		normalizeName: normalizer,
    94  	}
    95  }
    96  
    97  // MapStructureHookFunc is a decode hook function for mapstructure
    98  func (f *defaultFormats) MapStructureHookFunc() mapstructure.DecodeHookFunc {
    99  	return func(from reflect.Type, to reflect.Type, obj interface{}) (interface{}, error) {
   100  		if from.Kind() != reflect.String {
   101  			return obj, nil
   102  		}
   103  		data, ok := obj.(string)
   104  		if !ok {
   105  			return nil, fmt.Errorf("failed to cast %+v to string", obj)
   106  		}
   107  
   108  		for _, v := range f.data {
   109  			tpe, _ := f.GetType(v.Name)
   110  			if to == tpe {
   111  				switch v.Name {
   112  				case "date":
   113  					d, err := time.ParseInLocation(RFC3339FullDate, data, DefaultTimeLocation)
   114  					if err != nil {
   115  						return nil, err
   116  					}
   117  					return Date(d), nil
   118  				case "datetime":
   119  					input := data
   120  					if len(input) == 0 {
   121  						return nil, stderrors.New("empty string is an invalid datetime format")
   122  					}
   123  					return ParseDateTime(input)
   124  				case "duration":
   125  					dur, err := ParseDuration(data)
   126  					if err != nil {
   127  						return nil, err
   128  					}
   129  					return Duration(dur), nil
   130  				case "uri":
   131  					return URI(data), nil
   132  				case "email":
   133  					return Email(data), nil
   134  				case "uuid":
   135  					return UUID(data), nil
   136  				case "uuid3":
   137  					return UUID3(data), nil
   138  				case "uuid4":
   139  					return UUID4(data), nil
   140  				case "uuid5":
   141  					return UUID5(data), nil
   142  				case "hostname":
   143  					return Hostname(data), nil
   144  				case "ipv4":
   145  					return IPv4(data), nil
   146  				case "ipv6":
   147  					return IPv6(data), nil
   148  				case "cidr":
   149  					return CIDR(data), nil
   150  				case "mac":
   151  					return MAC(data), nil
   152  				case "isbn":
   153  					return ISBN(data), nil
   154  				case "isbn10":
   155  					return ISBN10(data), nil
   156  				case "isbn13":
   157  					return ISBN13(data), nil
   158  				case "creditcard":
   159  					return CreditCard(data), nil
   160  				case "ssn":
   161  					return SSN(data), nil
   162  				case "hexcolor":
   163  					return HexColor(data), nil
   164  				case "rgbcolor":
   165  					return RGBColor(data), nil
   166  				case "byte":
   167  					return Base64(data), nil
   168  				case "password":
   169  					return Password(data), nil
   170  				case "ulid":
   171  					ulid, err := ParseULID(data)
   172  					if err != nil {
   173  						return nil, err
   174  					}
   175  					return ulid, nil
   176  				default:
   177  					return nil, errors.InvalidTypeName(v.Name)
   178  				}
   179  			}
   180  		}
   181  		return data, nil
   182  	}
   183  }
   184  
   185  // Add adds a new format, return true if this was a new item instead of a replacement
   186  func (f *defaultFormats) Add(name string, strfmt Format, validator Validator) bool {
   187  	f.Lock()
   188  	defer f.Unlock()
   189  
   190  	nme := f.normalizeName(name)
   191  
   192  	tpe := reflect.TypeOf(strfmt)
   193  	if tpe.Kind() == reflect.Ptr {
   194  		tpe = tpe.Elem()
   195  	}
   196  
   197  	for i := range f.data {
   198  		v := &f.data[i]
   199  		if v.Name == nme {
   200  			v.Type = tpe
   201  			v.Validator = validator
   202  			return false
   203  		}
   204  	}
   205  
   206  	// turns out it's new after all
   207  	f.data = append(f.data, knownFormat{Name: nme, OrigName: name, Type: tpe, Validator: validator})
   208  	return true
   209  }
   210  
   211  // GetType gets the type for the specified name
   212  func (f *defaultFormats) GetType(name string) (reflect.Type, bool) {
   213  	f.Lock()
   214  	defer f.Unlock()
   215  	nme := f.normalizeName(name)
   216  	for _, v := range f.data {
   217  		if v.Name == nme {
   218  			return v.Type, true
   219  		}
   220  	}
   221  	return nil, false
   222  }
   223  
   224  // DelByName removes the format by the specified name, returns true when an item was actually removed
   225  func (f *defaultFormats) DelByName(name string) bool {
   226  	f.Lock()
   227  	defer f.Unlock()
   228  
   229  	nme := f.normalizeName(name)
   230  
   231  	for i, v := range f.data {
   232  		if v.Name == nme {
   233  			f.data[i] = knownFormat{} // release
   234  			f.data = append(f.data[:i], f.data[i+1:]...)
   235  			return true
   236  		}
   237  	}
   238  	return false
   239  }
   240  
   241  // DelByFormat removes the specified format, returns true when an item was actually removed
   242  func (f *defaultFormats) DelByFormat(strfmt Format) bool {
   243  	f.Lock()
   244  	defer f.Unlock()
   245  
   246  	tpe := reflect.TypeOf(strfmt)
   247  	if tpe.Kind() == reflect.Ptr {
   248  		tpe = tpe.Elem()
   249  	}
   250  
   251  	for i, v := range f.data {
   252  		if v.Type == tpe {
   253  			f.data[i] = knownFormat{} // release
   254  			f.data = append(f.data[:i], f.data[i+1:]...)
   255  			return true
   256  		}
   257  	}
   258  	return false
   259  }
   260  
   261  // ContainsName returns true if this registry contains the specified name
   262  func (f *defaultFormats) ContainsName(name string) bool {
   263  	f.Lock()
   264  	defer f.Unlock()
   265  	nme := f.normalizeName(name)
   266  	for _, v := range f.data {
   267  		if v.Name == nme {
   268  			return true
   269  		}
   270  	}
   271  	return false
   272  }
   273  
   274  // ContainsFormat returns true if this registry contains the specified format
   275  func (f *defaultFormats) ContainsFormat(strfmt Format) bool {
   276  	f.Lock()
   277  	defer f.Unlock()
   278  	tpe := reflect.TypeOf(strfmt)
   279  	if tpe.Kind() == reflect.Ptr {
   280  		tpe = tpe.Elem()
   281  	}
   282  
   283  	for _, v := range f.data {
   284  		if v.Type == tpe {
   285  			return true
   286  		}
   287  	}
   288  	return false
   289  }
   290  
   291  // Validates passed data against format.
   292  //
   293  // Note that the format name is automatically normalized, e.g. one may
   294  // use "date-time" to use the "datetime" format validator.
   295  func (f *defaultFormats) Validates(name, data string) bool {
   296  	f.Lock()
   297  	defer f.Unlock()
   298  	nme := f.normalizeName(name)
   299  	for _, v := range f.data {
   300  		if v.Name == nme {
   301  			return v.Validator(data)
   302  		}
   303  	}
   304  	return false
   305  }
   306  
   307  // Parse a string into the appropriate format representation type.
   308  //
   309  // E.g. parsing a string a "date" will return a Date type.
   310  func (f *defaultFormats) Parse(name, data string) (interface{}, error) {
   311  	f.Lock()
   312  	defer f.Unlock()
   313  	nme := f.normalizeName(name)
   314  	for _, v := range f.data {
   315  		if v.Name == nme {
   316  			nw := reflect.New(v.Type).Interface()
   317  			if dec, ok := nw.(encoding.TextUnmarshaler); ok {
   318  				if err := dec.UnmarshalText([]byte(data)); err != nil {
   319  					return nil, err
   320  				}
   321  				return nw, nil
   322  			}
   323  			return nil, errors.InvalidTypeName(name)
   324  		}
   325  	}
   326  	return nil, errors.InvalidTypeName(name)
   327  }
   328  

View as plain text