mirror of
https://gitee.com/johng/gf
synced 2026-06-09 19:13:58 +08:00
Compare commits
84 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7e95058cb5 | |||
| f33753e0cd | |||
| ac71e1d753 | |||
| 8151b6efd6 | |||
| 94de306c93 | |||
| 51f8ea26de | |||
| 29d9bb17cd | |||
| 30501a882d | |||
| f2f98e1d16 | |||
| cbf465eeea | |||
| 79c400e912 | |||
| cc4c49b5b0 | |||
| 96ea2c911d | |||
| 131b11680a | |||
| 3c6ab96283 | |||
| d2c4fa921a | |||
| dbb51a9328 | |||
| 55bfc3fa73 | |||
| d9c5182d1a | |||
| 899fcbf2da | |||
| 6e2c0d8706 | |||
| c4e599ce63 | |||
| 9cad9e4467 | |||
| 48019444ea | |||
| 76d31a7fbb | |||
| facb9d93c0 | |||
| 1d6bd46c5e | |||
| 8646dc1825 | |||
| 2992bdeed7 | |||
| c5601e6c88 | |||
| ef1d9a561c | |||
| 2d3b32c94a | |||
| 269378aa0d | |||
| 6889542801 | |||
| 8e6f1f1740 | |||
| b6ab1a992c | |||
| a5a267567c | |||
| f05f855c07 | |||
| bc5f773ba6 | |||
| 55308cc37c | |||
| 4d9db6edf0 | |||
| 38111a64e3 | |||
| bd8d3fca08 | |||
| 89ccaa3915 | |||
| 7c2cff7d99 | |||
| 788e15dbb6 | |||
| a0172d9d7e | |||
| bc1a7a1644 | |||
| c98234d3e6 | |||
| 45a94d23d5 | |||
| 351de5ee6a | |||
| 3559436d1e | |||
| 189f4c1637 | |||
| caead810e2 | |||
| ebdc5d9c9d | |||
| 4164059211 | |||
| bd27258c46 | |||
| ddf0605bc4 | |||
| 991dbe50e0 | |||
| 8050efb835 | |||
| 9355bc73a2 | |||
| acc2a6a353 | |||
| 09e83e7b8d | |||
| c0df8a3d80 | |||
| 33e890d225 | |||
| 750e5df962 | |||
| 74c65439fc | |||
| f290bd7170 | |||
| d398b749d4 | |||
| 2fd5a1574a | |||
| a7504f0629 | |||
| 3c35bb85ee | |||
| 6621edb04c | |||
| b0c722e297 | |||
| 47a6284274 | |||
| 150b8edb70 | |||
| aa5d3285eb | |||
| 993d6e3076 | |||
| 78ad9d8c2d | |||
| feff3ddce3 | |||
| 80fddad64d | |||
| 1e6dd0be02 | |||
| 22ecf4f1b6 | |||
| 5d21148657 |
@ -1,8 +1,9 @@
|
||||
|
||||
# MySQL数据库配置
|
||||
[database]
|
||||
# debug = true
|
||||
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local"
|
||||
debug = true
|
||||
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local"
|
||||
MaxOpen = 100
|
||||
|
||||
#[database]
|
||||
# [[database.default]]
|
||||
|
||||
@ -2,12 +2,34 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/glog"
|
||||
)
|
||||
|
||||
func SendXmlFile(gameId int, areaName string, filePath string) error {
|
||||
path := filepath.FromSlash(filePath)
|
||||
fmt.Println(path)
|
||||
data := g.Map{
|
||||
"gameName": gameId,
|
||||
"area": areaName,
|
||||
"file": "@file:" + path,
|
||||
"contentType": "json",
|
||||
}
|
||||
if r, err := ghttp.Post("http://127.0.0.1:8199/upload", data); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
defer r.Close()
|
||||
fmt.Println("ok")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
SendXmlFile(1, "xxx", "/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/net/ghttp/server/session.go")
|
||||
return
|
||||
path := "/home/john/Workspace/Go/github.com/gogf/gf/version.go"
|
||||
r, e := ghttp.Post("http://127.0.0.1:8199/upload", "upload-file=@file:"+path)
|
||||
if e != nil {
|
||||
|
||||
@ -8,7 +8,7 @@ import (
|
||||
// Upload uploads files to /tmp .
|
||||
func Upload(r *ghttp.Request) {
|
||||
saveDirPath := "/tmp/"
|
||||
files := r.GetUploadFiles("upload-file")
|
||||
files := r.GetUploadFiles("file")
|
||||
if _, err := files.Save(saveDirPath); err != nil {
|
||||
r.Response.WriteExit(err)
|
||||
}
|
||||
|
||||
@ -5,21 +5,18 @@ import (
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
}
|
||||
type User struct{}
|
||||
|
||||
func (c *User) Index(r *ghttp.Request) {
|
||||
r.Response.Write("Index")
|
||||
}
|
||||
|
||||
// 不符合规范,不会被注册
|
||||
func (c *User) Test(r *ghttp.Request, value interface{}) {
|
||||
func (c *User) Test(r *ghttp.Request) {
|
||||
r.Response.Write("Test")
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindObject("/user", new(User))
|
||||
u := new(User)
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.GET("/db-{table}/{id}", u, "Test")
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
|
||||
14
.example/os/glog/glog_ctx.go
Normal file
14
.example/os/glog/glog_ctx.go
Normal file
@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/os/glog"
|
||||
)
|
||||
|
||||
func main() {
|
||||
glog.SetCtxKeys("Trace-Id", "Span-Id", "Test")
|
||||
ctx := context.WithValue(context.Background(), "Trace-Id", "1234567890")
|
||||
ctx = context.WithValue(ctx, "Span-Id", "abcdefg")
|
||||
|
||||
glog.Ctx(ctx).Print(1, 2, 3)
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
|
||||
[database]
|
||||
debug = true
|
||||
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
|
||||
[redis]
|
||||
default = "127.0.0.1:6379,0"
|
||||
cache = "127.0.0.1:6379,1"
|
||||
|
||||
# Logger.
|
||||
[logger]
|
||||
Path = "/tmp/log/gf-app"
|
||||
Level = "all"
|
||||
Stdout = true
|
||||
|
||||
[viewer]
|
||||
delimiters = ["${", "}"]
|
||||
autoencode = true
|
||||
@ -1,29 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/encoding/gbase64"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/grand"
|
||||
)
|
||||
|
||||
// bytesToHexString converts binary content to hex string content.
|
||||
func bytesToHexStr(b []byte) string {
|
||||
dst := make([]byte, hex.EncodedLen(len(b)))
|
||||
hex.Encode(dst, b)
|
||||
return gconv.UnsafeBytesToStr(dst)
|
||||
}
|
||||
|
||||
func main() {
|
||||
b := make([]byte, 1024)
|
||||
for i := 0; i < 1024; i++ {
|
||||
b[i] = byte(grand.N(0, 255))
|
||||
}
|
||||
|
||||
fmt.Println(bytesToHexStr(b))
|
||||
fmt.Println(len(b))
|
||||
fmt.Println(len(bytesToHexStr(b)))
|
||||
fmt.Println(gbase64.EncodeToString(b))
|
||||
fmt.Println(len(gbase64.EncodeToString(b)))
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
r := ghttp.PostContent("http://127.0.0.1:8199/test", `<doc><id>1</id><name>john</name><password1>123Abc!@#</password1><password2>123Abc!@#</password2></doc>`)
|
||||
fmt.Println(r)
|
||||
}
|
||||
@ -8,6 +8,9 @@ import (
|
||||
|
||||
func main() {
|
||||
for i := 0; i < 100; i++ {
|
||||
fmt.Println(grand.Rand(0, 99999))
|
||||
fmt.Println(grand.S(16))
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
fmt.Println(grand.N(0, 99999999))
|
||||
}
|
||||
}
|
||||
|
||||
13
.example/util/guid/guid.go
Normal file
13
.example/util/guid/guid.go
Normal file
@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/util/guid"
|
||||
)
|
||||
|
||||
func main() {
|
||||
for i := 0; i < 100; i++ {
|
||||
s := guid.S()
|
||||
fmt.Println(s, len(s))
|
||||
}
|
||||
}
|
||||
20
.example/util/guid/guid_length.go
Normal file
20
.example/util/guid/guid_length.go
Normal file
@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 36*36^2+36*36+36
|
||||
var s string
|
||||
fmt.Println(strconv.ParseUint("zzz", 36, 3))
|
||||
fmt.Println(1 << 1)
|
||||
// MaxInt64
|
||||
s = strconv.FormatUint(math.MaxUint64, 16)
|
||||
fmt.Println(s, len(s))
|
||||
// PID
|
||||
s = strconv.FormatInt(1000000, 36)
|
||||
fmt.Println(s, len(s))
|
||||
}
|
||||
23
.example/util/guid/guid_unique.go
Normal file
23
.example/util/guid/guid_unique.go
Normal file
@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/util/guid"
|
||||
)
|
||||
|
||||
func main() {
|
||||
for i := 0; i < 100; i++ {
|
||||
s := guid.S([]byte("123"))
|
||||
fmt.Println(s, len(s))
|
||||
}
|
||||
fmt.Println()
|
||||
for i := 0; i < 100; i++ {
|
||||
s := guid.S([]byte("123"), []byte("456"))
|
||||
fmt.Println(s, len(s))
|
||||
}
|
||||
fmt.Println()
|
||||
for i := 0; i < 100; i++ {
|
||||
s := guid.S([]byte("123"), []byte("456"), []byte("789"))
|
||||
fmt.Println(s, len(s))
|
||||
}
|
||||
}
|
||||
13
.example/util/gvalid/gvalid_custom_message.go
Normal file
13
.example/util/gvalid/gvalid_custom_message.go
Normal file
@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gvalid"
|
||||
)
|
||||
|
||||
func main() {
|
||||
g.I18n().SetLanguage("cn")
|
||||
err := gvalid.Check("", "required", nil)
|
||||
fmt.Println(err.String())
|
||||
}
|
||||
14
.example/util/gvalid/i18n/cn.toml
Normal file
14
.example/util/gvalid/i18n/cn.toml
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
|
||||
"gf.gvalid.required" = "字段不能为空"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -15,3 +15,4 @@ cbuild
|
||||
**/.DS_Store
|
||||
.vscode/
|
||||
go.sum
|
||||
.example/other/
|
||||
20
DONATOR.MD
20
DONATOR.MD
@ -56,7 +56,7 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|
||||
|🚶|wechat|¥6.88| 喝杯冰阔落
|
||||
|a*l|wechat|¥10.00| gf
|
||||
|[wxkj](https://gitee.com/wxkj)|wechat|¥10.00|
|
||||
|*包|wechat|¥9.99|
|
||||
|[米司特包](https://github.com/misitebao)|wechat|¥9.99|
|
||||
|重庆宝尔威科技|wechat|¥6.66|
|
||||
|琦玉-QPT|wechat|¥6.66|
|
||||
|sailsea|wechat|¥11.00|
|
||||
@ -64,7 +64,7 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|
||||
|*华|wechat|¥6.66| 感谢郭强的热心
|
||||
|[Playhi](https://github.com/Playhi)|alipay|¥10.00|
|
||||
|北京京纬互动科技|alipay|¥200.00|
|
||||
|米司特包|wechat|¥99.99|
|
||||
|[米司特包](https://github.com/misitebao)|wechat|¥99.99|
|
||||
|金毛|alipay|¥100.00|
|
||||
|1*1x|wechat|¥100.00|
|
||||
|[ywanbing](https://github.com/ywanbing)|wechat|¥66.66|
|
||||
@ -73,10 +73,24 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|
||||
|蔡蔡|wechat|¥666.00| gf真强大,让项目省心
|
||||
|jack|wechat|¥100.00|
|
||||
|sbilly|wechat|¥100.00| 祝好!
|
||||
|米司|wechat|¥166.66| 大佬加油!
|
||||
|[米司特包](https://github.com/misitebao)|wechat|¥166.66| 大佬加油!
|
||||
|*秦|wechat|¥20.00| 给群主献上一杯咖啡...
|
||||
|[zhuhuan12](https://gitee.com/zhuhuan12)|wechat|¥50.00|
|
||||
|faddei|qq|¥9.99|
|
||||
|*天|wechat|¥10.00|
|
||||
|*.|wechat|¥20.00| 学生一个微薄之力,冲
|
||||
|李勇|wechat|¥50.00|
|
||||
|[米司特包](https://github.com/misitebao)|wechat|¥66.66|
|
||||
|千年|wechat|¥1.08|
|
||||
|[szzxing](https://github.com/szzxing)|wechat|¥50.00|
|
||||
|伟客互联|wechat|¥66.66|
|
||||
|sbilly|wechat|¥99.00|
|
||||
|**阳|alipay|¥100.01|
|
||||
|**亮|alipay|¥10.00|
|
||||
|[mingzaily](https://github.com/mingzaily)|alipay|¥30.00|
|
||||
|[seny0929](https://gitee.com/seny0929)|alipay|¥9.90|
|
||||
|Fly的狐狸|alipay|¥100.00|
|
||||
|
||||
|
||||
<img src="https://goframe.org/images/donate.png"/>
|
||||
|
||||
|
||||
20
README.MD
20
README.MD
@ -31,6 +31,15 @@ require github.com/gogf/gf latest
|
||||
golang version >= 1.11
|
||||
```
|
||||
|
||||
# Packages
|
||||
1. **Primary Package**
|
||||
|
||||
The `gf` repository maintains some basic and most commonly used packages, keeping it as lightweight and simple as possible.
|
||||
|
||||
1. **Community Package**
|
||||
|
||||
The community packages are contributed and maintained by community members, which are hosted in `gogf` organization. Some of the community packages are separated from the `gf` repository, which are not of common usage or are with heavy dependencies.
|
||||
|
||||
# Architecture
|
||||
<div align=center>
|
||||
<img src="https://goframe.org/images/arch.png?v=11"/>
|
||||
@ -106,10 +115,16 @@ The concurrency starts from `100` to `10000`.
|
||||
|
||||
`GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever.
|
||||
|
||||
# Known Users
|
||||
|
||||
Logos are not authorized to be shown due to trademark copyrights.
|
||||
|
||||
|
||||
# Contributors
|
||||
This project exists thanks to all the people who contribute. [[Contributors](https://github.com/gogf/gf/graphs/contributors)].
|
||||
<a href="https://github.com/gogf/gf/graphs/contributors"><img src="https://opencollective.com/goframe/contributors.svg?width=890&button=false" /></a>
|
||||
|
||||
|
||||
# Donators
|
||||
|
||||
We currently accept donation by Alipay/WechatPay, please note your github/gitee account in your payment bill. If you like `GF`, why not [buy developer a cup of coffee](DONATOR.MD)?
|
||||
@ -118,6 +133,7 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|
||||
We appreciate any kind of sponsorship for `GF` development. If you've got some interesting, please contact WeChat `389961817` / Email `john@goframe.org`.
|
||||
|
||||
|
||||
|
||||
# Thanks
|
||||
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/images/jetbrains.png" width="100" alt="JetBrains"/></a>
|
||||
|
||||
@ -128,3 +144,7 @@ We appreciate any kind of sponsorship for `GF` development. If you've got some i
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
20
README_ZH.MD
20
README_ZH.MD
@ -45,6 +45,17 @@ require github.com/gogf/gf latest
|
||||
golang版本 >= 1.11
|
||||
```
|
||||
|
||||
# 模块
|
||||
|
||||
1. **核心模块**
|
||||
|
||||
`GoFrame`提供了一些基础的、常用的模块,简单、易用和轻量级,并保持极少的外部依赖,这些模块由`gf`主仓库细致维护。
|
||||
|
||||
1. **社区模块**
|
||||
|
||||
社区模块主要由社区贡献并维护,大部分也是由`gf`主仓库的贡献者提供及维护,存放于`gogf`空间下,与`gf`主仓库处于同一级别。有的社区模块是从`gf`主仓库中剥离出来单独维护的模块,这些模块并不是特别常用,或者对外部依赖较重。
|
||||
|
||||
|
||||
# 架构
|
||||
<div align=center>
|
||||
<img src="https://goframe.org/images/arch.png?v=11"/>
|
||||
@ -116,12 +127,16 @@ ab -t 10 -c 100 http://127.0.0.1:3000/json
|
||||
- WX交流群:微信添加`389961817`备注`GF`
|
||||
- 主库ISSUE:https://github.com/gogf/gf/issues
|
||||
|
||||
> 建议通过阅读`Gorame`的源码以及API文档深度学习`GoFrame`,了解更多的精妙设计。
|
||||
> 建议通过阅读`GoFrame`的源码以及API文档深度学习`GoFrame`,了解更多的精妙设计。
|
||||
|
||||
# 协议
|
||||
|
||||
`GF` 使用非常友好的 [MIT](LICENSE) 开源协议进行发布,永久`100%`开源免费。
|
||||
|
||||
# 用户
|
||||
|
||||
由于商标版权缘故,未经厂商商务部授权允许无法用于宣传展示。
|
||||
|
||||
# 贡献
|
||||
|
||||
感谢所有参与`GoFrame`开发的贡献者。 [[贡献者列表](https://github.com/gogf/gf/graphs/contributors)].
|
||||
@ -138,4 +153,5 @@ ab -t 10 -c 100 http://127.0.0.1:3000/json
|
||||
赞助支持`GF`框架的快速研发,如果您感兴趣,请联系 微信 `389961817` / 邮件 `john@goframe.org`。
|
||||
|
||||
# 感谢
|
||||
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/images/jetbrains.png" width="100" alt="JetBrains"/></a>
|
||||
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/images/jetbrains.png" width="100" alt="JetBrains"/></a>
|
||||
|
||||
|
||||
@ -4,5 +4,5 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package garray provides concurrent-safe/unsafe arrays.
|
||||
// Package garray provides most commonly used array containers which also support concurrent-safe/unsafe switch feature.
|
||||
package garray
|
||||
|
||||
@ -22,6 +22,8 @@ import (
|
||||
)
|
||||
|
||||
// Array is a golang array with rich features.
|
||||
// It contains a concurrent-safe/unsafe switch, which should be set
|
||||
// when its initialization and cannot be changed then.
|
||||
type Array struct {
|
||||
mu rwmutex.RWMutex
|
||||
array []interface{}
|
||||
@ -288,7 +290,7 @@ func (a *Array) PopRight() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
if index <= 0 {
|
||||
if index < 0 {
|
||||
return nil, false
|
||||
}
|
||||
value = a.array[index]
|
||||
@ -488,6 +490,7 @@ func (a *Array) Search(value interface{}) int {
|
||||
}
|
||||
|
||||
// Unique uniques the array, clear repeated items.
|
||||
// Example: [1,1,2,3,2] -> [1,2,3]
|
||||
func (a *Array) Unique() *Array {
|
||||
a.mu.Lock()
|
||||
for i := 0; i < len(a.array)-1; i++ {
|
||||
@ -717,7 +720,8 @@ func (a *Array) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *Array) MarshalJSON() ([]byte, error) {
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a Array) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
|
||||
@ -20,6 +20,8 @@ import (
|
||||
)
|
||||
|
||||
// IntArray is a golang int array with rich features.
|
||||
// It contains a concurrent-safe/unsafe switch, which should be set
|
||||
// when its initialization and cannot be changed then.
|
||||
type IntArray struct {
|
||||
mu rwmutex.RWMutex
|
||||
array []int
|
||||
@ -264,7 +266,7 @@ func (a *IntArray) PopRight() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
if index <= 0 {
|
||||
if index < 0 {
|
||||
return 0, false
|
||||
}
|
||||
value = a.array[index]
|
||||
@ -504,6 +506,7 @@ func (a *IntArray) Search(value int) int {
|
||||
}
|
||||
|
||||
// Unique uniques the array, clear repeated items.
|
||||
// Example: [1,1,2,3,2] -> [1,2,3]
|
||||
func (a *IntArray) Unique() *IntArray {
|
||||
a.mu.Lock()
|
||||
for i := 0; i < len(a.array)-1; i++ {
|
||||
@ -716,7 +719,8 @@ func (a *IntArray) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *IntArray) MarshalJSON() ([]byte, error) {
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a IntArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
|
||||
@ -22,6 +22,8 @@ import (
|
||||
)
|
||||
|
||||
// StrArray is a golang string array with rich features.
|
||||
// It contains a concurrent-safe/unsafe switch, which should be set
|
||||
// when its initialization and cannot be changed then.
|
||||
type StrArray struct {
|
||||
mu rwmutex.RWMutex
|
||||
array []string
|
||||
@ -252,7 +254,7 @@ func (a *StrArray) PopRight() (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
if index <= 0 {
|
||||
if index < 0 {
|
||||
return "", false
|
||||
}
|
||||
value = a.array[index]
|
||||
@ -508,6 +510,7 @@ func (a *StrArray) Search(value string) int {
|
||||
}
|
||||
|
||||
// Unique uniques the array, clear repeated items.
|
||||
// Example: [1,1,2,3,2] -> [1,2,3]
|
||||
func (a *StrArray) Unique() *StrArray {
|
||||
a.mu.Lock()
|
||||
for i := 0; i < len(a.array)-1; i++ {
|
||||
@ -731,7 +734,8 @@ func (a *StrArray) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *StrArray) MarshalJSON() ([]byte, error) {
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a StrArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
|
||||
@ -22,7 +22,10 @@ import (
|
||||
)
|
||||
|
||||
// SortedArray is a golang sorted array with rich features.
|
||||
// It's using increasing order in default.
|
||||
// It is using increasing order in default, which can be changed by
|
||||
// setting it a custom comparator.
|
||||
// It contains a concurrent-safe/unsafe switch, which should be set
|
||||
// when its initialization and cannot be changed then.
|
||||
type SortedArray struct {
|
||||
mu rwmutex.RWMutex
|
||||
array []interface{}
|
||||
@ -224,7 +227,7 @@ func (a *SortedArray) PopRight() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
if index <= 0 {
|
||||
if index < 0 {
|
||||
return nil, false
|
||||
}
|
||||
value = a.array[index]
|
||||
@ -666,7 +669,8 @@ func (a *SortedArray) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *SortedArray) MarshalJSON() ([]byte, error) {
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a SortedArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
|
||||
@ -19,7 +19,10 @@ import (
|
||||
)
|
||||
|
||||
// SortedIntArray is a golang sorted int array with rich features.
|
||||
// It's using increasing order in default.
|
||||
// It is using increasing order in default, which can be changed by
|
||||
// setting it a custom comparator.
|
||||
// It contains a concurrent-safe/unsafe switch, which should be set
|
||||
// when its initialization and cannot be changed then.
|
||||
type SortedIntArray struct {
|
||||
mu rwmutex.RWMutex
|
||||
array []int
|
||||
@ -209,7 +212,7 @@ func (a *SortedIntArray) PopRight() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
if index <= 0 {
|
||||
if index < 0 {
|
||||
return 0, false
|
||||
}
|
||||
value = a.array[index]
|
||||
@ -640,7 +643,8 @@ func (a *SortedIntArray) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *SortedIntArray) MarshalJSON() ([]byte, error) {
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a SortedIntArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
|
||||
@ -20,7 +20,10 @@ import (
|
||||
)
|
||||
|
||||
// SortedStrArray is a golang sorted string array with rich features.
|
||||
// It's using increasing order in default.
|
||||
// It is using increasing order in default, which can be changed by
|
||||
// setting it a custom comparator.
|
||||
// It contains a concurrent-safe/unsafe switch, which should be set
|
||||
// when its initialization and cannot be changed then.
|
||||
type SortedStrArray struct {
|
||||
mu rwmutex.RWMutex
|
||||
array []string
|
||||
@ -195,7 +198,7 @@ func (a *SortedStrArray) PopRight() (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
if index <= 0 {
|
||||
if index < 0 {
|
||||
return "", false
|
||||
}
|
||||
value = a.array[index]
|
||||
@ -653,7 +656,8 @@ func (a *SortedStrArray) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *SortedStrArray) MarshalJSON() ([]byte, error) {
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a SortedStrArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
|
||||
@ -72,8 +72,50 @@ func Example_basic() {
|
||||
// []
|
||||
}
|
||||
|
||||
func Example_iterate() {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"a", "b", "c"})
|
||||
// Iterator is alias of IteratorAsc, which iterates the array readonly in ascending order
|
||||
// with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
array.Iterator(func(k int, v string) bool {
|
||||
fmt.Println(k, v)
|
||||
return true
|
||||
})
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
array.IteratorDesc(func(k int, v string) bool {
|
||||
fmt.Println(k, v)
|
||||
return true
|
||||
})
|
||||
|
||||
// Output:
|
||||
// 0 a
|
||||
// 1 b
|
||||
// 2 c
|
||||
// 2 c
|
||||
// 1 b
|
||||
// 0 a
|
||||
}
|
||||
|
||||
func Example_reverse() {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Reverse makes array with elements in reverse order.
|
||||
fmt.Println(array.Reverse().Slice())
|
||||
|
||||
// Output:
|
||||
// [9 8 7 6 5 4 3 2 1]
|
||||
}
|
||||
|
||||
func Example_shuffle() {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Shuffle randomly shuffles the array.
|
||||
fmt.Println(array.Shuffle().Slice())
|
||||
}
|
||||
|
||||
func Example_rand() {
|
||||
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Randomly retrieve and return 2 items from the array.
|
||||
// It does not delete the items from array.
|
||||
@ -84,6 +126,26 @@ func Example_rand() {
|
||||
fmt.Println(array.PopRand())
|
||||
}
|
||||
|
||||
func Example_join() {
|
||||
array := garray.NewFrom(g.Slice{"a", "b", "c", "d"})
|
||||
fmt.Println(array.Join(","))
|
||||
|
||||
// Output:
|
||||
// a,b,c,d
|
||||
}
|
||||
|
||||
func Example_chunk() {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
// the size of each array is determined by <size>.
|
||||
// The last chunk may contain less than size elements.
|
||||
fmt.Println(array.Chunk(2))
|
||||
|
||||
// Output:
|
||||
// [[1 2] [3 4] [5 6] [7 8] [9]]
|
||||
}
|
||||
|
||||
func Example_popItem() {
|
||||
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
@ -101,10 +163,38 @@ func Example_popItem() {
|
||||
// [7 8]
|
||||
}
|
||||
|
||||
func Example_walk() {
|
||||
var array garray.StrArray
|
||||
tables := g.SliceStr{"user", "user_detail"}
|
||||
prefix := "gf_"
|
||||
array.Append(tables...)
|
||||
// Add prefix for given table names.
|
||||
array.Walk(func(value string) string {
|
||||
return prefix + value
|
||||
})
|
||||
fmt.Println(array.Slice())
|
||||
|
||||
// Output:
|
||||
// [gf_user gf_user_detail]
|
||||
}
|
||||
|
||||
func Example_contains() {
|
||||
var array garray.StrArray
|
||||
array.Append("a")
|
||||
fmt.Println(array.Contains("a"))
|
||||
fmt.Println(array.Contains("A"))
|
||||
fmt.Println(array.ContainsI("A"))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
}
|
||||
|
||||
func Example_mergeArray() {
|
||||
array1 := garray.NewFrom([]interface{}{1, 2})
|
||||
array2 := garray.NewFrom([]interface{}{3, 4})
|
||||
slice1 := []interface{}{5, 6}
|
||||
array1 := garray.NewFrom(g.Slice{1, 2})
|
||||
array2 := garray.NewFrom(g.Slice{3, 4})
|
||||
slice1 := g.Slice{5, 6}
|
||||
slice2 := []int{7, 8}
|
||||
slice3 := []string{"9", "0"}
|
||||
fmt.Println(array1.Slice())
|
||||
|
||||
@ -137,6 +137,65 @@ func TestArray_PopRands(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PopLeft(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3})
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PopRight(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3})
|
||||
|
||||
v, ok := array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PopLefts(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3})
|
||||
t.Assert(array.PopLefts(2), g.Slice{1, 2})
|
||||
t.Assert(array.Len(), 1)
|
||||
t.Assert(array.PopLefts(2), g.Slice{3})
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PopRights(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3})
|
||||
t.Assert(array.PopRights(2), g.Slice{2, 3})
|
||||
t.Assert(array.Len(), 1)
|
||||
t.Assert(array.PopLefts(2), g.Slice{1})
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PopLeftsAndPopRights(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.New()
|
||||
@ -501,6 +560,7 @@ func TestArray_RLockFunc(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestArray_Json(t *testing.T) {
|
||||
// pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []interface{}{"a", "b", "d", "c"}
|
||||
a1 := garray.NewArrayFrom(s1)
|
||||
@ -519,7 +579,26 @@ func TestArray_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// value.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []interface{}{"a", "b", "d", "c"}
|
||||
a1 := *garray.NewArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.New()
|
||||
err2 = json.Unmarshal(b2, &a2)
|
||||
t.Assert(err2, nil)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.Array
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
@ -532,6 +611,25 @@ func TestArray_Json(t *testing.T) {
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
})
|
||||
// value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.Array
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
|
||||
@ -230,6 +230,65 @@ func TestIntArray_Fill(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_PopLeft(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayFrom(g.SliceInt{1, 2, 3})
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_PopRight(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayFrom(g.SliceInt{1, 2, 3})
|
||||
|
||||
v, ok := array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_PopLefts(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayFrom(g.SliceInt{1, 2, 3})
|
||||
t.Assert(array.PopLefts(2), g.Slice{1, 2})
|
||||
t.Assert(array.Len(), 1)
|
||||
t.Assert(array.PopLefts(2), g.Slice{3})
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_PopRights(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayFrom(g.SliceInt{1, 2, 3})
|
||||
t.Assert(array.PopRights(2), g.Slice{2, 3})
|
||||
t.Assert(array.Len(), 1)
|
||||
t.Assert(array.PopLefts(2), g.Slice{1})
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Chunk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5}
|
||||
@ -546,6 +605,7 @@ func TestIntArray_RLockFunc(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIntArray_Json(t *testing.T) {
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 4, 3, 2}
|
||||
a1 := garray.NewIntArrayFrom(s1)
|
||||
@ -563,7 +623,25 @@ func TestIntArray_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 4, 3, 2}
|
||||
a1 := *garray.NewIntArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewIntArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.IntArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
@ -576,6 +654,25 @@ func TestIntArray_Json(t *testing.T) {
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.IntArray
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
|
||||
@ -129,6 +129,65 @@ func TestStrArray_PushAndPop(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PopLeft(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2", "3"})
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PopRight(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2", "3"})
|
||||
|
||||
v, ok := array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PopLefts(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2", "3"})
|
||||
t.Assert(array.PopLefts(2), g.Slice{"1", "2"})
|
||||
t.Assert(array.Len(), 1)
|
||||
t.Assert(array.PopLefts(2), g.Slice{"3"})
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PopRights(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2", "3"})
|
||||
t.Assert(array.PopRights(2), g.Slice{"2", "3"})
|
||||
t.Assert(array.Len(), 1)
|
||||
t.Assert(array.PopLefts(2), g.Slice{"1"})
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PopLeftsAndPopRights(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArray()
|
||||
@ -545,6 +604,7 @@ func TestStrArray_LockFunc(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStrArray_Json(t *testing.T) {
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []string{"a", "b", "d", "c"}
|
||||
a1 := garray.NewStrArrayFrom(s1)
|
||||
@ -562,7 +622,25 @@ func TestStrArray_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []string{"a", "b", "d", "c"}
|
||||
a1 := *garray.NewStrArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewStrArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.StrArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
@ -575,6 +653,25 @@ func TestStrArray_Json(t *testing.T) {
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.StrArray
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []string{"A+", "A", "A"},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
|
||||
@ -148,34 +148,62 @@ func TestSortedArray_Remove(t *testing.T) {
|
||||
|
||||
func TestSortedArray_PopLeft(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
array1 := garray.NewSortedArrayFrom(
|
||||
[]interface{}{"a", "d", "c", "b"},
|
||||
gutil.ComparatorString,
|
||||
)
|
||||
i1, ok := array1.PopLeft()
|
||||
t.Assert(ok, true)
|
||||
t.Assert(gconv.String(i1), "a")
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1, []interface{}{"b", "c", "d"})
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedArrayFrom(g.Slice{1, 2, 3}, gutil.ComparatorInt)
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_PopRight(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
array1 := garray.NewSortedArrayFrom(
|
||||
[]interface{}{"a", "d", "c", "b"},
|
||||
gutil.ComparatorString,
|
||||
)
|
||||
i1, ok := array1.PopRight()
|
||||
t.Assert(ok, true)
|
||||
t.Assert(gconv.String(i1), "d")
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1, []interface{}{"a", "b", "c"})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedArrayFrom(g.Slice{1, 2, 3}, gutil.ComparatorInt)
|
||||
v, ok := array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_PopRand(t *testing.T) {
|
||||
@ -249,7 +277,6 @@ func TestSortedArray_PopLefts(t *testing.T) {
|
||||
t.Assert(len(i2), 4)
|
||||
t.AssertIN(i1, []interface{}{"a", "d", "c", "b", "e", "f"})
|
||||
t.Assert(array1.Len(), 0)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -267,7 +294,7 @@ func TestSortedArray_PopRights(t *testing.T) {
|
||||
|
||||
i2 := array1.PopRights(10)
|
||||
t.Assert(len(i2), 4)
|
||||
|
||||
t.Assert(array1.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
@ -612,6 +639,7 @@ func TestSortedArray_Merge(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSortedArray_Json(t *testing.T) {
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []interface{}{"a", "b", "d", "c"}
|
||||
s2 := []interface{}{"a", "b", "c", "d"}
|
||||
@ -631,7 +659,27 @@ func TestSortedArray_Json(t *testing.T) {
|
||||
t.Assert(a3.Slice(), s1)
|
||||
t.Assert(a3.Interfaces(), s1)
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []interface{}{"a", "b", "d", "c"}
|
||||
s2 := []interface{}{"a", "b", "c", "d"}
|
||||
a1 := *garray.NewSortedArrayFrom(s1, gutil.ComparatorString)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewSortedArray(gutil.ComparatorString)
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
|
||||
var a3 garray.SortedArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
t.Assert(a3.Interfaces(), s1)
|
||||
})
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
@ -663,6 +711,42 @@ func TestSortedArray_Json(t *testing.T) {
|
||||
t.AssertIN(v, data["Scores"])
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = user.Scores.PopLeft()
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.SortedArray
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.AssertNE(user.Scores, nil)
|
||||
t.Assert(user.Scores.Len(), 3)
|
||||
|
||||
v, ok := user.Scores.PopLeft()
|
||||
t.AssertIN(v, data["Scores"])
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = user.Scores.PopLeft()
|
||||
t.AssertIN(v, data["Scores"])
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = user.Scores.PopLeft()
|
||||
t.AssertIN(v, data["Scores"])
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = user.Scores.PopLeft()
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
|
||||
@ -133,6 +133,21 @@ func TestSortedIntArray_PopLeft(t *testing.T) {
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1.Search(1), -1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedIntArrayFrom(g.SliceInt{1, 2, 3})
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_PopRight(t *testing.T) {
|
||||
@ -145,6 +160,23 @@ func TestSortedIntArray_PopRight(t *testing.T) {
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1.Search(5), -1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedIntArrayFrom(g.SliceInt{1, 2, 3})
|
||||
v, ok := array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_PopRand(t *testing.T) {
|
||||
@ -504,6 +536,7 @@ func TestSortedIntArray_Merge(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Json(t *testing.T) {
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 4, 3, 2}
|
||||
s2 := []int{1, 2, 3, 4}
|
||||
@ -522,7 +555,26 @@ func TestSortedIntArray_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 4, 3, 2}
|
||||
s2 := []int{1, 2, 3, 4}
|
||||
a1 := *garray.NewSortedIntArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewSortedIntArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
|
||||
var a3 garray.SortedIntArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
@ -535,6 +587,25 @@ func TestSortedIntArray_Json(t *testing.T) {
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, []int{98, 99, 100})
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.SortedIntArray
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
|
||||
@ -137,6 +137,21 @@ func TestSortedStrArray_PopLeft(t *testing.T) {
|
||||
t.Assert(array1.Len(), 4)
|
||||
t.Assert(array1.Contains("a"), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedStrArrayFrom(g.SliceStr{"1", "2", "3"})
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_PopRight(t *testing.T) {
|
||||
@ -149,6 +164,23 @@ func TestSortedStrArray_PopRight(t *testing.T) {
|
||||
t.Assert(array1.Len(), 4)
|
||||
t.Assert(array1.Contains("e"), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedStrArrayFrom(g.SliceStr{"1", "2", "3"})
|
||||
v, ok := array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_PopRand(t *testing.T) {
|
||||
@ -210,6 +242,7 @@ func TestSortedStrArray_PopLefts(t *testing.T) {
|
||||
s1 = array1.PopLefts(4)
|
||||
t.Assert(len(s1), 3)
|
||||
t.Assert(s1, []string{"c", "d", "e"})
|
||||
t.Assert(array1.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
@ -523,6 +556,7 @@ func TestSortedStrArray_Merge(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Json(t *testing.T) {
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []string{"a", "b", "d", "c"}
|
||||
s2 := []string{"a", "b", "c", "d"}
|
||||
@ -543,7 +577,28 @@ func TestSortedStrArray_Json(t *testing.T) {
|
||||
t.Assert(a3.Slice(), s1)
|
||||
t.Assert(a3.Interfaces(), s1)
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []string{"a", "b", "d", "c"}
|
||||
s2 := []string{"a", "b", "c", "d"}
|
||||
a1 := *garray.NewSortedStrArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewSortedStrArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
t.Assert(a2.Interfaces(), s2)
|
||||
|
||||
var a3 garray.SortedStrArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
t.Assert(a3.Interfaces(), s1)
|
||||
})
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
@ -556,6 +611,25 @@ func TestSortedStrArray_Json(t *testing.T) {
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, []string{"A", "A", "A+"})
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.SortedStrArray
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []string{"A+", "A", "A"},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
|
||||
@ -5,25 +5,26 @@
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
// Package glist provides a concurrent-safe/unsafe doubly linked list.
|
||||
// Package glist provides most commonly used doubly linked list container which also supports concurrent-safe/unsafe switch feature.
|
||||
package glist
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
|
||||
"github.com/gogf/gf/internal/rwmutex"
|
||||
)
|
||||
|
||||
type (
|
||||
// List is a doubly linked list containing a concurrent-safe/unsafe switch.
|
||||
// The switch should be set when its initialization and cannot be changed then.
|
||||
List struct {
|
||||
mu rwmutex.RWMutex
|
||||
list list.List
|
||||
list *list.List
|
||||
}
|
||||
|
||||
// Element the item type of the list.
|
||||
Element = list.Element
|
||||
)
|
||||
|
||||
@ -31,7 +32,7 @@ type (
|
||||
func New(safe ...bool) *List {
|
||||
return &List{
|
||||
mu: rwmutex.Create(safe...),
|
||||
list: list.List{},
|
||||
list: list.New(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,7 +40,7 @@ func New(safe ...bool) *List {
|
||||
// The parameter <safe> is used to specify whether using list in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewFrom(array []interface{}, safe ...bool) *List {
|
||||
l := list.List{}
|
||||
l := list.New()
|
||||
for _, v := range array {
|
||||
l.PushBack(v)
|
||||
}
|
||||
@ -52,6 +53,9 @@ func NewFrom(array []interface{}, safe ...bool) *List {
|
||||
// PushFront inserts a new element <e> with value <v> at the front of list <l> and returns <e>.
|
||||
func (l *List) PushFront(v interface{}) (e *Element) {
|
||||
l.mu.Lock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
e = l.list.PushFront(v)
|
||||
l.mu.Unlock()
|
||||
return
|
||||
@ -60,6 +64,9 @@ func (l *List) PushFront(v interface{}) (e *Element) {
|
||||
// PushBack inserts a new element <e> with value <v> at the back of list <l> and returns <e>.
|
||||
func (l *List) PushBack(v interface{}) (e *Element) {
|
||||
l.mu.Lock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
e = l.list.PushBack(v)
|
||||
l.mu.Unlock()
|
||||
return
|
||||
@ -68,6 +75,9 @@ func (l *List) PushBack(v interface{}) (e *Element) {
|
||||
// PushFronts inserts multiple new elements with values <values> at the front of list <l>.
|
||||
func (l *List) PushFronts(values []interface{}) {
|
||||
l.mu.Lock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
for _, v := range values {
|
||||
l.list.PushFront(v)
|
||||
}
|
||||
@ -77,6 +87,9 @@ func (l *List) PushFronts(values []interface{}) {
|
||||
// PushBacks inserts multiple new elements with values <values> at the back of list <l>.
|
||||
func (l *List) PushBacks(values []interface{}) {
|
||||
l.mu.Lock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
for _, v := range values {
|
||||
l.list.PushBack(v)
|
||||
}
|
||||
@ -86,20 +99,28 @@ func (l *List) PushBacks(values []interface{}) {
|
||||
// PopBack removes the element from back of <l> and returns the value of the element.
|
||||
func (l *List) PopBack() (value interface{}) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
return
|
||||
}
|
||||
if e := l.list.Back(); e != nil {
|
||||
value = l.list.Remove(e)
|
||||
}
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// PopFront removes the element from front of <l> and returns the value of the element.
|
||||
func (l *List) PopFront() (value interface{}) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
return
|
||||
}
|
||||
if e := l.list.Front(); e != nil {
|
||||
value = l.list.Remove(e)
|
||||
}
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -107,6 +128,11 @@ func (l *List) PopFront() (value interface{}) {
|
||||
// and returns values of the removed elements as slice.
|
||||
func (l *List) PopBacks(max int) (values []interface{}) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
if max > 0 && max < length {
|
||||
@ -117,7 +143,6 @@ func (l *List) PopBacks(max int) (values []interface{}) {
|
||||
values[i] = l.list.Remove(l.list.Back())
|
||||
}
|
||||
}
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -125,6 +150,11 @@ func (l *List) PopBacks(max int) (values []interface{}) {
|
||||
// and returns values of the removed elements as slice.
|
||||
func (l *List) PopFronts(max int) (values []interface{}) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
if max > 0 && max < length {
|
||||
@ -135,7 +165,6 @@ func (l *List) PopFronts(max int) (values []interface{}) {
|
||||
values[i] = l.list.Remove(l.list.Front())
|
||||
}
|
||||
}
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -154,6 +183,10 @@ func (l *List) PopFrontAll() []interface{} {
|
||||
// FrontAll copies and returns values of all elements from front of <l> as slice.
|
||||
func (l *List) FrontAll() (values []interface{}) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
values = make([]interface{}, length)
|
||||
@ -161,13 +194,16 @@ func (l *List) FrontAll() (values []interface{}) {
|
||||
values[i] = e.Value
|
||||
}
|
||||
}
|
||||
l.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// BackAll copies and returns values of all elements from back of <l> as slice.
|
||||
func (l *List) BackAll() (values []interface{}) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
values = make([]interface{}, length)
|
||||
@ -175,43 +211,54 @@ func (l *List) BackAll() (values []interface{}) {
|
||||
values[i] = e.Value
|
||||
}
|
||||
}
|
||||
l.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// FrontValue returns value of the first element of <l> or nil if the list is empty.
|
||||
func (l *List) FrontValue() (value interface{}) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
if e := l.list.Front(); e != nil {
|
||||
value = e.Value
|
||||
}
|
||||
l.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// BackValue returns value of the last element of <l> or nil if the list is empty.
|
||||
func (l *List) BackValue() (value interface{}) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
if e := l.list.Back(); e != nil {
|
||||
value = e.Value
|
||||
}
|
||||
l.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Front returns the first element of list <l> or nil if the list is empty.
|
||||
func (l *List) Front() (e *Element) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
e = l.list.Front()
|
||||
l.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Back returns the last element of list <l> or nil if the list is empty.
|
||||
func (l *List) Back() (e *Element) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
e = l.list.Back()
|
||||
l.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -219,8 +266,11 @@ func (l *List) Back() (e *Element) {
|
||||
// The complexity is O(1).
|
||||
func (l *List) Len() (length int) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
length = l.list.Len()
|
||||
l.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -234,8 +284,11 @@ func (l *List) Size() int {
|
||||
// The element and <p> must not be nil.
|
||||
func (l *List) MoveBefore(e, p *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.MoveBefore(e, p)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// MoveAfter moves element <e> to its new position after <p>.
|
||||
@ -243,8 +296,11 @@ func (l *List) MoveBefore(e, p *Element) {
|
||||
// The element and <p> must not be nil.
|
||||
func (l *List) MoveAfter(e, p *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.MoveAfter(e, p)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// MoveToFront moves element <e> to the front of list <l>.
|
||||
@ -252,8 +308,11 @@ func (l *List) MoveAfter(e, p *Element) {
|
||||
// The element must not be nil.
|
||||
func (l *List) MoveToFront(e *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.MoveToFront(e)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// MoveToBack moves element <e> to the back of list <l>.
|
||||
@ -261,8 +320,11 @@ func (l *List) MoveToFront(e *Element) {
|
||||
// The element must not be nil.
|
||||
func (l *List) MoveToBack(e *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.MoveToBack(e)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// PushBackList inserts a copy of an other list at the back of list <l>.
|
||||
@ -273,8 +335,11 @@ func (l *List) PushBackList(other *List) {
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
l.mu.Lock()
|
||||
l.list.PushBackList(&other.list)
|
||||
l.mu.Unlock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.PushBackList(other.list)
|
||||
}
|
||||
|
||||
// PushFrontList inserts a copy of an other list at the front of list <l>.
|
||||
@ -285,8 +350,11 @@ func (l *List) PushFrontList(other *List) {
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
l.mu.Lock()
|
||||
l.list.PushFrontList(&other.list)
|
||||
l.mu.Unlock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.PushFrontList(other.list)
|
||||
}
|
||||
|
||||
// InsertAfter inserts a new element <e> with value <v> immediately after <p> and returns <e>.
|
||||
@ -294,8 +362,11 @@ func (l *List) PushFrontList(other *List) {
|
||||
// The <p> must not be nil.
|
||||
func (l *List) InsertAfter(p *Element, v interface{}) (e *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
e = l.list.InsertAfter(v, p)
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -304,8 +375,11 @@ func (l *List) InsertAfter(p *Element, v interface{}) (e *Element) {
|
||||
// The <p> must not be nil.
|
||||
func (l *List) InsertBefore(p *Element, v interface{}) (e *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
e = l.list.InsertBefore(v, p)
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -314,25 +388,31 @@ func (l *List) InsertBefore(p *Element, v interface{}) (e *Element) {
|
||||
// The element must not be nil.
|
||||
func (l *List) Remove(e *Element) (value interface{}) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
value = l.list.Remove(e)
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Removes removes multiple elements <es> from <l> if <es> are elements of list <l>.
|
||||
func (l *List) Removes(es []*Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
for _, e := range es {
|
||||
l.list.Remove(e)
|
||||
}
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// RemoveAll removes all elements from list <l>.
|
||||
func (l *List) RemoveAll() {
|
||||
l.mu.Lock()
|
||||
l.list = list.List{}
|
||||
l.list = list.New()
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
@ -345,14 +425,19 @@ func (l *List) Clear() {
|
||||
func (l *List) RLockFunc(f func(list *list.List)) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
f(&l.list)
|
||||
if l.list != nil {
|
||||
f(l.list)
|
||||
}
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
|
||||
func (l *List) LockFunc(f func(list *list.List)) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
f(&l.list)
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
f(l.list)
|
||||
}
|
||||
|
||||
// Iterator is alias of IteratorAsc.
|
||||
@ -365,6 +450,9 @@ func (l *List) Iterator(f func(e *Element) bool) {
|
||||
func (l *List) IteratorAsc(f func(e *Element) bool) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
for i, e := 0, l.list.Front(); i < length; i, e = i+1, e.Next() {
|
||||
@ -380,6 +468,9 @@ func (l *List) IteratorAsc(f func(e *Element) bool) {
|
||||
func (l *List) IteratorDesc(f func(e *Element) bool) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
for i, e := 0, l.list.Back(); i < length; i, e = i+1, e.Prev() {
|
||||
@ -394,17 +485,14 @@ func (l *List) IteratorDesc(f func(e *Element) bool) {
|
||||
func (l *List) Join(glue string) string {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
s := ""
|
||||
for i, e := 0, l.list.Front(); i < length; i, e = i+1, e.Next() {
|
||||
s = gconv.String(e.Value)
|
||||
if gstr.IsNumeric(s) {
|
||||
buffer.WriteString(s)
|
||||
} else {
|
||||
buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`)
|
||||
}
|
||||
buffer.WriteString(gconv.String(e.Value))
|
||||
if i != length-1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
@ -427,6 +515,9 @@ func (l *List) MarshalJSON() ([]byte, error) {
|
||||
func (l *List) UnmarshalJSON(b []byte) error {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
var array []interface{}
|
||||
if err := json.Unmarshal(b, &array); err != nil {
|
||||
return err
|
||||
@ -439,6 +530,9 @@ func (l *List) UnmarshalJSON(b []byte) error {
|
||||
func (l *List) UnmarshalValue(value interface{}) (err error) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
var array []interface{}
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
|
||||
"github.com/gogf/gf/container/glist"
|
||||
)
|
||||
@ -96,5 +97,30 @@ func Example_iterate() {
|
||||
//10987654321
|
||||
//12345678910
|
||||
//10987654321
|
||||
//[1,2,3,4,5,"M",7,8,9,10]
|
||||
//[1,2,3,4,5,M,7,8,9,10]
|
||||
}
|
||||
|
||||
func Example_popItem() {
|
||||
l := glist.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
fmt.Println(l.PopBack())
|
||||
fmt.Println(l.PopBacks(2))
|
||||
fmt.Println(l.PopFront())
|
||||
fmt.Println(l.PopFronts(2))
|
||||
|
||||
// Output:
|
||||
// 9
|
||||
// [8 7]
|
||||
// 1
|
||||
// [2 3]
|
||||
}
|
||||
|
||||
func Example_join() {
|
||||
var l glist.List
|
||||
l.PushBacks(g.Slice{"a", "b", "c", "d"})
|
||||
|
||||
fmt.Println(l.Join(","))
|
||||
|
||||
// Output:
|
||||
// a,b,c,d
|
||||
}
|
||||
|
||||
@ -15,7 +15,6 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// 检查链表长度
|
||||
func checkListLen(t *gtest.T, l *List, len int) bool {
|
||||
if n := l.Len(); n != len {
|
||||
t.Errorf("l.Len() = %d, want %d", n, len)
|
||||
@ -24,7 +23,6 @@ func checkListLen(t *gtest.T, l *List, len int) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// 检查指针地址
|
||||
func checkListPointers(t *gtest.T, l *List, es []*Element) {
|
||||
if !checkListLen(t, l, len(es)) {
|
||||
return
|
||||
@ -605,6 +603,17 @@ func TestList_Removes(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_Pop(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
t.Assert(l.PopBack(), 9)
|
||||
t.Assert(l.PopBacks(2), []interface{}{8, 7})
|
||||
t.Assert(l.PopFront(), 1)
|
||||
t.Assert(l.PopFronts(2), []interface{}{2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_Clear(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
@ -672,15 +681,15 @@ func TestList_Iterator(t *testing.T) {
|
||||
func TestList_Join(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewFrom([]interface{}{1, 2, "a", `"b"`, `\c`})
|
||||
t.Assert(l.Join(","), `1,2,"a","\"b\"","\\c"`)
|
||||
t.Assert(l.Join("."), `1.2."a"."\"b\""."\\c"`)
|
||||
t.Assert(l.Join(","), `1,2,a,"b",\c`)
|
||||
t.Assert(l.Join("."), `1.2.a."b".\c`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_String(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewFrom([]interface{}{1, 2, "a", `"b"`, `\c`})
|
||||
t.Assert(l.String(), `[1,2,"a","\"b\"","\\c"]`)
|
||||
t.Assert(l.String(), `[1,2,a,"b",\c]`)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
// If a copy of the MIT was not distributed with gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gmap provides concurrent-safe/unsafe map containers.
|
||||
// Package gmap provides most commonly used map container which also support concurrent-safe/unsafe switch feature.
|
||||
package gmap
|
||||
|
||||
type (
|
||||
|
||||
@ -98,6 +98,7 @@ func (m *AnyAnyMap) MapStrAny() map[string]interface{} {
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *AnyAnyMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
@ -108,6 +109,17 @@ func (m *AnyAnyMap) FilterEmpty() {
|
||||
}
|
||||
}
|
||||
|
||||
// FilterNil deletes all key-value pair of which the value is nil.
|
||||
func (m *AnyAnyMap) FilterNil() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for k, v := range m.data {
|
||||
if empty.IsNil(v) {
|
||||
delete(m.data, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *AnyAnyMap) Set(key interface{}, value interface{}) {
|
||||
m.mu.Lock()
|
||||
|
||||
@ -98,6 +98,7 @@ func (m *IntAnyMap) MapCopy() map[int]interface{} {
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *IntAnyMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
@ -108,6 +109,17 @@ func (m *IntAnyMap) FilterEmpty() {
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// FilterNil deletes all key-value pair of which the value is nil.
|
||||
func (m *IntAnyMap) FilterNil() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for k, v := range m.data {
|
||||
if empty.IsNil(v) {
|
||||
delete(m.data, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *IntAnyMap) Set(key int, val interface{}) {
|
||||
m.mu.Lock()
|
||||
|
||||
@ -96,6 +96,7 @@ func (m *IntIntMap) MapCopy() map[int]int {
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *IntIntMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
|
||||
@ -96,6 +96,7 @@ func (m *IntStrMap) MapCopy() map[int]string {
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *IntStrMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
|
||||
@ -92,6 +92,7 @@ func (m *StrAnyMap) MapCopy() map[string]interface{} {
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *StrAnyMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
@ -102,6 +103,17 @@ func (m *StrAnyMap) FilterEmpty() {
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// FilterNil deletes all key-value pair of which the value is nil.
|
||||
func (m *StrAnyMap) FilterNil() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for k, v := range m.data {
|
||||
if empty.IsNil(v) {
|
||||
delete(m.data, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *StrAnyMap) Set(key string, val interface{}) {
|
||||
m.mu.Lock()
|
||||
|
||||
@ -96,6 +96,7 @@ func (m *StrIntMap) MapCopy() map[string]int {
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *StrIntMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
|
||||
@ -97,6 +97,7 @@ func (m *StrStrMap) MapCopy() map[string]string {
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *StrStrMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
|
||||
@ -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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
)
|
||||
@ -59,13 +66,97 @@ func Example_normalBasic() {
|
||||
m.Clear()
|
||||
|
||||
fmt.Println(m.Size())
|
||||
|
||||
}
|
||||
|
||||
func Example_keysValues() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Keys())
|
||||
fmt.Println(m.Values())
|
||||
|
||||
// May Output:
|
||||
// [k1 k2 k3 k4]
|
||||
// [v2 v3 v4 v1]
|
||||
}
|
||||
|
||||
func Example_flip() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
})
|
||||
m.Flip()
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// May Output:
|
||||
// map[v1:k1 v2:k2]
|
||||
}
|
||||
|
||||
func Example_pop() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Pop())
|
||||
fmt.Println(m.Pops(2))
|
||||
fmt.Println(m.Size())
|
||||
|
||||
// May Output:
|
||||
// k1 v1
|
||||
// map[k2:v2 k4:v4]
|
||||
// 1
|
||||
}
|
||||
|
||||
func Example_filter() {
|
||||
m1 := gmap.NewFrom(g.MapAnyAny{
|
||||
"k1": "",
|
||||
"k2": nil,
|
||||
"k3": 0,
|
||||
"k4": 1,
|
||||
})
|
||||
m2 := gmap.NewFrom(g.MapAnyAny{
|
||||
"k1": "",
|
||||
"k2": nil,
|
||||
"k3": 0,
|
||||
"k4": 1,
|
||||
})
|
||||
m1.FilterEmpty()
|
||||
m2.FilterNil()
|
||||
fmt.Println(m1.Map())
|
||||
fmt.Println(m2.Map())
|
||||
|
||||
// Output:
|
||||
// map[k4:1]
|
||||
// map[k1: k3:0 k4:1]
|
||||
}
|
||||
|
||||
func Example_setIfNotExist() {
|
||||
var m gmap.Map
|
||||
fmt.Println(m.SetIfNotExist("k1", "v1"))
|
||||
fmt.Println(m.SetIfNotExist("k1", "v1"))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[k1:v1]
|
||||
}
|
||||
|
||||
func Example_normalMerge() {
|
||||
m1 := gmap.New()
|
||||
m2 := gmap.New()
|
||||
var m1, m2 gmap.Map
|
||||
m1.Set("key1", "val1")
|
||||
m2.Set("key2", "val2")
|
||||
m1.Merge(m2)
|
||||
m1.Merge(&m2)
|
||||
fmt.Println(m1.Map())
|
||||
|
||||
// May Output:
|
||||
// map[key1:val1 key2:val2]
|
||||
}
|
||||
|
||||
@ -9,12 +9,12 @@ package gmap_test
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_AnyAnyMap_Var(t *testing.T) {
|
||||
@ -218,6 +218,18 @@ func Test_AnyAnyMap_FilterEmpty(t *testing.T) {
|
||||
t.Assert(m.Get(1), nil)
|
||||
t.Assert(m.Get(2), 2)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewAnyAnyMap()
|
||||
m.Set(1, 0)
|
||||
m.Set("time1", time.Time{})
|
||||
m.Set("time2", time.Now())
|
||||
t.Assert(m.Get(1), 0)
|
||||
t.Assert(m.Get("time1"), time.Time{})
|
||||
m.FilterEmpty()
|
||||
t.Assert(m.Get(1), nil)
|
||||
t.Assert(m.Get("time1"), nil)
|
||||
t.AssertNE(m.Get("time2"), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Json(t *testing.T) {
|
||||
|
||||
@ -61,12 +61,12 @@ func (set *Set) Iterator(f func(v interface{}) bool) {
|
||||
}
|
||||
|
||||
// Add adds one or multiple items to the set.
|
||||
func (set *Set) Add(item ...interface{}) {
|
||||
func (set *Set) Add(items ...interface{}) {
|
||||
set.mu.Lock()
|
||||
if set.data == nil {
|
||||
set.data = make(map[interface{}]struct{})
|
||||
}
|
||||
for _, v := range item {
|
||||
for _, v := range items {
|
||||
set.data[v] = struct{}{}
|
||||
}
|
||||
set.mu.Unlock()
|
||||
@ -456,9 +456,11 @@ func (set *Set) Pops(size int) []interface{} {
|
||||
func (set *Set) Walk(f func(item interface{}) interface{}) *Set {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
m := make(map[interface{}]struct{}, len(set.data))
|
||||
for k, v := range set.data {
|
||||
set.data[f(k)] = v
|
||||
m[f(k)] = v
|
||||
}
|
||||
set.data = m
|
||||
return set
|
||||
}
|
||||
|
||||
|
||||
@ -416,9 +416,11 @@ func (set *IntSet) Pops(size int) []int {
|
||||
func (set *IntSet) Walk(f func(item int) int) *IntSet {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
m := make(map[int]struct{}, len(set.data))
|
||||
for k, v := range set.data {
|
||||
set.data[f(k)] = v
|
||||
m[f(k)] = v
|
||||
}
|
||||
set.data = m
|
||||
return set
|
||||
}
|
||||
|
||||
|
||||
@ -444,9 +444,11 @@ func (set *StrSet) Pops(size int) []string {
|
||||
func (set *StrSet) Walk(f func(item string) string) *StrSet {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
m := make(map[string]struct{}, len(set.data))
|
||||
for k, v := range set.data {
|
||||
set.data[f(k)] = v
|
||||
m[f(k)] = v
|
||||
}
|
||||
set.data = m
|
||||
return set
|
||||
}
|
||||
|
||||
|
||||
119
container/gset/gset_z_example_test.go
Normal file
119
container/gset/gset_z_example_test.go
Normal file
@ -0,0 +1,119 @@
|
||||
// Copyright 2020 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gset_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/gset"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func Example_intersectDiffUnionComplement() {
|
||||
s1 := gset.NewFrom(g.Slice{1, 2, 3})
|
||||
s2 := gset.NewFrom(g.Slice{4, 5, 6})
|
||||
s3 := gset.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7})
|
||||
|
||||
fmt.Println(s3.Intersect(s1).Slice())
|
||||
fmt.Println(s3.Diff(s1).Slice())
|
||||
fmt.Println(s1.Union(s2).Slice())
|
||||
fmt.Println(s1.Complement(s3).Slice())
|
||||
|
||||
// May Output:
|
||||
// [2 3 1]
|
||||
// [5 6 7 4]
|
||||
// [6 1 2 3 4 5]
|
||||
// [4 5 6 7]
|
||||
}
|
||||
|
||||
func Example_isSubsetOf() {
|
||||
var s1, s2 gset.Set
|
||||
s1.Add(g.Slice{1, 2, 3}...)
|
||||
s2.Add(g.Slice{2, 3}...)
|
||||
fmt.Println(s1.IsSubsetOf(&s2))
|
||||
fmt.Println(s2.IsSubsetOf(&s1))
|
||||
|
||||
// Output:
|
||||
// false
|
||||
// true
|
||||
}
|
||||
|
||||
func Example_addIfNotExist() {
|
||||
var set gset.Set
|
||||
fmt.Println(set.AddIfNotExist(1))
|
||||
fmt.Println(set.AddIfNotExist(1))
|
||||
fmt.Println(set.Slice())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// [1]
|
||||
}
|
||||
|
||||
func Example_pop() {
|
||||
var set gset.Set
|
||||
set.Add(1, 2, 3, 4)
|
||||
fmt.Println(set.Pop())
|
||||
fmt.Println(set.Pops(2))
|
||||
fmt.Println(set.Size())
|
||||
|
||||
// May Output:
|
||||
// 1
|
||||
// [2 3]
|
||||
// 1
|
||||
}
|
||||
|
||||
func Example_join() {
|
||||
var set gset.Set
|
||||
set.Add("a", "b", "c", "d")
|
||||
fmt.Println(set.Join(","))
|
||||
|
||||
// May Output:
|
||||
// a,b,c,d
|
||||
}
|
||||
|
||||
func Example_contains() {
|
||||
var set gset.StrSet
|
||||
set.Add("a")
|
||||
fmt.Println(set.Contains("a"))
|
||||
fmt.Println(set.Contains("A"))
|
||||
fmt.Println(set.ContainsI("A"))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
}
|
||||
|
||||
func Example_Contains() {
|
||||
var set gset.StrSet
|
||||
set.Add("a")
|
||||
fmt.Println(set.Contains("a"))
|
||||
fmt.Println(set.Contains("A"))
|
||||
fmt.Println(set.ContainsI("A"))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
}
|
||||
|
||||
func Example_walk() {
|
||||
var (
|
||||
set gset.StrSet
|
||||
names = g.SliceStr{"user", "user_detail"}
|
||||
prefix = "gf_"
|
||||
)
|
||||
set.Add(names...)
|
||||
// Add prefix for given table names.
|
||||
set.Walk(func(item string) string {
|
||||
return prefix + item
|
||||
})
|
||||
fmt.Println(set.Slice())
|
||||
|
||||
// May Output:
|
||||
// [gf_user gf_user_detail]
|
||||
}
|
||||
@ -389,6 +389,19 @@ func TestSet_AddIfNotExistFunc(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var set gset.Set
|
||||
set.Add(g.Slice{1, 2}...)
|
||||
set.Walk(func(item interface{}) interface{} {
|
||||
return gconv.Int(item) + 10
|
||||
})
|
||||
t.Assert(set.Size(), 2)
|
||||
t.Assert(set.Contains(11), true)
|
||||
t.Assert(set.Contains(12), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_AddIfNotExistFuncLock(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.New(true)
|
||||
|
||||
@ -377,6 +377,19 @@ func TestIntSet_Json(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntSet_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var set gset.IntSet
|
||||
set.Add(g.SliceInt{1, 2}...)
|
||||
set.Walk(func(item int) int {
|
||||
return item + 10
|
||||
})
|
||||
t.Assert(set.Size(), 2)
|
||||
t.Assert(set.Contains(11), true)
|
||||
t.Assert(set.Contains(12), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntSet_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
|
||||
@ -423,6 +423,24 @@ func TestStrSet_Json(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrSet_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
set gset.StrSet
|
||||
names = g.SliceStr{"user", "user_detail"}
|
||||
prefix = "gf_"
|
||||
)
|
||||
set.Add(names...)
|
||||
// Add prefix for given table names.
|
||||
set.Walk(func(item string) string {
|
||||
return prefix + item
|
||||
})
|
||||
t.Assert(set.Size(), 2)
|
||||
t.Assert(set.Contains("gf_user"), true)
|
||||
t.Assert(set.Contains("gf_user_detail"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrSet_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
|
||||
@ -22,7 +22,7 @@ func (v *Var) MapStrStr(tags ...string) map[string]string {
|
||||
func (v *Var) MapStrVar(tags ...string) map[string]*Var {
|
||||
m := v.Map(tags...)
|
||||
if len(m) > 0 {
|
||||
vMap := make(map[string]*Var)
|
||||
vMap := make(map[string]*Var, len(m))
|
||||
for k, v := range m {
|
||||
vMap[k] = New(v)
|
||||
}
|
||||
@ -45,7 +45,7 @@ func (v *Var) MapStrStrDeep(tags ...string) map[string]string {
|
||||
func (v *Var) MapStrVarDeep(tags ...string) map[string]*Var {
|
||||
m := v.MapDeep(tags...)
|
||||
if len(m) > 0 {
|
||||
vMap := make(map[string]*Var)
|
||||
vMap := make(map[string]*Var, len(m))
|
||||
for k, v := range m {
|
||||
vMap[k] = New(v)
|
||||
}
|
||||
@ -60,26 +60,26 @@ func (v *Var) Maps(tags ...string) []map[string]interface{} {
|
||||
return gconv.Maps(v.Val(), tags...)
|
||||
}
|
||||
|
||||
// MapToMap converts map type variable <params> to another map type variable <pointer>.
|
||||
// MapToMap converts any map type variable <params> to another map type variable <pointer>.
|
||||
// See gconv.MapToMap.
|
||||
func (v *Var) MapToMap(pointer interface{}) (err error) {
|
||||
return gconv.MapToMap(v.Val(), pointer)
|
||||
}
|
||||
|
||||
// MapToMapDeep converts map type variable <params> to another map type variable
|
||||
// MapToMapDeep converts any map type variable <params> to another map type variable
|
||||
// <pointer> recursively.
|
||||
// See gconv.MapToMapDeep.
|
||||
func (v *Var) MapToMapDeep(pointer interface{}) (err error) {
|
||||
return gconv.MapToMapDeep(v.Val(), pointer)
|
||||
}
|
||||
|
||||
// MapToMaps converts map type variable <params> to another map type variable <pointer>.
|
||||
// MapToMaps converts any map type variable <params> to another map type variable <pointer>.
|
||||
// See gconv.MapToMaps.
|
||||
func (v *Var) MapToMaps(pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
return gconv.MapToMaps(v.Val(), pointer, mapping...)
|
||||
}
|
||||
|
||||
// MapToMapsDeep converts map type variable <params> to another map type variable
|
||||
// MapToMapsDeep converts any map type variable <params> to another map type variable
|
||||
// <pointer> recursively.
|
||||
// See gconv.MapToMapsDeep.
|
||||
func (v *Var) MapToMapsDeep(pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
|
||||
@ -734,9 +734,11 @@ func (c *Core) rowsToResult(rows *sql.Rows) (Result, error) {
|
||||
columnTypes[k] = v.DatabaseTypeName()
|
||||
columnNames[k] = v.Name()
|
||||
}
|
||||
values := make([]sql.RawBytes, len(columnNames))
|
||||
records := make(Result, 0)
|
||||
scanArgs := make([]interface{}, len(values))
|
||||
var (
|
||||
values = make([]sql.RawBytes, len(columnNames))
|
||||
records = make(Result, 0)
|
||||
scanArgs = make([]interface{}, len(values))
|
||||
)
|
||||
for i := range values {
|
||||
scanArgs[i] = &values[i]
|
||||
}
|
||||
|
||||
@ -39,10 +39,10 @@ type ConfigNode struct {
|
||||
DryRun bool // (Optional) Dry run, which does SELECT but no INSERT/UPDATE/DELETE statements.
|
||||
Weight int // (Optional) Weight for load balance calculating, it's useless if there's just one node.
|
||||
Charset string // (Optional, "utf8mb4" in default) Custom charset when operating on database.
|
||||
LinkInfo string // (Optional) Custom link information, when it is used, configuration Host/Port/User/Pass/Name are ignored.
|
||||
MaxIdleConnCount int // (Optional) Max idle connection configuration for underlying connection pool.
|
||||
MaxOpenConnCount int // (Optional) Max open connection configuration for underlying connection pool.
|
||||
MaxConnLifetime time.Duration // (Optional) Max connection TTL configuration for underlying connection pool.
|
||||
LinkInfo string `json:"link"` // (Optional) Custom link information, when it is used, configuration Host/Port/User/Pass/Name are ignored.
|
||||
MaxIdleConnCount int `json:"maxidle"` // (Optional) Max idle connection configuration for underlying connection pool.
|
||||
MaxOpenConnCount int `json:"maxopen"` // (Optional) Max open connection configuration for underlying connection pool.
|
||||
MaxConnLifetime time.Duration `json:"maxlifetime"` // (Optional) Max connection TTL configuration for underlying connection pool.
|
||||
}
|
||||
|
||||
// configs is internal used configuration object.
|
||||
|
||||
@ -248,6 +248,9 @@ func GetPrimaryKeyCondition(primary string, where ...interface{}) (newWhereCondi
|
||||
// The internal handleArguments function might be called twice during the SQL procedure,
|
||||
// but do not worry about it, it's safe and efficient.
|
||||
func formatSql(sql string, args []interface{}) (newQuery string, newArgs []interface{}) {
|
||||
sql = gstr.Trim(sql)
|
||||
sql = gstr.Replace(sql, "\n", " ")
|
||||
sql, _ = gregex.ReplaceString(`\s{2,}`, ` `, sql)
|
||||
return handleArguments(sql, args)
|
||||
}
|
||||
|
||||
@ -408,11 +411,15 @@ func formatWhereKeyValue(db DB, buffer *bytes.Buffer, newArgs []interface{}, key
|
||||
// underlying driver.
|
||||
func handleArguments(sql string, args []interface{}) (newSql string, newArgs []interface{}) {
|
||||
newSql = sql
|
||||
// insertHolderCount is used to calculate the inserting position for the '?' holder.
|
||||
insertHolderCount := 0
|
||||
// Handles the slice arguments.
|
||||
if len(args) > 0 {
|
||||
for index, arg := range args {
|
||||
rv := reflect.ValueOf(arg)
|
||||
kind := rv.Kind()
|
||||
var (
|
||||
rv = reflect.ValueOf(arg)
|
||||
kind = rv.Kind()
|
||||
)
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
@ -428,17 +435,25 @@ func handleArguments(sql string, args []interface{}) (newSql string, newArgs []i
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
newArgs = append(newArgs, rv.Index(i).Interface())
|
||||
}
|
||||
// It the '?' holder count equals the length of the slice,
|
||||
// If the '?' holder count equals the length of the slice,
|
||||
// it does not implement the arguments splitting logic.
|
||||
// Eg: db.Query("SELECT ?+?", g.Slice{1, 2})
|
||||
if len(args) == 1 && gstr.Count(newSql, "?") == rv.Len() {
|
||||
break
|
||||
}
|
||||
// counter is used to finding the inserting position for the '?' holder.
|
||||
counter := 0
|
||||
var (
|
||||
counter = 0
|
||||
replaced = false
|
||||
)
|
||||
newSql, _ = gregex.ReplaceStringFunc(`\?`, newSql, func(s string) string {
|
||||
if replaced {
|
||||
return s
|
||||
}
|
||||
counter++
|
||||
if counter == index+1 {
|
||||
if counter == index+insertHolderCount+1 {
|
||||
replaced = true
|
||||
insertHolderCount += rv.Len() - 1
|
||||
return "?" + strings.Repeat(",?", rv.Len()-1)
|
||||
}
|
||||
return s
|
||||
|
||||
@ -12,14 +12,10 @@ import (
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
)
|
||||
|
||||
// Unscoped enables/disables the soft deleting feature.
|
||||
func (m *Model) Unscoped(unscoped ...bool) *Model {
|
||||
// Unscoped disables the soft deleting feature.
|
||||
func (m *Model) Unscoped() *Model {
|
||||
model := m.getModel()
|
||||
if len(unscoped) > 0 {
|
||||
model.unscoped = unscoped[0]
|
||||
} else {
|
||||
model.unscoped = true
|
||||
}
|
||||
model.unscoped = true
|
||||
return model
|
||||
}
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ import (
|
||||
)
|
||||
|
||||
// Filter marks filtering the fields which does not exist in the fields of the operated table.
|
||||
// Note that this function supports only single table operations.
|
||||
func (m *Model) Filter() *Model {
|
||||
if gstr.Contains(m.tables, " ") {
|
||||
panic("function Filter supports only single table operations")
|
||||
@ -30,6 +31,7 @@ func (m *Model) Fields(fields string) *Model {
|
||||
}
|
||||
|
||||
// FieldsEx sets the excluded operation fields of the model, multiple fields joined using char ','.
|
||||
// Note that this function supports only single table operations.
|
||||
func (m *Model) FieldsEx(fields string) *Model {
|
||||
if gstr.Contains(m.tables, " ") {
|
||||
panic("function FieldsEx supports only single table operations")
|
||||
|
||||
@ -63,10 +63,18 @@ func (m *Model) doFilterDataMapForInsertOrUpdate(data Map, allowOmitEmpty bool)
|
||||
if r.IsZero() {
|
||||
continue
|
||||
}
|
||||
case *time.Time:
|
||||
if r.IsZero() {
|
||||
continue
|
||||
}
|
||||
case gtime.Time:
|
||||
if r.IsZero() {
|
||||
continue
|
||||
}
|
||||
case *gtime.Time:
|
||||
if r.IsZero() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
tempMap[k] = v
|
||||
}
|
||||
|
||||
@ -25,22 +25,47 @@ func (c *Core) convertValue(fieldValue []byte, fieldType string) interface{} {
|
||||
t, _ := gregex.ReplaceString(`\(.+\)`, "", fieldType)
|
||||
t = strings.ToLower(t)
|
||||
switch t {
|
||||
case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob":
|
||||
case
|
||||
"binary",
|
||||
"varbinary",
|
||||
"blob",
|
||||
"tinyblob",
|
||||
"mediumblob",
|
||||
"longblob":
|
||||
return fieldValue
|
||||
|
||||
case "int", "tinyint", "small_int", "smallint", "medium_int", "mediumint":
|
||||
case
|
||||
"int",
|
||||
"tinyint",
|
||||
"small_int",
|
||||
"smallint",
|
||||
"medium_int",
|
||||
"mediumint",
|
||||
"serial":
|
||||
if gstr.ContainsI(fieldType, "unsigned") {
|
||||
gconv.Uint(string(fieldValue))
|
||||
}
|
||||
return gconv.Int(string(fieldValue))
|
||||
|
||||
case "big_int", "bigint":
|
||||
case
|
||||
"big_int",
|
||||
"bigint",
|
||||
"bigserial":
|
||||
if gstr.ContainsI(fieldType, "unsigned") {
|
||||
gconv.Uint64(string(fieldValue))
|
||||
}
|
||||
return gconv.Int64(string(fieldValue))
|
||||
|
||||
case "float", "double", "decimal":
|
||||
case "real":
|
||||
return gconv.Float32(string(fieldValue))
|
||||
|
||||
case
|
||||
"float",
|
||||
"double",
|
||||
"decimal",
|
||||
"money",
|
||||
"numeric",
|
||||
"smallmoney":
|
||||
return gconv.Float64(string(fieldValue))
|
||||
|
||||
case "bit":
|
||||
@ -61,17 +86,19 @@ func (c *Core) convertValue(fieldValue []byte, fieldType string) interface{} {
|
||||
t, _ := gtime.StrToTime(string(fieldValue))
|
||||
return t.Format("Y-m-d")
|
||||
|
||||
case "datetime", "timestamp":
|
||||
case
|
||||
"datetime",
|
||||
"timestamp":
|
||||
t, _ := gtime.StrToTime(string(fieldValue))
|
||||
return t.String()
|
||||
|
||||
default:
|
||||
// Auto detect field type, using key match.
|
||||
switch {
|
||||
case strings.Contains(t, "text") || strings.Contains(t, "char"):
|
||||
case strings.Contains(t, "text") || strings.Contains(t, "char") || strings.Contains(t, "character"):
|
||||
return string(fieldValue)
|
||||
|
||||
case strings.Contains(t, "float") || strings.Contains(t, "double"):
|
||||
case strings.Contains(t, "float") || strings.Contains(t, "double") || strings.Contains(t, "numeric"):
|
||||
return gconv.Float64(string(fieldValue))
|
||||
|
||||
case strings.Contains(t, "bool"):
|
||||
|
||||
40
database/gdb/gdb_z_example_test.go
Normal file
40
database/gdb/gdb_z_example_test.go
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func Example_transaction() {
|
||||
db.Transaction(func(tx *gdb.TX) error {
|
||||
// user
|
||||
result, err := tx.Insert("user", g.Map{
|
||||
"passport": "john",
|
||||
"password": "12345678",
|
||||
"nickname": "JohnGuo",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// user_detail
|
||||
id, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.Insert("user_detail", g.Map{
|
||||
"uid": id,
|
||||
"site": "https://johng.cn",
|
||||
"true_name": "GuoQiang",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@ -1023,15 +1023,6 @@ func Test_Model_Where(t *testing.T) {
|
||||
t.AssertGT(len(result), 0)
|
||||
t.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Table(table).Where(g.Map{
|
||||
"id": g.Slice{1, 2, 3},
|
||||
"passport": g.Slice{"user_2", "user_3"},
|
||||
}).Or("nickname=?", g.Slice{"name_4"}).And("id", 3).One()
|
||||
t.Assert(err, nil)
|
||||
t.AssertGT(len(result), 0)
|
||||
t.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Table(table).Where("id=3", g.Slice{}).One()
|
||||
t.Assert(err, nil)
|
||||
@ -1343,7 +1334,7 @@ func Test_Model_WherePri(t *testing.T) {
|
||||
}).Or("nickname=?", g.Slice{"name_4"}).And("id", 3).One()
|
||||
t.Assert(err, nil)
|
||||
t.AssertGT(len(result), 0)
|
||||
t.Assert(result["id"].Int(), 3)
|
||||
t.Assert(result["id"].Int(), 2)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Table(table).WherePri("id=3", g.Slice{}).One()
|
||||
@ -1833,6 +1824,32 @@ func Test_Model_Option_Where(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Where_MultiSliceArguments(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
r, err := db.Table(table).Where(g.Map{
|
||||
"id": g.Slice{1, 2, 3, 4},
|
||||
"passport": g.Slice{"user_2", "user_3", "user_4"},
|
||||
"nickname": g.Slice{"name_2", "name_4"},
|
||||
"id >= 4": nil,
|
||||
}).All()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(r), 1)
|
||||
t.Assert(r[0]["id"], 4)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Table(table).Where(g.Map{
|
||||
"id": g.Slice{1, 2, 3},
|
||||
"passport": g.Slice{"user_2", "user_3"},
|
||||
}).Or("nickname=?", g.Slice{"name_4"}).And("id", 3).One()
|
||||
t.Assert(err, nil)
|
||||
t.AssertGT(len(result), 0)
|
||||
t.Assert(result["id"].Int(), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_FieldsEx(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
@ -96,6 +96,150 @@ func Test_Model_Inherit_MapToStruct(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Struct_Pointer_Attribute(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
type User struct {
|
||||
Id *int
|
||||
Passport *string
|
||||
Password *string
|
||||
Nickname string
|
||||
}
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
user := new(User)
|
||||
err = one.Struct(user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(*user.Id, 1)
|
||||
t.Assert(*user.Passport, "user_1")
|
||||
t.Assert(*user.Password, "pass_1")
|
||||
t.Assert(user.Nickname, "name_1")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
user := new(User)
|
||||
err := db.Table(table).Struct(user, "id=1")
|
||||
t.Assert(err, nil)
|
||||
t.Assert(*user.Id, 1)
|
||||
t.Assert(*user.Passport, "user_1")
|
||||
t.Assert(*user.Password, "pass_1")
|
||||
t.Assert(user.Nickname, "name_1")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user *User
|
||||
err := db.Table(table).Struct(&user, "id=1")
|
||||
t.Assert(err, nil)
|
||||
t.Assert(*user.Id, 1)
|
||||
t.Assert(*user.Passport, "user_1")
|
||||
t.Assert(*user.Password, "pass_1")
|
||||
t.Assert(user.Nickname, "name_1")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Structs_Pointer_Attribute(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
type User struct {
|
||||
Id *int
|
||||
Passport *string
|
||||
Password *string
|
||||
Nickname string
|
||||
}
|
||||
// All
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).All("id < 3")
|
||||
t.Assert(err, nil)
|
||||
users := make([]User, 0)
|
||||
err = one.Structs(&users)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(*users[0].Id, 1)
|
||||
t.Assert(*users[0].Passport, "user_1")
|
||||
t.Assert(*users[0].Password, "pass_1")
|
||||
t.Assert(users[0].Nickname, "name_1")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).All("id < 3")
|
||||
t.Assert(err, nil)
|
||||
users := make([]*User, 0)
|
||||
err = one.Structs(&users)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(*users[0].Id, 1)
|
||||
t.Assert(*users[0].Passport, "user_1")
|
||||
t.Assert(*users[0].Password, "pass_1")
|
||||
t.Assert(users[0].Nickname, "name_1")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []User
|
||||
one, err := db.Table(table).All("id < 3")
|
||||
t.Assert(err, nil)
|
||||
err = one.Structs(&users)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(*users[0].Id, 1)
|
||||
t.Assert(*users[0].Passport, "user_1")
|
||||
t.Assert(*users[0].Password, "pass_1")
|
||||
t.Assert(users[0].Nickname, "name_1")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []*User
|
||||
one, err := db.Table(table).All("id < 3")
|
||||
t.Assert(err, nil)
|
||||
err = one.Structs(&users)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(*users[0].Id, 1)
|
||||
t.Assert(*users[0].Passport, "user_1")
|
||||
t.Assert(*users[0].Password, "pass_1")
|
||||
t.Assert(users[0].Nickname, "name_1")
|
||||
})
|
||||
// Structs
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
users := make([]User, 0)
|
||||
err := db.Table(table).Structs(&users, "id < 3")
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(*users[0].Id, 1)
|
||||
t.Assert(*users[0].Passport, "user_1")
|
||||
t.Assert(*users[0].Password, "pass_1")
|
||||
t.Assert(users[0].Nickname, "name_1")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
users := make([]*User, 0)
|
||||
err := db.Table(table).Structs(&users, "id < 3")
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(*users[0].Id, 1)
|
||||
t.Assert(*users[0].Passport, "user_1")
|
||||
t.Assert(*users[0].Password, "pass_1")
|
||||
t.Assert(users[0].Nickname, "name_1")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []User
|
||||
err := db.Table(table).Structs(&users, "id < 3")
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(*users[0].Id, 1)
|
||||
t.Assert(*users[0].Passport, "user_1")
|
||||
t.Assert(*users[0].Password, "pass_1")
|
||||
t.Assert(users[0].Nickname, "name_1")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []*User
|
||||
err := db.Table(table).Structs(&users, "id < 3")
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(*users[0].Id, 1)
|
||||
t.Assert(*users[0].Passport, "user_1")
|
||||
t.Assert(*users[0].Password, "pass_1")
|
||||
t.Assert(users[0].Nickname, "name_1")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Struct_Empty(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
@ -210,11 +210,15 @@ func (r *Redis) Do(command string, args ...interface{}) (interface{}, error) {
|
||||
return conn.Do(command, args...)
|
||||
}
|
||||
|
||||
// DoVar returns value from Do as gvar.Var.
|
||||
// DoVar returns value from Do as *gvar.Var.
|
||||
func (r *Redis) DoVar(command string, args ...interface{}) (*gvar.Var, error) {
|
||||
v, err := r.Do(command, args...)
|
||||
if result, ok := v.([]byte); ok {
|
||||
return gvar.New(gconv.UnsafeBytesToStr(result)), err
|
||||
}
|
||||
// It treats all returned slice as string slice.
|
||||
if result, ok := v.([]interface{}); ok {
|
||||
return gvar.New(gconv.Strings(result)), err
|
||||
}
|
||||
return gvar.New(v), err
|
||||
}
|
||||
|
||||
@ -6,9 +6,44 @@
|
||||
|
||||
package gredis
|
||||
|
||||
import "github.com/gogf/gf/container/gvar"
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// DoVar returns value from Do as gvar.Var.
|
||||
// Do sends a command to the server and returns the received reply.
|
||||
// It uses json.Marshal for struct/slice/map type values before committing them to redis.
|
||||
func (c *Conn) Do(commandName string, args ...interface{}) (reply interface{}, err error) {
|
||||
var (
|
||||
reflectValue reflect.Value
|
||||
reflectKind reflect.Kind
|
||||
)
|
||||
for k, v := range args {
|
||||
reflectValue = reflect.ValueOf(v)
|
||||
reflectKind = reflectValue.Kind()
|
||||
if reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
switch reflectKind {
|
||||
case
|
||||
reflect.Struct,
|
||||
reflect.Map,
|
||||
reflect.Slice,
|
||||
reflect.Array:
|
||||
// Ignore slice type of: []byte.
|
||||
if _, ok := v.([]byte); !ok {
|
||||
if args[k], err = json.Marshal(v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return c.Conn.Do(commandName, args...)
|
||||
}
|
||||
|
||||
// DoVar retrieves and returns the result from command as gvar.Var.
|
||||
func (c *Conn) DoVar(command string, args ...interface{}) (*gvar.Var, error) {
|
||||
v, err := c.Do(command, args...)
|
||||
return gvar.New(v), err
|
||||
|
||||
89
database/gredis/gredis_z_example_test.go
Normal file
89
database/gredis/gredis_z_example_test.go
Normal file
@ -0,0 +1,89 @@
|
||||
// Copyright 2020 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 gredis_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func Example_autoMarshalUnmarshalMap() {
|
||||
var (
|
||||
err error
|
||||
result *gvar.Var
|
||||
key = "user"
|
||||
data = g.Map{
|
||||
"id": 10000,
|
||||
"name": "john",
|
||||
}
|
||||
)
|
||||
_, err = g.Redis().Do("SET", key, data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
result, err = g.Redis().DoVar("GET", key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(result.Map())
|
||||
}
|
||||
|
||||
func Example_autoMarshalUnmarshalStruct() {
|
||||
type User struct {
|
||||
Id int
|
||||
Name string
|
||||
}
|
||||
var (
|
||||
err error
|
||||
result *gvar.Var
|
||||
key = "user"
|
||||
user = &User{
|
||||
Id: 10000,
|
||||
Name: "john",
|
||||
}
|
||||
)
|
||||
|
||||
_, err = g.Redis().Do("SET", key, user)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
result, err = g.Redis().DoVar("GET", key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var user2 *User
|
||||
if err = result.Struct(&user2); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(user2.Id, user2.Name)
|
||||
}
|
||||
|
||||
func Example_hashSet() {
|
||||
var (
|
||||
err error
|
||||
result *gvar.Var
|
||||
key = "user"
|
||||
)
|
||||
_, err = g.Redis().Do("HSET", key, "id", 10000)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = g.Redis().Do("HSET", key, "name", "john")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
result, err = g.Redis().DoVar("HGETALL", key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(result.Map())
|
||||
|
||||
// May Output:
|
||||
// map[id:10000 name:john]
|
||||
}
|
||||
@ -8,7 +8,7 @@ package gredis_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/guuid"
|
||||
"github.com/gogf/gf/util/guid"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -234,7 +234,7 @@ func Test_Bool(t *testing.T) {
|
||||
func Test_Int(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
redis := gredis.New(config)
|
||||
key := guuid.New()
|
||||
key := guid.S()
|
||||
defer redis.Do("DEL", key)
|
||||
|
||||
_, err := redis.Do("SET", key, 1)
|
||||
@ -249,7 +249,7 @@ func Test_Int(t *testing.T) {
|
||||
func Test_HSet(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
redis := gredis.New(config)
|
||||
key := guuid.New()
|
||||
key := guid.S()
|
||||
defer redis.Do("DEL", key)
|
||||
|
||||
_, err := redis.Do("HSET", key, "name", "john")
|
||||
@ -261,14 +261,14 @@ func Test_HSet(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_HGetAll(t *testing.T) {
|
||||
func Test_HGetAll1(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var err error
|
||||
redis := gredis.New(config)
|
||||
key := guuid.New()
|
||||
key := guid.S()
|
||||
defer redis.Do("DEL", key)
|
||||
|
||||
_, err = redis.Do("HSET", key, "id", "100")
|
||||
_, err = redis.Do("HSET", key, "id", 100)
|
||||
t.Assert(err, nil)
|
||||
_, err = redis.Do("HSET", key, "name", "john")
|
||||
t.Assert(err, nil)
|
||||
@ -281,3 +281,61 @@ func Test_HGetAll(t *testing.T) {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_HGetAll2(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
err error
|
||||
key = guid.S()
|
||||
redis = gredis.New(config)
|
||||
)
|
||||
defer redis.Do("DEL", key)
|
||||
|
||||
_, err = redis.Do("HSET", key, "id", 100)
|
||||
t.Assert(err, nil)
|
||||
_, err = redis.Do("HSET", key, "name", "john")
|
||||
t.Assert(err, nil)
|
||||
|
||||
result, err := redis.DoVar("HGETALL", key)
|
||||
t.Assert(err, nil)
|
||||
|
||||
t.Assert(gconv.Uint(result.MapStrVar()["id"]), 100)
|
||||
t.Assert(result.MapStrVar()["id"].Uint(), 100)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Auto_Marshal(t *testing.T) {
|
||||
var (
|
||||
err error
|
||||
redis = gredis.New(config)
|
||||
key = guid.S()
|
||||
)
|
||||
defer redis.Do("DEL", key)
|
||||
|
||||
type User struct {
|
||||
Id int
|
||||
Name string
|
||||
}
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
user := &User{
|
||||
Id: 10000,
|
||||
Name: "john",
|
||||
}
|
||||
|
||||
_, err = redis.Do("SET", key, user)
|
||||
t.Assert(err, nil)
|
||||
|
||||
r, err := redis.DoVar("GET", key)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(r.Map(), g.MapStrAny{
|
||||
"Id": user.Id,
|
||||
"Name": user.Name,
|
||||
})
|
||||
|
||||
var user2 *User
|
||||
t.Assert(r.Struct(&user2), nil)
|
||||
t.Assert(user2.Id, user.Id)
|
||||
t.Assert(user2.Name, user.Name)
|
||||
})
|
||||
}
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
package gbinary
|
||||
|
||||
// 实验特性
|
||||
// NOTE: THIS IS AN EXPERIMENTAL FEATURE!
|
||||
|
||||
// 二进制位(0|1)
|
||||
type Bit int8
|
||||
@ -4,7 +4,7 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package ghash provides some popular hash functions(uint32/uint64) in go.
|
||||
// Package ghash provides some classic hash functions(uint32/uint64) in go.
|
||||
package ghash
|
||||
|
||||
// BKDR Hash Function
|
||||
@ -160,7 +160,7 @@ func DJBHash(str []byte) uint32 {
|
||||
return hash
|
||||
}
|
||||
|
||||
// DJB Hash Function 64
|
||||
// DJB Hash Function 64.
|
||||
func DJBHash64(str []byte) uint64 {
|
||||
var hash uint64 = 5381
|
||||
for i := 0; i < len(str); i++ {
|
||||
|
||||
@ -203,7 +203,7 @@ func checkDataType(content []byte) string {
|
||||
return "yml"
|
||||
} else if (gregex.IsMatch(`^[\s\t\[*\]].?*[\w\-]+\s*=\s*.+`, content) || gregex.IsMatch(`\n[\s\t\[*\]]*[\w\-]+\s*=\s*.+`, content)) && gregex.IsMatch(`\n[\s\t]*[\w\-]+\s*=*\"*.+\"`, content) == false && gregex.IsMatch(`^[\s\t]*[\w\-]+\s*=*\"*.+\"`, content) == false {
|
||||
return "ini"
|
||||
} else if gregex.IsMatch(`^[\s\t]*[\w\-]+\s*=\s*.+`, content) || gregex.IsMatch(`\n[\s\t]*[\w\-]+\s*=\s*.+`, content) {
|
||||
} else if gregex.IsMatch(`^[\s\t]*[\w\-\."]+\s*=\s*.+`, content) || gregex.IsMatch(`\n[\s\t]*[\w\-\."]+\s*=\s*.+`, content) {
|
||||
return "toml"
|
||||
} else {
|
||||
return ""
|
||||
|
||||
@ -8,6 +8,7 @@ package gjson_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/encoding/gjson"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"testing"
|
||||
@ -44,5 +45,41 @@ func Test_ToJson(t *testing.T) {
|
||||
t.Assert(gstr.Contains(content, `"id":"g0936lt1u0f"`), true)
|
||||
t.Assert(gstr.Contains(content, `"new":"4"`), true)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Test_MapAttributeConvert(t *testing.T) {
|
||||
var data = `
|
||||
{
|
||||
"title": {"l1":"标签1","l2":"标签2"}
|
||||
}
|
||||
`
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
j, err := gjson.LoadContent(data)
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
tx := struct {
|
||||
Title map[string]interface{}
|
||||
}{}
|
||||
|
||||
err = j.ToStruct(&tx)
|
||||
gtest.Assert(err, nil)
|
||||
t.Assert(tx.Title, g.Map{
|
||||
"l1": "标签1", "l2": "标签2",
|
||||
})
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
j, err := gjson.LoadContent(data)
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
tx := struct {
|
||||
Title map[string]string
|
||||
}{}
|
||||
|
||||
err = j.ToStruct(&tx)
|
||||
gtest.Assert(err, nil)
|
||||
t.Assert(tx.Title, g.Map{
|
||||
"l1": "标签1", "l2": "标签2",
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ package gins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
|
||||
@ -58,6 +59,7 @@ func Database(name ...string) gdb.DB {
|
||||
}
|
||||
}
|
||||
if len(cg) > 0 {
|
||||
intlog.Printf("%s, %#v", group, cg)
|
||||
gdb.SetConfigGroup(group, cg)
|
||||
}
|
||||
}
|
||||
@ -69,6 +71,7 @@ func Database(name ...string) gdb.DB {
|
||||
cg = append(cg, *node)
|
||||
}
|
||||
if len(cg) > 0 {
|
||||
intlog.Printf("%s, %#v", gdb.DEFAULT_GROUP_NAME, cg)
|
||||
gdb.SetConfigGroup(gdb.DEFAULT_GROUP_NAME, cg)
|
||||
}
|
||||
}
|
||||
|
||||
3
go.mod
3
go.mod
@ -8,13 +8,12 @@ require (
|
||||
github.com/fsnotify/fsnotify v1.4.7
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/gomodule/redigo v2.0.0+incompatible
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/gorilla/websocket v1.4.1
|
||||
github.com/gqcn/structs v1.1.1
|
||||
github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.1
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d // indirect
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9 // indirect
|
||||
golang.org/x/text v0.3.2
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
|
||||
)
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
package gi18n
|
||||
|
||||
var (
|
||||
// defaultManager is the default i18n instance for package functions.
|
||||
defaultManager = Instance()
|
||||
)
|
||||
|
||||
@ -26,7 +27,7 @@ func SetDelimiters(left, right string) {
|
||||
defaultManager.SetDelimiters(left, right)
|
||||
}
|
||||
|
||||
// T is alias of Translate.
|
||||
// T is alias of Translate for convenience.
|
||||
func T(content string, language ...string) string {
|
||||
return defaultManager.T(content, language...)
|
||||
}
|
||||
@ -36,3 +37,9 @@ func T(content string, language ...string) string {
|
||||
func Translate(content string, language ...string) string {
|
||||
return defaultManager.Translate(content, language...)
|
||||
}
|
||||
|
||||
// GetValue retrieves and returns the configured content for given key and specified language.
|
||||
// It returns an empty string if not found.
|
||||
func GetContent(key string, language ...string) string {
|
||||
return defaultManager.GetContent(key, language...)
|
||||
}
|
||||
|
||||
@ -14,7 +14,8 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
// Instances map.
|
||||
// instances is the instances map for management
|
||||
// for multiple i18n instance by name.
|
||||
instances = gmap.NewStrAnyMap(true)
|
||||
)
|
||||
|
||||
|
||||
@ -43,11 +43,13 @@ type Options struct {
|
||||
}
|
||||
|
||||
var (
|
||||
// defaultDelimiters defines the key variable delimiters.
|
||||
// defaultDelimiters defines the default key variable delimiters.
|
||||
defaultDelimiters = []string{"{#", "}"}
|
||||
)
|
||||
|
||||
// New creates and returns a new i18n manager.
|
||||
// The optional parameter <option> specifies the custom options for i18n manager.
|
||||
// It uses a default one if it's not passed.
|
||||
func New(options ...Options) *Manager {
|
||||
var opts Options
|
||||
if len(options) > 0 {
|
||||
@ -66,24 +68,27 @@ func New(options ...Options) *Manager {
|
||||
gregex.Quote(opts.Delimiters[1]),
|
||||
),
|
||||
}
|
||||
intlog.Printf(`New: %+v`, m)
|
||||
intlog.Printf(`New: %#v`, m)
|
||||
return m
|
||||
}
|
||||
|
||||
// DefaultOptions returns the default options for i18n manager.
|
||||
// DefaultOptions creates and returns a default options for i18n manager.
|
||||
func DefaultOptions() Options {
|
||||
path := "i18n"
|
||||
realPath, _ := gfile.Search(path)
|
||||
var (
|
||||
path = "i18n"
|
||||
realPath, _ = gfile.Search(path)
|
||||
)
|
||||
if realPath != "" {
|
||||
path = realPath
|
||||
// To avoid of the source of GF: github.com/gogf/i18n/gi18n
|
||||
// To avoid of the source path of GF: github.com/gogf/i18n/gi18n
|
||||
if gfile.Exists(path + gfile.Separator + "gi18n") {
|
||||
path = ""
|
||||
}
|
||||
}
|
||||
return Options{
|
||||
Path: path,
|
||||
Delimiters: []string{"{#", "}"},
|
||||
Language: "en",
|
||||
Delimiters: defaultDelimiters,
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,7 +119,7 @@ func (m *Manager) SetDelimiters(left, right string) {
|
||||
intlog.Printf(`SetDelimiters: %v`, m.pattern)
|
||||
}
|
||||
|
||||
// T is alias of Translate.
|
||||
// T is alias of Translate for convenience.
|
||||
func (m *Manager) T(content string, language ...string) string {
|
||||
return m.Translate(content, language...)
|
||||
}
|
||||
@ -140,20 +145,41 @@ func (m *Manager) Translate(content string, language ...string) string {
|
||||
return v
|
||||
}
|
||||
// Parse content as variables container.
|
||||
result, _ := gregex.ReplaceStringFuncMatch(m.pattern, content, func(match []string) string {
|
||||
if v, ok := data[match[1]]; ok {
|
||||
return v
|
||||
}
|
||||
return match[0]
|
||||
})
|
||||
result, _ := gregex.ReplaceStringFuncMatch(
|
||||
m.pattern, content,
|
||||
func(match []string) string {
|
||||
if v, ok := data[match[1]]; ok {
|
||||
return v
|
||||
}
|
||||
return match[0]
|
||||
})
|
||||
intlog.Printf(`Translate for language: %s`, transLang)
|
||||
return result
|
||||
}
|
||||
|
||||
// GetValue retrieves and returns the configured content for given key and specified language.
|
||||
// It returns an empty string if not found.
|
||||
func (m *Manager) GetContent(key string, language ...string) string {
|
||||
m.init()
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
transLang := m.options.Language
|
||||
if len(language) > 0 && language[0] != "" {
|
||||
transLang = language[0]
|
||||
} else {
|
||||
transLang = m.options.Language
|
||||
}
|
||||
if data, ok := m.data[transLang]; ok {
|
||||
return data[key]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// init initializes the manager for lazy initialization design.
|
||||
// The i18n manager is only initialized once.
|
||||
func (m *Manager) init() {
|
||||
m.mu.RLock()
|
||||
// If the data is not nil, means it's already initialized.
|
||||
if m.data != nil {
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
@ -165,10 +191,12 @@ func (m *Manager) init() {
|
||||
if gres.Contains(m.options.Path) {
|
||||
files := gres.ScanDirFile(m.options.Path, "*.*", true)
|
||||
if len(files) > 0 {
|
||||
var path string
|
||||
var name string
|
||||
var lang string
|
||||
var array []string
|
||||
var (
|
||||
path string
|
||||
name string
|
||||
lang string
|
||||
array []string
|
||||
)
|
||||
m.data = make(map[string]map[string]string)
|
||||
for _, file := range files {
|
||||
name = file.Name()
|
||||
@ -193,36 +221,45 @@ func (m *Manager) init() {
|
||||
}
|
||||
} else if m.options.Path != "" {
|
||||
files, _ := gfile.ScanDirFile(m.options.Path, "*.*", true)
|
||||
if len(files) > 0 {
|
||||
var path string
|
||||
var lang string
|
||||
var array []string
|
||||
m.data = make(map[string]map[string]string)
|
||||
for _, file := range files {
|
||||
path = file[len(m.options.Path)+1:]
|
||||
array = strings.Split(path, gfile.Separator)
|
||||
if len(array) > 1 {
|
||||
lang = array[0]
|
||||
} else {
|
||||
lang = gfile.Name(array[0])
|
||||
}
|
||||
if m.data[lang] == nil {
|
||||
m.data[lang] = make(map[string]string)
|
||||
}
|
||||
if j, err := gjson.LoadContent(gfile.GetBytes(file)); err == nil {
|
||||
for k, v := range j.ToMap() {
|
||||
m.data[lang][k] = gconv.String(v)
|
||||
}
|
||||
} else {
|
||||
glog.Errorf("load i18n file '%s' failed: %v", file, err)
|
||||
}
|
||||
}
|
||||
_, _ = gfsnotify.Add(path, func(event *gfsnotify.Event) {
|
||||
m.mu.Lock()
|
||||
m.data = nil
|
||||
m.mu.Unlock()
|
||||
gfsnotify.Exit()
|
||||
})
|
||||
if len(files) == 0 {
|
||||
intlog.Printf(
|
||||
"no i18n files found in configured directory: %s",
|
||||
m.options.Path,
|
||||
)
|
||||
return
|
||||
}
|
||||
var (
|
||||
path string
|
||||
lang string
|
||||
array []string
|
||||
)
|
||||
m.data = make(map[string]map[string]string)
|
||||
for _, file := range files {
|
||||
path = file[len(m.options.Path)+1:]
|
||||
array = strings.Split(path, gfile.Separator)
|
||||
if len(array) > 1 {
|
||||
lang = array[0]
|
||||
} else {
|
||||
lang = gfile.Name(array[0])
|
||||
}
|
||||
if m.data[lang] == nil {
|
||||
m.data[lang] = make(map[string]string)
|
||||
}
|
||||
if j, err := gjson.LoadContent(gfile.GetBytes(file)); err == nil {
|
||||
for k, v := range j.ToMap() {
|
||||
m.data[lang][k] = gconv.String(v)
|
||||
}
|
||||
} else {
|
||||
glog.Errorf("load i18n file '%s' failed: %v", file, err)
|
||||
}
|
||||
}
|
||||
// Monitor changes of i18n files for hot reload feature.
|
||||
_, _ = gfsnotify.Add(path, func(event *gfsnotify.Event) {
|
||||
// Any changes of i18n files, clear the data.
|
||||
m.mu.Lock()
|
||||
m.data = nil
|
||||
m.mu.Unlock()
|
||||
gfsnotify.Exit()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,10 +121,10 @@ func Test_Instance(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.I18n().T("{#hello}{#world}"), "你好世界")
|
||||
})
|
||||
|
||||
// Default language is: en
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gi18n.Instance(gconv.String(gtime.TimestampNano()))
|
||||
t.Assert(m.T("{#hello}{#world}"), "{#hello}{#world}")
|
||||
t.Assert(m.T("{#hello}{#world}"), "HelloWorld")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -4,13 +4,28 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package empty provides checks for empty variables.
|
||||
// Package empty provides functions for checking empty variables.
|
||||
package empty
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// apiString is used for type assert api for String().
|
||||
type apiString interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
// apiInterfaces is used for type assert api for Interfaces.
|
||||
type apiInterfaces interface {
|
||||
Interfaces() []interface{}
|
||||
}
|
||||
|
||||
// apiMapStrAny is the interface support for converting struct parameter to map.
|
||||
type apiMapStrAny interface {
|
||||
MapStrAny() map[string]interface{}
|
||||
}
|
||||
|
||||
// IsEmpty checks whether given <value> empty.
|
||||
// It returns true if <value> is in: 0, nil, false, "", len(slice/map/chan) == 0,
|
||||
// or else it returns false.
|
||||
@ -52,6 +67,16 @@ func IsEmpty(value interface{}) bool {
|
||||
case []rune:
|
||||
return len(value) == 0
|
||||
default:
|
||||
// Common interfaces checks.
|
||||
if f, ok := value.(apiString); ok {
|
||||
return f.String() == ""
|
||||
}
|
||||
if f, ok := value.(apiInterfaces); ok {
|
||||
return len(f.Interfaces()) == 0
|
||||
}
|
||||
if f, ok := value.(apiMapStrAny); ok {
|
||||
return len(f.MapStrAny()) == 0
|
||||
}
|
||||
// Finally using reflect.
|
||||
var rv reflect.Value
|
||||
if v, ok := value.(reflect.Value); ok {
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package intlog provides internal logging for GF development usage only.
|
||||
// Package intlog provides internal logging for GoFrame development usage only.
|
||||
package intlog
|
||||
|
||||
import (
|
||||
|
||||
@ -9,7 +9,7 @@ package mutex
|
||||
|
||||
import "sync"
|
||||
|
||||
// Mutex is a sync.Mutex with a switch of concurrent safe feature.
|
||||
// Mutex is a sync.Mutex with a switch for concurrent safe feature.
|
||||
type Mutex struct {
|
||||
sync.Mutex
|
||||
safe bool
|
||||
|
||||
@ -9,7 +9,7 @@ package rwmutex
|
||||
|
||||
import "sync"
|
||||
|
||||
// RWMutex is a sync.RWMutex with a switch of concurrent safe feature.
|
||||
// RWMutex is a sync.RWMutex with a switch for concurrent safe feature.
|
||||
// If its attribute *sync.RWMutex is not nil, it means it's in concurrent safety usage.
|
||||
// Its attribute *sync.RWMutex is nil in default, which makes this struct mush lightweight.
|
||||
type RWMutex struct {
|
||||
|
||||
63
internal/utils/utils_io.go
Normal file
63
internal/utils/utils_io.go
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright 2020 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 utils
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// ReadCloser implements the io.ReadCloser interface
|
||||
// which is used for reading request body content multiple times.
|
||||
//
|
||||
// Note that it cannot be closed.
|
||||
type ReadCloser struct {
|
||||
index int // Current read position.
|
||||
content []byte // Content.
|
||||
repeatable bool
|
||||
}
|
||||
|
||||
// NewRepeatReadCloser creates and returns a RepeatReadCloser object.
|
||||
func NewReadCloser(content []byte, repeatable bool) io.ReadCloser {
|
||||
return &ReadCloser{
|
||||
content: content,
|
||||
repeatable: repeatable,
|
||||
}
|
||||
}
|
||||
|
||||
// NewRepeatReadCloserWithReadCloser creates and returns a RepeatReadCloser object
|
||||
// with given io.ReadCloser.
|
||||
func NewReadCloserWithReadCloser(r io.ReadCloser, repeatable bool) (io.ReadCloser, error) {
|
||||
content, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer r.Close()
|
||||
return &ReadCloser{
|
||||
content: content,
|
||||
repeatable: repeatable,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read implements the io.ReadCloser interface.
|
||||
func (b *ReadCloser) Read(p []byte) (n int, err error) {
|
||||
n = copy(p, b.content[b.index:])
|
||||
b.index += n
|
||||
if b.index >= len(b.content) {
|
||||
// Make it repeatable reading.
|
||||
if b.repeatable {
|
||||
b.index = 0
|
||||
}
|
||||
return n, io.EOF
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Close implements the io.ReadCloser interface.
|
||||
func (b *ReadCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
65
internal/utils/utils_z_test.go
Normal file
65
internal/utils/utils_z_test.go
Normal file
@ -0,0 +1,65 @@
|
||||
// Copyright 2020 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 utils_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_ReadCloser(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
n int
|
||||
b = make([]byte, 3)
|
||||
body = utils.NewReadCloser([]byte{1, 2, 3, 4}, false)
|
||||
)
|
||||
n, _ = body.Read(b)
|
||||
t.Assert(b[:n], []byte{1, 2, 3})
|
||||
n, _ = body.Read(b)
|
||||
t.Assert(b[:n], []byte{4})
|
||||
|
||||
n, _ = body.Read(b)
|
||||
t.Assert(b[:n], []byte{})
|
||||
n, _ = body.Read(b)
|
||||
t.Assert(b[:n], []byte{})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
r []byte
|
||||
body = utils.NewReadCloser([]byte{1, 2, 3, 4}, false)
|
||||
)
|
||||
r, _ = ioutil.ReadAll(body)
|
||||
t.Assert(r, []byte{1, 2, 3, 4})
|
||||
r, _ = ioutil.ReadAll(body)
|
||||
t.Assert(r, []byte{})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
n int
|
||||
r []byte
|
||||
b = make([]byte, 3)
|
||||
body = utils.NewReadCloser([]byte{1, 2, 3, 4}, true)
|
||||
)
|
||||
n, _ = body.Read(b)
|
||||
t.Assert(b[:n], []byte{1, 2, 3})
|
||||
n, _ = body.Read(b)
|
||||
t.Assert(b[:n], []byte{4})
|
||||
|
||||
n, _ = body.Read(b)
|
||||
t.Assert(b[:n], []byte{1, 2, 3})
|
||||
n, _ = body.Read(b)
|
||||
t.Assert(b[:n], []byte{4})
|
||||
|
||||
r, _ = ioutil.ReadAll(body)
|
||||
t.Assert(r, []byte{1, 2, 3, 4})
|
||||
r, _ = ioutil.ReadAll(body)
|
||||
t.Assert(r, []byte{1, 2, 3, 4})
|
||||
})
|
||||
}
|
||||
@ -7,68 +7,41 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
// dumpTextFormat is the format of the dumped raw string
|
||||
const dumpTextFormat = `+---------------------------------------------+
|
||||
| %s |
|
||||
| %s |
|
||||
+---------------------------------------------+
|
||||
%s
|
||||
%s
|
||||
`
|
||||
|
||||
// ifDumpBody determines whether to output body according to content-type.
|
||||
func ifDumpBody(contentType string) bool {
|
||||
// the body should not be output when the body is html or stream.
|
||||
if gstr.Contains(contentType, "application/json") ||
|
||||
gstr.Contains(contentType, "application/xml") ||
|
||||
gstr.Contains(contentType, "multipart/form-data") ||
|
||||
gstr.Contains(contentType, "application/x-www-form-urlencoded") ||
|
||||
gstr.Contains(contentType, "text/plain") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// getRequestBody returns the raw text of the request body.
|
||||
func getRequestBody(req *http.Request) string {
|
||||
contentType := req.Header.Get("Content-Type")
|
||||
if !ifDumpBody(contentType) {
|
||||
if req.Body == nil {
|
||||
return ""
|
||||
}
|
||||
// so that the request body can be read again.
|
||||
bodyReader, errGetBody := req.GetBody()
|
||||
if errGetBody != nil {
|
||||
return ""
|
||||
}
|
||||
bytesBody, errReadBody := ioutil.ReadAll(bodyReader)
|
||||
if errReadBody != nil {
|
||||
return ""
|
||||
}
|
||||
return gconv.UnsafeBytesToStr(bytesBody)
|
||||
bodyContent, _ := ioutil.ReadAll(req.Body)
|
||||
req.Body = utils.NewReadCloser(bodyContent, true)
|
||||
return gconv.UnsafeBytesToStr(bodyContent)
|
||||
}
|
||||
|
||||
// getResponseBody returns the text of the response body.
|
||||
func getResponseBody(resp *http.Response) string {
|
||||
contentType := resp.Header.Get("Content-Type")
|
||||
if !ifDumpBody(contentType) {
|
||||
func getResponseBody(res *http.Response) string {
|
||||
if res.Body == nil {
|
||||
return ""
|
||||
}
|
||||
bytesBody, errReadBody := ioutil.ReadAll(resp.Body)
|
||||
if errReadBody != nil {
|
||||
return ""
|
||||
}
|
||||
// So the response body can be read again.
|
||||
resp.Body = ioutil.NopCloser(bytes.NewBuffer(bytesBody))
|
||||
return gconv.UnsafeBytesToStr(bytesBody)
|
||||
bodyContent, _ := ioutil.ReadAll(res.Body)
|
||||
res.Body = utils.NewReadCloser(bodyContent, true)
|
||||
return gconv.UnsafeBytesToStr(bodyContent)
|
||||
}
|
||||
|
||||
// RawRequest returns the raw content of the request.
|
||||
@ -116,3 +89,8 @@ func (r *ClientResponse) RawResponse() string {
|
||||
func (r *ClientResponse) Raw() string {
|
||||
return fmt.Sprintf("%s\n%s", r.RawRequest(), r.RawResponse())
|
||||
}
|
||||
|
||||
// RawDump outputs the raw text of the request and the response to stdout.
|
||||
func (r *ClientResponse) RawDump() {
|
||||
fmt.Println(r.Raw())
|
||||
}
|
||||
|
||||
@ -11,7 +11,9 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"os"
|
||||
@ -215,20 +217,25 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien
|
||||
if len(c.authUser) > 0 {
|
||||
req.SetBasicAuth(c.authUser, c.authPass)
|
||||
}
|
||||
// do not return nil even if the request fails
|
||||
resp = &ClientResponse{}
|
||||
resp = &ClientResponse{
|
||||
request: req,
|
||||
}
|
||||
// The request body can be reused for dumping
|
||||
// raw HTTP request-response procedure.
|
||||
reqBodyContent, _ := ioutil.ReadAll(req.Body)
|
||||
req.Body = utils.NewReadCloser(reqBodyContent, false)
|
||||
defer func() {
|
||||
resp.request.Body = utils.NewReadCloser(reqBodyContent, true)
|
||||
}()
|
||||
for {
|
||||
if resp.Response, err = c.Do(req); err != nil {
|
||||
if c.retryCount > 0 {
|
||||
c.retryCount--
|
||||
time.Sleep(c.retryInterval)
|
||||
} else {
|
||||
// we need a copy of the request when the request fails.
|
||||
resp.request = req
|
||||
return resp, err
|
||||
}
|
||||
} else {
|
||||
resp.request = resp.Request
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,12 +9,14 @@ package ghttp
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"github.com/gogf/gf/os/gres"
|
||||
"github.com/gogf/gf/os/gsession"
|
||||
"github.com/gogf/gf/os/gview"
|
||||
"github.com/gogf/gf/util/guid"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/os/gsession"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/text/gregex"
|
||||
@ -75,6 +77,18 @@ func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request {
|
||||
request.Middleware = &Middleware{
|
||||
request: request,
|
||||
}
|
||||
// Custom session id creating function.
|
||||
err := request.Session.SetIdFunc(func(ttl time.Duration) string {
|
||||
var (
|
||||
address = request.RemoteAddr
|
||||
header = fmt.Sprintf("%v", request.Header)
|
||||
)
|
||||
intlog.Print(address, header)
|
||||
return guid.S([]byte(address), []byte(header))
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return request
|
||||
}
|
||||
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// BodyReadCloser implements the io.ReadCloser interface
|
||||
// which is used for reading request body content multiple times.
|
||||
type BodyReadCloser struct {
|
||||
*bytes.Reader
|
||||
}
|
||||
|
||||
// RefillBody refills the request body object after read all of its content.
|
||||
// It makes the request body reusable for next reading.
|
||||
func (r *Request) RefillBody() {
|
||||
if r.bodyContent == nil {
|
||||
r.bodyContent, _ = ioutil.ReadAll(r.Body)
|
||||
}
|
||||
r.Body = &BodyReadCloser{
|
||||
bytes.NewReader(r.bodyContent),
|
||||
}
|
||||
}
|
||||
|
||||
// Close implements the io.ReadCloser interface.
|
||||
func (b *BodyReadCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
@ -14,6 +14,7 @@ import (
|
||||
"github.com/gogf/gf/encoding/gjson"
|
||||
"github.com/gogf/gf/encoding/gurl"
|
||||
"github.com/gogf/gf/encoding/gxml"
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"github.com/gogf/gf/text/gregex"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
@ -35,6 +36,10 @@ var (
|
||||
//
|
||||
// The parameter <pointer> can be type of: *struct/**struct/*[]struct/*[]*struct.
|
||||
//
|
||||
// It supports single and multiple struct convertion:
|
||||
// 1. Single struct, post content like: {"id":1, "name":"john"}
|
||||
// 2. Multiple struct, post content like: [{"id":1, "name":"john"}, {"id":, "name":"smith"}]
|
||||
//
|
||||
// TODO: Improve the performance by reducing duplicated reflect usage on the same variable across packages.
|
||||
func (r *Request) Parse(pointer interface{}) error {
|
||||
var (
|
||||
@ -52,15 +57,20 @@ func (r *Request) Parse(pointer interface{}) error {
|
||||
reflectKind2 = reflectVal2.Kind()
|
||||
)
|
||||
switch reflectKind2 {
|
||||
// Single struct, post content like:
|
||||
// {"id":1, "name":"john"}
|
||||
case reflect.Ptr, reflect.Struct:
|
||||
// Struct conversion.
|
||||
// Conversion.
|
||||
if err := r.GetStruct(pointer); err != nil {
|
||||
return err
|
||||
}
|
||||
// Struct validation.
|
||||
// Validation.
|
||||
if err := gvalid.CheckStruct(pointer, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Multiple struct, post content like:
|
||||
// [{"id":1, "name":"john"}, {"id":, "name":"smith"}]
|
||||
case reflect.Array, reflect.Slice:
|
||||
// If struct slice conversion, it might post JSON/XML content,
|
||||
// so it uses gjson for the conversion.
|
||||
@ -112,7 +122,7 @@ func (r *Request) GetRawString() string {
|
||||
func (r *Request) GetBody() []byte {
|
||||
if r.bodyContent == nil {
|
||||
r.bodyContent, _ = ioutil.ReadAll(r.Body)
|
||||
r.RefillBody()
|
||||
r.Body = utils.NewReadCloser(r.bodyContent, true)
|
||||
}
|
||||
return r.bodyContent
|
||||
}
|
||||
|
||||
@ -240,9 +240,9 @@ func Config() ServerConfig {
|
||||
HTTPSAddr: "",
|
||||
Handler: nil,
|
||||
ReadTimeout: 60 * time.Second,
|
||||
WriteTimeout: 60 * time.Second,
|
||||
WriteTimeout: 0, // No timeout.
|
||||
IdleTimeout: 60 * time.Second,
|
||||
MaxHeaderBytes: 1024,
|
||||
MaxHeaderBytes: 10240, // 10KB
|
||||
KeepAlive: true,
|
||||
IndexFiles: []string{"index.html", "index.htm"},
|
||||
IndexFolder: false,
|
||||
|
||||
@ -13,27 +13,29 @@ import (
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
)
|
||||
|
||||
// COOKIE对象,非并发安全。
|
||||
// Cookie for HTTP COOKIE management.
|
||||
type Cookie struct {
|
||||
data map[string]CookieItem // 数据项
|
||||
path string // 默认的cookie path
|
||||
domain string // 默认的cookie domain
|
||||
maxage time.Duration // 默认的cookie maxage
|
||||
server *Server // 所属Server
|
||||
request *Request // 所属HTTP请求对象
|
||||
response *Response // 所属HTTP返回对象
|
||||
data map[string]CookieItem // Underlying cookie items.
|
||||
path string // The default cookie path.
|
||||
domain string // The default cookie domain
|
||||
maxage time.Duration // The default cookie maxage.
|
||||
server *Server // Belonged HTTP server
|
||||
request *Request // Belonged HTTP request.
|
||||
response *Response // Belonged HTTP response.
|
||||
}
|
||||
|
||||
// cookie项
|
||||
// CookieItem is cookie item stored in Cookie management object.
|
||||
type CookieItem struct {
|
||||
value string
|
||||
domain string // 有效域名
|
||||
path string // 有效路径
|
||||
expireAt int64 // 过期时间
|
||||
value string // Cookie value.
|
||||
domain string // Cookie domain.
|
||||
path string // Cookie path.
|
||||
expireAt int64 // Cookie expiration timestamp.
|
||||
httpOnly bool
|
||||
}
|
||||
|
||||
// 获取或者创建一个COOKIE对象,与传入的请求对应(延迟初始化)
|
||||
// GetCookie creates or retrieves a cookie object with given request.
|
||||
// It retrieves and returns an existing cookie object if it already exists with given request.
|
||||
// It creates and returns a new cookie object if it does not exist with given request.
|
||||
func GetCookie(r *Request) *Cookie {
|
||||
if r.Cookie != nil {
|
||||
return r.Cookie
|
||||
@ -44,7 +46,7 @@ func GetCookie(r *Request) *Cookie {
|
||||
}
|
||||
}
|
||||
|
||||
// 从请求流中初始化,无锁,延迟初始化
|
||||
// init does lazy initialization for cookie object.
|
||||
func (c *Cookie) init() {
|
||||
if c.data == nil {
|
||||
c.data = make(map[string]CookieItem)
|
||||
@ -64,7 +66,7 @@ func (c *Cookie) init() {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取所有的Cookie并构造成map[string]string返回.
|
||||
// Map returns the cookie items as map[string]string.
|
||||
func (c *Cookie) Map() map[string]string {
|
||||
c.init()
|
||||
m := make(map[string]string)
|
||||
@ -74,7 +76,7 @@ func (c *Cookie) Map() map[string]string {
|
||||
return m
|
||||
}
|
||||
|
||||
// 判断Cookie中是否存在制定键名(并且没有过期)
|
||||
// Contains checks if given key exists and not expired in cookie.
|
||||
func (c *Cookie) Contains(key string) bool {
|
||||
c.init()
|
||||
if r, ok := c.data[key]; ok {
|
||||
@ -85,12 +87,14 @@ func (c *Cookie) Contains(key string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// 设置cookie,使用默认参数
|
||||
// Set sets cookie item with default domain, path and expiration age.
|
||||
func (c *Cookie) Set(key, value string) {
|
||||
c.SetCookie(key, value, c.domain, c.path, c.server.GetCookieMaxAge())
|
||||
}
|
||||
|
||||
// 设置cookie,带详细cookie参数
|
||||
// SetCookie sets cookie item given given domain, path and expiration age.
|
||||
// The optional parameter <httpOnly> specifies if the cookie item is only available in HTTP,
|
||||
// which is usually empty.
|
||||
func (c *Cookie) SetCookie(key, value, domain, path string, maxAge time.Duration, httpOnly ...bool) {
|
||||
c.init()
|
||||
isHttpOnly := false
|
||||
@ -102,17 +106,18 @@ func (c *Cookie) SetCookie(key, value, domain, path string, maxAge time.Duration
|
||||
}
|
||||
}
|
||||
|
||||
// 获得客户端提交的SessionId
|
||||
// GetSessionId retrieves and returns the session id from cookie.
|
||||
func (c *Cookie) GetSessionId() string {
|
||||
return c.Get(c.server.GetSessionIdName())
|
||||
}
|
||||
|
||||
// 设置SessionId到Cookie中
|
||||
// SetSessionId sets session id in the cookie.
|
||||
func (c *Cookie) SetSessionId(id string) {
|
||||
c.Set(c.server.GetSessionIdName(), id)
|
||||
}
|
||||
|
||||
// 查询cookie
|
||||
// Get retrieves and returns the value with specified key.
|
||||
// It returns <def> if specified key does not exist and <def> is given.
|
||||
func (c *Cookie) Get(key string, def ...string) string {
|
||||
c.init()
|
||||
if r, ok := c.data[key]; ok {
|
||||
@ -126,24 +131,26 @@ func (c *Cookie) Get(key string, def ...string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// 删除COOKIE,使用默认的domain&path
|
||||
// Remove deletes specified key and its value from cookie using default domain and path.
|
||||
// It actually tells the http client that the cookie is expired, do not send it to server next time.
|
||||
func (c *Cookie) Remove(key string) {
|
||||
c.SetCookie(key, "", c.domain, c.path, -86400)
|
||||
}
|
||||
|
||||
// 标记该cookie在对应的域名和路径失效
|
||||
// 删除cookie的重点是需要通知浏览器客户端cookie已过期
|
||||
// RemoveCookie deletes specified key and its value from cookie using given domain and path.
|
||||
// It actually tells the http client that the cookie is expired, do not send it to server next time.
|
||||
func (c *Cookie) RemoveCookie(key, domain, path string) {
|
||||
c.SetCookie(key, "", domain, path, -86400)
|
||||
}
|
||||
|
||||
// 输出到客户端
|
||||
// Output outputs the cookie items to client.
|
||||
func (c *Cookie) Output() {
|
||||
if len(c.data) == 0 {
|
||||
return
|
||||
}
|
||||
for k, v := range c.data {
|
||||
// 只有 expire != 0 的才是服务端在本次请求中设置的cookie
|
||||
// Cookie item matches expire != 0 means it is set in this request,
|
||||
// which should be outputted to client.
|
||||
if v.expireAt == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -10,13 +10,13 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 域名管理器对象
|
||||
// Domain is used for route register for domains.
|
||||
type Domain struct {
|
||||
server *Server // 所属Server
|
||||
domains map[string]struct{} // 多域名
|
||||
server *Server // Belonged server
|
||||
domains map[string]struct{} // Support multiple domains.
|
||||
}
|
||||
|
||||
// 生成一个域名对象, 参数 domains 支持给定多个域名。
|
||||
// Domain creates and returns a domain object for management for one or more domains.
|
||||
func (s *Server) Domain(domains string) *Domain {
|
||||
d := &Domain{
|
||||
server: s,
|
||||
|
||||
@ -19,19 +19,20 @@ import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// 优雅的Web Server对象封装
|
||||
// gracefulServer wraps the net/http.Server with graceful reload/restart feature.
|
||||
type gracefulServer struct {
|
||||
server *Server // Belonged server.
|
||||
fd uintptr // 热重启时传递的socket监听文件句柄
|
||||
address string // 监听地址信息
|
||||
httpServer *http.Server // 底层http.Server
|
||||
rawListener net.Listener // 原始listener
|
||||
listener net.Listener // 接口化封装的listener
|
||||
isHttps bool // 是否HTTPS
|
||||
status int // 当前Server状态(关闭/运行)
|
||||
fd uintptr // File descriptor for passing to child process when graceful reload.
|
||||
address string // Listening address like:":80", ":8080".
|
||||
httpServer *http.Server // Underlying http.Server.
|
||||
rawListener net.Listener // Underlying net.Listener.
|
||||
listener net.Listener // Wrapped net.Listener.
|
||||
isHttps bool // Is HTTPS.
|
||||
status int // Status of current server.
|
||||
}
|
||||
|
||||
// 创建一个优雅的Http Server
|
||||
// newGracefulServer creates and returns a graceful http server with given address.
|
||||
// The optional parameter <fd> specifies the file descriptor which is passed from parent server.
|
||||
func (s *Server) newGracefulServer(address string, fd ...int) *gracefulServer {
|
||||
// Change port to address like: 80 -> :80
|
||||
if gstr.IsNumeric(address) {
|
||||
@ -42,14 +43,13 @@ func (s *Server) newGracefulServer(address string, fd ...int) *gracefulServer {
|
||||
address: address,
|
||||
httpServer: s.newHttpServer(address),
|
||||
}
|
||||
// 是否有继承的文件描述符
|
||||
if len(fd) > 0 && fd[0] > 0 {
|
||||
gs.fd = uintptr(fd[0])
|
||||
}
|
||||
return gs
|
||||
}
|
||||
|
||||
// 生成一个底层的Web Server对象
|
||||
// newGracefulServer creates and returns a underlying http.Server with given address.
|
||||
func (s *Server) newHttpServer(address string) *http.Server {
|
||||
server := &http.Server{
|
||||
Addr: address,
|
||||
@ -64,7 +64,7 @@ func (s *Server) newHttpServer(address string) *http.Server {
|
||||
return server
|
||||
}
|
||||
|
||||
// 执行HTTP监听
|
||||
// ListenAndServe starts listening on configured address.
|
||||
func (s *gracefulServer) ListenAndServe() error {
|
||||
ln, err := s.getNetListener()
|
||||
if err != nil {
|
||||
@ -75,7 +75,8 @@ func (s *gracefulServer) ListenAndServe() error {
|
||||
return s.doServe()
|
||||
}
|
||||
|
||||
// 获得文件描述符
|
||||
// Fd retrieves and returns the file descriptor of current server.
|
||||
// It is available ony in *nix like operation systems like: linux, unix, darwin.
|
||||
func (s *gracefulServer) Fd() uintptr {
|
||||
if s.rawListener != nil {
|
||||
file, err := s.rawListener.(*net.TCPListener).File()
|
||||
@ -86,12 +87,14 @@ func (s *gracefulServer) Fd() uintptr {
|
||||
return 0
|
||||
}
|
||||
|
||||
// 设置自定义fd
|
||||
// setFd sets the file descriptor for current server.
|
||||
func (s *gracefulServer) setFd(fd int) {
|
||||
s.fd = uintptr(fd)
|
||||
}
|
||||
|
||||
// 执行HTTPS监听
|
||||
// ListenAndServeTLS starts listening on configured address with HTTPS.
|
||||
// The parameter <certFile> and <keyFile> specify the necessary certification and key files for HTTPS.
|
||||
// The optional parameter <tlsConfig> specifies the custom TLS configuration.
|
||||
func (s *gracefulServer) ListenAndServeTLS(certFile, keyFile string, tlsConfig ...*tls.Config) error {
|
||||
var config *tls.Config
|
||||
if len(tlsConfig) > 0 && tlsConfig[0] != nil {
|
||||
@ -122,7 +125,7 @@ func (s *gracefulServer) ListenAndServeTLS(certFile, keyFile string, tlsConfig .
|
||||
return s.doServe()
|
||||
}
|
||||
|
||||
// 获取服务协议字符串
|
||||
// getProto retrieves and returns the proto string of current server.
|
||||
func (s *gracefulServer) getProto() string {
|
||||
proto := "http"
|
||||
if s.isHttps {
|
||||
@ -131,7 +134,7 @@ func (s *gracefulServer) getProto() string {
|
||||
return proto
|
||||
}
|
||||
|
||||
// 开始执行Web Server服务处理
|
||||
// doServe does staring the serving.
|
||||
func (s *gracefulServer) doServe() error {
|
||||
action := "started"
|
||||
if s.fd != 0 {
|
||||
@ -147,7 +150,7 @@ func (s *gracefulServer) doServe() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 自定义的net.Listener
|
||||
// getNetListener retrieves and returns the wrapped net.Listener.
|
||||
func (s *gracefulServer) getNetListener() (net.Listener, error) {
|
||||
var ln net.Listener
|
||||
var err error
|
||||
@ -167,7 +170,7 @@ func (s *gracefulServer) getNetListener() (net.Listener, error) {
|
||||
return ln, err
|
||||
}
|
||||
|
||||
// 执行请求优雅关闭
|
||||
// shutdown shuts down the server gracefully.
|
||||
func (s *gracefulServer) shutdown() {
|
||||
if s.status == SERVER_STATUS_STOPPED {
|
||||
return
|
||||
@ -180,7 +183,7 @@ func (s *gracefulServer) shutdown() {
|
||||
}
|
||||
}
|
||||
|
||||
// 执行请求强制关闭
|
||||
// close shuts down the server forcibly.
|
||||
func (s *gracefulServer) close() {
|
||||
if s.status == SERVER_STATUS_STOPPED {
|
||||
return
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user