Skip to content

Instantly share code, notes, and snippets.

@henvic
Last active April 4, 2018 09:03
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 henvic/17cc12f5e35766c9633f5dc0dee74791 to your computer and use it in GitHub Desktop.
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
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
}
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