Compare commits

...

4 Commits

Author SHA1 Message Date
07d4085201 util/reflect: fix reflect methods
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-27 12:55:48 +03:00
45f30c0be3 util/reflect: ZeroFieldByPath and SetFieldByPath
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-26 14:12:37 +03:00
bcaea675a7 util/reflect: add method to zero struct field
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-25 14:41:19 +03:00
3087ba1d73 regen
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-23 23:45:53 +03:00
13 changed files with 195 additions and 67 deletions

View File

@@ -17,7 +17,7 @@ syntax = "proto3";
package micro.codec;
option cc_enable_arenas = true;
option go_package = "github.com/unistack-org/micro/v3/codec;codec";
option go_package = "go.unistack.org/micro/v3/codec;codec";
option java_multiple_files = true;
option java_outer_classname = "MicroCodec";
option java_package = "micro.codec";

2
go.mod
View File

@@ -8,7 +8,7 @@ require (
github.com/imdario/mergo v0.3.12
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/silas/dag v0.0.0-20210626123444-3804bac2d6d4
github.com/unistack-org/micro-proto v0.0.9
go.unistack.org/micro-proto/v3 v3.1.0
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
)

10
go.sum
View File

@@ -1,7 +1,5 @@
github.com/ef-ds/deque v1.0.4 h1:iFAZNmveMT9WERAkqLJ+oaABF9AcVQ5AjXem/hroniI=
github.com/ef-ds/deque v1.0.4/go.mod h1:gXDnTC3yqvBcHbq2lcExjtAcVrOnJCbMcZXmuj8Z4tg=
github.com/golang-jwt/jwt/v4 v4.0.0 h1:RAqyYixv1p7uEnocuy8P1nru5wprCh/MH2BIlW5z5/o=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.1.0 h1:XUgk2Ex5veyVFVeLm0xhusUTQybEbexJXrvPNOKkSY0=
github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
@@ -11,14 +9,10 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/silas/dag v0.0.0-20210121180416-41cf55125c34 h1:vBfVmA5mZhsQa2jr1FOL9nfA37N/jnbBmi5XUfviVTI=
github.com/silas/dag v0.0.0-20210121180416-41cf55125c34/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I=
github.com/silas/dag v0.0.0-20210626123444-3804bac2d6d4 h1:fOH64AB0C3ixGf9emky61STvPJL3smxJg+1Zwx1oCdg=
github.com/silas/dag v0.0.0-20210626123444-3804bac2d6d4/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I=
github.com/unistack-org/micro-proto v0.0.9 h1:KrWLS4FUX7UAWNAilQf70uad6ZPf/0EudeddCXllRVc=
github.com/unistack-org/micro-proto v0.0.9/go.mod h1:Cckwmzd89gvS7ThxzZp9kQR/EOdksFQcsTAtDDyKwrg=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
go.unistack.org/micro-proto/v3 v3.1.0 h1:q39FwjFiRZn+Ux/tt+d3bJTmDtsQQWa+3SLYVo1vLfA=
go.unistack.org/micro-proto/v3 v3.1.0/go.mod h1:DpRhYCBXlmSJ/AAXTmntvlh7kQkYU6eFvlmYAx4BQS8=
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b h1:eB48h3HiRycXNy8E0Gf5e0hv7YT6Kt14L/D73G1fuwo=
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@@ -1,12 +1,12 @@
package meter
//go:generate sh -c "protoc -I./handler -I../ -I$(go list -f '{{ .Dir }}' -m github.com/unistack-org/micro-proto) --go-micro_out='components=micro|http|server',standalone=false,debug=true,paths=source_relative:./handler handler/handler.proto"
//go:generate sh -c "protoc -I./handler -I../ -I$(go list -f '{{ .Dir }}' -m go.unistack.org/micro-proto/v3) --go-micro_out='components=micro|http|server',standalone=false,debug=true,paths=source_relative:./handler handler/handler.proto"
import (
// import required packages
_ "github.com/unistack-org/micro-proto/api"
_ "go.unistack.org/micro-proto/v3/api"
// import required packages
_ "github.com/unistack-org/micro-proto/openapiv2"
_ "go.unistack.org/micro-proto/v3/openapiv3"
)

View File

