// 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 ( ) // 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 ( string, interface{}) error { var error := reflect.ValueOf() := reflect.TypeOf() // We need to dereference pointers if .Kind() == reflect.Ptr { = reflect.Indirect() = .Type() } // For some optional args if .Kind() == reflect.Ptr { if .IsNil() { .Set(reflect.New(.Elem())) } = reflect.Indirect() = .Type() } // The resulting type must be settable. reflect will catch issues like // passing the destination by value. if !.CanSet() { return errors.New("destination is not settable") } switch .Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: var int64 , = strconv.ParseInt(, 10, 64) if == nil { if .OverflowInt() { = fmt.Errorf("value '%s' overflows destination of type: %s", , .Kind()) } if == nil { .SetInt() } } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: var uint64 , = strconv.ParseUint(, 10, 64) if == nil { if .OverflowUint() { = fmt.Errorf("value '%s' overflows destination of type: %s", , .Kind()) } .SetUint() } case reflect.String: .SetString() = nil case reflect.Float64, reflect.Float32: var float64 , = strconv.ParseFloat(, 64) if == nil { if .OverflowFloat() { = fmt.Errorf("value '%s' overflows destination of type: %s", , .Kind()) } .SetFloat() } case reflect.Bool: var bool , = strconv.ParseBool() if == nil { .SetBool() } case reflect.Array: if , := .(encoding.TextUnmarshaler); { if := .UnmarshalText([]byte()); != nil { return fmt.Errorf("error unmarshaling '%s' text as %T: %s", , , ) } 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 , := .(Binder); { return .Bind() } if .ConvertibleTo(reflect.TypeOf(time.Time{})) { // Don't fail on empty string. if == "" { return nil } // Time is a special case of a struct that we handle , := time.Parse(time.RFC3339Nano, ) if != nil { , = time.Parse(types.DateFormat, ) if != nil { return fmt.Errorf("error parsing '%s' as RFC3339 or 2006-01-02 time: %s", , ) } } // 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 != reflect.TypeOf(time.Time{}) { := .Addr() := .Convert(reflect.TypeOf(&time.Time{})) = reflect.Indirect() } .Set(reflect.ValueOf()) return nil } if .ConvertibleTo(reflect.TypeOf(types.Date{})) { // Don't fail on empty string. if == "" { return nil } , := time.Parse(types.DateFormat, ) if != nil { return fmt.Errorf("error parsing '%s' as date: %s", , ) } := types.Date{Time: } // We have to do the same dance here to assign, just like with times // above. if != reflect.TypeOf(types.Date{}) { := .Addr() := .Convert(reflect.TypeOf(&types.Date{})) = reflect.Indirect() } .Set(reflect.ValueOf()) 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. = fmt.Errorf("can not bind to destination of type: %s", .Kind()) } if != nil { return fmt.Errorf("error binding string parameter: %w", ) } return nil }