Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
3ba8cb7f9e | |||
b07806b9a1 | |||
0f583218d4 | |||
f4d0237785 | |||
0f343dad0b | |||
7c29afba0b | |||
8159b9d233 | |||
45cdac5c29 | |||
98db0dc8bc |
@@ -5,9 +5,11 @@ import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/imdario/mergo"
|
||||
rutil "go.unistack.org/micro/v3/util/reflect"
|
||||
mtime "go.unistack.org/micro/v3/util/time"
|
||||
)
|
||||
|
||||
type defaultConfig struct {
|
||||
@@ -75,6 +77,7 @@ func fillValue(value reflect.Value, val string) error {
|
||||
if !rutil.IsEmpty(value) {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value.Kind() {
|
||||
case reflect.Map:
|
||||
t := value.Type()
|
||||
@@ -151,11 +154,26 @@ func fillValue(value reflect.Value, val string) error {
|
||||
}
|
||||
value.Set(reflect.ValueOf(int32(v)))
|
||||
case reflect.Int64:
|
||||
v, err := strconv.ParseInt(val, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
switch {
|
||||
case value.Type().String() == "time.Duration" && value.Type().PkgPath() == "time":
|
||||
v, err := time.ParseDuration(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
value.Set(reflect.ValueOf(v))
|
||||
case value.Type().String() == "time.Duration" && value.Type().PkgPath() == "go.unistack.org/micro/v3/util/time":
|
||||
v, err := mtime.ParseDuration(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
value.SetInt(int64(v))
|
||||
default:
|
||||
v, err := strconv.ParseInt(val, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
value.Set(reflect.ValueOf(v))
|
||||
}
|
||||
value.Set(reflect.ValueOf(v))
|
||||
case reflect.Uint:
|
||||
v, err := strconv.ParseUint(val, 10, 0)
|
||||
if err != nil {
|
||||
|
@@ -4,15 +4,19 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"go.unistack.org/micro/v3/config"
|
||||
mtime "go.unistack.org/micro/v3/util/time"
|
||||
)
|
||||
|
||||
type cfg struct {
|
||||
StringValue string `default:"string_value"`
|
||||
IgnoreValue string `json:"-"`
|
||||
StructValue *cfgStructValue
|
||||
IntValue int `default:"99"`
|
||||
StringValue string `default:"string_value"`
|
||||
IgnoreValue string `json:"-"`
|
||||
StructValue *cfgStructValue
|
||||
IntValue int `default:"99"`
|
||||
DurationValue time.Duration `default:"10s"`
|
||||
MDurationValue mtime.Duration `default:"10s"`
|
||||
}
|
||||
|
||||
type cfgStructValue struct {
|
||||
|
@@ -46,6 +46,11 @@ var (
|
||||
closeMapBytes = []byte("}")
|
||||
)
|
||||
|
||||
type protoMessage interface {
|
||||
Reset()
|
||||
ProtoMessage()
|
||||
}
|
||||
|
||||
type Wrapper struct {
|
||||
val interface{}
|
||||
s fmt.State
|
||||
@@ -53,7 +58,7 @@ type Wrapper struct {
|
||||
opts *Options
|
||||
depth int
|
||||
ignoreNextType bool
|
||||
takeAll map[int]bool
|
||||
takeMap map[int]bool
|
||||
protoWrapperType bool
|
||||
sqlWrapperType bool
|
||||
}
|
||||
@@ -111,7 +116,7 @@ func Tagged(b bool) Option {
|
||||
|
||||
func Unwrap(val interface{}, opts ...Option) *Wrapper {
|
||||
options := NewOptions(opts...)
|
||||
return &Wrapper{val: val, opts: &options, pointers: make(map[uintptr]int), takeAll: make(map[int]bool)}
|
||||
return &Wrapper{val: val, opts: &options, pointers: make(map[uintptr]int), takeMap: make(map[int]bool)}
|
||||
}
|
||||
|
||||
func (w *Wrapper) unpackValue(v reflect.Value) reflect.Value {
|
||||
@@ -237,9 +242,6 @@ func (w *Wrapper) format(v reflect.Value) {
|
||||
_, _ = w.s.Write(buf)
|
||||
return
|
||||
}
|
||||
if w.opts.Tagged {
|
||||
w.checkTakeAll(v, 1)
|
||||
}
|
||||
|
||||
// Handle invalid reflect values immediately.
|
||||
kind := v.Kind()
|
||||
@@ -256,6 +258,10 @@ func (w *Wrapper) format(v reflect.Value) {
|
||||
w.protoWrapperType = true
|
||||
} else if strings.HasPrefix(reflect.Indirect(v).Type().String(), "sql.Null") {
|
||||
w.sqlWrapperType = true
|
||||
} else if v.CanInterface() {
|
||||
if _, ok := v.Interface().(protoMessage); ok {
|
||||
w.protoWrapperType = true
|
||||
}
|
||||
}
|
||||
}
|
||||
w.formatPtr(v)
|
||||
@@ -378,6 +384,12 @@ func (w *Wrapper) format(v reflect.Value) {
|
||||
prevSkip := false
|
||||
|
||||
for i := 0; i < numFields; i++ {
|
||||
switch vt.Field(i).Type.PkgPath() {
|
||||
case "google.golang.org/protobuf/internal/impl", "google.golang.org/protobuf/internal/pragma":
|
||||
w.protoWrapperType = true
|
||||
prevSkip = true
|
||||
continue
|
||||
}
|
||||
if w.protoWrapperType && !vt.Field(i).IsExported() {
|
||||
prevSkip = true
|
||||
continue
|
||||
@@ -385,6 +397,9 @@ func (w *Wrapper) format(v reflect.Value) {
|
||||
prevSkip = true
|
||||
continue
|
||||
}
|
||||
if _, ok := vt.Field(i).Tag.Lookup("protobuf"); ok && !w.protoWrapperType {
|
||||
w.protoWrapperType = true
|
||||
}
|
||||
sv, ok := vt.Field(i).Tag.Lookup("logger")
|
||||
switch {
|
||||
case ok:
|
||||
@@ -395,11 +410,16 @@ func (w *Wrapper) format(v reflect.Value) {
|
||||
case "take":
|
||||
break
|
||||
}
|
||||
case w.takeAll[w.depth]:
|
||||
break
|
||||
case !ok && w.opts.Tagged:
|
||||
prevSkip = true
|
||||
continue
|
||||
// skip top level untagged
|
||||
if w.depth == 1 {
|
||||
prevSkip = true
|
||||
continue
|
||||
}
|
||||
if tv, ok := w.takeMap[w.depth]; ok && !tv {
|
||||
prevSkip = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if prevSkip {
|
||||
@@ -416,9 +436,7 @@ func (w *Wrapper) format(v reflect.Value) {
|
||||
_, _ = w.s.Write([]byte(vt.Name))
|
||||
_, _ = w.s.Write(colonBytes)
|
||||
}
|
||||
unpackValue := w.unpackValue(v.Field(i))
|
||||
w.checkTakeAll(unpackValue, w.depth)
|
||||
w.format(unpackValue)
|
||||
w.format(w.unpackValue(v.Field(i)))
|
||||
numWritten++
|
||||
}
|
||||
w.depth--
|
||||
@@ -461,6 +479,10 @@ func (w *Wrapper) Format(s fmt.State, verb rune) {
|
||||
return
|
||||
}
|
||||
|
||||
if w.opts.Tagged {
|
||||
w.buildTakeMap(reflect.ValueOf(w.val), 1)
|
||||
}
|
||||
|
||||
w.format(reflect.ValueOf(w.val))
|
||||
}
|
||||
|
||||
@@ -615,24 +637,28 @@ func (w *Wrapper) constructOrigFormat(verb rune) string {
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (w *Wrapper) checkTakeAll(v reflect.Value, depth int) {
|
||||
if _, ok := w.takeAll[depth]; ok {
|
||||
return
|
||||
}
|
||||
func (w *Wrapper) buildTakeMap(v reflect.Value, depth int) {
|
||||
if !v.IsValid() || v.IsZero() {
|
||||
return
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Slice, reflect.Array:
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
w.buildTakeMap(v.Index(i), depth+1)
|
||||
}
|
||||
w.takeMap[depth] = true
|
||||
return
|
||||
case reflect.Struct:
|
||||
break
|
||||
case reflect.Ptr:
|
||||
v = v.Elem()
|
||||
if v.Kind() != reflect.Struct {
|
||||
w.takeAll[depth] = true
|
||||
w.takeMap[depth] = true
|
||||
return
|
||||
}
|
||||
default:
|
||||
w.takeAll[depth] = true
|
||||
w.takeMap[depth] = true
|
||||
return
|
||||
}
|
||||
|
||||
@@ -641,8 +667,15 @@ func (w *Wrapper) checkTakeAll(v reflect.Value, depth int) {
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
sv, ok := vt.Field(i).Tag.Lookup("logger")
|
||||
if ok && sv == "take" {
|
||||
w.takeAll[depth] = false
|
||||
w.takeMap[depth] = false
|
||||
}
|
||||
w.checkTakeAll(v.Field(i), depth+1)
|
||||
if v.Kind() == reflect.Struct ||
|
||||
(v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct) {
|
||||
w.buildTakeMap(v.Field(i), depth+1)
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := w.takeMap[depth]; !ok {
|
||||
w.takeMap[depth] = true
|
||||
}
|
||||
}
|
||||
|
82
util/time/duration.go
Normal file
82
util/time/duration.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package time
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Duration int64
|
||||
|
||||
func ParseDuration(s string) (time.Duration, error) {
|
||||
if s == "" {
|
||||
return 0, fmt.Errorf(`time: invalid duration "` + s + `"`)
|
||||
}
|
||||
|
||||
//var sb strings.Builder
|
||||
/*
|
||||
for i, r := range s {
|
||||
switch r {
|
||||
case 'd':
|
||||
n, err := strconv.Atoi(s[idx:i])
|
||||
if err != nil {
|
||||
return 0, errors.New("time: invalid duration " + s)
|
||||
}
|
||||
s[idx:i] = fmt.Sprintf("%d", n*24)
|
||||
default:
|
||||
sb.WriteRune(r)
|
||||
}
|
||||
}
|
||||
*/
|
||||
var td time.Duration
|
||||
var err error
|
||||
switch s[len(s)-1] {
|
||||
case 's', 'm', 'h':
|
||||
td, err = time.ParseDuration(s)
|
||||
case 'd':
|
||||
if td, err = time.ParseDuration(s[:len(s)-1] + "h"); err == nil {
|
||||
td *= 24
|
||||
}
|
||||
case 'y':
|
||||
if td, err = time.ParseDuration(s[:len(s)-1] + "h"); err == nil {
|
||||
year := time.Date(time.Now().Year(), time.December, 31, 0, 0, 0, 0, time.Local)
|
||||
days := year.YearDay()
|
||||
td *= 24 * time.Duration(days)
|
||||
}
|
||||
}
|
||||
|
||||
return td, err
|
||||
}
|
||||
|
||||
func (d Duration) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(time.Duration(d).String())
|
||||
}
|
||||
|
||||
func (d *Duration) UnmarshalJSON(b []byte) error {
|
||||
var v interface{}
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
switch value := v.(type) {
|
||||
case float64:
|
||||
*d = Duration(time.Duration(value))
|
||||
return nil
|
||||
case string:
|
||||
dv, err := time.ParseDuration(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*d = Duration(dv)
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("invalid duration")
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func (d Duration) MarshalYAML() (interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d Duration) UnmarshalYAML(fn func(interface{}) error) error
|
||||
*/
|
54
util/time/duration_test.go
Normal file
54
util/time/duration_test.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package time
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestMarshalJSON(t *testing.T) {
|
||||
d := Duration(10000000)
|
||||
buf, err := json.Marshal(d)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(buf, []byte(`"10ms"`)) {
|
||||
t.Fatalf("invalid duration: %s != %s", buf, `"10ms"`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalJSON(t *testing.T) {
|
||||
type str struct {
|
||||
TTL Duration `json:"ttl"`
|
||||
}
|
||||
v := &str{}
|
||||
|
||||
err := json.Unmarshal([]byte(`{"ttl":"10ms"}`), v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if v.TTL != 10000000 {
|
||||
t.Fatalf("invalid duration %v != 10000000", v.TTL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseDuration(t *testing.T) {
|
||||
var td time.Duration
|
||||
var err error
|
||||
t.Skip()
|
||||
td, err = ParseDuration("14d4h")
|
||||
if err != nil {
|
||||
t.Fatalf("ParseDuration error: %v", err)
|
||||
}
|
||||
if td.String() != "336h0m0s" {
|
||||
t.Fatalf("ParseDuration 14d != 336h0m0s : %s", td.String())
|
||||
}
|
||||
|
||||
td, err = ParseDuration("1y")
|
||||
if err != nil {
|
||||
t.Fatalf("ParseDuration error: %v", err)
|
||||
}
|
||||
if td.String() != "8760h0m0s" {
|
||||
t.Fatalf("ParseDuration 1y != 8760h0m0s : %s", td.String())
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user