Compare commits

..

40 Commits

Author SHA1 Message Date
d9754a532b version updates 2021-03-30 17:20:13 +08:00
dc51023c61 upgrade github.com/olekukonko/tablewriter from 0.0.1 to 0.0.5 2021-03-30 16:54:52 +08:00
87ce5c5419 Merge branch 'master' of https://github.com/gogf/gf 2021-03-30 16:48:19 +08:00
c4fc9f9618 fix issue #1204: improve validation support for time value, eg: gtime.Time/*gtime.Time, time.Time/*time.Time. 2021-03-30 16:06:20 +08:00
8d7aa5db83 Merge pull request #1205 from Zh1Cheung/master
bug:The parameter of the New method is a pointer
2021-03-30 15:39:31 +08:00
9c70fe3be8 add more unit testing case for package gvalid 2021-03-30 15:25:26 +08:00
78027d2ec6 deprecated comments update 2021-03-30 13:43:08 +08:00
d4e4b9addf improve package gcfg for automatically checking and adding path of package main 2021-03-29 18:05:47 +08:00
b2acd22f58 improve package gi18n 2021-03-29 17:36:51 +08:00
ae5ecb5bfa update unit testing case for package gtrace 2021-03-29 16:12:14 +08:00
6f1340ce36 upgrade otel from v0.18.0 to v0.19.0 2021-03-29 16:00:56 +08:00
473fdba14f add replace in go.mod for otel 2021-03-29 15:51:03 +08:00
93a01a1aaf bug:The parameter of the New method is a pointer 2021-03-27 16:21:28 +08:00
bb6883213f fix issue #1202 2021-03-24 22:04:04 +08:00
54395b39a6 fix issue #1202 2021-03-24 21:19:23 +08:00
6deb817fd0 fix issue in unit testing case for package gpage 2021-03-23 17:58:09 +08:00
482e093331 add map/[]map converting support for gconv.Scan;improve gconv.MaptoMaps 2021-03-23 17:53:20 +08:00
042f903157 Merge branch 'master' of https://github.com/gogf/gf 2021-03-23 14:23:47 +08:00
c423ad4830 fix issue in gfile.MainPkgPath;improve fields filter for package gdb 2021-03-23 14:21:54 +08:00
958b109a12 fix removing cookie issue 2021-03-19 19:16:21 +08:00
113fffdd69 Merge pull request #1199 from yangyanqing/yangyanqing/fix-zte-chinese 2021-03-19 17:08:31 +08:00
4aaf09fded improve gfile.MainPkgPath 2021-03-19 16:35:55 +08:00
2f3df76f37 fix issue #1190 2021-03-19 15:37:33 +08:00
cf7706b16d ZTE 的中文名应该是“中兴通讯”,不是“中兴科技” 2021-03-19 11:45:17 +08:00
36963c05a2 fix issue #1190 2021-03-19 11:35:12 +08:00
48d840a1b8 ad go.sum 2021-03-18 15:29:42 +08:00
e3ebc908bb improve package gconv 2021-03-18 15:21:05 +08:00
9ed8d8c113 rename max content log size name for gtrace from maxlogsize to maxcontentlogsize 2021-03-18 11:22:44 +08:00
9eab5daf19 update required minimum go version from 1.11 to 1.14 2021-03-18 11:19:36 +08:00
6a24b595f0 upgrade otel from 0.16.0 to 0.18.0 2021-03-18 10:39:23 +08:00
150f237f13 fix issue in incorrect parameter sequence in package gi18n;improve package gcfg for detailed error printing 2021-03-16 14:39:01 +08:00
41f2138b39 fix issue of overflow in grand.D 2021-03-11 23:29:39 +08:00
6376b8aaa6 remove session storage file removing feature for package gsession 2021-03-11 21:21:47 +08:00
58362ad143 add grand.D for random time.Duration;add checking and removing session files for package gsession 2021-03-11 20:05:08 +08:00
d72d23c2eb change browser mode from boolean markable variable to cookiejar for ghttp.Client 2021-03-11 18:58:13 +08:00
7702c5bfde add internal logging for package gdb/gredis 2021-03-11 11:39:49 +08:00
20f2a6c003 add recursive validation feature of struct attribute for package gvalid for #1165 2021-03-10 23:28:34 +08:00
0d4c1c47d5 improve package grand 2021-03-10 21:19:11 +08:00
4d32733790 improve ghttp.Client for #1179 2021-03-09 22:54:38 +08:00
0e58b6e95b README updates 2021-03-08 23:27:48 +08:00
93 changed files with 1474 additions and 1130 deletions

View File

@ -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")

View File

@ -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
View File

@ -14,5 +14,4 @@ bin/
cbuild
**/.DS_Store
.vscode/
go.sum
.example/other/

View File

@ -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:

View File

@ -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`.

View File

@ -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/)

View File

@ -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) {

View File

@ -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 {

View File

@ -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),
))
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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()
}

View File

@ -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)),
))
}

View File

@ -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.

View File

@ -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.

View File

@ -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,
))

View File

@ -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
View File

@ -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
View 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=

View File

@ -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...)
}

View File

@ -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.

View File

@ -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
}

View File

@ -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()
}

View File

@ -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()
}

View File

@ -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.

View File

@ -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)
})
}

View File

@ -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
}

View File

@ -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")
}

View File

@ -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
}

View File

@ -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(),
"...",
)),
))
}

View File

@ -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)
}

View File

@ -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())
}

View File

@ -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)
}

View File

@ -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")
})
}

View File

@ -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

View File

@ -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...)

View File

@ -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 {

View File

@ -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 {

View File

@ -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, "")
})
}

View File

@ -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)
})
}

View File

@ -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...)
}

View File

@ -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)
}

View File

@ -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 ""
}

View File

@ -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.

View File

@ -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 {

View File

@ -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

View File

@ -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) {

View File

@ -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
}

View File

@ -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{}

View File

@ -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
View 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
}
}

View 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
}

View 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
}

View File

@ -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)

View File

@ -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
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()))

View File

@ -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
}

View File

@ -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

View File

@ -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))

View File

@ -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)
})
}

View File

@ -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",
},
})
})
}

View File

@ -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"])
})
}

View File

@ -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(

View File

@ -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
}

View File

@ -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() {

View File

@ -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++ {

View File

@ -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{}

View File

@ -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)

View File

@ -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...)

View File

@ -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.

View File

@ -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

View File

@ -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...)
}

View File

@ -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

View File

@ -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 {

View File

@ -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 {

View File

@ -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

View File

@ -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 {

View File

@ -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 (

View File

@ -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 {

View File

@ -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

View File

@ -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) {

View File

@ -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)
})
}

View File

@ -1,4 +1,4 @@
package gf
const VERSION = "v1.15.4"
const VERSION = "v1.15.5"
const AUTHORS = "john<john@goframe.org>"