123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 |
- // Copyright 2019 DeepMap, Inc.
- //
- // 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 runtime
- import (
- "encoding"
- "errors"
- "fmt"
- "reflect"
- "strconv"
- "time"
- "github.com/deepmap/oapi-codegen/pkg/types"
- )
- // BindStringToObject takes a string, and attempts to assign it to the destination
- // interface via whatever type conversion is necessary. We have to do this
- // via reflection instead of a much simpler type switch so that we can handle
- // type aliases. This function was the easy way out, the better way, since we
- // know the destination type each place that we use this, is to generate code
- // to read each specific type.
- func BindStringToObject(src string, dst interface{}) error {
- var err error
- v := reflect.ValueOf(dst)
- t := reflect.TypeOf(dst)
- // We need to dereference pointers
- if t.Kind() == reflect.Ptr {
- v = reflect.Indirect(v)
- t = v.Type()
- }
- // For some optioinal args
- if t.Kind() == reflect.Ptr {
- if v.IsNil() {
- v.Set(reflect.New(t.Elem()))
- }
- v = reflect.Indirect(v)
- t = v.Type()
- }
- // The resulting type must be settable. reflect will catch issues like
- // passing the destination by value.
- if !v.CanSet() {
- return errors.New("destination is not settable")
- }
- switch t.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- var val int64
- val, err = strconv.ParseInt(src, 10, 64)
- if err == nil {
- if v.OverflowInt(val) {
- err = fmt.Errorf("value '%s' overflows destination of type: %s", src, t.Kind())
- }
- if err == nil {
- v.SetInt(val)
- }
- }
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- var val uint64
- val, err = strconv.ParseUint(src, 10, 64)
- if err == nil {
- if v.OverflowUint(val) {
- err = fmt.Errorf("value '%s' overflows destination of type: %s", src, t.Kind())
- }
- v.SetUint(val)
- }
- case reflect.String:
- v.SetString(src)
- err = nil
- case reflect.Float64, reflect.Float32:
- var val float64
- val, err = strconv.ParseFloat(src, 64)
- if err == nil {
- if v.OverflowFloat(val) {
- err = fmt.Errorf("value '%s' overflows destination of type: %s", src, t.Kind())
- }
- v.SetFloat(val)
- }
- case reflect.Bool:
- var val bool
- val, err = strconv.ParseBool(src)
- if err == nil {
- v.SetBool(val)
- }
- case reflect.Array:
- if tu, ok := dst.(encoding.TextUnmarshaler); ok {
- if err := tu.UnmarshalText([]byte(src)); err != nil {
- return fmt.Errorf("error unmarshalling '%s' text as %T: %s", src, dst, err)
- }
- return nil
- }
- fallthrough
- case reflect.Struct:
- // if this is not of type Time or of type Date look to see if this is of type Binder.
- if dstType, ok := dst.(Binder); ok {
- return dstType.Bind(src)
- }
- if t.ConvertibleTo(reflect.TypeOf(time.Time{})) {
- // Don't fail on empty string.
- if src == "" {
- return nil
- }
- // Time is a special case of a struct that we handle
- parsedTime, err := time.Parse(time.RFC3339Nano, src)
- if err != nil {
- parsedTime, err = time.Parse(types.DateFormat, src)
- if err != nil {
- return fmt.Errorf("error parsing '%s' as RFC3339 or 2006-01-02 time: %s", src, err)
- }
- }
- // So, assigning this gets a little fun. We have a value to the
- // dereference destination. We can't do a conversion to
- // time.Time because the result isn't assignable, so we need to
- // convert pointers.
- if t != reflect.TypeOf(time.Time{}) {
- vPtr := v.Addr()
- vtPtr := vPtr.Convert(reflect.TypeOf(&time.Time{}))
- v = reflect.Indirect(vtPtr)
- }
- v.Set(reflect.ValueOf(parsedTime))
- return nil
- }
- if t.ConvertibleTo(reflect.TypeOf(types.Date{})) {
- // Don't fail on empty string.
- if src == "" {
- return nil
- }
- parsedTime, err := time.Parse(types.DateFormat, src)
- if err != nil {
- return fmt.Errorf("error parsing '%s' as date: %s", src, err)
- }
- parsedDate := types.Date{Time: parsedTime}
- // We have to do the same dance here to assign, just like with times
- // above.
- if t != reflect.TypeOf(types.Date{}) {
- vPtr := v.Addr()
- vtPtr := vPtr.Convert(reflect.TypeOf(&types.Date{}))
- v = reflect.Indirect(vtPtr)
- }
- v.Set(reflect.ValueOf(parsedDate))
- return nil
- }
- // We fall through to the error case below if we haven't handled the
- // destination type above.
- fallthrough
- default:
- // We've got a bunch of types unimplemented, don't fail silently.
- err = fmt.Errorf("can not bind to destination of type: %s", t.Kind())
- }
- if err != nil {
- return fmt.Errorf("error binding string parameter: %s", err)
- }
- return nil
- }
|