Skip to content

Instantly share code, notes, and snippets.

@maxbeutel
Last active December 27, 2017 06:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maxbeutel/1fe5b36f5c3ac90e250d13d93932db18 to your computer and use it in GitHub Desktop.
Save maxbeutel/1fe5b36f5c3ac90e250d13d93932db18 to your computer and use it in GitHub Desktop.
utility for type conversions
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