Documentation
¶
Overview ¶
Package reflect extends the standard reflect package.
Index ¶
- func DoEqual(x, y any) (err error)
- func IsEqual(x, y any) bool
- func IsNil(v any) bool
- func Marshal(obj any) (out []byte, err error)
- func Set(obj reflect.Value, val string) (err error)
- func Tag(field reflect.StructField, tag string) (val string, opts []string, hasTag bool)
- func Unmarshal(obj reflect.Value, val []byte) (ok bool, err error)
- type Equaler
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DoEqual ¶
DoEqual is a naive interfaces comparison for two values.
If the type is a struct and implement Equaler interface it will use the [Equal] method in that struct to compare the values. 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 ¶
IsEqual is a naive interfaces comparison for two values.
If the type is a struct and implement Equaler interface it will use the [Equal] method in that struct to compare the values. 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 ¶
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 any
}{
{}, // Uninitialized any.
{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 any.
}
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 ¶
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 ¶
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 ¶
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
hasTag bool
)
vtype = reflect.TypeOf(t)
for x := range vtype.NumField() {
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 ¶
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 {
// Equal compare the struct receiver with parameter v.
// The v value can be converted to struct type T using (*T).
// If both struct values are equal it should return nil.
Equal(v any) error
}
Equaler is an interface that when implemented by a struct type, it will be used to compare the value in DoEqual or IsEqual.
Example ¶
// SPDX-FileCopyrightText: 2024 M. Shulhan <ms@kilabit.info>
//
// SPDX-License-Identifier: BSD-3-Clause
package main
import (
"fmt"
"log"
)
type ADT struct {
vint int
}
func (rnp *ADT) Equal(v any) (err error) {
var (
logp = `Equal`
got *ADT
ok bool
)
got, ok = v.(*ADT)
if !ok {
return fmt.Errorf(`%s: v type is %T, want %T`, logp, got, v)
}
if rnp.vint != got.vint {
return fmt.Errorf(`%s: vint: %d, want %d`,
logp, got.vint, rnp.vint)
}
return nil
}
func main() {
var (
rp1 = ADT{
vint: 1,
}
rp2 = ADT{
vint: 2,
}
)
var err = DoEqual(&rp1, &rp2)
if err == nil {
log.Fatal(`expecting error, got nil`)
}
var exp = `Equal: vint: want 1, got 2`
var got = err.Error()
if exp != got {
log.Fatalf(`want %q, got %q`, exp, got)
}
}