mirror of
https://gitee.com/johng/gf
synced 2026-07-02 19:31:07 +08:00
Merge branch 'gogf:master' into master
This commit is contained in:
28
.example/os/gcache/getorset_func_lock.go
Normal file
28
.example/os/gcache/getorset_func_lock.go
Normal file
@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/os/gcache"
|
||||
"github.com/gogf/gf/os/gctx"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
ch = make(chan struct{}, 0)
|
||||
ctx = gctx.New()
|
||||
key = `key`
|
||||
value = `value`
|
||||
)
|
||||
for i := 0; i < 10; i++ {
|
||||
go func(index int) {
|
||||
<-ch
|
||||
_, _ = gcache.Ctx(ctx).GetOrSetFuncLock(key, func() (interface{}, error) {
|
||||
fmt.Println(index, "entered")
|
||||
return value, nil
|
||||
}, 0)
|
||||
}(i)
|
||||
}
|
||||
close(ch)
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
19
.example/os/gcache/note_interface_key.go
Normal file
19
.example/os/gcache/note_interface_key.go
Normal file
@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/os/gcache"
|
||||
"github.com/gogf/gf/os/gctx"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
ctx = gctx.New()
|
||||
key1 int32 = 1
|
||||
key2 float64 = 1
|
||||
value = `value`
|
||||
)
|
||||
_ = gcache.Ctx(ctx).Set(key1, value, 0)
|
||||
fmt.Println(gcache.Ctx(ctx).Get(key1))
|
||||
fmt.Println(gcache.Ctx(ctx).Get(key2))
|
||||
}
|
||||
29
.example/os/gcache/note_interface_value.go
Normal file
29
.example/os/gcache/note_interface_value.go
Normal file
@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/os/gcache"
|
||||
"github.com/gogf/gf/os/gctx"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type User struct {
|
||||
Id int
|
||||
Name string
|
||||
Site string
|
||||
}
|
||||
var (
|
||||
ctx = gctx.New()
|
||||
user *User
|
||||
key = `UserKey`
|
||||
value = &User{
|
||||
Id: 1,
|
||||
Name: "GoFrame",
|
||||
Site: "https://goframe.org",
|
||||
}
|
||||
)
|
||||
_ = gcache.Ctx(ctx).Set(key, value, 0)
|
||||
v, _ := gcache.Ctx(ctx).GetVar(key)
|
||||
_ = v.Scan(&user)
|
||||
fmt.Printf(`%#v`, user)
|
||||
}
|
||||
@ -11,14 +11,13 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gcode"
|
||||
"net/url"
|
||||
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"github.com/gogf/gf/text/gregex"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"net/url"
|
||||
|
||||
_ "github.com/gogf/mysql"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
// DriverMysql is the driver for mysql database.
|
||||
@ -34,7 +33,7 @@ func (d *DriverMysql) New(core *Core, node *ConfigNode) (DB, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Open creates and returns a underlying sql.DB object for mysql.
|
||||
// Open creates and returns an underlying sql.DB object for mysql.
|
||||
// Note that it converts time.Time argument to local timezone in default.
|
||||
func (d *DriverMysql) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
var source string
|
||||
|
||||
@ -209,7 +209,12 @@ func ConvertDataForTableRecord(value interface{}) map[string]interface{} {
|
||||
data[k] = nil
|
||||
}
|
||||
|
||||
case *time.Time, *gtime.Time:
|
||||
case *gtime.Time:
|
||||
if r.IsZero() {
|
||||
data[k] = nil
|
||||
}
|
||||
|
||||
case *time.Time:
|
||||
continue
|
||||
|
||||
case Counter, *Counter:
|
||||
|
||||
@ -83,8 +83,10 @@ func (m *Model) Data(data ...interface{}) *Model {
|
||||
list[i] = ConvertDataForTableRecord(rv.Index(i).Interface())
|
||||
}
|
||||
model.data = list
|
||||
|
||||
case reflect.Map:
|
||||
model.data = ConvertDataForTableRecord(data[0])
|
||||
|
||||
case reflect.Struct:
|
||||
if v, ok := data[0].(apiInterfaces); ok {
|
||||
var (
|
||||
@ -98,6 +100,7 @@ func (m *Model) Data(data ...interface{}) *Model {
|
||||
} else {
|
||||
model.data = ConvertDataForTableRecord(data[0])
|
||||
}
|
||||
|
||||
default:
|
||||
model.data = data[0]
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ import (
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
)
|
||||
|
||||
// With creates and returns an ORM model based on meta data of given object.
|
||||
// With creates and returns an ORM model based on metadata of given object.
|
||||
// It also enables model association operations feature on given `object`.
|
||||
// It can be called multiple times to add one or more objects to model and enable
|
||||
// their mode association operations feature.
|
||||
@ -64,7 +64,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
|
||||
err error
|
||||
allowedTypeStrArray = make([]string, 0)
|
||||
)
|
||||
fieldMap, err := structs.FieldMap(structs.FieldMapInput{
|
||||
currentStructFieldMap, err := structs.FieldMap(structs.FieldMapInput{
|
||||
Pointer: pointer,
|
||||
PriorityTagArray: nil,
|
||||
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
|
||||
@ -74,7 +74,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
|
||||
}
|
||||
// It checks the with array and automatically calls the ScanList to complete association querying.
|
||||
if !m.withAll {
|
||||
for _, field := range fieldMap {
|
||||
for _, field := range currentStructFieldMap {
|
||||
for _, withItem := range m.withArray {
|
||||
withItemReflectValueType, err := structs.StructType(withItem)
|
||||
if err != nil {
|
||||
@ -91,7 +91,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, field := range fieldMap {
|
||||
for _, field := range currentStructFieldMap {
|
||||
var (
|
||||
fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]")
|
||||
parsedTagOutput = m.parseWithTagInFieldStruct(field)
|
||||
@ -99,43 +99,40 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
|
||||
if parsedTagOutput.With == "" {
|
||||
continue
|
||||
}
|
||||
// Just handler "with" type attribute struct.
|
||||
// It just handlers "with" type attribute struct, so it ignores other struct types.
|
||||
if !m.withAll && !gstr.InArray(allowedTypeStrArray, fieldTypeStr) {
|
||||
continue
|
||||
}
|
||||
array := gstr.SplitAndTrim(parsedTagOutput.With, "=")
|
||||
if len(array) == 1 {
|
||||
// It supports using only one column name
|
||||
// It also supports using only one column name
|
||||
// if both tables associates using the same column name.
|
||||
array = append(array, parsedTagOutput.With)
|
||||
}
|
||||
var (
|
||||
model *Model
|
||||
fieldKeys []string
|
||||
relatedFieldName = array[0]
|
||||
relatedAttrName = array[1]
|
||||
relatedFieldValue interface{}
|
||||
model *Model
|
||||
fieldKeys []string
|
||||
relatedSourceName = array[0]
|
||||
relatedTargetName = array[1]
|
||||
relatedTargetValue interface{}
|
||||
)
|
||||
// Find the value of related attribute from `pointer`.
|
||||
for attributeName, attributeValue := range fieldMap {
|
||||
if utils.EqualFoldWithoutChars(attributeName, relatedAttrName) {
|
||||
relatedFieldValue = attributeValue.Value.Interface()
|
||||
for attributeName, attributeValue := range currentStructFieldMap {
|
||||
if utils.EqualFoldWithoutChars(attributeName, relatedTargetName) {
|
||||
relatedTargetValue = attributeValue.Value.Interface()
|
||||
break
|
||||
}
|
||||
}
|
||||
if relatedFieldValue == nil {
|
||||
if relatedTargetValue == nil {
|
||||
return gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`cannot find the related value of attribute name "%s" in with tag "%s" for attribute "%s.%s"`,
|
||||
relatedAttrName, parsedTagOutput.With, reflect.TypeOf(pointer).Elem(), field.Name(),
|
||||
`cannot find the target related value of name "%s" in with tag "%s" for attribute "%s.%s"`,
|
||||
relatedTargetName, parsedTagOutput.With, reflect.TypeOf(pointer).Elem(), field.Name(),
|
||||
)
|
||||
}
|
||||
bindToReflectValue := field.Value
|
||||
switch bindToReflectValue.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
if bindToReflectValue.CanAddr() {
|
||||
bindToReflectValue = bindToReflectValue.Addr()
|
||||
}
|
||||
if bindToReflectValue.Kind() != reflect.Ptr && bindToReflectValue.CanAddr() {
|
||||
bindToReflectValue = bindToReflectValue.Addr()
|
||||
}
|
||||
|
||||
// It automatically retrieves struct field names from current attribute struct/slice.
|
||||
@ -159,7 +156,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
|
||||
model = model.Order(parsedTagOutput.Order)
|
||||
}
|
||||
|
||||
err = model.Fields(fieldKeys).Where(relatedFieldName, relatedFieldValue).Scan(bindToReflectValue)
|
||||
err = model.Fields(fieldKeys).Where(relatedSourceName, relatedTargetValue).Scan(bindToReflectValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -179,7 +176,7 @@ func (m *Model) doWithScanStructs(pointer interface{}) error {
|
||||
err error
|
||||
allowedTypeStrArray = make([]string, 0)
|
||||
)
|
||||
fieldMap, err := structs.FieldMap(structs.FieldMapInput{
|
||||
currentStructFieldMap, err := structs.FieldMap(structs.FieldMapInput{
|
||||
Pointer: pointer,
|
||||
PriorityTagArray: nil,
|
||||
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
|
||||
@ -189,7 +186,7 @@ func (m *Model) doWithScanStructs(pointer interface{}) error {
|
||||
}
|
||||
// It checks the with array and automatically calls the ScanList to complete association querying.
|
||||
if !m.withAll {
|
||||
for _, field := range fieldMap {
|
||||
for _, field := range currentStructFieldMap {
|
||||
for _, withItem := range m.withArray {
|
||||
withItemReflectValueType, err := structs.StructType(withItem)
|
||||
if err != nil {
|
||||
@ -207,7 +204,7 @@ func (m *Model) doWithScanStructs(pointer interface{}) error {
|
||||
}
|
||||
}
|
||||
|
||||
for fieldName, field := range fieldMap {
|
||||
for fieldName, field := range currentStructFieldMap {
|
||||
var (
|
||||
fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]")
|
||||
parsedTagOutput = m.parseWithTagInFieldStruct(field)
|
||||
@ -225,24 +222,24 @@ func (m *Model) doWithScanStructs(pointer interface{}) error {
|
||||
array = append(array, parsedTagOutput.With)
|
||||
}
|
||||
var (
|
||||
model *Model
|
||||
fieldKeys []string
|
||||
relatedFieldName = array[0]
|
||||
relatedAttrName = array[1]
|
||||
relatedFieldValue interface{}
|
||||
model *Model
|
||||
fieldKeys []string
|
||||
relatedSourceName = array[0]
|
||||
relatedTargetName = array[1]
|
||||
relatedTargetValue interface{}
|
||||
)
|
||||
// Find the value slice of related attribute from `pointer`.
|
||||
for attributeName, _ := range fieldMap {
|
||||
if utils.EqualFoldWithoutChars(attributeName, relatedAttrName) {
|
||||
relatedFieldValue = ListItemValuesUnique(pointer, attributeName)
|
||||
for attributeName, _ := range currentStructFieldMap {
|
||||
if utils.EqualFoldWithoutChars(attributeName, relatedTargetName) {
|
||||
relatedTargetValue = ListItemValuesUnique(pointer, attributeName)
|
||||
break
|
||||
}
|
||||
}
|
||||
if relatedFieldValue == nil {
|
||||
if relatedTargetValue == nil {
|
||||
return gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`cannot find the related value for attribute name "%s" of with tag "%s"`,
|
||||
relatedAttrName, parsedTagOutput.With,
|
||||
relatedTargetName, parsedTagOutput.With,
|
||||
)
|
||||
}
|
||||
|
||||
@ -267,7 +264,9 @@ func (m *Model) doWithScanStructs(pointer interface{}) error {
|
||||
model = model.Order(parsedTagOutput.Order)
|
||||
}
|
||||
|
||||
err = model.Fields(fieldKeys).Where(relatedFieldName, relatedFieldValue).ScanList(pointer, fieldName, parsedTagOutput.With)
|
||||
err = model.Fields(fieldKeys).
|
||||
Where(relatedSourceName, relatedTargetValue).
|
||||
ScanList(pointer, fieldName, parsedTagOutput.With)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -1867,3 +1867,127 @@ func Test_Table_Relation_With_MultipleDepends_Embedded(t *testing.T) {
|
||||
t.Assert(tableA[1].TableB.TableC.Id, 300)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Table_Relation_WithAll_Embedded_Meta_NameMatchingRule(t *testing.T) {
|
||||
var (
|
||||
tableUser = "user1"
|
||||
tableUserDetail = "user_detail1"
|
||||
tableUserScores = "user_scores1"
|
||||
)
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
name varchar(45) NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUser)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUser)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
user_id int(10) unsigned NOT NULL,
|
||||
address varchar(45) NOT NULL,
|
||||
PRIMARY KEY (user_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserDetail)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserDetail)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
user_id int(10) unsigned NOT NULL,
|
||||
score int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserScores)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserScores)
|
||||
|
||||
type UserDetail struct {
|
||||
gmeta.Meta `orm:"table:user_detail1"`
|
||||
UserID int `json:"user_id"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type UserScores struct {
|
||||
gmeta.Meta `orm:"table:user_scores1"`
|
||||
ID int `json:"id"`
|
||||
UserID int `json:"user_id"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
// For Test Only
|
||||
type UserEmbedded struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
gmeta.Meta `orm:"table:user1"`
|
||||
UserEmbedded
|
||||
UserDetail UserDetail `orm:"with:user_id=id"`
|
||||
UserScores []*UserScores `orm:"with:user_id=id"`
|
||||
}
|
||||
|
||||
// Initialize the data.
|
||||
var err error
|
||||
for i := 1; i <= 5; i++ {
|
||||
// User.
|
||||
_, err = db.Insert(tableUser, g.Map{
|
||||
"id": i,
|
||||
"name": fmt.Sprintf(`name_%d`, i),
|
||||
})
|
||||
gtest.AssertNil(err)
|
||||
// Detail.
|
||||
_, err = db.Insert(tableUserDetail, g.Map{
|
||||
"user_id": i,
|
||||
"address": fmt.Sprintf(`address_%d`, i),
|
||||
})
|
||||
gtest.AssertNil(err)
|
||||
// Scores.
|
||||
for j := 1; j <= 5; j++ {
|
||||
_, err = db.Insert(tableUserScores, g.Map{
|
||||
"user_id": i,
|
||||
"score": j,
|
||||
})
|
||||
gtest.AssertNil(err)
|
||||
}
|
||||
}
|
||||
|
||||
db.SetDebug(true)
|
||||
defer db.SetDebug(false)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user *User
|
||||
err := db.Model(tableUser).WithAll().Where("id", 3).Scan(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.ID, 3)
|
||||
t.AssertNE(user.UserDetail, nil)
|
||||
t.Assert(user.UserDetail.UserID, 3)
|
||||
t.Assert(user.UserDetail.Address, `address_3`)
|
||||
t.Assert(len(user.UserScores), 5)
|
||||
t.Assert(user.UserScores[0].UserID, 3)
|
||||
t.Assert(user.UserScores[0].Score, 1)
|
||||
t.Assert(user.UserScores[4].UserID, 3)
|
||||
t.Assert(user.UserScores[4].Score, 5)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user User
|
||||
err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.ID, 4)
|
||||
t.AssertNE(user.UserDetail, nil)
|
||||
t.Assert(user.UserDetail.UserID, 4)
|
||||
t.Assert(user.UserDetail.Address, `address_4`)
|
||||
t.Assert(len(user.UserScores), 5)
|
||||
t.Assert(user.UserScores[0].UserID, 4)
|
||||
t.Assert(user.UserScores[0].Score, 1)
|
||||
t.Assert(user.UserScores[4].UserID, 4)
|
||||
t.Assert(user.UserScores[4].Score, 5)
|
||||
})
|
||||
}
|
||||
|
||||
@ -7,10 +7,10 @@
|
||||
package gdb_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Instance(t *testing.T) {
|
||||
@ -27,3 +27,16 @@ func Test_Instance(t *testing.T) {
|
||||
t.Assert(err2, nil)
|
||||
})
|
||||
}
|
||||
|
||||
// Fix issue: https://github.com/gogf/gf/issues/819
|
||||
func Test_Func_ConvertDataForTableRecord(t *testing.T) {
|
||||
type Test struct {
|
||||
ResetPasswordTokenAt mysql.NullTime `orm:"reset_password_token_at"`
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gdb.ConvertDataForTableRecord(new(Test))
|
||||
t.Assert(len(m), 1)
|
||||
t.AssertNE(m["reset_password_token_at"], nil)
|
||||
t.Assert(m["reset_password_token_at"], new(mysql.NullTime))
|
||||
})
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@ import (
|
||||
"github.com/gogf/gf/os/gcmd"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"github.com/gogf/mysql"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@ -292,19 +291,6 @@ CREATE TABLE %s (
|
||||
})
|
||||
}
|
||||
|
||||
// Fix issue: https://github.com/gogf/gf/issues/819
|
||||
func Test_Func_ConvertDataForTableRecord(t *testing.T) {
|
||||
type Test struct {
|
||||
ResetPasswordTokenAt mysql.NullTime `orm:"reset_password_token_at"`
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := ConvertDataForTableRecord(new(Test))
|
||||
t.Assert(len(m), 1)
|
||||
t.AssertNE(m["reset_password_token_at"], nil)
|
||||
t.Assert(m["reset_password_token_at"], new(mysql.NullTime))
|
||||
})
|
||||
}
|
||||
|
||||
func Test_isSubQuery(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(isSubQuery("user"), false)
|
||||
|
||||
@ -3871,3 +3871,44 @@ func Test_Model_FieldAvg(t *testing.T) {
|
||||
t.Assert(all[0]["total"], 1)
|
||||
})
|
||||
}
|
||||
|
||||
// https://github.com/gogf/gf/issues/1387
|
||||
func Test_Model_GTime_DefaultValue(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
Nickname string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
data := User{
|
||||
Id: 1,
|
||||
Passport: "user_1",
|
||||
Password: "pass_1",
|
||||
Nickname: "name_1",
|
||||
}
|
||||
// Insert
|
||||
_, err := db.Model(table).Data(data).Insert()
|
||||
t.AssertNil(err)
|
||||
|
||||
// Select
|
||||
var (
|
||||
user *User
|
||||
)
|
||||
err = db.Model(table).Scan(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Passport, data.Passport)
|
||||
t.Assert(user.Password, data.Password)
|
||||
t.Assert(user.CreateTime, data.CreateTime)
|
||||
t.Assert(user.Nickname, data.Nickname)
|
||||
|
||||
// Insert
|
||||
user.Id = 2
|
||||
_, err = db.Model(table).Data(user).Insert()
|
||||
t.AssertNil(err)
|
||||
})
|
||||
}
|
||||
|
||||
@ -169,14 +169,16 @@ func (j *Json) MustToTomlString() string {
|
||||
// INI
|
||||
// ========================================================================
|
||||
|
||||
// ToIni json to ini
|
||||
func (j *Json) ToIni() ([]byte, error) {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gini.Encode((*(j.p)).(map[string]interface{}))
|
||||
}
|
||||
|
||||
// ToIniString ini to string
|
||||
func (j *Json) ToIniString() (string, error) {
|
||||
b, e := j.ToToml()
|
||||
b, e := j.ToIni()
|
||||
return string(b), e
|
||||
}
|
||||
|
||||
@ -188,6 +190,7 @@ func (j *Json) MustToIni() []byte {
|
||||
return result
|
||||
}
|
||||
|
||||
// MustToIniString .
|
||||
func (j *Json) MustToIniString() string {
|
||||
return gconv.UnsafeBytesToStr(j.MustToIni())
|
||||
}
|
||||
|
||||
2
go.mod
2
go.mod
@ -7,7 +7,7 @@ require (
|
||||
github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28
|
||||
github.com/fatih/color v1.12.0
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/gomodule/redigo v1.8.5
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf
|
||||
|
||||
4
go.sum
4
go.sum
@ -8,8 +8,8 @@ github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
|
||||
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
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/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 h1:pP/uEy52biKDytlgK/ug8kiYPAiYu6KajKVUHfGrtyw=
|
||||
github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579/go.mod h1:52e6mXyNnHAsFrXrSnj5JPRSKsZKpHylVtA3j4AtMz8=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/gomodule/redigo v1.8.5 h1:nRAxCa+SVsyjSBrtZmG/cqb6VbTmuRzpg/PoTFlpumc=
|
||||
github.com/gomodule/redigo v1.8.5/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gcache provides kinds of cache management for process.
|
||||
// It default provides a concurrent-safe in-memory cache adapter for process.
|
||||
// It provides a concurrent-safe in-memory cache adapter for process in default.
|
||||
package gcache
|
||||
|
||||
import (
|
||||
@ -23,62 +23,62 @@ func Ctx(ctx context.Context) *Cache {
|
||||
return defaultCache.Ctx(ctx)
|
||||
}
|
||||
|
||||
// Set sets cache with <key>-<value> pair, which is expired after <duration>.
|
||||
// It does not expire if <duration> == 0.
|
||||
func Set(key interface{}, value interface{}, duration time.Duration) {
|
||||
defaultCache.Set(key, value, duration)
|
||||
// Set sets cache with `key`-`value` pair, which is expired after `duration`.
|
||||
// It does not expire if `duration` == 0.
|
||||
func Set(key interface{}, value interface{}, duration time.Duration) error {
|
||||
return defaultCache.Set(key, value, duration)
|
||||
}
|
||||
|
||||
// SetIfNotExist sets cache with <key>-<value> pair if <key> does not exist in the cache,
|
||||
// which is expired after <duration>. It does not expire if <duration> == 0.
|
||||
// SetIfNotExist sets cache with `key`-`value` pair if `key` does not exist in the cache,
|
||||
// which is expired after `duration`. It does not expire if `duration` == 0.
|
||||
func SetIfNotExist(key interface{}, value interface{}, duration time.Duration) (bool, error) {
|
||||
return defaultCache.SetIfNotExist(key, value, duration)
|
||||
}
|
||||
|
||||
// Sets batch sets cache with key-value pairs by <data>, which is expired after <duration>.
|
||||
// Sets batch sets cache with key-value pairs by `data`, which is expired after `duration`.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// It does not expire if `duration` == 0.
|
||||
func Sets(data map[interface{}]interface{}, duration time.Duration) error {
|
||||
return defaultCache.Sets(data, duration)
|
||||
}
|
||||
|
||||
// Get returns the value of <key>.
|
||||
// Get returns the value of `key`.
|
||||
// It returns nil if it does not exist or its value is nil.
|
||||
func Get(key interface{}) (interface{}, error) {
|
||||
return defaultCache.Get(key)
|
||||
}
|
||||
|
||||
// GetVar retrieves and returns the value of <key> as gvar.Var.
|
||||
// GetVar retrieves and returns the value of `key` as gvar.Var.
|
||||
func GetVar(key interface{}) (*gvar.Var, error) {
|
||||
return defaultCache.GetVar(key)
|
||||
}
|
||||
|
||||
// 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 <duration>.
|
||||
// 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 `duration`.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// It does not expire if `duration` == 0.
|
||||
func GetOrSet(key interface{}, value interface{}, duration time.Duration) (interface{}, error) {
|
||||
return defaultCache.GetOrSet(key, value, duration)
|
||||
}
|
||||
|
||||
// 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 <duration>. It does not expire if <duration> == 0.
|
||||
// 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 `duration`. It does not expire if `duration` == 0.
|
||||
func GetOrSetFunc(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) {
|
||||
return defaultCache.GetOrSetFunc(key, f, 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 <duration>. It does not expire if <duration> == 0.
|
||||
// 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 `duration`. It does not expire if `duration` == 0.
|
||||
//
|
||||
// Note that the function <f> is executed within writing mutex lock.
|
||||
// Note that the function `f` is executed within writing mutex lock.
|
||||
func GetOrSetFuncLock(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) {
|
||||
return defaultCache.GetOrSetFuncLock(key, f, duration)
|
||||
}
|
||||
|
||||
// Contains returns true if <key> exists in the cache, or else returns false.
|
||||
// Contains returns true if `key` exists in the cache, or else returns false.
|
||||
func Contains(key interface{}) (bool, error) {
|
||||
return defaultCache.Contains(key)
|
||||
}
|
||||
@ -89,10 +89,10 @@ func Remove(keys ...interface{}) (value interface{}, err error) {
|
||||
return defaultCache.Remove(keys...)
|
||||
}
|
||||
|
||||
// Removes deletes <keys> in the cache.
|
||||
// Removes deletes `keys` in the cache.
|
||||
// Deprecated, use Remove instead.
|
||||
func Removes(keys []interface{}) {
|
||||
defaultCache.Removes(keys)
|
||||
defaultCache.Remove(keys...)
|
||||
}
|
||||
|
||||
// Data returns a copy of all key-value pairs in the cache as map type.
|
||||
@ -120,20 +120,20 @@ func Size() (int, error) {
|
||||
return defaultCache.Size()
|
||||
}
|
||||
|
||||
// GetExpire retrieves and returns the expiration of <key>.
|
||||
// It returns -1 if the <key> does not exist in the cache.
|
||||
// GetExpire retrieves and returns the expiration of `key`.
|
||||
// It returns -1 if the `key` does not exist in the cache.
|
||||
func GetExpire(key interface{}) (time.Duration, error) {
|
||||
return defaultCache.GetExpire(key)
|
||||
}
|
||||
|
||||
// Update updates the value of <key> without changing its expiration and returns the old value.
|
||||
// The returned <exist> value is false if the <key> does not exist in the cache.
|
||||
// Update updates the value of `key` without changing its expiration and returns the old value.
|
||||
// The returned `exist` value is false if the `key` does not exist in the cache.
|
||||
func Update(key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) {
|
||||
return defaultCache.Update(key, value)
|
||||
}
|
||||
|
||||
// UpdateExpire updates the expiration of <key> and returns the old expiration duration value.
|
||||
// It returns -1 if the <key> does not exist in the cache.
|
||||
// UpdateExpire updates the expiration of `key` and returns the old expiration duration value.
|
||||
// It returns -1 if the `key` does not exist in the cache.
|
||||
func UpdateExpire(key interface{}, duration time.Duration) (oldDuration time.Duration, err error) {
|
||||
return defaultCache.UpdateExpire(key, duration)
|
||||
}
|
||||
|
||||
@ -11,95 +11,95 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Adapter is the adapter for cache features implements.
|
||||
// Adapter is the core adapter for cache features implements.
|
||||
type Adapter interface {
|
||||
// Set sets cache with <key>-<value> pair, which is expired after <duration>.
|
||||
// Set sets cache with `key`-`value` pair, which is expired after `duration`.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// It deletes the <key> if <duration> < 0.
|
||||
// It does not expire if `duration` == 0.
|
||||
// It deletes the `key` if `duration` < 0.
|
||||
Set(ctx context.Context, key interface{}, value interface{}, duration time.Duration) error
|
||||
|
||||
// Sets batch sets cache with key-value pairs by <data>, which is expired after <duration>.
|
||||
// Sets batch sets cache with key-value pairs by `data`, which is expired after `duration`.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// It deletes the keys of <data> if <duration> < 0 or given <value> is nil.
|
||||
// It does not expire if `duration` == 0.
|
||||
// It deletes the keys of `data` if `duration` < 0 or given `value` is nil.
|
||||
Sets(ctx context.Context, data map[interface{}]interface{}, duration time.Duration) error
|
||||
|
||||
// SetIfNotExist sets cache with <key>-<value> pair which is expired after <duration>
|
||||
// if <key> does not exist in the cache. It returns true the <key> dose not exist in the
|
||||
// cache and it sets <value> successfully to the cache, or else it returns false.
|
||||
// SetIfNotExist sets cache with `key`-`value` pair which is expired after `duration`
|
||||
// if `key` does not exist in the cache. It returns true the `key` does not exist in the
|
||||
// cache, and it sets `value` successfully to the cache, or else it returns false.
|
||||
//
|
||||
// The parameter <value> can be type of <func() interface{}>, but it dose nothing if its
|
||||
// The parameter `value` can be type of `func() interface{}`, but it does nothing if its
|
||||
// result is nil.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// It deletes the <key> if <duration> < 0 or given <value> is nil.
|
||||
// It does not expire if `duration` == 0.
|
||||
// It deletes the `key` if `duration` < 0 or given `value` is nil.
|
||||
SetIfNotExist(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (bool, error)
|
||||
|
||||
// Get retrieves and returns the associated value of given <key>.
|
||||
// It returns nil if it does not exist, its value is nil or it's expired.
|
||||
// Get retrieves and returns the associated value of given `key`.
|
||||
// It returns nil if it does not exist, its value is nil, or it's expired.
|
||||
Get(ctx context.Context, key interface{}) (interface{}, error)
|
||||
|
||||
// GetOrSet retrieves and 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 <duration>.
|
||||
// GetOrSet retrieves and 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 `duration`.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// It deletes the <key> if <duration> < 0 or given <value> is nil, but it does nothing
|
||||
// if <value> is a function and the function result is nil.
|
||||
// It does not expire if `duration` == 0.
|
||||
// It deletes the `key` if `duration` < 0 or given `value` is nil, but it does nothing
|
||||
// if `value` is a function and the function result is nil.
|
||||
GetOrSet(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (interface{}, error)
|
||||
|
||||
// GetOrSetFunc retrieves and 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 <duration>.
|
||||
// GetOrSetFunc retrieves and 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 `duration`.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// It deletes the <key> if <duration> < 0 or given <value> is nil, but it does nothing
|
||||
// if <value> is a function and the function result is nil.
|
||||
// It does not expire if `duration` == 0.
|
||||
// It deletes the `key` if `duration` < 0 or given `value` is nil, but it does nothing
|
||||
// if `value` is a function and the function result is nil.
|
||||
GetOrSetFunc(ctx context.Context, key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error)
|
||||
|
||||
// GetOrSetFuncLock retrieves and 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 <duration>.
|
||||
// GetOrSetFuncLock retrieves and 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 `duration`.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// It does nothing if function <f> returns nil.
|
||||
// It does not expire if `duration` == 0.
|
||||
// It does nothing if function `f` returns nil.
|
||||
//
|
||||
// Note that the function <f> should be executed within writing mutex lock for concurrent
|
||||
// Note that the function `f` should be executed within writing mutex lock for concurrent
|
||||
// safety purpose.
|
||||
GetOrSetFuncLock(ctx context.Context, key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error)
|
||||
|
||||
// Contains returns true if <key> exists in the cache, or else returns false.
|
||||
// Contains returns true if `key` exists in the cache, or else returns false.
|
||||
Contains(ctx context.Context, key interface{}) (bool, error)
|
||||
|
||||
// GetExpire retrieves and returns the expiration of <key> in the cache.
|
||||
// GetExpire retrieves and returns the expiration of `key` in the cache.
|
||||
//
|
||||
// It returns 0 if the <key> does not expire.
|
||||
// It returns -1 if the <key> does not exist in the cache.
|
||||
// It returns 0 if the `key` does not expire.
|
||||
// It returns -1 if the `key` does not exist in the cache.
|
||||
GetExpire(ctx context.Context, key interface{}) (time.Duration, error)
|
||||
|
||||
// Remove deletes one or more keys from cache, and returns its value.
|
||||
// If multiple keys are given, it returns the value of the last deleted item.
|
||||
Remove(ctx context.Context, keys ...interface{}) (value interface{}, err error)
|
||||
|
||||
// Update updates the value of <key> without changing its expiration and returns the old value.
|
||||
// The returned value <exist> is false if the <key> does not exist in the cache.
|
||||
// Update updates the value of `key` without changing its expiration and returns the old value.
|
||||
// The returned value `exist` is false if the `key` does not exist in the cache.
|
||||
//
|
||||
// It deletes the <key> if given <value> is nil.
|
||||
// It does nothing if <key> does not exist in the cache.
|
||||
// It deletes the `key` if given `value` is nil.
|
||||
// It does nothing if `key` does not exist in the cache.
|
||||
Update(ctx context.Context, key interface{}, value interface{}) (oldValue interface{}, exist bool, err error)
|
||||
|
||||
// UpdateExpire updates the expiration of <key> and returns the old expiration duration value.
|
||||
// UpdateExpire updates the expiration of `key` and returns the old expiration duration value.
|
||||
//
|
||||
// It returns -1 and does nothing if the <key> does not exist in the cache.
|
||||
// It deletes the <key> if <duration> < 0.
|
||||
// It returns -1 and does nothing if the `key` does not exist in the cache.
|
||||
// It deletes the `key` if `duration` < 0.
|
||||
UpdateExpire(ctx context.Context, key interface{}, duration time.Duration) (oldDuration time.Duration, err error)
|
||||
|
||||
// Size returns the number of items in the cache.
|
||||
Size(ctx context.Context) (size int, err error)
|
||||
|
||||
// Data returns a copy of all key-value pairs in the cache as map type.
|
||||
// Note that this function may leads lots of memory usage, you can implement this function
|
||||
// Note that this function may lead lots of memory usage, you can implement this function
|
||||
// if necessary.
|
||||
Data(ctx context.Context) (map[interface{}]interface{}, error)
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@ type adapterMemory struct {
|
||||
expireTimes *adapterMemoryExpireTimes // expireTimes is the expiring key to its timestamp mapping, which is used for quick indexing and deleting.
|
||||
expireSets *adapterMemoryExpireSets // expireSets is the expiring timestamp to its key set mapping, which is used for quick indexing and deleting.
|
||||
lru *adapterMemoryLru // lru is the LRU manager, which is enabled when attribute cap > 0.
|
||||
lruGetList *glist.List // lruGetList is the LRU history according with Get function.
|
||||
lruGetList *glist.List // lruGetList is the LRU history according to Get function.
|
||||
eventList *glist.List // eventList is the asynchronous event list for internal data synchronization.
|
||||
closed *gtype.Bool // closed controls the cache closed or not.
|
||||
}
|
||||
@ -69,10 +69,10 @@ func newAdapterMemory(lruCap ...int) *adapterMemory {
|
||||
return c
|
||||
}
|
||||
|
||||
// Set sets cache with <key>-<value> pair, which is expired after <duration>.
|
||||
// Set sets cache with `key`-`value` pair, which is expired after `duration`.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// It deletes the <key> if <duration> < 0.
|
||||
// It does not expire if `duration` == 0.
|
||||
// It deletes the `key` if `duration` < 0.
|
||||
func (c *adapterMemory) Set(ctx context.Context, key interface{}, value interface{}, duration time.Duration) error {
|
||||
expireTime := c.getInternalExpire(duration)
|
||||
c.data.Set(key, adapterMemoryItem{
|
||||
@ -86,19 +86,19 @@ func (c *adapterMemory) Set(ctx context.Context, key interface{}, value interfac
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update updates the value of <key> without changing its expiration and returns the old value.
|
||||
// The returned value <exist> is false if the <key> does not exist in the cache.
|
||||
// Update updates the value of `key` without changing its expiration and returns the old value.
|
||||
// The returned value `exist` is false if the `key` does not exist in the cache.
|
||||
//
|
||||
// It deletes the <key> if given <value> is nil.
|
||||
// It does nothing if <key> does not exist in the cache.
|
||||
// It deletes the `key` if given `value` is nil.
|
||||
// It does nothing if `key` does not exist in the cache.
|
||||
func (c *adapterMemory) Update(ctx context.Context, key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) {
|
||||
return c.data.Update(key, value)
|
||||
}
|
||||
|
||||
// UpdateExpire updates the expiration of <key> and returns the old expiration duration value.
|
||||
// UpdateExpire updates the expiration of `key` and returns the old expiration duration value.
|
||||
//
|
||||
// It returns -1 and does nothing if the <key> does not exist in the cache.
|
||||
// It deletes the <key> if <duration> < 0.
|
||||
// It returns -1 and does nothing if the `key` does not exist in the cache.
|
||||
// It deletes the `key` if `duration` < 0.
|
||||
func (c *adapterMemory) UpdateExpire(ctx context.Context, key interface{}, duration time.Duration) (oldDuration time.Duration, err error) {
|
||||
newExpireTime := c.getInternalExpire(duration)
|
||||
oldDuration, err = c.data.UpdateExpire(key, newExpireTime)
|
||||
@ -114,10 +114,10 @@ func (c *adapterMemory) UpdateExpire(ctx context.Context, key interface{}, durat
|
||||
return
|
||||
}
|
||||
|
||||
// GetExpire retrieves and returns the expiration of <key> in the cache.
|
||||
// GetExpire retrieves and returns the expiration of `key` in the cache.
|
||||
//
|
||||
// It returns 0 if the <key> does not expire.
|
||||
// It returns -1 if the <key> does not exist in the cache.
|
||||
// It returns 0 if the `key` does not expire.
|
||||
// It returns -1 if the `key` does not exist in the cache.
|
||||
func (c *adapterMemory) GetExpire(ctx context.Context, key interface{}) (time.Duration, error) {
|
||||
if item, ok := c.data.Get(key); ok {
|
||||
return time.Duration(item.e-gtime.TimestampMilli()) * time.Millisecond, nil
|
||||
@ -125,14 +125,14 @@ func (c *adapterMemory) GetExpire(ctx context.Context, key interface{}) (time.Du
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
// SetIfNotExist sets cache with <key>-<value> pair which is expired after <duration>
|
||||
// if <key> does not exist in the cache. It returns true the <key> dose not exist in the
|
||||
// cache and it sets <value> successfully to the cache, or else it returns false.
|
||||
// The parameter <value> can be type of <func() interface{}>, but it dose nothing if its
|
||||
// SetIfNotExist sets cache with `key`-`value` pair which is expired after `duration`
|
||||
// if `key` does not exist in the cache. It returns true the `key` does not exist in the
|
||||
// cache, and it sets `value` successfully to the cache, or else it returns false.
|
||||
// The parameter `value` can be type of <func() interface{}>, but it dose nothing if its
|
||||
// result is nil.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// It deletes the <key> if <duration> < 0 or given <value> is nil.
|
||||
// It does not expire if `duration` == 0.
|
||||
// It deletes the `key` if `duration` < 0 or given `value` is nil.
|
||||
func (c *adapterMemory) SetIfNotExist(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (bool, error) {
|
||||
isContained, err := c.Contains(ctx, key)
|
||||
if err != nil {
|
||||
@ -148,10 +148,10 @@ func (c *adapterMemory) SetIfNotExist(ctx context.Context, key interface{}, valu
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Sets batch sets cache with key-value pairs by <data>, which is expired after <duration>.
|
||||
// Sets batch sets cache with key-value pairs by `data`, which is expired after `duration`.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// It deletes the keys of <data> if <duration> < 0 or given <value> is nil.
|
||||
// It does not expire if `duration` == 0.
|
||||
// It deletes the keys of `data` if `duration` < 0 or given `value` is nil.
|
||||
func (c *adapterMemory) Sets(ctx context.Context, data map[interface{}]interface{}, duration time.Duration) error {
|
||||
var (
|
||||
expireTime = c.getInternalExpire(duration)
|
||||
@ -169,7 +169,7 @@ func (c *adapterMemory) Sets(ctx context.Context, data map[interface{}]interface
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get retrieves and returns the associated value of given <key>.
|
||||
// Get retrieves and returns the associated value of given `key`.
|
||||
// It returns nil if it does not exist or its value is nil.
|
||||
func (c *adapterMemory) Get(ctx context.Context, key interface{}) (interface{}, error) {
|
||||
item, ok := c.data.Get(key)
|
||||
@ -183,13 +183,13 @@ func (c *adapterMemory) Get(ctx context.Context, key interface{}) (interface{},
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetOrSet retrieves and 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 <duration>.
|
||||
// GetOrSet retrieves and 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 `duration`.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// It deletes the <key> if <duration> < 0 or given <value> is nil, but it does nothing
|
||||
// if <value> is a function and the function result is nil.
|
||||
// It does not expire if `duration` == 0.
|
||||
// It deletes the `key` if `duration` < 0 or given `value` is nil, but it does nothing
|
||||
// if `value` is a function and the function result is nil.
|
||||
func (c *adapterMemory) GetOrSet(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (interface{}, error) {
|
||||
v, err := c.Get(ctx, key)
|
||||
if err != nil {
|
||||
@ -202,13 +202,13 @@ func (c *adapterMemory) GetOrSet(ctx context.Context, key interface{}, value int
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc retrieves and 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 <duration>.
|
||||
// GetOrSetFunc retrieves and 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 `duration`.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// It deletes the <key> if <duration> < 0 or given <value> is nil, but it does nothing
|
||||
// if <value> is a function and the function result is nil.
|
||||
// It does not expire if `duration` == 0.
|
||||
// It deletes the `key` if `duration` < 0 or given `value` is nil, but it does nothing
|
||||
// if `value` is a function and the function result is nil.
|
||||
func (c *adapterMemory) GetOrSetFunc(ctx context.Context, key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) {
|
||||
v, err := c.Get(ctx, key)
|
||||
if err != nil {
|
||||
@ -228,14 +228,14 @@ func (c *adapterMemory) GetOrSetFunc(ctx context.Context, key interface{}, f fun
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock retrieves and 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 <duration>.
|
||||
// GetOrSetFuncLock retrieves and 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 `duration`.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// It does nothing if function <f> returns nil.
|
||||
// It does not expire if `duration` == 0.
|
||||
// It does nothing if function `f` returns nil.
|
||||
//
|
||||
// Note that the function <f> should be executed within writing mutex lock for concurrent
|
||||
// Note that the function `f` should be executed within writing mutex lock for concurrent
|
||||
// safety purpose.
|
||||
func (c *adapterMemory) GetOrSetFuncLock(ctx context.Context, key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) {
|
||||
v, err := c.Get(ctx, key)
|
||||
@ -249,7 +249,7 @@ func (c *adapterMemory) GetOrSetFuncLock(ctx context.Context, key interface{}, f
|
||||
}
|
||||
}
|
||||
|
||||
// Contains returns true if <key> exists in the cache, or else returns false.
|
||||
// Contains returns true if `key` exists in the cache, or else returns false.
|
||||
func (c *adapterMemory) Contains(ctx context.Context, key interface{}) (bool, error) {
|
||||
v, err := c.Get(ctx, key)
|
||||
if err != nil {
|
||||
@ -310,14 +310,14 @@ func (c *adapterMemory) Close(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// doSetWithLockCheck sets cache with <key>-<value> pair if <key> does not exist in the
|
||||
// cache, which is expired after <duration>.
|
||||
// doSetWithLockCheck sets cache with `key`-`value` pair if `key` does not exist in the
|
||||
// cache, which is expired after `duration`.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// The parameter <value> can be type of <func() interface{}>, but it dose nothing if the
|
||||
// It does not expire if `duration` == 0.
|
||||
// The parameter `value` can be type of <func() interface{}>, but it dose nothing if the
|
||||
// function result is nil.
|
||||
//
|
||||
// It doubly checks the <key> whether exists in the cache using mutex writing lock
|
||||
// It doubly checks the `key` whether exists in the cache using mutex writing lock
|
||||
// before setting it to the cache.
|
||||
func (c *adapterMemory) doSetWithLockCheck(key interface{}, value interface{}, duration time.Duration) (result interface{}, err error) {
|
||||
expireTimestamp := c.getInternalExpire(duration)
|
||||
@ -335,14 +335,14 @@ func (c *adapterMemory) getInternalExpire(duration time.Duration) int64 {
|
||||
}
|
||||
}
|
||||
|
||||
// makeExpireKey groups the <expire> in milliseconds to its according seconds.
|
||||
// makeExpireKey groups the `expire` in milliseconds to its according seconds.
|
||||
func (c *adapterMemory) makeExpireKey(expire int64) int64 {
|
||||
return int64(math.Ceil(float64(expire/1000)+1) * 1000)
|
||||
}
|
||||
|
||||
// syncEventAndClearExpired does the asynchronous task loop:
|
||||
// 1. Asynchronously process the data in the event list,
|
||||
// and synchronize the results to the <expireTimes> and <expireSets> properties.
|
||||
// and synchronize the results to the `expireTimes` and `expireSets` properties.
|
||||
// 2. Clean up the expired key-value pair data.
|
||||
func (c *adapterMemory) syncEventAndClearExpired() {
|
||||
if c.closed.Val() {
|
||||
@ -411,13 +411,13 @@ func (c *adapterMemory) syncEventAndClearExpired() {
|
||||
}
|
||||
}
|
||||
|
||||
// clearByKey deletes the key-value pair with given <key>.
|
||||
// The parameter <force> specifies whether doing this deleting forcibly.
|
||||
// clearByKey deletes the key-value pair with given `key`.
|
||||
// The parameter `force` specifies whether doing this deleting forcibly.
|
||||
func (c *adapterMemory) clearByKey(key interface{}, force ...bool) {
|
||||
// Doubly check before really deleting it from cache.
|
||||
c.data.DeleteWithDoubleCheck(key, force...)
|
||||
|
||||
// Deleting its expire time from <expireTimes>.
|
||||
// Deleting its expire time from `expireTimes`.
|
||||
c.expireTimes.Delete(key)
|
||||
|
||||
// Deleting it from LRU.
|
||||
|
||||
@ -23,11 +23,11 @@ func newAdapterMemoryData() *adapterMemoryData {
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates the value of <key> without changing its expiration and returns the old value.
|
||||
// The returned value <exist> is false if the <key> does not exist in the cache.
|
||||
// Update updates the value of `key` without changing its expiration and returns the old value.
|
||||
// The returned value `exist` is false if the `key` does not exist in the cache.
|
||||
//
|
||||
// It deletes the <key> if given <value> is nil.
|
||||
// It does nothing if <key> does not exist in the cache.
|
||||
// It deletes the `key` if given `value` is nil.
|
||||
// It does nothing if `key` does not exist in the cache.
|
||||
func (d *adapterMemoryData) Update(key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
@ -41,10 +41,10 @@ func (d *adapterMemoryData) Update(key interface{}, value interface{}) (oldValue
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
// UpdateExpire updates the expiration of <key> and returns the old expiration duration value.
|
||||
// UpdateExpire updates the expiration of `key` and returns the old expiration duration value.
|
||||
//
|
||||
// It returns -1 and does nothing if the <key> does not exist in the cache.
|
||||
// It deletes the <key> if <duration> < 0.
|
||||
// It returns -1 and does nothing if the `key` does not exist in the cache.
|
||||
// It deletes the `key` if `duration` < 0.
|
||||
func (d *adapterMemoryData) UpdateExpire(key interface{}, expireTime int64) (oldDuration time.Duration, err error) {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
@ -152,10 +152,10 @@ func (d *adapterMemoryData) Set(key interface{}, value adapterMemoryItem) {
|
||||
d.mu.Unlock()
|
||||
}
|
||||
|
||||
// Sets batch sets cache with key-value pairs by <data>, which is expired after <duration>.
|
||||
// Sets batch sets cache with key-value pairs by `data`, which is expired after `duration`.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// It deletes the keys of <data> if <duration> < 0 or given <value> is nil.
|
||||
// It does not expire if `duration` == 0.
|
||||
// It deletes the keys of `data` if `duration` < 0 or given `value` is nil.
|
||||
func (d *adapterMemoryData) Sets(data map[interface{}]interface{}, expireTime int64) error {
|
||||
d.mu.Lock()
|
||||
for k, v := range data {
|
||||
|
||||
@ -10,7 +10,7 @@ import (
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
)
|
||||
|
||||
// IsExpired checks whether <item> is expired.
|
||||
// IsExpired checks whether `item` is expired.
|
||||
func (item *adapterMemoryItem) IsExpired() bool {
|
||||
// Note that it should use greater than or equal judgement here
|
||||
// imagining that the cache time is only 1 millisecond.
|
||||
|
||||
@ -43,7 +43,7 @@ func (lru *adapterMemoryLru) Close() {
|
||||
lru.closed.Set(true)
|
||||
}
|
||||
|
||||
// Remove deletes the <key> FROM <lru>.
|
||||
// Remove deletes the `key` FROM `lru`.
|
||||
func (lru *adapterMemoryLru) Remove(key interface{}) {
|
||||
if v := lru.data.Get(key); v != nil {
|
||||
lru.data.Remove(key)
|
||||
@ -51,17 +51,17 @@ func (lru *adapterMemoryLru) Remove(key interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// Size returns the size of <lru>.
|
||||
// Size returns the size of `lru`.
|
||||
func (lru *adapterMemoryLru) Size() int {
|
||||
return lru.data.Size()
|
||||
}
|
||||
|
||||
// Push pushes <key> to the tail of <lru>.
|
||||
// Push pushes `key` to the tail of `lru`.
|
||||
func (lru *adapterMemoryLru) Push(key interface{}) {
|
||||
lru.rawList.PushBack(key)
|
||||
}
|
||||
|
||||
// Pop deletes and returns the key from tail of <lru>.
|
||||
// Pop deletes and returns the key from tail of `lru`.
|
||||
func (lru *adapterMemoryLru) Pop() interface{} {
|
||||
if v := lru.list.PopBack(); v != nil {
|
||||
lru.data.Remove(v)
|
||||
@ -78,7 +78,7 @@ func (lru *adapterMemoryLru) Pop() interface{} {
|
||||
// fmt.Println()
|
||||
//}
|
||||
|
||||
// SyncAndClear synchronizes the keys from <rawList> to <list> and <data>
|
||||
// SyncAndClear synchronizes the keys from `rawList` to `list` and `data`
|
||||
// using Least Recently Used algorithm.
|
||||
func (lru *adapterMemoryLru) SyncAndClear() {
|
||||
if lru.closed.Val() {
|
||||
|
||||
@ -56,13 +56,13 @@ func (c *Cache) SetAdapter(adapter Adapter) {
|
||||
c.adapter = adapter
|
||||
}
|
||||
|
||||
// GetVar retrieves and returns the value of <key> as gvar.Var.
|
||||
// GetVar retrieves and returns the value of `key` as gvar.Var.
|
||||
func (c *Cache) GetVar(key interface{}) (*gvar.Var, error) {
|
||||
v, err := c.Get(key)
|
||||
return gvar.New(v), err
|
||||
}
|
||||
|
||||
// Removes deletes <keys> in the cache.
|
||||
// Removes deletes `keys` in the cache.
|
||||
// Deprecated, use Remove instead.
|
||||
func (c *Cache) Removes(keys []interface{}) error {
|
||||
_, err := c.Remove(keys...)
|
||||
|
||||
@ -10,85 +10,85 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Set sets cache with <key>-<value> pair, which is expired after <duration>.
|
||||
// Set sets cache with `key`-`value` pair, which is expired after `duration`.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// It deletes the <key> if <duration> < 0.
|
||||
// It does not expire if `duration` == 0.
|
||||
// It deletes the `key` if `duration` < 0.
|
||||
func (c *Cache) Set(key interface{}, value interface{}, duration time.Duration) error {
|
||||
return c.adapter.Set(c.getCtx(), key, value, duration)
|
||||
}
|
||||
|
||||
// Sets batch sets cache with key-value pairs by <data>, which is expired after <duration>.
|
||||
// Sets batch sets cache with key-value pairs by `data`, which is expired after `duration`.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// It deletes the keys of <data> if <duration> < 0 or given <value> is nil.
|
||||
// It does not expire if `duration` == 0.
|
||||
// It deletes the keys of `data` if `duration` < 0 or given `value` is nil.
|
||||
func (c *Cache) Sets(data map[interface{}]interface{}, duration time.Duration) error {
|
||||
return c.adapter.Sets(c.getCtx(), data, duration)
|
||||
}
|
||||
|
||||
// SetIfNotExist sets cache with <key>-<value> pair which is expired after <duration>
|
||||
// if <key> does not exist in the cache. It returns true the <key> dose not exist in the
|
||||
// cache and it sets <value> successfully to the cache, or else it returns false.
|
||||
// SetIfNotExist sets cache with `key`-`value` pair which is expired after `duration`
|
||||
// if `key` does not exist in the cache. It returns true the `key` does not exist in the
|
||||
// cache, and it sets `value` successfully to the cache, or else it returns false.
|
||||
//
|
||||
// The parameter <value> can be type of <func() interface{}>, but it dose nothing if its
|
||||
// The parameter `value` can be type of <func() interface{}>, but it does nothing if its
|
||||
// result is nil.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// It deletes the <key> if <duration> < 0 or given <value> is nil.
|
||||
// It does not expire if `duration` == 0.
|
||||
// It deletes the `key` if `duration` < 0 or given `value` is nil.
|
||||
func (c *Cache) SetIfNotExist(key interface{}, value interface{}, duration time.Duration) (bool, error) {
|
||||
return c.adapter.SetIfNotExist(c.getCtx(), key, value, duration)
|
||||
}
|
||||
|
||||
// Get retrieves and returns the associated value of given <key>.
|
||||
// Get retrieves and returns the associated value of given `key`.
|
||||
// It returns nil if it does not exist, its value is nil or it's expired.
|
||||
func (c *Cache) Get(key interface{}) (interface{}, error) {
|
||||
return c.adapter.Get(c.getCtx(), key)
|
||||
}
|
||||
|
||||
// GetOrSet retrieves and 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 <duration>.
|
||||
// GetOrSet retrieves and 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 `duration`.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// It deletes the <key> if <duration> < 0 or given <value> is nil, but it does nothing
|
||||
// if <value> is a function and the function result is nil.
|
||||
// It does not expire if `duration` == 0.
|
||||
// It deletes the `key` if `duration` < 0 or given `value` is nil, but it does nothing
|
||||
// if `value` is a function and the function result is nil.
|
||||
func (c *Cache) GetOrSet(key interface{}, value interface{}, duration time.Duration) (interface{}, error) {
|
||||
return c.adapter.GetOrSet(c.getCtx(), key, value, duration)
|
||||
}
|
||||
|
||||
// GetOrSetFunc retrieves and 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 <duration>.
|
||||
// GetOrSetFunc retrieves and 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 `duration`.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// It deletes the <key> if <duration> < 0 or given <value> is nil, but it does nothing
|
||||
// if <value> is a function and the function result is nil.
|
||||
// It does not expire if `duration` == 0.
|
||||
// It deletes the `key` if `duration` < 0 or given `value` is nil, but it does nothing
|
||||
// if `value` is a function and the function result is nil.
|
||||
func (c *Cache) GetOrSetFunc(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) {
|
||||
return c.adapter.GetOrSetFunc(c.getCtx(), key, f, duration)
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock retrieves and 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 <duration>.
|
||||
// GetOrSetFuncLock retrieves and 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 `duration`.
|
||||
//
|
||||
// It does not expire if <duration> == 0.
|
||||
// It does nothing if function <f> returns nil.
|
||||
// It does not expire if `duration` == 0.
|
||||
// It does nothing if function `f` returns nil.
|
||||
//
|
||||
// Note that the function <f> should be executed within writing mutex lock for concurrent
|
||||
// Note that the function `f` should be executed within writing mutex lock for concurrent
|
||||
// safety purpose.
|
||||
func (c *Cache) GetOrSetFuncLock(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) {
|
||||
return c.adapter.GetOrSetFuncLock(c.getCtx(), key, f, duration)
|
||||
}
|
||||
|
||||
// Contains returns true if <key> exists in the cache, or else returns false.
|
||||
// Contains returns true if `key` exists in the cache, or else returns false.
|
||||
func (c *Cache) Contains(key interface{}) (bool, error) {
|
||||
return c.adapter.Contains(c.getCtx(), key)
|
||||
}
|
||||
|
||||
// GetExpire retrieves and returns the expiration of <key> in the cache.
|
||||
// GetExpire retrieves and returns the expiration of `key` in the cache.
|
||||
//
|
||||
// It returns 0 if the <key> does not expire.
|
||||
// It returns -1 if the <key> does not exist in the cache.
|
||||
// It returns 0 if the `key` does not expire.
|
||||
// It returns -1 if the `key` does not exist in the cache.
|
||||
func (c *Cache) GetExpire(key interface{}) (time.Duration, error) {
|
||||
return c.adapter.GetExpire(c.getCtx(), key)
|
||||
}
|
||||
@ -99,19 +99,19 @@ func (c *Cache) Remove(keys ...interface{}) (value interface{}, err error) {
|
||||
return c.adapter.Remove(c.getCtx(), keys...)
|
||||
}
|
||||
|
||||
// Update updates the value of <key> without changing its expiration and returns the old value.
|
||||
// The returned value <exist> is false if the <key> does not exist in the cache.
|
||||
// Update updates the value of `key` without changing its expiration and returns the old value.
|
||||
// The returned value `exist` is false if the `key` does not exist in the cache.
|
||||
//
|
||||
// It deletes the <key> if given <value> is nil.
|
||||
// It does nothing if <key> does not exist in the cache.
|
||||
// It deletes the `key` if given `value` is nil.
|
||||
// It does nothing if `key` does not exist in the cache.
|
||||
func (c *Cache) Update(key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) {
|
||||
return c.adapter.Update(c.getCtx(), key, value)
|
||||
}
|
||||
|
||||
// UpdateExpire updates the expiration of <key> and returns the old expiration duration value.
|
||||
// UpdateExpire updates the expiration of `key` and returns the old expiration duration value.
|
||||
//
|
||||
// It returns -1 and does nothing if the <key> does not exist in the cache.
|
||||
// It deletes the <key> if <duration> < 0.
|
||||
// It returns -1 and does nothing if the `key` does not exist in the cache.
|
||||
// It deletes the `key` if `duration` < 0.
|
||||
func (c *Cache) UpdateExpire(key interface{}, duration time.Duration) (oldDuration time.Duration, err error) {
|
||||
return c.adapter.UpdateExpire(c.getCtx(), key, duration)
|
||||
}
|
||||
@ -122,7 +122,7 @@ func (c *Cache) Size() (size int, err error) {
|
||||
}
|
||||
|
||||
// Data returns a copy of all key-value pairs in the cache as map type.
|
||||
// Note that this function may leads lots of memory usage, you can implement this function
|
||||
// Note that this function may lead lots of memory usage, you can implement this function
|
||||
// if necessary.
|
||||
func (c *Cache) Data() (map[interface{}]interface{}, error) {
|
||||
return c.adapter.Data(c.getCtx())
|
||||
|
||||
@ -133,11 +133,13 @@ func Entries() []*Entry {
|
||||
}
|
||||
|
||||
// Start starts running the specified timed task named `name`.
|
||||
func Start(name string) {
|
||||
defaultCron.Start(name)
|
||||
// If no`name` specified, it starts the entire cron.
|
||||
func Start(name ...string) {
|
||||
defaultCron.Start(name...)
|
||||
}
|
||||
|
||||
// Stop stops running the specified timed task named `name`.
|
||||
func Stop(name string) {
|
||||
defaultCron.Stop(name)
|
||||
// If no`name` specified, it stops the entire cron.
|
||||
func Stop(name ...string) {
|
||||
defaultCron.Stop(name...)
|
||||
}
|
||||
|
||||
@ -184,6 +184,7 @@ func (c *Cron) Search(name string) *Entry {
|
||||
}
|
||||
|
||||
// Start starts running the specified timed task named `name`.
|
||||
// If no`name` specified, it starts the entire cron.
|
||||
func (c *Cron) Start(name ...string) {
|
||||
if len(name) > 0 {
|
||||
for _, v := range name {
|
||||
@ -197,6 +198,7 @@ func (c *Cron) Start(name ...string) {
|
||||
}
|
||||
|
||||
// Stop stops running the specified timed task named `name`.
|
||||
// If no`name` specified, it stops the entire cron.
|
||||
func (c *Cron) Stop(name ...string) {
|
||||
if len(name) > 0 {
|
||||
for _, v := range name {
|
||||
|
||||
@ -33,7 +33,7 @@ func GetContentsWithCache(path string, duration ...time.Duration) string {
|
||||
return string(GetBytesWithCache(path, duration...))
|
||||
}
|
||||
|
||||
// GetBinContents returns []byte content of given file by <path> from cache.
|
||||
// GetBytesWithCache returns []byte content of given file by <path> from cache.
|
||||
// If there's no content in the cache, it will read it from disk file specified by <path>.
|
||||
// The parameter <expire> specifies the caching time for this file content in seconds.
|
||||
func GetBytesWithCache(path string, duration ...time.Duration) []byte {
|
||||
|
||||
@ -12,7 +12,7 @@ import (
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
)
|
||||
|
||||
// watchLoop starts the loop for event listening fro underlying inotify monitor.
|
||||
// watchLoop starts the loop for event listening from underlying inotify monitor.
|
||||
func (w *Watcher) watchLoop() {
|
||||
go func() {
|
||||
for {
|
||||
|
||||
@ -277,8 +277,9 @@ func (l *Logger) printToStdout(ctx context.Context, input *HandlerInput) *bytes.
|
||||
)
|
||||
// This will lose color in Windows os system.
|
||||
// if _, err := os.Stdout.Write(input.getRealBuffer(true).Bytes()); err != nil {
|
||||
|
||||
// This will print color in Windows os system.
|
||||
if _, err := fmt.Fprintf(color.Output, buffer.String()); err != nil {
|
||||
if _, err := fmt.Fprint(color.Output, buffer.String()); err != nil {
|
||||
intlog.Error(ctx, err)
|
||||
}
|
||||
return buffer
|
||||
|
||||
@ -7,10 +7,12 @@
|
||||
package gproc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/errors/gcode"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"github.com/gogf/gf/net/gtcp"
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
@ -32,9 +34,10 @@ type MsgResponse struct {
|
||||
}
|
||||
|
||||
const (
|
||||
defaultGroupNameFoProcComm = "" // Default group name.
|
||||
defaultTcpPortForProcComm = 10000 // Starting port number for receiver listening.
|
||||
maxLengthForProcMsgQueue = 10000 // Max size for each message queue of the group.
|
||||
defaultFolderNameForProcComm = "gf_pid_port_mapping" // Default folder name for storing pid to port mapping files.
|
||||
defaultGroupNameForProcComm = "" // Default group name.
|
||||
defaultTcpPortForProcComm = 10000 // Starting port number for receiver listening.
|
||||
maxLengthForProcMsgQueue = 10000 // Max size for each message queue of the group.
|
||||
)
|
||||
|
||||
var (
|
||||
@ -43,17 +46,36 @@ var (
|
||||
commReceiveQueues = gmap.NewStrAnyMap(true)
|
||||
|
||||
// commPidFolderPath specifies the folder path storing pid to port mapping files.
|
||||
commPidFolderPath = gfile.TempDir("gproc")
|
||||
commPidFolderPath string
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Automatically create the storage folder.
|
||||
if !gfile.Exists(commPidFolderPath) {
|
||||
err := gfile.Mkdir(commPidFolderPath)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf(`create gproc folder failed: %v`, err))
|
||||
availablePaths := []string{
|
||||
"/var/tmp",
|
||||
"/var/run",
|
||||
}
|
||||
if homePath, _ := gfile.Home(); homePath != "" {
|
||||
availablePaths = append(availablePaths, gfile.Join(homePath, ".config"))
|
||||
}
|
||||
availablePaths = append(availablePaths, gfile.TempDir())
|
||||
for _, availablePath := range availablePaths {
|
||||
checkPath := gfile.Join(availablePath, defaultFolderNameForProcComm)
|
||||
if !gfile.Exists(checkPath) && gfile.Mkdir(checkPath) != nil {
|
||||
continue
|
||||
}
|
||||
if gfile.IsWritable(checkPath) {
|
||||
commPidFolderPath = checkPath
|
||||
break
|
||||
}
|
||||
}
|
||||
if commPidFolderPath == "" {
|
||||
intlog.Errorf(
|
||||
context.TODO(),
|
||||
`cannot find available folder for storing pid to port mapping files in paths: %+v, process communication feature will fail`,
|
||||
availablePaths,
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// getConnByPid creates and returns a TCP connection for specified pid.
|
||||
@ -72,9 +94,7 @@ func getConnByPid(pid int) (*gtcp.PoolConn, error) {
|
||||
// getPortByPid returns the listening port for specified pid.
|
||||
// It returns 0 if no port found for the specified pid.
|
||||
func getPortByPid(pid int) int {
|
||||
path := getCommFilePath(pid)
|
||||
content := gfile.GetContentsWithCache(path)
|
||||
return gconv.Int(content)
|
||||
return gconv.Int(gfile.GetContentsWithCache(getCommFilePath(pid)))
|
||||
}
|
||||
|
||||
// getCommFilePath returns the pid to port mapping file path for given pid.
|
||||
|
||||
@ -35,7 +35,7 @@ func Receive(group ...string) *MsgRequest {
|
||||
if len(group) > 0 {
|
||||
groupName = group[0]
|
||||
} else {
|
||||
groupName = defaultGroupNameFoProcComm
|
||||
groupName = defaultGroupNameForProcComm
|
||||
}
|
||||
queue := commReceiveQueues.GetOrSetFuncLock(groupName, func() interface{} {
|
||||
return gqueue.New(maxLengthForProcMsgQueue)
|
||||
@ -79,8 +79,10 @@ func receiveTcpListening() {
|
||||
|
||||
// receiveTcpHandler is the connection handler for receiving data.
|
||||
func receiveTcpHandler(conn *gtcp.Conn) {
|
||||
var result []byte
|
||||
var response MsgResponse
|
||||
var (
|
||||
result []byte
|
||||
response MsgResponse
|
||||
)
|
||||
for {
|
||||
response.Code = 0
|
||||
response.Message = ""
|
||||
|
||||
@ -18,7 +18,7 @@ func Send(pid int, data []byte, group ...string) error {
|
||||
msg := MsgRequest{
|
||||
SendPid: Pid(),
|
||||
RecvPid: pid,
|
||||
Group: defaultGroupNameFoProcComm,
|
||||
Group: defaultGroupNameForProcComm,
|
||||
Data: data,
|
||||
}
|
||||
if len(group) > 0 {
|
||||
|
||||
@ -197,7 +197,7 @@ func ISO8601() string {
|
||||
return time.Now().Format("2006-01-02T15:04:05-07:00")
|
||||
}
|
||||
|
||||
// ISO8601 returns current datetime in RFC822 format like "Mon, 02 Jan 06 15:04 MST".
|
||||
// RFC822 returns current datetime in RFC822 format like "Mon, 02 Jan 06 15:04 MST".
|
||||
func RFC822() string {
|
||||
return time.Now().Format("Mon, 02 Jan 06 15:04 MST")
|
||||
}
|
||||
@ -238,6 +238,9 @@ func parseDateStr(s string) (year, month, day int) {
|
||||
// If <format> is not given, it converts string as a "standard" datetime string.
|
||||
// Note that, it fails and returns error if there's no date string in <str>.
|
||||
func StrToTime(str string, format ...string) (*Time, error) {
|
||||
if str == "" {
|
||||
return &Time{wrapper{time.Time{}}}, nil
|
||||
}
|
||||
if len(format) > 0 {
|
||||
return StrToTimeFormat(str, format[0])
|
||||
}
|
||||
|
||||
@ -33,10 +33,13 @@ func New(param ...interface{}) *Time {
|
||||
return NewFromTime(r)
|
||||
case *time.Time:
|
||||
return NewFromTime(*r)
|
||||
|
||||
case Time:
|
||||
return &r
|
||||
|
||||
case *Time:
|
||||
return r
|
||||
|
||||
case string:
|
||||
if len(param) > 1 {
|
||||
switch t := param[1].(type) {
|
||||
@ -47,6 +50,7 @@ func New(param ...interface{}) *Time {
|
||||
}
|
||||
}
|
||||
return NewFromStr(r)
|
||||
|
||||
case []byte:
|
||||
if len(param) > 1 {
|
||||
switch t := param[1].(type) {
|
||||
@ -57,10 +61,13 @@ func New(param ...interface{}) *Time {
|
||||
}
|
||||
}
|
||||
return NewFromStr(string(r))
|
||||
|
||||
case int:
|
||||
return NewFromTimeStamp(int64(r))
|
||||
|
||||
case int64:
|
||||
return NewFromTimeStamp(r)
|
||||
|
||||
default:
|
||||
if v, ok := r.(apiUnixNano); ok {
|
||||
return NewFromTimeStamp(v.UnixNano())
|
||||
|
||||
@ -28,95 +28,22 @@ const (
|
||||
NotFoundIndex = -1
|
||||
)
|
||||
|
||||
// Replace returns a copy of the string <origin>
|
||||
// in which string <search> replaced by <replace> case-sensitively.
|
||||
func Replace(origin, search, replace string, count ...int) string {
|
||||
n := -1
|
||||
if len(count) > 0 {
|
||||
n = count[0]
|
||||
}
|
||||
return strings.Replace(origin, search, replace, n)
|
||||
}
|
||||
const (
|
||||
defaultSuffixForStrLimit = "..."
|
||||
)
|
||||
|
||||
// Replace returns a copy of the string <origin>
|
||||
// in which string <search> replaced by <replace> case-insensitively.
|
||||
func ReplaceI(origin, search, replace string, count ...int) string {
|
||||
n := -1
|
||||
if len(count) > 0 {
|
||||
n = count[0]
|
||||
}
|
||||
if n == 0 {
|
||||
return origin
|
||||
}
|
||||
var (
|
||||
length = len(search)
|
||||
searchLower = strings.ToLower(search)
|
||||
)
|
||||
for {
|
||||
originLower := strings.ToLower(origin)
|
||||
if pos := strings.Index(originLower, searchLower); pos != -1 {
|
||||
origin = origin[:pos] + replace + origin[pos+length:]
|
||||
if n--; n == 0 {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return origin
|
||||
}
|
||||
|
||||
// Count counts the number of <substr> appears in <s>.
|
||||
// It returns 0 if no <substr> found in <s>.
|
||||
// Count counts the number of `substr` appears in `s`.
|
||||
// It returns 0 if no `substr` found in `s`.
|
||||
func Count(s, substr string) int {
|
||||
return strings.Count(s, substr)
|
||||
}
|
||||
|
||||
// CountI counts the number of <substr> appears in <s>, case-insensitively.
|
||||
// It returns 0 if no <substr> found in <s>.
|
||||
// CountI counts the number of `substr` appears in `s`, case-insensitively.
|
||||
// It returns 0 if no `substr` found in `s`.
|
||||
func CountI(s, substr string) int {
|
||||
return strings.Count(ToLower(s), ToLower(substr))
|
||||
}
|
||||
|
||||
// ReplaceByArray returns a copy of <origin>,
|
||||
// which is replaced by a slice in order, case-sensitively.
|
||||
func ReplaceByArray(origin string, array []string) string {
|
||||
for i := 0; i < len(array); i += 2 {
|
||||
if i+1 >= len(array) {
|
||||
break
|
||||
}
|
||||
origin = Replace(origin, array[i], array[i+1])
|
||||
}
|
||||
return origin
|
||||
}
|
||||
|
||||
// ReplaceIByArray returns a copy of <origin>,
|
||||
// which is replaced by a slice in order, case-insensitively.
|
||||
func ReplaceIByArray(origin string, array []string) string {
|
||||
for i := 0; i < len(array); i += 2 {
|
||||
if i+1 >= len(array) {
|
||||
break
|
||||
}
|
||||
origin = ReplaceI(origin, array[i], array[i+1])
|
||||
}
|
||||
return origin
|
||||
}
|
||||
|
||||
// ReplaceByMap returns a copy of <origin>,
|
||||
// which is replaced by a map in unordered way, case-sensitively.
|
||||
func ReplaceByMap(origin string, replaces map[string]string) string {
|
||||
return utils.ReplaceByMap(origin, replaces)
|
||||
}
|
||||
|
||||
// ReplaceIByMap returns a copy of <origin>,
|
||||
// which is replaced by a map in unordered way, case-insensitively.
|
||||
func ReplaceIByMap(origin string, replaces map[string]string) string {
|
||||
for k, v := range replaces {
|
||||
origin = ReplaceI(origin, k, v)
|
||||
}
|
||||
return origin
|
||||
}
|
||||
|
||||
// ToLower returns a copy of the string s with all Unicode letters mapped to their lower case.
|
||||
func ToLower(s string) string {
|
||||
return strings.ToLower(s)
|
||||
@ -163,86 +90,7 @@ func IsNumeric(s string) bool {
|
||||
return utils.IsNumeric(s)
|
||||
}
|
||||
|
||||
// SubStr returns a portion of string <str> specified by the <start> and <length> parameters.
|
||||
func SubStr(str string, start int, length ...int) (substr string) {
|
||||
lth := len(str)
|
||||
|
||||
// Simple border checks.
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if start >= lth {
|
||||
start = lth
|
||||
}
|
||||
end := lth
|
||||
if len(length) > 0 {
|
||||
end = start + length[0]
|
||||
if end < start {
|
||||
end = lth
|
||||
}
|
||||
}
|
||||
if end > lth {
|
||||
end = lth
|
||||
}
|
||||
return str[start:end]
|
||||
}
|
||||
|
||||
// SubStrRune returns a portion of string <str> specified by the <start> and <length> parameters.
|
||||
// SubStrRune considers parameter <str> as unicode string.
|
||||
func SubStrRune(str string, start int, length ...int) (substr string) {
|
||||
// Converting to []rune to support unicode.
|
||||
rs := []rune(str)
|
||||
lth := len(rs)
|
||||
|
||||
// Simple border checks.
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if start >= lth {
|
||||
start = lth
|
||||
}
|
||||
end := lth
|
||||
if len(length) > 0 {
|
||||
end = start + length[0]
|
||||
if end < start {
|
||||
end = lth
|
||||
}
|
||||
}
|
||||
if end > lth {
|
||||
end = lth
|
||||
}
|
||||
return string(rs[start:end])
|
||||
}
|
||||
|
||||
// StrLimit returns a portion of string <str> specified by <length> parameters, if the length
|
||||
// of <str> is greater than <length>, then the <suffix> will be appended to the result string.
|
||||
func StrLimit(str string, length int, suffix ...string) string {
|
||||
if len(str) < length {
|
||||
return str
|
||||
}
|
||||
addStr := "..."
|
||||
if len(suffix) > 0 {
|
||||
addStr = suffix[0]
|
||||
}
|
||||
return str[0:length] + addStr
|
||||
}
|
||||
|
||||
// StrLimitRune returns a portion of string <str> specified by <length> parameters, if the length
|
||||
// of <str> is greater than <length>, then the <suffix> will be appended to the result string.
|
||||
// StrLimitRune considers parameter <str> as unicode string.
|
||||
func StrLimitRune(str string, length int, suffix ...string) string {
|
||||
rs := []rune(str)
|
||||
if len(rs) < length {
|
||||
return str
|
||||
}
|
||||
addStr := "..."
|
||||
if len(suffix) > 0 {
|
||||
addStr = suffix[0]
|
||||
}
|
||||
return string(rs[0:length]) + addStr
|
||||
}
|
||||
|
||||
// Reverse returns a string which is the reverse of <str>.
|
||||
// Reverse returns a string which is the reverse of `str`.
|
||||
func Reverse(str string) string {
|
||||
runes := []rune(str)
|
||||
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
|
||||
@ -252,9 +100,9 @@ func Reverse(str string) string {
|
||||
}
|
||||
|
||||
// NumberFormat formats a number with grouped thousands.
|
||||
// <decimals>: Sets the number of decimal points.
|
||||
// <decPoint>: Sets the separator for the decimal point.
|
||||
// <thousandsSep>: Sets the thousands separator.
|
||||
// `decimals`: Sets the number of decimal points.
|
||||
// `decPoint`: Sets the separator for the decimal point.
|
||||
// `thousandsSep`: Sets the thousands' separator.
|
||||
// See http://php.net/manual/en/function.number-format.php.
|
||||
func NumberFormat(number float64, decimals int, decPoint, thousandsSep string) string {
|
||||
neg := false
|
||||
@ -301,7 +149,7 @@ func NumberFormat(number float64, decimals int, decPoint, thousandsSep string) s
|
||||
// Can be used to split a string into smaller chunks which is useful for
|
||||
// e.g. converting BASE64 string output to match RFC 2045 semantics.
|
||||
// It inserts end every chunkLen characters.
|
||||
// It considers parameter <body> and <end> as unicode string.
|
||||
// It considers parameter `body` and `end` as unicode string.
|
||||
func ChunkSplit(body string, chunkLen int, end string) string {
|
||||
if end == "" {
|
||||
end = "\r\n"
|
||||
@ -329,7 +177,7 @@ func Compare(a, b string) int {
|
||||
return strings.Compare(a, b)
|
||||
}
|
||||
|
||||
// Equal reports whether <a> and <b>, interpreted as UTF-8 strings,
|
||||
// Equal reports whether `a` and `b`, interpreted as UTF-8 strings,
|
||||
// are equal under Unicode case-folding, case-insensitively.
|
||||
func Equal(a, b string) bool {
|
||||
return strings.EqualFold(a, b)
|
||||
@ -351,7 +199,7 @@ func HasSuffix(s, suffix string) bool {
|
||||
}
|
||||
|
||||
// CountWords returns information about words' count used in a string.
|
||||
// It considers parameter <str> as unicode string.
|
||||
// It considers parameter `str` as unicode string.
|
||||
func CountWords(str string) map[string]int {
|
||||
m := make(map[string]int)
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
@ -372,7 +220,7 @@ func CountWords(str string) map[string]int {
|
||||
}
|
||||
|
||||
// CountChars returns information about chars' count used in a string.
|
||||
// It considers parameter <str> as unicode string.
|
||||
// It considers parameter `str` as unicode string.
|
||||
func CountChars(str string, noSpace ...bool) map[string]int {
|
||||
m := make(map[string]int)
|
||||
countSpace := true
|
||||
@ -465,51 +313,8 @@ func Repeat(input string, multiplier int) string {
|
||||
return strings.Repeat(input, multiplier)
|
||||
}
|
||||
|
||||
// Str returns part of <haystack> string starting from and including
|
||||
// the first occurrence of <needle> to the end of <haystack>.
|
||||
// See http://php.net/manual/en/function.strstr.php.
|
||||
func Str(haystack string, needle string) string {
|
||||
if needle == "" {
|
||||
return ""
|
||||
}
|
||||
pos := strings.Index(haystack, needle)
|
||||
if pos == NotFoundIndex {
|
||||
return ""
|
||||
}
|
||||
return haystack[pos+len([]byte(needle))-1:]
|
||||
}
|
||||
|
||||
// StrEx returns part of <haystack> string starting from and excluding
|
||||
// the first occurrence of <needle> to the end of <haystack>.
|
||||
func StrEx(haystack string, needle string) string {
|
||||
if s := Str(haystack, needle); s != "" {
|
||||
return s[1:]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// StrTill returns part of <haystack> string ending to and including
|
||||
// the first occurrence of <needle> from the start of <haystack>.
|
||||
func StrTill(haystack string, needle string) string {
|
||||
pos := strings.Index(haystack, needle)
|
||||
if pos == NotFoundIndex || pos == 0 {
|
||||
return ""
|
||||
}
|
||||
return haystack[:pos+1]
|
||||
}
|
||||
|
||||
// StrTillEx returns part of <haystack> string ending to and excluding
|
||||
// the first occurrence of <needle> from the start of <haystack>.
|
||||
func StrTillEx(haystack string, needle string) string {
|
||||
pos := strings.Index(haystack, needle)
|
||||
if pos == NotFoundIndex || pos == 0 {
|
||||
return ""
|
||||
}
|
||||
return haystack[:pos]
|
||||
}
|
||||
|
||||
// Shuffle randomly shuffles a string.
|
||||
// It considers parameter <str> as unicode string.
|
||||
// It considers parameter `str` as unicode string.
|
||||
func Shuffle(str string) string {
|
||||
runes := []rune(str)
|
||||
s := make([]rune, len(runes))
|
||||
@ -519,12 +324,12 @@ func Shuffle(str string) string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// Split splits string <str> by a string <delimiter>, to an array.
|
||||
// Split splits string `str` by a string `delimiter`, to an array.
|
||||
func Split(str, delimiter string) []string {
|
||||
return strings.Split(str, delimiter)
|
||||
}
|
||||
|
||||
// SplitAndTrim splits string <str> by a string <delimiter> to an array,
|
||||
// SplitAndTrim splits string `str` by a string `delimiter` to an array,
|
||||
// and calls Trim to every element of this array. It ignores the elements
|
||||
// which are empty after Trim.
|
||||
func SplitAndTrim(str, delimiter string, characterMask ...string) []string {
|
||||
@ -538,7 +343,7 @@ func SplitAndTrim(str, delimiter string, characterMask ...string) []string {
|
||||
return array
|
||||
}
|
||||
|
||||
// SplitAndTrimSpace splits string <str> by a string <delimiter> to an array,
|
||||
// SplitAndTrimSpace splits string `str` by a string `delimiter` to an array,
|
||||
// and calls TrimSpace to every element of this array.
|
||||
// Deprecated, use SplitAndTrim instead.
|
||||
func SplitAndTrimSpace(str, delimiter string) []string {
|
||||
@ -552,27 +357,27 @@ func SplitAndTrimSpace(str, delimiter string) []string {
|
||||
return array
|
||||
}
|
||||
|
||||
// Join concatenates the elements of <array> to create a single string. The separator string
|
||||
// <sep> is placed between elements in the resulting string.
|
||||
// Join concatenates the elements of `array` to create a single string. The separator string
|
||||
// `sep` is placed between elements in the resulting string.
|
||||
func Join(array []string, sep string) string {
|
||||
return strings.Join(array, sep)
|
||||
}
|
||||
|
||||
// JoinAny concatenates the elements of <array> to create a single string. The separator string
|
||||
// <sep> is placed between elements in the resulting string.
|
||||
// JoinAny concatenates the elements of `array` to create a single string. The separator string
|
||||
// `sep` is placed between elements in the resulting string.
|
||||
//
|
||||
// The parameter <array> can be any type of slice, which be converted to string array.
|
||||
// The parameter `array` can be any type of slice, which be converted to string array.
|
||||
func JoinAny(array interface{}, sep string) string {
|
||||
return strings.Join(gconv.Strings(array), sep)
|
||||
}
|
||||
|
||||
// Explode splits string <str> by a string <delimiter>, to an array.
|
||||
// Explode splits string `str` by a string `delimiter`, to an array.
|
||||
// See http://php.net/manual/en/function.explode.php.
|
||||
func Explode(delimiter, str string) []string {
|
||||
return Split(str, delimiter)
|
||||
}
|
||||
|
||||
// Implode joins array elements <pieces> with a string <glue>.
|
||||
// Implode joins array elements `pieces` with a string `glue`.
|
||||
// http://php.net/manual/en/function.implode.php
|
||||
func Implode(glue string, pieces []string) string {
|
||||
return strings.Join(pieces, glue)
|
||||
@ -588,8 +393,8 @@ func Ord(char string) int {
|
||||
return int(char[0])
|
||||
}
|
||||
|
||||
// HideStr replaces part of the the string <str> to <hide> by <percentage> from the <middle>.
|
||||
// It considers parameter <str> as unicode string.
|
||||
// HideStr replaces part of the string `str` to `hide` by `percentage` from the `middle`.
|
||||
// It considers parameter `str` as unicode string.
|
||||
func HideStr(str string, percent int, hide string) string {
|
||||
array := strings.Split(str, "@")
|
||||
if len(array) > 1 {
|
||||
@ -619,7 +424,7 @@ func HideStr(str string, percent int, hide string) string {
|
||||
|
||||
// Nl2Br inserts HTML line breaks(<br>|<br />) before all newlines in a string:
|
||||
// \n\r, \r\n, \r, \n.
|
||||
// It considers parameter <str> as unicode string.
|
||||
// It considers parameter `str` as unicode string.
|
||||
func Nl2Br(str string, isXhtml ...bool) string {
|
||||
r, n, runes := '\r', '\n', []rune(str)
|
||||
var br []byte
|
||||
@ -705,9 +510,9 @@ func QuoteMeta(str string, chars ...string) string {
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// SearchArray searches string <s> in string slice <a> case-sensitively,
|
||||
// returns its index in <a>.
|
||||
// If <s> is not found in <a>, it returns -1.
|
||||
// SearchArray searches string `s` in string slice `a` case-sensitively,
|
||||
// returns its index in `a`.
|
||||
// If `s` is not found in `a`, it returns -1.
|
||||
func SearchArray(a []string, s string) int {
|
||||
for i, v := range a {
|
||||
if s == v {
|
||||
@ -717,7 +522,7 @@ func SearchArray(a []string, s string) int {
|
||||
return NotFoundIndex
|
||||
}
|
||||
|
||||
// InArray checks whether string <s> in slice <a>.
|
||||
// InArray checks whether string `s` in slice `a`.
|
||||
func InArray(a []string, s string) bool {
|
||||
return SearchArray(a, s) != NotFoundIndex
|
||||
}
|
||||
|
||||
@ -8,8 +8,8 @@ package gstr
|
||||
|
||||
import "strings"
|
||||
|
||||
// Pos returns the position of the first occurrence of <needle>
|
||||
// in <haystack> from <startOffset>, case-sensitively.
|
||||
// Pos returns the position of the first occurrence of `needle`
|
||||
// in `haystack` from <startOffset>, case-sensitively.
|
||||
// It returns -1, if not found.
|
||||
func Pos(haystack, needle string, startOffset ...int) int {
|
||||
length := len(haystack)
|
||||
|
||||
89
text/gstr/gstr_replace.go
Normal file
89
text/gstr/gstr_replace.go
Normal file
@ -0,0 +1,89 @@
|
||||
// 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 gstr
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Replace returns a copy of the string `origin`
|
||||
// in which string `search` replaced by `replace` case-sensitively.
|
||||
func Replace(origin, search, replace string, count ...int) string {
|
||||
n := -1
|
||||
if len(count) > 0 {
|
||||
n = count[0]
|
||||
}
|
||||
return strings.Replace(origin, search, replace, n)
|
||||
}
|
||||
|
||||
// ReplaceI returns a copy of the string `origin`
|
||||
// in which string `search` replaced by `replace` case-insensitively.
|
||||
func ReplaceI(origin, search, replace string, count ...int) string {
|
||||
n := -1
|
||||
if len(count) > 0 {
|
||||
n = count[0]
|
||||
}
|
||||
if n == 0 {
|
||||
return origin
|
||||
}
|
||||
var (
|
||||
length = len(search)
|
||||
searchLower = strings.ToLower(search)
|
||||
)
|
||||
for {
|
||||
originLower := strings.ToLower(origin)
|
||||
if pos := strings.Index(originLower, searchLower); pos != -1 {
|
||||
origin = origin[:pos] + replace + origin[pos+length:]
|
||||
if n--; n == 0 {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return origin
|
||||
}
|
||||
|
||||
// ReplaceByArray returns a copy of `origin`,
|
||||
// which is replaced by a slice in order, case-sensitively.
|
||||
func ReplaceByArray(origin string, array []string) string {
|
||||
for i := 0; i < len(array); i += 2 {
|
||||
if i+1 >= len(array) {
|
||||
break
|
||||
}
|
||||
origin = Replace(origin, array[i], array[i+1])
|
||||
}
|
||||
return origin
|
||||
}
|
||||
|
||||
// ReplaceIByArray returns a copy of `origin`,
|
||||
// which is replaced by a slice in order, case-insensitively.
|
||||
func ReplaceIByArray(origin string, array []string) string {
|
||||
for i := 0; i < len(array); i += 2 {
|
||||
if i+1 >= len(array) {
|
||||
break
|
||||
}
|
||||
origin = ReplaceI(origin, array[i], array[i+1])
|
||||
}
|
||||
return origin
|
||||
}
|
||||
|
||||
// ReplaceByMap returns a copy of `origin`,
|
||||
// which is replaced by a map in unordered way, case-sensitively.
|
||||
func ReplaceByMap(origin string, replaces map[string]string) string {
|
||||
return utils.ReplaceByMap(origin, replaces)
|
||||
}
|
||||
|
||||
// ReplaceIByMap returns a copy of `origin`,
|
||||
// which is replaced by a map in unordered way, case-insensitively.
|
||||
func ReplaceIByMap(origin string, replaces map[string]string) string {
|
||||
for k, v := range replaces {
|
||||
origin = ReplaceI(origin, k, v)
|
||||
}
|
||||
return origin
|
||||
}
|
||||
52
text/gstr/gstr_str.go
Normal file
52
text/gstr/gstr_str.go
Normal file
@ -0,0 +1,52 @@
|
||||
// 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 gstr
|
||||
|
||||
import "strings"
|
||||
|
||||
// Str returns part of `haystack` string starting from and including
|
||||
// the first occurrence of `needle` to the end of `haystack`.
|
||||
// See http://php.net/manual/en/function.strstr.php.
|
||||
func Str(haystack string, needle string) string {
|
||||
if needle == "" {
|
||||
return ""
|
||||
}
|
||||
pos := strings.Index(haystack, needle)
|
||||
if pos == NotFoundIndex {
|
||||
return ""
|
||||
}
|
||||
return haystack[pos+len([]byte(needle))-1:]
|
||||
}
|
||||
|
||||
// StrEx returns part of `haystack` string starting from and excluding
|
||||
// the first occurrence of `needle` to the end of `haystack`.
|
||||
func StrEx(haystack string, needle string) string {
|
||||
if s := Str(haystack, needle); s != "" {
|
||||
return s[1:]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// StrTill returns part of `haystack` string ending to and including
|
||||
// the first occurrence of `needle` from the start of `haystack`.
|
||||
func StrTill(haystack string, needle string) string {
|
||||
pos := strings.Index(haystack, needle)
|
||||
if pos == NotFoundIndex || pos == 0 {
|
||||
return ""
|
||||
}
|
||||
return haystack[:pos+1]
|
||||
}
|
||||
|
||||
// StrTillEx returns part of `haystack` string ending to and excluding
|
||||
// the first occurrence of `needle` from the start of `haystack`.
|
||||
func StrTillEx(haystack string, needle string) string {
|
||||
pos := strings.Index(haystack, needle)
|
||||
if pos == NotFoundIndex || pos == 0 {
|
||||
return ""
|
||||
}
|
||||
return haystack[:pos]
|
||||
}
|
||||
89
text/gstr/gstr_substr.go
Normal file
89
text/gstr/gstr_substr.go
Normal file
@ -0,0 +1,89 @@
|
||||
// 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 gstr
|
||||
|
||||
// SubStr returns a portion of string `str` specified by the `start` and `length` parameters.
|
||||
// The parameter `length` is optional, it uses the length of `str` in default.
|
||||
func SubStr(str string, start int, length ...int) (substr string) {
|
||||
strLength := len(str)
|
||||
// Simple border checks.
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if start >= strLength {
|
||||
start = strLength
|
||||
}
|
||||
end := strLength
|
||||
if len(length) > 0 {
|
||||
end = start + length[0]
|
||||
if end < start {
|
||||
end = strLength
|
||||
}
|
||||
}
|
||||
if end > strLength {
|
||||
end = strLength
|
||||
}
|
||||
return str[start:end]
|
||||
}
|
||||
|
||||
// SubStrRune returns a portion of string `str` specified by the `start` and `length` parameters.
|
||||
// SubStrRune considers parameter `str` as unicode string.
|
||||
// The parameter `length` is optional, it uses the length of `str` in default.
|
||||
func SubStrRune(str string, start int, length ...int) (substr string) {
|
||||
// Converting to []rune to support unicode.
|
||||
var (
|
||||
runes = []rune(str)
|
||||
runesLength = len(runes)
|
||||
)
|
||||
|
||||
// Simple border checks.
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if start >= runesLength {
|
||||
start = runesLength
|
||||
}
|
||||
end := runesLength
|
||||
if len(length) > 0 {
|
||||
end = start + length[0]
|
||||
if end < start {
|
||||
end = runesLength
|
||||
}
|
||||
}
|
||||
if end > runesLength {
|
||||
end = runesLength
|
||||
}
|
||||
return string(runes[start:end])
|
||||
}
|
||||
|
||||
// StrLimit returns a portion of string `str` specified by `length` parameters, if the length
|
||||
// of `str` is greater than `length`, then the `suffix` will be appended to the result string.
|
||||
func StrLimit(str string, length int, suffix ...string) string {
|
||||
if len(str) < length {
|
||||
return str
|
||||
}
|
||||
suffixStr := defaultSuffixForStrLimit
|
||||
if len(suffix) > 0 {
|
||||
suffixStr = suffix[0]
|
||||
}
|
||||
return str[0:length] + suffixStr
|
||||
}
|
||||
|
||||
// StrLimitRune returns a portion of string `str` specified by `length` parameters, if the length
|
||||
// of `str` is greater than `length`, then the `suffix` will be appended to the result string.
|
||||
// StrLimitRune considers parameter `str` as unicode string.
|
||||
func StrLimitRune(str string, length int, suffix ...string) string {
|
||||
runes := []rune(str)
|
||||
if len(runes) < length {
|
||||
return str
|
||||
}
|
||||
suffixStr := defaultSuffixForStrLimit
|
||||
if len(suffix) > 0 {
|
||||
suffixStr = suffix[0]
|
||||
}
|
||||
return string(runes[0:length]) + suffixStr
|
||||
}
|
||||
@ -17,8 +17,8 @@ func Trim(str string, characterMask ...string) string {
|
||||
return utils.Trim(str, characterMask...)
|
||||
}
|
||||
|
||||
// TrimStr strips all of the given <cut> string from the beginning and end of a string.
|
||||
// Note that it does not strips the whitespaces of its beginning or end.
|
||||
// TrimStr strips all the given <cut> string from the beginning and end of a string.
|
||||
// Note that it does not strip the whitespaces of its beginning or end.
|
||||
func TrimStr(str string, cut string, count ...int) string {
|
||||
return TrimLeftStr(TrimRightStr(str, cut, count...), cut, count...)
|
||||
}
|
||||
@ -32,8 +32,8 @@ func TrimLeft(str string, characterMask ...string) string {
|
||||
return strings.TrimLeft(str, trimChars)
|
||||
}
|
||||
|
||||
// TrimLeftStr strips all of the given <cut> string from the beginning of a string.
|
||||
// Note that it does not strips the whitespaces of its beginning.
|
||||
// TrimLeftStr strips all the given <cut> string from the beginning of a string.
|
||||
// Note that it does not strip the whitespaces of its beginning.
|
||||
func TrimLeftStr(str string, cut string, count ...int) string {
|
||||
var (
|
||||
lenCut = len(cut)
|
||||
@ -58,8 +58,8 @@ func TrimRight(str string, characterMask ...string) string {
|
||||
return strings.TrimRight(str, trimChars)
|
||||
}
|
||||
|
||||
// TrimRightStr strips all of the given <cut> string from the end of a string.
|
||||
// Note that it does not strips the whitespaces of its end.
|
||||
// TrimRightStr strips all the given <cut> string from the end of a string.
|
||||
// Note that it does not strip the whitespaces of its end.
|
||||
func TrimRightStr(str string, cut string, count ...int) string {
|
||||
var (
|
||||
lenStr = len(str)
|
||||
|
||||
@ -46,7 +46,8 @@ func Test_Struct_UnmarshalValue1(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
st := &MyTimeSt{}
|
||||
err := gconv.Struct(g.Map{"ServiceDate": nil}, st)
|
||||
t.AssertNE(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(st.ServiceDate.Time.IsZero(), true)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
st := &MyTimeSt{}
|
||||
|
||||
@ -639,6 +639,22 @@ func Test_Struct_Time(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Struct_GTime(t *testing.T) {
|
||||
// https://github.com/gogf/gf/issues/1387
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var user *User
|
||||
err := gconv.Struct(`{"Name":"John","CreateTime":""}`, &user)
|
||||
t.AssertNil(err)
|
||||
t.AssertNE(user, nil)
|
||||
t.Assert(user.Name, `John`)
|
||||
t.Assert(user.CreateTime, nil)
|
||||
})
|
||||
}
|
||||
|
||||
// Auto create struct when given pointer.
|
||||
func Test_Struct_Create(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
package gmeta
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/internal/structs"
|
||||
)
|
||||
@ -21,34 +20,27 @@ const (
|
||||
metaAttributeName = "Meta"
|
||||
)
|
||||
|
||||
var (
|
||||
// metaDataCacheMap is a cache map for struct type to enhance the performance.
|
||||
metaDataCacheMap = gmap.NewStrAnyMap(true)
|
||||
)
|
||||
|
||||
// Data retrieves and returns all meta data from `object`.
|
||||
// Data retrieves and returns all metadata from `object`.
|
||||
// It automatically parses and caches the tag string from "Mata" attribute as its meta data.
|
||||
func Data(object interface{}) map[string]interface{} {
|
||||
reflectType, err := structs.StructType(object)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return metaDataCacheMap.GetOrSetFuncLock(reflectType.Signature(), func() interface{} {
|
||||
if field, ok := reflectType.FieldByName(metaAttributeName); ok {
|
||||
var (
|
||||
tags = structs.ParseTag(string(field.Tag))
|
||||
data = make(map[string]interface{}, len(tags))
|
||||
)
|
||||
for k, v := range tags {
|
||||
data[k] = v
|
||||
}
|
||||
return data
|
||||
if field, ok := reflectType.FieldByName(metaAttributeName); ok {
|
||||
var (
|
||||
tags = structs.ParseTag(string(field.Tag))
|
||||
data = make(map[string]interface{}, len(tags))
|
||||
)
|
||||
for k, v := range tags {
|
||||
data[k] = v
|
||||
}
|
||||
return map[string]interface{}{}
|
||||
}).(map[string]interface{})
|
||||
return data
|
||||
}
|
||||
return map[string]interface{}{}
|
||||
}
|
||||
|
||||
// Get retrieves and returns specified meta data by `key` from `object`.
|
||||
// Get retrieves and returns specified metadata by `key` from `object`.
|
||||
func Get(object interface{}, key string) *gvar.Var {
|
||||
return gvar.New(Data(object)[key])
|
||||
}
|
||||
|
||||
@ -17,12 +17,15 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
sequence gtype.Uint32 // Sequence for unique purpose of current process.
|
||||
const (
|
||||
sequenceMax = uint32(46655) // Sequence max("zzz").
|
||||
randomStrBase = "0123456789abcdefghijklmnopqrstuvwxyz" // Random chars string(36 bytes).
|
||||
macAddrStr = "0000000" // MAC addresses hash result in 7 bytes.
|
||||
processIdStr = "0000" // Process id in 4 bytes.
|
||||
)
|
||||
|
||||
var (
|
||||
sequence gtype.Uint32 // Sequence for unique purpose of current process.
|
||||
macAddrStr = "0000000" // MAC addresses hash result in 7 bytes.
|
||||
processIdStr = "0000" // Process id in 4 bytes.
|
||||
)
|
||||
|
||||
// init initializes several fixed local variable.
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package gf
|
||||
|
||||
const VERSION = "v1.17.0"
|
||||
const VERSION = "v1.16.6"
|
||||
const AUTHORS = "john<john@goframe.org>"
|
||||
|
||||
Reference in New Issue
Block a user