Merge branch 'gogf:master' into master

This commit is contained in:
houseme
2021-12-21 22:55:21 +08:00
committed by GitHub
13 changed files with 210 additions and 93 deletions

View File

@ -12,6 +12,8 @@ import (
"strconv"
"strings"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/rwmutex"
"github.com/gogf/gf/v2/internal/utils"
"github.com/gogf/gf/v2/text/gstr"
@ -38,9 +40,14 @@ type Options struct {
StrNumber bool // StrNumber causes the Decoder to unmarshal a number into an interface{} as a string instead of as a float64.
}
// iInterface is used for type assert api for Interface().
type iInterface interface {
Interface() interface{}
// iInterfaces is used for type assert api for Interfaces().
type iInterfaces interface {
Interfaces() []interface{}
}
// iMapStrAny is the interface support for converting struct parameter to map.
type iMapStrAny interface {
MapStrAny() map[string]interface{}
}
// setValue sets `value` to `j` by `pattern`.
@ -48,16 +55,14 @@ type iInterface interface {
// 1. If value is nil and removed is true, means deleting this value;
// 2. It's quite complicated in hierarchical data search, node creating and data assignment;
func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
if value != nil {
if utils.IsStruct(value) {
if v, ok := value.(iInterface); ok {
value = v.Interface()
}
}
var (
err error
array = strings.Split(pattern, string(j.c))
length = len(array)
)
if value, err = j.convertValue(value); err != nil {
return gerror.Wrap(err, `Json Set failed`)
}
array := strings.Split(pattern, string(j.c))
length := len(array)
value = j.convertValue(value)
// Initialization checks.
if *j.p == nil {
if gstr.IsNumeric(array[0]) {
@ -252,30 +257,51 @@ done:
// convertValue converts `value` to map[string]interface{} or []interface{},
// which can be supported for hierarchical data access.
func (j *Json) convertValue(value interface{}) interface{} {
func (j *Json) convertValue(value interface{}) (convertedValue interface{}, err error) {
if value == nil {
return
}
switch value.(type) {
case map[string]interface{}:
return value
return value, nil
case []interface{}:
return value
return value, nil
default:
var (
reflectInfo = utils.OriginValueAndKind(value)
)
switch reflectInfo.OriginKind {
case reflect.Array:
return gconv.Interfaces(value)
return gconv.Interfaces(value), nil
case reflect.Slice:
return gconv.Interfaces(value)
return gconv.Interfaces(value), nil
case reflect.Map:
return gconv.Map(value)
return gconv.Map(value), nil
case reflect.Struct:
return gconv.Map(value)
if v, ok := value.(iMapStrAny); ok {
convertedValue = v.MapStrAny()
}
if utils.IsNil(convertedValue) {
if v, ok := value.(iInterfaces); ok {
convertedValue = v.Interfaces()
}
}
if utils.IsNil(convertedValue) {
convertedValue = gconv.Map(value)
}
if utils.IsNil(convertedValue) {
err = gerror.NewCodef(gcode.CodeInvalidParameter, `unsupported value type "%s"`, reflect.TypeOf(value))
}
return
default:
// Use json decode/encode at last.
b, _ := Encode(value)
v, _ := Decode(b)
return v
return value, nil
}
}
}

View File

@ -106,12 +106,26 @@ func (j *Json) Set(pattern string, value interface{}) error {
return j.setValue(pattern, value, false)
}
// MustSet performs as Set, but it panics if any error occurs.
func (j *Json) MustSet(pattern string, value interface{}) {
if err := j.Set(pattern, value); err != nil {
panic(err)
}
}
// Remove deletes value with specified `pattern`.
// It supports hierarchical data access by char separator, which is '.' in default.
func (j *Json) Remove(pattern string) error {
return j.setValue(pattern, nil, true)
}
// MustRemove performs as Remove, but it panics if any error occurs.
func (j *Json) MustRemove(pattern string) {
if err := j.Remove(pattern); err != nil {
panic(err)
}
}
// Contains checks whether the value by specified `pattern` exist.
func (j *Json) Contains(pattern string) bool {
return j.Get(pattern) != nil
@ -155,6 +169,13 @@ func (j *Json) Append(pattern string, value interface{}) error {
return gerror.NewCodef(gcode.CodeInvalidParameter, "invalid variable type of %s", pattern)
}
// MustAppend performs as Append, but it panics if any error occurs.
func (j *Json) MustAppend(pattern string, value interface{}) {
if err := j.Append(pattern, value); err != nil {
panic(err)
}
}
// Map converts current Json object to map[string]interface{}.
// It returns nil if fails.
func (j *Json) Map() map[string]interface{} {

View File

@ -29,3 +29,13 @@ func (j *Json) UnmarshalValue(value interface{}) error {
}
return nil
}
// MapStrAny implements interface function MapStrAny().
func (j *Json) MapStrAny() map[string]interface{} {
return j.Map()
}
// Interfaces implements interface function Interfaces().
func (j *Json) Interfaces() []interface{} {
return j.Array()
}

View File

@ -10,6 +10,7 @@ import (
"bytes"
"testing"
"github.com/gogf/gf/v2/container/garray"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/test/gtest"
@ -329,3 +330,12 @@ func Test_Set20(t *testing.T) {
), true)
})
}
func Test_Set_GArray(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
j := gjson.New(nil)
arr := garray.New().Append("test")
t.AssertNil(j.Set("arr", arr))
t.Assert(j.Get("arr").Array(), g.Slice{"test"})
})
}

View File

@ -510,8 +510,8 @@ func TestJson_Set_With_Struct(t *testing.T) {
"user3": g.Map{"name": "user3"},
})
user1 := v.GetJson("user1")
user1.Set("id", 111)
v.Set("user1", user1)
t.AssertNil(user1.Set("id", 111))
t.AssertNil(v.Set("user1", user1))
t.Assert(v.Get("user1.id"), 111)
})
}

