summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2022-06-01 22:11:10 +0700
committerShulhan <ms@kilabit.info>2022-06-01 22:11:10 +0700
commitf28f3cbe2b10e538fd79ac9aa6cd271b5a17acbd (patch)
treecbbe4f8db0e23f96d180c419261274f0dcf2db6e
parent907dde170237198446a1dcbdee4964dac13fede0 (diff)
downloadpakakeh.go-f28f3cbe2b10e538fd79ac9aa6cd271b5a17acbd.tar.xz
lib/reflect: add function Tag to 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 un-exported it will return empty val with hasTag is false ("", nil, false).
-rw-r--r--lib/reflect/reflect.go51
-rw-r--r--lib/reflect/reflect_example_test.go33
2 files changed, 84 insertions, 0 deletions
diff --git a/lib/reflect/reflect.go b/lib/reflect/reflect.go
index b8dba25d..4b22d3d9 100644
--- a/lib/reflect/reflect.go
+++ b/lib/reflect/reflect.go
@@ -8,6 +8,7 @@ package reflect
import (
"fmt"
"reflect"
+ "strings"
"unsafe"
)
@@ -59,6 +60,56 @@ func IsNil(v interface{}) bool {
return v == nil
}
+// 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 it will return empty val with hasTag is false
+// ("", nil, false).
+func Tag(field reflect.StructField, tag string) (val string, opts []string, hasTag bool) {
+ if len(field.PkgPath) != 0 {
+ // field is unexported.
+ return "", nil, false
+ }
+
+ var (
+ x int
+ )
+
+ val, hasTag = field.Tag.Lookup(tag)
+ if !hasTag {
+ // Tag not defined, so we use field name as key.
+ val = field.Name
+ } else {
+ opts = strings.Split(val, ",")
+ for x, val = range opts {
+ opts[x] = strings.TrimSpace(val)
+ }
+
+ val = opts[0]
+ opts = opts[1:]
+ if len(val) == 0 {
+ // Tag is empty, use field name as key and
+ // mark it as not OK.
+ val = field.Name
+ hasTag = false
+ }
+ }
+
+ return val, opts, hasTag
+}
+
// doEqual compare two kind of objects and return nils if both are equal.
//
// If its not equal, it will return the interface{} value of v1 and v2 and
diff --git a/lib/reflect/reflect_example_test.go b/lib/reflect/reflect_example_test.go
index 58e6d1cc..f0919a6f 100644
--- a/lib/reflect/reflect_example_test.go
+++ b/lib/reflect/reflect_example_test.go
@@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"net/http"
+ "reflect"
)
type F func()
@@ -70,3 +71,35 @@ func ExampleIsNil() {
// *errors.errorString: v == nil is false, IsNil() is false
// <nil>: v == nil is true, IsNil() is true
}
+
+func ExampleTag() {
+ type T struct {
+ F1 int `atag:" f1 , opt1 , opt2 ,"`
+ F2 int `atag:", opt1"`
+ F3 int
+ f4 int
+ }
+
+ 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 = Tag(field, "atag")
+ fmt.Printf("%q %v %v\n", val, opts, hasTag)
+ }
+ //Output:
+ //"f1" [opt1 opt2 ] true
+ //"F2" [opt1] false
+ //"F3" [] false
+ //"" [] false
+}