Last active
April 4, 2018 09:03
-
-
Save henvic/17cc12f5e35766c9633f5dc0dee74791 to your computer and use it in GitHub Desktop.
indirect (similar to json.Unmarshal(data []byte, v interface{}) - Public Domain, MIT License
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 indirect | |
import ( | |
"errors" | |
"fmt" | |
"reflect" | |
) | |
// Inspired by the unmarshal functions on | |
// https://golang.org/src/encoding/json/decode.go | |
// https://github.com/BurntSushi/toml/ | |
// https://github.com/go-yaml/yaml/blob/v2/decode.go | |
// Copy a data value to a given interface | |
func Copy(data, v interface{}) (err error) { | |
defer func() { | |
if r := recover(); r != nil && err == nil { | |
switch r.(type) { | |
case string: | |
err = errors.New(r.(string)) | |
case error: | |
err = r.(error) | |
default: | |
err = fmt.Errorf("recovered panic: %+v", r) | |
} | |
} | |
}() | |
rv := reflect.ValueOf(v) | |
if rv.Kind() != reflect.Ptr { | |
return fmt.Errorf("can't decode type %v", reflect.TypeOf(v)) | |
} | |
if rv.IsNil() { | |
return errors.New("can't decode nil") | |
} | |
tryAgain: | |
if rv.Kind() == reflect.Ptr { | |
if rv.IsNil() { | |
rv.Set(reflect.New(rv.Type().Elem())) | |
} | |
rv = rv.Elem() | |
goto tryAgain | |
} | |
rv.Set(reflect.ValueOf(data)) | |
return nil | |
} |
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 indirect | |
import ( | |
"reflect" | |
"testing" | |
) | |
func TestString(t *testing.T) { | |
var s = "This is a string" | |
var converted string | |
if err := Copy(s, &converted); err != nil { | |
t.Errorf("Expected no error, got %v instead", err) | |
} | |
if converted != "This is a string" { | |
t.Errorf("Strings doesn't match.") | |
} | |
} | |
func TestStringFromInterface(t *testing.T) { | |
var s interface{} = "This is a string" | |
var converted string | |
if err := indirectCopy(s, &converted); err != nil { | |
t.Errorf("Expected no error, got %v instead", err) | |
} | |
if converted != "This is a string" { | |
t.Errorf("Strings doesn't match.") | |
} | |
} | |
func indirectCopy(data, v interface{}) error { | |
err := Copy(data, v) | |
return err | |
} | |
func TestCustomType(t *testing.T) { | |
type bark string | |
var s bark = "This is a string" | |
var converted bark | |
if err := Copy(s, &converted); err != nil { | |
t.Errorf("Expected no error, got %v instead", err) | |
} | |
if converted != "This is a string" { | |
t.Errorf("Strings doesn't match.") | |
} | |
} | |
func TestStruct(t *testing.T) { | |
type dog struct { | |
Name string | |
Age int | |
Friends []string | |
} | |
var s = dog{"Rex", 2, []string{"Max", "Bella", "Lucy", "Molly"}} | |
var converted dog | |
if err := Copy(s, &converted); err != nil { | |
t.Errorf("Expected no error, got %v instead", err) | |
} | |
if !reflect.DeepEqual(s, converted) { | |
t.Errorf("Expected %v, got %v instead", s, converted) | |
} | |
} | |
func TestSliceStruct(t *testing.T) { | |
type dog struct { | |
Name string | |
Age int | |
Friends []string | |
} | |
var s = []dog{ | |
dog{"Rex", 2, []string{"Max", "Bella", "Lucy", "Molly"}}, | |
} | |
var converted []dog | |
if err := Copy(s, &converted); err != nil { | |
t.Errorf("Expected no error, got %v instead", err) | |
} | |
if !reflect.DeepEqual(s, converted) { | |
t.Errorf("Expected %v, got %v instead", s, converted) | |
} | |
} | |
func TestSlicePointerToStruct(t *testing.T) { | |
type dog struct { | |
Name string | |
Age int | |
Friends []string | |
} | |
var s = []*dog{ | |
nil, | |
&dog{"Rex", 2, []string{"Max", "Bella", "Lucy", "Molly"}}, | |
} | |
var converted []dog | |
var want = "reflect.Set: value of type []*indirect.dog is not assignable to type []indirect.dog" | |
if err := Copy(s, &converted); err == nil || err.Error() != want { | |
t.Errorf("Expected no error, got %v instead", err) | |
} | |
} | |
func TestNil(t *testing.T) { | |
var s *string | |
var converted *string | |
var want = "can't decode nil" | |
if err := Copy(s, converted); err == nil || err.Error() != want { | |
t.Errorf("Expected %v error, got %v instead", want, err) | |
} | |
} | |
func TestNilInPointer(t *testing.T) { | |
type x *string | |
type xt *x | |
var inner = "oi" | |
var bs = &inner | |
var s = &bs | |
var converted *xt | |
var want = "can't decode nil" | |
if err := Copy(s, converted); err == nil || err.Error() != want { | |
t.Errorf("Expected %v error, got %v instead", want, err) | |
} | |
} | |
func TestInvalidPointer(t *testing.T) { | |
var want = "can't decode type string" | |
if err := Copy("x", "y"); err == nil || err.Error() != want { | |
t.Errorf("Expected %v error, got %v instead", want, err) | |
} | |
} | |
func TestInvalidPointerString(t *testing.T) { | |
var want = "can't decode type <nil>" | |
var s = "my string" | |
var converted error | |
if err := Copy(&s, converted); err == nil || err.Error() != want { | |
t.Errorf("Expected %v error, got %v instead", want, err) | |
} | |
} | |
func TestInvalidPointerFunctionReceiver(t *testing.T) { | |
var want = "can't decode type func()" | |
if err := Copy("x", func() {}); err == nil || err.Error() != want { | |
t.Errorf("Expected %v error, got %v instead", want, err) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment