reflect

package
v0.54.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 4, 2024 License: BSD-3-Clause Imports: 5 Imported by: 0

Documentation

Overview

Package reflect extends the standard reflect package.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func DoEqual

func DoEqual(x, y interface{}) (err error)

DoEqual is a naive interfaces comparison that check and use Equaler interface and return an error if its not match.

A struct's field tagged with `noequal:""` will be skipped from being processed.

Example (Struct)
package main

import (
	"fmt"

	libreflect "git.sr.ht/~shulhan/pakakeh.go/lib/reflect"
)

func main() {
	type T struct {
		vstring  string
		vnoequal string `noequal:""` // This field will not checked for equality.
	}

	var (
		t1 = &T{
			vstring:  `a string`,
			vnoequal: `skip`,
		}
		t2 = &T{
			vstring:  `a string`,
			vnoequal: `skipped too`,
		}
	)

	fmt.Println(libreflect.DoEqual(t1, t2))
}
Output:

<nil>

func IsEqual

func IsEqual(x, y interface{}) bool

IsEqual is a naive interfaces comparison that check and use Equaler interface.

A struct's field tagged with `noequal:""` will be skipped from being processed.

Example (Struct)
package main

import (
	"fmt"

	libreflect "git.sr.ht/~shulhan/pakakeh.go/lib/reflect"
)

func main() {
	type T struct {
		vstring  string
		vnoequal string `noequal:""` // This field will not checked for equality.
	}

	var (
		t1 = &T{
			vstring:  `a string`,
			vnoequal: `skip`,
		}
		t2 = &T{
			vstring:  `a string`,
			vnoequal: `skipped too`,
		}
	)

	fmt.Println(libreflect.IsEqual(t1, t2))
}
Output:

true

func IsNil

func IsNil(v interface{}) bool

IsNil will return true if v's type is chan, func, interface, map, pointer, or slice and its value is `nil`; otherwise it will return false.

Example
package main

import (
	"errors"
	"fmt"
	"net/http"

	libreflect "git.sr.ht/~shulhan/pakakeh.go/lib/reflect"
)

type F func()

type T struct{}

func (t *T) J() bool {
	return true
}

func main() {
	var (
		aBoolean   bool
		aChannel   chan int
		aFunction  F
		aMap       map[int]int
		aPtr       *T
		aSlice     []int
		anInt      int
		emptyError error
		fs         http.FileSystem
	)

	cases := []struct {
		v interface{}
	}{
		{}, // Uninitialized interface{}.
		{v: aBoolean},
		{v: aChannel},          // Uninitialized channel.
		{v: aFunction},         // Empty func type.
		{v: aMap},              // Uninitialized map.
		{v: make(map[int]int)}, // Initialized map.
		{v: aPtr},              // Uninitialized pointer to struct.
		{v: &T{}},              // Initialized pointer to struct.
		{v: aSlice},            // Uninitialized slice.
		{v: make([]int, 0)},    // Initialized slice.
		{v: anInt},
		{v: emptyError},
		{v: errors.New("e")}, // Initialized error.
		{v: fs},              // Uninitialized interface type to interface{}.
	}

	for _, c := range cases {
		fmt.Printf("%19T: v == nil is %5t, IsNil() is %5t\n", c.v, c.v == nil, libreflect.IsNil(c.v))
	}

}
Output:

<nil>: v == nil is  true, IsNil() is  true
               bool: v == nil is false, IsNil() is false
           chan int: v == nil is false, IsNil() is  true
     reflect_test.F: v == nil is false, IsNil() is  true
        map[int]int: v == nil is false, IsNil() is  true
        map[int]int: v == nil is false, IsNil() is false
    *reflect_test.T: v == nil is false, IsNil() is  true
    *reflect_test.T: v == nil is false, IsNil() is false
              []int: v == nil is false, IsNil() is  true
              []int: v == nil is false, IsNil() is false
                int: v == nil is false, IsNil() is false
              <nil>: v == nil is  true, IsNil() is  true
*errors.errorString: v == nil is false, IsNil() is false
              <nil>: v == nil is  true, IsNil() is  true

func Marshal

func Marshal(obj interface{}) (out []byte, err error)

Marshal marshal the obj value to []byte by calling one of the method: MarshalBinary, MarshalJSON, or MarshalText; in respective order.

If obj implement one of the method with valid signature, it will return the marshaled bytes.

If the method signature invalid it will return an error.

If obj is nil or none of the method exist it will return nil without an error.

Example
package main

import (
	"errors"
	"fmt"
	"math/big"
	"net/url"

	libreflect "git.sr.ht/~shulhan/pakakeh.go/lib/reflect"
)

type InvalidMarshalText struct{}

func (imt *InvalidMarshalText) MarshalText() (string, error) {
	return "", nil
}

type ErrorMarshalJSON struct{}

func (emj *ErrorMarshalJSON) MarshalJSON() ([]byte, error) {
	return nil, errors.New(`ErrorMarshalJSON: test`)
}

func main() {
	var (
		vint    = 1
		vURL, _ = url.Parse("https://example.org")
		bigRat  = big.NewRat(100, 2)
		bigInt  = big.NewInt(50)
		imt     = &InvalidMarshalText{}
		emj     = &ErrorMarshalJSON{}

		out []byte
		err error
	)

	out, err = libreflect.Marshal(vint)
	fmt.Println(out, err)

	out, err = libreflect.Marshal(&vint)
	fmt.Println(out, err)

	out, err = libreflect.Marshal(vURL)
	fmt.Println(string(out), err)

	out, err = libreflect.Marshal(bigRat)
	fmt.Println(string(out), err)

	out, err = libreflect.Marshal(bigInt)
	fmt.Println(string(out), err)

	out, err = libreflect.Marshal(imt)
	fmt.Println(string(out), err)

	out, err = libreflect.Marshal(emj)
	fmt.Println(string(out), err)

}
Output:

[] <nil>
[] <nil>
https://example.org <nil>
50 <nil>
50 <nil>
 Marshal: expecting first return as []byte got string
 Marshal: ErrorMarshalJSON: test

func Set

func Set(obj reflect.Value, val string) (err error)

Set the obj value by converting the string val to the obj type.

If the obj type is an interface or struct, its value will be set by calling Unmarshal function.

It will return an error if,

  • obj is not setable, variable is passed without pointer or pointer not initialized.
  • val is overflow
  • obj Kind is Invalid, Array, Chan, Func, Map, or UnsafePointer.
Example (Bool)
package main

import (
	"fmt"
	"reflect"

	libreflect "git.sr.ht/~shulhan/pakakeh.go/lib/reflect"
)

func main() {
	type Bool bool

	var (
		err    error
		vbool  bool
		mybool Bool
	)

	err = libreflect.Set(reflect.ValueOf(&vbool), "YES")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println("YES:", vbool)
	}

	err = libreflect.Set(reflect.ValueOf(&vbool), "TRUE")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println("TRUE:", vbool)
	}

	err = libreflect.Set(reflect.ValueOf(&vbool), "False")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println("False:", vbool)
	}

	err = libreflect.Set(reflect.ValueOf(&vbool), "1")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println("1:", vbool)
	}

	err = libreflect.Set(reflect.ValueOf(&mybool), "true")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println("true:", mybool)
	}

}
Output:

YES: true
TRUE: true
False: false
1: true
true: true
Example (Float)
package main

import (
	"fmt"
	"reflect"

	libreflect "git.sr.ht/~shulhan/pakakeh.go/lib/reflect"
)

func main() {
	type myFloat float32

	var (
		vf32    float32
		myfloat myFloat
		err     error
	)

	err = libreflect.Set(reflect.ValueOf(&vf32), "1.223")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println(vf32)
	}

	err = libreflect.Set(reflect.ValueOf(&myfloat), "999.999")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println(myfloat)
	}

}
Output:

1.223
999.999
Example (Int)
package main

import (
	"fmt"
	"reflect"

	libreflect "git.sr.ht/~shulhan/pakakeh.go/lib/reflect"
)

