Last active
December 27, 2017 06:48
-
-
Save maxbeutel/1fe5b36f5c3ac90e250d13d93932db18 to your computer and use it in GitHub Desktop.
utility for type conversions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package convert | |
/* | |
Usage, assuming o is a map[string]interface{} | |
errs := []error{} | |
errs = append(errs, c.AsString(c.FromUntypedMap(o, "nr")).Assign(&tmp.OrderId)) | |
errs = append(errs, c.AsString(c.FromUntypedMap(o, "status")).Assign(&tmp.Status)) | |
errs = append(errs, c.AsTime(c.FromUntypedMap(o, "date")).Assign(&tmp.Date)) | |
// ... | |
if err := c.Fuse(errs); err != nil { | |
log.Println("Error at", o["order_nr"]) | |
return err | |
} | |
*/ | |
import ( | |
"errors" | |
"fmt" | |
"reflect" | |
"regexp" | |
"strconv" | |
"strings" | |
"time" | |
) | |
type AnyTypeVal struct { | |
dat interface{} | |
err error | |
} | |
type Int64Val struct { | |
dat int64 | |
err error | |
} | |
type IntVal struct { | |
dat int | |
err error | |
} | |
type StringVal struct { | |
dat string | |
err error | |
} | |
type TimeVal struct { | |
dat time.Time | |
err error | |
} | |
type StringSliceVal struct { | |
dat []string | |
err error | |
} | |
func FromUntypedMap(v map[string]interface{}, key ...string) AnyTypeVal { | |
raw, exists := v[key[0]] | |
if !exists { | |
return AnyTypeVal{nil, fmt.Errorf("Key %s is not in the map.", key[0])} | |
} | |
if len(key[1:]) > 0 { | |
tmp2, ok := raw.(map[string]interface{}) | |
if !ok { | |
return AnyTypeVal{ | |
nil, | |
fmt.Errorf( | |
"Key %s is not a map - can't descend. Type is %s.", | |
key[0], | |
reflect.TypeOf(tmp2), | |
), | |
} | |
} | |
return FromUntypedMap(tmp2, key[1:]...) | |
} | |
return AnyTypeVal{raw, nil} | |
} | |
func AsStringSlice(v AnyTypeVal) StringSliceVal { | |
empty := []string{} | |
if v.err != nil { | |
return StringSliceVal{empty, v.err} | |
} | |
if v.dat == nil { | |
return StringSliceVal{empty, nil} | |
} | |
slice, ok := v.dat.([]interface{}) | |
if !ok { | |
return StringSliceVal{empty, convertErr("[]string", "not a slice", v.dat)} | |
} | |
var converted []string | |
for i, tmp := range slice { | |
switch tmp.(type) { | |
case string: | |
converted = append(converted, tmp.(string)) | |
default: | |
return StringSliceVal{ | |
empty, | |
convertErr("[]string", fmt.Sprintf("no string at index %d", i), tmp), | |
} | |
} | |
} | |
return StringSliceVal{converted, nil} | |
} | |
func (v StringSliceVal) Assign(to *[]string) error { | |
if v.err != nil { | |
return v.err | |
} | |
*to = v.dat | |
return nil | |
} | |
func AsInt64(v AnyTypeVal) Int64Val { | |
if v.err != nil { | |
return Int64Val{0, v.err} | |
} | |
switch v.dat.(type) { | |
case int64: | |
return Int64Val{v.dat.(int64), nil} | |
case nil: | |
return Int64Val{0, nil} | |
default: | |
return Int64Val{0, convertErr("int64", "unknown type", v.dat)} | |
} | |
} | |
func (v Int64Val) AssignBool(to *bool) error { | |
if v.err != nil { | |
return v.err | |
} | |
switch v.dat { | |
case 0: | |
*to = false | |
case 1: | |
*to = true | |
default: | |
return fmt.Errorf("Value can't be converted to bool, should be int64 1 or 0") | |
} | |
return nil | |
} | |
func (v Int64Val) AssignInt(to *int) error { | |
if v.err != nil { | |
return v.err | |
} | |
*to = int(v.dat) | |
return nil | |
} | |
var emptyTime = time.Time{} | |
func AsTime(v AnyTypeVal) TimeVal { | |
if v.err != nil { | |
return TimeVal{emptyTime, v.err} | |
} | |
switch v.dat.(type) { | |
case time.Time: | |
return TimeVal{v.dat.(time.Time), nil} | |
case nil: | |
return TimeVal{emptyTime, nil} | |
default: | |
return TimeVal{emptyTime, convertErr("time.Time", "unknown type", v.dat)} | |
} | |
} | |
func (v TimeVal) Assign(to *time.Time) error { | |
if v.err != nil { | |
return v.err | |
} | |
*to = v.dat | |
return nil | |
} | |
func AsString(v AnyTypeVal) StringVal { | |
if v.err != nil { | |
return StringVal{"", v.err} | |
} | |
switch v.dat.(type) { | |
case string: | |
return StringVal{v.dat.(string), nil} | |
case nil: | |
return StringVal{"", nil} | |
default: | |
return StringVal{"", convertErr("string", "unknown type", v.dat)} | |
} | |
} | |
func AsStringUint8Enc(v AnyTypeVal) StringVal { | |
if v.err != nil { | |
return StringVal{"", v.err} | |
} | |
switch v.dat.(type) { | |
case []uint8: | |
return StringVal{string(v.dat.([]uint8)), nil} | |
case nil: | |
return StringVal{"", nil} | |
default: | |
return StringVal{"", convertErr("string", "unknown type", v.dat)} | |
} | |
} | |
func (v StringVal) Extract(re *regexp.Regexp, errMsg string) StringVal { | |
if v.err != nil { | |
return v | |
} | |
matches := re.FindAllString(v.dat, -1) | |
if len(matches) != 1 { | |
return StringVal{"", errors.New(errMsg)} | |
} | |
return StringVal{matches[0], nil} | |
} | |
func (v StringVal) StripThousandsSep() StringVal { | |
if v.err != nil { | |
return v | |
} | |
return StringVal{strings.Replace(v.dat, ",", "", -1), nil} | |
} | |
func (v StringVal) Trim() StringVal { | |
if v.err != nil { | |
return v | |
} | |
return StringVal{strings.TrimSpace(v.dat), nil} | |
} | |
func (v StringVal) AssignFloat64(to *float64) error { | |
if v.err != nil { | |
return v.err | |
} | |
f, err := strconv.ParseFloat(v.dat, 64) | |
if err != nil { | |
return err | |
} | |
*to = f | |
return nil | |
} | |
func (v StringVal) AssignInt(to *int) error { | |
if v.err != nil { | |
return v.err | |
} | |
i, err := strconv.Atoi(v.dat) | |
if err != nil { | |
return err | |
} | |
*to = i | |
return nil | |
} | |
func (v StringVal) Assign(to *string) error { | |
if v.err != nil { | |
return v.err | |
} | |
*to = v.dat | |
return nil | |
} | |
func Fuse(errs []error) error { | |
for i, err := range errs { | |
if err != nil { | |
return fmt.Errorf("Error at field index %d: %s", i, err.Error()) | |
} | |
} | |
return nil | |
} | |
func convertErr(typ string, why string, dat interface{}) error { | |
return fmt.Errorf( | |
"Failed to convert %s to %s (%s): %+v.", | |
reflect.TypeOf(dat), | |
typ, | |
why, | |
dat, | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment