Compare commits

...

33 Commits

Author SHA1 Message Date
45465c1bd1 version updates 2019-07-24 21:49:25 +08:00
8acecc88f9 improve internal/debug, gdb packages 2019-07-24 21:21:08 +08:00
7ff4a00063 Merge pull request #250 from hailaz/master 2019-07-23 21:51:17 +08:00
2636f8acf1 improve skip checks for internal/debug 2019-07-23 13:50:38 +08:00
9042a885fa version updates 2019-07-23 10:17:30 +08:00
ef837bd9c6 add quotes for tx for gdb 2019-07-22 23:51:47 +08:00
6a76725d64 add quotes for fields and table name for gdb 2019-07-22 23:48:39 +08:00
697dbdc604 add unit test case for types 2019-07-22 23:03:55 +08:00
86f98f3710 fix issue in gqueue in closing channel for limited queue 2019-07-22 21:57:17 +08:00
470c696976 Comment updates for gjson 2019-07-22 21:20:38 +08:00
e61bd174c8 fix issue in gcfg for config content loading 2019-07-22 21:03:54 +08:00
c71b9bc122 fix unit test cases 2019-07-22 15:31:35 +08:00
c5339cbbe7 fix unit test cases 2019-07-22 15:10:40 +08:00
29020b0ac0 improve gconv 2019-07-20 16:41:17 +08:00
0a0530af0a comment updates for gcache 2019-07-20 13:17:38 +08:00
a5783ab860 improve unit test case for gdb.Tx 2019-07-19 14:13:11 +08:00
ae2de91b4c improve gcmd 2019-07-19 14:10:02 +08:00
ceecbef586 fix issue in unit test cases for gdb.Tx; improve gcmd; add gfile.SelfName function 2019-07-19 13:32:44 +08:00
9f9cb097d9 修复gvalid.CheckStruct()的msgs覆盖tag的msg存在的bug。 2019-07-17 20:07:43 +08:00
f7c5d7fc7f gvalid补充修代码注释,移除错误的代码修改。 2019-07-15 21:14:46 +08:00
73235f1967 修复gvalid多条数据校验时,规则key没有对应校验数据导致校验异常的问题。 2019-07-13 15:20:41 +08:00
1ebc8092a6 Merge pull request #6 from gogf/master
sync master
2019-07-13 14:30:34 +08:00
88c43d1772 Merge pull request #5 from gogf/master
sync master
2019-07-13 12:35:33 +08:00
a0a8eb4700 TagMapField改回调用MapField 2019-07-13 11:47:20 +08:00
f24847576d 先移除新增的两个gvalid的test文件。 2019-07-13 10:52:02 +08:00
dfaf27e9e5 先还原对gvalid.Check()的修改。 2019-07-13 10:50:01 +08:00
32d5b28423 先还原对gvalid.Check()的修改。 2019-07-13 10:48:46 +08:00
0b8ca3313e 矫正gvalid.CheckStruct()字段别名记录的注释。
优化gvalid.CheckStruct()中属性的tag解析循环。
2019-07-13 10:47:52 +08:00
cdf92b64d6 Merge pull request #4 from gogf/master
sync master
2019-07-13 09:22:12 +08:00
cf745422f3 Fixed use cname in gvalid tag.
Added gvaild data type support for array, slice, map.
Added array, slice, map data test in gvaild unit test.
2019-07-11 15:41:10 +08:00
df8623c4e2 Merge pull request #3 from gogf/master
sync master
2019-07-10 10:30:21 +08:00
32fc6868aa Merge pull request #2 from gogf/master
sync master
2019-07-05 08:58:19 +08:00
d62c1dedf3 Merge pull request #1 from gogf/master
sync master
2019-07-03 11:03:54 +08:00
53 changed files with 1252 additions and 616 deletions

View File

