mirror of
https://gitee.com/johng/gf
synced 2026-06-11 20:01:45 +08:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d9754a532b | |||
| dc51023c61 | |||
| 87ce5c5419 | |||
| c4fc9f9618 | |||
| 8d7aa5db83 | |||
| 9c70fe3be8 | |||
| 78027d2ec6 | |||
| d4e4b9addf | |||
| b2acd22f58 | |||
| ae5ecb5bfa | |||
| 6f1340ce36 | |||
| 473fdba14f | |||
| 93a01a1aaf | |||
| bb6883213f | |||
| 54395b39a6 | |||
| 6deb817fd0 | |||
| 482e093331 | |||
| 042f903157 | |||
| c423ad4830 | |||
| 958b109a12 | |||
| 113fffdd69 | |||
| 4aaf09fded | |||
| 2f3df76f37 | |||
| cf7706b16d | |||
| 36963c05a2 | |||
| 48d840a1b8 | |||
| e3ebc908bb | |||
| 9ed8d8c113 | |||
| 9eab5daf19 | |||
| 6a24b595f0 | |||
| 150f237f13 | |||
| 41f2138b39 | |||
| 6376b8aaa6 | |||
| 58362ad143 | |||
| d72d23c2eb | |||
| 7702c5bfde | |||
| 20f2a6c003 | |||
| 0d4c1c47d5 | |||
| 4d32733790 | |||
| 0e58b6e95b |
@ -9,10 +9,11 @@ import (
|
||||
|
||||
// 使用原生gredis.New操作redis,但是注意需要自己调用Close方法关闭redis链接池
|
||||
func main() {
|
||||
redis := gredis.New(gredis.Config{
|
||||
config := &gredis.Config{
|
||||
Host: "127.0.0.1",
|
||||
Port: 6379,
|
||||
})
|
||||
}
|
||||
redis := gredis.New(config)
|
||||
defer redis.Close()
|
||||
redis.Do("SET", "k", "v")
|
||||
v, _ := redis.Do("GET", "k")
|
||||
|
||||
@ -8,7 +8,6 @@ import (
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.SetSessionCookieMaxAge(0)
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.GET("/set", func(r *ghttp.Request) {
|
||||
r.Session.Set("time", gtime.Timestamp())
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -14,5 +14,4 @@ bin/
|
||||
cbuild
|
||||
**/.DS_Store
|
||||
.vscode/
|
||||
go.sum
|
||||
.example/other/
|
||||
|
||||
@ -4,11 +4,9 @@ arch: arm64-graviton2
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.11.x"
|
||||
- "1.12.x"
|
||||
- "1.13.x"
|
||||
- "1.14.x"
|
||||
- "1.15.x"
|
||||
- "1.16.x"
|
||||
|
||||
branches:
|
||||
only:
|
||||
|
||||
@ -9,11 +9,8 @@
|
||||
|
||||
English | [简体中文](README_ZH.MD)
|
||||
|
||||
`GF(GoFrame)` is a modular, powerful, high-performance and enterprise-class application development framework
|
||||
of Golang. Providing a series of core components and dozens of practical modules, such as:
|
||||
cache, logging, containers, timer, resource, validator, database orm, etc.
|
||||
Supporting web server integrated with router, cookie, session, middleware, logger, configure,
|
||||
template, https, hooks, rewrites and many more features.
|
||||
`GoFrame` is a modular, powerful, high-performance and enterprise-class application development framework
|
||||
of Golang.
|
||||
|
||||
> If you're a newbie to `Go`, you may consider `GoFrame` easy and great as `Laravel` in `PHP`, `SpringBoot` in `Java` or `Django` in `Python`.
|
||||
|
||||
|
||||
@ -8,12 +8,7 @@
|
||||
|
||||
[English](README.MD) | 简体中文
|
||||
|
||||
`GF(Go Frame)`是一款模块化、高性能、企业级的Go基础开发框架。
|
||||
实现了比较完善的基础设施建设以及开发工具链,提供了常用的基础开发模块,
|
||||
如:缓存、日志、队列、数组、集合、容器、定时器、命令行、内存锁、对象池、
|
||||
配置管理、资源管理、数据校验、数据编码、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信等等。
|
||||
并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、Middleware、服务注册、模板引擎等等,
|
||||
支持热重启、热更新、域名绑定、TLS/HTTPS、Rewrite等特性。
|
||||
`GoFrame`是一款模块化、高性能、企业级的Go基础开发框架。
|
||||
|
||||
> 如果您初识`Go`语言,您可以将`GoFrame`类似于`PHP`中的`Laravel`, `Java`中的`SpringBoot`或者`Python`中的`Django`。
|
||||
|
||||
@ -86,7 +81,7 @@ golang版本 >= 1.11
|
||||
# 用户
|
||||
|
||||
- [腾讯科技](https://www.tencent.com/)
|
||||
- [中兴科技](https://www.zte.com.cn/china/)
|
||||
- [中兴通讯](https://www.zte.com.cn/china/)
|
||||
- [蚂蚁金服](https://www.antfin.com/)
|
||||
- [医联科技](https://www.medlinker.com/)
|
||||
- [库币科技](https://www.kucoin.io/)
|
||||
|
||||
@ -71,13 +71,6 @@ func (v *Var) MapToMap(pointer interface{}, mapping ...map[string]string) (err e
|
||||
return gconv.MapToMap(v.Val(), pointer, mapping...)
|
||||
}
|
||||
|
||||
// MapToMapDeep converts any map type variable <params> to another map type variable
|
||||
// <pointer> recursively.
|
||||
// See gconv.MapToMapDeep.
|
||||
func (v *Var) MapToMapDeep(pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
return gconv.MapToMapDeep(v.Val(), pointer, mapping...)
|
||||
}
|
||||
|
||||
// MapToMaps converts any map type variable <params> to another map type variable <pointer>.
|
||||
// See gconv.MapToMaps.
|
||||
func (v *Var) MapToMaps(pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
|
||||
@ -473,9 +473,13 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error
|
||||
v, _ := internalCache.GetOrSetFuncLock(node.String(), func() (interface{}, error) {
|
||||
sqlDb, err = c.db.Open(node)
|
||||
if err != nil {
|
||||
intlog.Printf("DB open failed: %v, %+v", err, node)
|
||||
intlog.Printf(`db open failed: %v, %+v`, err, node)
|
||||
return nil, err
|
||||
}
|
||||
intlog.Printf(
|
||||
`open new connection, master:%v, config:%+v, node:%+v`,
|
||||
master, c.config, node,
|
||||
)
|
||||
if c.config.MaxIdleConnCount > 0 {
|
||||
sqlDb.SetMaxIdleConns(c.config.MaxIdleConnCount)
|
||||
} else {
|
||||
|
||||
@ -14,8 +14,8 @@ import (
|
||||
"github.com/gogf/gf/net/gtrace"
|
||||
"github.com/gogf/gf/os/gcmd"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/label"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
@ -59,33 +59,33 @@ func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) {
|
||||
if sql.Error != nil {
|
||||
span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, sql.Error))
|
||||
}
|
||||
labels := make([]label.KeyValue, 0)
|
||||
labels := make([]attribute.KeyValue, 0)
|
||||
labels = append(labels, gtrace.CommonLabels()...)
|
||||
labels = append(labels,
|
||||
label.String(tracingAttrDbType, c.db.GetConfig().Type),
|
||||
attribute.String(tracingAttrDbType, c.db.GetConfig().Type),
|
||||
)
|
||||
if c.db.GetConfig().Host != "" {
|
||||
labels = append(labels, label.String(tracingAttrDbHost, c.db.GetConfig().Host))
|
||||
labels = append(labels, attribute.String(tracingAttrDbHost, c.db.GetConfig().Host))
|
||||
}
|
||||
if c.db.GetConfig().Port != "" {
|
||||
labels = append(labels, label.String(tracingAttrDbPort, c.db.GetConfig().Port))
|
||||
labels = append(labels, attribute.String(tracingAttrDbPort, c.db.GetConfig().Port))
|
||||
}
|
||||
if c.db.GetConfig().Name != "" {
|
||||
labels = append(labels, label.String(tracingAttrDbName, c.db.GetConfig().Name))
|
||||
labels = append(labels, attribute.String(tracingAttrDbName, c.db.GetConfig().Name))
|
||||
}
|
||||
if c.db.GetConfig().User != "" {
|
||||
labels = append(labels, label.String(tracingAttrDbUser, c.db.GetConfig().User))
|
||||
labels = append(labels, attribute.String(tracingAttrDbUser, c.db.GetConfig().User))
|
||||
}
|
||||
if filteredLinkInfo := c.db.FilteredLinkInfo(); filteredLinkInfo != "" {
|
||||
labels = append(labels, label.String(tracingAttrDbLink, c.db.FilteredLinkInfo()))
|
||||
labels = append(labels, attribute.String(tracingAttrDbLink, c.db.FilteredLinkInfo()))
|
||||
}
|
||||
if group := c.db.GetGroup(); group != "" {
|
||||
labels = append(labels, label.String(tracingAttrDbGroup, group))
|
||||
labels = append(labels, attribute.String(tracingAttrDbGroup, group))
|
||||
}
|
||||
span.SetAttributes(labels...)
|
||||
span.AddEvent(tracingEventDbExecution, trace.WithAttributes(
|
||||
label.String(tracingEventDbExecutionSql, sql.Format),
|
||||
label.String(tracingEventDbExecutionCost, fmt.Sprintf(`%d ms`, sql.End-sql.Start)),
|
||||
label.String(tracingEventDbExecutionType, sql.Type),
|
||||
attribute.String(tracingEventDbExecutionSql, sql.Format),
|
||||
attribute.String(tracingEventDbExecutionCost, fmt.Sprintf(`%d ms`, sql.End-sql.Start)),
|
||||
attribute.String(tracingEventDbExecutionType, sql.Type),
|
||||
))
|
||||
}
|
||||
|
||||
@ -95,7 +95,7 @@ func (m *Model) Group(groupBy string) *Model {
|
||||
|
||||
// GroupBy is alias of Model.Group.
|
||||
// See Model.Group.
|
||||
// Deprecated.
|
||||
// Deprecated, use Group instead.
|
||||
func (m *Model) GroupBy(groupBy string) *Model {
|
||||
return m.Group(groupBy)
|
||||
}
|
||||
@ -109,7 +109,7 @@ func (m *Model) Order(orderBy ...string) *Model {
|
||||
|
||||
// OrderBy is alias of Model.Order.
|
||||
// See Model.Order.
|
||||
// Deprecated.
|
||||
// Deprecated, use Order instead.
|
||||
func (m *Model) OrderBy(orderBy string) *Model {
|
||||
return m.Order(orderBy)
|
||||
}
|
||||
@ -153,7 +153,7 @@ func (m *Model) Page(page, limit int) *Model {
|
||||
|
||||
// ForPage is alias of Model.Page.
|
||||
// See Model.Page.
|
||||
// Deprecated.
|
||||
// Deprecated, use Page instead.
|
||||
func (m *Model) ForPage(page, limit int) *Model {
|
||||
return m.Page(page, limit)
|
||||
}
|
||||
|
||||
@ -36,18 +36,18 @@ func (m *Model) Fields(fieldNamesOrMapStruct ...interface{}) *Model {
|
||||
// String slice.
|
||||
case length >= 2:
|
||||
model := m.getModel()
|
||||
model.fields = gstr.Join(m.mappingAndFilterToTableFields(gconv.Strings(fieldNamesOrMapStruct)), ",")
|
||||
model.fields = gstr.Join(m.mappingAndFilterToTableFields(gconv.Strings(fieldNamesOrMapStruct), true), ",")
|
||||
return model
|
||||
// It need type asserting.
|
||||
case length == 1:
|
||||
model := m.getModel()
|
||||
switch r := fieldNamesOrMapStruct[0].(type) {
|
||||
case string:
|
||||
model.fields = gstr.Join(m.mappingAndFilterToTableFields([]string{r}), ",")
|
||||
model.fields = gstr.Join(m.mappingAndFilterToTableFields([]string{r}, false), ",")
|
||||
case []string:
|
||||
model.fields = gstr.Join(m.mappingAndFilterToTableFields(r), ",")
|
||||
model.fields = gstr.Join(m.mappingAndFilterToTableFields(r, true), ",")
|
||||
default:
|
||||
model.fields = gstr.Join(m.mappingAndFilterToTableFields(gutil.Keys(r)), ",")
|
||||
model.fields = gstr.Join(m.mappingAndFilterToTableFields(gutil.Keys(r), true), ",")
|
||||
}
|
||||
return model
|
||||
}
|
||||
@ -65,16 +65,16 @@ func (m *Model) FieldsEx(fieldNamesOrMapStruct ...interface{}) *Model {
|
||||
model := m.getModel()
|
||||
switch {
|
||||
case length >= 2:
|
||||
model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(gconv.Strings(fieldNamesOrMapStruct)), ",")
|
||||
model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(gconv.Strings(fieldNamesOrMapStruct), true), ",")
|
||||
return model
|
||||
case length == 1:
|
||||
switch r := fieldNamesOrMapStruct[0].(type) {
|
||||
case string:
|
||||
model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields([]string{r}), ",")
|
||||
model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields([]string{r}, false), ",")
|
||||
case []string:
|
||||
model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(r), ",")
|
||||
model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(r, true), ",")
|
||||
default:
|
||||
model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(gutil.Keys(r)), ",")
|
||||
model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(gutil.Keys(r), true), ",")
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
@ -29,7 +29,10 @@ func (m *Model) getModel() *Model {
|
||||
}
|
||||
|
||||
// mappingAndFilterToTableFields mappings and changes given field name to really table field name.
|
||||
func (m *Model) mappingAndFilterToTableFields(fields []string) []string {
|
||||
// Eg:
|
||||
// ID -> id
|
||||
// NICK_Name -> nickname
|
||||
func (m *Model) mappingAndFilterToTableFields(fields []string, filter bool) []string {
|
||||
fieldsMap, err := m.db.TableFields(m.tables)
|
||||
if err != nil || len(fieldsMap) == 0 {
|
||||
return fields
|
||||
@ -52,6 +55,8 @@ func (m *Model) mappingAndFilterToTableFields(fields []string) []string {
|
||||
// Eg: id, name
|
||||
if foundKey, _ := gutil.MapPossibleItemByKey(fieldsKeyMap, field); foundKey != "" {
|
||||
outputFieldsArray = append(outputFieldsArray, foundKey)
|
||||
} else if !filter {
|
||||
outputFieldsArray = append(outputFieldsArray, field)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -6,22 +6,22 @@
|
||||
|
||||
package gdb
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use Json instead.
|
||||
func (r Record) ToJson() string {
|
||||
return r.Json()
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use Xml instead.
|
||||
func (r Record) ToXml(rootTag ...string) string {
|
||||
return r.Xml(rootTag...)
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use Map instead.
|
||||
func (r Record) ToMap() Map {
|
||||
return r.Map()
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use Struct instead.
|
||||
func (r Record) ToStruct(pointer interface{}) error {
|
||||
return r.Struct(pointer)
|
||||
}
|
||||
|
||||
@ -189,5 +189,5 @@ func (r Result) RecordKeyUint(key string) map[uint]Record {
|
||||
// Structs converts `r` to struct slice.
|
||||
// Note that the parameter `pointer` should be type of *[]struct/*[]*struct.
|
||||
func (r Result) Structs(pointer interface{}) (err error) {
|
||||
return gconv.StructsTag(r, pointer, OrmTagForStruct)
|
||||
return gconv.StructsTag(r.List(), pointer, OrmTagForStruct)
|
||||
}
|
||||
|
||||
@ -6,52 +6,52 @@
|
||||
|
||||
package gdb
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use Json instead.
|
||||
func (r Result) ToJson() string {
|
||||
return r.Json()
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use Xml instead.
|
||||
func (r Result) ToXml(rootTag ...string) string {
|
||||
return r.Xml(rootTag...)
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use List instead.
|
||||
func (r Result) ToList() List {
|
||||
return r.List()
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use MapKeyStr instead.
|
||||
func (r Result) ToStringMap(key string) map[string]Map {
|
||||
return r.MapKeyStr(key)
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use MapKetInt instead.
|
||||
func (r Result) ToIntMap(key string) map[int]Map {
|
||||
return r.MapKeyInt(key)
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use MapKeyUint instead.
|
||||
func (r Result) ToUintMap(key string) map[uint]Map {
|
||||
return r.MapKeyUint(key)
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use RecordKeyStr instead.
|
||||
func (r Result) ToStringRecord(key string) map[string]Record {
|
||||
return r.RecordKeyStr(key)
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use RecordKetInt instead.
|
||||
func (r Result) ToIntRecord(key string) map[int]Record {
|
||||
return r.RecordKeyInt(key)
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use RecordKetUint instead.
|
||||
func (r Result) ToUintRecord(key string) map[uint]Record {
|
||||
return r.RecordKeyUint(key)
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use Structs instead.
|
||||
func (r Result) ToStructs(pointer interface{}) (err error) {
|
||||
return r.Structs(pointer)
|
||||
}
|
||||
|
||||
@ -8,7 +8,6 @@ package gdb_test
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
@ -363,6 +362,30 @@ func Test_Model_Scan_CustomType_Time(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Scan_CustomType_String(t *testing.T) {
|
||||
type MyString string
|
||||
|
||||
type MyStringSt struct {
|
||||
Passport MyString
|
||||
}
|
||||
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
st := new(MyStringSt)
|
||||
err := db.Model(table).Fields("Passport").WherePri(1).Scan(st)
|
||||
t.AssertNil(err)
|
||||
t.Assert(st.Passport, "user_1")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var sts []MyStringSt
|
||||
err := db.Model(table).Fields("Passport").Order("id asc").Scan(&sts)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(sts), TableSize)
|
||||
t.Assert(sts[0].Passport, "user_1")
|
||||
})
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
@ -374,11 +397,11 @@ type User struct {
|
||||
func (user *User) UnmarshalValue(value interface{}) error {
|
||||
switch result := value.(type) {
|
||||
case map[string]interface{}:
|
||||
user.Id = result["id"].(gdb.Value).Int()
|
||||
user.Passport = result["passport"].(gdb.Value).String()
|
||||
user.Id = result["id"].(int)
|
||||
user.Passport = result["passport"].(string)
|
||||
user.Password = ""
|
||||
user.Nickname = result["nickname"].(gdb.Value).String()
|
||||
user.CreateTime = result["create_time"].(gdb.Value).GTime()
|
||||
user.Nickname = result["nickname"].(string)
|
||||
user.CreateTime = gtime.New(result["create_time"])
|
||||
return nil
|
||||
default:
|
||||
return gconv.Struct(value, user)
|
||||
|
||||
@ -16,6 +16,7 @@ package gredis
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
@ -113,6 +114,7 @@ func New(config *Config) *Redis {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
intlog.Printf(`open new connection, config:%+v`, config)
|
||||
// AUTH
|
||||
if len(config.Pass) > 0 {
|
||||
if _, err := c.Do("AUTH", config.Pass); err != nil {
|
||||
@ -188,7 +190,7 @@ func (r *Redis) Conn() *Conn {
|
||||
}
|
||||
|
||||
// Alias of Conn, see Conn.
|
||||
// Deprecated.
|
||||
// Deprecated, use Conn instead.
|
||||
func (r *Redis) GetConn() *Conn {
|
||||
return r.Conn()
|
||||
}
|
||||
|
||||
@ -14,8 +14,8 @@ import (
|
||||
"github.com/gogf/gf/net/gtrace"
|
||||
"github.com/gogf/gf/os/gcmd"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/label"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
@ -68,14 +68,14 @@ func (c *Conn) addTracingItem(item *tracingItem) {
|
||||
}
|
||||
span.SetAttributes(gtrace.CommonLabels()...)
|
||||
span.SetAttributes(
|
||||
label.String(tracingAttrRedisHost, c.redis.config.Host),
|
||||
label.Int(tracingAttrRedisPort, c.redis.config.Port),
|
||||
label.Int(tracingAttrRedisDb, c.redis.config.Db),
|
||||
attribute.String(tracingAttrRedisHost, c.redis.config.Host),
|
||||
attribute.Int(tracingAttrRedisPort, c.redis.config.Port),
|
||||
attribute.Int(tracingAttrRedisDb, c.redis.config.Db),
|
||||
)
|
||||
jsonBytes, _ := json.Marshal(item.arguments)
|
||||
span.AddEvent(tracingEventRedisExecution, trace.WithAttributes(
|
||||
label.String(tracingEventRedisExecutionCommand, item.commandName),
|
||||
label.String(tracingEventRedisExecutionCost, fmt.Sprintf(`%d ms`, item.costMilli)),
|
||||
label.String(tracingEventRedisExecutionArguments, string(jsonBytes)),
|
||||
attribute.String(tracingEventRedisExecutionCommand, item.commandName),
|
||||
attribute.String(tracingEventRedisExecutionCost, fmt.Sprintf(`%d ms`, item.costMilli)),
|
||||
attribute.String(tracingEventRedisExecutionArguments, string(jsonBytes)),
|
||||
))
|
||||
}
|
||||
|
||||
@ -354,13 +354,6 @@ func (j *Json) GetMapToMap(pattern string, pointer interface{}, mapping ...map[s
|
||||
return gconv.MapToMap(j.Get(pattern), pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetMapToMapDeep retrieves the value by specified <pattern> and converts it to specified map
|
||||
// variable recursively.
|
||||
// See gconv.MapToMapDeep.
|
||||
func (j *Json) GetMapToMapDeep(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.MapToMapDeep(j.Get(pattern), pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetMapToMaps retrieves the value by specified <pattern> and converts it to specified map slice
|
||||
// variable.
|
||||
// See gconv.MapToMaps.
|
||||
|
||||
@ -85,15 +85,6 @@ func (j *Json) ToMapToMap(pointer interface{}, mapping ...map[string]string) err
|
||||
return gconv.MapToMap(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToMapToMapDeep converts current Json object to specified map variable recursively.
|
||||
// The parameter of <pointer> should be type of *map.
|
||||
// Deprecated, use MapToMap instead.
|
||||
func (j *Json) ToMapToMapDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.MapToMapDeep(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToMapToMaps converts current Json object to specified map variable slice.
|
||||
// The parameter of <pointer> should be type of []map/*map.
|
||||
// Deprecated, use MapToMaps instead.
|
||||
|
||||
@ -48,22 +48,26 @@ func Database(name ...string) gdb.DB {
|
||||
configMap = Config().GetMap(configNodeKey)
|
||||
}
|
||||
if len(configMap) == 0 && !gdb.IsConfigured() {
|
||||
if !Config().Available() {
|
||||
configFilePath, err := Config().GetFilePath()
|
||||
if configFilePath == "" {
|
||||
exampleFileName := "config.example.toml"
|
||||
if Config().Available(exampleFileName) {
|
||||
panic(gerror.Newf(
|
||||
if exampleConfigFilePath, _ := Config().GetFilePath(exampleFileName); exampleConfigFilePath != "" {
|
||||
panic(gerror.Wrapf(
|
||||
err,
|
||||
`configuration file "%s" not found, but found "%s", did you miss renaming the configuration example file?`,
|
||||
Config().GetFileName(),
|
||||
exampleFileName,
|
||||
))
|
||||
} else {
|
||||
panic(gerror.Newf(
|
||||
panic(gerror.Wrapf(
|
||||
err,
|
||||
`configuration file "%s" not found, did you miss the configuration file or the file name setting?`,
|
||||
Config().GetFileName(),
|
||||
))
|
||||
}
|
||||
}
|
||||
panic(gerror.Newf(
|
||||
panic(gerror.Wrapf(
|
||||
err,
|
||||
`database initialization failed: "%s" node not found, is configuration file or configuration node missing?`,
|
||||
configNodeNameDatabase,
|
||||
))
|
||||
|
||||
@ -47,7 +47,14 @@ func Redis(name ...string) *gredis.Redis {
|
||||
panic(fmt.Sprintf(`configuration for redis not found for group "%s"`, group))
|
||||
}
|
||||
} else {
|
||||
panic(fmt.Sprintf(`incomplete configuration for redis: "redis" node not found in config file "%s"`, config.FilePath()))
|
||||
filepath, err := config.GetFilePath()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
panic(fmt.Sprintf(
|
||||
`incomplete configuration for redis: "redis" node not found in config file "%s"`,
|
||||
filepath,
|
||||
))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
8
go.mod
8
go.mod
@ -1,6 +1,6 @@
|
||||
module github.com/gogf/gf
|
||||
|
||||
go 1.11
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
@ -11,8 +11,10 @@ require (
|
||||
github.com/gorilla/websocket v1.4.1
|
||||
github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf
|
||||
github.com/mattn/go-runewidth v0.0.10 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.1
|
||||
go.opentelemetry.io/otel v0.16.0
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
go.opentelemetry.io/otel v0.19.0
|
||||
go.opentelemetry.io/otel/oteltest v0.19.0
|
||||
go.opentelemetry.io/otel/trace v0.19.0
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102
|
||||
golang.org/x/text v0.3.4
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
|
||||
|
||||
59
go.sum
Normal file
59
go.sum
Normal file
@ -0,0 +1,59 @@
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 h1:LdXxtjzvZYhhUaonAaAKArG3pyC67kGL3YY+6hGG8G4=
|
||||
github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf h1:wIOAyJMMen0ELGiFzlmqxdcV1yGbkyHBAB6PolcNbLA=
|
||||
github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
|
||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
go.opentelemetry.io/otel v0.19.0 h1:Lenfy7QHRXPZVsw/12CWpxX6d/JkrX8wrx2vO8G80Ng=
|
||||
go.opentelemetry.io/otel v0.19.0/go.mod h1:j9bF567N9EfomkSidSfmMwIwIBuP37AMAIzVW85OxSg=
|
||||
go.opentelemetry.io/otel/metric v0.19.0 h1:dtZ1Ju44gkJkYvo+3qGqVXmf88tc+a42edOywypengg=
|
||||
go.opentelemetry.io/otel/metric v0.19.0/go.mod h1:8f9fglJPRnXuskQmKpnad31lcLJ2VmNNqIsx/uIwBSc=
|
||||
go.opentelemetry.io/otel/oteltest v0.19.0 h1:YVfA0ByROYqTwOxqHVZYZExzEpfZor+MU1rU+ip2v9Q=
|
||||
go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA=
|
||||
go.opentelemetry.io/otel/trace v0.19.0 h1:1ucYlenXIDA1OlHVLDZKX0ObXV5RLaq06DtUKz5e5zc=
|
||||
go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@ -7,45 +7,40 @@
|
||||
// Package gi18n implements internationalization and localization.
|
||||
package gi18n
|
||||
|
||||
var (
|
||||
// defaultManager is the default i18n instance for package functions.
|
||||
defaultManager = Instance()
|
||||
)
|
||||
|
||||
// SetPath sets the directory path storing i18n files.
|
||||
func SetPath(path string) error {
|
||||
return defaultManager.SetPath(path)
|
||||
return Instance().SetPath(path)
|
||||
}
|
||||
|
||||
// SetLanguage sets the language for translator.
|
||||
func SetLanguage(language string) {
|
||||
defaultManager.SetLanguage(language)
|
||||
Instance().SetLanguage(language)
|
||||
}
|
||||
|
||||
// SetDelimiters sets the delimiters for translator.
|
||||
func SetDelimiters(left, right string) {
|
||||
defaultManager.SetDelimiters(left, right)
|
||||
Instance().SetDelimiters(left, right)
|
||||
}
|
||||
|
||||
// T is alias of Translate for convenience.
|
||||
func T(content string, language ...string) string {
|
||||
return defaultManager.T(content, language...)
|
||||
return Instance().T(content, language...)
|
||||
}
|
||||
|
||||
// Tf is alias of TranslateFormat for convenience.
|
||||
func Tf(format string, values ...interface{}) string {
|
||||
return defaultManager.TranslateFormat(format, values...)
|
||||
return Instance().TranslateFormat(format, values...)
|
||||
}
|
||||
|
||||
// Tfl is alias of TranslateFormatLang for convenience.
|
||||
func Tfl(language string, format string, values ...interface{}) string {
|
||||
return defaultManager.TranslateFormatLang(language, format, values...)
|
||||
return Instance().TranslateFormatLang(language, format, values...)
|
||||
}
|
||||
|
||||
// TranslateFormat translates, formats and returns the <format> with configured language
|
||||
// and given <values>.
|
||||
func TranslateFormat(format string, values ...interface{}) string {
|
||||
return defaultManager.TranslateFormat(format, values...)
|
||||
return Instance().TranslateFormat(format, values...)
|
||||
}
|
||||
|
||||
// TranslateFormatLang translates, formats and returns the <format> with configured language
|
||||
@ -53,17 +48,17 @@ func TranslateFormat(format string, values ...interface{}) string {
|
||||
// configured language. If <language> is given empty string, it uses the default configured
|
||||
// language for the translation.
|
||||
func TranslateFormatLang(language string, format string, values ...interface{}) string {
|
||||
return defaultManager.TranslateFormatLang(format, language, values...)
|
||||
return Instance().TranslateFormatLang(language, format, values...)
|
||||
}
|
||||
|
||||
// Translate translates <content> with configured language and returns the translated content.
|
||||
// The parameter <language> specifies custom translation language ignoring configured language.
|
||||
func Translate(content string, language ...string) string {
|
||||
return defaultManager.Translate(content, language...)
|
||||
return Instance().Translate(content, language...)
|
||||
}
|
||||
|
||||
// GetValue retrieves and returns the configured content for given key and specified language.
|
||||
// It returns an empty string if not found.
|
||||
func GetContent(key string, language ...string) string {
|
||||
return defaultManager.GetContent(key, language...)
|
||||
return Instance().GetContent(key, language...)
|
||||
}
|
||||
|
||||
@ -6,6 +6,8 @@
|
||||
|
||||
package structs
|
||||
|
||||
import "reflect"
|
||||
|
||||
// Tag returns the value associated with key in the tag string. If there is no
|
||||
// such key in the tag, Tag returns the empty string.
|
||||
func (f *Field) Tag(key string) string {
|
||||
@ -34,6 +36,24 @@ func (f *Field) Type() Type {
|
||||
}
|
||||
}
|
||||
|
||||
// Kind returns the reflect.Kind for Value of Field `f`.
|
||||
func (f *Field) Kind() reflect.Kind {
|
||||
return f.Value.Kind()
|
||||
}
|
||||
|
||||
// OriginalKind retrieves and returns the original reflect.Kind for Value of Field `f`.
|
||||
func (f *Field) OriginalKind() reflect.Kind {
|
||||
var (
|
||||
kind = f.Value.Kind()
|
||||
value = f.Value
|
||||
)
|
||||
for kind == reflect.Ptr {
|
||||
value = value.Elem()
|
||||
kind = value.Kind()
|
||||
}
|
||||
return kind
|
||||
}
|
||||
|
||||
// FieldMap retrieves and returns struct field as map[name/tag]*Field from `pointer`.
|
||||
//
|
||||
// The parameter `pointer` should be type of struct/*struct.
|
||||
|
||||
@ -13,16 +13,17 @@ import (
|
||||
"github.com/gogf/gf/net/ghttp/internal/client"
|
||||
"github.com/gogf/gf/net/ghttp/internal/httputil"
|
||||
"github.com/gogf/gf/net/gtrace"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/label"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
tracingMaxContentLogSize = 512 * 1024 // Max log size for request and response body.
|
||||
tracingInstrumentName = "github.com/gogf/gf/net/ghttp.Server"
|
||||
tracingEventHttpRequest = "http.request"
|
||||
tracingEventHttpRequestHeaders = "http.request.headers"
|
||||
@ -41,7 +42,7 @@ func MiddlewareClientTracing(c *Client, r *http.Request) (*ClientResponse, error
|
||||
// MiddlewareServerTracing is a serer middleware that enables tracing feature using standards of OpenTelemetry.
|
||||
func MiddlewareServerTracing(r *Request) {
|
||||
tr := otel.GetTracerProvider().Tracer(tracingInstrumentName, trace.WithInstrumentationVersion(gf.VERSION))
|
||||
ctx := otel.GetTextMapPropagator().Extract(r.Context(), r.Header)
|
||||
ctx := otel.GetTextMapPropagator().Extract(r.Context(), propagation.HeaderCarrier(r.Header))
|
||||
ctx, span := tr.Start(ctx, r.URL.String(), trace.WithSpanKind(trace.SpanKindServer))
|
||||
defer span.End()
|
||||
|
||||
@ -51,21 +52,17 @@ func MiddlewareServerTracing(r *Request) {
|
||||
r.SetCtx(ctx)
|
||||
|
||||
// Request content logging.
|
||||
var reqBodyContent string
|
||||
if r.ContentLength <= tracingMaxContentLogSize {
|
||||
reqBodyContentBytes, _ := ioutil.ReadAll(r.Body)
|
||||
r.Body = utils.NewReadCloser(reqBodyContentBytes, false)
|
||||
reqBodyContent = string(reqBodyContentBytes)
|
||||
} else {
|
||||
reqBodyContent = fmt.Sprintf(
|
||||
"[Request Body Too Large For Tracing, Max: %d bytes]",
|
||||
tracingMaxContentLogSize,
|
||||
)
|
||||
}
|
||||
reqBodyContentBytes, _ := ioutil.ReadAll(r.Body)
|
||||
r.Body = utils.NewReadCloser(reqBodyContentBytes, false)
|
||||
|
||||
span.AddEvent(tracingEventHttpRequest, trace.WithAttributes(
|
||||
label.Any(tracingEventHttpRequestHeaders, httputil.HeaderToMap(r.Header)),
|
||||
label.Any(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ctx)),
|
||||
label.String(tracingEventHttpRequestBody, reqBodyContent),
|
||||
attribute.Any(tracingEventHttpRequestHeaders, httputil.HeaderToMap(r.Header)),
|
||||
attribute.Any(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ctx)),
|
||||
attribute.String(tracingEventHttpRequestBody, gstr.StrLimit(
|
||||
string(reqBodyContentBytes),
|
||||
gtrace.MaxContentLogSize(),
|
||||
"...",
|
||||
)),
|
||||
))
|
||||
|
||||
// Continue executing.
|
||||
@ -77,17 +74,16 @@ func MiddlewareServerTracing(r *Request) {
|
||||
}
|
||||
// Response content logging.
|
||||
var resBodyContent string
|
||||
if r.Response.BufferLength() <= tracingMaxContentLogSize {
|
||||
resBodyContent = r.Response.BufferString()
|
||||
} else {
|
||||
resBodyContent = fmt.Sprintf(
|
||||
"[Response Body Too Large For Tracing, Max: %d bytes]",
|
||||
tracingMaxContentLogSize,
|
||||
)
|
||||
}
|
||||
resBodyContent = r.Response.BufferString()
|
||||
resBodyContent = gstr.StrLimit(
|
||||
r.Response.BufferString(),
|
||||
gtrace.MaxContentLogSize(),
|
||||
"...",
|
||||
)
|
||||
|
||||
span.AddEvent(tracingEventHttpResponse, trace.WithAttributes(
|
||||
label.Any(tracingEventHttpResponseHeaders, httputil.HeaderToMap(r.Response.Header())),
|
||||
label.String(tracingEventHttpResponseBody, resBodyContent),
|
||||
attribute.Any(tracingEventHttpResponseHeaders, httputil.HeaderToMap(r.Response.Header())),
|
||||
attribute.String(tracingEventHttpResponseBody, resBodyContent),
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
@ -139,14 +139,14 @@ func (r *Request) GetVar(key string, def ...interface{}) *gvar.Var {
|
||||
|
||||
// GetRaw is alias of GetBody.
|
||||
// See GetBody.
|
||||
// Deprecated.
|
||||
// Deprecated, use GetBody instead.
|
||||
func (r *Request) GetRaw() []byte {
|
||||
return r.GetBody()
|
||||
}
|
||||
|
||||
// GetRawString is alias of GetBodyString.
|
||||
// See GetBodyString.
|
||||
// Deprecated.
|
||||
// Deprecated, use GetBodyString instead.
|
||||
func (r *Request) GetRawString() string {
|
||||
return r.GetBodyString()
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ import (
|
||||
// Note that if there're multiple parameters with the same name, the parameters are retrieved
|
||||
// and overwrote in order of priority: form > body.
|
||||
//
|
||||
// Deprecated.
|
||||
// Deprecated, use GetForm instead.
|
||||
func (r *Request) GetPost(key string, def ...interface{}) interface{} {
|
||||
r.parseForm()
|
||||
if len(r.formMap) > 0 {
|
||||
@ -38,82 +38,82 @@ func (r *Request) GetPost(key string, def ...interface{}) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use GetFormVar instead.
|
||||
func (r *Request) GetPostVar(key string, def ...interface{}) *gvar.Var {
|
||||
return gvar.New(r.GetPost(key, def...))
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use GetFormString instead.
|
||||
func (r *Request) GetPostString(key string, def ...interface{}) string {
|
||||
return r.GetPostVar(key, def...).String()
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use GetFormBool instead.
|
||||
func (r *Request) GetPostBool(key string, def ...interface{}) bool {
|
||||
return r.GetPostVar(key, def...).Bool()
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use GetFormInt instead.
|
||||
func (r *Request) GetPostInt(key string, def ...interface{}) int {
|
||||
return r.GetPostVar(key, def...).Int()
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use GetFormInt32 instead.
|
||||
func (r *Request) GetPostInt32(key string, def ...interface{}) int32 {
|
||||
return r.GetPostVar(key, def...).Int32()
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use GetFormInt64 instead.
|
||||
func (r *Request) GetPostInt64(key string, def ...interface{}) int64 {
|
||||
return r.GetPostVar(key, def...).Int64()
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use GetFormInts instead.
|
||||
func (r *Request) GetPostInts(key string, def ...interface{}) []int {
|
||||
return r.GetPostVar(key, def...).Ints()
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use GetFormUint instead.
|
||||
func (r *Request) GetPostUint(key string, def ...interface{}) uint {
|
||||
return r.GetPostVar(key, def...).Uint()
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use GetFormUint32 instead.
|
||||
func (r *Request) GetPostUint32(key string, def ...interface{}) uint32 {
|
||||
return r.GetPostVar(key, def...).Uint32()
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use GetFormUint64 instead.
|
||||
func (r *Request) GetPostUint64(key string, def ...interface{}) uint64 {
|
||||
return r.GetPostVar(key, def...).Uint64()
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use GetFormFloat32 instead.
|
||||
func (r *Request) GetPostFloat32(key string, def ...interface{}) float32 {
|
||||
return r.GetPostVar(key, def...).Float32()
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use GetFormFloat64 instead.
|
||||
func (r *Request) GetPostFloat64(key string, def ...interface{}) float64 {
|
||||
return r.GetPostVar(key, def...).Float64()
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use GetFormFloats instead.
|
||||
func (r *Request) GetPostFloats(key string, def ...interface{}) []float64 {
|
||||
return r.GetPostVar(key, def...).Floats()
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use GetFormArray instead.
|
||||
func (r *Request) GetPostArray(key string, def ...interface{}) []string {
|
||||
return r.GetPostVar(key, def...).Strings()
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use GetFormStrings instead.
|
||||
func (r *Request) GetPostStrings(key string, def ...interface{}) []string {
|
||||
return r.GetPostVar(key, def...).Strings()
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Deprecated, use GetFormInterfaces instead.
|
||||
func (r *Request) GetPostInterfaces(key string, def ...interface{}) []interface{} {
|
||||
return r.GetPostVar(key, def...).Interfaces()
|
||||
}
|
||||
|
||||
@ -160,14 +160,14 @@ func (c *Cookie) Remove(key string) {
|
||||
"",
|
||||
c.request.Server.GetCookieDomain(),
|
||||
c.request.Server.GetCookiePath(),
|
||||
-86400,
|
||||
-24*time.Hour,
|
||||
)
|
||||
}
|
||||
|
||||
// RemoveCookie deletes specified key and its value from cookie using given domain and path.
|
||||
// It actually tells the http client that the cookie is expired, do not send it to server next time.
|
||||
func (c *Cookie) RemoveCookie(key, domain, path string) {
|
||||
c.SetCookie(key, "", domain, path, -86400)
|
||||
c.SetCookie(key, "", domain, path, -24*time.Hour)
|
||||
}
|
||||
|
||||
// Flush outputs the cookie items to client.
|
||||
|
||||
@ -52,7 +52,5 @@ func Test_Client_Request_13_Dump(t *testing.T) {
|
||||
t.Assert(gstr.Contains(dumpedText3, "test_for_request_body"), true)
|
||||
dumpedText4 := r2.RawResponse()
|
||||
t.Assert(gstr.Contains(dumpedText4, "test_for_request_body"), false)
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ import (
|
||||
"golang.org/x/net/proxy"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
@ -36,7 +37,6 @@ type Client struct {
|
||||
prefix string // Prefix for request.
|
||||
authUser string // HTTP basic authentication: user.
|
||||
authPass string // HTTP basic authentication: pass.
|
||||
browserMode bool // Whether auto saving and sending cookie content.
|
||||
retryCount int // Retry count when request fails.
|
||||
retryInterval time.Duration // Retry interval when request fails.
|
||||
middlewareHandler []HandlerFunc // Interceptor handlers
|
||||
@ -84,7 +84,10 @@ func (c *Client) Clone() *Client {
|
||||
// When browser mode is enabled, it automatically saves and sends cookie content
|
||||
// from and to server.
|
||||
func (c *Client) SetBrowserMode(enabled bool) *Client {
|
||||
c.browserMode = enabled
|
||||
if enabled {
|
||||
jar, _ := cookiejar.New(nil)
|
||||
c.Jar = jar
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
|
||||
@ -115,18 +115,6 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Respo
|
||||
} else {
|
||||
resp, err = c.callRequest(req)
|
||||
}
|
||||
|
||||
// Auto saving cookie content.
|
||||
if c.browserMode && resp != nil && resp.Response != nil {
|
||||
now := time.Now()
|
||||
for _, v := range resp.Response.Cookies() {
|
||||
if !v.Expires.IsZero() && v.Expires.UnixNano() < now.UnixNano() {
|
||||
delete(c.cookies, v.Name)
|
||||
} else {
|
||||
c.cookies[v.Name] = v.Value
|
||||
}
|
||||
}
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
@ -136,53 +124,67 @@ func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *h
|
||||
if len(c.prefix) > 0 {
|
||||
url = c.prefix + gstr.Trim(url)
|
||||
}
|
||||
param := ""
|
||||
var params string
|
||||
if len(data) > 0 {
|
||||
switch c.header["Content-Type"] {
|
||||
case "application/json":
|
||||
switch data[0].(type) {
|
||||
case string, []byte:
|
||||
param = gconv.String(data[0])
|
||||
params = gconv.String(data[0])
|
||||
default:
|
||||
if b, err := json.Marshal(data[0]); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
param = gconv.UnsafeBytesToStr(b)
|
||||
params = string(b)
|
||||
}
|
||||
}
|
||||
case "application/xml":
|
||||
switch data[0].(type) {
|
||||
case string, []byte:
|
||||
param = gconv.String(data[0])
|
||||
params = gconv.String(data[0])
|
||||
default:
|
||||
if b, err := gparser.VarToXml(data[0]); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
param = gconv.UnsafeBytesToStr(b)
|
||||
params = string(b)
|
||||
}
|
||||
}
|
||||
default:
|
||||
param = httputil.BuildParams(data[0])
|
||||
params = httputil.BuildParams(data[0])
|
||||
}
|
||||
}
|
||||
if method == "GET" {
|
||||
// It appends the parameters to the url if http method is GET.
|
||||
if param != "" {
|
||||
if gstr.Contains(url, "?") {
|
||||
url = url + "&" + param
|
||||
} else {
|
||||
url = url + "?" + param
|
||||
var bodyBuffer *bytes.Buffer
|
||||
if params != "" {
|
||||
switch c.header["Content-Type"] {
|
||||
case
|
||||
"application/json",
|
||||
"application/xml":
|
||||
bodyBuffer = bytes.NewBuffer([]byte(params))
|
||||
default:
|
||||
// It appends the parameters to the url
|
||||
// if http method is GET and Content-Type is not specified.
|
||||
if gstr.Contains(url, "?") {
|
||||
url = url + "&" + params
|
||||
} else {
|
||||
url = url + "?" + params
|
||||
}
|
||||
bodyBuffer = bytes.NewBuffer(nil)
|
||||
}
|
||||
} else {
|
||||
bodyBuffer = bytes.NewBuffer(nil)
|
||||
}
|
||||
if req, err = http.NewRequest(method, url, bytes.NewBuffer(nil)); err != nil {
|
||||
if req, err = http.NewRequest(method, url, bodyBuffer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if strings.Contains(param, "@file:") {
|
||||
if strings.Contains(params, "@file:") {
|
||||
// File uploading request.
|
||||
buffer := new(bytes.Buffer)
|
||||
writer := multipart.NewWriter(buffer)
|
||||
for _, item := range strings.Split(param, "&") {
|
||||
var (
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
writer = multipart.NewWriter(buffer)
|
||||
)
|
||||
for _, item := range strings.Split(params, "&") {
|
||||
array := strings.Split(item, "=")
|
||||
if len(array[1]) > 6 && strings.Compare(array[1][0:6], "@file:") == 0 {
|
||||
path := array[1][6:]
|
||||
@ -225,7 +227,7 @@ func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *h
|
||||
}
|
||||
} else {
|
||||
// Normal request.
|
||||
paramBytes := []byte(param)
|
||||
paramBytes := []byte(params)
|
||||
if req, err = http.NewRequest(method, url, bytes.NewReader(paramBytes)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
@ -236,7 +238,7 @@ func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *h
|
||||
if (paramBytes[0] == '[' || paramBytes[0] == '{') && json.Valid(paramBytes) {
|
||||
// Auto detecting and setting the post content format: JSON.
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
} else if gregex.IsMatchString(`^[\w\[\]]+=.+`, param) {
|
||||
} else if gregex.IsMatchString(`^[\w\[\]]+=.+`, params) {
|
||||
// If the parameters passed like "name=value", it then uses form type.
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
}
|
||||
|
||||
@ -12,9 +12,11 @@ import (
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"github.com/gogf/gf/net/ghttp/internal/httputil"
|
||||
"github.com/gogf/gf/net/gtrace"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/label"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
@ -22,7 +24,6 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
tracingMaxContentLogSize = 512 * 1024 // Max log size for request and response body.
|
||||
tracingInstrumentName = "github.com/gogf/gf/net/ghttp.Client"
|
||||
tracingAttrHttpAddressRemote = "http.address.remote"
|
||||
tracingAttrHttpAddressLocal = "http.address.local"
|
||||
@ -48,7 +49,7 @@ func MiddlewareTracing(c *Client, r *http.Request) (response *Response, err erro
|
||||
span.SetAttributes(gtrace.CommonLabels()...)
|
||||
|
||||
// Inject tracing content into http header.
|
||||
otel.GetTextMapPropagator().Inject(ctx, r.Header)
|
||||
otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(r.Header))
|
||||
|
||||
// Continue client handler executing.
|
||||
response, err = c.Next(
|
||||
@ -64,21 +65,17 @@ func MiddlewareTracing(c *Client, r *http.Request) (response *Response, err erro
|
||||
if response == nil || response.Response == nil {
|
||||
return
|
||||
}
|
||||
var resBodyContent string
|
||||
if response.ContentLength <= tracingMaxContentLogSize {
|
||||
reqBodyContentBytes, _ := ioutil.ReadAll(response.Body)
|
||||
resBodyContent = string(reqBodyContentBytes)
|
||||
response.Body = utils.NewReadCloser(reqBodyContentBytes, false)
|
||||
} else {
|
||||
resBodyContent = fmt.Sprintf(
|
||||
"[Response Body Too Large For Tracing, Max: %d bytes]",
|
||||
tracingMaxContentLogSize,
|
||||
)
|
||||
}
|
||||
|
||||
reqBodyContentBytes, _ := ioutil.ReadAll(response.Body)
|
||||
response.Body = utils.NewReadCloser(reqBodyContentBytes, false)
|
||||
|
||||
span.AddEvent(tracingEventHttpResponse, trace.WithAttributes(
|
||||
label.Any(tracingEventHttpResponseHeaders, httputil.HeaderToMap(response.Header)),
|
||||
label.String(tracingEventHttpResponseBody, resBodyContent),
|
||||
attribute.Any(tracingEventHttpResponseHeaders, httputil.HeaderToMap(response.Header)),
|
||||
attribute.String(tracingEventHttpResponseBody, gstr.StrLimit(
|
||||
string(reqBodyContentBytes),
|
||||
gtrace.MaxContentLogSize(),
|
||||
"...",
|
||||
)),
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
@ -12,8 +12,9 @@ import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"github.com/gogf/gf/net/gtrace"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/label"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
@ -39,11 +40,11 @@ func newClientTrace(ctx context.Context, span trace.Span, request *http.Request)
|
||||
request: request,
|
||||
headers: make(map[string]interface{}),
|
||||
}
|
||||
if ct.request.ContentLength <= tracingMaxContentLogSize {
|
||||
reqBodyContent, _ := ioutil.ReadAll(ct.request.Body)
|
||||
ct.requestBody = reqBodyContent
|
||||
ct.request.Body = utils.NewReadCloser(reqBodyContent, false)
|
||||
}
|
||||
|
||||
reqBodyContent, _ := ioutil.ReadAll(ct.request.Body)
|
||||
ct.requestBody = reqBodyContent
|
||||
ct.request.Body = utils.NewReadCloser(reqBodyContent, false)
|
||||
|
||||
return &httptrace.ClientTrace{
|
||||
GetConn: ct.getConn,
|
||||
GotConn: ct.gotConn,
|
||||
@ -70,8 +71,8 @@ func (ct *clientTracer) getConn(host string) {
|
||||
|
||||
func (ct *clientTracer) gotConn(info httptrace.GotConnInfo) {
|
||||
ct.span.SetAttributes(
|
||||
label.String(tracingAttrHttpAddressRemote, info.Conn.RemoteAddr().String()),
|
||||
label.String(tracingAttrHttpAddressLocal, info.Conn.LocalAddr().String()),
|
||||
attribute.String(tracingAttrHttpAddressRemote, info.Conn.RemoteAddr().String()),
|
||||
attribute.String(tracingAttrHttpAddressLocal, info.Conn.LocalAddr().String()),
|
||||
)
|
||||
}
|
||||
|
||||
@ -83,7 +84,7 @@ func (ct *clientTracer) putIdleConn(err error) {
|
||||
|
||||
func (ct *clientTracer) dnsStart(info httptrace.DNSStartInfo) {
|
||||
ct.span.SetAttributes(
|
||||
label.String(tracingAttrHttpDnsStart, info.Host),
|
||||
attribute.String(tracingAttrHttpDnsStart, info.Host),
|
||||
)
|
||||
}
|
||||
|
||||
@ -99,13 +100,13 @@ func (ct *clientTracer) dnsDone(info httptrace.DNSDoneInfo) {
|
||||
ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, info.Err))
|
||||
}
|
||||
ct.span.SetAttributes(
|
||||
label.String(tracingAttrHttpDnsDone, buffer.String()),
|
||||
attribute.String(tracingAttrHttpDnsDone, buffer.String()),
|
||||
)
|
||||
}
|
||||
|
||||
func (ct *clientTracer) connectStart(network, addr string) {
|
||||
ct.span.SetAttributes(
|
||||
label.String(tracingAttrHttpConnectStart, network+"@"+addr),
|
||||
attribute.String(tracingAttrHttpConnectStart, network+"@"+addr),
|
||||
)
|
||||
}
|
||||
|
||||
@ -114,7 +115,7 @@ func (ct *clientTracer) connectDone(network, addr string, err error) {
|
||||
ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err))
|
||||
}
|
||||
ct.span.SetAttributes(
|
||||
label.String(tracingAttrHttpConnectDone, network+"@"+addr),
|
||||
attribute.String(tracingAttrHttpConnectDone, network+"@"+addr),
|
||||
)
|
||||
}
|
||||
|
||||
@ -144,19 +145,15 @@ func (ct *clientTracer) wroteRequest(info httptrace.WroteRequestInfo) {
|
||||
if info.Err != nil {
|
||||
ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, info.Err))
|
||||
}
|
||||
var bodyContent string
|
||||
if ct.request.ContentLength <= tracingMaxContentLogSize {
|
||||
bodyContent = string(ct.requestBody)
|
||||
} else {
|
||||
bodyContent = fmt.Sprintf(
|
||||
"[Request Body Too Large For Logging, Max: %d bytes]",
|
||||
tracingMaxContentLogSize,
|
||||
)
|
||||
}
|
||||
|
||||
ct.span.AddEvent(tracingEventHttpRequest, trace.WithAttributes(
|
||||
label.Any(tracingEventHttpRequestHeaders, ct.headers),
|
||||
label.Any(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ct.Context)),
|
||||
label.String(tracingEventHttpRequestBody, bodyContent),
|
||||
attribute.Any(tracingEventHttpRequestHeaders, ct.headers),
|
||||
attribute.Any(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ct.Context)),
|
||||
attribute.String(tracingEventHttpRequestBody, gstr.StrLimit(
|
||||
string(ct.requestBody),
|
||||
gtrace.MaxContentLogSize(),
|
||||
"...",
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@ -9,11 +9,13 @@ package gtrace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/net/gipv4"
|
||||
"github.com/gogf/gf/os/gcmd"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/label"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"os"
|
||||
@ -23,12 +25,14 @@ import (
|
||||
const (
|
||||
tracingCommonKeyIpIntranet = `ip.intranet`
|
||||
tracingCommonKeyIpHostname = `hostname`
|
||||
cmdEnvKey = "gf.gtrace" // Configuration key for command argument or environment.
|
||||
)
|
||||
|
||||
var (
|
||||
intranetIps, _ = gipv4.GetIntranetIpArray()
|
||||
intranetIpStr = strings.Join(intranetIps, ",")
|
||||
hostname, _ = os.Hostname()
|
||||
intranetIps, _ = gipv4.GetIntranetIpArray()
|
||||
intranetIpStr = strings.Join(intranetIps, ",")
|
||||
hostname, _ = os.Hostname()
|
||||
tracingMaxContentLogSize = 256 * 1024 // Max log size for request and response body, especially for HTTP/RPC request.
|
||||
// defaultTextMapPropagator is the default propagator for context propagation between peers.
|
||||
defaultTextMapPropagator = propagation.NewCompositeTextMapPropagator(
|
||||
propagation.TraceContext{},
|
||||
@ -37,15 +41,23 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
if maxContentLogSize := gcmd.GetOptWithEnv(fmt.Sprintf("%s.maxcontentlogsize", cmdEnvKey)).Int(); maxContentLogSize > 0 {
|
||||
tracingMaxContentLogSize = maxContentLogSize
|
||||
}
|
||||
CheckSetDefaultTextMapPropagator()
|
||||
}
|
||||
|
||||
// MaxContentLogSize returns the max log size for request and response body, especially for HTTP/RPC request.
|
||||
func MaxContentLogSize() int {
|
||||
return tracingMaxContentLogSize
|
||||
}
|
||||
|
||||
// CommonLabels returns common used attribute labels:
|
||||
// ip.intranet, hostname.
|
||||
func CommonLabels() []label.KeyValue {
|
||||
return []label.KeyValue{
|
||||
label.String(tracingCommonKeyIpHostname, hostname),
|
||||
label.String(tracingCommonKeyIpIntranet, intranetIpStr),
|
||||
func CommonLabels() []attribute.KeyValue {
|
||||
return []attribute.KeyValue{
|
||||
attribute.String(tracingCommonKeyIpHostname, hostname),
|
||||
attribute.String(tracingCommonKeyIpIntranet, intranetIpStr),
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,7 +85,7 @@ func GetTraceId(ctx context.Context) string {
|
||||
if ctx == nil {
|
||||
return ""
|
||||
}
|
||||
traceId := trace.SpanContextFromContext(ctx).TraceID
|
||||
traceId := trace.SpanContextFromContext(ctx).TraceID()
|
||||
if traceId.IsValid() {
|
||||
return traceId.String()
|
||||
}
|
||||
@ -86,7 +98,7 @@ func GetSpanId(ctx context.Context) string {
|
||||
if ctx == nil {
|
||||
return ""
|
||||
}
|
||||
spanId := trace.SpanContextFromContext(ctx).SpanID
|
||||
spanId := trace.SpanContextFromContext(ctx).SpanID()
|
||||
if spanId.IsValid() {
|
||||
return spanId.String()
|
||||
}
|
||||
@ -94,13 +106,13 @@ func GetSpanId(ctx context.Context) string {
|
||||
}
|
||||
|
||||
// SetBaggageValue is a convenient function for adding one key-value pair to baggage.
|
||||
// Note that it uses label.Any to set the key-value pair.
|
||||
// Note that it uses attribute.Any to set the key-value pair.
|
||||
func SetBaggageValue(ctx context.Context, key string, value interface{}) context.Context {
|
||||
return NewBaggage(ctx).SetValue(key, value)
|
||||
}
|
||||
|
||||
// SetBaggageMap is a convenient function for adding map key-value pairs to baggage.
|
||||
// Note that it uses label.Any to set the key-value pair.
|
||||
// Note that it uses attribute.Any to set the key-value pair.
|
||||
func SetBaggageMap(ctx context.Context, data map[string]interface{}) context.Context {
|
||||
return NewBaggage(ctx).SetMap(data)
|
||||
}
|
||||
|
||||
@ -10,8 +10,8 @@ import (
|
||||
"context"
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/baggage"
|
||||
"go.opentelemetry.io/otel/label"
|
||||
)
|
||||
|
||||
// Baggage holds the data through all tracing spans.
|
||||
@ -35,18 +35,18 @@ func (b *Baggage) Ctx() context.Context {
|
||||
}
|
||||
|
||||
// SetValue is a convenient function for adding one key-value pair to baggage.
|
||||
// Note that it uses label.Any to set the key-value pair.
|
||||
// Note that it uses attribute.Any to set the key-value pair.
|
||||
func (b *Baggage) SetValue(key string, value interface{}) context.Context {
|
||||
b.ctx = baggage.ContextWithValues(b.ctx, label.Any(key, value))
|
||||
b.ctx = baggage.ContextWithValues(b.ctx, attribute.Any(key, value))
|
||||
return b.ctx
|
||||
}
|
||||
|
||||
// SetMap is a convenient function for adding map key-value pairs to baggage.
|
||||
// Note that it uses label.Any to set the key-value pair.
|
||||
// Note that it uses attribute.Any to set the key-value pair.
|
||||
func (b *Baggage) SetMap(data map[string]interface{}) context.Context {
|
||||
pairs := make([]label.KeyValue, 0)
|
||||
pairs := make([]attribute.KeyValue, 0)
|
||||
for k, v := range data {
|
||||
pairs = append(pairs, label.Any(k, v))
|
||||
pairs = append(pairs, attribute.Any(k, v))
|
||||
}
|
||||
b.ctx = baggage.ContextWithValues(b.ctx, pairs...)
|
||||
return b.ctx
|
||||
@ -70,6 +70,6 @@ func (b *Baggage) GetMap() *gmap.StrAnyMap {
|
||||
|
||||
// GetVar retrieves value and returns a *gvar.Var for specified key from baggage.
|
||||
func (b *Baggage) GetVar(key string) *gvar.Var {
|
||||
value := baggage.Value(b.ctx, label.Key(key))
|
||||
value := baggage.Value(b.ctx, attribute.Key(key))
|
||||
return gvar.New(value.AsInterface())
|
||||
}
|
||||
|
||||
@ -11,41 +11,49 @@ import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
type Carrier struct {
|
||||
data map[string]interface{}
|
||||
}
|
||||
// Carrier is the storage medium used by a TextMapPropagator.
|
||||
type Carrier map[string]interface{}
|
||||
|
||||
func NewCarrier(data ...map[string]interface{}) *Carrier {
|
||||
carrier := &Carrier{}
|
||||
func NewCarrier(data ...map[string]interface{}) Carrier {
|
||||
if len(data) > 0 && data[0] != nil {
|
||||
carrier.data = data[0]
|
||||
return data[0]
|
||||
} else {
|
||||
carrier.data = make(map[string]interface{})
|
||||
return make(map[string]interface{})
|
||||
}
|
||||
return carrier
|
||||
}
|
||||
|
||||
func (c *Carrier) Get(k string) string {
|
||||
return gconv.String(c.data[k])
|
||||
// Get returns the value associated with the passed key.
|
||||
func (c Carrier) Get(k string) string {
|
||||
return gconv.String(c[k])
|
||||
}
|
||||
|
||||
func (c *Carrier) Set(k, v string) {
|
||||
c.data[k] = v
|
||||
// Set stores the key-value pair.
|
||||
func (c Carrier) Set(k, v string) {
|
||||
c[k] = v
|
||||
}
|
||||
|
||||
func (c *Carrier) MustMarshal() []byte {
|
||||
b, err := json.Marshal(c.data)
|
||||
// Keys lists the keys stored in this carrier.
|
||||
func (c Carrier) Keys() []string {
|
||||
keys := make([]string, 0, len(c))
|
||||
for k := range c {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
func (c Carrier) MustMarshal() []byte {
|
||||
b, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (c *Carrier) String() string {
|
||||
func (c Carrier) String() string {
|
||||
return string(c.MustMarshal())
|
||||
}
|
||||
|
||||
func (c *Carrier) UnmarshalJSON(b []byte) error {
|
||||
func (c Carrier) UnmarshalJSON(b []byte) error {
|
||||
carrier := NewCarrier(nil)
|
||||
return json.Unmarshal(b, carrier.data)
|
||||
return json.Unmarshal(b, carrier)
|
||||
}
|
||||
|
||||
@ -46,11 +46,11 @@ func mustSpanIDFromHex(s string) (t trace.SpanID) {
|
||||
|
||||
func TestNewCarrier(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
ctx := trace.ContextWithRemoteSpanContext(context.Background(), trace.SpanContext{
|
||||
ctx := trace.ContextWithRemoteSpanContext(context.Background(), trace.NewSpanContext(trace.SpanContextConfig{
|
||||
TraceID: traceID,
|
||||
SpanID: spanID,
|
||||
TraceFlags: trace.FlagsSampled,
|
||||
})
|
||||
}))
|
||||
ctx, _ = oteltest.DefaultTracer().Start(ctx, "inject")
|
||||
carrier1 := gtrace.NewCarrier()
|
||||
otel.GetTextMapPropagator().Inject(ctx, carrier1)
|
||||
@ -58,7 +58,7 @@ func TestNewCarrier(t *testing.T) {
|
||||
|
||||
ctx = otel.GetTextMapPropagator().Extract(ctx, carrier1)
|
||||
gotSc := trace.RemoteSpanContextFromContext(ctx)
|
||||
t.Assert(gotSc.TraceID.String(), traceID.String())
|
||||
t.Assert(gotSc.SpanID.String(), "0000000000000002")
|
||||
t.Assert(gotSc.TraceID().String(), traceID.String())
|
||||
t.Assert(gotSc.SpanID().String(), "0000000000000002")
|
||||
})
|
||||
}
|
||||
|
||||
162
os/gcfg/gcfg.go
162
os/gcfg/gcfg.go
@ -11,9 +11,11 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"github.com/gogf/gf/os/gcmd"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gmode"
|
||||
|
||||
"github.com/gogf/gf/os/gres"
|
||||
|
||||
@ -45,7 +47,7 @@ var (
|
||||
)
|
||||
|
||||
// New returns a new configuration management object.
|
||||
// The parameter <file> specifies the default configuration file name for reading.
|
||||
// The parameter `file` specifies the default configuration file name for reading.
|
||||
func New(file ...string) *Config {
|
||||
name := DefaultConfigFile
|
||||
if len(file) > 0 {
|
||||
@ -67,7 +69,7 @@ func New(file ...string) *Config {
|
||||
_ = c.SetPath(customPath)
|
||||
} else {
|
||||
if errorPrint() {
|
||||
glog.Errorf("Configuration directory path does not exist: %s", customPath)
|
||||
glog.Errorf("[gcfg] Configuration directory path does not exist: %s", customPath)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -93,42 +95,8 @@ func New(file ...string) *Config {
|
||||
return c
|
||||
}
|
||||
|
||||
// filePath returns the absolute configuration file path for the given filename by <file>.
|
||||
func (c *Config) filePath(file ...string) (path string) {
|
||||
name := c.defaultName
|
||||
if len(file) > 0 {
|
||||
name = file[0]
|
||||
}
|
||||
path = c.FilePath(name)
|
||||
if path == "" {
|
||||
var (
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
)
|
||||
if c.searchPaths.Len() > 0 {
|
||||
buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" in following paths:", name))
|
||||
c.searchPaths.RLockFunc(func(array []string) {
|
||||
index := 1
|
||||
for _, v := range array {
|
||||
v = gstr.TrimRight(v, `\/`)
|
||||
buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v))
|
||||
index++
|
||||
buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v+gfile.Separator+"config"))
|
||||
index++
|
||||
}
|
||||
})
|
||||
|
||||
} else {
|
||||
buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" with no path set/add", name))
|
||||
}
|
||||
if errorPrint() {
|
||||
glog.Error(buffer.String())
|
||||
}
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
// SetPath sets the configuration directory path for file search.
|
||||
// The parameter <path> can be absolute or relative path,
|
||||
// The parameter `path` can be absolute or relative path,
|
||||
// but absolute path is strongly recommended.
|
||||
func (c *Config) SetPath(path string) error {
|
||||
var (
|
||||
@ -246,14 +214,14 @@ func (c *Config) AddPath(path string) error {
|
||||
} else {
|
||||
buffer.WriteString(fmt.Sprintf(`[gcfg] AddPath failed: path "%s" does not exist`, path))
|
||||
}
|
||||
err := errors.New(buffer.String())
|
||||
err := gerror.New(buffer.String())
|
||||
if errorPrint() {
|
||||
glog.Error(err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
if !isDir {
|
||||
err := fmt.Errorf(`[gcfg] AddPath failed: path "%s" should be directory type`, path)
|
||||
err := gerror.Newf(`[gcfg] AddPath failed: path "%s" should be directory type`, path)
|
||||
if errorPrint() {
|
||||
glog.Error(err)
|
||||
}
|
||||
@ -268,11 +236,38 @@ func (c *Config) AddPath(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFilePath returns the absolute path of the specified configuration file.
|
||||
// If <file> is not passed, it returns the configuration file path of the default name.
|
||||
// If the specified configuration file does not exist,
|
||||
// an empty string is returned.
|
||||
func (c *Config) FilePath(file ...string) (path string) {
|
||||
// SetFileName sets the default configuration file name.
|
||||
func (c *Config) SetFileName(name string) *Config {
|
||||
c.defaultName = name
|
||||
return c
|
||||
}
|
||||
|
||||
// GetFileName returns the default configuration file name.
|
||||
func (c *Config) GetFileName() string {
|
||||
return c.defaultName
|
||||
}
|
||||
|
||||
// Available checks and returns whether configuration of given `file` is available.
|
||||
func (c *Config) Available(file ...string) bool {
|
||||
var name string
|
||||
if len(file) > 0 && file[0] != "" {
|
||||
name = file[0]
|
||||
} else {
|
||||
name = c.defaultName
|
||||
}
|
||||
if path, _ := c.GetFilePath(name); path != "" {
|
||||
return true
|
||||
}
|
||||
if GetContent(name) != "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetFilePath returns the absolute configuration file path for the given filename by `file`.
|
||||
// If `file` is not passed, it returns the configuration file path of the default name.
|
||||
// It returns an empty `path` string and an error if the given `file` does not exist.
|
||||
func (c *Config) GetFilePath(file ...string) (path string, err error) {
|
||||
name := c.defaultName
|
||||
if len(file) > 0 {
|
||||
name = file[0]
|
||||
@ -296,6 +291,7 @@ func (c *Config) FilePath(file ...string) (path string) {
|
||||
}
|
||||
})
|
||||
}
|
||||
c.autoCheckAndAddMainPkgPathToSearchPaths()
|
||||
// Searching the file system.
|
||||
c.searchPaths.RLockFunc(func(array []string) {
|
||||
for _, prefix := range array {
|
||||
@ -308,38 +304,45 @@ func (c *Config) FilePath(file ...string) (path string) {
|
||||
}
|
||||
}
|
||||
})
|
||||
// If it cannot find the path of `file`, it formats and returns a detailed error.
|
||||
if path == "" {
|
||||
var (
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
)
|
||||
if c.searchPaths.Len() > 0 {
|
||||
buffer.WriteString(fmt.Sprintf(`[gcfg] cannot find config file "%s" in resource manager or the following paths:`, name))
|
||||
c.searchPaths.RLockFunc(func(array []string) {
|
||||
index := 1
|
||||
for _, v := range array {
|
||||
v = gstr.TrimRight(v, `\/`)
|
||||
buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v))
|
||||
index++
|
||||
buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v+gfile.Separator+"config"))
|
||||
index++
|
||||
}
|
||||
})
|
||||
} else {
|
||||
buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" with no path configured", name))
|
||||
}
|
||||
err = gerror.New(buffer.String())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SetFileName sets the default configuration file name.
|
||||
func (c *Config) SetFileName(name string) *Config {
|
||||
c.defaultName = name
|
||||
return c
|
||||
// autoCheckAndAddMainPkgPathToSearchPaths automatically checks and adds directory path of package main
|
||||
// to the searching path list if it's currently in development environment.
|
||||
func (c *Config) autoCheckAndAddMainPkgPathToSearchPaths() {
|
||||
if gmode.IsDevelop() {
|
||||
mainPkgPath := gfile.MainPkgPath()
|
||||
if mainPkgPath != "" {
|
||||
if !c.searchPaths.Contains(mainPkgPath) {
|
||||
c.searchPaths.Append(mainPkgPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetFileName returns the default configuration file name.
|
||||
func (c *Config) GetFileName() string {
|
||||
return c.defaultName
|
||||
}
|
||||
|
||||
// Available checks and returns whether configuration of given <file> is available.
|
||||
func (c *Config) Available(file ...string) bool {
|
||||
var name string
|
||||
if len(file) > 0 && file[0] != "" {
|
||||
name = file[0]
|
||||
} else {
|
||||
name = c.defaultName
|
||||
}
|
||||
if c.FilePath(name) != "" {
|
||||
return true
|
||||
}
|
||||
if GetContent(name) != "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// getJson returns a *gjson.Json object for the specified <file> content.
|
||||
// getJson returns a *gjson.Json object for the specified `file` content.
|
||||
// It would print error if file reading fails. It return nil if any error occurs.
|
||||
func (c *Config) getJson(file ...string) *gjson.Json {
|
||||
var name string
|
||||
@ -350,14 +353,18 @@ func (c *Config) getJson(file ...string) *gjson.Json {
|
||||
}
|
||||
r := c.jsonMap.GetOrSetFuncLock(name, func() interface{} {
|
||||
var (
|
||||
content = ""
|
||||
filePath = ""
|
||||
err error
|
||||
content string
|
||||
filePath string
|
||||
)
|
||||
// The configured content can be any kind of data type different from its file type.
|
||||
isFromConfigContent := true
|
||||
if content = GetContent(name); content == "" {
|
||||
isFromConfigContent = false
|
||||
filePath = c.filePath(name)
|
||||
filePath, err = c.GetFilePath(name)
|
||||
if err != nil && errorPrint() {
|
||||
glog.Error(err)
|
||||
}
|
||||
if filePath == "" {
|
||||
return nil
|
||||
}
|
||||
@ -369,8 +376,7 @@ func (c *Config) getJson(file ...string) *gjson.Json {
|
||||
}
|
||||
// Note that the underlying configuration json object operations are concurrent safe.
|
||||
var (
|
||||
j *gjson.Json
|
||||
err error
|
||||
j *gjson.Json
|
||||
)
|
||||
dataType := gfile.ExtName(name)
|
||||
if gjson.IsValidDataType(dataType) && !isFromConfigContent {
|
||||
@ -394,9 +400,9 @@ func (c *Config) getJson(file ...string) *gjson.Json {
|
||||
}
|
||||
if errorPrint() {
|
||||
if filePath != "" {
|
||||
glog.Criticalf(`[gcfg] Load config file "%s" failed: %s`, filePath, err.Error())
|
||||
glog.Criticalf(`[gcfg] load config file "%s" failed: %s`, filePath, err.Error())
|
||||
} else {
|
||||
glog.Criticalf(`[gcfg] Load configuration failed: %s`, err.Error())
|
||||
glog.Criticalf(`[gcfg] load configuration failed: %s`, err.Error())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@ -16,7 +16,7 @@ import (
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
)
|
||||
|
||||
// Set sets value with specified <pattern>.
|
||||
// Set sets value with specified `pattern`.
|
||||
// It supports hierarchical data access by char separator, which is '.' in default.
|
||||
// It is commonly used for updates certain configuration value in runtime.
|
||||
func (c *Config) Set(pattern string, value interface{}) error {
|
||||
@ -26,14 +26,14 @@ func (c *Config) Set(pattern string, value interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get retrieves and returns value by specified <pattern>.
|
||||
// It returns all values of current Json object if <pattern> is given empty or string ".".
|
||||
// It returns nil if no value found by <pattern>.
|
||||
// Get retrieves and returns value by specified `pattern`.
|
||||
// It returns all values of current Json object if `pattern` is given empty or string ".".
|
||||
// It returns nil if no value found by `pattern`.
|
||||
//
|
||||
// We can also access slice item by its index number in <pattern> like:
|
||||
// We can also access slice item by its index number in `pattern` like:
|
||||
// "list.10", "array.0.name", "array.0.1.id".
|
||||
//
|
||||
// It returns a default value specified by <def> if value for <pattern> is not found.
|
||||
// It returns a default value specified by `def` if value for `pattern` is not found.
|
||||
func (c *Config) Get(pattern string, def ...interface{}) interface{} {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.Get(pattern, def...)
|
||||
@ -41,7 +41,7 @@ func (c *Config) Get(pattern string, def ...interface{}) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetVar returns a gvar.Var with value by given <pattern>.
|
||||
// GetVar returns a gvar.Var with value by given `pattern`.
|
||||
func (c *Config) GetVar(pattern string, def ...interface{}) *gvar.Var {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetVar(pattern, def...)
|
||||
@ -49,7 +49,7 @@ func (c *Config) GetVar(pattern string, def ...interface{}) *gvar.Var {
|
||||
return gvar.New(nil)
|
||||
}
|
||||
|
||||
// Contains checks whether the value by specified <pattern> exist.
|
||||
// Contains checks whether the value by specified `pattern` exist.
|
||||
func (c *Config) Contains(pattern string) bool {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.Contains(pattern)
|
||||
@ -57,7 +57,7 @@ func (c *Config) Contains(pattern string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// GetMap retrieves and returns the value by specified <pattern> as map[string]interface{}.
|
||||
// GetMap retrieves and returns the value by specified `pattern` as map[string]interface{}.
|
||||
func (c *Config) GetMap(pattern string, def ...interface{}) map[string]interface{} {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetMap(pattern, def...)
|
||||
@ -65,7 +65,7 @@ func (c *Config) GetMap(pattern string, def ...interface{}) map[string]interface
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetMapStrStr retrieves and returns the value by specified <pattern> as map[string]string.
|
||||
// GetMapStrStr retrieves and returns the value by specified `pattern` as map[string]string.
|
||||
func (c *Config) GetMapStrStr(pattern string, def ...interface{}) map[string]string {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetMapStrStr(pattern, def...)
|
||||
@ -73,7 +73,7 @@ func (c *Config) GetMapStrStr(pattern string, def ...interface{}) map[string]str
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetArray retrieves the value by specified <pattern>,
|
||||
// GetArray retrieves the value by specified `pattern`,
|
||||
// and converts it to a slice of []interface{}.
|
||||
func (c *Config) GetArray(pattern string, def ...interface{}) []interface{} {
|
||||
if j := c.getJson(); j != nil {
|
||||
@ -82,7 +82,7 @@ func (c *Config) GetArray(pattern string, def ...interface{}) []interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBytes retrieves the value by specified <pattern> and converts it to []byte.
|
||||
// GetBytes retrieves the value by specified `pattern` and converts it to []byte.
|
||||
func (c *Config) GetBytes(pattern string, def ...interface{}) []byte {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetBytes(pattern, def...)
|
||||
@ -90,7 +90,7 @@ func (c *Config) GetBytes(pattern string, def ...interface{}) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetString retrieves the value by specified <pattern> and converts it to string.
|
||||
// GetString retrieves the value by specified `pattern` and converts it to string.
|
||||
func (c *Config) GetString(pattern string, def ...interface{}) string {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetString(pattern, def...)
|
||||
@ -98,7 +98,7 @@ func (c *Config) GetString(pattern string, def ...interface{}) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetStrings retrieves the value by specified <pattern> and converts it to []string.
|
||||
// GetStrings retrieves the value by specified `pattern` and converts it to []string.
|
||||
func (c *Config) GetStrings(pattern string, def ...interface{}) []string {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetStrings(pattern, def...)
|
||||
@ -115,7 +115,7 @@ func (c *Config) GetInterfaces(pattern string, def ...interface{}) []interface{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBool retrieves the value by specified <pattern>,
|
||||
// GetBool retrieves the value by specified `pattern`,
|
||||
// converts and returns it as bool.
|
||||
// It returns false when value is: "", 0, false, off, nil;
|
||||
// or returns true instead.
|
||||
@ -126,7 +126,7 @@ func (c *Config) GetBool(pattern string, def ...interface{}) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// GetFloat32 retrieves the value by specified <pattern> and converts it to float32.
|
||||
// GetFloat32 retrieves the value by specified `pattern` and converts it to float32.
|
||||
func (c *Config) GetFloat32(pattern string, def ...interface{}) float32 {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetFloat32(pattern, def...)
|
||||
@ -134,7 +134,7 @@ func (c *Config) GetFloat32(pattern string, def ...interface{}) float32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetFloat64 retrieves the value by specified <pattern> and converts it to float64.
|
||||
// GetFloat64 retrieves the value by specified `pattern` and converts it to float64.
|
||||
func (c *Config) GetFloat64(pattern string, def ...interface{}) float64 {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetFloat64(pattern, def...)
|
||||
@ -142,7 +142,7 @@ func (c *Config) GetFloat64(pattern string, def ...interface{}) float64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetFloats retrieves the value by specified <pattern> and converts it to []float64.
|
||||
// GetFloats retrieves the value by specified `pattern` and converts it to []float64.
|
||||
func (c *Config) GetFloats(pattern string, def ...interface{}) []float64 {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetFloats(pattern, def...)
|
||||
@ -150,7 +150,7 @@ func (c *Config) GetFloats(pattern string, def ...interface{}) []float64 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetInt retrieves the value by specified <pattern> and converts it to int.
|
||||
// GetInt retrieves the value by specified `pattern` and converts it to int.
|
||||
func (c *Config) GetInt(pattern string, def ...interface{}) int {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetInt(pattern, def...)
|
||||
@ -158,7 +158,7 @@ func (c *Config) GetInt(pattern string, def ...interface{}) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetInt8 retrieves the value by specified <pattern> and converts it to int8.
|
||||
// GetInt8 retrieves the value by specified `pattern` and converts it to int8.
|
||||
func (c *Config) GetInt8(pattern string, def ...interface{}) int8 {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetInt8(pattern, def...)
|
||||
@ -166,7 +166,7 @@ func (c *Config) GetInt8(pattern string, def ...interface{}) int8 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetInt16 retrieves the value by specified <pattern> and converts it to int16.
|
||||
// GetInt16 retrieves the value by specified `pattern` and converts it to int16.
|
||||
func (c *Config) GetInt16(pattern string, def ...interface{}) int16 {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetInt16(pattern, def...)
|
||||
@ -174,7 +174,7 @@ func (c *Config) GetInt16(pattern string, def ...interface{}) int16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetInt32 retrieves the value by specified <pattern> and converts it to int32.
|
||||
// GetInt32 retrieves the value by specified `pattern` and converts it to int32.
|
||||
func (c *Config) GetInt32(pattern string, def ...interface{}) int32 {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetInt32(pattern, def...)
|
||||
@ -182,7 +182,7 @@ func (c *Config) GetInt32(pattern string, def ...interface{}) int32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetInt64 retrieves the value by specified <pattern> and converts it to int64.
|
||||
// GetInt64 retrieves the value by specified `pattern` and converts it to int64.
|
||||
func (c *Config) GetInt64(pattern string, def ...interface{}) int64 {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetInt64(pattern, def...)
|
||||
@ -190,7 +190,7 @@ func (c *Config) GetInt64(pattern string, def ...interface{}) int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetInts retrieves the value by specified <pattern> and converts it to []int.
|
||||
// GetInts retrieves the value by specified `pattern` and converts it to []int.
|
||||
func (c *Config) GetInts(pattern string, def ...interface{}) []int {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetInts(pattern, def...)
|
||||
@ -198,7 +198,7 @@ func (c *Config) GetInts(pattern string, def ...interface{}) []int {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUint retrieves the value by specified <pattern> and converts it to uint.
|
||||
// GetUint retrieves the value by specified `pattern` and converts it to uint.
|
||||
func (c *Config) GetUint(pattern string, def ...interface{}) uint {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetUint(pattern, def...)
|
||||
@ -206,7 +206,7 @@ func (c *Config) GetUint(pattern string, def ...interface{}) uint {
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetUint8 retrieves the value by specified <pattern> and converts it to uint8.
|
||||
// GetUint8 retrieves the value by specified `pattern` and converts it to uint8.
|
||||
func (c *Config) GetUint8(pattern string, def ...interface{}) uint8 {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetUint8(pattern, def...)
|
||||
@ -214,7 +214,7 @@ func (c *Config) GetUint8(pattern string, def ...interface{}) uint8 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetUint16 retrieves the value by specified <pattern> and converts it to uint16.
|
||||
// GetUint16 retrieves the value by specified `pattern` and converts it to uint16.
|
||||
func (c *Config) GetUint16(pattern string, def ...interface{}) uint16 {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetUint16(pattern, def...)
|
||||
@ -222,7 +222,7 @@ func (c *Config) GetUint16(pattern string, def ...interface{}) uint16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetUint32 retrieves the value by specified <pattern> and converts it to uint32.
|
||||
// GetUint32 retrieves the value by specified `pattern` and converts it to uint32.
|
||||
func (c *Config) GetUint32(pattern string, def ...interface{}) uint32 {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetUint32(pattern, def...)
|
||||
@ -230,7 +230,7 @@ func (c *Config) GetUint32(pattern string, def ...interface{}) uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetUint64 retrieves the value by specified <pattern> and converts it to uint64.
|
||||
// GetUint64 retrieves the value by specified `pattern` and converts it to uint64.
|
||||
func (c *Config) GetUint64(pattern string, def ...interface{}) uint64 {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetUint64(pattern, def...)
|
||||
@ -238,7 +238,7 @@ func (c *Config) GetUint64(pattern string, def ...interface{}) uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetTime retrieves the value by specified <pattern> and converts it to time.Time.
|
||||
// GetTime retrieves the value by specified `pattern` and converts it to time.Time.
|
||||
func (c *Config) GetTime(pattern string, format ...string) time.Time {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetTime(pattern, format...)
|
||||
@ -246,7 +246,7 @@ func (c *Config) GetTime(pattern string, format ...string) time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
// GetDuration retrieves the value by specified <pattern> and converts it to time.Duration.
|
||||
// GetDuration retrieves the value by specified `pattern` and converts it to time.Duration.
|
||||
func (c *Config) GetDuration(pattern string, def ...interface{}) time.Duration {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetDuration(pattern, def...)
|
||||
@ -254,7 +254,7 @@ func (c *Config) GetDuration(pattern string, def ...interface{}) time.Duration {
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetGTime retrieves the value by specified <pattern> and converts it to *gtime.Time.
|
||||
// GetGTime retrieves the value by specified `pattern` and converts it to *gtime.Time.
|
||||
func (c *Config) GetGTime(pattern string, format ...string) *gtime.Time {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetGTime(pattern, format...)
|
||||
@ -262,7 +262,7 @@ func (c *Config) GetGTime(pattern string, format ...string) *gtime.Time {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetJson gets the value by specified <pattern>,
|
||||
// GetJson gets the value by specified `pattern`,
|
||||
// and converts it to a un-concurrent-safe Json object.
|
||||
func (c *Config) GetJson(pattern string, def ...interface{}) *gjson.Json {
|
||||
if j := c.getJson(); j != nil {
|
||||
@ -271,7 +271,7 @@ func (c *Config) GetJson(pattern string, def ...interface{}) *gjson.Json {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetJsons gets the value by specified <pattern>,
|
||||
// GetJsons gets the value by specified `pattern`,
|
||||
// and converts it to a slice of un-concurrent-safe Json object.
|
||||
func (c *Config) GetJsons(pattern string, def ...interface{}) []*gjson.Json {
|
||||
if j := c.getJson(); j != nil {
|
||||
@ -280,7 +280,7 @@ func (c *Config) GetJsons(pattern string, def ...interface{}) []*gjson.Json {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetJsonMap gets the value by specified <pattern>,
|
||||
// GetJsonMap gets the value by specified `pattern`,
|
||||
// and converts it to a map of un-concurrent-safe Json object.
|
||||
func (c *Config) GetJsonMap(pattern string, def ...interface{}) map[string]*gjson.Json {
|
||||
if j := c.getJson(); j != nil {
|
||||
@ -289,8 +289,8 @@ func (c *Config) GetJsonMap(pattern string, def ...interface{}) map[string]*gjso
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetStruct retrieves the value by specified <pattern> and converts it to specified object
|
||||
// <pointer>. The <pointer> should be the pointer to an object.
|
||||
// GetStruct retrieves the value by specified `pattern` and converts it to specified object
|
||||
// `pointer`. The `pointer` should be the pointer to an object.
|
||||
func (c *Config) GetStruct(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetStruct(pattern, pointer, mapping...)
|
||||
@ -324,7 +324,7 @@ func (c *Config) GetStructsDeep(pattern string, pointer interface{}, mapping ...
|
||||
return errors.New("configuration not found")
|
||||
}
|
||||
|
||||
// GetMapToMap retrieves the value by specified <pattern> and converts it to specified map variable.
|
||||
// GetMapToMap retrieves the value by specified `pattern` and converts it to specified map variable.
|
||||
// See gconv.MapToMap.
|
||||
func (c *Config) GetMapToMap(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
if j := c.getJson(); j != nil {
|
||||
@ -333,17 +333,7 @@ func (c *Config) GetMapToMap(pattern string, pointer interface{}, mapping ...map
|
||||
return errors.New("configuration not found")
|
||||
}
|
||||
|
||||
// GetMapToMapDeep retrieves the value by specified <pattern> and converts it to specified map
|
||||
// variable recursively.
|
||||
// See gconv.MapToMapDeep.
|
||||
func (c *Config) GetMapToMapDeep(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetMapToMapDeep(pattern, pointer, mapping...)
|
||||
}
|
||||
return errors.New("configuration not found")
|
||||
}
|
||||
|
||||
// GetMapToMaps retrieves the value by specified <pattern> and converts it to specified map slice
|
||||
// GetMapToMaps retrieves the value by specified `pattern` and converts it to specified map slice
|
||||
// variable.
|
||||
// See gconv.MapToMaps.
|
||||
func (c *Config) GetMapToMaps(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
@ -353,7 +343,7 @@ func (c *Config) GetMapToMaps(pattern string, pointer interface{}, mapping ...ma
|
||||
return errors.New("configuration not found")
|
||||
}
|
||||
|
||||
// GetMapToMapsDeep retrieves the value by specified <pattern> and converts it to specified map slice
|
||||
// GetMapToMapsDeep retrieves the value by specified `pattern` and converts it to specified map slice
|
||||
// variable recursively.
|
||||
// See gconv.MapToMapsDeep.
|
||||
func (c *Config) GetMapToMapsDeep(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
@ -382,7 +372,7 @@ func (c *Config) ToArray() []interface{} {
|
||||
}
|
||||
|
||||
// ToStruct converts current Json object to specified object.
|
||||
// The <pointer> should be a pointer type of *struct.
|
||||
// The `pointer` should be a pointer type of *struct.
|
||||
func (c *Config) ToStruct(pointer interface{}, mapping ...map[string]string) error {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.ToStruct(pointer, mapping...)
|
||||
@ -391,7 +381,7 @@ func (c *Config) ToStruct(pointer interface{}, mapping ...map[string]string) err
|
||||
}
|
||||
|
||||
// ToStructDeep converts current Json object to specified object recursively.
|
||||
// The <pointer> should be a pointer type of *struct.
|
||||
// The `pointer` should be a pointer type of *struct.
|
||||
func (c *Config) ToStructDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.ToStructDeep(pointer, mapping...)
|
||||
@ -400,7 +390,7 @@ func (c *Config) ToStructDeep(pointer interface{}, mapping ...map[string]string)
|
||||
}
|
||||
|
||||
// ToStructs converts current Json object to specified object slice.
|
||||
// The <pointer> should be a pointer type of []struct/*struct.
|
||||
// The `pointer` should be a pointer type of []struct/*struct.
|
||||
func (c *Config) ToStructs(pointer interface{}, mapping ...map[string]string) error {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.ToStructs(pointer, mapping...)
|
||||
@ -409,7 +399,7 @@ func (c *Config) ToStructs(pointer interface{}, mapping ...map[string]string) er
|
||||
}
|
||||
|
||||
// ToStructsDeep converts current Json object to specified object slice recursively.
|
||||
// The <pointer> should be a pointer type of []struct/*struct.
|
||||
// The `pointer` should be a pointer type of []struct/*struct.
|
||||
func (c *Config) ToStructsDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.ToStructsDeep(pointer, mapping...)
|
||||
@ -418,7 +408,7 @@ func (c *Config) ToStructsDeep(pointer interface{}, mapping ...map[string]string
|
||||
}
|
||||
|
||||
// ToMapToMap converts current Json object to specified map variable.
|
||||
// The parameter of <pointer> should be type of *map.
|
||||
// The parameter of `pointer` should be type of *map.
|
||||
func (c *Config) ToMapToMap(pointer interface{}, mapping ...map[string]string) error {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.ToMapToMap(pointer, mapping...)
|
||||
@ -426,17 +416,8 @@ func (c *Config) ToMapToMap(pointer interface{}, mapping ...map[string]string) e
|
||||
return errors.New("configuration not found")
|
||||
}
|
||||
|
||||
// ToMapToMapDeep converts current Json object to specified map variable recursively.
|
||||
// The parameter of <pointer> should be type of *map.
|
||||
func (c *Config) ToMapToMapDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.ToMapToMapDeep(pointer, mapping...)
|
||||
}
|
||||
return errors.New("configuration not found")
|
||||
}
|
||||
|
||||
// ToMapToMaps converts current Json object to specified map variable slice.
|
||||
// The parameter of <pointer> should be type of []map/*map.
|
||||
// The parameter of `pointer` should be type of []map/*map.
|
||||
func (c *Config) ToMapToMaps(pointer interface{}, mapping ...map[string]string) error {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.ToMapToMaps(pointer, mapping...)
|
||||
@ -445,7 +426,7 @@ func (c *Config) ToMapToMaps(pointer interface{}, mapping ...map[string]string)
|
||||
}
|
||||
|
||||
// ToMapToMapsDeep converts current Json object to specified map variable slice recursively.
|
||||
// The parameter of <pointer> should be type of []map/*map.
|
||||
// The parameter of `pointer` should be type of []map/*map.
|
||||
func (c *Config) ToMapToMapsDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.ToMapToMapsDeep(pointer, mapping...)
|
||||
|
||||
@ -16,14 +16,14 @@ var (
|
||||
configs = gmap.NewStrStrMap(true)
|
||||
)
|
||||
|
||||
// SetContent sets customized configuration content for specified <file>.
|
||||
// The <file> is unnecessary param, default is DefaultConfigFile.
|
||||
// SetContent sets customized configuration content for specified `file`.
|
||||
// The `file` is unnecessary param, default is DefaultConfigFile.
|
||||
func SetContent(content string, file ...string) {
|
||||
name := DefaultConfigFile
|
||||
if len(file) > 0 {
|
||||
name = file[0]
|
||||
}
|
||||
// Clear file cache for instances which cached <name>.
|
||||
// Clear file cache for instances which cached `name`.
|
||||
instances.LockFunc(func(m map[string]interface{}) {
|
||||
if configs.Contains(name) {
|
||||
for _, v := range m {
|
||||
@ -34,8 +34,8 @@ func SetContent(content string, file ...string) {
|
||||
})
|
||||
}
|
||||
|
||||
// GetContent returns customized configuration content for specified <file>.
|
||||
// The <file> is unnecessary param, default is DefaultConfigFile.
|
||||
// GetContent returns customized configuration content for specified `file`.
|
||||
// The `file` is unnecessary param, default is DefaultConfigFile.
|
||||
func GetContent(file ...string) string {
|
||||
name := DefaultConfigFile
|
||||
if len(file) > 0 {
|
||||
@ -44,14 +44,14 @@ func GetContent(file ...string) string {
|
||||
return configs.Get(name)
|
||||
}
|
||||
|
||||
// RemoveContent removes the global configuration with specified <file>.
|
||||
// If <name> is not passed, it removes configuration of the default group name.
|
||||
// RemoveContent removes the global configuration with specified `file`.
|
||||
// If `name` is not passed, it removes configuration of the default group name.
|
||||
func RemoveContent(file ...string) {
|
||||
name := DefaultConfigFile
|
||||
if len(file) > 0 {
|
||||
name = file[0]
|
||||
}
|
||||
// Clear file cache for instances which cached <name>.
|
||||
// Clear file cache for instances which cached `name`.
|
||||
instances.LockFunc(func(m map[string]interface{}) {
|
||||
if configs.Contains(name) {
|
||||
for _, v := range m {
|
||||
|
||||
@ -22,7 +22,7 @@ var (
|
||||
)
|
||||
|
||||
// Instance returns an instance of Config with default settings.
|
||||
// The parameter <name> is the name for the instance. But very note that, if the file "name.toml"
|
||||
// The parameter `name` is the name for the instance. But very note that, if the file "name.toml"
|
||||
// exists in the configuration directory, it then sets it as the default configuration file. The
|
||||
// toml file type is the default configuration file type.
|
||||
func Instance(name ...string) *Config {
|
||||
|
||||
@ -81,7 +81,8 @@ array = [1,2,3]
|
||||
"disk": "127.0.0.1:6379,0",
|
||||
"cache": "127.0.0.1:6379,1",
|
||||
})
|
||||
t.AssertEQ(c.FilePath(), gfile.Pwd()+gfile.Separator+path)
|
||||
filepath, _ := c.GetFilePath()
|
||||
t.AssertEQ(filepath, gfile.Pwd()+gfile.Separator+path)
|
||||
})
|
||||
}
|
||||
|
||||
@ -223,8 +224,8 @@ func Test_SetFileName(t *testing.T) {
|
||||
"disk": "127.0.0.1:6379,0",
|
||||
"cache": "127.0.0.1:6379,1",
|
||||
})
|
||||
t.AssertEQ(c.FilePath(), gfile.Pwd()+gfile.Separator+path)
|
||||
|
||||
filepath, _ := c.GetFilePath()
|
||||
t.AssertEQ(filepath, gfile.Pwd()+gfile.Separator+path)
|
||||
})
|
||||
}
|
||||
|
||||
@ -281,9 +282,9 @@ func TestCfg_AddPath(t *testing.T) {
|
||||
func TestCfg_FilePath(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
c := gcfg.New("config.yml")
|
||||
path := c.FilePath("tmp")
|
||||
path, _ := c.GetFilePath("tmp")
|
||||
t.Assert(path, "")
|
||||
path = c.FilePath("tmp")
|
||||
path, _ = c.GetFilePath("tmp")
|
||||
t.Assert(path, "")
|
||||
})
|
||||
}
|
||||
|
||||
@ -77,7 +77,8 @@ v4 = "1.234"
|
||||
"disk": "127.0.0.1:6379,0",
|
||||
"cache": "127.0.0.1:6379,1",
|
||||
})
|
||||
t.AssertEQ(c.FilePath(), gfile.Pwd()+gfile.Separator+path)
|
||||
filepath, _ := c.GetFilePath()
|
||||
t.AssertEQ(filepath, gfile.Pwd()+gfile.Separator+path)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -67,7 +67,7 @@ func GetArgAll() []string {
|
||||
}
|
||||
|
||||
// GetWithEnv is alias of GetOptWithEnv.
|
||||
// Deprecated.
|
||||
// Deprecated, use GetOptWithEnv instead.
|
||||
func GetWithEnv(key string, def ...interface{}) *gvar.Var {
|
||||
return GetOptWithEnv(key, def...)
|
||||
}
|
||||
|
||||
@ -343,10 +343,14 @@ func Name(path string) string {
|
||||
// Dir returns all but the last element of path, typically the path's directory.
|
||||
// After dropping the final element, Dir calls Clean on the path and trailing
|
||||
// slashes are removed.
|
||||
// If the path is empty, Dir returns ".".
|
||||
// If the path consists entirely of separators, Dir returns a single separator.
|
||||
// If the `path` is empty, Dir returns ".".
|
||||
// If the `path` is ".", Dir treats the path as current working directory.
|
||||
// If the `path` consists entirely of separators, Dir returns a single separator.
|
||||
// The returned path does not end in a separator unless it is the root directory.
|
||||
func Dir(path string) string {
|
||||
if path == "." {
|
||||
return filepath.Dir(RealPath(path))
|
||||
}
|
||||
return filepath.Dir(path)
|
||||
}
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ package gfile
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
@ -44,11 +45,16 @@ func MainPkgPath() string {
|
||||
if path != "" {
|
||||
return path
|
||||
}
|
||||
var lastFile string
|
||||
for i := 1; i < 10000; i++ {
|
||||
if pc, file, _, ok := runtime.Caller(i); ok {
|
||||
if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter {
|
||||
continue
|
||||
}
|
||||
if Ext(file) != ".go" {
|
||||
continue
|
||||
}
|
||||
lastFile = file
|
||||
// Check if it is called in package initialization function,
|
||||
// in which it here cannot retrieve main package path,
|
||||
// it so just returns that can make next check.
|
||||
@ -58,10 +64,7 @@ func MainPkgPath() string {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if Ext(file) != ".go" {
|
||||
continue
|
||||
}
|
||||
if gregex.IsMatchString(`package\s+main`, GetContents(file)) {
|
||||
if gregex.IsMatchString(`package\s+main\s+`, GetContents(file)) {
|
||||
mainPkgPath.Set(Dir(file))
|
||||
return Dir(file)
|
||||
}
|
||||
@ -69,5 +72,20 @@ func MainPkgPath() string {
|
||||
break
|
||||
}
|
||||
}
|
||||
// If it still cannot find the path of the package main,
|
||||
// it recursively searches the directory and its parents directory of the last go file.
|
||||
// It's usually necessary for uint testing cases of business project.
|
||||
if lastFile != "" {
|
||||
for path = Dir(lastFile); len(path) > 1 && Exists(path) && path[len(path)-1] != os.PathSeparator; {
|
||||
files, _ := ScanDir(path, "*.go")
|
||||
for _, v := range files {
|
||||
if gregex.IsMatchString(`package\s+main\s+`, GetContents(v)) {
|
||||
mainPkgPath.Set(path)
|
||||
return path
|
||||
}
|
||||
}
|
||||
path = Dir(path)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
@ -166,7 +166,7 @@ func (l *Logger) print(std io.Writer, lead string, values ...interface{}) {
|
||||
if l.ctx != nil {
|
||||
// Tracing values.
|
||||
spanCtx := trace.SpanContextFromContext(l.ctx)
|
||||
if traceId := spanCtx.TraceID; traceId.IsValid() {
|
||||
if traceId := spanCtx.TraceID(); traceId.IsValid() {
|
||||
buffer.WriteString(fmt.Sprintf("{TraceID:%s} ", traceId.String()))
|
||||
}
|
||||
// Context values.
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
package gsession
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
@ -48,10 +47,10 @@ func NewStorageFile(path ...string) *StorageFile {
|
||||
if len(path) > 0 && path[0] != "" {
|
||||
storagePath, _ = gfile.Search(path[0])
|
||||
if storagePath == "" {
|
||||
panic(fmt.Sprintf("'%s' does not exist", path[0]))
|
||||
panic(gerror.Newf("'%s' does not exist", path[0]))
|
||||
}
|
||||
if !gfile.IsWritable(storagePath) {
|
||||
panic(fmt.Sprintf("'%s' is not writable", path[0]))
|
||||
panic(gerror.Newf("'%s' is not writable", path[0]))
|
||||
}
|
||||
}
|
||||
if storagePath != "" {
|
||||
@ -65,26 +64,28 @@ func NewStorageFile(path ...string) *StorageFile {
|
||||
cryptoEnabled: DefaultStorageFileCryptoEnabled,
|
||||
updatingIdSet: gset.NewStrSet(true),
|
||||
}
|
||||
// Batch updates the TTL for session ids timely.
|
||||
gtimer.AddSingleton(DefaultStorageFileLoopInterval, func() {
|
||||
//intlog.Print("StorageFile.timer start")
|
||||
var (
|
||||
id string
|
||||
err error
|
||||
)
|
||||
for {
|
||||
if id = s.updatingIdSet.Pop(); id == "" {
|
||||
break
|
||||
}
|
||||
if err = s.doUpdateTTL(id); err != nil {
|
||||
intlog.Error(err)
|
||||
}
|
||||
}
|
||||
//intlog.Print("StorageFile.timer end")
|
||||
})
|
||||
|
||||
gtimer.AddSingleton(DefaultStorageFileLoopInterval, s.updateSessionTimely)
|
||||
return s
|
||||
}
|
||||
|
||||
// updateSessionTimely batch updates the TTL for sessions timely.
|
||||
func (s *StorageFile) updateSessionTimely() {
|
||||
var (
|
||||
id string
|
||||
err error
|
||||
)
|
||||
// Batch updating sessions.
|
||||
for {
|
||||
if id = s.updatingIdSet.Pop(); id == "" {
|
||||
break
|
||||
}
|
||||
if err = s.updateSessionTTl(id); err != nil {
|
||||
intlog.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetCryptoKey sets the crypto key for session storage.
|
||||
// The crypto key is used when crypto feature is enabled.
|
||||
func (s *StorageFile) SetCryptoKey(key []byte) {
|
||||
@ -229,9 +230,9 @@ func (s *StorageFile) UpdateTTL(id string, ttl time.Duration) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// doUpdateTTL updates the TTL for session id.
|
||||
func (s *StorageFile) doUpdateTTL(id string) error {
|
||||
intlog.Printf("StorageFile.doUpdateTTL: %s", id)
|
||||
// updateSessionTTL updates the TTL for specified session id.
|
||||
func (s *StorageFile) updateSessionTTl(id string) error {
|
||||
intlog.Printf("StorageFile.updateSession: %s", id)
|
||||
path := s.sessionFilePath(id)
|
||||
file, err := gfile.OpenWithFlag(path, os.O_WRONLY)
|
||||
if err != nil {
|
||||
|
||||
@ -341,7 +341,7 @@ func compareMap(value, expect interface{}) error {
|
||||
return fmt.Errorf(`[ASSERT] EXPECT MAP LENGTH %d == %d`, rvValue.Len(), rvExpect.Len())
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf(`[ASSERT] EXPECT VALUE TO BE A MAP`)
|
||||
return fmt.Errorf(`[ASSERT] EXPECT VALUE TO BE A MAP, BUT GIVEN "%s"`, rvValue.Kind())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@ -540,7 +540,7 @@ func SplitAndTrim(str, delimiter string, characterMask ...string) []string {
|
||||
|
||||
// SplitAndTrimSpace splits string <str> by a string <delimiter> to an array,
|
||||
// and calls TrimSpace to every element of this array.
|
||||
// Deprecated.
|
||||
// Deprecated, use SplitAndTrim instead.
|
||||
func SplitAndTrimSpace(str, delimiter string) []string {
|
||||
array := make([]string, 0)
|
||||
for _, v := range strings.Split(str, delimiter) {
|
||||
|
||||
@ -45,8 +45,8 @@ var (
|
||||
StructTagPriority = []string{"gconv", "param", "params", "c", "p", "json"}
|
||||
)
|
||||
|
||||
// Convert converts the variable <i> to the type <t>, the type <t> is specified by string.
|
||||
// The optional parameter <params> is used for additional necessary parameter for this conversion.
|
||||
// Convert converts the variable `i` to the type `t`, the type `t` is specified by string.
|
||||
// The optional parameter `params` is used for additional necessary parameter for this conversion.
|
||||
// It supports common types conversion as its conversion based on type name string.
|
||||
func Convert(any interface{}, t string, params ...interface{}) interface{} {
|
||||
switch t {
|
||||
@ -277,7 +277,7 @@ func Convert(any interface{}, t string, params ...interface{}) interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
// Byte converts <i> to byte.
|
||||
// Byte converts `i` to byte.
|
||||
func Byte(any interface{}) byte {
|
||||
if v, ok := any.(byte); ok {
|
||||
return v
|
||||
@ -285,7 +285,7 @@ func Byte(any interface{}) byte {
|
||||
return Uint8(any)
|
||||
}
|
||||
|
||||
// Bytes converts <i> to []byte.
|
||||
// Bytes converts `i` to []byte.
|
||||
func Bytes(any interface{}) []byte {
|
||||
if any == nil {
|
||||
return nil
|
||||
@ -296,11 +296,14 @@ func Bytes(any interface{}) []byte {
|
||||
case []byte:
|
||||
return value
|
||||
default:
|
||||
if f, ok := value.(apiBytes); ok {
|
||||
return f.Bytes()
|
||||
}
|
||||
return gbinary.Encode(any)
|
||||
}
|
||||
}
|
||||
|
||||
// Rune converts <i> to rune.
|
||||
// Rune converts `i` to rune.
|
||||
func Rune(any interface{}) rune {
|
||||
if v, ok := any.(rune); ok {
|
||||
return v
|
||||
@ -308,7 +311,7 @@ func Rune(any interface{}) rune {
|
||||
return rune(Int32(any))
|
||||
}
|
||||
|
||||
// Runes converts <i> to []rune.
|
||||
// Runes converts `i` to []rune.
|
||||
func Runes(any interface{}) []rune {
|
||||
if v, ok := any.([]rune); ok {
|
||||
return v
|
||||
@ -316,7 +319,7 @@ func Runes(any interface{}) []rune {
|
||||
return []rune(String(any))
|
||||
}
|
||||
|
||||
// String converts <i> to string.
|
||||
// String converts `i` to string.
|
||||
// It's most common used converting function.
|
||||
func String(any interface{}) string {
|
||||
if any == nil {
|
||||
@ -419,8 +422,8 @@ func String(any interface{}) string {
|
||||
}
|
||||
}
|
||||
|
||||
// Bool converts <i> to bool.
|
||||
// It returns false if <i> is: false, "", 0, "false", "off", "no", empty slice/map.
|
||||
// Bool converts `i` to bool.
|
||||
// It returns false if `i` is: false, "", 0, "false", "off", "no", empty slice/map.
|
||||
func Bool(any interface{}) bool {
|
||||
if any == nil {
|
||||
return false
|
||||
@ -439,6 +442,9 @@ func Bool(any interface{}) bool {
|
||||
}
|
||||
return true
|
||||
default:
|
||||
if f, ok := value.(apiBool); ok {
|
||||
return f.Bool()
|
||||
}
|
||||
rv := reflect.ValueOf(any)
|
||||
switch rv.Kind() {
|
||||
case reflect.Ptr:
|
||||
@ -461,7 +467,7 @@ func Bool(any interface{}) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// Int converts <i> to int.
|
||||
// Int converts `i` to int.
|
||||
func Int(any interface{}) int {
|
||||
if any == nil {
|
||||
return 0
|
||||
@ -472,7 +478,7 @@ func Int(any interface{}) int {
|
||||
return int(Int64(any))
|
||||
}
|
||||
|
||||
// Int8 converts <i> to int8.
|
||||
// Int8 converts `i` to int8.
|
||||
func Int8(any interface{}) int8 {
|
||||
if any == nil {
|
||||
return 0
|
||||
@ -483,7 +489,7 @@ func Int8(any interface{}) int8 {
|
||||
return int8(Int64(any))
|
||||
}
|
||||
|
||||
// Int16 converts <i> to int16.
|
||||
// Int16 converts `i` to int16.
|
||||
func Int16(any interface{}) int16 {
|
||||
if any == nil {
|
||||
return 0
|
||||
@ -494,7 +500,7 @@ func Int16(any interface{}) int16 {
|
||||
return int16(Int64(any))
|
||||
}
|
||||
|
||||
// Int32 converts <i> to int32.
|
||||
// Int32 converts `i` to int32.
|
||||
func Int32(any interface{}) int32 {
|
||||
if any == nil {
|
||||
return 0
|
||||
@ -505,7 +511,7 @@ func Int32(any interface{}) int32 {
|
||||
return int32(Int64(any))
|
||||
}
|
||||
|
||||
// Int64 converts <i> to int64.
|
||||
// Int64 converts `i` to int64.
|
||||
func Int64(any interface{}) int64 {
|
||||
if any == nil {
|
||||
return 0
|
||||
@ -543,6 +549,9 @@ func Int64(any interface{}) int64 {
|
||||
case []byte:
|
||||
return gbinary.DecodeToInt64(value)
|
||||
default:
|
||||
if f, ok := value.(apiInt64); ok {
|
||||
return f.Int64()
|
||||
}
|
||||
s := String(value)
|
||||
isMinus := false
|
||||
if len(s) > 0 {
|
||||
@ -583,7 +592,7 @@ func Int64(any interface{}) int64 {
|
||||
}
|
||||
}
|
||||
|
||||
// Uint converts <i> to uint.
|
||||
// Uint converts `i` to uint.
|
||||
func Uint(any interface{}) uint {
|
||||
if any == nil {
|
||||
return 0
|
||||
@ -594,7 +603,7 @@ func Uint(any interface{}) uint {
|
||||
return uint(Uint64(any))
|
||||
}
|
||||
|
||||
// Uint8 converts <i> to uint8.
|
||||
// Uint8 converts `i` to uint8.
|
||||
func Uint8(any interface{}) uint8 {
|
||||
if any == nil {
|
||||
return 0
|
||||
@ -605,7 +614,7 @@ func Uint8(any interface{}) uint8 {
|
||||
return uint8(Uint64(any))
|
||||
}
|
||||
|
||||
// Uint16 converts <i> to uint16.
|
||||
// Uint16 converts `i` to uint16.
|
||||
func Uint16(any interface{}) uint16 {
|
||||
if any == nil {
|
||||
return 0
|
||||
@ -616,7 +625,7 @@ func Uint16(any interface{}) uint16 {
|
||||
return uint16(Uint64(any))
|
||||
}
|
||||
|
||||
// Uint32 converts <i> to uint32.
|
||||
// Uint32 converts `i` to uint32.
|
||||
func Uint32(any interface{}) uint32 {
|
||||
if any == nil {
|
||||
return 0
|
||||
@ -627,7 +636,7 @@ func Uint32(any interface{}) uint32 {
|
||||
return uint32(Uint64(any))
|
||||
}
|
||||
|
||||
// Uint64 converts <i> to uint64.
|
||||
// Uint64 converts `i` to uint64.
|
||||
func Uint64(any interface{}) uint64 {
|
||||
if any == nil {
|
||||
return 0
|
||||
@ -665,6 +674,9 @@ func Uint64(any interface{}) uint64 {
|
||||
case []byte:
|
||||
return gbinary.DecodeToUint64(value)
|
||||
default:
|
||||
if f, ok := value.(apiUint64); ok {
|
||||
return f.Uint64()
|
||||
}
|
||||
s := String(value)
|
||||
// Hexadecimal
|
||||
if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') {
|
||||
@ -687,7 +699,7 @@ func Uint64(any interface{}) uint64 {
|
||||
}
|
||||
}
|
||||
|
||||
// Float32 converts <i> to float32.
|
||||
// Float32 converts `i` to float32.
|
||||
func Float32(any interface{}) float32 {
|
||||
if any == nil {
|
||||
return 0
|
||||
@ -700,12 +712,15 @@ func Float32(any interface{}) float32 {
|
||||
case []byte:
|
||||
return gbinary.DecodeToFloat32(value)
|
||||
default:
|
||||
if f, ok := value.(apiFloat32); ok {
|
||||
return f.Float32()
|
||||
}
|
||||
v, _ := strconv.ParseFloat(String(any), 64)
|
||||
return float32(v)
|
||||
}
|
||||
}
|
||||
|
||||
// Float64 converts <i> to float64.
|
||||
// Float64 converts `i` to float64.
|
||||
func Float64(any interface{}) float64 {
|
||||
if any == nil {
|
||||
return 0
|
||||
@ -718,6 +733,9 @@ func Float64(any interface{}) float64 {
|
||||
case []byte:
|
||||
return gbinary.DecodeToFloat64(value)
|
||||
default:
|
||||
if f, ok := value.(apiFloat64); ok {
|
||||
return f.Float64()
|
||||
}
|
||||
v, _ := strconv.ParseFloat(String(any), 64)
|
||||
return v
|
||||
}
|
||||
|
||||
@ -11,11 +11,41 @@ type apiString interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
// apiBool is used for type assert api for Bool().
|
||||
type apiBool interface {
|
||||
Bool() bool
|
||||
}
|
||||
|
||||
// apiInt64 is used for type assert api for Int64().
|
||||
type apiInt64 interface {
|
||||
Int64() int64
|
||||
}
|
||||
|
||||
// apiUint64 is used for type assert api for Uint64().
|
||||
type apiUint64 interface {
|
||||
Uint64() uint64
|
||||
}
|
||||
|
||||
// apiFloat32 is used for type assert api for Float32().
|
||||
type apiFloat32 interface {
|
||||
Float32() float32
|
||||
}
|
||||
|
||||
// apiFloat64 is used for type assert api for Float64().
|
||||
type apiFloat64 interface {
|
||||
Float64() float64
|
||||
}
|
||||
|
||||
// apiError is used for type assert api for Error().
|
||||
type apiError interface {
|
||||
Error() string
|
||||
}
|
||||
|
||||
// apiBytes is used for type assert api for Bytes().
|
||||
type apiBytes interface {
|
||||
Bytes() []byte
|
||||
}
|
||||
|
||||
// apiInterfaces is used for type assert api for Interfaces().
|
||||
type apiInterfaces interface {
|
||||
Interfaces() []interface{}
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"reflect"
|
||||
"strings"
|
||||
@ -16,17 +15,17 @@ import (
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
)
|
||||
|
||||
// Map converts any variable <value> to map[string]interface{}. If the parameter <value> is not a
|
||||
// Map converts any variable `value` to map[string]interface{}. If the parameter `value` is not a
|
||||
// map/struct/*struct type, then the conversion will fail and returns nil.
|
||||
//
|
||||
// If <value> is a struct/*struct object, the second parameter <tags> specifies the most priority
|
||||
// If `value` is a struct/*struct object, the second parameter `tags` specifies the most priority
|
||||
// tags that will be detected, otherwise it detects the tags in order of:
|
||||
// gconv, json, field name.
|
||||
func Map(value interface{}, tags ...string) map[string]interface{} {
|
||||
return doMapConvert(value, false, tags...)
|
||||
}
|
||||
|
||||
// MapDeep does Map function recursively, which means if the attribute of <value>
|
||||
// MapDeep does Map function recursively, which means if the attribute of `value`
|
||||
// is also a struct/*struct, calls Map function on this attribute converting it to
|
||||
// a map[string]interface{} type variable.
|
||||
// Also see Map.
|
||||
@ -35,7 +34,7 @@ func MapDeep(value interface{}, tags ...string) map[string]interface{} {
|
||||
}
|
||||
|
||||
// doMapConvert implements the map converting.
|
||||
// It automatically checks and converts json string to map if <value> is string/[]byte.
|
||||
// It automatically checks and converts json string to map if `value` is string/[]byte.
|
||||
//
|
||||
// TODO completely implement the recursive converting for all types, especially the map.
|
||||
func doMapConvert(value interface{}, recursive bool, tags ...string) map[string]interface{} {
|
||||
@ -154,7 +153,7 @@ func doMapConvert(value interface{}, recursive bool, tags ...string) map[string]
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
switch reflectKind {
|
||||
// If <value> is type of array, it converts the value of even number index as its key and
|
||||
// If `value` is type of array, it converts the value of even number index as its key and
|
||||
// the value of odd number index as its corresponding value, for example:
|
||||
// []string{"k1","v1","k2","v2"} => map[string]interface{}{"k1":"v1", "k2":"v2"}
|
||||
// []string{"k1","v1","k2"} => map[string]interface{}{"k1":"v1", "k2":nil}
|
||||
@ -354,7 +353,7 @@ func doMapConvertForMapOrStructValue(isRoot bool, value interface{}, recursive b
|
||||
return value
|
||||
}
|
||||
|
||||
// MapStrStr converts <value> to map[string]string.
|
||||
// MapStrStr converts `value` to map[string]string.
|
||||
// Note that there might be data copy for this map type converting.
|
||||
func MapStrStr(value interface{}, tags ...string) map[string]string {
|
||||
if r, ok := value.(map[string]string); ok {
|
||||
@ -371,7 +370,7 @@ func MapStrStr(value interface{}, tags ...string) map[string]string {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MapStrStrDeep converts <value> to map[string]string recursively.
|
||||
// MapStrStrDeep converts `value` to map[string]string recursively.
|
||||
// Note that there might be data copy for this map type converting.
|
||||
func MapStrStrDeep(value interface{}, tags ...string) map[string]string {
|
||||
if r, ok := value.(map[string]string); ok {
|
||||
@ -387,196 +386,3 @@ func MapStrStrDeep(value interface{}, tags ...string) map[string]string {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MapToMap converts any map type variable <params> to another map type variable <pointer>
|
||||
// using reflect.
|
||||
// See doMapToMap.
|
||||
func MapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) error {
|
||||
return doMapToMap(params, pointer, mapping...)
|
||||
}
|
||||
|
||||
// MapToMapDeep converts any map type variable <params> to another map type variable <pointer>
|
||||
// using reflect recursively.
|
||||
// Deprecated, use MapToMap instead.
|
||||
func MapToMapDeep(params interface{}, pointer interface{}, mapping ...map[string]string) error {
|
||||
return doMapToMap(params, pointer, mapping...)
|
||||
}
|
||||
|
||||
// doMapToMap converts any map type variable <params> to another map type variable <pointer>.
|
||||
//
|
||||
// The parameter <params> can be any type of map, like:
|
||||
// map[string]string, map[string]struct, , map[string]*struct, etc.
|
||||
//
|
||||
// The parameter <pointer> should be type of *map, like:
|
||||
// map[int]string, map[string]struct, , map[string]*struct, etc.
|
||||
//
|
||||
// The optional parameter <mapping> is used for struct attribute to map key mapping, which makes
|
||||
// sense only if the items of original map <params> is type struct.
|
||||
func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
var (
|
||||
paramsRv = reflect.ValueOf(params)
|
||||
paramsKind = paramsRv.Kind()
|
||||
)
|
||||
if paramsKind == reflect.Ptr {
|
||||
paramsRv = paramsRv.Elem()
|
||||
paramsKind = paramsRv.Kind()
|
||||
}
|
||||
if paramsKind != reflect.Map {
|
||||
return gerror.New("params should be type of map")
|
||||
}
|
||||
// Empty params map, no need continue.
|
||||
if paramsRv.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
var pointerRv reflect.Value
|
||||
if v, ok := pointer.(reflect.Value); ok {
|
||||
pointerRv = v
|
||||
} else {
|
||||
pointerRv = reflect.ValueOf(pointer)
|
||||
}
|
||||
pointerKind := pointerRv.Kind()
|
||||
for pointerKind == reflect.Ptr {
|
||||
pointerRv = pointerRv.Elem()
|
||||
pointerKind = pointerRv.Kind()
|
||||
}
|
||||
if pointerKind != reflect.Map {
|
||||
return gerror.New("pointer should be type of *map")
|
||||
}
|
||||
defer func() {
|
||||
// Catch the panic, especially the reflect operation panics.
|
||||
if exception := recover(); exception != nil {
|
||||
if e, ok := exception.(errorStack); ok {
|
||||
err = e
|
||||
} else {
|
||||
err = gerror.NewSkipf(1, "%v", exception)
|
||||
}
|
||||
}
|
||||
}()
|
||||
var (
|
||||
paramsKeys = paramsRv.MapKeys()
|
||||
pointerKeyType = pointerRv.Type().Key()
|
||||
pointerValueType = pointerRv.Type().Elem()
|
||||
pointerValueKind = pointerValueType.Kind()
|
||||
dataMap = reflect.MakeMapWithSize(pointerRv.Type(), len(paramsKeys))
|
||||
)
|
||||
// Retrieve the true element type of target map.
|
||||
if pointerValueKind == reflect.Ptr {
|
||||
pointerValueKind = pointerValueType.Elem().Kind()
|
||||
}
|
||||
for _, key := range paramsKeys {
|
||||
e := reflect.New(pointerValueType).Elem()
|
||||
switch pointerValueKind {
|
||||
case reflect.Map, reflect.Struct:
|
||||
if err = Struct(paramsRv.MapIndex(key).Interface(), e, mapping...); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
e.Set(
|
||||
reflect.ValueOf(
|
||||
Convert(
|
||||
paramsRv.MapIndex(key).Interface(),
|
||||
pointerValueType.String(),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
dataMap.SetMapIndex(
|
||||
reflect.ValueOf(
|
||||
Convert(
|
||||
key.Interface(),
|
||||
pointerKeyType.Name(),
|
||||
),
|
||||
),
|
||||
e,
|
||||
)
|
||||
}
|
||||
pointerRv.Set(dataMap)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MapToMaps converts any map type variable <params> to another map type variable <pointer>.
|
||||
// See doMapToMaps.
|
||||
func MapToMaps(params interface{}, pointer interface{}, mapping ...map[string]string) error {
|
||||
return doMapToMaps(params, pointer, mapping...)
|
||||
}
|
||||
|
||||
// MapToMapsDeep converts any map type variable <params> to another map type variable
|
||||
// <pointer> recursively.
|
||||
// Deprecated, use MapToMaps instead.
|
||||
func MapToMapsDeep(params interface{}, pointer interface{}, mapping ...map[string]string) error {
|
||||
return doMapToMaps(params, pointer, mapping...)
|
||||
}
|
||||
|
||||
// doMapToMaps converts any map type variable <params> to another map type variable <pointer>.
|
||||
//
|
||||
// The parameter <params> can be any type of map, of which the item type is slice map, like:
|
||||
// map[int][]map, map[string][]map.
|
||||
//
|
||||
// The parameter <pointer> should be type of *map, of which the item type is slice map, like:
|
||||
// map[string][]struct, map[string][]*struct.
|
||||
//
|
||||
// The optional parameter <mapping> is used for struct attribute to map key mapping, which makes
|
||||
// sense only if the items of original map is type struct.
|
||||
//
|
||||
// TODO it's supposed supporting target type <pointer> like: map[int][]map, map[string][]map.
|
||||
func doMapToMaps(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
var (
|
||||
paramsRv = reflect.ValueOf(params)
|
||||
paramsKind = paramsRv.Kind()
|
||||
)
|
||||
if paramsKind == reflect.Ptr {
|
||||
paramsRv = paramsRv.Elem()
|
||||
paramsKind = paramsRv.Kind()
|
||||
}
|
||||
if paramsKind != reflect.Map {
|
||||
return gerror.New("params should be type of map")
|
||||
}
|
||||
// Empty params map, no need continue.
|
||||
if paramsRv.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
pointerRv = reflect.ValueOf(pointer)
|
||||
pointerKind = pointerRv.Kind()
|
||||
)
|
||||
for pointerKind == reflect.Ptr {
|
||||
pointerRv = pointerRv.Elem()
|
||||
pointerKind = pointerRv.Kind()
|
||||
}
|
||||
if pointerKind != reflect.Map {
|
||||
return gerror.New("pointer should be type of *map/**map")
|
||||
}
|
||||
defer func() {
|
||||
// Catch the panic, especially the reflect operation panics.
|
||||
if exception := recover(); exception != nil {
|
||||
if e, ok := exception.(errorStack); ok {
|
||||
err = e
|
||||
} else {
|
||||
err = gerror.NewSkipf(1, "%v", exception)
|
||||
}
|
||||
}
|
||||
}()
|
||||
var (
|
||||
paramsKeys = paramsRv.MapKeys()
|
||||
pointerKeyType = pointerRv.Type().Key()
|
||||
pointerValueType = pointerRv.Type().Elem()
|
||||
dataMap = reflect.MakeMapWithSize(pointerRv.Type(), len(paramsKeys))
|
||||
)
|
||||
for _, key := range paramsKeys {
|
||||
e := reflect.New(pointerValueType).Elem()
|
||||
if err = Structs(paramsRv.MapIndex(key).Interface(), e.Addr(), mapping...); err != nil {
|
||||
return err
|
||||
}
|
||||
dataMap.SetMapIndex(
|
||||
reflect.ValueOf(
|
||||
Convert(
|
||||
key.Interface(),
|
||||
pointerKeyType.Name(),
|
||||
),
|
||||
),
|
||||
e,
|
||||
)
|
||||
}
|
||||
pointerRv.Set(dataMap)
|
||||
return nil
|
||||
}
|
||||
|
||||
119
util/gconv/gconv_maps.go
Normal file
119
util/gconv/gconv_maps.go
Normal file
@ -0,0 +1,119 @@
|
||||
// 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.
|
||||
|
||||
package gconv
|
||||
|
||||
import "github.com/gogf/gf/internal/json"
|
||||
|
||||
// SliceMap is alias of Maps.
|
||||
func SliceMap(any interface{}) []map[string]interface{} {
|
||||
return Maps(any)
|
||||
}
|
||||
|
||||
// SliceMapDeep is alias of MapsDeep.
|
||||
func SliceMapDeep(any interface{}) []map[string]interface{} {
|
||||
return MapsDeep(any)
|
||||
}
|
||||
|
||||
// SliceStruct is alias of Structs.
|
||||
func SliceStruct(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
return Structs(params, pointer, mapping...)
|
||||
}
|
||||
|
||||
// Maps converts `i` to []map[string]interface{}.
|
||||
// Note that it automatically checks and converts json string to []map if `value` is string/[]byte.
|
||||
func Maps(value interface{}, tags ...string) []map[string]interface{} {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
switch r := value.(type) {
|
||||
case string:
|
||||
list := make([]map[string]interface{}, 0)
|
||||
if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' {
|
||||
if err := json.Unmarshal([]byte(r), &list); err != nil {
|
||||
return nil
|
||||
}
|
||||
return list
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
case []byte:
|
||||
list := make([]map[string]interface{}, 0)
|
||||
if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' {
|
||||
if err := json.Unmarshal(r, &list); err != nil {
|
||||
return nil
|
||||
}
|
||||
return list
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
case []map[string]interface{}:
|
||||
return r
|
||||
|
||||
default:
|
||||
array := Interfaces(value)
|
||||
if len(array) == 0 {
|
||||
return nil
|
||||
}
|
||||
list := make([]map[string]interface{}, len(array))
|
||||
for k, v := range array {
|
||||
list[k] = Map(v, tags...)
|
||||
}
|
||||
return list
|
||||
}
|
||||
}
|
||||
|
||||
// MapsDeep converts `i` to []map[string]interface{} recursively.
|
||||
//
|
||||
// TODO completely implement the recursive converting for all types.
|
||||
func MapsDeep(value interface{}, tags ...string) []map[string]interface{} {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
switch r := value.(type) {
|
||||
case string:
|
||||
list := make([]map[string]interface{}, 0)
|
||||
if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' {
|
||||
if err := json.Unmarshal([]byte(r), &list); err != nil {
|
||||
return nil
|
||||
}
|
||||
return list
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
case []byte:
|
||||
list := make([]map[string]interface{}, 0)
|
||||
if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' {
|
||||
if err := json.Unmarshal(r, &list); err != nil {
|
||||
return nil
|
||||
}
|
||||
return list
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
case []map[string]interface{}:
|
||||
list := make([]map[string]interface{}, len(r))
|
||||
for k, v := range r {
|
||||
list[k] = MapDeep(v, tags...)
|
||||
}
|
||||
return list
|
||||
|
||||
default:
|
||||
array := Interfaces(value)
|
||||
if len(array) == 0 {
|
||||
return nil
|
||||
}
|
||||
list := make([]map[string]interface{}, len(array))
|
||||
for k, v := range array {
|
||||
list[k] = MapDeep(v, tags...)
|
||||
}
|
||||
return list
|
||||
}
|
||||
}
|
||||
141
util/gconv/gconv_maptomap.go
Normal file
141
util/gconv/gconv_maptomap.go
Normal file
@ -0,0 +1,141 @@
|
||||
// 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.
|
||||
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// MapToMap converts any map type variable `params` to another map type variable `pointer`
|
||||
// using reflect.
|
||||
// See doMapToMap.
|
||||
func MapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) error {
|
||||
return doMapToMap(params, pointer, mapping...)
|
||||
}
|
||||
|
||||
// doMapToMap converts any map type variable `params` to another map type variable `pointer`.
|
||||
//
|
||||
// The parameter `params` can be any type of map, like:
|
||||
// map[string]string, map[string]struct, , map[string]*struct, etc.
|
||||
//
|
||||
// The parameter `pointer` should be type of *map, like:
|
||||
// map[int]string, map[string]struct, , map[string]*struct, etc.
|
||||
//
|
||||
// The optional parameter `mapping` is used for struct attribute to map key mapping, which makes
|
||||
// sense only if the items of original map `params` is type struct.
|
||||
func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
// If given `params` is JSON, it then uses json.Unmarshal doing the converting.
|
||||
switch r := params.(type) {
|
||||
case []byte:
|
||||
if json.Valid(r) {
|
||||
if rv, ok := pointer.(reflect.Value); ok {
|
||||
if rv.Kind() == reflect.Ptr {
|
||||
return json.Unmarshal(r, rv.Interface())
|
||||
}
|
||||
} else {
|
||||
return json.Unmarshal(r, pointer)
|
||||
}
|
||||
}
|
||||
case string:
|
||||
if paramsBytes := []byte(r); json.Valid(paramsBytes) {
|
||||
if rv, ok := pointer.(reflect.Value); ok {
|
||||
if rv.Kind() == reflect.Ptr {
|
||||
return json.Unmarshal(paramsBytes, rv.Interface())
|
||||
}
|
||||
} else {
|
||||
return json.Unmarshal(paramsBytes, pointer)
|
||||
}
|
||||
}
|
||||
}
|
||||
var (
|
||||
paramsRv reflect.Value
|
||||
paramsKind reflect.Kind
|
||||
)
|
||||
if v, ok := params.(reflect.Value); ok {
|
||||
paramsRv = v
|
||||
} else {
|
||||
paramsRv = reflect.ValueOf(params)
|
||||
}
|
||||
paramsKind = paramsRv.Kind()
|
||||
if paramsKind == reflect.Ptr {
|
||||
paramsRv = paramsRv.Elem()
|
||||
paramsKind = paramsRv.Kind()
|
||||
}
|
||||
if paramsKind != reflect.Map {
|
||||
return doMapToMap(Map(params), pointer, mapping...)
|
||||
}
|
||||
// Empty params map, no need continue.
|
||||
if paramsRv.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
var pointerRv reflect.Value
|
||||
if v, ok := pointer.(reflect.Value); ok {
|
||||
pointerRv = v
|
||||
} else {
|
||||
pointerRv = reflect.ValueOf(pointer)
|
||||
}
|
||||
pointerKind := pointerRv.Kind()
|
||||
for pointerKind == reflect.Ptr {
|
||||
pointerRv = pointerRv.Elem()
|
||||
pointerKind = pointerRv.Kind()
|
||||
}
|
||||
if pointerKind != reflect.Map {
|
||||
return gerror.Newf("pointer should be type of *map, but got:%s", pointerKind)
|
||||
}
|
||||
defer func() {
|
||||
// Catch the panic, especially the reflect operation panics.
|
||||
if exception := recover(); exception != nil {
|
||||
if e, ok := exception.(errorStack); ok {
|
||||
err = e
|
||||
} else {
|
||||
err = gerror.NewSkipf(1, "%v", exception)
|
||||
}
|
||||
}
|
||||
}()
|
||||
var (
|
||||
paramsKeys = paramsRv.MapKeys()
|
||||
pointerKeyType = pointerRv.Type().Key()
|
||||
pointerValueType = pointerRv.Type().Elem()
|
||||
pointerValueKind = pointerValueType.Kind()
|
||||
dataMap = reflect.MakeMapWithSize(pointerRv.Type(), len(paramsKeys))
|
||||
)
|
||||
// Retrieve the true element type of target map.
|
||||
if pointerValueKind == reflect.Ptr {
|
||||
pointerValueKind = pointerValueType.Elem().Kind()
|
||||
}
|
||||
for _, key := range paramsKeys {
|
||||
e := reflect.New(pointerValueType).Elem()
|
||||
switch pointerValueKind {
|
||||
case reflect.Map, reflect.Struct:
|
||||
if err = Struct(paramsRv.MapIndex(key).Interface(), e, mapping...); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
e.Set(
|
||||
reflect.ValueOf(
|
||||
Convert(
|
||||
paramsRv.MapIndex(key).Interface(),
|
||||
pointerValueType.String(),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
dataMap.SetMapIndex(
|
||||
reflect.ValueOf(
|
||||
Convert(
|
||||
key.Interface(),
|
||||
pointerKeyType.Name(),
|
||||
),
|
||||
),
|
||||
e,
|
||||
)
|
||||
}
|
||||
pointerRv.Set(dataMap)
|
||||
return nil
|
||||
}
|
||||
146
util/gconv/gconv_maptomaps.go
Normal file
146
util/gconv/gconv_maptomaps.go
Normal file
@ -0,0 +1,146 @@
|
||||
// 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.
|
||||
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// MapToMaps converts any slice type variable `params` to another map slice type variable `pointer`.
|
||||
// See doMapToMaps.
|
||||
func MapToMaps(params interface{}, pointer interface{}, mapping ...map[string]string) error {
|
||||
return doMapToMaps(params, pointer, mapping...)
|
||||
}
|
||||
|
||||
// MapToMapsDeep converts any slice type variable `params` to another map slice type variable
|
||||
// `pointer` recursively.
|
||||
// Deprecated, use MapToMaps instead.
|
||||
func MapToMapsDeep(params interface{}, pointer interface{}, mapping ...map[string]string) error {
|
||||
return doMapToMaps(params, pointer, mapping...)
|
||||
}
|
||||
|
||||
// doMapToMaps converts any map type variable `params` to another map slice variable `pointer`.
|
||||
//
|
||||
// The parameter `params` can be type of []map, []*map, []struct, []*struct.
|
||||
//
|
||||
// The parameter `pointer` should be type of []map, []*map.
|
||||
//
|
||||
// The optional parameter `mapping` is used for struct attribute to map key mapping, which makes
|
||||
// sense only if the item of `params` is type struct.
|
||||
func doMapToMaps(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
// If given `params` is JSON, it then uses json.Unmarshal doing the converting.
|
||||
switch r := params.(type) {
|
||||
case []byte:
|
||||
if json.Valid(r) {
|
||||
if rv, ok := pointer.(reflect.Value); ok {
|
||||
if rv.Kind() == reflect.Ptr {
|
||||
return json.Unmarshal(r, rv.Interface())
|
||||
}
|
||||
} else {
|
||||
return json.Unmarshal(r, pointer)
|
||||
}
|
||||
}
|
||||
case string:
|
||||
if paramsBytes := []byte(r); json.Valid(paramsBytes) {
|
||||
if rv, ok := pointer.(reflect.Value); ok {
|
||||
if rv.Kind() == reflect.Ptr {
|
||||
return json.Unmarshal(paramsBytes, rv.Interface())
|
||||
}
|
||||
} else {
|
||||
return json.Unmarshal(paramsBytes, pointer)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Params and its element type check.
|
||||
var (
|
||||
paramsRv reflect.Value
|
||||
paramsKind reflect.Kind
|
||||
)
|
||||
if v, ok := params.(reflect.Value); ok {
|
||||
paramsRv = v
|
||||
} else {
|
||||
paramsRv = reflect.ValueOf(params)
|
||||
}
|
||||
paramsKind = paramsRv.Kind()
|
||||
if paramsKind == reflect.Ptr {
|
||||
paramsRv = paramsRv.Elem()
|
||||
paramsKind = paramsRv.Kind()
|
||||
}
|
||||
if paramsKind != reflect.Array && paramsKind != reflect.Slice {
|
||||
return gerror.New("params should be type of slice, eg: []map/[]*map/[]struct/[]*struct")
|
||||
}
|
||||
var (
|
||||
paramsElem = paramsRv.Type().Elem()
|
||||
paramsElemKind = paramsElem.Kind()
|
||||
)
|
||||
if paramsElemKind == reflect.Ptr {
|
||||
paramsElem = paramsElem.Elem()
|
||||
paramsElemKind = paramsElem.Kind()
|
||||
}
|
||||
if paramsElemKind != reflect.Map && paramsElemKind != reflect.Struct && paramsElemKind != reflect.Interface {
|
||||
return gerror.Newf("params element should be type of map/*map/struct/*struct, but got: %s", paramsElemKind)
|
||||
}
|
||||
// Empty slice, no need continue.
|
||||
if paramsRv.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
// Pointer and its element type check.
|
||||
var (
|
||||
pointerRv = reflect.ValueOf(pointer)
|
||||
pointerKind = pointerRv.Kind()
|
||||
)
|
||||
for pointerKind == reflect.Ptr {
|
||||
pointerRv = pointerRv.Elem()
|
||||
pointerKind = pointerRv.Kind()
|
||||
}
|
||||
if pointerKind != reflect.Array && pointerKind != reflect.Slice {
|
||||
return gerror.New("pointer should be type of *[]map/*[]*map")
|
||||
}
|
||||
var (
|
||||
pointerElemType = pointerRv.Type().Elem()
|
||||
pointerElemKind = pointerElemType.Kind()
|
||||
)
|
||||
if pointerElemKind == reflect.Ptr {
|
||||
pointerElemKind = pointerElemType.Elem().Kind()
|
||||
}
|
||||
if pointerElemKind != reflect.Map {
|
||||
return gerror.New("pointer element should be type of map/*map")
|
||||
}
|
||||
defer func() {
|
||||
// Catch the panic, especially the reflect operation panics.
|
||||
if exception := recover(); exception != nil {
|
||||
if e, ok := exception.(errorStack); ok {
|
||||
err = e
|
||||
} else {
|
||||
err = gerror.NewSkipf(1, "%v", exception)
|
||||
}
|
||||
}
|
||||
}()
|
||||
var (
|
||||
pointerSlice = reflect.MakeSlice(pointerRv.Type(), paramsRv.Len(), paramsRv.Len())
|
||||
)
|
||||
for i := 0; i < paramsRv.Len(); i++ {
|
||||
var item reflect.Value
|
||||
if pointerElemType.Kind() == reflect.Ptr {
|
||||
item = reflect.New(pointerElemType.Elem())
|
||||
if err = MapToMap(paramsRv.Index(i).Interface(), item, mapping...); err != nil {
|
||||
return err
|
||||
}
|
||||
pointerSlice.Index(i).Set(item)
|
||||
} else {
|
||||
item = reflect.New(pointerElemType)
|
||||
if err = MapToMap(paramsRv.Index(i).Interface(), item, mapping...); err != nil {
|
||||
return err
|
||||
}
|
||||
pointerSlice.Index(i).Set(item.Elem())
|
||||
}
|
||||
}
|
||||
pointerRv.Set(pointerSlice)
|
||||
return
|
||||
}
|
||||
@ -11,18 +11,39 @@ import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Scan automatically calls Struct or Structs function according to the type of parameter
|
||||
// <pointer> to implement the converting.
|
||||
// It calls function Struct if <pointer> is type of *struct/**struct to do the converting.
|
||||
// It calls function Structs if <pointer> is type of *[]struct/*[]*struct to do the converting.
|
||||
// Scan automatically calls MapToMap, MapToMaps, Struct or Structs function according to
|
||||
// the type of parameter `pointer` to implement the converting.
|
||||
// It calls function MapToMap if `pointer` is type of *map to do the converting.
|
||||
// It calls function MapToMaps if `pointer` is type of *[]map/*[]*map to do the converting.
|
||||
// It calls function Struct if `pointer` is type of *struct/**struct to do the converting.
|
||||
// It calls function Structs if `pointer` is type of *[]struct/*[]*struct to do the converting.
|
||||
func Scan(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
t := reflect.TypeOf(pointer)
|
||||
k := t.Kind()
|
||||
if k != reflect.Ptr {
|
||||
return gerror.Newf("params should be type of pointer, but got: %v", k)
|
||||
var (
|
||||
pointerType = reflect.TypeOf(pointer)
|
||||
pointerKind = pointerType.Kind()
|
||||
)
|
||||
if pointerKind != reflect.Ptr {
|
||||
return gerror.Newf("params should be type of pointer, but got: %v", pointerKind)
|
||||
}
|
||||
switch t.Elem().Kind() {
|
||||
var (
|
||||
pointerElem = pointerType.Elem()
|
||||
pointerElemKind = pointerElem.Kind()
|
||||
)
|
||||
switch pointerElemKind {
|
||||
case reflect.Map:
|
||||
return MapToMap(params, pointer, mapping...)
|
||||
case reflect.Array, reflect.Slice:
|
||||
var (
|
||||
sliceElem = pointerElem.Elem()
|
||||
sliceElemKind = sliceElem.Kind()
|
||||
)
|
||||
for sliceElemKind == reflect.Ptr {
|
||||
sliceElem = sliceElem.Elem()
|
||||
sliceElemKind = sliceElem.Kind()
|
||||
}
|
||||
if sliceElemKind == reflect.Map {
|
||||
return MapToMaps(params, pointer, mapping...)
|
||||
}
|
||||
return Structs(params, pointer, mapping...)
|
||||
default:
|
||||
return Struct(params, pointer, mapping...)
|
||||
@ -30,9 +51,9 @@ func Scan(params interface{}, pointer interface{}, mapping ...map[string]string)
|
||||
}
|
||||
|
||||
// ScanDeep automatically calls StructDeep or StructsDeep function according to the type of
|
||||
// parameter <pointer> to implement the converting..
|
||||
// It calls function StructDeep if <pointer> is type of *struct/**struct to do the converting.
|
||||
// It calls function StructsDeep if <pointer> is type of *[]struct/*[]*struct to do the converting.
|
||||
// parameter `pointer` to implement the converting..
|
||||
// It calls function StructDeep if `pointer` is type of *struct/**struct to do the converting.
|
||||
// It calls function StructsDeep if `pointer` is type of *[]struct/*[]*struct to do the converting.
|
||||
// Deprecated, use Scan instead.
|
||||
func ScanDeep(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
t := reflect.TypeOf(pointer)
|
||||
|
||||
@ -5,123 +5,3 @@
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/internal/json"
|
||||
)
|
||||
|
||||
// SliceMap is alias of Maps.
|
||||
func SliceMap(any interface{}) []map[string]interface{} {
|
||||
return Maps(any)
|
||||
}
|
||||
|
||||
// SliceMapDeep is alias of MapsDeep.
|
||||
func SliceMapDeep(any interface{}) []map[string]interface{} {
|
||||
return MapsDeep(any)
|
||||
}
|
||||
|
||||
// SliceStruct is alias of Structs.
|
||||
func SliceStruct(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
return Structs(params, pointer, mapping...)
|
||||
}
|
||||
|
||||
// SliceStructDeep is alias of StructsDeep.
|
||||
// Deprecated, use SliceStruct instead.
|
||||
func SliceStructDeep(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
return StructsDeep(params, pointer, mapping...)
|
||||
}
|
||||
|
||||
// Maps converts <i> to []map[string]interface{}.
|
||||
// Note that it automatically checks and converts json string to []map if <value> is string/[]byte.
|
||||
func Maps(value interface{}, tags ...string) []map[string]interface{} {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
switch r := value.(type) {
|
||||
case string:
|
||||
list := make([]map[string]interface{}, 0)
|
||||
if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' {
|
||||
if err := json.Unmarshal([]byte(r), &list); err != nil {
|
||||
return nil
|
||||
}
|
||||
return list
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
case []byte:
|
||||
list := make([]map[string]interface{}, 0)
|
||||
if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' {
|
||||
if err := json.Unmarshal(r, &list); err != nil {
|
||||
return nil
|
||||
}
|
||||
return list
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
case []map[string]interface{}:
|
||||
return r
|
||||
|
||||
default:
|
||||
array := Interfaces(value)
|
||||
if len(array) == 0 {
|
||||
return nil
|
||||
}
|
||||
list := make([]map[string]interface{}, len(array))
|
||||
for k, v := range array {
|
||||
list[k] = Map(v, tags...)
|
||||
}
|
||||
return list
|
||||
}
|
||||
}
|
||||
|
||||
// MapsDeep converts <i> to []map[string]interface{} recursively.
|
||||
//
|
||||
// TODO completely implement the recursive converting for all types.
|
||||
func MapsDeep(value interface{}, tags ...string) []map[string]interface{} {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
switch r := value.(type) {
|
||||
case string:
|
||||
list := make([]map[string]interface{}, 0)
|
||||
if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' {
|
||||
if err := json.Unmarshal([]byte(r), &list); err != nil {
|
||||
return nil
|
||||
}
|
||||
return list
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
case []byte:
|
||||
list := make([]map[string]interface{}, 0)
|
||||
if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' {
|
||||
if err := json.Unmarshal(r, &list); err != nil {
|
||||
return nil
|
||||
}
|
||||
return list
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
case []map[string]interface{}:
|
||||
list := make([]map[string]interface{}, len(r))
|
||||
for k, v := range r {
|
||||
list[k] = MapDeep(v, tags...)
|
||||
}
|
||||
return list
|
||||
|
||||
default:
|
||||
array := Interfaces(value)
|
||||
if len(array) == 0 {
|
||||
return nil
|
||||
}
|
||||
list := make([]map[string]interface{}, len(array))
|
||||
for k, v := range array {
|
||||
list[k] = MapDeep(v, tags...)
|
||||
}
|
||||
return list
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ func SliceAny(any interface{}) []interface{} {
|
||||
return Interfaces(any)
|
||||
}
|
||||
|
||||
// Interfaces converts <i> to []interface{}.
|
||||
// Interfaces converts `i` to []interface{}.
|
||||
func Interfaces(any interface{}) []interface{} {
|
||||
if any == nil {
|
||||
return nil
|
||||
|
||||
@ -23,12 +23,12 @@ func SliceFloat64(any interface{}) []float64 {
|
||||
return Floats(any)
|
||||
}
|
||||
|
||||
// Floats converts <i> to []float64.
|
||||
// Floats converts `i` to []float64.
|
||||
func Floats(any interface{}) []float64 {
|
||||
return Float64s(any)
|
||||
}
|
||||
|
||||
// Float32s converts <i> to []float32.
|
||||
// Float32s converts `i` to []float32.
|
||||
func Float32s(any interface{}) []float32 {
|
||||
if any == nil {
|
||||
return nil
|
||||
@ -148,7 +148,7 @@ func Float32s(any interface{}) []float32 {
|
||||
return array
|
||||
}
|
||||
|
||||
// Float64s converts <i> to []float64.
|
||||
// Float64s converts `i` to []float64.
|
||||
func Float64s(any interface{}) []float64 {
|
||||
if any == nil {
|
||||
return nil
|
||||
|
||||
@ -23,7 +23,7 @@ func SliceInt64(any interface{}) []int64 {
|
||||
return Int64s(any)
|
||||
}
|
||||
|
||||
// Ints converts <i> to []int.
|
||||
// Ints converts `i` to []int.
|
||||
func Ints(any interface{}) []int {
|
||||
if any == nil {
|
||||
return nil
|
||||
@ -153,7 +153,7 @@ func Ints(any interface{}) []int {
|
||||
return array
|
||||
}
|
||||
|
||||
// Int32s converts <i> to []int32.
|
||||
// Int32s converts `i` to []int32.
|
||||
func Int32s(any interface{}) []int32 {
|
||||
if any == nil {
|
||||
return nil
|
||||
@ -283,7 +283,7 @@ func Int32s(any interface{}) []int32 {
|
||||
return array
|
||||
}
|
||||
|
||||
// Int64s converts <i> to []int64.
|
||||
// Int64s converts `i` to []int64.
|
||||
func Int64s(any interface{}) []int64 {
|
||||
if any == nil {
|
||||
return nil
|
||||
|
||||
@ -13,7 +13,7 @@ func SliceStr(any interface{}) []string {
|
||||
return Strings(any)
|
||||
}
|
||||
|
||||
// Strings converts <i> to []string.
|
||||
// Strings converts `i` to []string.
|
||||
func Strings(any interface{}) []string {
|
||||
if any == nil {
|
||||
return nil
|
||||
|
||||
@ -23,7 +23,7 @@ func SliceUint64(any interface{}) []uint64 {
|
||||
return Uint64s(any)
|
||||
}
|
||||
|
||||
// Uints converts <i> to []uint.
|
||||
// Uints converts `i` to []uint.
|
||||
func Uints(any interface{}) []uint {
|
||||
if any == nil {
|
||||
return nil
|
||||
@ -149,7 +149,7 @@ func Uints(any interface{}) []uint {
|
||||
return array
|
||||
}
|
||||
|
||||
// Uint32s converts <i> to []uint32.
|
||||
// Uint32s converts `i` to []uint32.
|
||||
func Uint32s(any interface{}) []uint32 {
|
||||
if any == nil {
|
||||
return nil
|
||||
@ -274,7 +274,7 @@ func Uint32s(any interface{}) []uint32 {
|
||||
return array
|
||||
}
|
||||
|
||||
// Uint64s converts <i> to []uint64.
|
||||
// Uint64s converts `i` to []uint64.
|
||||
func Uint64s(any interface{}) []uint64 {
|
||||
if any == nil {
|
||||
return nil
|
||||
|
||||
@ -19,15 +19,15 @@ import (
|
||||
)
|
||||
|
||||
// Struct maps the params key-value pairs to the corresponding struct object's attributes.
|
||||
// The third parameter <mapping> is unnecessary, indicating the mapping rules between the
|
||||
// The third parameter `mapping` is unnecessary, indicating the mapping rules between the
|
||||
// custom key name and the attribute name(case sensitive).
|
||||
//
|
||||
// Note:
|
||||
// 1. The <params> can be any type of map/struct, usually a map.
|
||||
// 2. The <pointer> should be type of *struct/**struct, which is a pointer to struct object
|
||||
// 1. The `params` can be any type of map/struct, usually a map.
|
||||
// 2. The `pointer` should be type of *struct/**struct, which is a pointer to struct object
|
||||
// or struct pointer.
|
||||
// 3. Only the public attributes of struct object can be mapped.
|
||||
// 4. If <params> is a map, the key of the map <params> can be lowercase.
|
||||
// 4. If `params` is a map, the key of the map `params` can be lowercase.
|
||||
// It will automatically convert the first letter of the key to uppercase
|
||||
// in mapping procedure to do the matching.
|
||||
// It ignores the map key, if it does not match.
|
||||
@ -59,7 +59,7 @@ func StructDeep(params interface{}, pointer interface{}, mapping ...map[string]s
|
||||
// doStruct is the core internal converting function for any data to struct.
|
||||
func doStruct(params interface{}, pointer interface{}, mapping map[string]string, priorityTag string) (err error) {
|
||||
if params == nil {
|
||||
// If <params> is nil, no conversion.
|
||||
// If `params` is nil, no conversion.
|
||||
return nil
|
||||
}
|
||||
if pointer == nil {
|
||||
@ -77,7 +77,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string
|
||||
}
|
||||
}()
|
||||
|
||||
// If given <params> is JSON, it then uses json.Unmarshal doing the converting.
|
||||
// If given `params` is JSON, it then uses json.Unmarshal doing the converting.
|
||||
switch r := params.(type) {
|
||||
case []byte:
|
||||
if json.Valid(r) {
|
||||
@ -140,7 +140,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string
|
||||
}
|
||||
|
||||
// It automatically creates struct object if necessary.
|
||||
// For example, if <pointer> is **User, then <elem> is *User, which is a pointer to User.
|
||||
// For example, if `pointer` is **User, then `elem` is *User, which is a pointer to User.
|
||||
if pointerElemReflectValue.Kind() == reflect.Ptr {
|
||||
if !pointerElemReflectValue.IsValid() || pointerElemReflectValue.IsNil() {
|
||||
e := reflect.New(pointerElemReflectValue.Type().Elem()).Elem()
|
||||
@ -249,7 +249,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string
|
||||
// string cases and chars like '-'/'_'/'.'/' '.
|
||||
|
||||
// Matching the parameters to struct tag names.
|
||||
// The <tagV> is the attribute name of the struct.
|
||||
// The `tagV` is the attribute name of the struct.
|
||||
for attrKey, cmpKey := range tagMap {
|
||||
if strings.EqualFold(checkName, cmpKey) {
|
||||
attrName = attrKey
|
||||
@ -349,7 +349,7 @@ func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value i
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// bindVarToReflectValue sets <value> to reflect value object <structFieldValue>.
|
||||
// bindVarToReflectValue sets `value` to reflect value object `structFieldValue`.
|
||||
func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, mapping map[string]string, priorityTag string) (err error) {
|
||||
if err, ok := bindVarToReflectValueWithInterfaceCheck(structFieldValue, value); ok {
|
||||
return err
|
||||
@ -455,7 +455,7 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, ma
|
||||
)
|
||||
}
|
||||
}()
|
||||
// It here uses reflect converting <value> to type of the attribute and assigns
|
||||
// It here uses reflect converting `value` to type of the attribute and assigns
|
||||
// the result value to the attribute. It might fail and panic if the usual Go
|
||||
// conversion rules do not allow conversion.
|
||||
structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
|
||||
|
||||
@ -40,14 +40,14 @@ func StructsDeep(params interface{}, pointer interface{}, mapping ...map[string]
|
||||
|
||||
// doStructs converts any slice to given struct slice.
|
||||
//
|
||||
// It automatically checks and converts json string to []map if <params> is string/[]byte.
|
||||
// It automatically checks and converts json string to []map if `params` is string/[]byte.
|
||||
//
|
||||
// The parameter <pointer> should be type of pointer to slice of struct.
|
||||
// Note that if <pointer> is a pointer to another pointer of type of slice of struct,
|
||||
// The parameter `pointer` should be type of pointer to slice of struct.
|
||||
// Note that if `pointer` is a pointer to another pointer of type of slice of struct,
|
||||
// it will create the struct/pointer internally.
|
||||
func doStructs(params interface{}, pointer interface{}, mapping map[string]string, priorityTag string) (err error) {
|
||||
if params == nil {
|
||||
// If <params> is nil, no conversion.
|
||||
// If `params` is nil, no conversion.
|
||||
return nil
|
||||
}
|
||||
if pointer == nil {
|
||||
@ -68,7 +68,7 @@ func doStructs(params interface{}, pointer interface{}, mapping map[string]strin
|
||||
}
|
||||
}
|
||||
}()
|
||||
// If given <params> is JSON, it then uses json.Unmarshal doing the converting.
|
||||
// If given `params` is JSON, it then uses json.Unmarshal doing the converting.
|
||||
switch r := params.(type) {
|
||||
case []byte:
|
||||
if json.Valid(r) {
|
||||
@ -99,9 +99,9 @@ func doStructs(params interface{}, pointer interface{}, mapping map[string]strin
|
||||
return gerror.Newf("pointer should be type of pointer, but got: %v", kind)
|
||||
}
|
||||
}
|
||||
// Converting <params> to map slice.
|
||||
// Converting `params` to map slice.
|
||||
paramsMaps := Maps(params)
|
||||
// If <params> is an empty slice, no conversion.
|
||||
// If `params` is an empty slice, no conversion.
|
||||
if len(paramsMaps) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ import (
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
)
|
||||
|
||||
// Time converts <i> to time.Time.
|
||||
// Time converts `i` to time.Time.
|
||||
func Time(any interface{}, format ...string) time.Time {
|
||||
// It's already this type.
|
||||
if len(format) == 0 {
|
||||
@ -27,9 +27,9 @@ func Time(any interface{}, format ...string) time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
// Duration converts <i> to time.Duration.
|
||||
// If <i> is string, then it uses time.ParseDuration to convert it.
|
||||
// If <i> is numeric, then it converts <i> as nanoseconds.
|
||||
// Duration converts `i` to time.Duration.
|
||||
// If `i` is string, then it uses time.ParseDuration to convert it.
|
||||
// If `i` is numeric, then it converts `i` as nanoseconds.
|
||||
func Duration(any interface{}) time.Duration {
|
||||
// It's already this type.
|
||||
if v, ok := any.(time.Duration); ok {
|
||||
@ -43,10 +43,10 @@ func Duration(any interface{}) time.Duration {
|
||||
return time.Duration(Int64(any))
|
||||
}
|
||||
|
||||
// GTime converts <i> to *gtime.Time.
|
||||
// The parameter <format> can be used to specify the format of <i>.
|
||||
// If no <format> given, it converts <i> using gtime.NewFromTimeStamp if <i> is numeric,
|
||||
// or using gtime.StrToTime if <i> is string.
|
||||
// GTime converts `i` to *gtime.Time.
|
||||
// The parameter `format` can be used to specify the format of `i`.
|
||||
// If no `format` given, it converts `i` using gtime.NewFromTimeStamp if `i` is numeric,
|
||||
// or using gtime.StrToTime if `i` is string.
|
||||
func GTime(any interface{}, format ...string) *gtime.Time {
|
||||
if any == nil {
|
||||
return nil
|
||||
|
||||
@ -9,14 +9,14 @@ package gconv
|
||||
import "unsafe"
|
||||
|
||||
// UnsafeStrToBytes converts string to []byte without memory copy.
|
||||
// Note that, if you completely sure you will never use <s> variable in the feature,
|
||||
// Note that, if you completely sure you will never use `s` variable in the feature,
|
||||
// you can use this unsafe function to implement type conversion in high performance.
|
||||
func UnsafeStrToBytes(s string) []byte {
|
||||
return *(*[]byte)(unsafe.Pointer(&s))
|
||||
}
|
||||
|
||||
// UnsafeBytesToStr converts []byte to string without memory copy.
|
||||
// Note that, if you completely sure you will never use <b> variable in the feature,
|
||||
// Note that, if you completely sure you will never use `b` variable in the feature,
|
||||
// you can use this unsafe function to implement type conversion in high performance.
|
||||
func UnsafeBytesToStr(b []byte) string {
|
||||
return *(*string)(unsafe.Pointer(&b))
|
||||
|
||||
@ -143,184 +143,46 @@ func Test_MapToMapDeep(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_MapToMaps1(t *testing.T) {
|
||||
func Test_MapToMaps(t *testing.T) {
|
||||
params := g.Slice{
|
||||
g.Map{"id": 1, "name": "john"},
|
||||
g.Map{"id": 2, "name": "smith"},
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var s []g.Map
|
||||
err := gconv.MapToMaps(params, &s)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(s), 2)
|
||||
t.Assert(s, params)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var s []*g.Map
|
||||
err := gconv.MapToMaps(params, &s)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(s), 2)
|
||||
t.Assert(s, params)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_MapToMaps_StructParams(t *testing.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Name int
|
||||
}
|
||||
params := g.Map{
|
||||
"key1": g.Slice{
|
||||
g.Map{"id": 1, "name": "john"},
|
||||
g.Map{"id": 2, "name": "smith"},
|
||||
},
|
||||
"key2": g.Slice{
|
||||
g.Map{"id": 3, "name": "green"},
|
||||
g.Map{"id": 4, "name": "jim"},
|
||||
},
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := make(map[string][]User)
|
||||
err := gconv.MapToMaps(params, &m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(m), 2)
|
||||
t.Assert(m["key1"][0].Id, 1)
|
||||
t.Assert(m["key1"][1].Id, 2)
|
||||
t.Assert(m["key2"][0].Id, 3)
|
||||
t.Assert(m["key2"][1].Id, 4)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := (map[string][]User)(nil)
|
||||
err := gconv.MapToMaps(params, &m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(m), 2)
|
||||
t.Assert(m["key1"][0].Id, 1)
|
||||
t.Assert(m["key1"][1].Id, 2)
|
||||
t.Assert(m["key2"][0].Id, 3)
|
||||
t.Assert(m["key2"][1].Id, 4)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := make(map[string][]*User)
|
||||
err := gconv.MapToMaps(params, &m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(m), 2)
|
||||
t.Assert(m["key1"][0].Id, 1)
|
||||
t.Assert(m["key1"][1].Id, 2)
|
||||
t.Assert(m["key2"][0].Id, 3)
|
||||
t.Assert(m["key2"][1].Id, 4)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := (map[string][]*User)(nil)
|
||||
err := gconv.MapToMaps(params, &m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(m), 2)
|
||||
t.Assert(m["key1"][0].Id, 1)
|
||||
t.Assert(m["key1"][1].Id, 2)
|
||||
t.Assert(m["key2"][0].Id, 3)
|
||||
t.Assert(m["key2"][1].Id, 4)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_MapToMaps2(t *testing.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Name int
|
||||
}
|
||||
params := g.MapIntAny{
|
||||
100: g.Slice{
|
||||
g.Map{"id": 1, "name": "john"},
|
||||
g.Map{"id": 2, "name": "smith"},
|
||||
},
|
||||
200: g.Slice{
|
||||
g.Map{"id": 3, "name": "green"},
|
||||
g.Map{"id": 4, "name": "jim"},
|
||||
},
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := make(map[int][]User)
|
||||
err := gconv.MapToMaps(params, &m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(m), 2)
|
||||
t.Assert(m[100][0].Id, 1)
|
||||
t.Assert(m[100][1].Id, 2)
|
||||
t.Assert(m[200][0].Id, 3)
|
||||
t.Assert(m[200][1].Id, 4)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := make(map[int][]*User)
|
||||
err := gconv.MapToMaps(params, &m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(m), 2)
|
||||
t.Assert(m[100][0].Id, 1)
|
||||
t.Assert(m[100][1].Id, 2)
|
||||
t.Assert(m[200][0].Id, 3)
|
||||
t.Assert(m[200][1].Id, 4)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := make(map[string][]*User)
|
||||
err := gconv.MapToMaps(params, &m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(m), 2)
|
||||
t.Assert(m["100"][0].Id, 1)
|
||||
t.Assert(m["100"][1].Id, 2)
|
||||
t.Assert(m["200"][0].Id, 3)
|
||||
t.Assert(m["200"][1].Id, 4)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_MapToMaps3(t *testing.T) {
|
||||
type Ids struct {
|
||||
Id int
|
||||
Uid int
|
||||
}
|
||||
type Base struct {
|
||||
Ids
|
||||
Time string
|
||||
}
|
||||
type User struct {
|
||||
Base
|
||||
Name string
|
||||
}
|
||||
params := g.MapIntAny{
|
||||
100: g.Slice{
|
||||
g.Map{"id": 1, "name": "john"},
|
||||
g.Map{"id": 2, "name": "smith"},
|
||||
},
|
||||
200: g.Slice{
|
||||
g.Map{"id": 3, "name": "green"},
|
||||
g.Map{"id": 4, "name": "jim"},
|
||||
},
|
||||
params := g.Slice{
|
||||
User{1, "name1"},
|
||||
User{2, "name2"},
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := make(map[string][]*User)
|
||||
err := gconv.MapToMaps(params, &m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(m), 2)
|
||||
t.Assert(m["100"][0].Id, 1)
|
||||
t.Assert(m["100"][1].Id, 2)
|
||||
t.Assert(m["100"][0].Name, "john")
|
||||
t.Assert(m["100"][1].Name, "smith")
|
||||
t.Assert(m["200"][0].Id, 3)
|
||||
t.Assert(m["200"][1].Id, 4)
|
||||
t.Assert(m["200"][0].Name, "green")
|
||||
t.Assert(m["200"][1].Name, "jim")
|
||||
var s []g.Map
|
||||
err := gconv.MapToMaps(params, &s)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(s), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_MapToMapsWithTag(t *testing.T) {
|
||||
type Ids struct {
|
||||
Id int
|
||||
Uid int
|
||||
}
|
||||
type Base struct {
|
||||
Ids `json:"ids"`
|
||||
Time string
|
||||
}
|
||||
type User struct {
|
||||
Base `json:"base"`
|
||||
Name string
|
||||
}
|
||||
params := g.MapIntAny{
|
||||
100: g.Slice{
|
||||
g.Map{"id": 1, "name": "john"},
|
||||
g.Map{"id": 2, "name": "smith"},
|
||||
},
|
||||
200: g.Slice{
|
||||
g.Map{"id": 3, "name": "green"},
|
||||
g.Map{"id": 4, "name": "jim"},
|
||||
},
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := make(map[string][]*User)
|
||||
err := gconv.MapToMaps(params, &m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(m), 2)
|
||||
t.Assert(m["100"][0].Id, 1)
|
||||
t.Assert(m["100"][1].Id, 2)
|
||||
t.Assert(m["100"][0].Name, "john")
|
||||
t.Assert(m["100"][1].Name, "smith")
|
||||
t.Assert(m["200"][0].Id, 3)
|
||||
t.Assert(m["200"][1].Id, 4)
|
||||
t.Assert(m["200"][0].Name, "green")
|
||||
t.Assert(m["200"][1].Name, "jim")
|
||||
var s []*g.Map
|
||||
err := gconv.MapToMaps(params, &s)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(s), 2)
|
||||
})
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Scan(t *testing.T) {
|
||||
func Test_Scan_StructStructs(t *testing.T) {
|
||||
type User struct {
|
||||
Uid int
|
||||
Name string
|
||||
@ -76,7 +76,7 @@ func Test_Scan(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ScanStr(t *testing.T) {
|
||||
func Test_Scan_StructStr(t *testing.T) {
|
||||
type User struct {
|
||||
Uid int
|
||||
Name string
|
||||
@ -123,3 +123,73 @@ func Test_ScanStr(t *testing.T) {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Scan_Map(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m map[string]string
|
||||
data := g.Map{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
}
|
||||
err := gconv.Scan(data, &m)
|
||||
t.AssertNil(err)
|
||||
t.Assert(data, m)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m map[int]int
|
||||
data := g.Map{
|
||||
"1": "11",
|
||||
"2": "22",
|
||||
}
|
||||
err := gconv.Scan(data, &m)
|
||||
t.AssertNil(err)
|
||||
t.Assert(data, m)
|
||||
})
|
||||
// json string parameter.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m map[string]string
|
||||
data := `{"k1":"v1","k2":"v2"}`
|
||||
err := gconv.Scan(data, &m)
|
||||
t.AssertNil(err)
|
||||
t.Assert(m, g.Map{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Scan_Maps(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var maps []map[string]string
|
||||
data := g.Slice{
|
||||
g.Map{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
},
|
||||
g.Map{
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
},
|
||||
}
|
||||
err := gconv.Scan(data, &maps)
|
||||
t.AssertNil(err)
|
||||
t.Assert(data, maps)
|
||||
})
|
||||
// json string parameter.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var maps []map[string]string
|
||||
data := `[{"k1":"v1","k2":"v2"},{"k3":"v3","k4":"v4"}]`
|
||||
err := gconv.Scan(data, &maps)
|
||||
t.AssertNil(err)
|
||||
t.Assert(maps, g.Slice{
|
||||
g.Map{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
},
|
||||
g.Map{
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
package gconv_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
@ -1139,3 +1140,27 @@ func Test_Struct_JsonParam(t *testing.T) {
|
||||
t.Assert(a.Name, "john")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Struct_GVarAttribute(t *testing.T) {
|
||||
type A struct {
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Status bool `json:"status"`
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
a = A{}
|
||||
data = g.Map{
|
||||
"id": 100,
|
||||
"name": "john",
|
||||
"status": gvar.New(false),
|
||||
}
|
||||
)
|
||||
err := gconv.Struct(data, &a)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a.Id, data["id"])
|
||||
t.Assert(a.Name, data["name"])
|
||||
t.Assert(a.Status, data["status"])
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@ -21,9 +21,9 @@ type Page struct {
|
||||
TotalPage int // Total page, which is automatically calculated.
|
||||
CurrentPage int // Current page number >= 1.
|
||||
UrlTemplate string // Custom url template for page url producing.
|
||||
LinkStyle string // CSS style name for HTML link tag <a>.
|
||||
SpanStyle string // CSS style name for HTML span tag <span>, which is used for first, current and last page tag.
|
||||
SelectStyle string // CSS style name for HTML select tag <select>.
|
||||
LinkStyle string // CSS style name for HTML link tag `a`.
|
||||
SpanStyle string // CSS style name for HTML span tag `span`, which is used for first, current and last page tag.
|
||||
SelectStyle string // CSS style name for HTML select tag `select`.
|
||||
NextPageTag string // Tag name for next p.
|
||||
PrevPageTag string // Tag name for prev p.
|
||||
FirstPageTag string // Tag name for first p.
|
||||
@ -40,9 +40,9 @@ const (
|
||||
)
|
||||
|
||||
// New creates and returns a pagination manager.
|
||||
// Note that the parameter <urlTemplate> specifies the URL producing template, like:
|
||||
// Note that the parameter `urlTemplate` specifies the URL producing template, like:
|
||||
// /user/list/{.page}, /user/list/{.page}.html, /user/list?page={.page}&type=1, etc.
|
||||
// The build-in variable in <urlTemplate> "{.page}" specifies the page number, which will be replaced by certain
|
||||
// The build-in variable in `urlTemplate` "{.page}" specifies the page number, which will be replaced by certain
|
||||
// page number when producing.
|
||||
func New(totalSize, pageSize, currentPage int, urlTemplate string) *Page {
|
||||
p := &Page{
|
||||
@ -209,7 +209,7 @@ func (p *Page) GetUrl(page int) string {
|
||||
return gstr.Replace(p.UrlTemplate, PAGE_PLACE_HOLDER, gconv.String(page))
|
||||
}
|
||||
|
||||
// GetLink returns the HTML link tag <a> content for given page number.
|
||||
// GetLink returns the HTML link tag `a` content for given page number.
|
||||
func (p *Page) GetLink(page int, text, title string) string {
|
||||
if len(p.AjaxActionName) > 0 {
|
||||
return fmt.Sprintf(
|
||||
|
||||
@ -9,6 +9,7 @@ package grand
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@ -22,8 +23,8 @@ var (
|
||||
// Intn returns a int number which is between 0 and max: [0, max).
|
||||
//
|
||||
// Note that:
|
||||
// 1. The <max> can only be greater than 0, or else it returns <max> directly;
|
||||
// 2. The result is greater than or equal to 0, but less than <max>;
|
||||
// 1. The `max` can only be greater than 0, or else it returns `max` directly;
|
||||
// 2. The result is greater than or equal to 0, but less than `max`;
|
||||
// 3. The result number is 32bit and less than math.MaxUint32.
|
||||
func Intn(max int) int {
|
||||
if max <= 0 {
|
||||
@ -36,7 +37,7 @@ func Intn(max int) int {
|
||||
return n
|
||||
}
|
||||
|
||||
// B retrieves and returns random bytes of given length <n>.
|
||||
// B retrieves and returns random bytes of given length `n`.
|
||||
func B(n int) []byte {
|
||||
if n <= 0 {
|
||||
return nil
|
||||
@ -54,7 +55,7 @@ func B(n int) []byte {
|
||||
}
|
||||
|
||||
// N returns a random int between min and max: [min, max].
|
||||
// The <min> and <max> also support negative numbers.
|
||||
// The `min` and `max` also support negative numbers.
|
||||
func N(min, max int) int {
|
||||
if min >= max {
|
||||
return min
|
||||
@ -76,8 +77,8 @@ func N(min, max int) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// S returns a random string which contains digits and letters, and its length is <n>.
|
||||
// The optional parameter <symbols> specifies whether the result could contain symbols,
|
||||
// S returns a random string which contains digits and letters, and its length is `n`.
|
||||
// The optional parameter `symbols` specifies whether the result could contain symbols,
|
||||
// which is false in default.
|
||||
func S(n int, symbols ...bool) string {
|
||||
if n <= 0 {
|
||||
@ -97,7 +98,21 @@ func S(n int, symbols ...bool) string {
|
||||
return *(*string)(unsafe.Pointer(&b))
|
||||
}
|
||||
|
||||
// Str randomly picks and returns <n> count of chars from given string <s>.
|
||||
// D returns a random time.Duration between min and max: [min, max].
|
||||
func D(min, max time.Duration) time.Duration {
|
||||
multiple := int64(1)
|
||||
if min != 0 {
|
||||
for min%10 == 0 {
|
||||
multiple *= 10
|
||||
min /= 10
|
||||
max /= 10
|
||||
}
|
||||
}
|
||||
n := int64(N(int(min), int(max)))
|
||||
return time.Duration(n * multiple)
|
||||
}
|
||||
|
||||
// Str randomly picks and returns `n` count of chars from given string `s`.
|
||||
// It also supports unicode string like Chinese/Russian/Japanese, etc.
|
||||
func Str(s string, n int) string {
|
||||
if n <= 0 {
|
||||
@ -120,7 +135,7 @@ func Str(s string, n int) string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// Digits returns a random string which contains only digits, and its length is <n>.
|
||||
// Digits returns a random string which contains only digits, and its length is `n`.
|
||||
func Digits(n int) string {
|
||||
if n <= 0 {
|
||||
return ""
|
||||
@ -135,7 +150,7 @@ func Digits(n int) string {
|
||||
return *(*string)(unsafe.Pointer(&b))
|
||||
}
|
||||
|
||||
// Letters returns a random string which contains only letters, and its length is <n>.
|
||||
// Letters returns a random string which contains only letters, and its length is `n`.
|
||||
func Letters(n int) string {
|
||||
if n <= 0 {
|
||||
return ""
|
||||
@ -150,7 +165,7 @@ func Letters(n int) string {
|
||||
return *(*string)(unsafe.Pointer(&b))
|
||||
}
|
||||
|
||||
// Symbols returns a random string which contains only symbols, and its length is <n>.
|
||||
// Symbols returns a random string which contains only symbols, and its length is `n`.
|
||||
func Symbols(n int) string {
|
||||
if n <= 0 {
|
||||
return ""
|
||||
@ -177,7 +192,7 @@ func Perm(n int) []int {
|
||||
return m
|
||||
}
|
||||
|
||||
// Meet randomly calculate whether the given probability <num>/<total> is met.
|
||||
// Meet randomly calculate whether the given probability `num`/`total` is met.
|
||||
func Meet(num, total int) bool {
|
||||
return Intn(total) < num
|
||||
}
|
||||
|
||||
@ -12,13 +12,13 @@ import (
|
||||
|
||||
const (
|
||||
// Buffer size for uint32 random number.
|
||||
gBUFFER_SIZE = 10000
|
||||
bufferChanSize = 10000
|
||||
)
|
||||
|
||||
var (
|
||||
// bufferChan is the buffer for random bytes,
|
||||
// every item storing 4 bytes.
|
||||
bufferChan = make(chan []byte, gBUFFER_SIZE)
|
||||
bufferChan = make(chan []byte, bufferChanSize)
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
@ -11,6 +11,7 @@ package grand_test
|
||||
import (
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"github.com/gogf/gf/util/grand"
|
||||
@ -73,6 +74,23 @@ func Test_N(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_D(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
for i := 0; i < 100; i++ {
|
||||
t.Assert(grand.D(time.Second, time.Second), time.Second)
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
t.Assert(grand.D(0, 0), time.Duration(0))
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
t.AssertIN(
|
||||
grand.D(1*time.Second, 3*time.Second),
|
||||
[]time.Duration{1 * time.Second, 2 * time.Second, 3 * time.Second},
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Rand(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
for i := 0; i < 100; i++ {
|
||||
|
||||
@ -50,11 +50,11 @@ func init() {
|
||||
|
||||
// S creates and returns a global unique string in 32 bytes that meets most common
|
||||
// usages without strict UUID algorithm. It returns an unique string using default
|
||||
// unique algorithm if no <data> is given.
|
||||
// unique algorithm if no `data` is given.
|
||||
//
|
||||
// The specified <data> can be no more than 2 parts. No matter how long each of the
|
||||
// <data> size is, each of them will be hashed into 7 bytes as part of the result.
|
||||
// If given <data> parts is less than 2, the leftover size of the result bytes will
|
||||
// The specified `data` can be no more than 2 parts. No matter how long each of the
|
||||
// `data` size is, each of them will be hashed into 7 bytes as part of the result.
|
||||
// If given `data` parts is less than 2, the leftover size of the result bytes will
|
||||
// be token by random string.
|
||||
//
|
||||
// The returned string is composed with:
|
||||
@ -63,7 +63,7 @@ func init() {
|
||||
//
|
||||
// Note that:
|
||||
// 1. The returned length is fixed to 32 bytes for performance purpose.
|
||||
// 2. The custom parameter <data> composed should have unique attribute in your
|
||||
// 2. The custom parameter `data` composed should have unique attribute in your
|
||||
// business situation.
|
||||
func S(data ...[]byte) string {
|
||||
var (
|
||||
@ -103,7 +103,7 @@ func getSequence() []byte {
|
||||
return b
|
||||
}
|
||||
|
||||
// getRandomStr randomly picks and returns <n> count of chars from randomStrBase.
|
||||
// getRandomStr randomly picks and returns `n` count of chars from randomStrBase.
|
||||
func getRandomStr(n int) []byte {
|
||||
if n <= 0 {
|
||||
return []byte{}
|
||||
|
||||
@ -32,7 +32,7 @@ func Try(try func()) (err error) {
|
||||
}
|
||||
|
||||
// TryCatch implements try...catch... logistics using internal panic...recover.
|
||||
// It automatically calls function <catch> if any exception occurs ans passes the exception as an error.
|
||||
// It automatically calls function `catch` if any exception occurs ans passes the exception as an error.
|
||||
func TryCatch(try func(), catch ...func(exception error)) {
|
||||
defer func() {
|
||||
if exception := recover(); exception != nil && len(catch) > 0 {
|
||||
@ -46,8 +46,8 @@ func TryCatch(try func(), catch ...func(exception error)) {
|
||||
try()
|
||||
}
|
||||
|
||||
// IsEmpty checks given <value> empty or not.
|
||||
// It returns false if <value> is: integer(0), bool(false), slice/map(len=0), nil;
|
||||
// IsEmpty checks given `value` empty or not.
|
||||
// It returns false if `value` is: integer(0), bool(false), slice/map(len=0), nil;
|
||||
// or else returns true.
|
||||
func IsEmpty(value interface{}) bool {
|
||||
return empty.IsEmpty(value)
|
||||
|
||||
@ -10,16 +10,16 @@ import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// ListItemValues retrieves and returns the elements of all item struct/map with key <key>.
|
||||
// Note that the parameter <list> should be type of slice which contains elements of map or struct,
|
||||
// ListItemValues retrieves and returns the elements of all item struct/map with key `key`.
|
||||
// Note that the parameter `list` should be type of slice which contains elements of map or struct,
|
||||
// or else it returns an empty slice.
|
||||
//
|
||||
// The parameter <list> supports types like:
|
||||
// The parameter `list` supports types like:
|
||||
// []map[string]interface{}
|
||||
// []map[string]sub-map
|
||||
// []struct
|
||||
// []struct:sub-struct
|
||||
// Note that the sub-map/sub-struct makes sense only if the optional parameter <subKey> is given.
|
||||
// Note that the sub-map/sub-struct makes sense only if the optional parameter `subKey` is given.
|
||||
func ListItemValues(list interface{}, key interface{}, subKey ...interface{}) (values []interface{}) {
|
||||
var reflectValue reflect.Value
|
||||
if v, ok := list.(reflect.Value); ok {
|
||||
@ -58,8 +58,8 @@ func ListItemValues(list interface{}, key interface{}, subKey ...interface{}) (v
|
||||
return
|
||||
}
|
||||
|
||||
// ItemValue retrieves and returns its value of which name/attribute specified by <key>.
|
||||
// The parameter <item> can be type of map/*map/struct/*struct.
|
||||
// ItemValue retrieves and returns its value of which name/attribute specified by `key`.
|
||||
// The parameter `item` can be type of map/*map/struct/*struct.
|
||||
func ItemValue(item interface{}, key interface{}) (value interface{}, found bool) {
|
||||
var reflectValue reflect.Value
|
||||
if v, ok := item.(reflect.Value); ok {
|
||||
@ -84,7 +84,7 @@ func ItemValue(item interface{}, key interface{}) (value interface{}, found bool
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Array, reflect.Slice:
|
||||
// The <key> must be type of string.
|
||||
// The `key` must be type of string.
|
||||
values := ListItemValues(reflectValue, keyValue.String())
|
||||
if values == nil {
|
||||
return nil, false
|
||||
@ -99,7 +99,7 @@ func ItemValue(item interface{}, key interface{}) (value interface{}, found bool
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
// The <mapKey> must be type of string.
|
||||
// The `mapKey` must be type of string.
|
||||
v := reflectValue.FieldByName(keyValue.String())
|
||||
if v.IsValid() {
|
||||
found = true
|
||||
@ -109,8 +109,8 @@ func ItemValue(item interface{}, key interface{}) (value interface{}, found bool
|
||||
return
|
||||
}
|
||||
|
||||
// ListItemValuesUnique retrieves and returns the unique elements of all struct/map with key <key>.
|
||||
// Note that the parameter <list> should be type of slice which contains elements of map or struct,
|
||||
// ListItemValuesUnique retrieves and returns the unique elements of all struct/map with key `key`.
|
||||
// Note that the parameter `list` should be type of slice which contains elements of map or struct,
|
||||
// or else it returns an empty slice.
|
||||
func ListItemValuesUnique(list interface{}, key string, subKey ...interface{}) []interface{} {
|
||||
values := ListItemValues(list, key, subKey...)
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// MapCopy does a shallow copy from map <data> to <copy> for most commonly used map type
|
||||
// MapCopy does a shallow copy from map `data` to `copy` for most commonly used map type
|
||||
// map[string]interface{}.
|
||||
func MapCopy(data map[string]interface{}) (copy map[string]interface{}) {
|
||||
copy = make(map[string]interface{}, len(data))
|
||||
@ -21,7 +21,7 @@ func MapCopy(data map[string]interface{}) (copy map[string]interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
// MapContains checks whether map <data> contains <key>.
|
||||
// MapContains checks whether map `data` contains `key`.
|
||||
func MapContains(data map[string]interface{}, key string) (ok bool) {
|
||||
if len(data) == 0 {
|
||||
return
|
||||
@ -30,7 +30,7 @@ func MapContains(data map[string]interface{}, key string) (ok bool) {
|
||||
return
|
||||
}
|
||||
|
||||
// MapDelete deletes all <keys> from map <data>.
|
||||
// MapDelete deletes all `keys` from map `data`.
|
||||
func MapDelete(data map[string]interface{}, keys ...string) {
|
||||
if len(data) == 0 {
|
||||
return
|
||||
@ -40,7 +40,7 @@ func MapDelete(data map[string]interface{}, keys ...string) {
|
||||
}
|
||||
}
|
||||
|
||||
// MapMerge merges all map from <src> to map <dst>.
|
||||
// MapMerge merges all map from `src` to map `dst`.
|
||||
func MapMerge(dst map[string]interface{}, src ...map[string]interface{}) {
|
||||
if dst == nil {
|
||||
return
|
||||
@ -52,7 +52,7 @@ func MapMerge(dst map[string]interface{}, src ...map[string]interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// MapMergeCopy creates and returns a new map which merges all map from <src>.
|
||||
// MapMergeCopy creates and returns a new map which merges all map from `src`.
|
||||
func MapMergeCopy(src ...map[string]interface{}) (copy map[string]interface{}) {
|
||||
copy = make(map[string]interface{})
|
||||
for _, m := range src {
|
||||
@ -82,7 +82,7 @@ func MapPossibleItemByKey(data map[string]interface{}, key string) (foundKey str
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// MapContainsPossibleKey checks if the given <key> is contained in given map <data>.
|
||||
// MapContainsPossibleKey checks if the given `key` is contained in given map `data`.
|
||||
// It checks the key ignoring cases and symbols.
|
||||
//
|
||||
// Note that this function might be of low performance.
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// SliceCopy does a shallow copy of slice <data> for most commonly used slice type
|
||||
// SliceCopy does a shallow copy of slice `data` for most commonly used slice type
|
||||
// []interface{}.
|
||||
func SliceCopy(data []interface{}) []interface{} {
|
||||
newData := make([]interface{}, len(data))
|
||||
@ -19,8 +19,8 @@ func SliceCopy(data []interface{}) []interface{} {
|
||||
return newData
|
||||
}
|
||||
|
||||
// SliceDelete deletes an element at <index> and returns the new slice.
|
||||
// It does nothing if the given <index> is invalid.
|
||||
// SliceDelete deletes an element at `index` and returns the new slice.
|
||||
// It does nothing if the given `index` is invalid.
|
||||
func SliceDelete(data []interface{}, index int) (newSlice []interface{}) {
|
||||
if index < 0 || index >= len(data) {
|
||||
return data
|
||||
|
||||
@ -163,12 +163,12 @@ var (
|
||||
// Check checks single value with specified rules.
|
||||
// It returns nil if successful validation.
|
||||
//
|
||||
// The parameter <value> can be any type of variable, which will be converted to string
|
||||
// The parameter `value` can be any type of variable, which will be converted to string
|
||||
// for validation.
|
||||
// The parameter <rules> can be one or more rules, multiple rules joined using char '|'.
|
||||
// The parameter <messages> specifies the custom error messages, which can be type of:
|
||||
// The parameter `rules` can be one or more rules, multiple rules joined using char '|'.
|
||||
// The parameter `messages` specifies the custom error messages, which can be type of:
|
||||
// string/map/struct/*struct.
|
||||
// The optional parameter <params> specifies the extra validation parameters for some rules
|
||||
// The optional parameter `params` specifies the extra validation parameters for some rules
|
||||
// like: required-*、same、different, etc.
|
||||
func Check(value interface{}, rules string, messages interface{}, params ...interface{}) *Error {
|
||||
return defaultValidator.Check(value, rules, messages, params...)
|
||||
@ -176,19 +176,19 @@ func Check(value interface{}, rules string, messages interface{}, params ...inte
|
||||
|
||||
// CheckMap validates map and returns the error result. It returns nil if with successful validation.
|
||||
//
|
||||
// The parameter <rules> can be type of []string/map[string]string. It supports sequence in error result
|
||||
// if <rules> is type of []string.
|
||||
// The optional parameter <messages> specifies the custom error messages for specified keys and rules.
|
||||
// The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result
|
||||
// if `rules` is type of []string.
|
||||
// The optional parameter `messages` specifies the custom error messages for specified keys and rules.
|
||||
func CheckMap(params interface{}, rules interface{}, messages ...CustomMsg) *Error {
|
||||
return defaultValidator.CheckMap(params, rules, messages...)
|
||||
}
|
||||
|
||||
// CheckStruct validates strcut and returns the error result.
|
||||
//
|
||||
// The parameter <object> should be type of struct/*struct.
|
||||
// The parameter <rules> can be type of []string/map[string]string. It supports sequence in error result
|
||||
// if <rules> is type of []string.
|
||||
// The optional parameter <messages> specifies the custom error messages for specified keys and rules.
|
||||
// The parameter `object` should be type of struct/*struct.
|
||||
// The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result
|
||||
// if `rules` is type of []string.
|
||||
// The optional parameter `messages` specifies the custom error messages for specified keys and rules.
|
||||
func CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *Error {
|
||||
return defaultValidator.CheckStruct(object, rules, messages...)
|
||||
}
|
||||
|
||||
@ -7,10 +7,10 @@
|
||||
package gvalid
|
||||
|
||||
// RuleFunc is the custom function for data validation.
|
||||
// The parameter <rule> specifies the validation rule string, like "required", "between:1,100", etc.
|
||||
// The parameter <value> specifies the value for this rule to validate.
|
||||
// The parameter <message> specifies the custom error message or configured i18n message for this rule.
|
||||
// The parameter <params> specifies all the parameters that needs. You can ignore parameter <params> if
|
||||
// The parameter `rule` specifies the validation rule string, like "required", "between:1,100", etc.
|
||||
// The parameter `value` specifies the value for this rule to validate.
|
||||
// The parameter `message` specifies the custom error message or configured i18n message for this rule.
|
||||
// The parameter `params` specifies all the parameters that needs. You can ignore parameter `params` if
|
||||
// you do not really need it in your custom validation rule.
|
||||
type RuleFunc func(rule string, value interface{}, message string, params map[string]interface{}) error
|
||||
|
||||
|
||||
@ -16,17 +16,23 @@ import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type apiTime interface {
|
||||
Date() (year int, month time.Month, day int)
|
||||
IsZero() bool
|
||||
}
|
||||
|
||||
// Check checks single value with specified rules.
|
||||
// It returns nil if successful validation.
|
||||
//
|
||||
// The parameter <value> can be any type of variable, which will be converted to string
|
||||
// The parameter `value` can be any type of variable, which will be converted to string
|
||||
// for validation.
|
||||
// The parameter <rules> can be one or more rules, multiple rules joined using char '|'.
|
||||
// The parameter <messages> specifies the custom error messages, which can be type of:
|
||||
// The parameter `rules` can be one or more rules, multiple rules joined using char '|'.
|
||||
// The parameter `messages` specifies the custom error messages, which can be type of:
|
||||
// string/map/struct/*struct.
|
||||
// The optional parameter <params> specifies the extra validation parameters for some rules
|
||||
// The optional parameter `params` specifies the extra validation parameters for some rules
|
||||
// like: required-*、same、different, etc.
|
||||
func (v *Validator) Check(value interface{}, rules string, messages interface{}, params ...interface{}) *Error {
|
||||
return v.doCheck("", value, rules, messages, params...)
|
||||
@ -196,6 +202,10 @@ func (v *Validator) doCheckBuildInRules(
|
||||
|
||||
// Date rules.
|
||||
case "date":
|
||||
// support for time value, eg: gtime.Time/*gtime.Time, time.Time/*time.Time.
|
||||
if v, ok := value.(apiTime); ok {
|
||||
return !v.IsZero(), nil
|
||||
}
|
||||
// Standard date string, which must contain char '-' or '.'.
|
||||
if _, err := gtime.StrToTime(valueStr); err == nil {
|
||||
match = true
|
||||
@ -209,6 +219,10 @@ func (v *Validator) doCheckBuildInRules(
|
||||
|
||||
// Date rule with specified format.
|
||||
case "date-format":
|
||||
// support for time value, eg: gtime.Time/*gtime.Time, time.Time/*time.Time.
|
||||
if v, ok := value.(apiTime); ok {
|
||||
return !v.IsZero(), nil
|
||||
}
|
||||
if _, err := gtime.StrToTimeFormat(valueStr, rulePattern); err == nil {
|
||||
match = true
|
||||
} else {
|
||||
|
||||
@ -13,9 +13,9 @@ import (
|
||||
|
||||
// CheckMap validates map and returns the error result. It returns nil if with successful validation.
|
||||
//
|
||||
// The parameter <rules> can be type of []string/map[string]string. It supports sequence in error result
|
||||
// if <rules> is type of []string.
|
||||
// The optional parameter <messages> specifies the custom error messages for specified keys and rules.
|
||||
// The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result
|
||||
// if `rules` is type of []string.
|
||||
// The optional parameter `messages` specifies the custom error messages for specified keys and rules.
|
||||
func (v *Validator) CheckMap(params interface{}, rules interface{}, messages ...CustomMsg) *Error {
|
||||
// If there's no validation rules, it does nothing and returns quickly.
|
||||
if params == nil || rules == nil {
|
||||
|
||||
@ -9,6 +9,7 @@ package gvalid
|
||||
import (
|
||||
"github.com/gogf/gf/internal/structs"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -17,13 +18,31 @@ var (
|
||||
aliasNameTagPriority = []string{"param", "params", "p"} // aliasNameTagPriority specifies the alias tag priority array.
|
||||
)
|
||||
|
||||
// CheckStruct validates strcut and returns the error result.
|
||||
// CheckStruct validates struct and returns the error result.
|
||||
//
|
||||
// The parameter <object> should be type of struct/*struct.
|
||||
// The parameter <rules> can be type of []string/map[string]string. It supports sequence in error result
|
||||
// if <rules> is type of []string.
|
||||
// The optional parameter <messages> specifies the custom error messages for specified keys and rules.
|
||||
// The parameter `object` should be type of struct/*struct.
|
||||
// The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result
|
||||
// if `rules` is type of []string.
|
||||
// The optional parameter `messages` specifies the custom error messages for specified keys and rules.
|
||||
func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *Error {
|
||||
var (
|
||||
errorMaps = make(ErrorMap) // Returned error.
|
||||
)
|
||||
mapField, err := structs.FieldMap(object, aliasNameTagPriority)
|
||||
if err != nil {
|
||||
return newErrorStr("invalid_object", err.Error())
|
||||
}
|
||||
// It checks the struct recursively the its attribute is also a struct.
|
||||
for _, field := range mapField {
|
||||
if field.OriginalKind() == reflect.Struct {
|
||||
if err := v.CheckStruct(field.Value, rules, messages...); err != nil {
|
||||
// It merges the errors into single error map.
|
||||
for k, m := range err.errors {
|
||||
errorMaps[k] = m
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// It here must use structs.TagFields not structs.FieldMap to ensure error sequence.
|
||||
tagField, err := structs.TagFields(object, structTagPriority)
|
||||
if err != nil {
|
||||
@ -37,9 +56,8 @@ func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages
|
||||
params = make(map[string]interface{})
|
||||
checkRules = make(map[string]string)
|
||||
customMessage = make(CustomMsg)
|
||||
fieldAliases = make(map[string]string) // Alias names for <messages> overwriting struct tag names.
|
||||
fieldAliases = make(map[string]string) // Alias names for `messages` overwriting struct tag names.
|
||||
errorRules = make([]string, 0) // Sequence rules.
|
||||
errorMaps = make(ErrorMap) // Returned error
|
||||
)
|
||||
switch v := rules.(type) {
|
||||
// Sequence tag: []sequence tag
|
||||
@ -85,10 +103,6 @@ func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages
|
||||
return nil
|
||||
}
|
||||
// Checks and extends the parameters map with struct alias tag.
|
||||
mapField, err := structs.FieldMap(object, aliasNameTagPriority)
|
||||
if err != nil {
|
||||
return newErrorStr("invalid_object", err.Error())
|
||||
}
|
||||
for nameOrTag, field := range mapField {
|
||||
params[nameOrTag] = field.Value.Interface()
|
||||
params[field.Name()] = field.Value.Interface()
|
||||
@ -145,7 +159,7 @@ func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages
|
||||
}
|
||||
|
||||
// Custom error messages,
|
||||
// which have the most priority than <rules> and struct tag.
|
||||
// which have the most priority than `rules` and struct tag.
|
||||
if len(messages) > 0 && len(messages[0]) > 0 {
|
||||
for k, v := range messages[0] {
|
||||
if a, ok := fieldAliases[k]; ok {
|
||||
@ -167,10 +181,10 @@ func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages
|
||||
// It checks each rule and its value in loop.
|
||||
if e := v.doCheck(key, value, rule, customMessage[key], params); e != nil {
|
||||
_, item := e.FirstItem()
|
||||
// ===========================================================
|
||||
// Only in map and struct validations, if value is nil or empty
|
||||
// string and has no required* rules, it clears the error message.
|
||||
// ===========================================================
|
||||
// ===================================================================
|
||||
// Only in map and struct validations, if value is nil or empty string
|
||||
// and has no required* rules, it clears the error message.
|
||||
// ===================================================================
|
||||
if value == nil || gconv.String(value) == "" {
|
||||
required := false
|
||||
// rule => error
|
||||
|
||||
@ -12,7 +12,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// checkLength checks <value> using length rules.
|
||||
// checkLength checks `value` using length rules.
|
||||
// The length is calculated using unicode string, which means one chinese character or letter
|
||||
// both has the length of 1.
|
||||
func (v *Validator) checkLength(value, ruleKey, ruleVal string, customMsgMap map[string]string) string {
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
package gvalid
|
||||
|
||||
// checkLuHn checks <value> with LUHN algorithm.
|
||||
// checkLuHn checks `value` with LUHN algorithm.
|
||||
// It's usually used for bank card number validation.
|
||||
func (v *Validator) checkLuHn(value string) bool {
|
||||
var (
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// checkRange checks <value> using range rules.
|
||||
// checkRange checks `value` using range rules.
|
||||
func (v *Validator) checkRange(value, ruleKey, ruleVal string, customMsgMap map[string]string) string {
|
||||
msg := ""
|
||||
switch ruleKey {
|
||||
|
||||
@ -12,7 +12,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// checkRequired checks <value> using required rules.
|
||||
// checkRequired checks `value` using required rules.
|
||||
// It also supports require checks for `value` of type: slice, map.
|
||||
func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string, params map[string]string) bool {
|
||||
required := false
|
||||
|
||||
@ -8,7 +8,9 @@ package gvalid_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
@ -205,6 +207,14 @@ func Test_DateFormat(t *testing.T) {
|
||||
t.Assert(err5, nil)
|
||||
t.AssertNE(err6, nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t1 := gtime.Now()
|
||||
t2 := time.Time{}
|
||||
err1 := gvalid.Check(t1, "date-format:Y", nil)
|
||||
err2 := gvalid.Check(t2, "date-format:Y", nil)
|
||||
t.Assert(err1, nil)
|
||||
t.AssertNE(err2, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Email(t *testing.T) {
|
||||
|
||||
@ -226,7 +226,7 @@ func Test_CheckStruct(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_CheckStruct_With_EmbedObject(t *testing.T) {
|
||||
func Test_CheckStruct_With_EmbeddedObject(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type Pass struct {
|
||||
Pass1 string `valid:"password1@required|same:password2#请输入您的密码|您两次输入的密码不一致"`
|
||||
@ -252,6 +252,32 @@ func Test_CheckStruct_With_EmbedObject(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_CheckStruct_With_StructAttribute(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type Pass struct {
|
||||
Pass1 string `valid:"password1@required|same:password2#请输入您的密码|您两次输入的密码不一致"`
|
||||
Pass2 string `valid:"password2@required|same:password1#请再次输入您的密码|您两次输入的密码不一致"`
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Name string `valid:"name@required#请输入您的姓名"`
|
||||
Passwords Pass
|
||||
}
|
||||
user := &User{
|
||||
Name: "",
|
||||
Passwords: Pass{
|
||||
Pass1: "1",
|
||||
Pass2: "2",
|
||||
},
|
||||
}
|
||||
err := gvalid.CheckStruct(user, nil)
|
||||
t.AssertNE(err, nil)
|
||||
t.Assert(err.Maps()["name"], g.Map{"required": "请输入您的姓名"})
|
||||
t.Assert(err.Maps()["password1"], g.Map{"same": "您两次输入的密码不一致"})
|
||||
t.Assert(err.Maps()["password2"], g.Map{"same": "您两次输入的密码不一致"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_CheckStruct_Optional(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type Params struct {
|
||||
@ -309,3 +335,20 @@ func Test_CheckStruct_NoTag(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_CheckStruct_InvalidRule(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type Params struct {
|
||||
Name string
|
||||
Age uint
|
||||
Phone string `v:"mobile"`
|
||||
}
|
||||
obj := &Params{
|
||||
Name: "john",
|
||||
Age: 18,
|
||||
Phone: "123",
|
||||
}
|
||||
err := gvalid.CheckStruct(obj, nil)
|
||||
t.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package gf
|
||||
|
||||
const VERSION = "v1.15.4"
|
||||
const VERSION = "v1.15.5"
|
||||
const AUTHORS = "john<john@goframe.org>"
|
||||
|
||||
Reference in New Issue
Block a user