func main() {
	type myInt int

	var (
		vint   int
		vint8  int8
		vint16 int16
		vmyint myInt
		err    error
	)

	err = libreflect.Set(reflect.ValueOf(&vint), "")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println(vint)
	}

	err = libreflect.Set(reflect.ValueOf(&vint), "1")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println(vint)
	}

	err = libreflect.Set(reflect.ValueOf(&vint8), "-128")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println(vint8)
	}

	// Value of int16 is overflow.
	err = libreflect.Set(reflect.ValueOf(&vint16), "32768")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println(vint16)
	}

	err = libreflect.Set(reflect.ValueOf(&vmyint), "32768")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println(vmyint)
	}

}
Output:

0
1
-128
error: Set: int16 value is overflow: 32768
32768
Example (SliceByte)
package main

import (
	"fmt"
	"reflect"

	libreflect "git.sr.ht/~shulhan/pakakeh.go/lib/reflect"
)

func main() {
	type myBytes []byte

	var (
		vbytes   []byte
		vmyBytes myBytes
		err      error
	)

	err = libreflect.Set(reflect.ValueOf(vbytes), "Show me")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println(string(vbytes))
	}

	err = libreflect.Set(reflect.ValueOf(&vbytes), "a hero")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println(string(vbytes))
	}

	err = libreflect.Set(reflect.ValueOf(&vbytes), "")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println(string(vbytes))
	}

	err = libreflect.Set(reflect.ValueOf(&vmyBytes), "and I will write you a tragedy")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println(string(vmyBytes))
	}

}
Output:

error: Set: object []uint8 is not setable
a hero

and I will write you a tragedy
Example (SliceString)
package main

import (
	"fmt"
	"reflect"

	libreflect "git.sr.ht/~shulhan/pakakeh.go/lib/reflect"
)

func main() {
	var (
		vstring []string
		err     error
	)

	err = libreflect.Set(reflect.ValueOf(vstring), "Show me")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println(vstring)
	}

	err = libreflect.Set(reflect.ValueOf(&vstring), "a hero")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println(vstring)
	}

	err = libreflect.Set(reflect.ValueOf(&vstring), "and I will write you a tragedy")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println(vstring)
	}

}
Output:

error: Set: object []string is not setable
[a hero]
[a hero and I will write you a tragedy]
Example (Unmarshal)
package main

import (
	"fmt"
	"math/big"
	"net/url"
	"reflect"

	libreflect "git.sr.ht/~shulhan/pakakeh.go/lib/reflect"
)

func main() {
	var (
		rat    = big.NewRat(0, 1)
		myURL  = &url.URL{}
		bigInt = big.NewInt(1)

		err error
	)

	// This Set will call UnmarshalText on big.Rat.
	err = libreflect.Set(reflect.ValueOf(rat), "1.234")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println(rat.FloatString(4))
	}

	err = libreflect.Set(reflect.ValueOf(rat), "")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println(rat.FloatString(4))
	}

	// This Set will call UnmarshalBinary on url.URL.
	err = libreflect.Set(reflect.ValueOf(myURL), "https://kilabit.info")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println(myURL)
	}

	// This Set will call UnmarshalJSON.
	err = libreflect.Set(reflect.ValueOf(bigInt), "123_456")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println(bigInt)
	}

	err = libreflect.Set(reflect.ValueOf(bigInt), "")
	if err != nil {
		fmt.Println("error:", err)
	} else {
		fmt.Println(bigInt)
	}

}
Output:

1.2340
0.0000
https://kilabit.info
123456
0

func Tag

func Tag(field reflect.StructField, tag string) (val string, opts []string, hasTag bool)

Tag simplify lookup on struct's field tag.

Given a StructField and the name of tag, return the tag's value and options inside the tag. The options is any string after tag's value, separated by comma. For example, given the following field definition

F `tag:"name,opt1, opt2"`

It will return (name, [opt1 opt2], true).

If the field is exported but does not have tag, it will return the field name (as is without converting to lower case) in val with hasTag is false: (Name, nil, false).

If the field is unexported or tag is "-" it will return empty val with hasTag is false ("", nil, false).

Example
package main

import (
	"fmt"
	"reflect"

	libreflect "git.sr.ht/~shulhan/pakakeh.go/lib/reflect"
)

