diff --git a/README.MD b/README.MD index ab18f553f..aa3e5a7f8 100644 --- a/README.MD +++ b/README.MD @@ -40,6 +40,7 @@ golang version >= 1.10 # Quick Start +## Hello World ```go package main @@ -56,8 +57,231 @@ func main() { s.Run() } ``` +## Rich Router +```go +package main -[View More..](https://goframe.org/start/index) +import ( + "github.com/gogf/gf/g/net/ghttp" + "github.com/gogf/gf/g" +) + +func main() { + s := g.Server() + s.BindHandler("/{class}-{course}/:name/*act", func(r *ghttp.Request) { + r.Response.Writeln(r.Get("class")) + r.Response.Writeln(r.Get("course")) + r.Response.Writeln(r.Get("name")) + r.Response.Writeln(r.Get("act")) + }) + s.SetPort(8199) + s.Run() +} +``` +## Group Routers +```go +package main + +import ( + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/net/ghttp" +) + +type Object struct {} + +func (o *Object) Show(r *ghttp.Request) { + r.Response.Writeln("Object Show") +} + +func (o *Object) Delete(r *ghttp.Request) { + r.Response.Writeln("Object REST Delete") +} + +func Handler(r *ghttp.Request) { + r.Response.Writeln("Handler") +} + +func HookHandler(r *ghttp.Request) { + r.Response.Writeln("Hook Handler") +} + +func main() { + s := g.Server() + obj := new(Object) + group := s.Group("/api") + group.ALL ("*", HookHandler, ghttp.HOOK_BEFORE_SERVE) + group.ALL ("/handler", Handler) + group.ALL ("/obj", obj) + group.GET ("/obj/showit", obj, "Show") + group.REST("/obj/rest", obj) + s.SetPort(8199) + s.Run() +} +``` +or +```go +func main() { + s := g.Server() + obj := new(Object) + s.Group("/api").Bind([]ghttp.GroupItem{ + {"ALL", "*", HookHandler, ghttp.HOOK_BEFORE_SERVE}, + {"ALL", "/handler", Handler}, + {"ALL", "/obj", obj}, + {"GET", "/obj/showit", obj, "Show"}, + {"REST", "/obj/rest", obj}, + }) + s.SetPort(8199) + s.Run() +} +``` +## Multi ports & domains +```go +package main + +import ( + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/net/ghttp" +) + +func Hello1(r *ghttp.Request) { + r.Response.Write("127.0.0.1: Hello1!") +} + +func Hello2(r *ghttp.Request) { + r.Response.Write("localhost: Hello2!") +} + +func main() { + s := g.Server() + s.Domain("127.0.0.1").BindHandler("/", Hello1) + s.Domain("localhost").BindHandler("/", Hello2) + s.SetPort(8100, 8200, 8300) + s.Run() +} +``` +## Template Engine +```go +package main + +import ( + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/net/ghttp" +) + +func main() { + s := g.Server() + s.BindHandler("/template", func(r *ghttp.Request) { + r.Response.WriteTpl("index.tpl", g.Map{ + "id": 123, + "name": "john", + }) + }) + s.SetPort(8199) + s.Run() +} +``` +## File Uploading +```go +func Upload(r *ghttp.Request) { + if f, h, e := r.FormFile("upload-file"); e == nil { + defer f.Close() + name := gfile.Basename(h.Filename) + buffer := make([]byte, h.Size) + f.Read(buffer) + gfile.PutBinContents("/tmp/" + name, buffer) + r.Response.Write(name + " uploaded successly") + } else { + r.Response.Write(e.Error()) + } +} +``` + +## ORM Operations + +### 1. Retrieving instance +```go +db := g.DB() +db := g.DB("user-center") +``` +### 2. Chaining Operations + +`Where + string` +```go +// SELECT * FROM user WHERE uid>1 LIMIT 0,10 +r, err := db.Table("user").Where("uid > ?", 1).Limit(0, 10).Select() + +// SELECT uid,name FROM user WHERE uid>1 LIMIT 0,10 +r, err := db.Table("user").Fileds("uid,name").Where("uid > ?", 1).Limit(0, 10).Select() + +// SELECT * FROM user WHERE uid=1 +r, err := db.Table("user").Where("u.uid=1",).One() +r, err := db.Table("user").Where("u.uid", 1).One() +r, err := db.Table("user").Where("u.uid=?", 1).One() +// SELECT * FROM user WHERE (uid=1) AND (name='john') +r, err := db.Table("user").Where("uid", 1).Where("name", "john").One() +r, err := db.Table("user").Where("uid=?", 1).And("name=?", "john").One() +// SELECT * FROM user WHERE (uid=1) OR (name='john') +r, err := db.Table("user").Where("uid=?", 1).Or("name=?", "john").One() +``` +`Where + map` +```go +// SELECT * FROM user WHERE uid=1 AND name='john' +r, err := db.Table("user").Where(g.Map{"uid" : 1, "name" : "john"}).One() +// SELECT * FROM user WHERE uid=1 AND age>18 +r, err := db.Table("user").Where(g.Map{"uid" : 1, "age>" : 18}).One() +``` +`Where + struct/*struct` +```go +type User struct { + Id int `json:"uid"` + UserName string `gconv:"name"` +} +// SELECT * FROM user WHERE uid =1 AND name='john' +r, err := db.Table("user").Where(User{ Id : 1, UserName : "john"}).One() +// SELECT * FROM user WHERE uid =1 +r, err := db.Table("user").Where(&User{ Id : 1}).One() +``` +### 3. Update & Delete +```go +// UPDATE user SET name='john guo' WHERE name='john' +r, err := db.Table("user").Data(gdb.Map{"name" : "john guo"}).Where("name=?", "john").Update() +r, err := db.Table("user").Data("name='john guo'").Where("name=?", "john").Update() +// UPDATE user SET status=1 ORDER BY login_time asc LIMIT 10 +r, err := db.Table("user").Data("status", 1).OrderBy("login_time asc").Limit(10).Update + +// DELETE FROM user WHERE uid=10 +r, err := db.Table("user").Where("uid=?", 10).Delete() +// DELETE FROM user ORDER BY login_time asc LIMIT 10 +r, err := db.Table("user").OrderBy("login_time asc").Limit(10).Delete() +``` +### 4. Insert & Replace & Save +```go +r, err := db.Table("user").Data(g.Map{"name": "john"}).Insert() +r, err := db.Table("user").Data(g.Map{"uid": 10000, "name": "john"}).Replace() +r, err := db.Table("user").Data(g.Map{"uid": 10001, "name": "john"}).Save() +``` +### 5. Transaction +```go +if tx, err := db.Begin(); err == nil { + r, err := tx.Save("user", g.Map{ + "uid" : 1, + "name" : "john", + }) + tx.Commit() +} +``` +### 6. Error Handling +```go +func GetOrderInfo(id int) (order *Order, err error) { + err = g.DB().Table("order").Where("id", id).Struct(&order) + if err != nil && err == sql.ErrNoRows { + err = nil + } + return +} +``` + +[More Features...](https://goframe.org/start/index) # License diff --git a/TODO.MD b/TODO.MD index 831cc43bd..357d77c94 100644 --- a/TODO.MD +++ b/TODO.MD @@ -51,7 +51,6 @@ 1. gdb.Model在链式安全的对象创建中增加sync.Pool的使用; 1. 增加g.Table快捷方法以方便操作数据表,但是得考虑后续模型操作设计,特别是脚手架的模型管理; - # DONE 1. gconv完善针对不同类型的判断,例如:尽量减少sprintf("%v", xxx)来执行string类型的转换; 2. ghttp.Server请求执行中增加服务退出的方法,不再执行后续操作; diff --git a/g/container/garray/garray_z_unit_basic_test.go b/g/container/garray/garray_z_unit_basic_test.go index a387bd3c0..61a40cf5d 100644 --- a/g/container/garray/garray_z_unit_basic_test.go +++ b/g/container/garray/garray_z_unit_basic_test.go @@ -31,6 +31,7 @@ func Test_SortedIntArray1(t *testing.T) { array.Add(i) } gtest.Assert(array.Slice(), expect) + gtest.Assert(array.Add().Slice(), expect) } func Test_SortedIntArray2(t *testing.T) { @@ -44,11 +45,15 @@ func Test_SortedIntArray2(t *testing.T) { func Test_SortedStringArray1(t *testing.T) { expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"} - array := garray.NewSortedStringArray() + array1 := garray.NewSortedStringArray() + array2 := garray.NewSortedStringArray(true) for i := 10; i > -1; i-- { - array.Add(gconv.String(i)) + array1.Add(gconv.String(i)) + array2.Add(gconv.String(i)) } - gtest.Assert(array.Slice(), expect) + gtest.Assert(array1.Slice(), expect) + gtest.Assert(array2.Slice(), expect) + } func Test_SortedStringArray2(t *testing.T) { @@ -58,6 +63,8 @@ func Test_SortedStringArray2(t *testing.T) { array.Add(gconv.String(i)) } gtest.Assert(array.Slice(), expect) + array.Add() + gtest.Assert(array.Slice(), expect) } func Test_SortedArray1(t *testing.T) { @@ -73,13 +80,18 @@ func Test_SortedArray1(t *testing.T) { func Test_SortedArray2(t *testing.T) { expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"} - array := garray.NewSortedArray(func(v1, v2 interface{}) int { + func1 := func(v1, v2 interface{}) int { return strings.Compare(gconv.String(v1), gconv.String(v2)) - }) + } + array := garray.NewSortedArray(func1) + array2 := garray.NewSortedArray(func1, true) for i := 0; i <= 10; i++ { array.Add(gconv.String(i)) + array2.Add(gconv.String(i)) } gtest.Assert(array.Slice(), expect) + gtest.Assert(array.Add().Slice(), expect) + gtest.Assert(array2.Slice(), expect) } func TestNewFromCopy(t *testing.T) { @@ -89,6 +101,5 @@ func TestNewFromCopy(t *testing.T) { gtest.AssertIN(array1.PopRands(2), a1) gtest.Assert(len(array1.PopRands(1)), 1) gtest.Assert(len(array1.PopRands(9)), 3) - }) } diff --git a/g/container/garray/garray_z_unit_int_test.go b/g/container/garray/garray_z_unit_int_test.go index 2a65f89f3..1f43da646 100644 --- a/g/container/garray/garray_z_unit_int_test.go +++ b/g/container/garray/garray_z_unit_int_test.go @@ -9,7 +9,9 @@ package garray_test import ( + "github.com/gogf/gf/g/util/gconv" "testing" + "time" "github.com/gogf/gf/g/container/garray" "github.com/gogf/gf/g/test/gtest" @@ -18,12 +20,15 @@ import ( func Test_IntArray_Basic(t *testing.T) { gtest.Case(t, func() { expect := []int{0, 1, 2, 3} + expect2 := []int{} array := garray.NewIntArrayFrom(expect) + array2 := garray.NewIntArrayFrom(expect2) gtest.Assert(array.Slice(), expect) array.Set(0, 100) gtest.Assert(array.Get(0), 100) gtest.Assert(array.Get(1), 1) gtest.Assert(array.Search(100), 0) + gtest.Assert(array2.Search(100), -1) gtest.Assert(array.Contains(100), true) gtest.Assert(array.Remove(0), 100) gtest.Assert(array.Contains(100), false) @@ -44,13 +49,16 @@ func TestIntArray_Sort(t *testing.T) { expect1 := []int{0, 1, 2, 3} expect2 := []int{3, 2, 1, 0} array := garray.NewIntArray() + array2 := garray.NewIntArray(true) for i := 3; i >= 0; i-- { array.Append(i) + array2.Append(i) } array.Sort() gtest.Assert(array.Slice(), expect1) array.Sort(true) gtest.Assert(array.Slice(), expect2) + gtest.Assert(array2.Slice(), expect2) }) } @@ -98,21 +106,48 @@ func TestIntArray_Range(t *testing.T) { gtest.Case(t, func() { value1 := []int{0, 1, 2, 3, 4, 5, 6} array1 := garray.NewIntArrayFrom(value1) + array2 := garray.NewIntArrayFrom(value1, true) gtest.Assert(array1.Range(0, 1), []int{0}) gtest.Assert(array1.Range(1, 2), []int{1}) gtest.Assert(array1.Range(0, 2), []int{0, 1}) gtest.Assert(array1.Range(10, 2), nil) gtest.Assert(array1.Range(-1, 10), value1) + gtest.Assert(array2.Range(1, 2), []int{1}) }) } func TestIntArray_Merge(t *testing.T) { gtest.Case(t, func() { - a1 := []int{0, 1, 2, 3} - a2 := []int{4, 5, 6, 7} - array1 := garray.NewIntArrayFrom(a1) - array2 := garray.NewIntArrayFrom(a2) - gtest.Assert(array1.Merge(array2).Slice(), []int{0, 1, 2, 3, 4, 5, 6, 7}) + func1 := func(v1, v2 interface{}) int { + if gconv.Int(v1) < gconv.Int(v2) { + return 0 + } + return 1 + } + + n1 := []int{0, 1, 2, 3} + n2 := []int{4, 5, 6, 7} + i1 := []interface{}{"1", "2"} + s1 := []string{"a", "b", "c"} + s2 := []string{"e", "f"} + a1 := garray.NewIntArrayFrom(n1) + a2 := garray.NewIntArrayFrom(n2) + a3 := garray.NewArrayFrom(i1) + a4 := garray.NewStringArrayFrom(s1) + + a5 := garray.NewSortedStringArrayFrom(s2) + a6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3}) + + a7 := garray.NewSortedStringArrayFrom(s1) + a8 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1) + + gtest.Assert(a1.Merge(a2).Slice(), []int{0, 1, 2, 3, 4, 5, 6, 7}) + gtest.Assert(a1.Merge(a3).Len(), 10) + gtest.Assert(a1.Merge(a4).Len(), 13) + gtest.Assert(a1.Merge(a5).Len(), 15) + gtest.Assert(a1.Merge(a6).Len(), 18) + gtest.Assert(a1.Merge(a7).Len(), 21) + gtest.Assert(a1.Merge(a8).Len(), 23) }) } @@ -124,6 +159,7 @@ func TestIntArray_Fill(t *testing.T) { array2 := garray.NewIntArrayFrom(a2) gtest.Assert(array1.Fill(1, 2, 100).Slice(), []int{0, 100, 100}) gtest.Assert(array2.Fill(0, 2, 100).Slice(), []int{100, 100}) + gtest.Assert(array2.Fill(-1, 2, 100).Slice(), []int{100, 100}) }) } @@ -136,6 +172,7 @@ func TestIntArray_Chunk(t *testing.T) { gtest.Assert(chunks[0], []int{1, 2}) gtest.Assert(chunks[1], []int{3, 4}) gtest.Assert(chunks[2], []int{5}) + gtest.Assert(array1.Chunk(0), nil) }) } @@ -153,6 +190,7 @@ func TestIntArray_SubSlice(t *testing.T) { gtest.Case(t, func() { a1 := []int{0, 1, 2, 3, 4, 5, 6} array1 := garray.NewIntArrayFrom(a1) + array2 := garray.NewIntArrayFrom(a1, true) gtest.Assert(array1.SubSlice(6), []int{6}) gtest.Assert(array1.SubSlice(5), []int{5, 6}) gtest.Assert(array1.SubSlice(8), nil) @@ -168,6 +206,7 @@ func TestIntArray_SubSlice(t *testing.T) { gtest.Assert(array1.SubSlice(-9, 3), nil) gtest.Assert(array1.SubSlice(1, -1), []int{0}) gtest.Assert(array1.SubSlice(1, -3), nil) + gtest.Assert(array2.SubSlice(0, 2), []int{0, 1}) }) } @@ -193,7 +232,6 @@ func TestIntArray_PopRands(t *testing.T) { ns2 := array.PopRands(7) gtest.AssertIN(len(ns2), 6) gtest.AssertIN(ns2, []int{100, 200, 300, 400, 500, 600}) - }) } @@ -226,6 +264,7 @@ func TestNewSortedIntArrayFrom(t *testing.T) { a1 := []int{0, 3, 2, 1, 4, 5, 6} array1 := garray.NewSortedIntArrayFrom(a1, true) gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6") + gtest.Assert(array1.Slice(), a1) }) } @@ -260,7 +299,6 @@ func TestSortedIntArray_Sort(t *testing.T) { gtest.Assert(array2.Len(), 4) gtest.Assert(array2, []int{0, 1, 2, 3}) - }) } @@ -271,7 +309,6 @@ func TestSortedIntArray_Get(t *testing.T) { gtest.Assert(array1.Get(0), 0) gtest.Assert(array1.Get(1), 1) gtest.Assert(array1.Get(3), 5) - }) } @@ -296,7 +333,6 @@ func TestSortedIntArray_Remove(t *testing.T) { i3 = array2.Remove(1) gtest.Assert(array2.Search(4), -1) gtest.Assert(i3, 4) - }) } @@ -308,7 +344,6 @@ func TestSortedIntArray_PopLeft(t *testing.T) { gtest.Assert(i1, 1) gtest.Assert(array1.Len(), 3) gtest.Assert(array1.Search(1), -1) - }) } @@ -348,7 +383,6 @@ func TestSortedIntArray_PopRands(t *testing.T) { gtest.Assert(array2.Len(), 0) gtest.Assert(len(ns2), 4) gtest.AssertIN(ns2, []int{1, 3, 5, 2}) - }) } @@ -365,7 +399,6 @@ func TestSortedIntArray_PopLefts(t *testing.T) { ns2 := array2.PopLefts(5) gtest.Assert(array2.Len(), 0) gtest.AssertIN(ns2, []int{1, 3, 5, 2}) - }) } @@ -389,6 +422,7 @@ func TestSortedIntArray_Range(t *testing.T) { gtest.Case(t, func() { a1 := []int{1, 3, 5, 2, 6, 7} array1 := garray.NewSortedIntArrayFrom(a1) + array2 := garray.NewSortedIntArrayFrom(a1, true) ns1 := array1.Range(1, 4) gtest.Assert(len(ns1), 3) gtest.Assert(ns1, []int{2, 3, 5}) @@ -401,7 +435,7 @@ func TestSortedIntArray_Range(t *testing.T) { nsl := array1.Range(5, 8) gtest.Assert(len(nsl), 1) - + gtest.Assert(array2.Range(1, 2), []int{2}) }) } @@ -418,7 +452,6 @@ func TestSortedIntArray_Contains(t *testing.T) { gtest.Case(t, func() { a1 := []int{1, 3, 5} array1 := garray.NewSortedIntArrayFrom(a1) - //gtest.Assert(array1.Contains(3),true) //todo 这一行应该返回true gtest.Assert(array1.Contains(4), false) }) } @@ -439,7 +472,6 @@ func TestSortedIntArray_Clear(t *testing.T) { array1 := garray.NewSortedIntArrayFrom(a1) array1.Clear() gtest.Assert(array1.Len(), 0) - }) } @@ -453,7 +485,6 @@ func TestSortedIntArray_Chunk(t *testing.T) { gtest.Assert(ns1[0], []int{1, 2}) gtest.Assert(ns1[2], []int{5}) gtest.Assert(len(ns2), 0) - }) } @@ -461,6 +492,7 @@ func TestSortedIntArray_SubSlice(t *testing.T) { gtest.Case(t, func() { a1 := []int{1, 2, 3, 4, 5} array1 := garray.NewSortedIntArrayFrom(a1) + array2 := garray.NewSortedIntArrayFrom(a1, true) ns1 := array1.SubSlice(1, 2) gtest.Assert(len(ns1), 2) gtest.Assert(ns1, []int{2, 3}) @@ -475,6 +507,10 @@ func TestSortedIntArray_SubSlice(t *testing.T) { ns4 := array1.SubSlice(3, 1) gtest.Assert(len(ns4), 1) gtest.Assert(ns4, []int{4}) + gtest.Assert(array1.SubSlice(-1, 1), []int{5}) + gtest.Assert(array1.SubSlice(-9, 1), nil) + gtest.Assert(array1.SubSlice(1, -9), nil) + gtest.Assert(array2.SubSlice(1, 2), []int{2, 3}) }) } @@ -519,7 +555,6 @@ func TestSortedIntArray_SetUnique(t *testing.T) { array1.SetUnique(true) gtest.Assert(array1.Len(), 5) gtest.Assert(array1, []int{1, 2, 3, 4, 5}) - }) } @@ -531,7 +566,6 @@ func TestIntArray_SetArray(t *testing.T) { array1.SetArray(a2) gtest.Assert(array1.Len(), 2) gtest.Assert(array1, []int{6, 7}) - }) } @@ -621,3 +655,171 @@ func TestIntArray_Remove(t *testing.T) { gtest.Assert(array1.Len(), 2) }) } + +func TestIntArray_LockFunc(t *testing.T) { + gtest.Case(t, func() { + s1 := []int{1, 2, 3, 4} + a1 := garray.NewIntArrayFrom(s1) + + ch1 := make(chan int64, 3) + ch2 := make(chan int64, 3) + //go1 + go a1.LockFunc(func(n1 []int) { //读写锁 + time.Sleep(2 * time.Second) //暂停2秒 + n1[2] = 6 + ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }) + + //go2 + go func() { + time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行. + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + a1.Len() + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }() + + t1 := <-ch1 + t2 := <-ch1 + <-ch2 //等待go1完成 + + // 防止ci抖动,以豪秒为单位 + gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。 + gtest.Assert(a1.Contains(6), true) + }) +} + +func TestIntArray_SortFunc(t *testing.T) { + gtest.Case(t, func() { + s1 := []int{1, 4, 3, 2} + a1 := garray.NewIntArrayFrom(s1) + func1 := func(v1, v2 int) bool { + return v1 < v2 + } + a11 := a1.SortFunc(func1) + gtest.Assert(a11, []int{1, 2, 3, 4}) + + }) +} + +func TestIntArray_RLockFunc(t *testing.T) { + gtest.Case(t, func() { + s1 := []int{1, 2, 3, 4} + a1 := garray.NewIntArrayFrom(s1) + + ch1 := make(chan int64, 3) + ch2 := make(chan int64, 1) + //go1 + go a1.RLockFunc(func(n1 []int) { //读锁 + time.Sleep(2 * time.Second) //暂停1秒 + n1[2] = 6 + ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }) + + //go2 + go func() { + time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行. + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + a1.Len() + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }() + + t1 := <-ch1 + t2 := <-ch1 + <-ch2 //等待go1完成 + + // 防止ci抖动,以豪秒为单位 + gtest.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候,并没有阻塞。 + gtest.Assert(a1.Contains(6), true) + }) +} + +func TestSortedIntArray_LockFunc(t *testing.T) { + gtest.Case(t, func() { + s1 := []int{1, 2, 3, 4} + a1 := garray.NewSortedIntArrayFrom(s1) + ch1 := make(chan int64, 3) + ch2 := make(chan int64, 3) + //go1 + go a1.LockFunc(func(n1 []int) { //读写锁 + time.Sleep(2 * time.Second) //暂停2秒 + n1[2] = 6 + ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }) + + //go2 + go func() { + time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行. + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + a1.Len() + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }() + + t1 := <-ch1 + t2 := <-ch1 + <-ch2 //等待go1完成 + + // 防止ci抖动,以豪秒为单位 + gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。 + gtest.Assert(a1.Contains(6), true) + }) +} + +func TestSortedIntArray_RLockFunc(t *testing.T) { + gtest.Case(t, func() { + s1 := []int{1, 2, 3, 4} + a1 := garray.NewSortedIntArrayFrom(s1) + + ch1 := make(chan int64, 3) + ch2 := make(chan int64, 1) + //go1 + go a1.RLockFunc(func(n1 []int) { //读锁 + time.Sleep(2 * time.Second) //暂停1秒 + n1[2] = 6 + ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }) + + //go2 + go func() { + time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行. + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + a1.Len() + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }() + + t1 := <-ch1 + t2 := <-ch1 + <-ch2 //等待go1完成 + + // 防止ci抖动,以豪秒为单位 + gtest.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候,并没有阻塞。 + gtest.Assert(a1.Contains(6), true) + }) +} + +func TestSortedIntArray_Merge(t *testing.T) { + gtest.Case(t, func() { + func1 := func(v1, v2 interface{}) int { + if gconv.Int(v1) < gconv.Int(v2) { + return 0 + } + return 1 + } + i0 := []int{1, 2, 3, 4} + s2 := []string{"e", "f"} + i1 := garray.NewIntArrayFrom([]int{1, 2, 3}) + i2 := garray.NewArrayFrom([]interface{}{3}) + s3 := garray.NewStringArrayFrom([]string{"g", "h"}) + s4 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1) + s5 := garray.NewSortedStringArrayFrom(s2) + s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3}) + a1 := garray.NewSortedIntArrayFrom(i0) + + gtest.Assert(a1.Merge(s2).Len(), 6) + gtest.Assert(a1.Merge(i1).Len(), 9) + gtest.Assert(a1.Merge(i2).Len(), 10) + gtest.Assert(a1.Merge(s3).Len(), 12) + gtest.Assert(a1.Merge(s4).Len(), 14) + gtest.Assert(a1.Merge(s5).Len(), 16) + gtest.Assert(a1.Merge(s6).Len(), 19) + }) +} diff --git a/g/container/garray/garray_z_unit_interface_test.go b/g/container/garray/garray_z_unit_interface_test.go index 90ed36d6b..d4671f356 100644 --- a/g/container/garray/garray_z_unit_interface_test.go +++ b/g/container/garray/garray_z_unit_interface_test.go @@ -14,28 +14,36 @@ import ( "github.com/gogf/gf/g/util/gconv" "strings" "testing" + "time" ) func Test_Array_Basic(t *testing.T) { gtest.Case(t, func() { expect := []interface{}{0, 1, 2, 3} array := garray.NewArrayFrom(expect) + array2 := garray.NewArrayFrom(expect) + array3 := garray.NewArrayFrom([]interface{}{}) gtest.Assert(array.Slice(), expect) array.Set(0, 100) gtest.Assert(array.Get(0), 100) gtest.Assert(array.Get(1), 1) gtest.Assert(array.Search(100), 0) + gtest.Assert(array3.Search(100), -1) gtest.Assert(array.Contains(100), true) gtest.Assert(array.Remove(0), 100) + + gtest.Assert(array2.Remove(3), 3) + gtest.Assert(array2.Remove(1), 1) + gtest.Assert(array.Contains(100), false) array.Append(4) gtest.Assert(array.Len(), 4) array.InsertBefore(0, 100) array.InsertAfter(0, 200) - gtest.Assert(array.Slice(), []interface{}{100, 200, 1, 2, 3, 4}) + gtest.Assert(array.Slice(), []interface{}{100, 200, 2, 2, 3, 4}) array.InsertBefore(5, 300) array.InsertAfter(6, 400) - gtest.Assert(array.Slice(), []interface{}{100, 200, 1, 2, 3, 300, 4, 400}) + gtest.Assert(array.Slice(), []interface{}{100, 200, 2, 2, 3, 300, 4, 400}) gtest.Assert(array.Clear().Len(), 0) }) } @@ -111,20 +119,48 @@ func TestArray_Range(t *testing.T) { gtest.Case(t, func() { value1 := []interface{}{0, 1, 2, 3, 4, 5, 6} array1 := garray.NewArrayFrom(value1) + array2 := garray.NewArrayFrom(value1, true) gtest.Assert(array1.Range(0, 1), []interface{}{0}) gtest.Assert(array1.Range(1, 2), []interface{}{1}) gtest.Assert(array1.Range(0, 2), []interface{}{0, 1}) gtest.Assert(array1.Range(-1, 10), value1) + gtest.Assert(array1.Range(10, 2), nil) + gtest.Assert(array2.Range(1, 3), []interface{}{1, 2}) }) } func TestArray_Merge(t *testing.T) { gtest.Case(t, func() { - a1 := []interface{}{0, 1, 2, 3} - a2 := []interface{}{4, 5, 6, 7} - array1 := garray.NewArrayFrom(a1) - array2 := garray.NewArrayFrom(a2) + func1 := func(v1, v2 interface{}) int { + if gconv.Int(v1) < gconv.Int(v2) { + return 0 + } + return 1 + } + + i1 := []interface{}{0, 1, 2, 3} + i2 := []interface{}{4, 5, 6, 7} + array1 := garray.NewArrayFrom(i1) + array2 := garray.NewArrayFrom(i2) gtest.Assert(array1.Merge(array2).Slice(), []interface{}{0, 1, 2, 3, 4, 5, 6, 7}) + + //s1 := []string{"a", "b", "c", "d"} + s2 := []string{"e", "f"} + i3 := garray.NewIntArrayFrom([]int{1, 2, 3}) + i4 := garray.NewArrayFrom([]interface{}{3}) + s3 := garray.NewStringArrayFrom([]string{"g", "h"}) + s4 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1) + s5 := garray.NewSortedStringArrayFrom(s2) + s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3}) + a1 := garray.NewArrayFrom(i1) + + gtest.Assert(a1.Merge(s2).Len(), 6) + gtest.Assert(a1.Merge(i3).Len(), 9) + gtest.Assert(a1.Merge(i4).Len(), 10) + gtest.Assert(a1.Merge(s3).Len(), 12) + gtest.Assert(a1.Merge(s4).Len(), 14) + gtest.Assert(a1.Merge(s5).Len(), 16) + gtest.Assert(a1.Merge(s6).Len(), 19) }) } @@ -133,9 +169,10 @@ func TestArray_Fill(t *testing.T) { a1 := []interface{}{0} a2 := []interface{}{0} array1 := garray.NewArrayFrom(a1) - array2 := garray.NewArrayFrom(a2) + array2 := garray.NewArrayFrom(a2, true) gtest.Assert(array1.Fill(1, 2, 100).Slice(), []interface{}{0, 100, 100}) gtest.Assert(array2.Fill(0, 2, 100).Slice(), []interface{}{100, 100}) + gtest.Assert(array2.Fill(-1, 2, 100).Slice(), []interface{}{100, 100}) }) } @@ -148,6 +185,7 @@ func TestArray_Chunk(t *testing.T) { gtest.Assert(chunks[0], []interface{}{1, 2}) gtest.Assert(chunks[1], []interface{}{3, 4}) gtest.Assert(chunks[2], []interface{}{5}) + gtest.Assert(array1.Chunk(0), nil) }) } @@ -165,9 +203,15 @@ func TestArray_SubSlice(t *testing.T) { gtest.Case(t, func() { a1 := []interface{}{0, 1, 2, 3, 4, 5, 6} array1 := garray.NewArrayFrom(a1) + array2 := garray.NewArrayFrom(a1, true) gtest.Assert(array1.SubSlice(0, 2), []interface{}{0, 1}) gtest.Assert(array1.SubSlice(2, 2), []interface{}{2, 3}) gtest.Assert(array1.SubSlice(5, 8), []interface{}{5, 6}) + gtest.Assert(array1.SubSlice(9, 1), nil) + gtest.Assert(array1.SubSlice(-2, 2), []interface{}{5, 6}) + gtest.Assert(array1.SubSlice(-9, 2), nil) + gtest.Assert(array1.SubSlice(1, -2), nil) + gtest.Assert(array2.SubSlice(0, 2), []interface{}{0, 1}) }) } @@ -179,6 +223,14 @@ func TestArray_Rand(t *testing.T) { gtest.Assert(len(array1.Rands(10)), 7) gtest.AssertIN(array1.Rands(1)[0], a1) }) + + gtest.Case(t, func() { + s1 := []interface{}{"a", "b", "c", "d"} + a1 := garray.NewArrayFrom(s1) + i1 := a1.Rand() + gtest.Assert(a1.Contains(i1), true) + gtest.Assert(a1.Len(), 4) + }) } func TestArray_Shuffle(t *testing.T) { @@ -496,6 +548,7 @@ func TestSortedArray_Range(t *testing.T) { return strings.Compare(gconv.String(v1), gconv.String(v2)) } array1 := garray.NewSortedArrayFrom(a1, func1) + array2 := garray.NewSortedArrayFrom(a1, func1, true) i1 := array1.Range(2, 5) gtest.Assert(i1, []interface{}{"c", "d", "e"}) gtest.Assert(array1.Len(), 6) @@ -509,6 +562,8 @@ func TestSortedArray_Range(t *testing.T) { gtest.Assert(len(i2), 2) gtest.Assert(i2, []interface{}{"e", "f"}) + gtest.Assert(array2.Range(1, 3), []interface{}{"b", "c"}) + }) } @@ -587,6 +642,7 @@ func TestSortedArray_SubSlice(t *testing.T) { return strings.Compare(gconv.String(v1), gconv.String(v2)) } array1 := garray.NewSortedArrayFrom(a1, func1) + array2 := garray.NewSortedArrayFrom(a1, func1, true) i1 := array1.SubSlice(2, 3) gtest.Assert(len(i1), 3) gtest.Assert(i1, []interface{}{"c", "d", "e"}) @@ -598,6 +654,13 @@ func TestSortedArray_SubSlice(t *testing.T) { i1 = array1.SubSlice(7, 2) gtest.Assert(len(i1), 0) + s1 := array1.SubSlice(1, -2) + gtest.Assert(s1, nil) + + s1 = array1.SubSlice(-9, 2) + gtest.Assert(s1, nil) + gtest.Assert(array2.SubSlice(1, 3), []interface{}{"b", "c", "d"}) + }) } @@ -676,3 +739,168 @@ func TestSortedArray_SetUnique(t *testing.T) { gtest.Assert(array1, []interface{}{"a", "c", "d"}) }) } + +func TestSortedArray_LockFunc(t *testing.T) { + gtest.Case(t, func() { + func1 := func(v1, v2 interface{}) int { + return strings.Compare(gconv.String(v1), gconv.String(v2)) + } + s1 := []interface{}{"a", "b", "c", "d"} + a1 := garray.NewSortedArrayFrom(s1, func1) + + ch1 := make(chan int64, 3) + ch2 := make(chan int64, 3) + //go1 + go a1.LockFunc(func(n1 []interface{}) { //读写锁 + time.Sleep(2 * time.Second) //暂停2秒 + n1[2] = "g" + ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }) + + //go2 + go func() { + time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行. + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + a1.Len() + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }() + + t1 := <-ch1 + t2 := <-ch1 + <-ch2 //等待go1完成 + + // 防止ci抖动,以豪秒为单位 + gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。 + gtest.Assert(a1.Contains("g"), true) + }) +} + +func TestSortedArray_RLockFunc(t *testing.T) { + gtest.Case(t, func() { + func1 := func(v1, v2 interface{}) int { + return strings.Compare(gconv.String(v1), gconv.String(v2)) + } + s1 := []interface{}{"a", "b", "c", "d"} + a1 := garray.NewSortedArrayFrom(s1, func1) + + ch1 := make(chan int64, 3) + ch2 := make(chan int64, 3) + //go1 + go a1.RLockFunc(func(n1 []interface{}) { //读写锁 + time.Sleep(2 * time.Second) //暂停2秒 + n1[2] = "g" + ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }) + + //go2 + go func() { + time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行. + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + a1.Len() + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }() + + t1 := <-ch1 + t2 := <-ch1 + <-ch2 //等待go1完成 + + // 防止ci抖动,以豪秒为单位 + gtest.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候不会被阻塞。 + gtest.Assert(a1.Contains("g"), true) + }) +} + +func TestSortedArray_Merge(t *testing.T) { + gtest.Case(t, func() { + func1 := func(v1, v2 interface{}) int { + if gconv.Int(v1) < gconv.Int(v2) { + return 0 + } + return 1 + } + + s1 := []interface{}{"a", "b", "c", "d"} + s2 := []string{"e", "f"} + i1 := garray.NewIntArrayFrom([]int{1, 2, 3}) + i2 := garray.NewArrayFrom([]interface{}{3}) + s3 := garray.NewStringArrayFrom([]string{"g", "h"}) + s4 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1) + s5 := garray.NewSortedStringArrayFrom(s2) + s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3}) + + a1 := garray.NewSortedArrayFrom(s1, func1) + + gtest.Assert(a1.Merge(s2).Len(), 6) + gtest.Assert(a1.Merge(i1).Len(), 9) + gtest.Assert(a1.Merge(i2).Len(), 10) + gtest.Assert(a1.Merge(s3).Len(), 12) + gtest.Assert(a1.Merge(s4).Len(), 14) + gtest.Assert(a1.Merge(s5).Len(), 16) + gtest.Assert(a1.Merge(s6).Len(), 19) + + }) +} + +func TestArray_LockFunc(t *testing.T) { + gtest.Case(t, func() { + s1 := []interface{}{"a", "b", "c", "d"} + a1 := garray.NewArrayFrom(s1) + + ch1 := make(chan int64, 3) + ch2 := make(chan int64, 3) + //go1 + go a1.LockFunc(func(n1 []interface{}) { //读写锁 + time.Sleep(2 * time.Second) //暂停2秒 + n1[2] = "g" + ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }) + + //go2 + go func() { + time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行. + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + a1.Len() + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }() + + t1 := <-ch1 + t2 := <-ch1 + <-ch2 //等待go1完成 + + // 防止ci抖动,以豪秒为单位 + gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。 + gtest.Assert(a1.Contains("g"), true) + }) +} + +func TestArray_RLockFunc(t *testing.T) { + gtest.Case(t, func() { + s1 := []interface{}{"a", "b", "c", "d"} + a1 := garray.NewArrayFrom(s1) + + ch1 := make(chan int64, 3) + ch2 := make(chan int64, 1) + //go1 + go a1.RLockFunc(func(n1 []interface{}) { //读锁 + time.Sleep(2 * time.Second) //暂停1秒 + n1[2] = "g" + ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }) + + //go2 + go func() { + time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行. + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + a1.Len() + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }() + + t1 := <-ch1 + t2 := <-ch1 + <-ch2 //等待go1完成 + + // 防止ci抖动,以豪秒为单位 + gtest.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候,并没有阻塞。 + gtest.Assert(a1.Contains("g"), true) + }) +} diff --git a/g/container/garray/garray_z_unit_string_test.go b/g/container/garray/garray_z_unit_string_test.go index b7c912e63..4e8e8fd6e 100644 --- a/g/container/garray/garray_z_unit_string_test.go +++ b/g/container/garray/garray_z_unit_string_test.go @@ -14,12 +14,15 @@ import ( "github.com/gogf/gf/g/util/gconv" "strings" "testing" + "time" ) func Test_StringArray_Basic(t *testing.T) { gtest.Case(t, func() { expect := []string{"0", "1", "2", "3"} array := garray.NewStringArrayFrom(expect) + array2 := garray.NewStringArrayFrom(expect, true) + array3 := garray.NewStringArrayFrom([]string{}) gtest.Assert(array.Slice(), expect) array.Set(0, "100") gtest.Assert(array.Get(0), 100) @@ -37,6 +40,8 @@ func Test_StringArray_Basic(t *testing.T) { array.InsertAfter(6, "400") gtest.Assert(array.Slice(), []string{"100", "200", "1", "2", "3", "300", "4", "400"}) gtest.Assert(array.Clear().Len(), 0) + gtest.Assert(array2.Slice(), expect) + gtest.Assert(array3.Search("100"), -1) }) } @@ -99,20 +104,48 @@ func TestString_Range(t *testing.T) { gtest.Case(t, func() { value1 := []string{"0", "1", "2", "3", "4", "5", "6"} array1 := garray.NewStringArrayFrom(value1) + array2 := garray.NewStringArrayFrom(value1, true) gtest.Assert(array1.Range(0, 1), []interface{}{"0"}) gtest.Assert(array1.Range(1, 2), []interface{}{"1"}) gtest.Assert(array1.Range(0, 2), []interface{}{"0", "1"}) gtest.Assert(array1.Range(-1, 10), value1) + gtest.Assert(array1.Range(10, 1), nil) + gtest.Assert(array2.Range(0, 1), []interface{}{"0"}) }) } func TestStringArray_Merge(t *testing.T) { gtest.Case(t, func() { - a1 := []string{"0", "1", "2", "3"} - a2 := []string{"4", "5", "6", "7"} - array1 := garray.NewStringArrayFrom(a1) - array2 := garray.NewStringArrayFrom(a2) + a11 := []string{"0", "1", "2", "3"} + a21 := []string{"4", "5", "6", "7"} + array1 := garray.NewStringArrayFrom(a11) + array2 := garray.NewStringArrayFrom(a21) gtest.Assert(array1.Merge(array2).Slice(), []string{"0", "1", "2", "3", "4", "5", "6", "7"}) + + func1 := func(v1, v2 interface{}) int { + if gconv.Int(v1) < gconv.Int(v2) { + return 0 + } + return 1 + } + + s1 := []string{"a", "b", "c", "d"} + s2 := []string{"e", "f"} + i1 := garray.NewIntArrayFrom([]int{1, 2, 3}) + i2 := garray.NewArrayFrom([]interface{}{3}) + s3 := garray.NewStringArrayFrom([]string{"g", "h"}) + s4 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1) + s5 := garray.NewSortedStringArrayFrom(s2) + s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3}) + a1 := garray.NewStringArrayFrom(s1) + + gtest.Assert(a1.Merge(s2).Len(), 6) + gtest.Assert(a1.Merge(i1).Len(), 9) + gtest.Assert(a1.Merge(i2).Len(), 10) + gtest.Assert(a1.Merge(s3).Len(), 12) + gtest.Assert(a1.Merge(s4).Len(), 14) + gtest.Assert(a1.Merge(s5).Len(), 16) + gtest.Assert(a1.Merge(s6).Len(), 19) }) } @@ -139,7 +172,6 @@ func TestStringArray_Chunk(t *testing.T) { gtest.Assert(chunks[1], []string{"3", "4"}) gtest.Assert(chunks[2], []string{"5"}) gtest.Assert(len(array1.Chunk(0)), 0) - }) } @@ -157,9 +189,15 @@ func TestStringArray_SubSlice(t *testing.T) { gtest.Case(t, func() { a1 := []string{"0", "1", "2", "3", "4", "5", "6"} array1 := garray.NewStringArrayFrom(a1) + array2 := garray.NewStringArrayFrom(a1, true) gtest.Assert(array1.SubSlice(0, 2), []string{"0", "1"}) gtest.Assert(array1.SubSlice(2, 2), []string{"2", "3"}) gtest.Assert(array1.SubSlice(5, 8), []string{"5", "6"}) + gtest.Assert(array1.SubSlice(8, 2), nil) + gtest.Assert(array1.SubSlice(1, -2), nil) + gtest.Assert(array1.SubSlice(-5, 2), []string{"2", "3"}) + gtest.Assert(array1.SubSlice(-10, 1), nil) + gtest.Assert(array2.SubSlice(0, 2), []string{"0", "1"}) }) } @@ -172,7 +210,6 @@ func TestStringArray_Rand(t *testing.T) { gtest.AssertIN(array1.Rands(1)[0], a1) gtest.Assert(len(array1.Rand()), 1) gtest.AssertIN(array1.Rand(), a1) - }) } @@ -181,10 +218,9 @@ func TestStringArray_PopRands(t *testing.T) { a1 := []string{"a", "b", "c", "d", "e", "f", "g"} a2 := []string{"1", "2", "3", "4", "5", "6", "7"} array1 := garray.NewStringArrayFrom(a1) - //todo gtest.AssertIN(array1.PopRands(1),a1) gtest.AssertIN(array1.PopRands(1), strings.Join(a1, ",")) gtest.AssertNI(array1.PopRands(1), strings.Join(a2, ",")) - + gtest.Assert(len(array1.PopRands(10)), 5) }) } @@ -275,26 +311,6 @@ func TestStringArray_Sum(t *testing.T) { }) } -//func TestStringArray_SortFunc(t *testing.T) { -// gtest.Case(t, func() { -// a1 := []string{"0","1","2","3","4","5","6"} -// //a2 := []string{"0","a","3","4","5","6"} -// array1 := garray.NewStringArrayFrom(a1) -// -// lesss:=func(v1,v2 string)bool{ -// if v1>v2{ -// return true -// } -// return false -// } -// gtest.Assert(array1.Len(),7) -// gtest.Assert(lesss("1","2"),false) -// gtest.Assert(array1.SortFunc(lesss("1","2")) ,false) -// -// -// }) -//} - func TestStringArray_PopRand(t *testing.T) { gtest.Case(t, func() { a1 := []string{"0", "1", "2", "3", "4", "5", "6"} @@ -324,7 +340,6 @@ func TestStringArray_CountValues(t *testing.T) { gtest.Assert(len(m1), 6) gtest.Assert(m1["2"], 1) gtest.Assert(m1["4"], 2) - }) } @@ -357,7 +372,6 @@ func TestSortedStringArray_SetArray(t *testing.T) { gtest.Assert(array1.Contains("d"), false) gtest.Assert(array1.Contains("b"), false) gtest.Assert(array1.Contains("g"), true) - }) } @@ -367,7 +381,7 @@ func TestSortedStringArray_Sort(t *testing.T) { array1 := garray.NewSortedStringArrayFrom(a1) gtest.Assert(array1, []string{"a", "b", "c", "d"}) - array1.Sort() //todo 这个SortedStringArray.sort这个方法没有必要, + array1.Sort() gtest.Assert(array1.Len(), 4) gtest.Assert(array1.Contains("c"), true) gtest.Assert(array1, []string{"a", "b", "c", "d"}) @@ -380,7 +394,6 @@ func TestSortedStringArray_Get(t *testing.T) { array1 := garray.NewSortedStringArrayFrom(a1) gtest.Assert(array1.Get(2), "c") gtest.Assert(array1.Get(0), "a") - }) } @@ -400,7 +413,6 @@ func TestSortedStringArray_Remove(t *testing.T) { // 此时array1里的元素只剩下2个 gtest.Assert(array1.Remove(1), "d") gtest.Assert(array1.Len(), 1) - }) } @@ -486,6 +498,7 @@ func TestSortedStringArray_Range(t *testing.T) { gtest.Case(t, func() { a1 := []string{"e", "a", "d", "c", "b", "f", "g"} array1 := garray.NewSortedStringArrayFrom(a1) + array2 := garray.NewSortedStringArrayFrom(a1, true) s1 := array1.Range(2, 4) gtest.Assert(len(s1), 2) gtest.Assert(s1, []string{"c", "d"}) @@ -497,6 +510,11 @@ func TestSortedStringArray_Range(t *testing.T) { s1 = array1.Range(4, 8) gtest.Assert(len(s1), 3) gtest.Assert(s1, []string{"e", "f", "g"}) + gtest.Assert(array1.Range(10, 2), nil) + + s2 := array2.Range(2, 4) + gtest.Assert(s2, []string{"c", "d"}) + }) } @@ -528,7 +546,6 @@ func TestSortedStringArray_Clear(t *testing.T) { array1 := garray.NewSortedStringArrayFrom(a1) array1.Clear() gtest.Assert(array1.Len(), 0) - }) } @@ -536,6 +553,7 @@ func TestSortedStringArray_SubSlice(t *testing.T) { gtest.Case(t, func() { a1 := []string{"e", "a", "d", "c", "b", "f", "g"} array1 := garray.NewSortedStringArrayFrom(a1) + array2 := garray.NewSortedStringArrayFrom(a1, true) s1 := array1.SubSlice(1, 3) gtest.Assert(len(s1), 3) gtest.Assert(s1, []string{"b", "c", "d"}) @@ -547,6 +565,16 @@ func TestSortedStringArray_SubSlice(t *testing.T) { s3 := array1.SubSlice(10, 2) gtest.Assert(len(s3), 0) + s3 = array1.SubSlice(-5, 2) + gtest.Assert(s3, []string{"c", "d"}) + + s3 = array1.SubSlice(-10, 2) + gtest.Assert(s3, nil) + + s3 = array1.SubSlice(1, -2) + gtest.Assert(s3, nil) + + gtest.Assert(array2.SubSlice(1, 3), []string{"b", "c", "d"}) }) } @@ -564,7 +592,6 @@ func TestSortedStringArray_Rand(t *testing.T) { a1 := []string{"e", "a", "d"} array1 := garray.NewSortedStringArrayFrom(a1) gtest.AssertIN(array1.Rand(), []string{"e", "a", "d"}) - }) } @@ -611,6 +638,7 @@ func TestSortedStringArray_Chunk(t *testing.T) { gtest.Assert(len(array2), 3) gtest.Assert(len(array2[0]), 2) gtest.Assert(array2[1], []string{"c", "d"}) + gtest.Assert(array1.Chunk(0), nil) }) } @@ -634,6 +662,174 @@ func TestStringArray_Remove(t *testing.T) { s1 = array1.Remove(3) gtest.Assert(s1, "c") gtest.Assert(array1.Len(), 3) - + }) +} + +func TestStringArray_RLockFunc(t *testing.T) { + gtest.Case(t, func() { + s1 := []string{"a", "b", "c", "d"} + a1 := garray.NewStringArrayFrom(s1, true) + + ch1 := make(chan int64, 3) + ch2 := make(chan int64, 1) + //go1 + go a1.RLockFunc(func(n1 []string) { //读锁 + time.Sleep(2 * time.Second) //暂停1秒 + n1[2] = "g" + ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }) + + //go2 + go func() { + time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行. + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + a1.Len() + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }() + + t1 := <-ch1 + t2 := <-ch1 + <-ch2 //等待go1完成 + + // 防止ci抖动,以豪秒为单位 + gtest.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候,并没有阻塞。 + gtest.Assert(a1.Contains("g"), true) + }) +} + +func TestSortedStringArray_LockFunc(t *testing.T) { + gtest.Case(t, func() { + s1 := []string{"a", "b", "c", "d"} + a1 := garray.NewSortedStringArrayFrom(s1) + + ch1 := make(chan int64, 3) + ch2 := make(chan int64, 3) + //go1 + go a1.LockFunc(func(n1 []string) { //读写锁 + time.Sleep(2 * time.Second) //暂停2秒 + n1[2] = "g" + ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }) + + //go2 + go func() { + time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行. + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + a1.Len() + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }() + + t1 := <-ch1 + t2 := <-ch1 + <-ch2 //等待go1完成 + + // 防止ci抖动,以豪秒为单位 + gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。 + gtest.Assert(a1.Contains("g"), true) + }) +} + +func TestSortedStringArray_RLockFunc(t *testing.T) { + gtest.Case(t, func() { + s1 := []string{"a", "b", "c", "d"} + a1 := garray.NewSortedStringArrayFrom(s1) + + ch1 := make(chan int64, 3) + ch2 := make(chan int64, 1) + //go1 + go a1.RLockFunc(func(n1 []string) { //读锁 + time.Sleep(2 * time.Second) //暂停1秒 + n1[2] = "g" + ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }) + + //go2 + go func() { + time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行. + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + a1.Len() + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }() + + t1 := <-ch1 + t2 := <-ch1 + <-ch2 //等待go1完成 + + // 防止ci抖动,以豪秒为单位 + gtest.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候,并没有阻塞。 + gtest.Assert(a1.Contains("g"), true) + }) +} + +func TestSortedStringArray_Merge(t *testing.T) { + gtest.Case(t, func() { + func1 := func(v1, v2 interface{}) int { + if gconv.Int(v1) < gconv.Int(v2) { + return 0 + } + return 1 + } + + s1 := []string{"a", "b", "c", "d"} + s2 := []string{"e", "f"} + i1 := garray.NewIntArrayFrom([]int{1, 2, 3}) + i2 := garray.NewArrayFrom([]interface{}{3}) + s3 := garray.NewStringArrayFrom([]string{"g", "h"}) + s4 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1) + s5 := garray.NewSortedStringArrayFrom(s2) + s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3}) + a1 := garray.NewSortedStringArrayFrom(s1) + + gtest.Assert(a1.Merge(s2).Len(), 6) + gtest.Assert(a1.Merge(i1).Len(), 9) + gtest.Assert(a1.Merge(i2).Len(), 10) + gtest.Assert(a1.Merge(s3).Len(), 12) + gtest.Assert(a1.Merge(s4).Len(), 14) + gtest.Assert(a1.Merge(s5).Len(), 16) + gtest.Assert(a1.Merge(s6).Len(), 19) + }) +} + +func TestStringArray_SortFunc(t *testing.T) { + gtest.Case(t, func() { + s1 := []string{"a", "d", "c", "b"} + a1 := garray.NewStringArrayFrom(s1) + func1 := func(v1, v2 string) bool { + return v1 < v2 + } + a11 := a1.SortFunc(func1) + gtest.Assert(a11, []string{"a", "b", "c", "d"}) + }) +} + +func TestStringArray_LockFunc(t *testing.T) { + gtest.Case(t, func() { + s1 := []string{"a", "b", "c", "d"} + a1 := garray.NewStringArrayFrom(s1) + + ch1 := make(chan int64, 3) + ch2 := make(chan int64, 3) + //go1 + go a1.LockFunc(func(n1 []string) { //读写锁 + time.Sleep(2 * time.Second) //暂停2秒 + n1[2] = "g" + ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }) + + //go2 + go func() { + time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行. + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + a1.Len() + ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000) + }() + + t1 := <-ch1 + t2 := <-ch1 + <-ch2 //等待go1完成 + + // 防止ci抖动,以豪秒为单位 + gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。 + gtest.Assert(a1.Contains("g"), true) }) } diff --git a/g/container/gpool/gpool_z_unit_test.go b/g/container/gpool/gpool_z_unit_test.go index 32577a25c..19bea8843 100644 --- a/g/container/gpool/gpool_z_unit_test.go +++ b/g/container/gpool/gpool_z_unit_test.go @@ -1,7 +1,14 @@ +// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + package gpool_test import ( "errors" + "github.com/gogf/gf/g" "testing" "time" @@ -30,7 +37,7 @@ func Test_Gpool(t *testing.T) { //test won't be timeout v1, err1 := p1.Get() gtest.Assert(err1, nil) - gtest.Assert(v1, 1) + gtest.AssertIN(v1, g.Slice{1, 2}) //test clear p1.Clear() gtest.Assert(p1.Size(), 0) @@ -43,13 +50,12 @@ func Test_Gpool(t *testing.T) { p1.Put(4) v1, err1 = p1.Get() gtest.Assert(err1, nil) - gtest.Assert(v1, 3) + gtest.AssertIN(v1, g.Slice{3, 4}) //test close p1.Close() v1, err1 = p1.Get() gtest.Assert(err1, nil) gtest.Assert(v1, "hello") - }) gtest.Case(t, func() { diff --git a/g/container/gvar/gvar.go b/g/container/gvar/gvar.go index 05b3098e8..bbf3543d9 100644 --- a/g/container/gvar/gvar.go +++ b/g/container/gvar/gvar.go @@ -58,33 +58,6 @@ func (v *Var) Interface() interface{} { return v.Val() } -// Time converts and returns as time.Time. -// The parameter specifies the format of the time string using gtime, -// eg: Y-m-d H:i:s. -func (v *Var) Time(format ...string) time.Time { - return gconv.Time(v.Val(), format...) -} - -// Duration converts and returns as time.Duration. -// If value of is string, then it uses time.ParseDuration for conversion. -func (v *Var) Duration() time.Duration { - return gconv.Duration(v.Val()) -} - -// GTime converts and returns as *gtime.Time. -// The parameter specifies the format of the time string using gtime, -// eg: Y-m-d H:i:s. -func (v *Var) GTime(format ...string) *gtime.Time { - return gconv.GTime(v.Val(), format...) -} - -// Struct maps value of to . -// The parameter should be a pointer to a struct instance. -// The parameter is used to specify the key-to-attribute mapping rules. -func (v *Var) Struct(pointer interface{}, mapping ...map[string]string) error { - return gconv.Struct(v.Val(), pointer, mapping...) -} - // IsNil checks whether is nil. func (v *Var) IsNil() bool { return v.Val() == nil @@ -184,3 +157,57 @@ func (v *Var) Strings() []string { func (v *Var) Interfaces() []interface{} { return gconv.Interfaces(v.Val()) } + +// Time converts and returns as time.Time. +// The parameter specifies the format of the time string using gtime, +// eg: Y-m-d H:i:s. +func (v *Var) Time(format ...string) time.Time { + return gconv.Time(v.Val(), format...) +} + +// Duration converts and returns as time.Duration. +// If value of is string, then it uses time.ParseDuration for conversion. +func (v *Var) Duration() time.Duration { + return gconv.Duration(v.Val()) +} + +// GTime converts and returns as *gtime.Time. +// The parameter specifies the format of the time string using gtime, +// eg: Y-m-d H:i:s. +func (v *Var) GTime(format ...string) *gtime.Time { + return gconv.GTime(v.Val(), format...) +} + +// Map converts to map[string]interface{}. +func (v *Var) Map(tags ...string) map[string]interface{} { + return gconv.Map(v.Val(), tags...) +} + +// MapDeep converts to map[string]interface{} recursively. +func (v *Var) MapDeep(tags ...string) map[string]interface{} { + return gconv.MapDeep(v.Val(), tags...) +} + +// Struct maps value of to . +// The parameter should be a pointer to a struct instance. +// The parameter is used to specify the key-to-attribute mapping rules. +func (v *Var) Struct(pointer interface{}, mapping ...map[string]string) error { + return gconv.Struct(v.Val(), pointer, mapping...) +} + +// Struct maps value of to recursively. +// The parameter should be a pointer to a struct instance. +// The parameter is used to specify the key-to-attribute mapping rules. +func (v *Var) StructDeep(pointer interface{}, mapping ...map[string]string) error { + return gconv.StructDeep(v.Val(), pointer, mapping...) +} + +// Structs converts to given struct slice. +func (v *Var) Structs(pointer interface{}, mapping ...map[string]string) (err error) { + return gconv.Structs(v.Val(), pointer, mapping...) +} + +// StructsDeep converts to given struct slice recursively. +func (v *Var) StructsDeep(pointer interface{}, mapping ...map[string]string) (err error) { + return gconv.StructsDeep(v.Val(), pointer, mapping...) +} diff --git a/g/database/gdb/gdb_model.go b/g/database/gdb/gdb_model.go index b9168016f..ef61c8041 100644 --- a/g/database/gdb/gdb_model.go +++ b/g/database/gdb/gdb_model.go @@ -511,21 +511,18 @@ func (md *Model) Structs(objPointerSlice interface{}) error { // 链式操作,将结果转换为指定的struct/*struct/[]struct/[]*struct, // 参数应该为指针类型,否则返回失败。 // 该方法自动识别参数类型,调用Struct/Structs方法。 -func (md *Model) Scan(objPointer interface{}) error { - t := reflect.TypeOf(objPointer) +func (md *Model) Scan(pointer interface{}) error { + t := reflect.TypeOf(pointer) k := t.Kind() if k != reflect.Ptr { return fmt.Errorf("params should be type of pointer, but got: %v", k) } - k = t.Elem().Kind() - switch k { + switch t.Elem().Kind() { case reflect.Array: case reflect.Slice: - return md.Structs(objPointer) - case reflect.Struct: - return md.Struct(objPointer) + return md.Structs(pointer) default: - return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k) + return md.Struct(pointer) } return nil } diff --git a/g/database/gdb/gdb_type_result.go b/g/database/gdb/gdb_type_result.go index 7a67d981e..91854d043 100644 --- a/g/database/gdb/gdb_type_result.go +++ b/g/database/gdb/gdb_type_result.go @@ -102,14 +102,14 @@ func (r Result) ToUintRecord(key string) map[uint]Record { } // 将结果列表转换为指定对象的slice。 -func (r Result) ToStructs(objPointerSlice interface{}) (err error) { +func (r Result) ToStructs(pointer interface{}) (err error) { l := len(r) if l == 0 { return sql.ErrNoRows } - t := reflect.TypeOf(objPointerSlice) + t := reflect.TypeOf(pointer) if t.Kind() != reflect.Ptr { - return fmt.Errorf("params should be type of pointer, but got: %v", t.Kind()) + return fmt.Errorf("pointer should be type of pointer, but got: %v", t.Kind()) } array := reflect.MakeSlice(t.Elem(), l, l) itemType := array.Index(0).Type() @@ -128,6 +128,6 @@ func (r Result) ToStructs(objPointerSlice interface{}) (err error) { array.Index(i).Set(e) } } - reflect.ValueOf(objPointerSlice).Elem().Set(array) + reflect.ValueOf(pointer).Elem().Set(array) return nil } diff --git a/g/database/gdb/gdb_unit_model_test.go b/g/database/gdb/gdb_unit_model_test.go index 7c09bccf7..7b47901da 100644 --- a/g/database/gdb/gdb_unit_model_test.go +++ b/g/database/gdb/gdb_unit_model_test.go @@ -358,6 +358,40 @@ func TestModel_Struct(t *testing.T) { gtest.Assert(user.NickName, "T111") gtest.Assert(user.CreateTime.String(), "2018-10-10 00:01:10") }) + // Auto creating struct object. + gtest.Case(t, func() { + type User struct { + Id int + Passport string + Password string + NickName string + CreateTime *gtime.Time + } + user := (*User)(nil) + err := db.Table("user").Where("id=1").Struct(&user) + if err != nil { + gtest.Fatal(err) + } + gtest.Assert(user.NickName, "T111") + gtest.Assert(user.CreateTime.String(), "2018-10-10 00:01:10") + }) + // Just using Scan. + gtest.Case(t, func() { + type User struct { + Id int + Passport string + Password string + NickName string + CreateTime *gtime.Time + } + user := (*User)(nil) + err := db.Table("user").Where("id=1").Scan(&user) + if err != nil { + gtest.Fatal(err) + } + gtest.Assert(user.NickName, "T111") + gtest.Assert(user.CreateTime.String(), "2018-10-10 00:01:10") + }) gtest.Case(t, func() { type User struct { @@ -396,6 +430,7 @@ func TestModel_Structs(t *testing.T) { gtest.Assert(users[2].NickName, "T3") gtest.Assert(users[0].CreateTime.String(), "2018-10-10 00:01:10") }) + // Auto create struct slice. gtest.Case(t, func() { type User struct { Id int @@ -418,7 +453,29 @@ func TestModel_Structs(t *testing.T) { gtest.Assert(users[2].NickName, "T3") gtest.Assert(users[0].CreateTime.String(), "2018-10-10 00:01:10") }) - + // Just using Scan. + gtest.Case(t, func() { + type User struct { + Id int + Passport string + Password string + NickName string + CreateTime *gtime.Time + } + var users []*User + err := db.Table("user").OrderBy("id asc").Scan(&users) + if err != nil { + gtest.Fatal(err) + } + gtest.Assert(len(users), 3) + gtest.Assert(users[0].Id, 1) + gtest.Assert(users[1].Id, 2) + gtest.Assert(users[2].Id, 3) + gtest.Assert(users[0].NickName, "T111") + gtest.Assert(users[1].NickName, "T2") + gtest.Assert(users[2].NickName, "T3") + gtest.Assert(users[0].CreateTime.String(), "2018-10-10 00:01:10") + }) gtest.Case(t, func() { type User struct { Id int @@ -595,6 +652,21 @@ func TestModel_Where(t *testing.T) { gtest.Assert(err, nil) gtest.Assert(result["id"].Int(), 3) }) + // slice + gtest.Case(t, func() { + result, err := db.Table("user").Where("id=? AND nickname=?", g.Slice{3, "T3"}...).One() + if err != nil { + gtest.Fatal(err) + } + gtest.Assert(result["id"].Int(), 3) + }) + gtest.Case(t, func() { + result, err := db.Table("user").Where("passport like ? and nickname like ?", g.Slice{"t3", "T3"}...).One() + if err != nil { + gtest.Fatal(err) + } + gtest.Assert(result["id"].Int(), 3) + }) // map gtest.Case(t, func() { result, err := db.Table("user").Where(g.Map{"id": 3, "nickname": "T3"}).One() diff --git a/g/encoding/gbase64/gbase64.go b/g/encoding/gbase64/gbase64.go index e19733002..0a01680ff 100644 --- a/g/encoding/gbase64/gbase64.go +++ b/g/encoding/gbase64/gbase64.go @@ -22,10 +22,7 @@ func Encode(src []byte) []byte { func Decode(dst []byte) ([]byte, error) { src := make([]byte, base64.StdEncoding.DecodedLen(len(dst))) n, err := base64.StdEncoding.Decode(src, dst) - if err != nil { - return nil, err - } - return src[:n], nil + return src[:n], err } // EncodeString encodes bytes with BASE64 algorithm. diff --git a/g/encoding/gjson/gjson_api.go b/g/encoding/gjson/gjson_api.go index b83316d30..a03a50f60 100644 --- a/g/encoding/gjson/gjson_api.go +++ b/g/encoding/gjson/gjson_api.go @@ -266,11 +266,32 @@ func (j *Json) GetToVar(pattern string, pointer interface{}) error { return nil } -// GetToStruct gets the value by specified , -// and converts it to specified object . -// The should be the pointer to an object. -func (j *Json) GetToStruct(pattern string, pointer interface{}) error { - return gconv.Struct(j.Get(pattern), pointer) +// GetStruct gets the value by specified , +// and converts it to specified object . +// The should be the pointer to an object. +func (j *Json) GetStruct(pattern string, pointer interface{}, mapping ...map[string]string) error { + return gconv.Struct(j.Get(pattern), pointer, mapping...) +} + +// GetStructDeep does GetStruct recursively. +func (j *Json) GetStructDeep(pattern string, pointer interface{}, mapping ...map[string]string) error { + return gconv.StructDeep(j.Get(pattern), pointer, mapping...) +} + +// GetStructs converts any slice to given struct slice. +func (j *Json) GetStructs(pattern string, pointer interface{}, mapping ...map[string]string) error { + return gconv.Structs(j.Get(pattern), pointer, mapping...) +} + +// GetStructsDeep converts any slice to given struct slice recursively. +func (j *Json) GetStructsDeep(pattern string, pointer interface{}, mapping ...map[string]string) error { + return gconv.StructsDeep(j.Get(pattern), pointer, mapping...) +} + +// GetToStruct is alias of GetStruct. +// Deprecated. +func (j *Json) GetToStruct(pattern string, pointer interface{}, mapping ...map[string]string) error { + return j.GetStruct(pattern, pointer, mapping...) } // ToMap converts current Json object to map[string]interface{}. @@ -290,7 +311,7 @@ func (j *Json) ToArray() []interface{} { } // ToStruct converts current Json object to specified object. -// The should be a pointer type. +// The should be a pointer type. func (j *Json) ToStruct(pointer interface{}) error { j.mu.RLock() defer j.mu.RUnlock() diff --git a/g/encoding/gparser/gparser_api.go b/g/encoding/gparser/gparser_api.go index 659b2c7dc..2b5261a09 100644 --- a/g/encoding/gparser/gparser_api.go +++ b/g/encoding/gparser/gparser_api.go @@ -145,11 +145,32 @@ func (p *Parser) GetToVar(pattern string, pointer interface{}) error { return p.json.GetToVar(pattern, pointer) } -// GetToStruct gets the value by specified , +// GetStruct gets the value by specified , // and converts it to specified object . -// The should be the pointer to a struct. -func (p *Parser) GetToStruct(pattern string, pointer interface{}) error { - return p.json.GetToStruct(pattern, pointer) +// The should be the pointer to an object. +func (p *Parser) GetStruct(pattern string, pointer interface{}, mapping ...map[string]string) error { + return p.json.GetStruct(pattern, pointer, mapping...) +} + +// GetStructDeep does GetStruct recursively. +func (p *Parser) GetStructDeep(pattern string, pointer interface{}, mapping ...map[string]string) error { + return p.json.GetStructDeep(pattern, pointer, mapping...) +} + +// GetStructs converts any slice to given struct slice. +func (p *Parser) GetStructs(pattern string, pointer interface{}, mapping ...map[string]string) error { + return p.json.GetStructs(pattern, pointer, mapping...) +} + +// GetStructsDeep converts any slice to given struct slice recursively. +func (p *Parser) GetStructsDeep(pattern string, pointer interface{}, mapping ...map[string]string) error { + return p.json.GetStructsDeep(pattern, pointer, mapping...) +} + +// GetToStruct is alias of GetStruct. +// Deprecated. +func (p *Parser) GetToStruct(pattern string, pointer interface{}, mapping ...map[string]string) error { + return p.json.GetStruct(pattern, pointer, mapping...) } // Set sets value with specified . diff --git a/g/frame/gins/gins.go b/g/frame/gins/gins.go index f2030224d..3414599cd 100644 --- a/g/frame/gins/gins.go +++ b/g/frame/gins/gins.go @@ -87,75 +87,34 @@ func Database(name ...string) gdb.DB { glog.Error(`database init failed: "database" node not found, is config file or configuration missing?`) return nil } - for group, v := range m { + // Parse as map-slice. + for group, groupConfig := range m { cg := gdb.ConfigGroup{} - if list, ok := v.([]interface{}); ok { - for _, nodeValue := range list { - node := gdb.ConfigNode{} - nodeMap := nodeValue.(map[string]interface{}) - if value, ok := nodeMap["host"]; ok { - node.Host = gconv.String(value) + switch value := groupConfig.(type) { + case []interface{}: + for _, v := range value { + if node := parseDBConfigNode(v); node != nil { + cg = append(cg, *node) } - if value, ok := nodeMap["port"]; ok { - node.Port = gconv.String(value) - } - if value, ok := nodeMap["user"]; ok { - node.User = gconv.String(value) - } - if value, ok := nodeMap["pass"]; ok { - node.Pass = gconv.String(value) - } - if value, ok := nodeMap["name"]; ok { - node.Name = gconv.String(value) - } - if value, ok := nodeMap["type"]; ok { - node.Type = gconv.String(value) - } - if value, ok := nodeMap["role"]; ok { - node.Role = gconv.String(value) - } - if value, ok := nodeMap["charset"]; ok { - node.Charset = gconv.String(value) - } - if value, ok := nodeMap["priority"]; ok { - node.Priority = gconv.Int(value) - } - // Deprecated - if value, ok := nodeMap["linkinfo"]; ok { - node.LinkInfo = gconv.String(value) - } - // Deprecated - if value, ok := nodeMap["link-info"]; ok { - node.LinkInfo = gconv.String(value) - } - if value, ok := nodeMap["linkInfo"]; ok { - node.LinkInfo = gconv.String(value) - } - // Deprecated - if value, ok := nodeMap["max-idle"]; ok { - node.MaxIdleConnCount = gconv.Int(value) - } - if value, ok := nodeMap["maxIdle"]; ok { - node.MaxIdleConnCount = gconv.Int(value) - } - // Deprecated - if value, ok := nodeMap["max-open"]; ok { - node.MaxOpenConnCount = gconv.Int(value) - } - if value, ok := nodeMap["maxOpen"]; ok { - node.MaxOpenConnCount = gconv.Int(value) - } - // Deprecated - if value, ok := nodeMap["max-lifetime"]; ok { - node.MaxConnLifetime = gconv.Int(value) - } - if value, ok := nodeMap["maxLifetime"]; ok { - node.MaxConnLifetime = gconv.Int(value) - } - cg = append(cg, node) + } + case map[string]interface{}: + if node := parseDBConfigNode(value); node != nil { + cg = append(cg, *node) } } - gdb.AddConfigGroup(group, cg) + if len(cg) > 0 { + gdb.AddConfigGroup(group, cg) + } + } + // Parse as a single node configuration. + if node := parseDBConfigNode(m); node != nil { + cg := gdb.ConfigGroup{} + if node.LinkInfo != "" || node.Host != "" { + cg = append(cg, *node) + } + if len(cg) > 0 { + gdb.AddConfigGroup(group, cg) + } } addConfigMonitor(key, config) } @@ -172,6 +131,81 @@ func Database(name ...string) gdb.DB { return nil } +// 解析数据库配置节点项 +func parseDBConfigNode(value interface{}) *gdb.ConfigNode { + nodeMap, ok := value.(map[string]interface{}) + if !ok { + return nil + } + node := &gdb.ConfigNode{} + if value, ok := nodeMap["host"]; ok { + node.Host = gconv.String(value) + } + if value, ok := nodeMap["port"]; ok { + node.Port = gconv.String(value) + } + if value, ok := nodeMap["user"]; ok { + node.User = gconv.String(value) + } + if value, ok := nodeMap["pass"]; ok { + node.Pass = gconv.String(value) + } + if value, ok := nodeMap["name"]; ok { + node.Name = gconv.String(value) + } + if value, ok := nodeMap["type"]; ok { + node.Type = gconv.String(value) + } + if value, ok := nodeMap["role"]; ok { + node.Role = gconv.String(value) + } + if value, ok := nodeMap["charset"]; ok { + node.Charset = gconv.String(value) + } + if value, ok := nodeMap["priority"]; ok { + node.Priority = gconv.Int(value) + } + if value, ok := nodeMap["linkinfo"]; ok { + node.LinkInfo = gconv.String(value) + } + if value, ok := nodeMap["link-info"]; ok { + node.LinkInfo = gconv.String(value) + } + if value, ok := nodeMap["linkInfo"]; ok { + node.LinkInfo = gconv.String(value) + } + if value, ok := nodeMap["link"]; ok { + node.LinkInfo = gconv.String(value) + } + if value, ok := nodeMap["max-idle"]; ok { + node.MaxIdleConnCount = gconv.Int(value) + } + if value, ok := nodeMap["maxIdle"]; ok { + node.MaxIdleConnCount = gconv.Int(value) + } + if value, ok := nodeMap["max-open"]; ok { + node.MaxOpenConnCount = gconv.Int(value) + } + if value, ok := nodeMap["maxOpen"]; ok { + node.MaxOpenConnCount = gconv.Int(value) + } + if value, ok := nodeMap["max-lifetime"]; ok { + node.MaxConnLifetime = gconv.Int(value) + } + if value, ok := nodeMap["maxLifetime"]; ok { + node.MaxConnLifetime = gconv.Int(value) + } + // Parse link syntax. + if node.LinkInfo != "" && node.Type == "" { + match, _ := gregex.MatchString(`([a-z]+):(.+)`, node.LinkInfo) + if len(match) == 3 { + node.Type = match[1] + node.LinkInfo = match[2] + } + } + return node +} + // Redis操作对象,使用了连接池 func Redis(name ...string) *gredis.Redis { config := Config() diff --git a/g/net/ghttp/ghttp_request_post.go b/g/net/ghttp/ghttp_request_post.go index 6a594fd1b..193544256 100644 --- a/g/net/ghttp/ghttp_request_post.go +++ b/g/net/ghttp/ghttp_request_post.go @@ -154,5 +154,5 @@ func (r *Request) GetPostToStruct(pointer interface{}, mapping ...map[string]str for k, v := range r.GetPostMap() { params[k] = v } - return gconv.Struct(params, pointer, tagMap) + return gconv.StructDeep(params, pointer, tagMap) } diff --git a/g/os/gcfg/gcfg.go b/g/os/gcfg/gcfg.go index 1badfa033..adb3fa570 100644 --- a/g/os/gcfg/gcfg.go +++ b/g/os/gcfg/gcfg.go @@ -489,14 +489,41 @@ func (c *Config) GetGTime(pattern string, format ...string) *gtime.Time { return nil } -func (c *Config) GetToStruct(pattern string, pointer interface{}, def ...interface{}) error { +func (c *Config) GetStruct(pattern string, pointer interface{}, mapping ...map[string]string) error { if j := c.getJson(); j != nil { - return j.GetToStruct(pattern, pointer) + return j.GetStruct(pattern, pointer, mapping...) } return errors.New("config file not found") } -// Deprecated. See Clear. +func (c *Config) GetStructDeep(pattern string, pointer interface{}, mapping ...map[string]string) error { + if j := c.getJson(); j != nil { + return j.GetStructDeep(pattern, pointer, mapping...) + } + return errors.New("config file not found") +} + +func (c *Config) GetStructs(pattern string, pointer interface{}, mapping ...map[string]string) error { + if j := c.getJson(); j != nil { + return j.GetStructs(pattern, pointer, mapping...) + } + return errors.New("config file not found") +} + +func (c *Config) GetStructsDeep(pattern string, pointer interface{}, mapping ...map[string]string) error { + if j := c.getJson(); j != nil { + return j.GetStructsDeep(pattern, pointer, mapping...) + } + return errors.New("config file not found") +} + +// Deprecated. +func (c *Config) GetToStruct(pattern string, pointer interface{}) error { + return c.GetStruct(pattern, pointer) +} + +// Reload is alias of Clear. +// Deprecated. func (c *Config) Reload() { c.jsons.Clear() } diff --git a/g/os/gfile/gfile.go b/g/os/gfile/gfile.go index 81db327e2..717ffae7e 100644 --- a/g/os/gfile/gfile.go +++ b/g/os/gfile/gfile.go @@ -11,11 +11,8 @@ import ( "bytes" "errors" "fmt" - "github.com/gogf/gf/g/container/gtype" - "github.com/gogf/gf/g/text/gregex" - "github.com/gogf/gf/g/text/gstr" - "github.com/gogf/gf/g/util/gconv" "io" + "io/ioutil" "os" "os/exec" "os/user" @@ -24,6 +21,11 @@ import ( "sort" "strings" "time" + + "github.com/gogf/gf/g/container/gtype" + "github.com/gogf/gf/g/text/gregex" + "github.com/gogf/gf/g/text/gstr" + "github.com/gogf/gf/g/util/gconv" ) const ( @@ -142,29 +144,109 @@ func Rename(src string, dst string) error { return Move(src, dst) } -// Copy file from to . +// Copy file/directory from to . // -// @TODO directory copy support. +// If is file, it calls CopyFile to implements copy feature, +// or else it calls CopyDir. func Copy(src string, dst string) error { - srcFile, err := Open(src) + if IsFile(src) { + return CopyFile(src, dst) + } + return CopyDir(src, dst) +} + +// CopyFile copies the contents of the file named src to the file named +// by dst. The file will be created if it does not already exist. If the +// destination file exists, all it's contents will be replaced by the contents +// of the source file. The file mode will be copied from the source and +// the copied data is synced/flushed to stable storage. +// Thanks: https://gist.github.com/r0l1/92462b38df26839a3ca324697c8cba04 +func CopyFile(src, dst string) (err error) { + in, err := os.Open(src) + if err != nil { + return + } + defer func() { + if e := in.Close(); e != nil { + err = e + } + }() + out, err := os.Create(dst) + if err != nil { + return + } + defer func() { + if e := out.Close(); e != nil { + err = e + } + }() + _, err = io.Copy(out, in) + if err != nil { + return + } + err = out.Sync() + if err != nil { + return + } + si, err := os.Stat(src) + if err != nil { + return + } + err = os.Chmod(dst, si.Mode()) + if err != nil { + return + } + return +} + +// CopyDir recursively copies a directory tree, attempting to preserve permissions. +// Source directory must exist, destination directory must *not* exist. +// Symlinks are ignored and skipped. +func CopyDir(src string, dst string) (err error) { + src = filepath.Clean(src) + dst = filepath.Clean(dst) + si, err := os.Stat(src) if err != nil { return err } - defer srcFile.Close() - dstFile, err := Create(dst) - if err != nil { - return err + if !si.IsDir() { + return fmt.Errorf("source is not a directory") } - defer dstFile.Close() - _, err = io.Copy(dstFile, srcFile) - if err != nil { - return err + _, err = os.Stat(dst) + if err != nil && !os.IsNotExist(err) { + return } - err = dstFile.Sync() - if err != nil { - return err + if err == nil { + return fmt.Errorf("destination already exists") } - return nil + err = os.MkdirAll(dst, si.Mode()) + if err != nil { + return + } + entries, err := ioutil.ReadDir(src) + if err != nil { + return + } + for _, entry := range entries { + srcPath := filepath.Join(src, entry.Name()) + dstPath := filepath.Join(dst, entry.Name()) + if entry.IsDir() { + err = CopyDir(srcPath, dstPath) + if err != nil { + return + } + } else { + // Skip symlinks. + if entry.Mode()&os.ModeSymlink != 0 { + continue + } + err = CopyFile(srcPath, dstPath) + if err != nil { + return + } + } + } + return } // DirNames returns sub-file names of given directory . diff --git a/g/os/gfile/gfile_z_test.go b/g/os/gfile/gfile_z_test.go index b7d438434..44b8008ed 100644 --- a/g/os/gfile/gfile_z_test.go +++ b/g/os/gfile/gfile_z_test.go @@ -1,12 +1,13 @@ package gfile_test import ( - "github.com/gogf/gf/g/os/gfile" - "github.com/gogf/gf/g/test/gtest" "os" "path/filepath" "strings" "testing" + + "github.com/gogf/gf/g/os/gfile" + "github.com/gogf/gf/g/test/gtest" ) func TestIsDir(t *testing.T) { @@ -677,3 +678,71 @@ func TestMainPkgPath(t *testing.T) { gtest.Assert(reads, "") }) } + +func TestCopyFile(t *testing.T) { + gtest.Case(t, func() { + var ( + paths string = "/testfile_copyfile1.txt" + topath string = "/testfile_copyfile2.txt" + ) + + createTestFile(paths, "") + defer delTestFiles(paths) + + gtest.Assert(gfile.CopyFile(testpath()+paths, testpath()+topath), nil) + defer delTestFiles(topath) + + gtest.Assert(gfile.IsFile(testpath()+topath), true) + gtest.AssertNE(gfile.CopyFile("", ""), nil) + }) +} + +func TestCopyDir(t *testing.T) { + gtest.Case(t, func() { + var ( + dirpath1 string = "/testcopydir1" + dirpath2 string = "/testcopydir2" + ) + + havelist1 := []string{ + "t1.txt", + "t2.txt", + } + + createDir(dirpath1) + for _, v := range havelist1 { + createTestFile(dirpath1+"/"+v, "") + } + defer delTestFiles(dirpath1) + + yfolder := testpath() + dirpath1 + tofolder := testpath() + dirpath2 + + if gfile.IsDir(tofolder) { + gtest.Assert(gfile.Remove(tofolder), nil) + gtest.Assert(gfile.Remove(""), nil) + } + + gtest.Assert(gfile.CopyDir(yfolder, tofolder), nil) + defer delTestFiles(tofolder) + + // 检查复制后的旧文件夹是否真实存在 + gtest.Assert(gfile.IsDir(yfolder), true) + + // 检查复制后的旧文件夹中的文件是否真实存在 + for _, v := range havelist1 { + gtest.Assert(gfile.IsFile(yfolder+"/"+v), true) + } + + // 检查复制后的新文件夹是否真实存在 + gtest.Assert(gfile.IsDir(tofolder), true) + + // 检查复制后的新文件夹中的文件是否真实存在 + for _, v := range havelist1 { + gtest.Assert(gfile.IsFile(tofolder+"/"+v), true) + } + + gtest.Assert(gfile.Remove(tofolder), nil) + gtest.Assert(gfile.Remove(""), nil) + }) +} diff --git a/g/util/gconv/gconv_slice.go b/g/util/gconv/gconv_slice.go index 28ef116e7..2b7178bae 100644 --- a/g/util/gconv/gconv_slice.go +++ b/g/util/gconv/gconv_slice.go @@ -7,10 +7,53 @@ package gconv import ( - "github.com/gogf/gf/g/text/gstr" + "errors" + "fmt" "reflect" + + "github.com/gogf/gf/g/text/gstr" ) +// SliceInt is alias of Ints. +func SliceInt(i interface{}) []int { + return Ints(i) +} + +// SliceStr is alias of Strings. +func SliceStr(i interface{}) []string { + return Strings(i) +} + +// SliceAny is alias of Interfaces. +func SliceAny(i interface{}) []interface{} { + return Interfaces(i) +} + +// SliceFloat is alias of Floats. +func SliceFloat(i interface{}) []float64 { + return Floats(i) +} + +// SliceMap is alias of Maps. +func SliceMap(i interface{}) []map[string]interface{} { + return Maps(i) +} + +// SliceMapDeep is alias of MapsDeep. +func SliceMapDeep(i interface{}) []map[string]interface{} { + return MapsDeep(i) +} + +// SliceStruct is alias of Structs. +func SliceStruct(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { + return Structs(params, pointer, mapping...) +} + +// SliceStructDeep is alias of StructsDeep. +func SliceStructDeep(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { + return StructsDeep(params, pointer, mapping...) +} + // Ints converts to []int. func Ints(i interface{}) []int { if i == nil { @@ -306,9 +349,7 @@ func Interfaces(i interface{}) []interface{} { kind = rv.Kind() } switch kind { - case reflect.Slice: - fallthrough - case reflect.Array: + case reflect.Slice, reflect.Array: for i := 0; i < rv.Len(); i++ { array = append(array, rv.Index(i).Interface()) } @@ -348,3 +389,99 @@ func Maps(i interface{}) []map[string]interface{} { return list } } + +// MapsDeep converts to []map[string]interface{} recursively. +func MapsDeep(i interface{}) []map[string]interface{} { + if i == nil { + return nil + } + if r, ok := i.([]map[string]interface{}); ok { + return r + } else { + array := Interfaces(i) + if len(array) == 0 { + return nil + } + list := make([]map[string]interface{}, len(array)) + for k, v := range array { + list[k] = MapDeep(v) + } + return list + } +} + +// Structs converts any slice to given struct slice. +func Structs(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { + return doStructs(params, pointer, false, mapping...) +} + +// StructsDeep converts any slice to given struct slice recursively. +func StructsDeep(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { + return doStructs(params, pointer, true, mapping...) +} + +// doStructs converts any slice to given struct slice. +// +// The parameter should be type of slice. +// +// The parameter should be type of pointer to slice of struct. +// Note that if is a pointer to another pointer of type of slice of struct, +// it will create the struct/pointer internally. +func doStructs(params interface{}, pointer interface{}, deep bool, mapping ...map[string]string) (err error) { + if params == nil { + return errors.New("params cannot be nil") + } + if pointer == nil { + return errors.New("object pointer cannot be nil") + } + pointerRt := reflect.TypeOf(pointer) + if kind := pointerRt.Kind(); kind != reflect.Ptr { + return fmt.Errorf("pointer should be type of pointer, but got: %v", kind) + } + + rv := reflect.ValueOf(params) + kind := rv.Kind() + if kind == reflect.Ptr { + rv = rv.Elem() + kind = rv.Kind() + } + switch kind { + case reflect.Slice, reflect.Array: + array := reflect.MakeSlice(pointerRt.Elem(), rv.Len(), rv.Len()) + itemType := array.Index(0).Type() + for i := 0; i < rv.Len(); i++ { + if itemType.Kind() == reflect.Ptr { + // Slice element is type pointer. + e := reflect.New(itemType.Elem()).Elem() + if deep { + if err = StructDeep(rv.Index(i).Interface(), e, mapping...); err != nil { + return err + } + } else { + if err = Struct(rv.Index(i).Interface(), e, mapping...); err != nil { + return err + } + } + array.Index(i).Set(e.Addr()) + } else { + // Slice element is not type of pointer. + e := reflect.New(itemType).Elem() + + if deep { + if err = StructDeep(rv.Index(i).Interface(), e, mapping...); err != nil { + return err + } + } else { + if err = Struct(rv.Index(i).Interface(), e, mapping...); err != nil { + return err + } + } + array.Index(i).Set(e) + } + } + reflect.ValueOf(pointer).Elem().Set(array) + return nil + default: + return fmt.Errorf("params should be type of slice, but got: %v", kind) + } +} diff --git a/g/util/gconv/gconv_struct.go b/g/util/gconv/gconv_struct.go index a4df30132..e3a0f5137 100644 --- a/g/util/gconv/gconv_struct.go +++ b/g/util/gconv/gconv_struct.go @@ -52,6 +52,13 @@ func Struct(params interface{}, pointer interface{}, mapping ...map[string]strin return errors.New("object pointer cannot be nil") } elem = rv.Elem() + // Auto create struct object. + // For example, if is **User, then is *User, which is a pointer to User. + if elem.Type().Kind() == reflect.Ptr && (!elem.IsValid() || elem.IsNil()) { + e := reflect.New(elem.Type().Elem()).Elem() + elem.Set(e.Addr()) + elem = e + } } // It only performs one converting to the same attribute. // doneMap is used to check repeated converting. diff --git a/g/util/gconv/gconv_z_unit_slice_test.go b/g/util/gconv/gconv_z_unit_slice_test.go index 017a3d9b6..3b7c54322 100644 --- a/g/util/gconv/gconv_z_unit_slice_test.go +++ b/g/util/gconv/gconv_z_unit_slice_test.go @@ -7,10 +7,11 @@ package gconv_test import ( + "testing" + "github.com/gogf/gf/g" "github.com/gogf/gf/g/test/gtest" "github.com/gogf/gf/g/util/gconv" - "testing" ) func Test_Slice(t *testing.T) { @@ -35,3 +36,50 @@ func Test_Slice_PrivateAttribute(t *testing.T) { gtest.Assert(gconv.Interfaces(user), g.Slice{1}) }) } + +func Test_Slice_Structs(t *testing.T) { + type Base struct { + Age int + } + type User struct { + Id int + Name string + Base + } + + gtest.Case(t, func() { + users := make([]User, 0) + params := []g.Map{ + {"id": 1, "name": "john", "age": 18}, + {"id": 2, "name": "smith", "age": 20}, + } + err := gconv.Structs(params, &users) + gtest.Assert(err, nil) + gtest.Assert(len(users), 2) + gtest.Assert(users[0].Id, params[0]["id"]) + gtest.Assert(users[0].Name, params[0]["name"]) + gtest.Assert(users[0].Age, 0) + + gtest.Assert(users[1].Id, params[1]["id"]) + gtest.Assert(users[1].Name, params[1]["name"]) + gtest.Assert(users[1].Age, 0) + }) + + gtest.Case(t, func() { + users := make([]User, 0) + params := []g.Map{ + {"id": 1, "name": "john", "age": 18}, + {"id": 2, "name": "smith", "age": 20}, + } + err := gconv.StructsDeep(params, &users) + gtest.Assert(err, nil) + gtest.Assert(len(users), 2) + gtest.Assert(users[0].Id, params[0]["id"]) + gtest.Assert(users[0].Name, params[0]["name"]) + gtest.Assert(users[0].Age, params[0]["age"]) + + gtest.Assert(users[1].Id, params[1]["id"]) + gtest.Assert(users[1].Name, params[1]["name"]) + gtest.Assert(users[1].Age, params[1]["age"]) + }) +} diff --git a/g/util/gconv/gconv_z_unit_struct_test.go b/g/util/gconv/gconv_z_unit_struct_test.go index 0078302c3..a424eab2e 100644 --- a/g/util/gconv/gconv_z_unit_struct_test.go +++ b/g/util/gconv/gconv_z_unit_struct_test.go @@ -26,9 +26,8 @@ func Test_Struct_Basic1(t *testing.T) { Pass1 string `gconv:"password1"` Pass2 string `gconv:"password2"` } - user := (*User)(nil) // 使用默认映射规则绑定属性值到对象 - user = new(User) + user := new(User) params1 := g.Map{ "uid": 1, "Name": "john", @@ -339,6 +338,29 @@ func Test_Struct_PrivateAttribute(t *testing.T) { } func Test_Struct_Deep(t *testing.T) { + + gtest.Case(t, func() { + type Base struct { + Age int + } + type User struct { + Id int + Name string + Base + } + user := new(User) + params := g.Map{ + "id": 1, + "name": "john", + "age": 18, + } + err := gconv.StructDeep(params, user) + gtest.Assert(err, nil) + gtest.Assert(user.Id, params["id"]) + gtest.Assert(user.Name, params["name"]) + gtest.Assert(user.Age, params["age"]) + }) + gtest.Case(t, func() { type Ids struct { Id int `json:"id"` @@ -363,7 +385,8 @@ func Test_Struct_Deep(t *testing.T) { "create_time": "2019", } user := new(User) - gconv.StructDeep(data, user) + err := gconv.StructDeep(data, user) + gtest.Assert(err, nil) gtest.Assert(user.Id, 100) gtest.Assert(user.Uid, 101) gtest.Assert(user.Nickname, "T1") @@ -432,3 +455,38 @@ func Test_Struct_Time(t *testing.T) { gtest.Assert(user.CreateTime.Time.UTC().String(), now.UTC().String()) }) } + +// Auto create struct when given pointer. +func Test_Struct_Create(t *testing.T) { + gtest.Case(t, func() { + type User struct { + Uid int + Name string + } + user := (*User)(nil) + params := g.Map{ + "uid": 1, + "Name": "john", + } + err := gconv.Struct(params, &user) + gtest.Assert(err, nil) + gtest.Assert(user.Uid, 1) + gtest.Assert(user.Name, "john") + }) + + gtest.Case(t, func() { + type User struct { + Uid int + Name string + } + user := (*User)(nil) + params := g.Map{ + "uid": 1, + "Name": "john", + } + err := gconv.Struct(params, user) + gtest.AssertNE(err, nil) + gtest.Assert(user, nil) + }) + +} diff --git a/geg/database/gdb/mysql/config2.toml b/geg/database/gdb/mysql/config2.toml new file mode 100644 index 000000000..8722ede8c --- /dev/null +++ b/geg/database/gdb/mysql/config2.toml @@ -0,0 +1,4 @@ + +# MySQL数据库配置 +[database] + link = "mysql:root:8692651@tcp(192.168.1.11:3306)/test" diff --git a/geg/database/gdb/mysql/config3.toml b/geg/database/gdb/mysql/config3.toml new file mode 100644 index 000000000..ce3512864 --- /dev/null +++ b/geg/database/gdb/mysql/config3.toml @@ -0,0 +1,7 @@ + +# MySQL数据库配置 +[database] + [database.default] + link = "mysql:root:8692651@tcp(192.168.1.11:3306)/test" + [database.user] + link = "mysql:root:8692651@tcp(192.168.1.11:3306)/test" \ No newline at end of file diff --git a/geg/database/gdb/mysql/gdb_config.go b/geg/database/gdb/mysql/gdb_config.go index b27c0a318..5bab7abee 100644 --- a/geg/database/gdb/mysql/gdb_config.go +++ b/geg/database/gdb/mysql/gdb_config.go @@ -6,7 +6,6 @@ import ( ) func main() { - g.Config().AddPath("/home/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/geg/frame") if r, err := g.DB().Table("user").Where("uid=?", 1).One(); err == nil { fmt.Println(r["uid"].Int()) fmt.Println(r["name"].String()) diff --git a/geg/database/gdb/mysql/gdb_config2.go b/geg/database/gdb/mysql/gdb_config2.go new file mode 100644 index 000000000..330d78108 --- /dev/null +++ b/geg/database/gdb/mysql/gdb_config2.go @@ -0,0 +1,16 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/g" +) + +func main() { + g.Config().SetFileName("config2.toml") + if r, err := g.DB().Table("user").Where("uid=?", 1).One(); err == nil { + fmt.Println(r["uid"].Int()) + fmt.Println(r["name"].String()) + } else { + fmt.Println(err) + } +} diff --git a/geg/database/gdb/mysql/gdb_config3.go b/geg/database/gdb/mysql/gdb_config3.go new file mode 100644 index 000000000..37608057d --- /dev/null +++ b/geg/database/gdb/mysql/gdb_config3.go @@ -0,0 +1,23 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/g" +) + +func main() { + g.Config().SetFileName("config3.toml") + if r, err := g.DB().Table("user").Where("uid=?", 1).One(); err == nil { + fmt.Println(r["uid"].Int()) + fmt.Println(r["name"].String()) + } else { + fmt.Println(err) + } + + if r, err := g.DB("user").Table("user").Where("uid=?", 1).One(); err == nil { + fmt.Println(r["uid"].Int()) + fmt.Println(r["name"].String()) + } else { + fmt.Println(err) + } +} diff --git a/geg/database/gdb/mysql/gdb_struct.go b/geg/database/gdb/mysql/gdb_struct.go new file mode 100644 index 000000000..1a3cb26de --- /dev/null +++ b/geg/database/gdb/mysql/gdb_struct.go @@ -0,0 +1,23 @@ +package main + +import ( + "fmt" + + "github.com/gogf/gf/g" +) + +func main() { + db := g.DB() + // 开启调试模式,以便于记录所有执行的SQL + db.SetDebug(true) + + type User struct { + Uid int + Name string + } + user := (*User)(nil) + fmt.Println(user) + err := db.Table("test").Where("id=1").Struct(&user) + fmt.Println(err) + fmt.Println(user) +} diff --git a/geg/database/gdb/mysql/gdb_value.go b/geg/database/gdb/mysql/gdb_value.go index e53c42376..5174fcef5 100644 --- a/geg/database/gdb/mysql/gdb_value.go +++ b/geg/database/gdb/mysql/gdb_value.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "github.com/gogf/gf/g" ) diff --git a/geg/os/gfsnotify/gfsnotify.go b/geg/os/gfsnotify/gfsnotify.go index 1b8da5698..f93992764 100644 --- a/geg/os/gfsnotify/gfsnotify.go +++ b/geg/os/gfsnotify/gfsnotify.go @@ -7,7 +7,7 @@ import ( func main() { //path := `D:\temp` - path := "/home/john/temp" + path := "/Users/john/Temp" _, err := gfsnotify.Add(path, func(event *gfsnotify.Event) { glog.Println(event) }) diff --git a/geg/other/test.go b/geg/other/test.go index c2b8288c3..381a581b9 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,40 +1,19 @@ package main import ( + "encoding/base64" "fmt" - - "github.com/gogf/gf/g" - - "github.com/gogf/gf/g/util/gconv" - - "github.com/gogf/gf/g/encoding/gparser" + "github.com/gogf/gf/g/encoding/gbase64" ) func main() { - type User struct { - Uid int - Name string - SiteUrl string `gconv:"-"` - NickName string `gconv:"nickname, omitempty"` - Pass1 string `gconv:"password1"` - Pass2 string `gconv:"password2"` - } - - g.Dump(gconv.Map(User{ - Uid: 100, - Name: "john", - SiteUrl: "https://goframe.org", - Pass1: "123", - Pass2: "456", - })) - - s, err := gparser.VarToJsonString(User{ - Uid: 100, - Name: "john", - SiteUrl: "https://goframe.org", - Pass1: "123", - Pass2: "456", - }) + data := "HwHsGhXMaGc===" + datab, err := gbase64.Decode([]byte(data)) fmt.Println(err) - fmt.Println(s) + fmt.Println(datab) + fmt.Println(string(datab)) + + s, e := base64.StdEncoding.DecodeString(data) + fmt.Println(e) + fmt.Println(string(s)) } diff --git a/geg/util/gconv/gconv_map_tag.go b/geg/util/gconv/gconv_map_tag.go new file mode 100644 index 000000000..b7f653e44 --- /dev/null +++ b/geg/util/gconv/gconv_map_tag.go @@ -0,0 +1,18 @@ +package main + +import ( + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/util/gconv" +) + +func main() { + type User struct { + Id int `json:"uid"` + Name string `my-tag:"nick-name" json:"name"` + } + user := &User{ + Id: 1, + Name: "john", + } + g.Dump(gconv.Map(user, "my-tag")) +} diff --git a/geg/util/gconv/gconv_struct_create.go b/geg/util/gconv/gconv_struct_create.go new file mode 100644 index 000000000..54d22e54d --- /dev/null +++ b/geg/util/gconv/gconv_struct_create.go @@ -0,0 +1,23 @@ +package main + +import ( + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/util/gconv" +) + +func main() { + type User struct { + Uid int + Name string + } + user := (*User)(nil) + params := g.Map{ + "uid": 1, + "name": "john", + } + err := gconv.Struct(params, &user) + if err != nil { + panic(err) + } + g.Dump(user) +} diff --git a/go.mod b/go.mod index ef37cb8d6..28729e4f6 100644 --- a/go.mod +++ b/go.mod @@ -1 +1,2 @@ module github.com/gogf/gf + diff --git a/version.go b/version.go index 103986c4d..4c74aa0db 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.7.2" +const VERSION = "v1.7.3" const AUTHORS = "john"