@@ -1,24 +1,20 @@
syntax = "proto3";
package micro.meter.handler;
option go_package = "github.com/unistack-org/micro/v3/meter/handler;handler";
option go_package = "go.unistack.org/micro/v3/meter/handler;handler";
import "api/annotations.proto";
import "openapiv2/annotations.proto";
import "openapiv3/annotations.proto";
import "codec/frame.proto";
service Meter {
rpc Metrics(micro.codec.Frame) returns (micro.codec.Frame) {
option (micro.openapiv2.openapiv2_operation) = {
option (micro.openapiv3.openapiv3_operation) = {
operation_id: "Metrics";
responses: {
response_code: {
name: "default";
value: {
json_reference: {
description: "Error response";
_ref: "micro.codec.Frame";
};
default: {
reference: {
_ref: "micro.codec.Frame";
};
};
};

View File

@@ -1,12 +1,11 @@
// Code generated by protoc-gen-go-micro. DO NOT EDIT.
// protoc-gen-go-micro version: v3.4.2
// protoc-gen-go-micro version: v3.5.2
// source: handler.proto
package handler
import (
context "context"
api "go.unistack.org/micro/v3/api"
codec "go.unistack.org/micro/v3/codec"
)

View File

@@ -1,12 +1,11 @@
// Code generated by protoc-gen-go-micro. DO NOT EDIT.
// protoc-gen-go-micro version: v3.4.2
// protoc-gen-go-micro version: v3.5.2
// source: handler.proto
package handler
import (
context "context"
api "go.unistack.org/micro/v3/api"
codec "go.unistack.org/micro/v3/codec"
server "go.unistack.org/micro/v3/server"

View File

@@ -1,12 +1,12 @@
package server
//go:generate sh -c "protoc -I./health -I../ -I$(go list -f '{{ .Dir }}' -m github.com/unistack-org/micro-proto) --go-micro_out='components=micro|http|server',standalone=false,debug=true,paths=source_relative:./health health/health.proto"
//go:generate sh -c "protoc -I./health -I../ -I$(go list -f '{{ .Dir }}' -m go.unistack.org/micro-proto/v3) --go-micro_out='components=micro|http|server',standalone=false,debug=true,paths=source_relative:./health health/health.proto"
import (
// import required packages
_ "github.com/unistack-org/micro-proto/api"
_ "go.unistack.org/micro-proto/v3/api"
// import required packages
_ "github.com/unistack-org/micro-proto/openapiv2"
_ "go.unistack.org/micro-proto/v3/openapiv3"
)

View File

@@ -1,24 +1,20 @@
syntax = "proto3";
package micro.server.health;
option go_package = "github.com/unistack-org/micro/v3/server/health;health";
option go_package = "go.unistack.org/micro/v3/server/health;health";
import "api/annotations.proto";
import "openapiv2/annotations.proto";
import "openapiv3/annotations.proto";
import "codec/frame.proto";
service Health {
rpc Live(micro.codec.Frame) returns (micro.codec.Frame) {
option (micro.openapiv2.openapiv2_operation) = {
option (micro.openapiv3.openapiv3_operation) = {
operation_id: "Live";
responses: {
response_code: {
name: "default";
value: {
json_reference: {
description: "Error response";
_ref: "micro.codec.Frame";
};
default: {
reference: {
_ref: "micro.codec.Frame";
};
};
};
@@ -26,16 +22,12 @@ service Health {
option (micro.api.http) = { get: "/live"; };
};
rpc Ready(micro.codec.Frame) returns (micro.codec.Frame) {
option (micro.openapiv2.openapiv2_operation) = {
option (micro.openapiv3.openapiv3_operation) = {
operation_id: "Ready";
responses: {
response_code: {
name: "default";
value: {
json_reference: {
description: "Error response";
_ref: "micro.codec.Frame";
};
default: {
reference: {
_ref: "micro.codec.Frame";
};
};
};
@@ -43,16 +35,12 @@ service Health {
option (micro.api.http) = { get: "/ready"; };
};
rpc Version(micro.codec.Frame) returns (micro.codec.Frame) {
option (micro.openapiv2.openapiv2_operation) = {
option (micro.openapiv3.openapiv3_operation) = {
operation_id: "Version";
responses: {
response_code: {
name: "default";
value: {
json_reference: {
description: "Error response";
_ref: "micro.codec.Frame";
};
default: {
reference: {
_ref: "micro.codec.Frame";
};
};
};

View File

@@ -1,12 +1,11 @@
// Code generated by protoc-gen-go-micro. DO NOT EDIT.
// protoc-gen-go-micro version: v3.4.2
// protoc-gen-go-micro version: v3.5.2
// source: health.proto
package health
import (
context "context"
api "go.unistack.org/micro/v3/api"
codec "go.unistack.org/micro/v3/codec"
)

View File

@@ -1,12 +1,11 @@
// Code generated by protoc-gen-go-micro. DO NOT EDIT.
// protoc-gen-go-micro version: v3.4.2
// protoc-gen-go-micro version: v3.5.2
// source: health.proto
package health
import (
context "context"
api "go.unistack.org/micro/v3/api"
codec "go.unistack.org/micro/v3/codec"
server "go.unistack.org/micro/v3/server"

View File

@@ -69,6 +69,89 @@ func StructFieldByTag(src interface{}, tkey string, tval string) (interface{}, e
return nil, ErrNotFound
}
// ZeroFieldByPath clean struct field by its path
func ZeroFieldByPath(src interface{}, path string) error {
var err error
val := reflect.ValueOf(src)
for _, p := range strings.Split(path, ".") {
val, err = structValueByName(val, p)
if err != nil {
return err
}
}
if IsEmpty(val) {
return nil
}
if !val.CanSet() {
return ErrInvalidStruct
}
val.Set(reflect.Zero(val.Type()))
return nil
}
// SetFieldByPath set struct field by its path
func SetFieldByPath(src interface{}, dst interface{}, path string) error {
var err error
val := reflect.ValueOf(src)
for _, p := range strings.Split(path, ".") {
val, err = structValueByName(val, p)
if err != nil {
return err
}
}
if !val.CanSet() {
return ErrInvalidStruct
}
val.Set(reflect.ValueOf(dst))
return nil
}
// structValueByName get struct field by its name
func structValueByName(sv reflect.Value, tkey string) (reflect.Value, error) {
if sv.Kind() == reflect.Ptr {
sv = sv.Elem()
}
if sv.Kind() != reflect.Struct {
return reflect.Zero(reflect.TypeOf(sv)), ErrInvalidStruct
}
typ := sv.Type()
for idx := 0; idx < typ.NumField(); idx++ {
fld := typ.Field(idx)
val := sv.Field(idx)
if len(fld.PkgPath) != 0 {
continue
}
if fld.Name == tkey || strings.EqualFold(strings.ToLower(fld.Name), strings.ToLower(tkey)) {
return val, nil
}
switch val.Kind() {
case reflect.Ptr:
if val = val.Elem(); val.Kind() == reflect.Struct {
if iface, err := structValueByName(val, tkey); err == nil {
return iface, nil
}
}
case reflect.Struct:
if iface, err := structValueByName(val, tkey); err == nil {
return iface, nil
}
}
}
return reflect.Zero(reflect.TypeOf(sv)), ErrNotFound
}
// StructFieldByPath get struct field by its path
func StructFieldByPath(src interface{}, path string) (interface{}, error) {
var err error
@@ -98,10 +181,7 @@ func StructFieldByName(src interface{}, tkey string) (interface{}, error) {
if len(fld.PkgPath) != 0 {
continue
}
if fld.Name == tkey {
if val.Kind() != reflect.Ptr && val.CanAddr() {
val = val.Addr()
}
if fld.Name == tkey || strings.EqualFold(strings.ToLower(fld.Name), strings.ToLower(tkey)) {
return val.Interface(), nil
}

View File

@@ -8,6 +8,80 @@ import (
rutil "go.unistack.org/micro/v3/util/reflect"
)
func TestSetFieldByPath(t *testing.T) {
type NestedStr struct {
BBB string `json:"bbb"`
CCC int `json:"ccc"`
}
type Str1 struct {
Name []string `json:"name" codec:"flatten"`
XXX string `json:"xxx"`
Nested NestedStr `json:"nested"`
}
type Str2 struct {
XXX string `json:"xxx"`
Nested *NestedStr `json:"nested"`
Name []string `json:"name" codec:"flatten"`
}
var err error
val1 := &Str1{Name: []string{"first", "second"}, XXX: "ttt", Nested: NestedStr{BBB: "ddd", CCC: 9}}
val2 := &Str2{Name: []string{"first", "second"}, XXX: "ttt", Nested: &NestedStr{BBB: "ddd", CCC: 9}}
err = rutil.SetFieldByPath(val1, "xxx", "Nested.BBB")
if err != nil {
t.Fatal(err)
}
if val1.Nested.BBB != "xxx" {
t.Fatalf("SetFieldByPath not works: %#+v", val1)
}
err = rutil.SetFieldByPath(val2, "xxx", "Nested.BBB")
if err != nil {
t.Fatal(err)
}
if val2.Nested.BBB != "xxx" {
t.Fatalf("SetFieldByPath not works: %#+v", val1)
}
}
func TestZeroFieldByPath(t *testing.T) {
type NestedStr struct {
BBB string `json:"bbb"`
CCC int `json:"ccc"`
}
type Str1 struct {
Name []string `json:"name" codec:"flatten"`
XXX string `json:"xxx"`
Nested NestedStr `json:"nested"`
}
type Str2 struct {
XXX string `json:"xxx"`
Nested *NestedStr `json:"nested"`
Name []string `json:"name" codec:"flatten"`
}
var err error
val1 := &Str1{Name: []string{"first", "second"}, XXX: "ttt", Nested: NestedStr{BBB: "ddd", CCC: 9}}
err = rutil.ZeroFieldByPath(val1, "Nested.BBB")
if err != nil {
t.Fatal(err)
}
err = rutil.ZeroFieldByPath(val1, "Nested")
if err != nil {
t.Fatal(err)
}
if val1.Nested.BBB == "ddd" {
t.Fatalf("zero field not works: %v", val1)
}
val2 := &Str2{Name: []string{"first", "second"}, XXX: "ttt", Nested: &NestedStr{BBB: "ddd", CCC: 9}}
err = rutil.ZeroFieldByPath(val2, "Nested")
if err != nil {
t.Fatal(err)
}
if val2.Nested != nil {
t.Fatalf("zero field not works: %v", val2)
}
}
func TestStructFieldsMap(t *testing.T) {
type NestedStr struct {
BBB string
@@ -108,9 +182,9 @@ func TestStructByName(t *testing.T) {
t.Fatal(err)
}
if v, ok := iface.(*[]string); !ok {
t.Fatalf("not *[]string %v", iface)
} else if len(*v) != 2 {
if v, ok := iface.([]string); !ok {
t.Fatalf("not []string %v", iface)
} else if len(v) != 2 {
t.Fatalf("invalid number %v", iface)
}
}