View File

@ -84,8 +84,8 @@ func NewSkipf(skip int, format string, args ...interface{}) error {
}
}
// Wrap wraps error with text.
// It returns nil if given err is nil.
// Wrap wraps error with text. It returns nil if given err is nil.
// Note that it does not lose the error code of wrapped error, as it inherits the error code from it.
func Wrap(err error, text string) error {
if err == nil {
return nil
@ -98,9 +98,9 @@ func Wrap(err error, text string) error {
}
}
// Wrapf returns an error annotating err with a stack trace
// at the point Wrapf is called, and the format specifier.
// Wrapf returns an error annotating err with a stack trace at the point Wrapf is called, and the format specifier.
// It returns nil if given `err` is nil.
// Note that it does not lose the error code of wrapped error, as it inherits the error code from it.
func Wrapf(err error, format string, args ...interface{}) error {
if err == nil {
return nil
@ -113,9 +113,9 @@ func Wrapf(err error, format string, args ...interface{}) error {
}
}
// WrapSkip wraps error with text.
// It returns nil if given err is nil.
// WrapSkip wraps error with text. It returns nil if given err is nil.
// The parameter `skip` specifies the stack callers skipped amount.
// Note that it does not lose the error code of wrapped error, as it inherits the error code from it.
func WrapSkip(skip int, err error, text string) error {
if err == nil {
return nil
@ -128,9 +128,9 @@ func WrapSkip(skip int, err error, text string) error {
}
}
// WrapSkipf wraps error with text that is formatted with given format and args.
// It returns nil if given err is nil.
// WrapSkipf wraps error with text that is formatted with given format and args. It returns nil if given err is nil.
// The parameter `skip` specifies the stack callers skipped amount.
// Note that it does not lose the error code of wrapped error, as it inherits the error code from it.
func WrapSkipf(skip int, err error, format string, args ...interface{}) error {
if err == nil {
return nil

View File

@ -298,7 +298,7 @@ func IsEmpty(value interface{}) bool {
// return false
//}
// IsNil checks whether given `value` is nil.
// IsNil checks whether given `value` is nil, especially for interface{} type value.
// Parameter `traceSource` is used for tracing to the source variable if given `value` is type of pinter
// that also points to a pointer. It returns nil if the source is nil when `traceSource` is true.
// Note that it might use reflect feature which affects performance a little.

View File

@ -12,7 +12,7 @@ import (
"github.com/gogf/gf/v2/internal/empty"
)
// IsNil checks whether `value` is nil.
// IsNil checks whether `value` is nil, especially for interface{} type value.
func IsNil(value interface{}) bool {
return empty.IsNil(value)
}

View File

@ -69,7 +69,7 @@ func CaseSnakeFirstUpper(word string, underscore ...string) string {
}
for {
m := firstCamelCaseStart.FindAllStringSubmatch(word, 1)
m = firstCamelCaseStart.FindAllStringSubmatch(word, 1)
if len(m) > 0 && m[0][1] != "" {
w := strings.ToLower(m[0][1])
w = w[:len(w)-1] + replace + string(w[len(w)-1])

View File

@ -7,8 +7,9 @@
package gstr
import (
"github.com/gogf/gf/v2/internal/utils"
"strings"
"github.com/gogf/gf/v2/internal/utils"
)
// Replace returns a copy of the string `origin`
@ -32,13 +33,21 @@ func ReplaceI(origin, search, replace string, count ...int) string {
return origin
}
var (
length = len(search)
searchLower = strings.ToLower(search)
searchLength = len(search)
searchLower = strings.ToLower(search)
originLower string
pos int
diff = len(replace) - searchLength
)
for {
originLower := strings.ToLower(origin)
if pos := strings.Index(originLower, searchLower); pos != -1 {
origin = origin[:pos] + replace + origin[pos+length:]
originLower = strings.ToLower(origin)
if pos = Pos(originLower, searchLower, pos); pos != -1 {
origin = origin[:pos] + replace + origin[pos+searchLength:]
if diff < 0 {
pos += -diff
} else {
pos += diff + 1
}
if n--; n == 0 {
break
}

View File

@ -0,0 +1,90 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// go test *.go -bench=".*"
package gstr_test
import (
"testing"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/text/gstr"
)
func Test_Replace(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := "abcdEFG乱入的中文abcdefg"
t.Assert(gstr.Replace(s1, "ab", "AB"), "ABcdEFG乱入的中文ABcdefg")
t.Assert(gstr.Replace(s1, "EF", "ef"), "abcdefG乱入的中文abcdefg")
t.Assert(gstr.Replace(s1, "MN", "mn"), s1)
t.Assert(gstr.ReplaceByArray(s1, g.ArrayStr{
"a", "A",
"A", "-",
"a",
}), "-bcdEFG乱入的中文-bcdefg")
t.Assert(gstr.ReplaceByMap(s1, g.MapStrStr{
"a": "A",
"G": "g",
}), "AbcdEFg乱入的中文Abcdefg")
})
}
func Test_ReplaceI_1(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := "abcd乱入的中文ABCD"
s2 := "a"
t.Assert(gstr.ReplaceI(s1, "ab", "aa"), "aacd乱入的中文aaCD")
t.Assert(gstr.ReplaceI(s1, "ab", "aa", 0), "abcd乱入的中文ABCD")
t.Assert(gstr.ReplaceI(s1, "ab", "aa", 1), "aacd乱入的中文ABCD")
t.Assert(gstr.ReplaceI(s1, "abcd", "-"), "-乱入的中文-")
t.Assert(gstr.ReplaceI(s1, "abcd", "-", 1), "-乱入的中文ABCD")
t.Assert(gstr.ReplaceI(s1, "abcd乱入的", ""), "中文ABCD")
t.Assert(gstr.ReplaceI(s1, "ABCD乱入的", ""), "中文ABCD")
t.Assert(gstr.ReplaceI(s2, "A", "-"), "-")
t.Assert(gstr.ReplaceI(s2, "a", "-"), "-")
t.Assert(gstr.ReplaceIByArray(s1, g.ArrayStr{
"abcd乱入的", "-",
"-", "=",
"a",
}), "=中文ABCD")
t.Assert(gstr.ReplaceIByMap(s1, g.MapStrStr{
"ab": "-",
"CD": "=",
}), "-=乱入的中文-=")
})
}
func Test_ReplaceI_2(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
t.Assert(gstr.ReplaceI("aaa", "A", "-a-"), `-a--a--a-`)
t.Assert(gstr.ReplaceI("aaaa", "AA", "-"), `--`)
t.Assert(gstr.ReplaceI("a a a", "A", "b"), `b b b`)
t.Assert(gstr.ReplaceI("aaaaaa", "aa", "a"), `aaa`)
t.Assert(gstr.ReplaceI("aaaaaa", "AA", "A"), `AAA`)
t.Assert(gstr.ReplaceI("aaa", "A", "AA"), `AAAAAA`)
t.Assert(gstr.ReplaceI("aaa", "A", "AA"), `AAAAAA`)
t.Assert(gstr.ReplaceI("a duration", "duration", "recordduration"), `a recordduration`)
})
// With count parameter.
gtest.C(t, func(t *gtest.T) {
t.Assert(gstr.ReplaceI("aaaaaa", "aa", "a", 2), `aaaa`)
t.Assert(gstr.ReplaceI("aaaaaa", "AA", "A", 1), `Aaaaa`)
t.Assert(gstr.ReplaceI("aaaaaa", "AA", "A", 3), `AAA`)
t.Assert(gstr.ReplaceI("aaaaaa", "AA", "A", 4), `AAA`)
t.Assert(gstr.ReplaceI("aaa", "A", "AA", 2), `AAAAa`)
t.Assert(gstr.ReplaceI("aaa", "A", "AA", 3), `AAAAAA`)
t.Assert(gstr.ReplaceI("aaa", "A", "AA", 4), `AAAAAA`)
})
}

View File

@ -11,61 +11,10 @@ package gstr_test
import (
"testing"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/text/gstr"
)
func Test_Replace(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := "abcdEFG乱入的中文abcdefg"
t.Assert(gstr.Replace(s1, "ab", "AB"), "ABcdEFG乱入的中文ABcdefg")
t.Assert(gstr.Replace(s1, "EF", "ef"), "abcdefG乱入的中文abcdefg")
t.Assert(gstr.Replace(s1, "MN", "mn"), s1)
t.Assert(gstr.ReplaceByArray(s1, g.ArrayStr{
"a", "A",
"A", "-",
"a",
}), "-bcdEFG乱入的中文-bcdefg")
t.Assert(gstr.ReplaceByMap(s1, g.MapStrStr{
"a": "A",
"G": "g",
}), "AbcdEFg乱入的中文Abcdefg")
})
}
func Test_ReplaceI_1(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := "abcd乱入的中文ABCD"
s2 := "a"
t.Assert(gstr.ReplaceI(s1, "ab", "aa"), "aacd乱入的中文aaCD")
t.Assert(gstr.ReplaceI(s1, "ab", "aa", 0), "abcd乱入的中文ABCD")
t.Assert(gstr.ReplaceI(s1, "ab", "aa", 1), "aacd乱入的中文ABCD")
t.Assert(gstr.ReplaceI(s1, "abcd", "-"), "-乱入的中文-")
t.Assert(gstr.ReplaceI(s1, "abcd", "-", 1), "-乱入的中文ABCD")
t.Assert(gstr.ReplaceI(s1, "abcd乱入的", ""), "中文ABCD")
t.Assert(gstr.ReplaceI(s1, "ABCD乱入的", ""), "中文ABCD")
t.Assert(gstr.ReplaceI(s2, "A", "-"), "-")
t.Assert(gstr.ReplaceI(s2, "a", "-"), "-")
t.Assert(gstr.ReplaceIByArray(s1, g.ArrayStr{
"abcd乱入的", "-",
"-", "=",
"a",
}), "=中文ABCD")
t.Assert(gstr.ReplaceIByMap(s1, g.MapStrStr{
"ab": "-",
"CD": "=",
}), "-=乱入的中文-=")
})
}
func Test_ToLower(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := "abcdEFG乱入的中文abcdefg"

View File

@ -306,8 +306,10 @@ func Bytes(any interface{}) []byte {
switch value := any.(type) {
case string:
return []byte(value)
case []byte:
return value
default:
if f, ok := value.(iBytes); ok {
return f.Bytes()