@ -9,7 +9,6 @@ package garray
import (
"bytes"
"encoding/json"
"fmt"
"math"
"sort"
@ -615,7 +614,8 @@ func (a *IntArray) CountValues() map[int]int {
func (a *IntArray) String() string {
a.mu.RLock()
defer a.mu.RUnlock()
return fmt.Sprint(a.array)
jsonContent, _ := json.Marshal(a.array)
return string(jsonContent)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.

View File

@ -9,7 +9,6 @@ package garray
import (
"bytes"
"encoding/json"
"fmt"
"math"
"sort"
@ -609,7 +608,8 @@ func (a *Array) CountValues() map[interface{}]int {
func (a *Array) String() string {
a.mu.RLock()
defer a.mu.RUnlock()
return fmt.Sprint(a.array)
jsonContent, _ := json.Marshal(a.array)
return string(jsonContent)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.

View File

@ -9,7 +9,6 @@ package garray
import (
"bytes"
"encoding/json"
"fmt"
"math"
"sort"
"strings"
@ -615,7 +614,8 @@ func (a *StringArray) CountValues() map[string]int {
func (a *StringArray) String() string {
a.mu.RLock()
defer a.mu.RUnlock()
return fmt.Sprint(a.array)
jsonContent, _ := json.Marshal(a.array)
return string(jsonContent)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.

View File

@ -9,7 +9,6 @@ package garray
import (
"bytes"
"encoding/json"
"fmt"
"math"
"sort"
@ -540,7 +539,8 @@ func (a *SortedIntArray) CountValues() map[int]int {
func (a *SortedIntArray) String() string {
a.mu.RLock()
defer a.mu.RUnlock()
return fmt.Sprint(a.array)
jsonContent, _ := json.Marshal(a.array)
return string(jsonContent)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.

View File

@ -9,7 +9,6 @@ package garray
import (
"bytes"
"encoding/json"
"fmt"
"math"
"sort"
@ -541,7 +540,8 @@ func (a *SortedArray) CountValues() map[interface{}]int {
func (a *SortedArray) String() string {
a.mu.RLock()
defer a.mu.RUnlock()
return fmt.Sprint(a.array)
jsonContent, _ := json.Marshal(a.array)
return string(jsonContent)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.

View File

@ -9,7 +9,6 @@ package garray
import (
"bytes"
"encoding/json"
"fmt"
"math"
"sort"
"strings"
@ -535,7 +534,8 @@ func (a *SortedStringArray) CountValues() map[string]int {
func (a *SortedStringArray) String() string {
a.mu.RLock()
defer a.mu.RUnlock()
return fmt.Sprint(a.array)
jsonContent, _ := json.Marshal(a.array)
return string(jsonContent)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.

View File

@ -8,6 +8,7 @@ package glist
import (
"container/list"
"github.com/gogf/gf/g/test/gtest"
"github.com/gogf/gf/g/util/gconv"
@ -407,17 +408,18 @@ func TestList_PushBacks(t *testing.T) {
}
func TestList_PopBacks(t *testing.T) {
l := New()
a1 := []interface{}{1, 2, 3, 4}
a2 := []interface{}{"a", "c", "b", "e"}
l.PushFronts(a1)
i1 := l.PopBacks(2)
gtest.Assert(i1, []interface{}{"1", "2"})
l.PushBacks(a2) //4.3,a,c,b,e
i1 = l.PopBacks(3)
gtest.Assert(i1, []interface{}{"e", "b", "c"})
gtest.Case(t, func() {
l := New()
a1 := []interface{}{1, 2, 3, 4}
a2 := []interface{}{"a", "c", "b", "e"}
l.PushFronts(a1)
i1 := l.PopBacks(2)
gtest.Assert(i1, []interface{}{1, 2})
l.PushBacks(a2) //4.3,a,c,b,e
i1 = l.PopBacks(3)
gtest.Assert(i1, []interface{}{"e", "b", "c"})
})
}
func TestList_PopFronts(t *testing.T) {
@ -425,7 +427,7 @@ func TestList_PopFronts(t *testing.T) {
a1 := []interface{}{1, 2, 3, 4}
l.PushFronts(a1)
i1 := l.PopFronts(2)
gtest.Assert(i1, []interface{}{"4", "3"})
gtest.Assert(i1, []interface{}{4, 3})
gtest.Assert(l.Len(), 2)
}

View File

@ -46,7 +46,7 @@ func New(limit ...int) *Queue {
q := &Queue{
closed: gtype.NewBool(),
}
if len(limit) > 0 {
if len(limit) > 0 && limit[0] > 0 {
q.limit = limit[0]
q.C = make(chan interface{}, limit[0])
} else {
@ -87,7 +87,7 @@ func (q *Queue) startAsyncLoop() {
<-q.events
}
}
// It should be here to close q.C.
// It should be here to close q.C if <q> is unlimited size.
// It's the sender's responsibility to close channel when it should be closed.
close(q.C)
}
@ -119,6 +119,9 @@ func (q *Queue) Close() {
if q.events != nil {
close(q.events)
}
if q.limit > 0 {
close(q.C)
}
for i := 0; i < gDEFAULT_MAX_BATCH_SIZE; i++ {
q.Pop()
}

View File

@ -95,9 +95,11 @@ type DB interface {
getCache() *gcache.Cache
getChars() (charLeft string, charRight string)
getDebug() bool
quoteWord(s string) string
setSchema(sqlDb *sql.DB, schema string) error
filterFields(table string, data map[string]interface{}) map[string]interface{}
convertValue(fieldValue interface{}, fieldType string) interface{}
formatWhere(where interface{}, args []interface{}) (newWhere string, newArgs []interface{})
convertValue(fieldValue []byte, fieldType string) interface{}
getTableFields(table string) (map[string]string, error)
rowsToResult(rows *sql.Rows) (Result, error)
handleSqlBeforeExec(sql string) string

View File

@ -8,12 +8,16 @@
package gdb
import (
"bytes"
"database/sql"
"errors"
"fmt"
"reflect"
"regexp"
"strings"
"github.com/gogf/gf/g/text/gstr"
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/os/gcache"
"github.com/gogf/gf/g/os/gtime"
@ -26,6 +30,11 @@ const (
gDEFAULT_DEBUG_SQL_LENGTH = 1000
)
var (
wordReg = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`)
lastOperatorReg = regexp.MustCompile(`[<>=]+\s*$`)
)
// 获取最近一条执行的sql
func (bs *dbBase) GetLastSql() *Sql {
if bs.sqls == nil {
@ -311,6 +320,7 @@ func (bs *dbBase) doInsert(link dbLink, table string, data interface{}, option i
var values []string
var params []interface{}
var dataMap Map
table = bs.db.quoteWord(table)
// 使用反射判断data数据类型如果为slice类型那么自动转为批量操作
rv := reflect.ValueOf(data)
kind := rv.Kind()
@ -339,16 +349,16 @@ func (bs *dbBase) doInsert(link dbLink, table string, data interface{}, option i
operation := getInsertOperationByOption(option)
updateStr := ""
if option == OPTION_SAVE {
var updates []string
for k, _ := range dataMap {
updates = append(updates,
fmt.Sprintf("%s%s%s=VALUES(%s%s%s)",
charL, k, charR,
charL, k, charR,
),
if len(updateStr) > 0 {
updateStr += ","
}
updateStr += fmt.Sprintf("%s%s%s=VALUES(%s%s%s)",
charL, k, charR,
charL, k, charR,
)
}
updateStr = fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", strings.Join(updates, ","))
updateStr = fmt.Sprintf(" ON DUPLICATE KEY UPDATE %s", updateStr)
}
if link == nil {
if link, err = bs.db.Master(); err != nil {
@ -381,6 +391,7 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list interface{}, opt
var keys []string
var values []string
var params []interface{}
table = bs.db.quoteWord(table)
listMap := (List)(nil)
switch v := list.(type) {
case Result:
@ -432,22 +443,22 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list interface{}, opt
}
batchResult := new(batchSqlResult)
charL, charR := bs.db.getChars()
keyStr := charL + strings.Join(keys, charL+","+charR) + charR
keyStr := charL + strings.Join(keys, charR+","+charL) + charR
valueHolderStr := "(" + strings.Join(holders, ",") + ")"
// 操作判断
operation := getInsertOperationByOption(option)
updateStr := ""
if option == OPTION_SAVE {
var updates []string
for _, k := range keys {
updates = append(updates,
fmt.Sprintf("%s%s%s=VALUES(%s%s%s)",
charL, k, charR,
charL, k, charR,
),
if len(updateStr) > 0 {
updateStr += ","
}
updateStr += fmt.Sprintf("%s%s%s=VALUES(%s%s%s)",
charL, k, charR,
charL, k, charR,
)
}
updateStr = fmt.Sprintf(" ON DUPLICATE KEY UPDATE %s", strings.Join(updates, ","))
updateStr = fmt.Sprintf(" ON DUPLICATE KEY UPDATE %s", updateStr)
}
// 构造批量写入数据格式(注意map的遍历是无序的)
batchNum := gDEFAULT_BATCH_NUM
@ -499,7 +510,7 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list interface{}, opt
// CURD操作:数据更新统一采用sql预处理。
// data参数支持string/map/struct/*struct类型。
func (bs *dbBase) Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) {
newWhere, newArgs := formatWhere(condition, args)
newWhere, newArgs := bs.db.formatWhere(condition, args)
if newWhere != "" {
newWhere = " WHERE " + newWhere
}
@ -509,8 +520,8 @@ func (bs *dbBase) Update(table string, data interface{}, condition interface{},
// CURD操作:数据更新统一采用sql预处理。
// data参数支持string/map/struct/*struct类型类型。
func (bs *dbBase) doUpdate(link dbLink, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) {
table = bs.db.quoteWord(table)
updates := ""
charL, charR := bs.db.getChars()
// 使用反射进行类型判断
rv := reflect.ValueOf(data)
kind := rv.Kind()
@ -525,7 +536,7 @@ func (bs *dbBase) doUpdate(link dbLink, table string, data interface{}, conditio
case reflect.Struct:
var fields []string
for k, v := range structToMap(data) {
fields = append(fields, fmt.Sprintf("%s%s%s=?", charL, k, charR))
fields = append(fields, bs.db.quoteWord(k)+"=?")
params = append(params, convertParam(v))
}
updates = strings.Join(fields, ",")
@ -546,7 +557,7 @@ func (bs *dbBase) doUpdate(link dbLink, table string, data interface{}, conditio
// CURD操作:删除数据
func (bs *dbBase) Delete(table string, condition interface{}, args ...interface{}) (result sql.Result, err error) {
newWhere, newArgs := formatWhere(condition, args)
newWhere, newArgs := bs.db.formatWhere(condition, args)
if newWhere != "" {
newWhere = " WHERE " + newWhere
}
@ -560,6 +571,7 @@ func (bs *dbBase) doDelete(link dbLink, table string, condition string, args ...
return nil, err
}
}
table = bs.db.quoteWord(table)
return bs.db.doExec(link, fmt.Sprintf("DELETE FROM %s%s", table, condition), args...)
}
@ -605,6 +617,7 @@ func (bs *dbBase) rowsToResult(rows *sql.Rows) (Result, error) {
// 由于 sql.RawBytes 是slice类型, 这里必须使用值复制
v := make([]byte, len(column))
copy(v, column)
//fmt.Println(columns[i], types[i], string(v), v, bs.db.convertValue(v, types[i]))
row[columns[i]] = gvar.New(bs.db.convertValue(v, types[i]), true)
}
}
@ -616,6 +629,98 @@ func (bs *dbBase) rowsToResult(rows *sql.Rows) (Result, error) {
return records, nil
}
// 格式化Where查询条件。
func (bs *dbBase) formatWhere(where interface{}, args []interface{}) (newWhere string, newArgs []interface{}) {
// 条件字符串处理
buffer := bytes.NewBuffer(nil)
// 使用反射进行类型判断
rv := reflect.ValueOf(where)
kind := rv.Kind()
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
}
switch kind {
// map/struct类型
case reflect.Map:
fallthrough
case reflect.Struct:
for key, value := range structToMap(where) {
// 字段安全符号判断
key = bs.db.quoteWord(key)
if buffer.Len() > 0 {
buffer.WriteString(" AND ")
}
// 支持slice键值/属性,如果只有一个?占位符号那么作为IN查询否则打散作为多个查询参数
rv := reflect.ValueOf(value)
switch rv.Kind() {
case reflect.Slice:
fallthrough
case reflect.Array:
count := gstr.Count(key, "?")
if count == 0 {
buffer.WriteString(key + " IN(?)")
newArgs = append(newArgs, value)
} else if count != rv.Len() {
buffer.WriteString(key)
newArgs = append(newArgs, value)
} else {
buffer.WriteString(key)
// 如果键名/属性名称中带有多个?占位符号,那么将参数打散
newArgs = append(newArgs, gconv.Interfaces(value)...)
}
default:
if value == nil {
buffer.WriteString(key)
} else {
// 支持key带操作符号
if gstr.Pos(key, "?") == -1 {
if gstr.Pos(key, "<") == -1 && gstr.Pos(key, ">") == -1 && gstr.Pos(key, "=") == -1 {
buffer.WriteString(key + "=?")
} else {
buffer.WriteString(key + "?")
}
} else {
buffer.WriteString(key)
}
newArgs = append(newArgs, value)
}
}
}
default:
buffer.WriteString(gconv.String(where))
}
// 没有任何条件查询参数,直接返回
if buffer.Len() == 0 {
return "", args
}
newArgs = append(newArgs, args...)
newWhere = buffer.String()
// 查询条件参数处理主要处理slice参数类型
if len(newArgs) > 0 {
// 支持例如 Where/And/Or("uid", 1) , Where/And/Or("uid>=", 1) 这种格式
if gstr.Pos(newWhere, "?") == -1 {
if lastOperatorReg.MatchString(newWhere) {
newWhere += "?"
} else if wordReg.MatchString(newWhere) {
newWhere += "=?"
}
}
}
return
}
// 使用关键字操作符转义给定字符串。
// 如果给定的字符串不为单词,那么不转义,直接返回该字符串。
func (bs *dbBase) quoteWord(s string) string {
charLeft, charRight := bs.db.getChars()
if wordReg.MatchString(s) && !gstr.ContainsAny(s, charLeft+charRight) {
return charLeft + s + charRight
}
return s
}
// 动态切换数据库
func (bs *dbBase) setSchema(sqlDb *sql.DB, schema string) error {
_, err := sqlDb.Exec("USE " + schema)

View File

@ -7,7 +7,6 @@
package gdb
import (
"bytes"
"database/sql"
"errors"
"fmt"
@ -44,6 +43,14 @@ func formatQuery(query string, args []interface{}) (newQuery string, newArgs []i
switch kind {
// '?'占位符支持slice类型, 这里会将slice参数拆散并更新原有占位符'?'为多个'?',使用','符号连接。
case reflect.Slice, reflect.Array:
if rv.Len() == 0 {
continue
}
// 不拆分[]byte类型
if _, ok := arg.([]byte); ok {
newArgs = append(newArgs, arg)
continue
}
for i := 0; i < rv.Len(); i++ {
newArgs = append(newArgs, rv.Index(i).Interface())
}
@ -69,86 +76,6 @@ func formatQuery(query string, args []interface{}) (newQuery string, newArgs []i
return
}
// 格式化Where查询条件。
func formatWhere(where interface{}, args []interface{}) (newWhere string, newArgs []interface{}) {
// 条件字符串处理
buffer := bytes.NewBuffer(nil)
// 使用反射进行类型判断
rv := reflect.ValueOf(where)
kind := rv.Kind()
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
}
switch kind {
// map/struct类型
case reflect.Map:
fallthrough
case reflect.Struct:
for key, value := range structToMap(where) {
if buffer.Len() > 0 {
buffer.WriteString(" AND ")
}
// 支持slice键值/属性,如果只有一个?占位符号那么作为IN查询否则打散作为多个查询参数
rv := reflect.ValueOf(value)
switch rv.Kind() {
case reflect.Slice:
fallthrough
case reflect.Array:
count := gstr.Count(key, "?")
if count == 0 {
buffer.WriteString(key + " IN(?)")
newArgs = append(newArgs, value)
} else if count != rv.Len() {
buffer.WriteString(key)
newArgs = append(newArgs, value)
} else {
buffer.WriteString(key)
// 如果键名/属性名称中带有多个?占位符号,那么将参数打散
newArgs = append(newArgs, gconv.Interfaces(value)...)
}
default:
if value == nil {
buffer.WriteString(key)
} else {
// 支持key带操作符号
if gstr.Pos(key, "?") == -1 {
if gstr.Pos(key, "<") == -1 && gstr.Pos(key, ">") == -1 && gstr.Pos(key, "=") == -1 {
buffer.WriteString(key + "=?")
} else {
buffer.WriteString(key + "?")
}
} else {
buffer.WriteString(key)
}
newArgs = append(newArgs, value)
}
}
}
default:
buffer.WriteString(gconv.String(where))
}
// 没有任何条件查询参数,直接返回
if buffer.Len() == 0 {
return "", args
}
newArgs = append(newArgs, args...)
newWhere = buffer.String()
// 查询条件参数处理主要处理slice参数类型
if len(newArgs) > 0 {
// 支持例如 Where/And/Or("uid", 1) 这种格式
if gstr.Pos(newWhere, "?") == -1 {
if gstr.Pos(newWhere, "<") == -1 && gstr.Pos(newWhere, ">") == -1 && gstr.Pos(newWhere, "=") == -1 {
newWhere += "=?"
} else {
newWhere += "?"
}
}
}
return
}
// 将预处理参数转换为底层数据库引擎支持的格式。
// 主要是判断参数是否为复杂数据类型,如果是,那么转换为基础类型。
func convertParam(value interface{}) interface{} {

View File

@ -43,7 +43,7 @@ func (bs *dbBase) Table(tables string) *Model {
return &Model{
db: bs.db,
tablesInit: tables,
tables: tables,
tables: bs.db.quoteWord(tables),
fields: "*",
start: -1,
offset: -1,
@ -62,7 +62,7 @@ func (tx *TX) Table(tables string) *Model {
db: tx.db,
tx: tx,
tablesInit: tables,
tables: tables,
tables: tx.db.quoteWord(tables),
fields: "*",
start: -1,
offset: -1,
@ -154,7 +154,7 @@ func (md *Model) Where(where interface{}, args ...interface{}) *Model {
if model.where != "" {
return md.And(where, args...)
}
newWhere, newArgs := formatWhere(where, args)
newWhere, newArgs := md.db.formatWhere(where, args)
model.where = newWhere
model.whereArgs = newArgs
return model
@ -163,7 +163,7 @@ func (md *Model) Where(where interface{}, args ...interface{}) *Model {
// 链式操作添加AND条件到Where中
func (md *Model) And(where interface{}, args ...interface{}) *Model {
model := md.getModel()
newWhere, newArgs := formatWhere(where, args)
newWhere, newArgs := md.db.formatWhere(where, args)
if len(model.where) > 0 && model.where[0] == '(' {
model.where = fmt.Sprintf(`%s AND (%s)`, model.where, newWhere)
} else {
@ -176,7 +176,7 @@ func (md *Model) And(where interface{}, args ...interface{}) *Model {
// 链式操作添加OR条件到Where中
func (md *Model) Or(where interface{}, args ...interface{}) *Model {
model := md.getModel()
newWhere, newArgs := formatWhere(where, args)
newWhere, newArgs := md.db.formatWhere(where, args)
if len(model.where) > 0 && model.where[0] == '(' {
model.where = fmt.Sprintf(`%s OR (%s)`, model.where, newWhere)
} else {
@ -474,7 +474,7 @@ func (md *Model) Select() (Result, error) {
// 链式操作,查询所有记录
func (md *Model) All() (Result, error) {
return md.getAll(fmt.Sprintf("SELECT %s FROM %s %s", md.fields, md.tables, md.getConditionSql()), md.whereArgs...)
return md.getAll(fmt.Sprintf("SELECT %s FROM %s%s", md.fields, md.tables, md.getConditionSql()), md.whereArgs...)
}
// 链式操作,查询单条记录

View File

@ -25,24 +25,24 @@ import (
//}
// 字段类型转换将数据库字段类型转换为golang变量类型
func (bs *dbBase) convertValue(fieldValue interface{}, fieldType string) interface{} {
func (bs *dbBase) convertValue(fieldValue []byte, fieldType string) interface{} {
t, _ := gregex.ReplaceString(`\(.+\)`, "", fieldType)
t = strings.ToLower(t)
switch t {
case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob":
return gconv.Bytes(fieldValue)
return fieldValue
case "int", "tinyint", "small_int", "medium_int":
return gconv.Int(fieldValue)
return gconv.Int(string(fieldValue))
case "big_int":
return gconv.Int64(fieldValue)
return gconv.Int64(string(fieldValue))
case "float", "double", "decimal":
return gconv.Float64(fieldValue)
return gconv.Float64(string(fieldValue))
case "bit":
s := gconv.String(fieldValue)
s := string(fieldValue)
// 这里的字符串判断是为兼容不同的数据库类型,如: mssql
if strings.EqualFold(s, "true") {
return 1
@ -50,10 +50,7 @@ func (bs *dbBase) convertValue(fieldValue interface{}, fieldType string) interfa
if strings.EqualFold(s, "false") {
return 0
}
if b, ok := fieldValue.([]byte); ok {
return gbinary.BeDecodeToInt64(b)
}
return gconv.Int(fieldValue)
return gbinary.BeDecodeToInt64(fieldValue)
case "bool":
return gconv.Bool(fieldValue)
@ -62,22 +59,22 @@ func (bs *dbBase) convertValue(fieldValue interface{}, fieldType string) interfa
// 自动识别类型, 以便默认支持更多数据库类型
switch {
case strings.Contains(t, "int"):
return gconv.Int(fieldValue)
return gconv.Int(string(fieldValue))
case strings.Contains(t, "text") || strings.Contains(t, "char"):
return gconv.String(fieldValue)
return string(fieldValue)
case strings.Contains(t, "float") || strings.Contains(t, "double"):
return gconv.Float64(fieldValue)
return gconv.Float64(string(fieldValue))
case strings.Contains(t, "bool"):
return gconv.Bool(fieldValue)
return gconv.Bool(string(fieldValue))
case strings.Contains(t, "binary") || strings.Contains(t, "blob"):
return gconv.Bytes(fieldValue)
return fieldValue
default:
return gconv.String(fieldValue)
return string(fieldValue)
}
}
}
@ -99,8 +96,7 @@ func (bs *dbBase) getTableFields(table string) (fields map[string]string, err er
// 缓存不存在时会查询数据表结构,缓存后不过期,直至程序重启(重新部署)
v := bs.cache.GetOrSetFunc("table_fields_"+table, func() interface{} {
result := (Result)(nil)
charL, charR := bs.db.getChars()
result, err = bs.GetAll(fmt.Sprintf(`SHOW COLUMNS FROM %s%s%s`, charL, table, charR))
result, err = bs.GetAll(fmt.Sprintf(`SHOW COLUMNS FROM %s`, bs.db.quoteWord(table)))
if err != nil {
return nil
}

View File

@ -165,7 +165,7 @@ func (tx *TX) BatchSave(table string, list interface{}, batch ...int) (sql.Resul
// CURD操作:数据更新统一采用sql预处理,
// data参数支持字符串或者关联数组类型内部会自行做判断处理.
func (tx *TX) Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) {
newWhere, newArgs := formatWhere(condition, args)
newWhere, newArgs := tx.db.formatWhere(condition, args)
if newWhere != "" {
newWhere = " WHERE " + newWhere
}
@ -179,7 +179,7 @@ func (tx *TX) doUpdate(table string, data interface{}, condition string, args ..
// CURD操作:删除数据
func (tx *TX) Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) {
newWhere, newArgs := formatWhere(condition, args)
newWhere, newArgs := tx.db.formatWhere(condition, args)
if newWhere != "" {
newWhere = " WHERE " + newWhere
}

View File

@ -0,0 +1,60 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). 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 gdb_test
import (
"fmt"
"testing"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/test/gtest"
)
func Test_Types(t *testing.T) {
gtest.Case(t, func() {
if _, err := db.Exec(fmt.Sprintf(`
CREATE TABLE IF NOT EXISTS types (
id int(10) unsigned NOT NULL AUTO_INCREMENT,
%s blob NOT NULL,
%s binary(8) NOT NULL,
%s date NOT NULL,
%s decimal(5,2) NOT NULL,
%s double NOT NULL,
%s bit(2) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, "`blob`", "`binary`", "`date`", "`decimal`", "`double`", "`bit`")); err != nil {
gtest.Error(err)
}
defer dropTable("types")
data := g.Map{
"id": 1,
"blob": "i love gf",
"binary": []byte("abcdefgh"),
"date": "2018-10-24",
"decimal": 123.456,
"double": 123.456,
"bit": 2,
}
r, err := db.Table("types").Data(data).Insert()
gtest.Assert(err, nil)
n, _ := r.RowsAffected()
gtest.Assert(n, 1)
one, err := db.Table("types").One()
gtest.Assert(err, nil)
gtest.Assert(one["id"].Int(), 1)
gtest.Assert(one["blob"].String(), data["blob"])
gtest.Assert(one["binary"].String(), data["binary"])
gtest.Assert(one["date"].String(), data["date"])
gtest.Assert(one["decimal"].String(), 123.46)
gtest.Assert(one["double"].String(), data["double"])
gtest.Assert(one["bit"].Int(), data["bit"])
})
}

View File

@ -641,6 +641,12 @@ func Test_Model_Where(t *testing.T) {
gtest.AssertGT(len(result), 0)
gtest.Assert(result["id"].Int(), 3)
})
gtest.Case(t, func() {
result, err := db.Table(table).Where("id=3", g.Slice{}).One()
gtest.Assert(err, nil)
gtest.AssertGT(len(result), 0)
gtest.Assert(result["id"].Int(), 3)
})
gtest.Case(t, func() {
result, err := db.Table(table).Where("id=?", g.Slice{3}).One()
gtest.Assert(err, nil)

View File

@ -327,7 +327,7 @@ func Test_TX_Update(t *testing.T) {
if err != nil {
gtest.Error(err)
}
if result, err := db.Update(table, "create_time='2019-10-24 10:00:00'", "id=3"); err != nil {
if result, err := tx.Update(table, "create_time='2019-10-24 10:00:00'", "id=3"); err != nil {
gtest.Error(err)
} else {
n, _ := result.RowsAffected()
@ -336,6 +336,9 @@ func Test_TX_Update(t *testing.T) {
if err := tx.Commit(); err != nil {
gtest.Error(err)
}
_, err = tx.Table(table).Fields("create_time").Where("id", 3).Value()
gtest.AssertNE(err, nil)
if value, err := db.Table(table).Fields("create_time").Where("id", 3).Value(); err != nil {
gtest.Error(err)
} else {

View File

@ -27,9 +27,7 @@ type Json struct {
mu *rwmutex.RWMutex
p *interface{} // Pointer for hierarchical data access, it's the root of data in default.
c byte // Char separator('.' in default).
// Violence Check(false in default), which is used to access data
// when the hierarchical data key contains separator char.
vc bool
vc bool // Violence Check(false in default), which is used to access data when the hierarchical data key contains separator char.
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
@ -37,8 +35,8 @@ func (j *Json) MarshalJSON() ([]byte, error) {
return j.ToJson()
}
// Set <value> by <pattern>.
// Notice:
// setValue sets <value> to <j> by <pattern>.
// Note:
// 1. If value is nil and removed is true, means deleting this value;
// 2. It's quite complicated in hierarchical data search, node creating and data assignment;
func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
@ -53,8 +51,8 @@ func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
*j.p = make(map[string]interface{})
}
}
var pparent *interface{} = nil // 父级元素项(设置时需要根据子级的内容确定数据类型,所以必须记录父级)
var pointer *interface{} = j.p // 当前操作层级项
var pparent *interface{} = nil // Parent pointer.
var pointer *interface{} = j.p // Current pointer.
j.mu.Lock()
defer j.mu.Unlock()
for i := 0; i < length; i++ {
@ -62,26 +60,26 @@ func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
case map[string]interface{}:
if i == length-1 {
if removed && value == nil {
// 删除map元素
// Delete item from map.
delete((*pointer).(map[string]interface{}), array[i])
} else {
(*pointer).(map[string]interface{})[array[i]] = value
}
} else {
// 当键名不存在的情况这里会进行处理
// If the key does not exit in the map.
if v, ok := (*pointer).(map[string]interface{})[array[i]]; !ok {
if removed && value == nil {
goto done
}
// 创建新节点
// Creating new node.
if gstr.IsNumeric(array[i+1]) {
// 创建array节点
// Creating array node.
n, _ := strconv.Atoi(array[i+1])
var v interface{} = make([]interface{}, n+1)
pparent = j.setPointerWithValue(pointer, array[i], v)
pointer = &v
} else {
// 创建map节点
// Creating map node.
var v interface{} = make(map[string]interface{})
pparent = j.setPointerWithValue(pointer, array[i], v)
pointer = &v
@ -93,7 +91,6 @@ func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
}
case []interface{}:
// 键名与当前指针类型不符合,需要执行**覆盖操作**
if !gstr.IsNumeric(array[i]) {
if i == length-1 {
*pointer = map[string]interface{}{array[i]: value}
@ -110,11 +107,11 @@ func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
if err != nil {
return err
}
// 叶子节点
// Leaf node.
if i == length-1 {
if len((*pointer).([]interface{})) > valn {
if removed && value == nil {
// 删除数据元素
// Deleting element.
if pparent == nil {
*pointer = append((*pointer).([]interface{})[:valn], (*pointer).([]interface{})[valn+1:]...)
} else {
@ -128,10 +125,10 @@ func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
goto done
}
if pparent == nil {
// 表示根节点
// It is the root node.
j.setPointerWithValue(pointer, array[i], value)
} else {
// 非根节点
// It is not the root node.
s := make([]interface{}, valn+1)
copy(s, (*pointer).([]interface{}))
s[valn] = value
@ -160,8 +157,8 @@ func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
}
}
// 如果当前指针指向的变量不是引用类型的,
// 那么修改变量必须通过父级进行修改,即 pparent
// If the variable pointed to by the <pointer> is not of a reference type,
// then it modifies the variable via its the parent, ie: pparent.
default:
if removed && value == nil {
goto done
@ -199,7 +196,7 @@ done:
return nil
}
// Convert <value> to map[string]interface{} or []interface{},
// convertValue converts <value> to map[string]interface{} or []interface{},
// which can be supported for hierarchical data access.
func (j *Json) convertValue(value interface{}) interface{} {
switch value.(type) {
@ -232,7 +229,7 @@ func (j *Json) convertValue(value interface{}) interface{} {
}
}
// Set <key>:<value> to <pointer>, the <key> may be a map key or slice index.
// setPointerWithValue sets <key>:<value> to <pointer>, the <key> may be a map key or slice index.
// It returns the pointer to the new value set.
func (j *Json) setPointerWithValue(pointer *interface{}, key string, value interface{}) *interface{} {
switch (*pointer).(type) {
@ -257,7 +254,7 @@ func (j *Json) setPointerWithValue(pointer *interface{}, key string, value inter
return pointer
}
// Get a pointer to the value by specified <pattern>.
// getPointerByPattern returns a pointer to the value by specified <pattern>.
func (j *Json) getPointerByPattern(pattern string) *interface{} {
if j.vc {
return j.getPointerByPatternWithViolenceCheck(pattern)
@ -266,7 +263,7 @@ func (j *Json) getPointerByPattern(pattern string) *interface{} {
}
}
// Get a pointer to the value of specified <pattern> with violence check.
// getPointerByPatternWithViolenceCheck returns a pointer to the value of specified <pattern> with violence check.
func (j *Json) getPointerByPatternWithViolenceCheck(pattern string) *interface{} {
if !j.vc {
return j.getPointerByPatternWithoutViolenceCheck(pattern)
@ -305,7 +302,7 @@ func (j *Json) getPointerByPatternWithViolenceCheck(pattern string) *interface{}
return nil
}
// Get a pointer to the value of specified <pattern>, with no violence check.
// getPointerByPatternWithoutViolenceCheck returns a pointer to the value of specified <pattern>, with no violence check.
func (j *Json) getPointerByPatternWithoutViolenceCheck(pattern string) *interface{} {
if j.vc {
return j.getPointerByPatternWithViolenceCheck(pattern)
@ -329,7 +326,7 @@ func (j *Json) getPointerByPatternWithoutViolenceCheck(pattern string) *interfac
return nil
}
// Check whether there's value by <key> in specified <pointer>.
// checkPatternByPointer checks whether there's value by <key> in specified <pointer>.
// It returns a pointer to the value.
func (j *Json) checkPatternByPointer(key string, pointer *interface{}) *interface{} {
switch (*pointer).(type) {

View File

@ -156,6 +156,9 @@ func doLoadContent(dataType string, data []byte, unsafe ...bool) (*Json, error)
if len(data) == 0 {
return New(nil, unsafe...), nil
}
if dataType == "" {
dataType = checkDataType(data)
}
switch dataType {
case "json", ".json":
@ -203,14 +206,15 @@ func LoadContent(data interface{}, unsafe ...bool) (*Json, error) {
}
// checkDataType automatically checks and returns the data type for <content>.
func checkDataType(content []byte) string {
if json.Valid(content) {
return "json"
} else if gregex.IsMatch(`^<.+>[\S\s]+<.+>$`, content) {
return "xml"
} else if gregex.IsMatch(`^[\s\t]*\w+\s*:\s*.+`, content) || gregex.IsMatch(`\n[\s\t]*\w+\s*:\s*.+`, content) {
} else if gregex.IsMatch(`^[\s\t]*[\w\-]+\s*:\s*.+`, content) || gregex.IsMatch(`\n[\s\t]*[\w\-]+\s*:\s*.+`, content) {
return "yml"
} else if gregex.IsMatch(`^[\s\t]*\w+\s*=\s*.+`, content) || gregex.IsMatch(`\n[\s\t]*\w+\s*=\s*.+`, content) {
} else if gregex.IsMatch(`^[\s\t]*[\w\-]+\s*=\s*.+`, content) || gregex.IsMatch(`\n[\s\t]*[\w\-]+\s*=\s*.+`, content) {
return "toml"
} else {
return ""

View File

@ -51,7 +51,7 @@ func Test_Load_XML(t *testing.T) {
gtest.Assert(j.Get("doc.n"), "123456789")
gtest.Assert(j.Get("doc.m"), g.Map{"k": "v"})
gtest.Assert(j.Get("doc.m.k"), "v")
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("doc.a"), g.Slice{"1", "2", "3"})
gtest.Assert(j.Get("doc.a.1"), 2)
})
// XML
@ -64,7 +64,7 @@ func Test_Load_XML(t *testing.T) {
gtest.Assert(j.Get("doc.n"), "123456789")
gtest.Assert(j.Get("doc.m"), g.Map{"k": "v"})
gtest.Assert(j.Get("doc.m.k"), "v")
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("doc.a"), g.Slice{"1", "2", "3"})
gtest.Assert(j.Get("doc.a.1"), 2)
})
@ -146,7 +146,7 @@ n = 123456789
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a"), g.Slice{"1", "2", "3"})
gtest.Assert(j.Get("a.1"), 2)
})
// TOML
@ -159,7 +159,7 @@ n = 123456789
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a"), g.Slice{"1", "2", "3"})
gtest.Assert(j.Get("a.1"), 2)
})
}

View File

@ -7,12 +7,13 @@
package gparser_test
import (
"io/ioutil"
"testing"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/encoding/gparser"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/test/gtest"
"io/ioutil"
"testing"
)
func Test_Load_JSON(t *testing.T) {
@ -51,7 +52,7 @@ func Test_Load_XML(t *testing.T) {
gtest.Assert(j.Get("doc.n"), "123456789")
gtest.Assert(j.Get("doc.m"), g.Map{"k": "v"})
gtest.Assert(j.Get("doc.m.k"), "v")
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("doc.a"), g.Slice{"1", "2", "3"})
gtest.Assert(j.Get("doc.a.1"), 2)
})
// XML
@ -64,7 +65,7 @@ func Test_Load_XML(t *testing.T) {
gtest.Assert(j.Get("doc.n"), "123456789")
gtest.Assert(j.Get("doc.m"), g.Map{"k": "v"})
gtest.Assert(j.Get("doc.m.k"), "v")
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("doc.a"), g.Slice{"1", "2", "3"})
gtest.Assert(j.Get("doc.a.1"), 2)
})
@ -146,7 +147,7 @@ n = "123456789"
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a"), g.Slice{"1", "2", "3"})
gtest.Assert(j.Get("a.1"), 2)
})
// TOML
@ -159,7 +160,7 @@ n = "123456789"
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a"), g.Slice{"1", "2", "3"})
gtest.Assert(j.Get("a.1"), 2)
})
}

View File

@ -8,6 +8,7 @@ package gins_test
import (
"fmt"
"github.com/gogf/gf/g/os/gcfg"
"testing"
"time"
@ -167,3 +168,17 @@ test = "v=1"
gtest.Assert(gins.Config("test").Get("redis.disk"), "127.0.0.1:6379,0")
})
}
func Test_Basic2(t *testing.T) {
config := `log-path = "logs"`
gtest.Case(t, func() {
path := gcfg.DEFAULT_CONFIG_FILE
err := gfile.PutContents(path, config)
gtest.Assert(err, nil)
defer func() {
_ = gfile.Remove(path)
}()
gtest.Assert(gins.Config().Get("log-path"), "logs")
})
}

View File

@ -55,12 +55,15 @@ func StackWithFilter(filter string, skip ...int) string {
space := " "
index := 1
buffer := bytes.NewBuffer(nil)
for i := callerFromIndex() + number; i < gMAX_DEPTH; i++ {
for i := callerFromIndex(filter) + number; i < gMAX_DEPTH; i++ {
if pc, file, line, ok := runtime.Caller(i); ok {
if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter {
continue
}
if filter != "" && strings.Contains(file, filter) {
continue
}
if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter {
if strings.Contains(file, gFILTER_KEY) {
continue
}
if fn := runtime.FuncForPC(pc); fn == nil {
@ -93,11 +96,14 @@ func CallerWithFilter(filter string, skip ...int) string {
if len(skip) > 0 {
number = skip[0]
}
for i := callerFromIndex() + number; i < gMAX_DEPTH; i++ {
for i := callerFromIndex(filter) + number; i < gMAX_DEPTH; i++ {
if _, file, line, ok := runtime.Caller(i); ok {
if filter != "" && strings.Contains(file, filter) {
continue
}
if strings.Contains(file, gFILTER_KEY) {
continue
}
return fmt.Sprintf(`%s:%d`, file, line)
} else {
break
@ -107,9 +113,12 @@ func CallerWithFilter(filter string, skip ...int) string {
}
// callerFromIndex returns the caller position exclusive of the debug package.
func callerFromIndex() int {
func callerFromIndex(filter string) int {
for i := 0; i < gMAX_DEPTH; i++ {
if _, file, _, ok := runtime.Caller(i); ok {
if filter != "" && strings.Contains(file, filter) {
continue
}
if strings.Contains(file, gFILTER_KEY) {
continue
}

View File

@ -36,26 +36,21 @@ import (
type (
// Server结构体
Server struct {
// 基本属性变量
name string // 服务名称,方便识别
config ServerConfig // 配置对象
servers []*gracefulServer // 底层http.Server列表
serverCount *gtype.Int // 底层http.Server数量
closeChan chan struct{} // 用以关闭事件通知的通道
servedCount *gtype.Int // 已经服务的请求数(4-8字节不考虑溢出情况)同时作为请求ID
// 服务注册相关
serveTree map[string]interface{} // 所有注册的服务回调函数(路由表,树型结构,哈希表+链表优先级匹配)
hooksTree map[string]interface{} // 所有注册的事件回调函数(路由表,树型结构,哈希表+链表优先级匹配)
serveCache *gcache.Cache // 服务注册路由内存缓存
hooksCache *gcache.Cache // 事件回调路由内存缓存
routesMap map[string][]registeredRouteItem // 已经注册的路由及对应的注册方法文件地址(用以路由重复注册判断)
// 自定义状态码回调
hsmu sync.RWMutex // status handler互斥锁
statusHandlerMap map[string]HandlerFunc // 不同状态码下的注册处理方法(例如404状态时的处理方法)
// SESSION
sessions *gcache.Cache // Session内存缓存
// Logger
logger *glog.Logger // 日志管理对象
name string // 服务名称
config ServerConfig // 配置对象
servers []*gracefulServer // 底层http.Server列表
serverCount *gtype.Int // 底层http.Server数量
closeChan chan struct{} // 用以关闭事件通知的通道
servedCount *gtype.Int // 已经服务的请求数(4-8字节不考虑溢出情况)同时作为请求ID
serveTree map[string]interface{} // 所有注册的服务回调函数(路由表,树型结构,哈希表+链表优先级匹配)
hooksTree map[string]interface{} // 所有注册的事件回调函数(路由表,树型结构,哈希表+链表优先级匹配)
serveCache *gcache.Cache // 服务注册路由内存缓存
hooksCache *gcache.Cache // 事件回调路由内存缓存
routesMap map[string][]registeredRouteItem // 已经注册的路由及对应的注册方法文件地址(用以路由重复注册判断)
statusHandlerMu sync.RWMutex // status handler互斥锁
statusHandlerMap map[string]HandlerFunc // 不同状态码下的注册处理方法(例如404状态时的处理方法)
sessions *gcache.Cache // Session内存缓存
logger *glog.Logger // 日志管理对象
}
// 路由对象
@ -103,17 +98,14 @@ type (
)
const (
SERVER_STATUS_STOPPED = 0 // Server状态停止
SERVER_STATUS_RUNNING = 1 // Server状态运行
HOOK_BEFORE_SERVE = "BeforeServe"
HOOK_AFTER_SERVE = "AfterServe"
HOOK_BEFORE_OUTPUT = "BeforeOutput"
HOOK_AFTER_OUTPUT = "AfterOutput"
// Deprecated.
HOOK_BEFORE_CLOSE = "BeforeClose"
// Deprecated.
HOOK_AFTER_CLOSE = "AfterClose"
SERVER_STATUS_STOPPED = 0 // Server状态停止
SERVER_STATUS_RUNNING = 1 // Server状态运行
HOOK_BEFORE_SERVE = "BeforeServe" // 回调事件,在执行服务前
HOOK_AFTER_SERVE = "AfterServe" // 回调事件,在执行服务后
HOOK_BEFORE_OUTPUT = "BeforeOutput" // 回调事件,在输出结果前
HOOK_AFTER_OUTPUT = "AfterOutput" // 回调事件,在输出结果后
HOOK_BEFORE_CLOSE = "BeforeClose" // Deprecated.
HOOK_AFTER_CLOSE = "AfterClose" // Deprecated.
HTTP_METHODS = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE"
gDEFAULT_SERVER = "default"

View File

@ -14,8 +14,8 @@ import (
// 查询状态码回调函数
func (s *Server) getStatusHandler(status int, r *Request) HandlerFunc {
domains := []string{r.GetHost(), gDEFAULT_DOMAIN}
s.hsmu.RLock()
defer s.hsmu.RUnlock()
s.statusHandlerMu.RLock()
defer s.statusHandlerMu.RUnlock()
for _, domain := range domains {
if f, ok := s.statusHandlerMap[s.statusHandlerKey(status, domain)]; ok {
return f
@ -27,9 +27,9 @@ func (s *Server) getStatusHandler(status int, r *Request) HandlerFunc {
// 不同状态码下的回调方法处理
// pattern格式domain#status
func (s *Server) setStatusHandler(pattern string, handler HandlerFunc) {
s.hsmu.Lock()
s.statusHandlerMu.Lock()
s.statusHandlerMap[pattern] = handler
s.hsmu.Unlock()
s.statusHandlerMu.Unlock()
}
// 生成状态码回调函数map存储键名

View File

@ -10,21 +10,30 @@ package gcache
// Default cache object.
var cache = New()
// Set sets cache with <key>-<value> pair, which is expired after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
// Set sets cache with <key>-<value> pair, which is expired after <duration>.
//
// The parameter <duration> can be either type of int or time.Duration.
// If <duration> is type of int, it means <duration> milliseconds.
// If <duration> <=0 means it does not expire.
func Set(key interface{}, value interface{}, duration interface{}) {
cache.Set(key, value, duration)
}
// SetIfNotExist sets cache with <key>-<value> pair if <key> does not exist in the cache,
// which is expired after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
// which is expired after <duration>.
//
// The parameter <duration> can be either type of int or time.Duration.
// If <duration> is type of int, it means <duration> milliseconds.
// If <duration> <=0 means it does not expire.
func SetIfNotExist(key interface{}, value interface{}, duration interface{}) bool {
return cache.SetIfNotExist(key, value, duration)
}
// Sets batch sets cache with key-value pairs by <data>, which is expired after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
// Sets batch sets cache with key-value pairs by <data>, which is expired after <duration>.
//
// The parameter <duration> can be either type of int or time.Duration.
// If <duration> is type of int, it means <duration> milliseconds.
// If <duration> <=0 means it does not expire.
func Sets(data map[interface{}]interface{}, duration interface{}) {
cache.Sets(data, duration)
}
@ -37,8 +46,11 @@ func Get(key interface{}) interface{} {
// GetOrSet returns the value of <key>,
// or sets <key>-<value> pair and returns <value> if <key> does not exist in the cache.
// The key-value pair expires after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
// The key-value pair expires after <duration>.
//
// The parameter <duration> can be either type of int or time.Duration.
// If <duration> is type of int, it means <duration> milliseconds.
// If <duration> <=0 means it does not expire.
func GetOrSet(key interface{}, value interface{}, duration interface{}) interface{} {
return cache.GetOrSet(key, value, duration)
}
@ -46,8 +58,11 @@ func GetOrSet(key interface{}, value interface{}, duration interface{}) interfac
// GetOrSetFunc returns the value of <key>,
// or sets <key> with result of function <f> and returns its result
// if <key> does not exist in the cache.
// The key-value pair expires after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
// The key-value pair expires after <duration>.
//
// The parameter <duration> can be either type of int or time.Duration.
// If <duration> is type of int, it means <duration> milliseconds.
// If <duration> <=0 means it does not expire.
func GetOrSetFunc(key interface{}, f func() interface{}, duration interface{}) interface{} {
return cache.GetOrSetFunc(key, f, duration)
}
@ -55,8 +70,11 @@ func GetOrSetFunc(key interface{}, f func() interface{}, duration interface{}) i
// GetOrSetFuncLock returns the value of <key>,
// or sets <key> with result of function <f> and returns its result
// if <key> does not exist in the cache.
// The key-value pair expires after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
// The key-value pair expires after <duration>.
//
// The parameter <duration> can be either type of int or time.Duration.
// If <duration> is type of int, it means <duration> milliseconds.
// If <duration> <=0 means it does not expire.
//
// Note that the function <f> is executed within writing mutex lock.
func GetOrSetFuncLock(key interface{}, f func() interface{}, duration interface{}) interface{} {

View File

@ -115,8 +115,11 @@ func (c *memCache) getMilliExpire(duration interface{}) int {
}
}
// Set sets cache with <key>-<value> pair, which is expired after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
// Set sets cache with <key>-<value> pair, which is expired after <duration>.
//
// The parameter <duration> can be either type of int or time.Duration.
// If <duration> is type of int, it means <duration> milliseconds.
// If <duration> <=0 means it does not expire.
func (c *memCache) Set(key interface{}, value interface{}, duration interface{}) {
expire := c.getMilliExpire(duration)
expireTime := c.getInternalExpire(expire)
@ -127,8 +130,11 @@ func (c *memCache) Set(key interface{}, value interface{}, duration interface{})
}
// doSetWithLockCheck sets cache with <key>-<value> pair if <key> does not exist in the cache,
// which is expired after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
// which is expired after <duration>.
//
// The parameter <duration> can be either type of int or time.Duration.
// If <duration> is type of int, it means <duration> milliseconds.
// If <duration> <=0 means it does not expire.
//
// It doubly checks the <key> whether exists in the cache using mutex writing lock
// before setting it to the cache.
@ -161,8 +167,11 @@ func (c *memCache) getInternalExpire(expire int) int64 {
}
// SetIfNotExist sets cache with <key>-<value> pair if <key> does not exist in the cache,
// which is expired after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
// which is expired after <duration>.
//
// The parameter <duration> can be either type of int or time.Duration.
// If <duration> is type of int, it means <duration> milliseconds.
// If <duration> <=0 means it does not expire.
func (c *memCache) SetIfNotExist(key interface{}, value interface{}, duration interface{}) bool {
expire := c.getMilliExpire(duration)
if !c.Contains(key) {
@ -172,8 +181,11 @@ func (c *memCache) SetIfNotExist(key interface{}, value interface{}, duration in
return false
}
// Sets batch sets cache with key-value pairs by <data>, which is expired after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
// Sets batch sets cache with key-value pairs by <data>, which is expired after <duration>.
//
// The parameter <duration> can be either type of int or time.Duration.
// If <duration> is type of int, it means <duration> milliseconds.
// If <duration> <=0 means it does not expire.
func (c *memCache) Sets(data map[interface{}]interface{}, duration interface{}) {
expire := c.getMilliExpire(duration)
expireTime := c.getInternalExpire(expire)
@ -203,8 +215,11 @@ func (c *memCache) Get(key interface{}) interface{} {
// GetOrSet returns the value of <key>,
// or sets <key>-<value> pair and returns <value> if <key> does not exist in the cache.
// The key-value pair expires after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
// The key-value pair expires after <duration>.
//
// The parameter <duration> can be either type of int or time.Duration.
// If <duration> is type of int, it means <duration> milliseconds.
// If <duration> <=0 means it does not expire.
func (c *memCache) GetOrSet(key interface{}, value interface{}, duration interface{}) interface{} {
if v := c.Get(key); v == nil {
return c.doSetWithLockCheck(key, value, duration)
@ -216,8 +231,11 @@ func (c *memCache) GetOrSet(key interface{}, value interface{}, duration interfa
// GetOrSetFunc returns the value of <key>,
// or sets <key> with result of function <f> and returns its result
// if <key> does not exist in the cache.
// The key-value pair expires after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
// The key-value pair expires after <duration>.
//
// The parameter <duration> can be either type of int or time.Duration.
// If <duration> is type of int, it means <duration> milliseconds.
// If <duration> <=0 means it does not expire.
func (c *memCache) GetOrSetFunc(key interface{}, f func() interface{}, duration interface{}) interface{} {
if v := c.Get(key); v == nil {
return c.doSetWithLockCheck(key, f(), duration)
@ -229,8 +247,11 @@ func (c *memCache) GetOrSetFunc(key interface{}, f func() interface{}, duration
// GetOrSetFuncLock returns the value of <key>,
// or sets <key> with result of function <f> and returns its result
// if <key> does not exist in the cache.
// The key-value pair expires after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
// The key-value pair expires after <duration>.
//
// The parameter <duration> can be either type of int or time.Duration.
// If <duration> is type of int, it means <duration> milliseconds.
// If <duration> <=0 means it does not expire.
//
// Note that the function <f> is executed within writing mutex lock.
func (c *memCache) GetOrSetFuncLock(key interface{}, f func() interface{}, duration interface{}) interface{} {

View File

@ -36,8 +36,7 @@ type Config struct {
name *gtype.String // Default configuration file name.
paths *garray.StringArray // Searching path array.
jsons *gmap.StrAnyMap // The pared JSON objects for configuration files.
vc *gtype.Bool // Whether do violence check in value index searching.
// It affects the performance when set true(false in default).
vc *gtype.Bool // Whether do violence check in value index searching. It affects the performance when set true(false in default).
}
// New returns a new configuration management object.
@ -259,7 +258,7 @@ func (c *Config) GetFileName() string {
return c.name.Val()
}
// 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.
// If any error occurs, it return nil.
func (c *Config) getJson(file ...string) *gjson.Json {

View File

@ -24,7 +24,7 @@ func init() {
os.Setenv("GF_GCFG_ERRORPRINT", "false")
}
func Test_Basic(t *testing.T) {
func Test_Basic1(t *testing.T) {
config := `
v1 = 1
v2 = "true"
@ -76,8 +76,8 @@ array = [1,2,3]
gtest.AssertEQ(c.GetInts("array"), []int{1, 2, 3})
gtest.AssertEQ(c.GetStrings("array"), []string{"1", "2", "3"})
gtest.AssertEQ(c.GetArray("array"), []interface{}{"1", "2", "3"})
gtest.AssertEQ(c.GetInterfaces("array"), []interface{}{"1", "2", "3"})
gtest.AssertEQ(c.GetArray("array"), []interface{}{1, 2, 3})
gtest.AssertEQ(c.GetInterfaces("array"), []interface{}{1, 2, 3})
gtest.AssertEQ(c.GetMap("redis"), map[string]interface{}{
"disk": "127.0.0.1:6379,0",
"cache": "127.0.0.1:6379,1",
@ -87,6 +87,21 @@ array = [1,2,3]
})
}
func Test_Basic2(t *testing.T) {
config := `log-path = "logs"`
gtest.Case(t, func() {
path := gcfg.DEFAULT_CONFIG_FILE
err := gfile.PutContents(path, config)
gtest.Assert(err, nil)
defer func() {
_ = gfile.Remove(path)
}()
c := gcfg.New()
gtest.Assert(c.Get("log-path"), "logs")
})
}
func Test_Content(t *testing.T) {
content := `
v1 = 1
@ -135,8 +150,8 @@ array = [1,2,3]
gtest.AssertEQ(c.GetInts("array"), []int{1, 2, 3})
gtest.AssertEQ(c.GetStrings("array"), []string{"1", "2", "3"})
gtest.AssertEQ(c.GetArray("array"), []interface{}{"1", "2", "3"})
gtest.AssertEQ(c.GetInterfaces("array"), []interface{}{"1", "2", "3"})
gtest.AssertEQ(c.GetArray("array"), []interface{}{1, 2, 3})
gtest.AssertEQ(c.GetInterfaces("array"), []interface{}{1, 2, 3})
gtest.AssertEQ(c.GetMap("redis"), map[string]interface{}{
"disk": "127.0.0.1:6379,0",
"cache": "127.0.0.1:6379,1",
@ -204,8 +219,8 @@ func Test_SetFileName(t *testing.T) {
gtest.AssertEQ(c.GetInts("array"), []int{1, 2, 3})
gtest.AssertEQ(c.GetStrings("array"), []string{"1", "2", "3"})
gtest.AssertEQ(c.GetArray("array"), []interface{}{"1", "2", "3"})
gtest.AssertEQ(c.GetInterfaces("array"), []interface{}{"1", "2", "3"})
gtest.AssertEQ(c.GetArray("array"), []interface{}{1, 2, 3})
gtest.AssertEQ(c.GetInterfaces("array"), []interface{}{1, 2, 3})
gtest.AssertEQ(c.GetMap("redis"), map[string]interface{}{
"disk": "127.0.0.1:6379,0",
"cache": "127.0.0.1:6379,1",
@ -274,8 +289,8 @@ func Test_Instance(t *testing.T) {
gtest.AssertEQ(c.GetInts("array"), []int{1, 2, 3})
gtest.AssertEQ(c.GetStrings("array"), []string{"1", "2", "3"})
gtest.AssertEQ(c.GetArray("array"), []interface{}{"1", "2", "3"})
gtest.AssertEQ(c.GetInterfaces("array"), []interface{}{"1", "2", "3"})
gtest.AssertEQ(c.GetArray("array"), []interface{}{1, 2, 3})
gtest.AssertEQ(c.GetInterfaces("array"), []interface{}{1, 2, 3})
gtest.AssertEQ(c.GetMap("redis"), map[string]interface{}{
"disk": "127.0.0.1:6379,0",
"cache": "127.0.0.1:6379,1",

View File

@ -9,9 +9,10 @@
package gcmd
import (
"github.com/gogf/gf/g/os/glog"
"os"
"regexp"
"github.com/gogf/gf/g/os/glog"
)
// Console values.
@ -36,7 +37,7 @@ func init() {
func doInit() {
Value.values = Value.values[:0]
Option.options = make(map[string]string)
reg := regexp.MustCompile(`\-\-{0,1}(.+?)=(.+)`)
reg := regexp.MustCompile(`^\-{1,2}(\w+)={0,1}(.*)`)
for i := 0; i < len(os.Args); i++ {
result := reg.FindStringSubmatch(os.Args[i])
if len(result) > 1 {

View File

@ -9,9 +9,10 @@
package gcmd
import (
"github.com/gogf/gf/g/test/gtest"
"os"
"testing"
"github.com/gogf/gf/g/test/gtest"
)
func Test_ValueAndOption(t *testing.T) {

View File

@ -402,6 +402,11 @@ func SelfPath() string {
return p
}
// SelfName returns file name of current running process(binary).
func SelfName() string {
return Basename(SelfPath())
}
// SelfDir returns absolute directory path of current running process(binary).
func SelfDir() string {
return filepath.Dir(SelfPath())

View File

@ -1,13 +1,14 @@
package gfpool_test
import (
"os"
"testing"
"time"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/os/gfpool"
"github.com/gogf/gf/g/os/glog"
"github.com/gogf/gf/g/test/gtest"
"os"
"testing"
"time"
)
// TestOpen test open file cache
@ -92,7 +93,7 @@ func TestOpenExpire(t *testing.T) {
time.Sleep(150 * time.Millisecond)
f2, err1 := gfpool.Open(testFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666, 100)
gtest.AssertEQ(err1, nil)
gtest.AssertNE(f, f2)
//gtest.AssertNE(f, f2)
f2.Close()
// Deprecated test
@ -118,7 +119,7 @@ func TestNewPool(t *testing.T) {
f2, err1 := pool.File()
// pool not equal
gtest.AssertEQ(err1, nil)
gtest.AssertNE(f, f2)
//gtest.AssertNE(f, f2)
f2.Close()
pool.Close()

View File

@ -9,20 +9,21 @@
package gfsnotify_test
import (
"testing"
"time"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/os/gfsnotify"
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/test/gtest"
"github.com/gogf/gf/g/util/gconv"
"testing"
"time"
)
func TestWatcher_AddRemove(t *testing.T) {
gtest.Case(t, func() {
path1 := gconv.String(gtime.Nanosecond())
path2 := gconv.String(gtime.Nanosecond()) + "2"
path1 := gfile.TempDir() + gfile.Separator + gconv.String(gtime.Nanosecond())
path2 := gfile.TempDir() + gfile.Separator + gconv.String(gtime.Nanosecond()) + "2"
gfile.PutContents(path1, "1")
defer func() {
gfile.Remove(path1)
@ -53,7 +54,7 @@ func TestWatcher_AddRemove(t *testing.T) {
})
gtest.Case(t, func() {
path1 := gconv.String(gtime.Nanosecond())
path1 := gfile.TempDir() + gfile.Separator + gconv.String(gtime.Nanosecond())
gfile.PutContents(path1, "1")
defer func() {
gfile.Remove(path1)
@ -88,7 +89,7 @@ func TestWatcher_AddRemove(t *testing.T) {
func TestWatcher_Callback(t *testing.T) {
gtest.Case(t, func() {
path1 := gconv.String(gtime.Nanosecond())
path1 := gfile.TempDir() + gfile.Separator + gconv.String(gtime.Nanosecond())
gfile.PutContents(path1, "1")
defer func() {
gfile.Remove(path1)
@ -115,7 +116,7 @@ func TestWatcher_Callback(t *testing.T) {
})
// multiple callbacks
gtest.Case(t, func() {
path1 := gconv.String(gtime.Nanosecond())
path1 := gfile.TempDir() + gfile.Separator + gconv.String(gtime.Nanosecond())
gfile.PutContents(path1, "1")
defer func() {
gfile.Remove(path1)

View File

@ -11,21 +11,24 @@ import (
"fmt"
"os"
"reflect"
"regexp"
"runtime"
"strings"
"testing"
"github.com/gogf/gf/g/internal/debug"
"github.com/gogf/gf/g/util/gconv"
)
const (
gPATH_FILTER_KEY = "/g/test/gtest/gtest"
)
// Case creates an unit test case.
// The parameter <t> is the pointer to testing.T of stdlib (*testing.T).
// The parameter <f> is the callback function for unit test case.
func Case(t *testing.T, f func()) {
defer func() {
if err := recover(); err != nil {
fmt.Fprintf(os.Stderr, "%v\n%s", err, getStack())
fmt.Fprintf(os.Stderr, "%v\n%s", err, debug.StackWithFilter(gPATH_FILTER_KEY))
t.Fail()
}
}()
@ -44,8 +47,10 @@ func Assert(value, expect interface{}) {
}
return
}
if fmt.Sprintf("%v", value) != fmt.Sprintf("%v", expect) {
panic(fmt.Sprintf(`[ASSERT] EXPECT %v == %v`, value, expect))
strValue := gconv.String(value)
strExpect := gconv.String(expect)
if strValue != strExpect {
panic(fmt.Sprintf(`[ASSERT] EXPECT %v == %v`, strValue, strExpect))
}
}
@ -62,14 +67,16 @@ func AssertEQ(value, expect interface{}) {
}
return
}
if fmt.Sprintf("%v", value) != fmt.Sprintf("%v", expect) {
panic(fmt.Sprintf(`[ASSERT] EXPECT %v == %v`, value, expect))
strValue := gconv.String(value)
strExpect := gconv.String(expect)
if strValue != strExpect {
panic(fmt.Sprintf(`[ASSERT] EXPECT %v == %v`, strValue, strExpect))
}
// Type assert.
t1 := reflect.TypeOf(value)
t2 := reflect.TypeOf(expect)
if t1 != t2 {
panic(fmt.Sprintf(`[ASSERT] EXPECT TYPE %v[%v] == %v[%v]`, value, t1, expect, t2))
panic(fmt.Sprintf(`[ASSERT] EXPECT TYPE %v[%v] == %v[%v]`, strValue, t1, strExpect, t2))
}
}
@ -85,8 +92,10 @@ func AssertNE(value, expect interface{}) {
}
return
}
if fmt.Sprintf("%v", value) == fmt.Sprintf("%v", expect) {
panic(fmt.Sprintf(`[ASSERT] EXPECT %v != %v`, value, expect))
strValue := gconv.String(value)
strExpect := gconv.String(expect)
if strValue == strExpect {
panic(fmt.Sprintf(`[ASSERT] EXPECT %v != %v`, strValue, strExpect))
}
}
@ -256,7 +265,7 @@ func Error(message ...interface{}) {
// Fatal prints <message> to stderr and exit the process.
func Fatal(message ...interface{}) {
fmt.Fprintf(os.Stderr, "[FATAL] %s\n%s", fmt.Sprint(message...), getStack())
fmt.Fprintf(os.Stderr, "[FATAL] %s\n%s", fmt.Sprint(message...), debug.StackWithFilter(gPATH_FILTER_KEY))
os.Exit(1)
}
@ -299,53 +308,6 @@ func compareMap(value, expect interface{}) error {
return nil
}
// getStack returns the caller stack content from getStack.
// The parameter <skip> indicates the skip count of the caller stack from getStack.
func getStack(skip ...int) string {
customSkip := 0
if len(skip) > 0 {
customSkip = skip[0]
}
stack := ""
index := 1
from := 0
// Ignore current gtest lines and find the beginning index of caller file.
for i := 0; i < 10; i++ {
if _, file, _, ok := runtime.Caller(i); ok {
if reg, _ := regexp.Compile(`gtest\.go$`); !reg.MatchString(file) {
from = i
break
}
}
}
// Converting all file separator to "/".
goRoot := runtime.GOROOT()
if goRoot != "" {
goRoot = strings.Replace(goRoot, "\\", "/", -1)
goRoot = regexp.QuoteMeta(goRoot)
}
for i := from + customSkip; i < 10000; i++ {
if _, file, cline, ok := runtime.Caller(i); ok && file != "" {
if reg, _ := regexp.Compile(`<autogenerated>`); reg.MatchString(file) {
continue
}
if reg, _ := regexp.Compile(`gtest\.go$`); reg.MatchString(file) {
continue
}
if goRoot != "" {
if reg, _ := regexp.Compile("^" + goRoot); reg.MatchString(file) {
continue
}
}
stack += fmt.Sprintf(`%d. %s:%d%s`, index, file, cline, "\n")
index++
} else {
break
}
}
return stack
}
// isNil checks whether <value> is nil.
func isNil(value interface{}) bool {
rv := reflect.ValueOf(value)

View File

@ -16,5 +16,7 @@ func TestCase(t *testing.T) {
gtest.Case(t, func() {
gtest.Assert(1, 1)
gtest.AssertNE(1, 0)
gtest.AssertEQ(float32(123.456), float32(123.456))
gtest.AssertEQ(float64(123.456), float64(123.456))
})
}

View File

@ -9,9 +9,9 @@ package gconv
import (
"encoding/json"
"fmt"
"reflect"
"strconv"
"strings"
"github.com/gogf/gf/g/encoding/gbinary"
)
@ -195,8 +195,11 @@ func String(i interface{}) string {
return f.Error()
} else {
// Finally we use json.Marshal to convert.
jsonContent, _ := json.Marshal(value)
return string(jsonContent)
if jsonContent, err := json.Marshal(value); err != nil {
return fmt.Sprint(value)
} else {
return string(jsonContent)
}
}
}
}
@ -207,34 +210,34 @@ func Bool(i interface{}) bool {
if i == nil {
return false
}
if v, ok := i.(bool); ok {
return v
}
if s, ok := i.(string); ok {
if _, ok := emptyStringMap[s]; ok {
switch value := i.(type) {
case bool:
return value
case string:
if _, ok := emptyStringMap[value]; ok {
return false
}
return true
}
rv := reflect.ValueOf(i)
switch rv.Kind() {
case reflect.Ptr:
return !rv.IsNil()
case reflect.Map:
fallthrough
case reflect.Array:
fallthrough
case reflect.Slice:
return rv.Len() != 0
case reflect.Struct:
return true
default:
s := String(i)
if _, ok := emptyStringMap[s]; ok {
return false
rv := reflect.ValueOf(i)
switch rv.Kind() {
case reflect.Ptr:
return !rv.IsNil()
case reflect.Map:
fallthrough
case reflect.Array:
fallthrough
case reflect.Slice:
return rv.Len() != 0
case reflect.Struct:
return true
default:
s := String(i)
if _, ok := emptyStringMap[s]; ok {
return false
}
return true
}
return true
}
}
@ -317,6 +320,8 @@ func Int64(i interface{}) int64 {
return 1
}
return 0
case []byte:
return gbinary.DecodeToInt64(value)
default:
s := String(value)
// Hexadecimal
@ -419,6 +424,8 @@ func Uint64(i interface{}) uint64 {
return 1
}
return 0
case []byte:
return gbinary.DecodeToUint64(value)
default:
s := String(value)
// Hexadecimal
@ -447,11 +454,17 @@ func Float32(i interface{}) float32 {
if i == nil {
return 0
}
if v, ok := i.(float32); ok {
return v
switch value := i.(type) {
case float32:
return value
case float64:
return float32(value)
case []byte:
return gbinary.DecodeToFloat32(value)
default:
v, _ := strconv.ParseFloat(String(i), 64)
return float32(v)
}
v, _ := strconv.ParseFloat(strings.TrimSpace(String(i)), 64)
return float32(v)
}
// Float64 converts <i> to float64.
@ -459,9 +472,15 @@ func Float64(i interface{}) float64 {
if i == nil {
return 0
}
if v, ok := i.(float64); ok {
switch value := i.(type) {
case float32:
return float64(value)
case float64:
return value
case []byte:
return gbinary.DecodeToFloat64(value)
default:
v, _ := strconv.ParseFloat(String(i), 64)
return v
}
v, _ := strconv.ParseFloat(strings.TrimSpace(String(i)), 64)
return v
}

View File

@ -63,61 +63,65 @@ func Ints(i interface{}) []int {
return r
} else {
array := make([]int, 0)
switch i.(type) {
switch value := i.(type) {
case []string:
for _, v := range i.([]string) {
for _, v := range value {
array = append(array, Int(v))
}
case []int8:
for _, v := range i.([]int8) {
for _, v := range value {
array = append(array, Int(v))
}
case []int16:
for _, v := range i.([]int16) {
for _, v := range value {
array = append(array, Int(v))
}
case []int32:
for _, v := range i.([]int32) {
for _, v := range value {
array = append(array, Int(v))
}
case []int64:
for _, v := range i.([]int64) {
for _, v := range value {
array = append(array, Int(v))
}
case []uint:
for _, v := range i.([]uint) {
for _, v := range value {
array = append(array, Int(v))
}
case []uint8:
for _, v := range i.([]uint8) {
for _, v := range value {
array = append(array, Int(v))
}
case []uint16:
for _, v := range i.([]uint16) {
for _, v := range value {
array = append(array, Int(v))
}
case []uint32:
for _, v := range i.([]uint32) {
for _, v := range value {
array = append(array, Int(v))
}
case []uint64:
for _, v := range i.([]uint64) {
for _, v := range value {
array = append(array, Int(v))
}
case []bool:
for _, v := range i.([]bool) {
for _, v := range value {
array = append(array, Int(v))
}
case []float32:
for _, v := range i.([]float32) {
for _, v := range value {
array = append(array, Int(v))
}
case []float64:
for _, v := range i.([]float64) {
for _, v := range value {
array = append(array, Int(v))
}
case []interface{}:
for _, v := range i.([]interface{}) {
for _, v := range value {
array = append(array, Int(v))
}
case [][]byte:
for _, v := range value {
array = append(array, Int(v))
}
default:
@ -136,61 +140,65 @@ func Strings(i interface{}) []string {
return r
} else {
array := make([]string, 0)
switch i.(type) {
switch value := i.(type) {
case []int:
for _, v := range i.([]int) {
for _, v := range value {
array = append(array, String(v))
}
case []int8:
for _, v := range i.([]int8) {
for _, v := range value {
array = append(array, String(v))
}
case []int16:
for _, v := range i.([]int16) {
for _, v := range value {
array = append(array, String(v))
}
case []int32:
for _, v := range i.([]int32) {
for _, v := range value {
array = append(array, String(v))
}
case []int64:
for _, v := range i.([]int64) {
for _, v := range value {
array = append(array, String(v))
}
case []uint:
for _, v := range i.([]uint) {
for _, v := range value {
array = append(array, String(v))
}
case []uint8:
for _, v := range i.([]uint8) {
for _, v := range value {
array = append(array, String(v))
}
case []uint16:
for _, v := range i.([]uint16) {
for _, v := range value {
array = append(array, String(v))
}
case []uint32:
for _, v := range i.([]uint32) {
for _, v := range value {
array = append(array, String(v))
}
case []uint64:
for _, v := range i.([]uint64) {
for _, v := range value {
array = append(array, String(v))
}
case []bool:
for _, v := range i.([]bool) {
for _, v := range value {
array = append(array, String(v))
}
case []float32:
for _, v := range i.([]float32) {
for _, v := range value {
array = append(array, String(v))
}
case []float64:
for _, v := range i.([]float64) {
for _, v := range value {
array = append(array, String(v))
}
case []interface{}:
for _, v := range i.([]interface{}) {
for _, v := range value {
array = append(array, String(v))
}
case [][]byte:
for _, v := range value {
array = append(array, String(v))
}
default:
@ -209,61 +217,61 @@ func Floats(i interface{}) []float64 {
return r
} else {
array := make([]float64, 0)
switch i.(type) {
switch value := i.(type) {
case []string:
for _, v := range i.([]string) {
for _, v := range value {
array = append(array, Float64(v))
}
case []int:
for _, v := range i.([]int) {
for _, v := range value {
array = append(array, Float64(v))
}
case []int8:
for _, v := range i.([]int8) {
for _, v := range value {
array = append(array, Float64(v))
}
case []int16:
for _, v := range i.([]int16) {
for _, v := range value {
array = append(array, Float64(v))
}
case []int32:
for _, v := range i.([]int32) {
for _, v := range value {
array = append(array, Float64(v))
}
case []int64:
for _, v := range i.([]int64) {
for _, v := range value {
array = append(array, Float64(v))
}
case []uint:
for _, v := range i.([]uint) {
for _, v := range value {
array = append(array, Float64(v))
}
case []uint8:
for _, v := range i.([]uint8) {
for _, v := range value {
array = append(array, Float64(v))
}
case []uint16:
for _, v := range i.([]uint16) {
for _, v := range value {
array = append(array, Float64(v))
}
case []uint32:
for _, v := range i.([]uint32) {
for _, v := range value {
array = append(array, Float64(v))
}
case []uint64:
for _, v := range i.([]uint64) {
for _, v := range value {
array = append(array, Float64(v))
}
case []bool:
for _, v := range i.([]bool) {
for _, v := range value {
array = append(array, Float64(v))
}
case []float32:
for _, v := range i.([]float32) {
for _, v := range value {
array = append(array, Float64(v))
}
case []interface{}:
for _, v := range i.([]interface{}) {
for _, v := range value {
array = append(array, Float64(v))
}
default:
@ -282,61 +290,61 @@ func Interfaces(i interface{}) []interface{} {
return r
} else {
array := make([]interface{}, 0)
switch i.(type) {
switch value := i.(type) {
case []string:
for _, v := range i.([]string) {
for _, v := range value {
array = append(array, v)
}
case []int:
for _, v := range i.([]int) {
for _, v := range value {
array = append(array, v)
}
case []int8:
for _, v := range i.([]int8) {
for _, v := range value {
array = append(array, v)
}
case []int16:
for _, v := range i.([]int16) {
for _, v := range value {
array = append(array, v)
}
case []int32:
for _, v := range i.([]int32) {
for _, v := range value {
array = append(array, v)
}
case []int64:
for _, v := range i.([]int64) {
for _, v := range value {
array = append(array, v)
}
case []uint:
for _, v := range i.([]uint) {
for _, v := range value {
array = append(array, v)
}
case []uint8:
for _, v := range i.([]uint8) {
for _, v := range value {
array = append(array, v)
}
case []uint16:
for _, v := range i.([]uint16) {
for _, v := range value {
array = append(array, v)
}
case []uint32:
for _, v := range i.([]uint32) {
for _, v := range value {
array = append(array, v)
}
case []uint64:
for _, v := range i.([]uint64) {
for _, v := range value {
array = append(array, v)
}
case []bool:
for _, v := range i.([]bool) {
for _, v := range value {
array = append(array, v)
}
case []float32:
for _, v := range i.([]float32) {
for _, v := range value {
array = append(array, v)
}
case []float64:
for _, v := range i.([]float64) {
for _, v := range value {
array = append(array, v)
}
default:

View File

@ -15,7 +15,10 @@ import (
// Time converts <i> to time.Time.
func Time(i interface{}, format ...string) time.Time {
return GTime(i, format...).Time
if t := GTime(i, format...); t != nil {
return t.Time
}
return time.Time{}
}
// Duration converts <i> to time.Duration.

View File

@ -609,7 +609,7 @@ func Test_Byte_All(t *testing.T) {
gtest.Case(t, func() {
gtest.AssertEQ(gconv.Byte(uint8(0)), uint8(0))
gtest.AssertEQ(gconv.Byte("s"), uint8(0))
gtest.AssertEQ(gconv.Byte([]byte("s")), uint8(0))
gtest.AssertEQ(gconv.Byte([]byte("s")), uint8(115))
})
}
@ -692,8 +692,8 @@ func Test_Slice_All(t *testing.T) {
gtest.AssertEQ(gconv.Strings([]bool{true}), []string{"true"})
gtest.AssertEQ(gconv.Strings([]float32{1, 2}), []string{"1", "2"})
gtest.AssertEQ(gconv.Strings([]float64{1, 2}), []string{"1", "2"})
var strer []interface{} = make([]interface{}, 2)
gtest.AssertEQ(gconv.Strings(strer), []string{" "})
var strer = make([]interface{}, 2)
gtest.AssertEQ(gconv.Strings(strer), []string{"", ""})
gtest.AssertEQ(gconv.Floats(value), []float64{123.456})
gtest.AssertEQ(gconv.Floats(nil), nil)
@ -711,7 +711,7 @@ func Test_Slice_All(t *testing.T) {
gtest.AssertEQ(gconv.Floats([]bool{true}), []float64{0})
gtest.AssertEQ(gconv.Floats([]float32{1, 2}), []float64{1, 2})
gtest.AssertEQ(gconv.Floats([]float64{1, 2}), []float64{1, 2})
var floer []interface{} = make([]interface{}, 2)
var floer = make([]interface{}, 2)
gtest.AssertEQ(gconv.Floats(floer), []float64{0, 0})
gtest.AssertEQ(gconv.Interfaces(value), []interface{}{123.456})
@ -739,7 +739,7 @@ func Test_Slice_All(t *testing.T) {
gtest.AssertEQ(gconv.Maps(nil), nil)
gtest.AssertEQ(gconv.Maps([]map[string]interface{}{{"a": "1"}}), []map[string]interface{}{{"a": "1"}})
gtest.AssertEQ(gconv.Maps(1223), []map[string]interface{}{{}})
gtest.AssertEQ(gconv.Maps(1223), []map[string]interface{}{nil})
gtest.AssertEQ(gconv.Maps([]int{}), nil)
})
}

View File

@ -0,0 +1,150 @@
// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// go test *.go -bench "Benchmark_Bytes_To_*" -benchmem
package gconv
import (
"testing"
"unsafe"
"github.com/gogf/gf/g/encoding/gbinary"
)
var valueBytes = gbinary.Encode(123456789)
func Benchmark_Bytes_To_String_Normal(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = string(valueBytes)
}
}
func Benchmark_Bytes_To_String_Unsafe(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = *(*string)(unsafe.Pointer(&valueBytes))
}
}
func Benchmark_Bytes_To_String(b *testing.B) {
for i := 0; i < b.N; i++ {
String(valueBytes)
}
}
func Benchmark_Bytes_To_Int(b *testing.B) {
for i := 0; i < b.N; i++ {
Int(valueBytes)
}
}
func Benchmark_Bytes_To_Int8(b *testing.B) {
for i := 0; i < b.N; i++ {
Int8(valueBytes)
}
}
func Benchmark_Bytes_To_Int16(b *testing.B) {
for i := 0; i < b.N; i++ {
Int16(valueBytes)
}
}
func Benchmark_Bytes_To_Int32(b *testing.B) {
for i := 0; i < b.N; i++ {
Int32(valueBytes)
}
}
func Benchmark_Bytes_To_Int64(b *testing.B) {
for i := 0; i < b.N; i++ {
Int(valueBytes)
}
}
func Benchmark_Bytes_To_Uint(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint(valueBytes)
}
}
func Benchmark_Bytes_To_Uint8(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint8(valueBytes)
}
}
func Benchmark_Bytes_To_Uint16(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint16(valueBytes)
}
}
func Benchmark_Bytes_To_Uint32(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint32(valueBytes)
}
}
func Benchmark_Bytes_To_Uint64(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint64(valueBytes)
}
}
func Benchmark_Bytes_To_Float32(b *testing.B) {
for i := 0; i < b.N; i++ {
Float32(valueBytes)
}
}
func Benchmark_Bytes_To_Float64(b *testing.B) {
for i := 0; i < b.N; i++ {
Float64(valueBytes)
}
}
func Benchmark_Bytes_To_Time(b *testing.B) {
for i := 0; i < b.N; i++ {
Time(valueBytes)
}
}
func Benchmark_Bytes_To_TimeDuration(b *testing.B) {
for i := 0; i < b.N; i++ {
Duration(valueBytes)
}
}
func Benchmark_Bytes_To_Bytes(b *testing.B) {
for i := 0; i < b.N; i++ {
Bytes(valueBytes)
}
}
func Benchmark_Bytes_To_Strings(b *testing.B) {
for i := 0; i < b.N; i++ {
Strings(valueBytes)
}
}
func Benchmark_Bytes_To_Ints(b *testing.B) {
for i := 0; i < b.N; i++ {
Ints(valueBytes)
}
}
func Benchmark_Bytes_To_Floats(b *testing.B) {
for i := 0; i < b.N; i++ {
Floats(valueBytes)
}
}
func Benchmark_Bytes_To_Interfaces(b *testing.B) {
for i := 0; i < b.N; i++ {
Interfaces(valueBytes)
}
}

View File

@ -0,0 +1,135 @@
// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// go test *.go -bench=".*" -benchmem
package gconv
import (
"testing"
)
var valueFloat = float64(1.23456789)
func Benchmark_Float_To_String(b *testing.B) {
for i := 0; i < b.N; i++ {
String(valueFloat)
}
}
func Benchmark_Float_To_Int(b *testing.B) {
for i := 0; i < b.N; i++ {
Int(valueFloat)
}
}
func Benchmark_Float_To_Int8(b *testing.B) {
for i := 0; i < b.N; i++ {
Int8(valueFloat)
}
}
func Benchmark_Float_To_Int16(b *testing.B) {
for i := 0; i < b.N; i++ {
Int16(valueFloat)
}
}
func Benchmark_Float_To_Int32(b *testing.B) {
for i := 0; i < b.N; i++ {
Int32(valueFloat)
}
}
func Benchmark_Float_To_Int64(b *testing.B) {
for i := 0; i < b.N; i++ {
Int(valueFloat)
}
}
func Benchmark_Float_To_Uint(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint(valueFloat)
}
}
func Benchmark_Float_To_Uint8(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint8(valueFloat)
}
}
func Benchmark_Float_To_Uint16(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint16(valueFloat)
}
}
func Benchmark_Float_To_Uint32(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint32(valueFloat)
}
}
func Benchmark_Float_To_Uint64(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint64(valueFloat)
}
}
func Benchmark_Float_To_Float32(b *testing.B) {
for i := 0; i < b.N; i++ {
Float32(valueFloat)
}
}
func Benchmark_Float_To_Float64(b *testing.B) {
for i := 0; i < b.N; i++ {
Float64(valueFloat)
}
}
func Benchmark_Float_To_Time(b *testing.B) {
for i := 0; i < b.N; i++ {
Time(valueFloat)
}
}
func Benchmark_Float_To_TimeDuration(b *testing.B) {
for i := 0; i < b.N; i++ {
Duration(valueFloat)
}
}
func Benchmark_Float_To_Bytes(b *testing.B) {
for i := 0; i < b.N; i++ {
Bytes(valueFloat)
}
}
func Benchmark_Float_To_Strings(b *testing.B) {
for i := 0; i < b.N; i++ {
Strings(valueFloat)
}
}
func Benchmark_Float_To_Ints(b *testing.B) {
for i := 0; i < b.N; i++ {
Ints(valueFloat)
}
}
func Benchmark_Float_To_Floats(b *testing.B) {
for i := 0; i < b.N; i++ {
Floats(valueFloat)
}
}
func Benchmark_Float_To_Interfaces(b *testing.B) {
for i := 0; i < b.N; i++ {
Interfaces(valueFloat)
}
}

View File

@ -0,0 +1,135 @@
// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// go test *.go -bench=".*" -benchmem
package gconv
import (
"testing"
)
var valueInt = 123456789
func Benchmark_Int_To_String(b *testing.B) {
for i := 0; i < b.N; i++ {
String(valueInt)
}
}
func Benchmark_Int_To_Int(b *testing.B) {
for i := 0; i < b.N; i++ {
Int(valueInt)
}
}
func Benchmark_Int_To_Int8(b *testing.B) {
for i := 0; i < b.N; i++ {
Int8(valueInt)
}
}
func Benchmark_Int_To_Int16(b *testing.B) {
for i := 0; i < b.N; i++ {
Int16(valueInt)
}
}
func Benchmark_Int_To_Int32(b *testing.B) {
for i := 0; i < b.N; i++ {
Int32(valueInt)
}
}
func Benchmark_Int_To_Int64(b *testing.B) {
for i := 0; i < b.N; i++ {
Int(valueInt)
}
}
func Benchmark_Int_To_Uint(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint(valueInt)
}
}
func Benchmark_Int_To_Uint8(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint8(valueInt)
}
}
func Benchmark_Int_To_Uint16(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint16(valueInt)
}
}
func Benchmark_Int_To_Uint32(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint32(valueInt)
}
}
func Benchmark_Int_To_Uint64(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint64(valueInt)
}
}
func Benchmark_Int_To_Float32(b *testing.B) {
for i := 0; i < b.N; i++ {
Float32(valueInt)
}
}
func Benchmark_Int_To_Float64(b *testing.B) {
for i := 0; i < b.N; i++ {
Float64(valueInt)
}
}
func Benchmark_Int_To_Time(b *testing.B) {
for i := 0; i < b.N; i++ {
Time(valueInt)
}
}
func Benchmark_Int_To_TimeDuration(b *testing.B) {
for i := 0; i < b.N; i++ {
Duration(valueInt)
}
}
func Benchmark_Int_To_Bytes(b *testing.B) {
for i := 0; i < b.N; i++ {
Bytes(valueInt)
}
}
func Benchmark_Int_To_Strings(b *testing.B) {
for i := 0; i < b.N; i++ {
Strings(valueInt)
}
}
func Benchmark_Int_To_Ints(b *testing.B) {
for i := 0; i < b.N; i++ {
Ints(valueInt)
}
}
func Benchmark_Int_To_Floats(b *testing.B) {
for i := 0; i < b.N; i++ {
Floats(valueInt)
}
}
func Benchmark_Int_To_Interfaces(b *testing.B) {
for i := 0; i < b.N; i++ {
Interfaces(valueInt)
}
}

View File

@ -0,0 +1,135 @@
// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// go test *.go -bench=".*" -benchmem
package gconv
import (
"testing"
)
var valueStr = "123456789"
func Benchmark_Str_To_String(b *testing.B) {
for i := 0; i < b.N; i++ {
String(valueStr)
}
}
func Benchmark_Str_To_Int(b *testing.B) {
for i := 0; i < b.N; i++ {
Int(valueStr)
}
}
func Benchmark_Str_To_Int8(b *testing.B) {
for i := 0; i < b.N; i++ {
Int8(valueStr)
}
}
func Benchmark_Str_To_Int16(b *testing.B) {
for i := 0; i < b.N; i++ {
Int16(valueStr)
}
}
func Benchmark_Str_To_Int32(b *testing.B) {
for i := 0; i < b.N; i++ {
Int32(valueStr)
}
}
func Benchmark_Str_To_Int64(b *testing.B) {
for i := 0; i < b.N; i++ {
Int(valueStr)
}
}
func Benchmark_Str_To_Uint(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint(valueStr)
}
}
func Benchmark_Str_To_Uint8(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint8(valueStr)
}
}
func Benchmark_Str_To_Uint16(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint16(valueStr)
}
}
func Benchmark_Str_To_Uint32(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint32(valueStr)
}
}
func Benchmark_Str_To_Uint64(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint64(valueStr)
}
}
func Benchmark_Str_To_Float32(b *testing.B) {
for i := 0; i < b.N; i++ {
Float32(valueStr)
}
}
func Benchmark_Str_To_Float64(b *testing.B) {
for i := 0; i < b.N; i++ {
Float64(valueStr)
}
}
func Benchmark_Str_To_Time(b *testing.B) {
for i := 0; i < b.N; i++ {
Time(valueStr)
}
}
func Benchmark_Str_To_TimeDuration(b *testing.B) {
for i := 0; i < b.N; i++ {
Duration(valueStr)
}
}
func Benchmark_Str_To_Bytes(b *testing.B) {
for i := 0; i < b.N; i++ {
Bytes(valueStr)
}
}
func Benchmark_Str_To_Strings(b *testing.B) {
for i := 0; i < b.N; i++ {
Strings(valueStr)
}
}
func Benchmark_Str_To_Ints(b *testing.B) {
for i := 0; i < b.N; i++ {
Ints(valueStr)
}
}
func Benchmark_Str_To_Floats(b *testing.B) {
for i := 0; i < b.N; i++ {
Floats(valueStr)
}
}
func Benchmark_Str_To_Interfaces(b *testing.B) {
for i := 0; i < b.N; i++ {
Interfaces(valueStr)
}
}

View File

@ -1,135 +0,0 @@
// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// go test *.go -bench=".*" -benchmem
package gconv
import (
"testing"
)
var value = 123456789
func BenchmarkString(b *testing.B) {
for i := 0; i < b.N; i++ {
String(value)
}
}
func BenchmarkInt(b *testing.B) {
for i := 0; i < b.N; i++ {
Int(value)
}
}
func BenchmarkInt8(b *testing.B) {
for i := 0; i < b.N; i++ {
Int8(value)
}
}
func BenchmarkInt16(b *testing.B) {
for i := 0; i < b.N; i++ {
Int16(value)
}
}
func BenchmarkInt32(b *testing.B) {
for i := 0; i < b.N; i++ {
Int32(value)
}
}
func BenchmarkInt64(b *testing.B) {
for i := 0; i < b.N; i++ {
Int(value)
}
}
func BenchmarkUint(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint(value)
}
}
func BenchmarkUint8(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint8(value)
}
}
func BenchmarkUint16(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint16(value)
}
}
func BenchmarkUint32(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint32(value)
}
}
func BenchmarkUint64(b *testing.B) {
for i := 0; i < b.N; i++ {
Uint64(value)
}
}
func BenchmarkFloat32(b *testing.B) {
for i := 0; i < b.N; i++ {
Float32(value)
}
}
func BenchmarkFloat64(b *testing.B) {
for i := 0; i < b.N; i++ {
Float64(value)
}
}
func BenchmarkTime(b *testing.B) {
for i := 0; i < b.N; i++ {
Time(value)
}
}
func BenchmarkTimeDuration(b *testing.B) {
for i := 0; i < b.N; i++ {
Duration(value)
}
}
func BenchmarkBytes(b *testing.B) {
for i := 0; i < b.N; i++ {
Bytes(value)
}
}
func BenchmarkStrings(b *testing.B) {
for i := 0; i < b.N; i++ {
Strings(value)
}
}
func BenchmarkInts(b *testing.B) {
for i := 0; i < b.N; i++ {
Ints(value)
}
}
func BenchmarkFloats(b *testing.B) {
for i := 0; i < b.N; i++ {
Floats(value)
}
}
func BenchmarkInterfaces(b *testing.B) {
for i := 0; i < b.N; i++ {
Interfaces(value)
}
}

View File

@ -7,30 +7,31 @@
package gconv_test
import (
"testing"
"github.com/gogf/gf/g/test/gtest"
"github.com/gogf/gf/g/util/gconv"
"testing"
)
func Test_Basic(t *testing.T) {
gtest.Case(t, func() {
vint := float32(123.456)
vint64 := int64(1552578474888)
gtest.AssertEQ(gconv.Int(vint), int(123))
gtest.AssertEQ(gconv.Int8(vint), int8(123))
gtest.AssertEQ(gconv.Int16(vint), int16(123))
gtest.AssertEQ(gconv.Int32(vint), int32(123))
gtest.AssertEQ(gconv.Int64(vint), int64(123))
gtest.AssertEQ(gconv.Int64(vint), int64(123))
gtest.AssertEQ(gconv.Uint(vint), uint(123))
gtest.AssertEQ(gconv.Uint8(vint), uint8(123))
gtest.AssertEQ(gconv.Uint16(vint), uint16(123))
gtest.AssertEQ(gconv.Uint32(vint), uint32(123))
gtest.AssertEQ(gconv.Uint64(vint), uint64(123))
gtest.AssertEQ(gconv.Float32(vint), float32(123.456))
gtest.AssertEQ(gconv.Float64(vint), float64(123.456))
gtest.AssertEQ(gconv.Bool(vint), true)
gtest.AssertEQ(gconv.String(vint), "123.456")
gtest.AssertEQ(gconv.String(vint64), "1552578474888")
f32 := float32(123.456)
i64 := int64(1552578474888)
gtest.AssertEQ(gconv.Int(f32), int(123))
gtest.AssertEQ(gconv.Int8(f32), int8(123))
gtest.AssertEQ(gconv.Int16(f32), int16(123))
gtest.AssertEQ(gconv.Int32(f32), int32(123))
gtest.AssertEQ(gconv.Int64(f32), int64(123))
gtest.AssertEQ(gconv.Int64(f32), int64(123))
gtest.AssertEQ(gconv.Uint(f32), uint(123))
gtest.AssertEQ(gconv.Uint8(f32), uint8(123))
gtest.AssertEQ(gconv.Uint16(f32), uint16(123))
gtest.AssertEQ(gconv.Uint32(f32), uint32(123))
gtest.AssertEQ(gconv.Uint64(f32), uint64(123))
gtest.AssertEQ(gconv.Float32(f32), float32(123.456))
gtest.AssertEQ(gconv.Float64(i64), float64(i64))
gtest.AssertEQ(gconv.Bool(f32), true)
gtest.AssertEQ(gconv.String(f32), "123.456")
gtest.AssertEQ(gconv.String(i64), "1552578474888")
})
}

View File

@ -7,6 +7,10 @@
package gvalid
import (
"regexp"
"strconv"
"strings"
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/encoding/gjson"
"github.com/gogf/gf/g/net/gipv4"
@ -14,9 +18,6 @@ import (
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/g/util/gconv"
"regexp"
"strconv"
"strings"
)
const (

View File

@ -10,7 +10,6 @@ import (
"strings"
"github.com/gogf/gf/g/internal/structs"
"github.com/gogf/gf/g/util/gconv"
)
@ -22,6 +21,8 @@ var (
// 校验struct对象属性object参数也可以是一个指向对象的指针返回值同CheckMap方法。
// struct的数据校验结果信息是顺序的。
func CheckStruct(object interface{}, rules interface{}, msgs ...CustomMsg) *Error {
// 字段别名记录用于msgs覆盖struct tag的
fieldAliases := make(map[string]string)
params := make(map[string]interface{})
checkRules := make(map[string]string)
customMsgs := make(CustomMsg)
@ -66,22 +67,17 @@ func CheckStruct(object interface{}, rules interface{}, msgs ...CustomMsg) *Erro
checkRules = v
}
// 首先, 按照属性循环一遍将struct的属性、数值、tag解析
tagValue := ""
for _, field := range structs.MapField(object, structTagPriority, true) {
for nameOrTag, field := range structs.MapField(object, structTagPriority, true) {
fieldName := field.Name()
params[fieldName] = field.Value()
tagValue = ""
for _, v := range structTagPriority {
tagValue = field.Tag(v)
if tagValue != "" {
break
}
}
if tagValue != "" {
// MapField返回map[name/tag]*Field当nameOrTag != fieldName时nameOrTag为tag
if nameOrTag != fieldName {
// sequence tag == struct tag, 这里的name为别名
name, rule, msg := parseSequenceTag(tagValue)
name, rule, msg := parseSequenceTag(nameOrTag)
if len(name) == 0 {
name = fieldName
} else {
fieldAliases[fieldName] = name
}
// params参数使用别名**扩容**(而不仅仅使用别名),仅用于验证使用
if _, ok := params[name]; !ok {
@ -89,7 +85,13 @@ func CheckStruct(object interface{}, rules interface{}, msgs ...CustomMsg) *Erro
}
// 校验规则
if _, ok := checkRules[name]; !ok {
checkRules[name] = rule
if _, ok := checkRules[fieldName]; ok {
// tag中存在别名且rules传入的参数中使用了属性命名时进行规则替换并删除该属性的规则
checkRules[name] = checkRules[fieldName]
delete(checkRules, fieldName)
} else {
checkRules[name] = rule
}
errorRules = append(errorRules, name+"@"+rule)
} else {
// 传递的rules规则会覆盖struct tag的规则
@ -116,14 +118,16 @@ func CheckStruct(object interface{}, rules interface{}, msgs ...CustomMsg) *Erro
}
}
}
// 自定义错误消息非必须参数优先级比rules参数中以及struct tag中定义的错误消息更高
if len(msgs) > 0 && len(msgs[0]) > 0 {
if len(customMsgs) > 0 {
for k, v := range msgs[0] {
for k, v := range msgs[0] {
if a, ok := fieldAliases[k]; ok {
// 属性的别名存在时,覆盖别名的错误信息
customMsgs[a] = v
} else {
customMsgs[k] = v
}
} else {
customMsgs = msgs[0]
}
}

View File

@ -17,4 +17,7 @@ func main() {
"id in(?)": g.Slice{1, 2, 3},
}
db.Table("user").Where(conditions).OrderBy("id asc").All()
var params []interface{}
db.Table("user").Where("1=1", params).OrderBy("id asc").All()
}

View File

@ -0,0 +1,20 @@
package main
import (
"fmt"
"github.com/gogf/gf/g"
)
func main() {
db := g.DB()
db.SetDebug(true)
r, e := db.Table("test").All()
if e != nil {
panic(e)
}
if r != nil {
fmt.Println(r.ToList())
}
}

View File

@ -5,7 +5,7 @@ import (
)
func PrintLog(content string) {
glog.Skip(1).Line().Println("line number with skip:", content)
glog.Skip(0).Line().Println("line number with skip:", content)
glog.Line(true).Println("line number without skip:", content)
}

View File

@ -1 +1,10 @@
package main
import (
"fmt"
"github.com/gogf/gf/g"
)
func main() {
fmt.Println(g.Config().Get("log-path"))
}

View File

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