func main() {
	type T struct {
		F1 int `atag:" f1 , opt1 , opt2  ,"`
		F2 int `atag:", opt1"`
		F3 int
		F4 int `atag:" - ,opt1"`
	}

	var (
		t      T
		vtype  reflect.Type
		field  reflect.StructField
		val    string
		opts   []string
		x      int
		hasTag bool
	)

	vtype = reflect.TypeOf(t)

	for x = 0; x < vtype.NumField(); x++ {
		field = vtype.Field(x)
		val, opts, hasTag = libreflect.Tag(field, "atag")
		fmt.Println(val, opts, hasTag)
	}
}
Output:

f1 [opt1 opt2 ] true
F2 [opt1] false
F3 [] false
 [] false

func Unmarshal

func Unmarshal(obj reflect.Value, val []byte) (ok bool, err error)

Unmarshal set the obj value by calling one of the method: UnmarshalBinary, UnmarshalJSON, or UnmarshalText; in respective order.

Just like reflect, the obj value must be pointer to initialized variable (&T) or pointer-to-pointer to uninitialized variable (**T).

If obj implement one of the method, it will return (true, nil) if there is no error.

If none of the method exist on obj, it will return (false, nil).

Example (UnmarshalBinary)
package main

import (
	"fmt"
	"log"
	"net/url"
	"reflect"

	libreflect "git.sr.ht/~shulhan/pakakeh.go/lib/reflect"
)

func main() {
	var (
		val = []byte("https://kilabit.info")

		err error
		ok  bool
	)

	// Passing variable will not work...
	var varB url.URL
	ok, err = libreflect.Unmarshal(reflect.ValueOf(varB), val)
	if err != nil {
		return
	}
	fmt.Println(varB.String(), ok)

	// Pass it like these.
	ok, err = libreflect.Unmarshal(reflect.ValueOf(&varB), val)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(varB.String(), ok)

	// Passing un-initialized pointer also not working...
	var varPtrB *url.URL
	ok, err = libreflect.Unmarshal(reflect.ValueOf(varPtrB), val)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(varPtrB, ok)

	// Pass it as **T.
	ok, err = libreflect.Unmarshal(reflect.ValueOf(&varPtrB), val)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(varPtrB, ok)

	var ptrB = &url.URL{}
	ok, err = libreflect.Unmarshal(reflect.ValueOf(&ptrB), val)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(ptrB, ok)

}
Output:

  false
https://kilabit.info true
<nil> false
https://kilabit.info true
https://kilabit.info true
Example (UnmarshalJSON)
package main

import (
	"fmt"
	"math/big"
	"reflect"

	libreflect "git.sr.ht/~shulhan/pakakeh.go/lib/reflect"
)

func main() {
	var (
		vals = [][]byte{
			[]byte("123.456"),
			[]byte("123_456"),
			[]byte("123456"),
		}
		bigInt = big.NewInt(1)

		val []byte
		err error
	)

	for _, val = range vals {
		_, err = libreflect.Unmarshal(reflect.ValueOf(bigInt), val)
		if err != nil {
			fmt.Println(err)
		} else {
			fmt.Println(bigInt)
		}
	}
}
Output:

Unmarshal: math/big: cannot unmarshal "123.456" into a *big.Int
123456
123456
Example (UnmarshalText)
package main

import (
	"fmt"
	"math/big"
	"reflect"

	libreflect "git.sr.ht/~shulhan/pakakeh.go/lib/reflect"
)

func main() {
	var (
		vals = [][]byte{
			[]byte(""),
			[]byte("123.456"),
			[]byte("123_456"),
			[]byte("123456"),
		}
		r = big.NewRat(0, 1)

		val []byte
		err error
	)

	for _, val = range vals {
		_, err = libreflect.Unmarshal(reflect.ValueOf(r), val)
		if err != nil {
			fmt.Println(err)
		} else {
			fmt.Println(r)
		}
	}
}
Output:

0/1
15432/125
123456/1
123456/1

Types

type Equaler

type Equaler interface {
	IsEqual(v interface{}) bool
}

Equaler is an interface that when implemented by a type, it will be used to compare the value in Assert.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL