mirror of
https://gitee.com/johng/gf
synced 2026-06-19 23:02:56 +08:00
Compare commits
100 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| edb745ed92 | |||
| 61f4e0da6f | |||
| 767afa3106 | |||
| 16779359ee | |||
| 770653fafa | |||
| 0c021b6da7 | |||
| ec92b08f25 | |||
| 13e2353729 | |||
| 90d19a84ce | |||
| b4c0b95d8a | |||
| b7e8255066 | |||
| 5f8e6ad9ed | |||
| c8d253eb56 | |||
| 4814624cff | |||
| cc67f3d388 | |||
| f7c2a51c9f | |||
| 3db83e1159 | |||
| 3bb002796c | |||
| 45170bc53e | |||
| b79ff84c6f | |||
| 938c46fec9 | |||
| 8a13d94526 | |||
| 1e844d505a | |||
| a123a2c086 | |||
| 1eeeeb853e | |||
| 6e7224e306 | |||
| 9e064e2651 | |||
| 8d9dd17eac | |||
| cf1d3d3d2b | |||
| 9480ffcdc0 | |||
| 5db10add4a | |||
| fa66bf5d9d | |||
| f69da3ace1 | |||
| e01bfa05c3 | |||
| 231238c157 | |||
| 7edec099ab | |||
| 83eb8be064 | |||
| 7af30df494 | |||
| f026686fda | |||
| 35a50b9c6c | |||
| 010e2f951a | |||
| f7f86ad65a | |||
| 1e19f447d1 | |||
| 8cc378331d | |||
| 4721f68fd8 | |||
| 0d11c0a1f8 | |||
| 5076613a8f | |||
| c5a44daa65 | |||
| 520970b71f | |||
| ebfb08ee3f | |||
| 9e38b2cb90 | |||
| 71b1f00dc5 | |||
| 9160bee1af | |||
| e64fd088b9 | |||
| 15672e7a09 |
@ -1,16 +1,68 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/encoding/gcompress"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := gcompress.ZipPath(
|
||||
`D:\Workspace\Go\GOPATH\src\github.com\gogf\gf\geg`,
|
||||
`D:\Workspace\Go\GOPATH\src\github.com\gogf\gf\geg\encoding\gcompress\data.zip`,
|
||||
"my-dir",
|
||||
)
|
||||
fmt.Println(err)
|
||||
// srcFile could be a single file or a directory
|
||||
func Zip(srcFile string, destZip string) error {
|
||||
zipfile, err := os.Create(destZip)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer zipfile.Close()
|
||||
|
||||
archive := zip.NewWriter(zipfile)
|
||||
defer archive.Close()
|
||||
|
||||
filepath.Walk(srcFile, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
header, err := zip.FileInfoHeader(info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
header.Name = strings.TrimPrefix(path, filepath.Dir(srcFile)+"/")
|
||||
// header.Name = path
|
||||
if info.IsDir() {
|
||||
header.Name += "/"
|
||||
} else {
|
||||
header.Method = zip.Deflate
|
||||
}
|
||||
|
||||
writer, err := archive.CreateHeader(header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
_, err = io.Copy(writer, file)
|
||||
}
|
||||
return err
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func main() {
|
||||
src := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/test`
|
||||
dst := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/test.zip`
|
||||
//src := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/README.MD`
|
||||
//dst := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/README.MD.zip`
|
||||
fmt.Println(gcompress.ZipPath(src, dst))
|
||||
//fmt.Println(Zip(src, dst))
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ import (
|
||||
func Upload(r *ghttp.Request) {
|
||||
saveDirPath := "/tmp/"
|
||||
files := r.GetUploadFiles("upload-file")
|
||||
if err := files.Save(saveDirPath); err != nil {
|
||||
if _, err := files.Save(saveDirPath); err != nil {
|
||||
r.Response.WriteExit(err)
|
||||
}
|
||||
r.Response.WriteExit("upload successfully")
|
||||
|
||||
@ -9,7 +9,7 @@ import (
|
||||
func Upload(r *ghttp.Request) {
|
||||
saveDirPath := "/tmp/"
|
||||
files := r.GetUploadFiles("upload-file")
|
||||
if err := files.Save(saveDirPath); err != nil {
|
||||
if _, err := files.Save(saveDirPath); err != nil {
|
||||
r.Response.WriteExit(err)
|
||||
}
|
||||
r.Response.WriteExit("upload successfully")
|
||||
|
||||
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)
|
||||
}
|
||||
@ -15,4 +15,11 @@
|
||||
|
||||
[viewer]
|
||||
delimiters = ["${", "}"]
|
||||
autoencode = true
|
||||
autoencode = true
|
||||
|
||||
|
||||
[server]
|
||||
Address = ":8800"
|
||||
ServerRoot = "/Users/john/Downloads"
|
||||
ServerAgent = "gf-app"
|
||||
# LogPath = "./log/gf-app/server"
|
||||
@ -1,5 +1,13 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/encoding/gjson"
|
||||
)
|
||||
|
||||
func main() {
|
||||
body := "{\"id\": 413231383385427875}"
|
||||
if dat, err := gjson.DecodeToJson(body); err == nil {
|
||||
fmt.Println(dat.MustToJsonString())
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
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)
|
||||
s := g.Server()
|
||||
s.SetIndexFolder(true)
|
||||
s.Run()
|
||||
}
|
||||
|
||||
@ -8,6 +8,6 @@ import (
|
||||
|
||||
func main() {
|
||||
for i := 0; i < 100; i++ {
|
||||
fmt.Println(grand.Rand(0, 99999))
|
||||
fmt.Println(grand.S(16))
|
||||
}
|
||||
}
|
||||
|
||||
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 < 1000; i++ {
|
||||
s := guid.S()
|
||||
fmt.Println(s, len(s))
|
||||
}
|
||||
}
|
||||
15
.example/util/guid/guid_length.go
Normal file
15
.example/util/guid/guid_length.go
Normal file
@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(strconv.FormatUint(4589634556, 36))
|
||||
fmt.Println(strconv.FormatUint(math.MaxUint64-2, 36))
|
||||
fmt.Println(strconv.FormatUint(math.MaxUint32-1, 36))
|
||||
fmt.Println(strconv.FormatUint(math.MaxUint32, 36))
|
||||
fmt.Println(strconv.FormatUint(2000000, 36))
|
||||
}
|
||||
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 < 10; i++ {
|
||||
s := guid.S([]byte("123"))
|
||||
fmt.Println(s, len(s))
|
||||
}
|
||||
fmt.Println()
|
||||
for i := 0; i < 10; i++ {
|
||||
s := guid.S([]byte("123"), []byte("456"))
|
||||
fmt.Println(s, len(s))
|
||||
}
|
||||
fmt.Println()
|
||||
for i := 0; i < 10; 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,6 +1,8 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.11.x"
|
||||
- "1.12.x"
|
||||
- "1.13.x"
|
||||
- "1.14.x"
|
||||
|
||||
|
||||
@ -73,6 +73,13 @@ 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| 大佬加油!
|
||||
|*秦|wechat|¥20.00| 给群主献上一杯咖啡...
|
||||
|[zhuhuan12](https://gitee.com/zhuhuan12)|wechat|¥50.00|
|
||||
|faddei|qq|¥9.99|
|
||||
|*天|wechat|¥10.00|
|
||||
|*.|wechat|¥20.00| 学生一个微薄之力,冲
|
||||
|李勇|wechat|¥50.00|
|
||||
|
||||
|
||||
<img src="https://goframe.org/images/donate.png"/>
|
||||
|
||||
81
README.MD
81
README.MD
@ -9,12 +9,13 @@
|
||||
|
||||
English | [简体中文](README_ZH.MD)
|
||||
|
||||
`GF(GoFrame)` is a modular, full-featured and production-ready application development framework
|
||||
`GF(GoFrame)` is a modular, powerful, high-performance and production-ready application development framework
|
||||
of golang. Providing a series of core components and dozens of practical modules, such as:
|
||||
cache, logging, containers, timer, resource, validator, database orm, etc.
|
||||
Supporting web server integrated with router, cookie, session, middleware, logger, configure,
|
||||
template, https, hooks, rewrites and many more features.
|
||||
|
||||
> If you're a newbie to `Go`, you may consider `GoFrame` easy and great as `Laravel` in `PHP`, `SpringBoot` in `Java` or `Django` in `Python`.
|
||||
|
||||
# Installation
|
||||
```
|
||||
@ -27,9 +28,76 @@ require github.com/gogf/gf latest
|
||||
|
||||
# Limitation
|
||||
```
|
||||
golang version >= 1.13
|
||||
golang version >= 1.11
|
||||
```
|
||||
|
||||
# Packages
|
||||
1. **Primary**
|
||||
|
||||
The `gf` repository maintains some basic and most commonly used packages, keeping it as lightweight and simple as possible.
|
||||
|
||||
1. **Community**
|
||||
|
||||
The community packages are contrinuted and maintained by community members, which are reposited in `gogf` organization. Some of the community packages are seperated from th `gf` repository, which are not of common usage or are with heavy dependecies.
|
||||
|
||||
# Architecture
|
||||
<div align=center>
|
||||
<img src="https://goframe.org/images/arch.png?v=11"/>
|
||||
</div>
|
||||
|
||||
# Performance
|
||||
|
||||
Here's the most popular Golang frameworks and libraries performance testing result in `WEB Server`. Performance testing cases source codes are hosted at: https://github.com/gogf/gf-performance
|
||||
|
||||
## Environment
|
||||
|
||||
OS : Ubuntu 18.04 amd64
|
||||
CPU : AMD A8-6600K x 4
|
||||
MEM : 32GB
|
||||
GO : v1.13.4
|
||||
|
||||
## Testing Tool
|
||||
|
||||
`ab`: Apache HTTP server benchmarking tool.
|
||||
|
||||
Command:
|
||||
```
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/hello
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/query?id=10000
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/json
|
||||
```
|
||||
The concurrency starts from `100` to `10000`.
|
||||
|
||||
> Run `5` times for each case of each project and pick up the best testing result.
|
||||
|
||||
## 1. Hello World
|
||||
<table>
|
||||
<tr>
|
||||
<th>Throughputs</th>
|
||||
<th>Mean Latency</th>
|
||||
<th>P99 Latency</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs1.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency1.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency1.jpeg"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## 2. Json Response
|
||||
<table>
|
||||
<tr>
|
||||
<th>Throughputs</th>
|
||||
<th>Mean Latency</th>
|
||||
<th>P99 Latency</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs3.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency3.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency3.jpeg"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
# Documentation
|
||||
|
||||
* 中文官网: https://goframe.org
|
||||
@ -43,12 +111,6 @@ golang version >= 1.13
|
||||
|
||||
> It's recommended learning `GoFrame` through its awesome source codes and API reference.
|
||||
|
||||
# Architecture
|
||||
<div align=center>
|
||||
<img src="https://goframe.org/images/arch.png?v=11"/>
|
||||
</div>
|
||||
|
||||
|
||||
# License
|
||||
|
||||
`GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever.
|
||||
@ -57,13 +119,14 @@ golang version >= 1.13
|
||||
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)?
|
||||
|
||||
# Sponsors
|
||||
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>
|
||||
|
||||
75
README_ZH.MD
75
README_ZH.MD
@ -15,16 +15,17 @@
|
||||
并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、Middleware、服务注册、模板引擎等等,
|
||||
支持热重启、热更新、域名绑定、TLS/HTTPS、Rewrite等特性。
|
||||
|
||||
> 如果您初识`Go`语言,您可以将`GoFrame`类似于`PHP`中的`Laravel`, `Java`中的`SpringBoot`或者`Python`中的`Django`。
|
||||
|
||||
# 特点
|
||||
* 模块化、松耦合设计;
|
||||
* 模块丰富,开箱即用;
|
||||
* 简便易用,易于维护;
|
||||
* 社区活跃,大牛谦逊低调脾气好;
|
||||
* 模块丰富、开箱即用;
|
||||
* 简便易用、易于维护;
|
||||
* 高代码质量、高单元测试覆盖率;
|
||||
* 社区活跃,大牛谦逊低调脾气好;
|
||||
* 详尽的开发文档及示例;
|
||||
* 完善的本地中文化支持;
|
||||
* 更适合企业及团队使用;
|
||||
* 设计为团队及企业使用;
|
||||
|
||||
# 地址
|
||||
- **主库**:https://github.com/gogf/gf
|
||||
@ -41,14 +42,78 @@ require github.com/gogf/gf latest
|
||||
|
||||
# 限制
|
||||
```shell
|
||||
golang版本 >= 1.13
|
||||
golang版本 >= 1.11
|
||||
```
|
||||
|
||||
# 模块
|
||||
|
||||
1. **核心模块**
|
||||
|
||||
`GoFrame`提供了一些基础的、常用的模块,简单、易用和轻量级,并保持极少的外部依赖,这些模块由`gf`主仓库细致维护。
|
||||
|
||||
1. **社区模块**
|
||||
|
||||
社区模块主要由社区贡献并维护,大部分也是由`gf`主仓库的贡献者提供及维护,存放于`gogf`组织下,与`gf`主仓库处于同一级别。有的社区模块是从`gf`主仓库中剥离出来单独维护的模块,这些模块并不是特别常用,或者对外部依赖较重。
|
||||
|
||||
|
||||
# 架构
|
||||
<div align=center>
|
||||
<img src="https://goframe.org/images/arch.png?v=11"/>
|
||||
</div>
|
||||
|
||||
# 性能
|
||||
|
||||
以下是目前最流行的`WEB Server` Golang框架/类库性能测试结果。
|
||||
性能测试用例源代码仓库: https://github.com/gogf/gf-performance
|
||||
|
||||
## 环境:
|
||||
|
||||
OS : Ubuntu 18.04 amd64
|
||||
CPU : AMD A8-6600K x 4
|
||||
MEM : 32GB
|
||||
GO : v1.13.4
|
||||
|
||||
## 工具
|
||||
|
||||
`ab`: Apache HTTP server benchmarking tool.
|
||||
|
||||
测试命令:
|
||||
```
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/hello
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/query?id=10000
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/json
|
||||
```
|
||||
并发客户端数量从 `100` 递增到 `10000`。
|
||||
|
||||
> 每个项目的每个用例均运行`5`次,取最优的结果展示。
|
||||
|
||||
## 1. Hello World
|
||||
<table>
|
||||
<tr>
|
||||
<th>Throughputs</th>
|
||||
<th>Mean Latency</th>
|
||||
<th>P99 Latency</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs1.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency1.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency1.jpeg"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## 2. Json Response
|
||||
<table>
|
||||
<tr>
|
||||
<th>Throughputs</th>
|
||||
<th>Mean Latency</th>
|
||||
<th>P99 Latency</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs3.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency3.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency3.jpeg"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
# 文档
|
||||
|
||||
@ -288,7 +288,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]
|
||||
@ -473,18 +473,7 @@ func (a *Array) Contains(value interface{}) bool {
|
||||
// or returns -1 if not exists.
|
||||
func (a *Array) Search(value interface{}) int {
|
||||
a.mu.RLock()
|
||||
result := -1
|
||||
for index, v := range a.array {
|
||||
if v == value {
|
||||
result = index
|
||||
break
|
||||
}
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
return result
|
||||
}
|
||||
|
||||
func (a *Array) doSearch(value interface{}) int {
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return -1
|
||||
}
|
||||
@ -499,6 +488,7 @@ func (a *Array) doSearch(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++ {
|
||||
@ -728,7 +718,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)
|
||||
|
||||
@ -264,7 +264,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]
|
||||
@ -489,6 +489,10 @@ func (a *IntArray) Contains(value int) bool {
|
||||
// or returns -1 if not exists.
|
||||
func (a *IntArray) Search(value int) int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return -1
|
||||
}
|
||||
result := -1
|
||||
for index, v := range a.array {
|
||||
if v == value {
|
||||
@ -496,11 +500,11 @@ func (a *IntArray) Search(value int) int {
|
||||
break
|
||||
}
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
return result
|
||||
}
|
||||
|
||||
// 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++ {
|
||||
@ -713,7 +717,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)
|
||||
|
||||
@ -252,7 +252,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]
|
||||
@ -473,13 +473,30 @@ func (a *StrArray) Contains(value string) bool {
|
||||
return a.Search(value) != -1
|
||||
}
|
||||
|
||||
// ContainsI checks whether a value exists in the array with case-insensitively.
|
||||
// Note that it internally iterates the whole array to do the comparison with case-insensitively.
|
||||
func (a *StrArray) ContainsI(value string) bool {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, v := range a.array {
|
||||
if strings.EqualFold(v, value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Search searches array by <value>, returns the index of <value>,
|
||||
// or returns -1 if not exists.
|
||||
func (a *StrArray) Search(value string) int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return -1
|
||||
}
|
||||
a.mu.RLock()
|
||||
result := -1
|
||||
for index, v := range a.array {
|
||||
if strings.Compare(v, value) == 0 {
|
||||
@ -487,11 +504,11 @@ func (a *StrArray) Search(value string) int {
|
||||
break
|
||||
}
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
return result
|
||||
}
|
||||
|
||||
// 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++ {
|
||||
@ -715,7 +732,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)
|
||||
|
||||
@ -122,7 +122,13 @@ func (a *SortedArray) Sort() *SortedArray {
|
||||
}
|
||||
|
||||
// Add adds one or multiple values to sorted array, the array always keeps sorted.
|
||||
// It's alias of function Append, see Append.
|
||||
func (a *SortedArray) Add(values ...interface{}) *SortedArray {
|
||||
return a.Append(values...)
|
||||
}
|
||||
|
||||
// Append adds one or multiple values to sorted array, the array always keeps sorted.
|
||||
func (a *SortedArray) Append(values ...interface{}) *SortedArray {
|
||||
if len(values) == 0 {
|
||||
return a
|
||||
}
|
||||
@ -218,7 +224,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]
|
||||
@ -425,13 +431,13 @@ func (a *SortedArray) Search(value interface{}) (index int) {
|
||||
// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
|
||||
// If <result> greater than 0, it means the value at <index> is greater than <value>.
|
||||
func (a *SortedArray) binSearch(value interface{}, lock bool) (index int, result int) {
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
}
|
||||
if lock {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
}
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
}
|
||||
min := 0
|
||||
max := len(a.array) - 1
|
||||
mid := 0
|
||||
@ -660,7 +666,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)
|
||||
|
||||
@ -107,7 +107,13 @@ func (a *SortedIntArray) Sort() *SortedIntArray {
|
||||
}
|
||||
|
||||
// Add adds one or multiple values to sorted array, the array always keeps sorted.
|
||||
// It's alias of function Append, see Append.
|
||||
func (a *SortedIntArray) Add(values ...int) *SortedIntArray {
|
||||
return a.Append(values...)
|
||||
}
|
||||
|
||||
// Append adds one or multiple values to sorted array, the array always keeps sorted.
|
||||
func (a *SortedIntArray) Append(values ...int) *SortedIntArray {
|
||||
if len(values) == 0 {
|
||||
return a
|
||||
}
|
||||
@ -203,7 +209,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]
|
||||
@ -422,13 +428,13 @@ func (a *SortedIntArray) Search(value int) (index int) {
|
||||
// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
|
||||
// If <result> greater than 0, it means the value at <index> is greater than <value>.
|
||||
func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int) {
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
}
|
||||
if lock {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
}
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
}
|
||||
min := 0
|
||||
max := len(a.array) - 1
|
||||
mid := 0
|
||||
@ -634,7 +640,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)
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/internal/rwmutex"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
@ -92,7 +93,13 @@ func (a *SortedStrArray) Sort() *SortedStrArray {
|
||||
}
|
||||
|
||||
// Add adds one or multiple values to sorted array, the array always keeps sorted.
|
||||
// It's alias of function Append, see Append.
|
||||
func (a *SortedStrArray) Add(values ...string) *SortedStrArray {
|
||||
return a.Append(values...)
|
||||
}
|
||||
|
||||
// Append adds one or multiple values to sorted array, the array always keeps sorted.
|
||||
func (a *SortedStrArray) Append(values ...string) *SortedStrArray {
|
||||
if len(values) == 0 {
|
||||
return a
|
||||
}
|
||||
@ -188,7 +195,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]
|
||||
@ -392,6 +399,22 @@ func (a *SortedStrArray) Contains(value string) bool {
|
||||
return a.Search(value) != -1
|
||||
}
|
||||
|
||||
// ContainsI checks whether a value exists in the array with case-insensitively.
|
||||
// Note that it internally iterates the whole array to do the comparison with case-insensitively.
|
||||
func (a *SortedStrArray) ContainsI(value string) bool {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, v := range a.array {
|
||||
if strings.EqualFold(v, value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Search searches array by <value>, returns the index of <value>,
|
||||
// or returns -1 if not exists.
|
||||
func (a *SortedStrArray) Search(value string) (index int) {
|
||||
@ -407,13 +430,13 @@ func (a *SortedStrArray) Search(value string) (index int) {
|
||||
// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
|
||||
// If <result> greater than 0, it means the value at <index> is greater than <value>.
|
||||
func (a *SortedStrArray) binSearch(value string, lock bool) (index int, result int) {
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
}
|
||||
if lock {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
}
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
}
|
||||
min := 0
|
||||
max := len(a.array) - 1
|
||||
mid := 0
|
||||
@ -630,7 +653,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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -64,6 +64,16 @@ func Test_StrArray_Basic(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_ContainsI(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := garray.NewStrArray()
|
||||
s.Append("a", "b", "C")
|
||||
t.Assert(s.Contains("A"), false)
|
||||
t.Assert(s.Contains("a"), true)
|
||||
t.Assert(s.ContainsI("A"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Sort(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect1 := []string{"0", "1", "2", "3"}
|
||||
@ -119,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()
|
||||
@ -535,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)
|
||||
@ -552,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
|
||||
@ -565,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)
|
||||
|
||||
@ -51,6 +51,16 @@ func TestSortedStrArray_SetArray(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_ContainsI(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.Append("a", "b", "C")
|
||||
t.Assert(s.Contains("A"), false)
|
||||
t.Assert(s.Contains("a"), true)
|
||||
t.Assert(s.ContainsI("A"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Sort(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"a", "d", "c", "b"}
|
||||
@ -127,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) {
|
||||
@ -139,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) {
|
||||
@ -200,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)
|
||||
})
|
||||
}
|
||||
|
||||
@ -513,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"}
|
||||
@ -533,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
|
||||
@ -546,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)
|
||||
|
||||
@ -1,70 +0,0 @@
|
||||
// Copyright 2017 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 gchan provides graceful channel for no panic operations.
|
||||
//
|
||||
// It's safe to call Chan.Push/Close functions repeatedly.
|
||||
package gchan
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/gogf/gf/container/gtype"
|
||||
)
|
||||
|
||||
// Graceful channel.
|
||||
type Chan struct {
|
||||
channel chan interface{}
|
||||
closed *gtype.Bool
|
||||
}
|
||||
|
||||
// New creates a graceful channel with given <limit>.
|
||||
func New(limit int) *Chan {
|
||||
return &Chan{
|
||||
channel: make(chan interface{}, limit),
|
||||
closed: gtype.NewBool(),
|
||||
}
|
||||
}
|
||||
|
||||
// Push pushes <value> to channel.
|
||||
// It is safe to be called repeatedly.
|
||||
func (c *Chan) Push(value interface{}) error {
|
||||
if c.closed.Val() {
|
||||
return errors.New("channel is closed")
|
||||
}
|
||||
c.channel <- value
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pop pops value from channel.
|
||||
// If there's no value in channel, it would block to wait.
|
||||
// If the channel is closed, it will return a nil value immediately.
|
||||
func (c *Chan) Pop() interface{} {
|
||||
return <-c.channel
|
||||
}
|
||||
|
||||
// Close closes the channel.
|
||||
// It is safe to be called repeatedly.
|
||||
func (c *Chan) Close() {
|
||||
if !c.closed.Set(true) {
|
||||
close(c.channel)
|
||||
}
|
||||
}
|
||||
|
||||
// See Len.
|
||||
func (c *Chan) Size() int {
|
||||
return c.Len()
|
||||
}
|
||||
|
||||
// Len returns the length of the channel.
|
||||
func (c *Chan) Len() int {
|
||||
return len(c.channel)
|
||||
}
|
||||
|
||||
// Cap returns the capacity of the channel.
|
||||
func (c *Chan) Cap() int {
|
||||
return cap(c.channel)
|
||||
}
|
||||
@ -1,30 +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 gchan_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/container/gchan"
|
||||
)
|
||||
|
||||
func Example_basic() {
|
||||
n := 10
|
||||
c := gchan.New(n)
|
||||
for i := 0; i < n; i++ {
|
||||
c.Push(i)
|
||||
}
|
||||
fmt.Println(c.Len(), c.Cap())
|
||||
for i := 0; i < n; i++ {
|
||||
fmt.Print(c.Pop())
|
||||
}
|
||||
c.Close()
|
||||
|
||||
// Output:
|
||||
//10 10
|
||||
//0123456789
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
package gchan_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/container/gchan"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
func Test_Gchan(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
ch := gchan.New(10)
|
||||
|
||||
t.Assert(ch.Cap(), 10)
|
||||
t.Assert(ch.Push(1), nil)
|
||||
t.Assert(ch.Len(), 1)
|
||||
t.Assert(ch.Size(), 1)
|
||||
ch.Pop()
|
||||
t.Assert(ch.Len(), 0)
|
||||
t.Assert(ch.Size(), 0)
|
||||
ch.Close()
|
||||
t.Assert(ch.Push(1), errors.New("channel is closed"))
|
||||
|
||||
ch = gchan.New(0)
|
||||
ch1 := gchan.New(0)
|
||||
go func() {
|
||||
var i = 0
|
||||
for {
|
||||
v := ch.Pop()
|
||||
if v == nil {
|
||||
ch1.Push(i)
|
||||
break
|
||||
}
|
||||
t.Assert(v, i)
|
||||
i++
|
||||
}
|
||||
}()
|
||||
|
||||
for index := 0; index < 10; index++ {
|
||||
ch.Push(index)
|
||||
}
|
||||
ch.Close()
|
||||
t.Assert(ch1.Pop(), 10)
|
||||
ch1.Close()
|
||||
})
|
||||
}
|
||||
@ -452,6 +452,16 @@ func (set *Set) Pops(size int) []interface{} {
|
||||
return array
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of set.
|
||||
func (set *Set) Walk(f func(item interface{}) interface{}) *Set {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
for k, v := range set.data {
|
||||
set.data[f(k)] = v
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (set *Set) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(set.Slice())
|
||||
|
||||
@ -412,6 +412,16 @@ func (set *IntSet) Pops(size int) []int {
|
||||
return array
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of set.
|
||||
func (set *IntSet) Walk(f func(item int) int) *IntSet {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
for k, v := range set.data {
|
||||
set.data[f(k)] = v
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (set *IntSet) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(set.Slice())
|
||||
|
||||
@ -13,6 +13,7 @@ import (
|
||||
"github.com/gogf/gf/internal/rwmutex"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type StrSet struct {
|
||||
@ -139,6 +140,19 @@ func (set *StrSet) Contains(item string) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
// ContainsI checks whether a value exists in the set with case-insensitively.
|
||||
// Note that it internally iterates the whole set to do the comparison with case-insensitively.
|
||||
func (set *StrSet) ContainsI(item string) bool {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for k, _ := range set.data {
|
||||
if strings.EqualFold(k, item) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Remove deletes <item> from set.
|
||||
func (set *StrSet) Remove(item string) {
|
||||
set.mu.Lock()
|
||||
@ -426,6 +440,16 @@ func (set *StrSet) Pops(size int) []string {
|
||||
return array
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of set.
|
||||
func (set *StrSet) Walk(f func(item string) string) *StrSet {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
for k, v := range set.data {
|
||||
set.data[f(k)] = v
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (set *StrSet) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(set.Slice())
|
||||
|
||||
@ -62,6 +62,16 @@ func TestStrSet_Basic(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrSet_ContainsI(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewStrSet()
|
||||
s.Add("a", "b", "C")
|
||||
t.Assert(s.Contains("A"), false)
|
||||
t.Assert(s.Contains("a"), true)
|
||||
t.Assert(s.ContainsI("A"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrSet_Iterator(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewStrSet()
|
||||
|
||||
@ -235,100 +235,6 @@ func (v *Var) GTime(format ...string) *gtime.Time {
|
||||
return gconv.GTime(v.Val(), format...)
|
||||
}
|
||||
|
||||
// Map converts <v> to map[string]interface{}.
|
||||
func (v *Var) Map(tags ...string) map[string]interface{} {
|
||||
return gconv.Map(v.Val(), tags...)
|
||||
}
|
||||
|
||||
// MapStrStr converts <v> to map[string]string.
|
||||
func (v *Var) MapStrStr(tags ...string) map[string]string {
|
||||
return gconv.MapStrStr(v.Val(), tags...)
|
||||
}
|
||||
|
||||
// MapStrVar converts <v> to map[string]*Var.
|
||||
func (v *Var) MapStrVar(tags ...string) map[string]*Var {
|
||||
m := v.Map(tags...)
|
||||
if len(m) > 0 {
|
||||
vMap := make(map[string]*Var)
|
||||
for k, v := range m {
|
||||
vMap[k] = New(v)
|
||||
}
|
||||
return vMap
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MapDeep converts <v> to map[string]interface{} recursively.
|
||||
func (v *Var) MapDeep(tags ...string) map[string]interface{} {
|
||||
return gconv.MapDeep(v.Val(), tags...)
|
||||
}
|
||||
|
||||
// MapDeep converts <v> to map[string]string recursively.
|
||||
func (v *Var) MapStrStrDeep(tags ...string) map[string]string {
|
||||
return gconv.MapStrStrDeep(v.Val(), tags...)
|
||||
}
|
||||
|
||||
// MapStrVarDeep converts <v> to map[string]*Var recursively.
|
||||
func (v *Var) MapStrVarDeep(tags ...string) map[string]*Var {
|
||||
m := v.MapDeep(tags...)
|
||||
if len(m) > 0 {
|
||||
vMap := make(map[string]*Var)
|
||||
for k, v := range m {
|
||||
vMap[k] = New(v)
|
||||
}
|
||||
return vMap
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Struct maps value of <v> to <pointer>.
|
||||
// The parameter <pointer> should be a pointer to a struct instance.
|
||||
// The parameter <mapping> is used to specify the key-to-attribute mapping rules.
|
||||
func (v *Var) Struct(pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.Struct(v.Val(), pointer, mapping...)
|
||||
}
|
||||
|
||||
// Struct maps value of <v> to <pointer> recursively.
|
||||
// The parameter <pointer> should be a pointer to a struct instance.
|
||||
// The parameter <mapping> is used to specify the key-to-attribute mapping rules.
|
||||
func (v *Var) StructDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.StructDeep(v.Val(), pointer, mapping...)
|
||||
}
|
||||
|
||||
// Structs converts <v> to given struct slice.
|
||||
func (v *Var) Structs(pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
return gconv.Structs(v.Val(), pointer, mapping...)
|
||||
}
|
||||
|
||||
// StructsDeep converts <v> to given struct slice recursively.
|
||||
func (v *Var) StructsDeep(pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
return gconv.StructsDeep(v.Val(), pointer, mapping...)
|
||||
}
|
||||
|
||||
// MapToMap converts map type variable <params> to another map type variable <pointer>.
|
||||
// The elements of <pointer> should be type of struct/*struct.
|
||||
func (v *Var) MapToMap(pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
return gconv.MapToMap(v.Val(), pointer, mapping...)
|
||||
}
|
||||
|
||||
// MapToMapDeep recursively converts map type variable <params> to another map type variable <pointer>.
|
||||
// The elements of <pointer> should be type of struct/*struct.
|
||||
func (v *Var) MapToMapDeep(pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
return gconv.MapToMapDeep(v.Val(), pointer, mapping...)
|
||||
}
|
||||
|
||||
// MapToMaps converts map type variable <params> to another map type variable <pointer>.
|
||||
// The elements of <pointer> should be type of []struct/[]*struct.
|
||||
func (v *Var) MapToMaps(pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
return gconv.MapToMaps(v.Val(), pointer, mapping...)
|
||||
}
|
||||
|
||||
// MapToMapsDeep recursively converts map type variable <params> to another map type variable <pointer>.
|
||||
// The elements of <pointer> should be type of []struct/[]*struct.
|
||||
func (v *Var) MapToMapsDeep(pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
return gconv.MapToMapsDeep(v.Val(), pointer, mapping...)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (v *Var) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(v.Val())
|
||||
|
||||
87
container/gvar/gvar_map.go
Normal file
87
container/gvar/gvar_map.go
Normal file
@ -0,0 +1,87 @@
|
||||
// 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 gvar
|
||||
|
||||
import "github.com/gogf/gf/util/gconv"
|
||||
|
||||
// Map converts and returns <v> as map[string]interface{}.
|
||||
func (v *Var) Map(tags ...string) map[string]interface{} {
|
||||
return gconv.Map(v.Val(), tags...)
|
||||
}
|
||||
|
||||
// MapStrStr converts and returns <v> as map[string]string.
|
||||
func (v *Var) MapStrStr(tags ...string) map[string]string {
|
||||
return gconv.MapStrStr(v.Val(), tags...)
|
||||
}
|
||||
|
||||
// MapStrVar converts and returns <v> as map[string]*Var.
|
||||
func (v *Var) MapStrVar(tags ...string) map[string]*Var {
|
||||
m := v.Map(tags...)
|
||||
if len(m) > 0 {
|
||||
vMap := make(map[string]*Var)
|
||||
for k, v := range m {
|
||||
vMap[k] = New(v)
|
||||
}
|
||||
return vMap
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MapDeep converts and returns <v> as map[string]interface{} recursively.
|
||||
func (v *Var) MapDeep(tags ...string) map[string]interface{} {
|
||||
return gconv.MapDeep(v.Val(), tags...)
|
||||
}
|
||||
|
||||
// MapDeep converts and returns <v> as map[string]string recursively.
|
||||
func (v *Var) MapStrStrDeep(tags ...string) map[string]string {
|
||||
return gconv.MapStrStrDeep(v.Val(), tags...)
|
||||
}
|
||||
|
||||
// MapStrVarDeep converts and returns <v> as map[string]*Var recursively.
|
||||
func (v *Var) MapStrVarDeep(tags ...string) map[string]*Var {
|
||||
m := v.MapDeep(tags...)
|
||||
if len(m) > 0 {
|
||||
vMap := make(map[string]*Var)
|
||||
for k, v := range m {
|
||||
vMap[k] = New(v)
|
||||
}
|
||||
return vMap
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Maps converts and returns <v> as map[string]string.
|
||||
// See gconv.Maps.
|
||||
func (v *Var) Maps(tags ...string) []map[string]interface{} {
|
||||
return gconv.Maps(v.Val(), tags...)
|
||||
}
|
||||
|
||||
// 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 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 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 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) {
|
||||
return gconv.MapToMapsDeep(v.Val(), pointer, mapping...)
|
||||
}
|
||||
33
container/gvar/gvar_struct.go
Normal file
33
container/gvar/gvar_struct.go
Normal file
@ -0,0 +1,33 @@
|
||||
// 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 gvar
|
||||
|
||||
import "github.com/gogf/gf/util/gconv"
|
||||
|
||||
// Struct maps value of <v> to <pointer>.
|
||||
// The parameter <pointer> should be a pointer to a struct instance.
|
||||
// The parameter <mapping> is used to specify the key-to-attribute mapping rules.
|
||||
func (v *Var) Struct(pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.Struct(v.Val(), pointer, mapping...)
|
||||
}
|
||||
|
||||
// Struct maps value of <v> to <pointer> recursively.
|
||||
// The parameter <pointer> should be a pointer to a struct instance.
|
||||
// The parameter <mapping> is used to specify the key-to-attribute mapping rules.
|
||||
func (v *Var) StructDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.StructDeep(v.Val(), pointer, mapping...)
|
||||
}
|
||||
|
||||
// Structs converts and returns <v> as given struct slice.
|
||||
func (v *Var) Structs(pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
return gconv.Structs(v.Val(), pointer, mapping...)
|
||||
}
|
||||
|
||||
// StructsDeep converts and returns <v> as given struct slice recursively.
|
||||
func (v *Var) StructsDeep(pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
return gconv.StructsDeep(v.Val(), pointer, mapping...)
|
||||
}
|
||||
@ -9,14 +9,10 @@ package gvar_test
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/frame/g"
|
||||
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
@ -305,90 +301,6 @@ func Test_Duration(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Map(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := g.Map{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
}
|
||||
objOne := gvar.New(m, true)
|
||||
t.Assert(objOne.Map()["k1"], m["k1"])
|
||||
t.Assert(objOne.Map()["k2"], m["k2"])
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Struct(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type StTest struct {
|
||||
Test int
|
||||
}
|
||||
|
||||
Kv := make(map[string]int, 1)
|
||||
Kv["Test"] = 100
|
||||
|
||||
testObj := &StTest{}
|
||||
|
||||
objOne := gvar.New(Kv, true)
|
||||
|
||||
objOne.Struct(testObj)
|
||||
|
||||
t.Assert(testObj.Test, Kv["Test"])
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type StTest struct {
|
||||
Test int8
|
||||
}
|
||||
o := &StTest{}
|
||||
v := gvar.New(g.Slice{"Test", "-25"})
|
||||
v.Struct(o)
|
||||
t.Assert(o.Test, -25)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Json(t *testing.T) {
|
||||
// Marshal
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := "i love gf"
|
||||
v := gvar.New(s)
|
||||
b1, err1 := json.Marshal(v)
|
||||
b2, err2 := json.Marshal(s)
|
||||
t.Assert(err1, err2)
|
||||
t.Assert(b1, b2)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := int64(math.MaxInt64)
|
||||
v := gvar.New(s)
|
||||
b1, err1 := json.Marshal(v)
|
||||
b2, err2 := json.Marshal(s)
|
||||
t.Assert(err1, err2)
|
||||
t.Assert(b1, b2)
|
||||
})
|
||||
|
||||
// Unmarshal
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := "i love gf"
|
||||
v := gvar.New(nil)
|
||||
b, err := json.Marshal(s)
|
||||
t.Assert(err, nil)
|
||||
|
||||
err = json.Unmarshal(b, v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.String(), s)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v gvar.Var
|
||||
s := "i love gf"
|
||||
b, err := json.Marshal(s)
|
||||
t.Assert(err, nil)
|
||||
|
||||
err = json.Unmarshal(b, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.String(), s)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
59
container/gvar/gvar_z_unit_json_test.go
Normal file
59
container/gvar/gvar_z_unit_json_test.go
Normal file
@ -0,0 +1,59 @@
|
||||
// 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 gvar_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Json(t *testing.T) {
|
||||
// Marshal
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := "i love gf"
|
||||
v := gvar.New(s)
|
||||
b1, err1 := json.Marshal(v)
|
||||
b2, err2 := json.Marshal(s)
|
||||
t.Assert(err1, err2)
|
||||
t.Assert(b1, b2)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := int64(math.MaxInt64)
|
||||
v := gvar.New(s)
|
||||
b1, err1 := json.Marshal(v)
|
||||
b2, err2 := json.Marshal(s)
|
||||
t.Assert(err1, err2)
|
||||
t.Assert(b1, b2)
|
||||
})
|
||||
|
||||
// Unmarshal
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := "i love gf"
|
||||
v := gvar.New(nil)
|
||||
b, err := json.Marshal(s)
|
||||
t.Assert(err, nil)
|
||||
|
||||
err = json.Unmarshal(b, v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.String(), s)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v gvar.Var
|
||||
s := "i love gf"
|
||||
b, err := json.Marshal(s)
|
||||
t.Assert(err, nil)
|
||||
|
||||
err = json.Unmarshal(b, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.String(), s)
|
||||
})
|
||||
}
|
||||
26
container/gvar/gvar_z_unit_map_test.go
Normal file
26
container/gvar/gvar_z_unit_map_test.go
Normal file
@ -0,0 +1,26 @@
|
||||
// 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 gvar_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Map(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := g.Map{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
}
|
||||
objOne := gvar.New(m, true)
|
||||
t.Assert(objOne.Map()["k1"], m["k1"])
|
||||
t.Assert(objOne.Map()["k2"], m["k2"])
|
||||
})
|
||||
}
|
||||
69
container/gvar/gvar_z_unit_maptomap_test.go
Normal file
69
container/gvar/gvar_z_unit_maptomap_test.go
Normal file
@ -0,0 +1,69 @@
|
||||
// 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 gvar_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_MapToMap(t *testing.T) {
|
||||
// map[int]int -> map[string]string
|
||||
// empty original map.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := g.MapIntInt{}
|
||||
m2 := g.MapStrStr{}
|
||||
t.Assert(gvar.New(m1).MapToMap(&m2), nil)
|
||||
t.Assert(len(m1), len(m2))
|
||||
})
|
||||
// map[int]int -> map[string]string
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := g.MapIntInt{
|
||||
1: 100,
|
||||
2: 200,
|
||||
}
|
||||
m2 := g.MapStrStr{}
|
||||
t.Assert(gvar.New(m1).MapToMap(&m2), nil)
|
||||
t.Assert(m2["1"], m1[1])
|
||||
t.Assert(m2["2"], m1[2])
|
||||
})
|
||||
// map[string]interface{} -> map[string]string
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := g.Map{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
}
|
||||
m2 := g.MapStrStr{}
|
||||
t.Assert(gvar.New(m1).MapToMap(&m2), nil)
|
||||
t.Assert(m2["k1"], m1["k1"])
|
||||
t.Assert(m2["k2"], m1["k2"])
|
||||
})
|
||||
// map[string]string -> map[string]interface{}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := g.MapStrStr{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
}
|
||||
m2 := g.Map{}
|
||||
t.Assert(gvar.New(m1).MapToMap(&m2), nil)
|
||||
t.Assert(m2["k1"], m1["k1"])
|
||||
t.Assert(m2["k2"], m1["k2"])
|
||||
})
|
||||
// map[string]interface{} -> map[interface{}]interface{}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := g.MapStrStr{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
}
|
||||
m2 := g.MapAnyAny{}
|
||||
t.Assert(gvar.New(m1).MapToMap(&m2), nil)
|
||||
t.Assert(m2["k1"], m1["k1"])
|
||||
t.Assert(m2["k2"], m1["k2"])
|
||||
})
|
||||
}
|
||||
42
container/gvar/gvar_z_unit_struct_test.go
Normal file
42
container/gvar/gvar_z_unit_struct_test.go
Normal file
@ -0,0 +1,42 @@
|
||||
// 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 gvar_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Struct(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type StTest struct {
|
||||
Test int
|
||||
}
|
||||
|
||||
Kv := make(map[string]int, 1)
|
||||
Kv["Test"] = 100
|
||||
|
||||
testObj := &StTest{}
|
||||
|
||||
objOne := gvar.New(Kv, true)
|
||||
|
||||
objOne.Struct(testObj)
|
||||
|
||||
t.Assert(testObj.Test, Kv["Test"])
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type StTest struct {
|
||||
Test int8
|
||||
}
|
||||
o := &StTest{}
|
||||
v := gvar.New(g.Slice{"Test", "-25"})
|
||||
v.Struct(o)
|
||||
t.Assert(o.Test, -25)
|
||||
})
|
||||
}
|
||||
@ -64,6 +64,7 @@ type DB interface {
|
||||
|
||||
// Transaction.
|
||||
Begin() (*TX, error)
|
||||
Transaction(f func(tx *TX) error) (err error)
|
||||
|
||||
Insert(table string, data interface{}, batch ...int) (sql.Result, error)
|
||||
InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error)
|
||||
|
||||
@ -203,9 +203,6 @@ func (c *Core) GetStruct(pointer interface{}, sql string, args ...interface{}) e
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(one) == 0 {
|
||||
return ErrNoRows
|
||||
}
|
||||
return one.Struct(pointer)
|
||||
}
|
||||
|
||||
@ -216,9 +213,6 @@ func (c *Core) GetStructs(pointer interface{}, sql string, args ...interface{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(all) == 0 {
|
||||
return ErrNoRows
|
||||
}
|
||||
return all.Structs(pointer)
|
||||
}
|
||||
|
||||
@ -310,6 +304,34 @@ func (c *Core) Begin() (*TX, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Transaction wraps the transaction logic using function <f>.
|
||||
// It rollbacks the transaction and returns the error from function <f> if
|
||||
// it returns non-nil error. It commits the transaction and returns nil if
|
||||
// function <f> returns nil.
|
||||
//
|
||||
// Note that, you should not Commit or Rollback the transaction in function <f>
|
||||
// as it is automatically handled by this function.
|
||||
func (c *Core) Transaction(f func(tx *TX) error) (err error) {
|
||||
var tx *TX
|
||||
tx, err = c.DB.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if e := tx.Rollback(); e != nil {
|
||||
err = e
|
||||
}
|
||||
} else {
|
||||
if e := tx.Commit(); e != nil {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
}()
|
||||
err = f(tx)
|
||||
return
|
||||
}
|
||||
|
||||
// Insert does "INSERT INTO ..." statement for the table.
|
||||
// If there's already one unique record of the data in the table, it returns error.
|
||||
//
|
||||
@ -507,7 +529,7 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i
|
||||
listMap[i] = DataToMapDeep(rv.Index(i).Interface())
|
||||
}
|
||||
case reflect.Map, reflect.Struct:
|
||||
listMap = List{DataToMapDeep(list)}
|
||||
listMap = List{DataToMapDeep(v)}
|
||||
default:
|
||||
return result, errors.New(fmt.Sprint("unsupported list type:", kind))
|
||||
}
|
||||
@ -712,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]
|
||||
}
|
||||
|
||||
@ -83,12 +83,15 @@ func (d *DriverMysql) Tables(schema ...string) (tables []string, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// TableFields retrieves and returns the fields information of specified table of current schema.
|
||||
// TableFields retrieves and returns the fields information of specified table of current
|
||||
// schema.
|
||||
//
|
||||
// Note that it returns a map containing the field name and its corresponding fields.
|
||||
// As a map is unsorted, the TableField struct has a "Index" field marks its sequence in the fields.
|
||||
// As a map is unsorted, the TableField struct has a "Index" field marks its sequence in
|
||||
// the fields.
|
||||
//
|
||||
// It's using cache feature to enhance the performance, which is never expired util the process restarts.
|
||||
// It's using cache feature to enhance the performance, which is never expired util the
|
||||
// process restarts.
|
||||
func (d *DriverMysql) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
|
||||
charL, charR := d.GetChars()
|
||||
table = gstr.Trim(table, charL+charR)
|
||||
|
||||
@ -206,15 +206,16 @@ func GetPrimaryKey(pointer interface{}) string {
|
||||
|
||||
// GetPrimaryKeyCondition returns a new where condition by primary field name.
|
||||
// The optional parameter <where> is like follows:
|
||||
// 123
|
||||
// []int{1, 2, 3}
|
||||
// "john"
|
||||
// []string{"john", "smith"}
|
||||
// g.Map{"id": g.Slice{1,2,3}}
|
||||
// g.Map{"id": 1, "name": "john"}
|
||||
// 123 => primary=123
|
||||
// []int{1, 2, 3} => primary IN(1,2,3)
|
||||
// "john" => primary='john'
|
||||
// []string{"john", "smith"} => primary IN('john','smith')
|
||||
// g.Map{"id": g.Slice{1,2,3}} => id IN(1,2,3)
|
||||
// g.Map{"id": 1, "name": "john"} => id=1 AND name='john'
|
||||
// etc.
|
||||
//
|
||||
// Note that it returns the given <where> parameter directly if there's the <primary> is empty.
|
||||
// Note that it returns the given <where> parameter directly if the <primary> is empty
|
||||
// or length of <where> > 1.
|
||||
func GetPrimaryKeyCondition(primary string, where ...interface{}) (newWhereCondition []interface{}) {
|
||||
if len(where) == 0 {
|
||||
return nil
|
||||
@ -231,6 +232,7 @@ func GetPrimaryKeyCondition(primary string, where ...interface{}) (newWhereCondi
|
||||
}
|
||||
switch kind {
|
||||
case reflect.Map, reflect.Struct:
|
||||
// Ignore the parameter <primary>.
|
||||
break
|
||||
|
||||
default:
|
||||
@ -246,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)
|
||||
}
|
||||
|
||||
|
||||
@ -18,19 +18,26 @@ import (
|
||||
// If the parameter <duration> = 0, which means it never expires.
|
||||
// If the parameter <duration> > 0, which means it expires after <duration>.
|
||||
//
|
||||
// The optional parameter <name> is used to bind a name to the cache, which means you can later
|
||||
// control the cache like changing the <duration> or clearing the cache with specified <name>.
|
||||
// The optional parameter <name> is used to bind a name to the cache, which means you can
|
||||
// later control the cache like changing the <duration> or clearing the cache with specified
|
||||
// <name>.
|
||||
//
|
||||
// Note that, the cache feature is disabled if the model is operating on a transaction.
|
||||
// Note that, the cache feature is disabled if the model is performing select statement
|
||||
// on a transaction.
|
||||
func (m *Model) Cache(duration time.Duration, name ...string) *Model {
|
||||
model := m.getModel()
|
||||
model.cacheDuration = duration
|
||||
if len(name) > 0 {
|
||||
model.cacheName = name[0]
|
||||
}
|
||||
// It does not support cache on transaction.
|
||||
if model.tx == nil {
|
||||
model.cacheEnabled = true
|
||||
}
|
||||
model.cacheEnabled = true
|
||||
return model
|
||||
}
|
||||
|
||||
// checkAndRemoveCache checks and removes the cache in insert/update/delete statement if
|
||||
// cache feature is enabled.
|
||||
func (m *Model) checkAndRemoveCache() {
|
||||
if m.cacheEnabled && m.cacheDuration < 0 && len(m.cacheName) > 0 {
|
||||
m.db.GetCache().Remove(m.cacheName)
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,9 @@
|
||||
|
||||
package gdb
|
||||
|
||||
import "github.com/gogf/gf/util/gconv"
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Where sets the condition statement for the model. The parameter <where> can be type of
|
||||
// string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times,
|
||||
@ -46,7 +48,8 @@ func (m *Model) Having(having interface{}, args ...interface{}) *Model {
|
||||
// WherePri does the same logic as Model.Where except that if the parameter <where>
|
||||
// is a single condition like int/string/float/slice, it treats the condition as the primary
|
||||
// key value. That is, if primary key is "id" and given <where> parameter as "123", the
|
||||
// WherePri function treats it as "id=123", but Model.Where treats it as string "123".
|
||||
// WherePri function treats the condition as "id=123", but Model.Where treats the condition
|
||||
// as string "123".
|
||||
func (m *Model) WherePri(where interface{}, args ...interface{}) *Model {
|
||||
if len(args) > 0 {
|
||||
return m.Where(where, args...)
|
||||
@ -98,9 +101,9 @@ func (m *Model) GroupBy(groupBy string) *Model {
|
||||
}
|
||||
|
||||
// Order sets the "ORDER BY" statement for the model.
|
||||
func (m *Model) Order(orderBy string) *Model {
|
||||
func (m *Model) Order(orderBy ...string) *Model {
|
||||
model := m.getModel()
|
||||
model.orderBy = m.db.QuoteString(orderBy)
|
||||
model.orderBy = m.db.QuoteString(strings.Join(orderBy, " "))
|
||||
return model
|
||||
}
|
||||
|
||||
@ -154,28 +157,3 @@ func (m *Model) Page(page, limit int) *Model {
|
||||
func (m *Model) ForPage(page, limit int) *Model {
|
||||
return m.Page(page, limit)
|
||||
}
|
||||
|
||||
// getAll does the query from database.
|
||||
func (m *Model) getAll(sql string, args ...interface{}) (result Result, err error) {
|
||||
cacheKey := ""
|
||||
// Retrieve from cache.
|
||||
if m.cacheEnabled {
|
||||
cacheKey = m.cacheName
|
||||
if len(cacheKey) == 0 {
|
||||
cacheKey = sql + "/" + gconv.String(args)
|
||||
}
|
||||
if v := m.db.GetCache().Get(cacheKey); v != nil {
|
||||
return v.(Result), nil
|
||||
}
|
||||
}
|
||||
result, err = m.db.DoGetAll(m.getLink(false), sql, m.mergeArguments(args)...)
|
||||
// Cache the result.
|
||||
if len(cacheKey) > 0 && err == nil {
|
||||
if m.cacheDuration < 0 {
|
||||
m.db.GetCache().Remove(cacheKey)
|
||||
} else {
|
||||
m.db.GetCache().Set(cacheKey, result, m.cacheDuration)
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/gset"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
)
|
||||
@ -34,21 +34,31 @@ func (m *Model) FieldsEx(fields string) *Model {
|
||||
if gstr.Contains(m.tables, " ") {
|
||||
panic("function FieldsEx supports only single table operations")
|
||||
}
|
||||
tableFields, err := m.db.TableFields(m.tables)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if len(tableFields) == 0 {
|
||||
panic(fmt.Sprintf(`empty table fields for table "%s"`, m.tables))
|
||||
}
|
||||
model := m.getModel()
|
||||
model.fieldsEx = fields
|
||||
fieldsExSet := gset.NewStrSetFrom(gstr.SplitAndTrim(fields, ","))
|
||||
if m, err := m.db.TableFields(m.tables); err == nil {
|
||||
model.fields = ""
|
||||
for k, _ := range m {
|
||||
if fieldsExSet.Contains(k) {
|
||||
continue
|
||||
}
|
||||
if len(model.fields) > 0 {
|
||||
model.fields += ","
|
||||
}
|
||||
model.fields += k
|
||||
}
|
||||
fieldsArray := make([]string, len(tableFields))
|
||||
for k, v := range tableFields {
|
||||
fieldsArray[v.Index] = k
|
||||
}
|
||||
model.fields = ""
|
||||
for _, k := range fieldsArray {
|
||||
if fieldsExSet.Contains(k) {
|
||||
continue
|
||||
}
|
||||
if len(model.fields) > 0 {
|
||||
model.fields += ","
|
||||
}
|
||||
model.fields += k
|
||||
}
|
||||
model.fields = model.db.QuoteString(model.fields)
|
||||
return model
|
||||
}
|
||||
|
||||
@ -59,14 +69,26 @@ func (m *Model) FieldsStr(prefix ...string) string {
|
||||
if len(prefix) > 0 {
|
||||
prefixStr = prefix[0]
|
||||
}
|
||||
if m, err := m.db.TableFields(m.tables); err == nil {
|
||||
fieldsArray := garray.NewStrArraySize(len(m), len(m))
|
||||
for _, field := range m {
|
||||
fieldsArray.Set(field.Index, prefixStr+field.Name)
|
||||
}
|
||||
return fieldsArray.Join(",")
|
||||
tableFields, err := m.db.TableFields(m.tables)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ""
|
||||
if len(tableFields) == 0 {
|
||||
panic(fmt.Sprintf(`empty table fields for table "%s"`, m.tables))
|
||||
}
|
||||
fieldsArray := make([]string, len(tableFields))
|
||||
for k, v := range tableFields {
|
||||
fieldsArray[v.Index] = k
|
||||
}
|
||||
newFields := ""
|
||||
for _, k := range fieldsArray {
|
||||
if len(newFields) > 0 {
|
||||
newFields += ","
|
||||
}
|
||||
newFields += prefixStr + k
|
||||
}
|
||||
newFields = m.db.QuoteString(newFields)
|
||||
return newFields
|
||||
}
|
||||
|
||||
// FieldsExStr retrieves and returns fields which are not in parameter <fields> from the table,
|
||||
@ -78,17 +100,28 @@ func (m *Model) FieldsExStr(fields string, prefix ...string) string {
|
||||
if len(prefix) > 0 {
|
||||
prefixStr = prefix[0]
|
||||
}
|
||||
if m, err := m.db.TableFields(m.tables); err == nil {
|
||||
fieldsArray := garray.NewStrArraySize(len(m), len(m))
|
||||
fieldsExSet := gset.NewStrSetFrom(gstr.SplitAndTrim(fields, ","))
|
||||
for _, field := range m {
|
||||
if fieldsExSet.Contains(field.Name) {
|
||||
continue
|
||||
}
|
||||
fieldsArray.Set(field.Index, prefixStr+field.Name)
|
||||
}
|
||||
fieldsArray.FilterEmpty()
|
||||
return fieldsArray.Join(",")
|
||||
tableFields, err := m.db.TableFields(m.tables)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ""
|
||||
if len(tableFields) == 0 {
|
||||
panic(fmt.Sprintf(`empty table fields for table "%s"`, m.tables))
|
||||
}
|
||||
fieldsExSet := gset.NewStrSetFrom(gstr.SplitAndTrim(fields, ","))
|
||||
fieldsArray := make([]string, len(tableFields))
|
||||
for k, v := range tableFields {
|
||||
fieldsArray[v.Index] = k
|
||||
}
|
||||
newFields := ""
|
||||
for _, k := range fieldsArray {
|
||||
if fieldsExSet.Contains(k) {
|
||||
continue
|
||||
}
|
||||
if len(newFields) > 0 {
|
||||
newFields += ","
|
||||
}
|
||||
newFields += prefixStr + k
|
||||
}
|
||||
newFields = m.db.QuoteString(newFields)
|
||||
return newFields
|
||||
}
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"reflect"
|
||||
@ -42,10 +41,12 @@ func (m *Model) All(where ...interface{}) (Result, error) {
|
||||
}
|
||||
conditionWhere += softDeletingCondition
|
||||
}
|
||||
return m.getAll(
|
||||
// DO NOT quote the m.fields where, in case of fields like:
|
||||
// DISTINCT t.user_id uid
|
||||
return m.doGetAll(
|
||||
fmt.Sprintf(
|
||||
"SELECT %s FROM %s%s",
|
||||
m.db.QuoteString(m.fields),
|
||||
m.fields,
|
||||
m.tables,
|
||||
conditionWhere+conditionExtra,
|
||||
),
|
||||
@ -169,9 +170,6 @@ func (m *Model) Struct(pointer interface{}, where ...interface{}) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(one) == 0 {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
return one.Struct(pointer)
|
||||
}
|
||||
|
||||
@ -196,9 +194,6 @@ func (m *Model) Structs(pointer interface{}, where ...interface{}) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(all) == 0 {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
return all.Structs(pointer)
|
||||
}
|
||||
|
||||
@ -249,7 +244,9 @@ func (m *Model) Count(where ...interface{}) (int, error) {
|
||||
}
|
||||
countFields := "COUNT(1)"
|
||||
if m.fields != "" && m.fields != "*" {
|
||||
countFields = fmt.Sprintf(`COUNT(%s)`, m.db.QuoteString(m.fields))
|
||||
// DO NOT quote the m.fields here, in case of fields like:
|
||||
// DISTINCT t.user_id uid
|
||||
countFields = fmt.Sprintf(`COUNT(%s)`, m.fields)
|
||||
}
|
||||
var (
|
||||
softDeletingCondition = m.getConditionForSoftDeleting()
|
||||
@ -268,7 +265,7 @@ func (m *Model) Count(where ...interface{}) (int, error) {
|
||||
if len(m.groupBy) > 0 {
|
||||
s = fmt.Sprintf("SELECT COUNT(1) FROM (%s) count_alias", s)
|
||||
}
|
||||
list, err := m.getAll(s, conditionArgs...)
|
||||
list, err := m.doGetAll(s, conditionArgs...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -340,3 +337,28 @@ func (m *Model) FindScan(pointer interface{}, where ...interface{}) error {
|
||||
}
|
||||
return m.Scan(pointer)
|
||||
}
|
||||
|
||||
// doGetAll does the select statement on the database.
|
||||
func (m *Model) doGetAll(sql string, args ...interface{}) (result Result, err error) {
|
||||
cacheKey := ""
|
||||
// Retrieve from cache.
|
||||
if m.cacheEnabled && m.tx == nil {
|
||||
cacheKey = m.cacheName
|
||||
if len(cacheKey) == 0 {
|
||||
cacheKey = sql + "/" + gconv.String(args)
|
||||
}
|
||||
if v := m.db.GetCache().Get(cacheKey); v != nil {
|
||||
return v.(Result), nil
|
||||
}
|
||||
}
|
||||
result, err = m.db.DoGetAll(m.getLink(false), sql, m.mergeArguments(args)...)
|
||||
// Cache the result.
|
||||
if cacheKey != "" && err == nil {
|
||||
if m.cacheDuration < 0 {
|
||||
m.db.GetCache().Remove(cacheKey)
|
||||
} else {
|
||||
m.db.GetCache().Set(cacheKey, result, m.cacheDuration)
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
@ -25,39 +25,39 @@ const (
|
||||
// If there's no field name for storing creating time, it returns an empty string.
|
||||
// It checks the key with or without cases or chars '-'/'_'/'.'/' '.
|
||||
func (m *Model) getSoftFieldNameCreate(table ...string) string {
|
||||
name := ""
|
||||
tableName := ""
|
||||
if len(table) > 0 {
|
||||
name = table[0]
|
||||
tableName = table[0]
|
||||
} else {
|
||||
name = m.getPrimaryTableName()
|
||||
tableName = m.getPrimaryTableName()
|
||||
}
|
||||
return m.getSoftFieldName(name, gSOFT_FIELD_NAME_CREATE)
|
||||
return m.getSoftFieldName(tableName, gSOFT_FIELD_NAME_CREATE)
|
||||
}
|
||||
|
||||
// getSoftFieldNameUpdate checks and returns the field name for record updating time.
|
||||
// If there's no field name for storing updating time, it returns an empty string.
|
||||
// It checks the key with or without cases or chars '-'/'_'/'.'/' '.
|
||||
func (m *Model) getSoftFieldNameUpdate(table ...string) (field string) {
|
||||
name := ""
|
||||
tableName := ""
|
||||
if len(table) > 0 {
|
||||
name = table[0]
|
||||
tableName = table[0]
|
||||
} else {
|
||||
name = m.getPrimaryTableName()
|
||||
tableName = m.getPrimaryTableName()
|
||||
}
|
||||
return m.getSoftFieldName(name, gSOFT_FIELD_NAME_UPDATE)
|
||||
return m.getSoftFieldName(tableName, gSOFT_FIELD_NAME_UPDATE)
|
||||
}
|
||||
|
||||
// getSoftFieldNameDelete checks and returns the field name for record deleting time.
|
||||
// If there's no field name for storing deleting time, it returns an empty string.
|
||||
// It checks the key with or without cases or chars '-'/'_'/'.'/' '.
|
||||
func (m *Model) getSoftFieldNameDelete(table ...string) (field string) {
|
||||
name := ""
|
||||
tableName := ""
|
||||
if len(table) > 0 {
|
||||
name = table[0]
|
||||
tableName = table[0]
|
||||
} else {
|
||||
name = m.getPrimaryTableName()
|
||||
tableName = m.getPrimaryTableName()
|
||||
}
|
||||
return m.getSoftFieldName(name, gSOFT_FIELD_NAME_DELETE)
|
||||
return m.getSoftFieldName(tableName, gSOFT_FIELD_NAME_DELETE)
|
||||
}
|
||||
|
||||
// getSoftFieldName retrieves and returns the field name of the table for possible key.
|
||||
|
||||
@ -80,6 +80,9 @@ func (m *Model) doFilterDataMapForInsertOrUpdate(data Map, allowOmitEmpty bool)
|
||||
charL, charR = m.db.GetChars()
|
||||
chars = charL + charR
|
||||
)
|
||||
set.Walk(func(item string) string {
|
||||
return gstr.Trim(item, chars)
|
||||
})
|
||||
for k := range data {
|
||||
k = gstr.Trim(k, chars)
|
||||
if !set.Contains(k) {
|
||||
@ -143,13 +146,6 @@ func (m *Model) getPrimaryKey() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// checkAndRemoveCache checks and remove the cache if necessary.
|
||||
func (m *Model) checkAndRemoveCache() {
|
||||
if m.cacheEnabled && m.cacheDuration < 0 && len(m.cacheName) > 0 {
|
||||
m.db.GetCache().Remove(m.cacheName)
|
||||
}
|
||||
}
|
||||
|
||||
// formatCondition formats where arguments of the model and returns a new condition sql and its arguments.
|
||||
// Note that this function does not change any attribute value of the <m>.
|
||||
//
|
||||
|
||||
@ -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",
|
||||
"smallmoney":
|
||||
if gstr.ContainsI(fieldType, "unsigned") {
|
||||
gconv.Uint(string(fieldValue))
|
||||
}
|
||||
return gconv.Int(string(fieldValue))
|
||||
|
||||
case "big_int", "bigint":
|
||||
case
|
||||
"big_int",
|
||||
"bigint",
|
||||
"bigserial",
|
||||
"money":
|
||||
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",
|
||||
"numeric":
|
||||
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"):
|
||||
|
||||
@ -8,10 +8,11 @@ package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
|
||||
"github.com/gogf/gf/encoding/gparser"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Json converts <r> to JSON format content.
|
||||
@ -42,10 +43,32 @@ func (r Record) GMap() *gmap.StrAnyMap {
|
||||
|
||||
// Struct converts <r> to a struct.
|
||||
// Note that the parameter <pointer> should be type of *struct/**struct.
|
||||
//
|
||||
// Note that it returns sql.ErrNoRows if <r> is empty.
|
||||
func (r Record) Struct(pointer interface{}) error {
|
||||
if r == nil {
|
||||
// If the record is empty, it returns error.
|
||||
if r.IsEmpty() {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
// Special handling for parameter type: reflect.Value
|
||||
if _, ok := pointer.(reflect.Value); ok {
|
||||
return mapToStruct(r.Map(), pointer)
|
||||
}
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(pointer)
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
if reflectKind != reflect.Ptr {
|
||||
return errors.New("parameter should be type of *struct/**struct")
|
||||
}
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
if reflectKind == reflect.Invalid {
|
||||
return errors.New("parameter is an invalid pointer, maybe nil")
|
||||
}
|
||||
if reflectKind != reflect.Ptr && reflectKind != reflect.Struct {
|
||||
return errors.New("parameter should be type of *struct/**struct")
|
||||
}
|
||||
return mapToStruct(r.Map(), pointer)
|
||||
}
|
||||
|
||||
|
||||
@ -6,36 +6,22 @@
|
||||
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/gogf/gf/encoding/gparser"
|
||||
)
|
||||
|
||||
// Deprecated.
|
||||
func (r Record) ToJson() string {
|
||||
content, _ := gparser.VarToJson(r.Map())
|
||||
return string(content)
|
||||
return r.Json()
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
func (r Record) ToXml(rootTag ...string) string {
|
||||
content, _ := gparser.VarToXml(r.Map(), rootTag...)
|
||||
return string(content)
|
||||
return r.Xml(rootTag...)
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
func (r Record) ToMap() Map {
|
||||
m := make(map[string]interface{})
|
||||
for k, v := range r {
|
||||
m[k] = v.Val()
|
||||
}
|
||||
return m
|
||||
return r.Map()
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
func (r Record) ToStruct(pointer interface{}) error {
|
||||
if r == nil {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
return mapToStruct(r.Map(), pointer)
|
||||
return r.Struct(pointer)
|
||||
}
|
||||
|
||||
@ -148,27 +148,48 @@ func (r Result) RecordKeyUint(key string) map[uint]Record {
|
||||
// Structs converts <r> to struct slice.
|
||||
// Note that the parameter <pointer> should be type of *[]struct/*[]*struct.
|
||||
func (r Result) Structs(pointer interface{}) (err error) {
|
||||
l := len(r)
|
||||
if l == 0 {
|
||||
return sql.ErrNoRows
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(pointer)
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
if reflectKind != reflect.Ptr {
|
||||
return fmt.Errorf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
|
||||
}
|
||||
t := reflect.TypeOf(pointer)
|
||||
if t.Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("pointer should be type of pointer, but got: %v", t.Kind())
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
if reflectKind != reflect.Slice && reflectKind != reflect.Array {
|
||||
return fmt.Errorf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
|
||||
}
|
||||
array := reflect.MakeSlice(t.Elem(), l, l)
|
||||
itemType := array.Index(0).Type()
|
||||
for i := 0; i < l; i++ {
|
||||
if itemType.Kind() == reflect.Ptr {
|
||||
length := len(r)
|
||||
if length == 0 {
|
||||
// The pointed slice is not empty.
|
||||
if reflectValue.Len() > 0 {
|
||||
// It here checks if it has struct item, which is already initialized.
|
||||
// It then returns error to warn the developer its empty and no conversion.
|
||||
if v := reflectValue.Index(0); v.Kind() != reflect.Ptr {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
}
|
||||
// Do nothing for empty struct slice.
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
reflectType = reflect.TypeOf(pointer)
|
||||
array = reflect.MakeSlice(reflectType.Elem(), length, length)
|
||||
itemType = array.Index(0).Type()
|
||||
itemKind = itemType.Kind()
|
||||
)
|
||||
for i := 0; i < length; i++ {
|
||||
if itemKind == reflect.Ptr {
|
||||
e := reflect.New(itemType.Elem()).Elem()
|
||||
if err = r[i].Struct(e); err != nil {
|
||||
return err
|
||||
return fmt.Errorf(`slice element conversion failed: %s`, err.Error())
|
||||
}
|
||||
array.Index(i).Set(e.Addr())
|
||||
} else {
|
||||
e := reflect.New(itemType).Elem()
|
||||
if err = r[i].Struct(e); err != nil {
|
||||
return err
|
||||
return fmt.Errorf(`slice element conversion failed: %s`, err.Error())
|
||||
}
|
||||
array.Index(i).Set(e)
|
||||
}
|
||||
|
||||
@ -6,128 +6,52 @@
|
||||
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/encoding/gparser"
|
||||
)
|
||||
|
||||
// Deprecated.
|
||||
func (r Result) ToJson() string {
|
||||
content, _ := gparser.VarToJson(r.List())
|
||||
return string(content)
|
||||
return r.Json()
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
func (r Result) ToXml(rootTag ...string) string {
|
||||
content, _ := gparser.VarToXml(r.List(), rootTag...)
|
||||
return string(content)
|
||||
return r.Xml(rootTag...)
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
func (r Result) ToList() List {
|
||||
l := make(List, len(r))
|
||||
for k, v := range r {
|
||||
l[k] = v.Map()
|
||||
}
|
||||
return l
|
||||
return r.List()
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
func (r Result) ToStringMap(key string) map[string]Map {
|
||||
m := make(map[string]Map)
|
||||
for _, item := range r {
|
||||
if v, ok := item[key]; ok {
|
||||
m[v.String()] = item.Map()
|
||||
}
|
||||
}
|
||||
return m
|
||||
return r.MapKeyStr(key)
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
func (r Result) ToIntMap(key string) map[int]Map {
|
||||
m := make(map[int]Map)
|
||||
for _, item := range r {
|
||||
if v, ok := item[key]; ok {
|
||||
m[v.Int()] = item.Map()
|
||||
}
|
||||
}
|
||||
return m
|
||||
return r.MapKeyInt(key)
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
func (r Result) ToUintMap(key string) map[uint]Map {
|
||||
m := make(map[uint]Map)
|
||||
for _, item := range r {
|
||||
if v, ok := item[key]; ok {
|
||||
m[v.Uint()] = item.Map()
|
||||
}
|
||||
}
|
||||
return m
|
||||
return r.MapKeyUint(key)
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
func (r Result) ToStringRecord(key string) map[string]Record {
|
||||
m := make(map[string]Record)
|
||||
for _, item := range r {
|
||||
if v, ok := item[key]; ok {
|
||||
m[v.String()] = item
|
||||
}
|
||||
}
|
||||
return m
|
||||
return r.RecordKeyStr(key)
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
func (r Result) ToIntRecord(key string) map[int]Record {
|
||||
m := make(map[int]Record)
|
||||
for _, item := range r {
|
||||
if v, ok := item[key]; ok {
|
||||
m[v.Int()] = item
|
||||
}
|
||||
}
|
||||
return m
|
||||
return r.RecordKeyInt(key)
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
func (r Result) ToUintRecord(key string) map[uint]Record {
|
||||
m := make(map[uint]Record)
|
||||
for _, item := range r {
|
||||
if v, ok := item[key]; ok {
|
||||
m[v.Uint()] = item
|
||||
}
|
||||
}
|
||||
return m
|
||||
return r.RecordKeyUint(key)
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
func (r Result) ToStructs(pointer interface{}) (err error) {
|
||||
l := len(r)
|
||||
if l == 0 {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
t := reflect.TypeOf(pointer)
|
||||
if t.Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("pointer should be type of pointer, but got: %v", t.Kind())
|
||||
}
|
||||
array := reflect.MakeSlice(t.Elem(), l, l)
|
||||
itemType := array.Index(0).Type()
|
||||
for i := 0; i < l; i++ {
|
||||
if itemType.Kind() == reflect.Ptr {
|
||||
e := reflect.New(itemType.Elem()).Elem()
|
||||
if err = r[i].Struct(e); err != nil {
|
||||
return err
|
||||
}
|
||||
array.Index(i).Set(e.Addr())
|
||||
} else {
|
||||
e := reflect.New(itemType).Elem()
|
||||
if err = r[i].Struct(e); err != nil {
|
||||
return err
|
||||
}
|
||||
array.Index(i).Set(e)
|
||||
}
|
||||
}
|
||||
reflect.ValueOf(pointer).Elem().Set(array)
|
||||
return nil
|
||||
return r.Structs(pointer)
|
||||
}
|
||||
|
||||
@ -820,7 +820,7 @@ func Test_Model_Structs(t *testing.T) {
|
||||
}
|
||||
var users []*User
|
||||
err := db.Table(table).Where("id<0").Structs(&users)
|
||||
t.Assert(err, sql.ErrNoRows)
|
||||
t.Assert(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -905,12 +905,14 @@ func Test_Model_Scan(t *testing.T) {
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
users := new([]*User)
|
||||
var (
|
||||
user = new(User)
|
||||
users = new([]*User)
|
||||
)
|
||||
err1 := db.Table(table).Where("id < 0").Scan(user)
|
||||
err2 := db.Table(table).Where("id < 0").Scan(users)
|
||||
t.Assert(err1, sql.ErrNoRows)
|
||||
t.Assert(err2, sql.ErrNoRows)
|
||||
t.Assert(err2, nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -1870,8 +1872,8 @@ func Test_Model_FieldsStr(t *testing.T) {
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(db.Table(table).FieldsStr(), "id,passport,password,nickname,create_time")
|
||||
t.Assert(db.Table(table).FieldsStr("a."), "a.id,a.passport,a.password,a.nickname,a.create_time")
|
||||
t.Assert(db.Table(table).FieldsStr(), "`id`,`passport`,`password`,`nickname`,`create_time`")
|
||||
t.Assert(db.Table(table).FieldsStr("a."), "`a`.`id`,`a`.`passport`,`a`.`password`,`a`.`nickname`,`a`.`create_time`")
|
||||
})
|
||||
}
|
||||
|
||||
@ -1880,8 +1882,8 @@ func Test_Model_FieldsExStr(t *testing.T) {
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(db.Table(table).FieldsExStr("create_time,nickname"), "id,passport,password")
|
||||
t.Assert(db.Table(table).FieldsExStr("create_time,nickname", "a."), "a.id,a.passport,a.password")
|
||||
t.Assert(db.Table(table).FieldsExStr("create_time,nickname"), "`id`,`passport`,`password`")
|
||||
t.Assert(db.Table(table).FieldsExStr("create_time,nickname", "a."), "`a`.`id`,`a`.`passport`,`a`.`password`")
|
||||
})
|
||||
}
|
||||
|
||||
@ -2213,6 +2215,63 @@ func Test_Model_Cache(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
t.Assert(one["passport"], "user_200")
|
||||
})
|
||||
// transaction.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
// make cache for id 3
|
||||
one, err := db.Table(table).Cache(time.Second, "test3").FindOne(3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(one["passport"], "user_3")
|
||||
|
||||
r, err := db.Table(table).Data("passport", "user_300").Cache(time.Second, "test3").WherePri(3).Update()
|
||||
t.Assert(err, nil)
|
||||
n, err := r.RowsAffected()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(n, 1)
|
||||
|
||||
err = db.Transaction(func(tx *gdb.TX) error {
|
||||
one, err := tx.Table(table).Cache(time.Second, "test3").FindOne(3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(one["passport"], "user_300")
|
||||
return nil
|
||||
})
|
||||
t.Assert(err, nil)
|
||||
|
||||
one, err = db.Table(table).Cache(time.Second, "test3").FindOne(3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(one["passport"], "user_3")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
// make cache for id 4
|
||||
one, err := db.Table(table).Cache(time.Second, "test4").FindOne(4)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(one["passport"], "user_4")
|
||||
|
||||
r, err := db.Table(table).Data("passport", "user_400").Cache(time.Second, "test3").WherePri(4).Update()
|
||||
t.Assert(err, nil)
|
||||
n, err := r.RowsAffected()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(n, 1)
|
||||
|
||||
err = db.Transaction(func(tx *gdb.TX) error {
|
||||
// Cache feature disabled.
|
||||
one, err := tx.Table(table).Cache(time.Second, "test4").FindOne(4)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(one["passport"], "user_400")
|
||||
// Update the cache.
|
||||
r, err := tx.Table(table).Data("passport", "user_4000").
|
||||
Cache(-1, "test4").WherePri(4).Update()
|
||||
t.Assert(err, nil)
|
||||
n, err := r.RowsAffected()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(n, 1)
|
||||
return nil
|
||||
})
|
||||
t.Assert(err, nil)
|
||||
// Read from db.
|
||||
one, err = db.Table(table).Cache(time.Second, "test4").FindOne(4)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(one["passport"], "user_4000")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Having(t *testing.T) {
|
||||
@ -2240,3 +2299,58 @@ func Test_Model_Having(t *testing.T) {
|
||||
t.Assert(len(all), 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Distinct(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
all, err := db.Table(table, "t").Fields("distinct t.id").Where("id > 1").Having("id > 8").All()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(all), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Min_Max(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
value, err := db.Table(table, "t").Fields("min(t.id)").Where("id > 1").Value()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(value.Int(), 2)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
value, err := db.Table(table, "t").Fields("max(t.id)").Where("id > 1").Value()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(value.Int(), 10)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_NullField(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport *string
|
||||
}
|
||||
data := g.Map{
|
||||
"id": 1,
|
||||
"passport": nil,
|
||||
}
|
||||
result, err := db.Table(table).Data(data).Insert()
|
||||
t.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
one, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
|
||||
var user *User
|
||||
err = one.Struct(&user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Id, data["id"])
|
||||
t.Assert(user.Passport, data["passport"])
|
||||
})
|
||||
}
|
||||
|
||||
@ -94,5 +94,231 @@ func Test_Model_Inherit_MapToStruct(t *testing.T) {
|
||||
t.Assert(user.CreateTime, data["create_time"])
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
Nickname string
|
||||
}
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).Where("id=100").One()
|
||||
t.Assert(err, nil)
|
||||
user := new(User)
|
||||
t.AssertNE(one.Struct(user), nil)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).Where("id=100").One()
|
||||
t.Assert(err, nil)
|
||||
var user *User
|
||||
t.AssertNE(one.Struct(&user), nil)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).Where("id=100").One()
|
||||
t.Assert(err, nil)
|
||||
var user *User
|
||||
t.AssertNE(one.Struct(user), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Structs_Empty(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
Nickname string
|
||||
}
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
all, err := db.Table(table).Where("id>100").All()
|
||||
t.Assert(err, nil)
|
||||
users := make([]User, 0)
|
||||
t.Assert(all.Structs(&users), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
all, err := db.Table(table).Where("id>100").All()
|
||||
t.Assert(err, nil)
|
||||
users := make([]User, 10)
|
||||
t.AssertNE(all.Structs(&users), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
all, err := db.Table(table).Where("id>100").All()
|
||||
t.Assert(err, nil)
|
||||
var users []User
|
||||
t.Assert(all.Structs(&users), nil)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
all, err := db.Table(table).Where("id>100").All()
|
||||
t.Assert(err, nil)
|
||||
users := make([]*User, 0)
|
||||
t.Assert(all.Structs(&users), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
all, err := db.Table(table).Where("id>100").All()
|
||||
t.Assert(err, nil)
|
||||
users := make([]*User, 10)
|
||||
t.Assert(all.Structs(&users), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
all, err := db.Table(table).Where("id>100").All()
|
||||
t.Assert(err, nil)
|
||||
var users []*User
|
||||
t.Assert(all.Structs(&users), nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -7,7 +7,9 @@
|
||||
package gdb_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/frame/g"
|
||||
@ -300,7 +302,6 @@ func Test_TX_Replace(t *testing.T) {
|
||||
t.Assert(value.String(), "name_1")
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Test_TX_Save(t *testing.T) {
|
||||
@ -713,5 +714,53 @@ func Test_TX_Delete(t *testing.T) {
|
||||
t.AssertNE(n, 0)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Test_Transaction(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
err := db.Transaction(func(tx *gdb.TX) error {
|
||||
if _, err := tx.Replace(table, g.Map{
|
||||
"id": 1,
|
||||
"passport": "USER_1",
|
||||
"password": "PASS_1",
|
||||
"nickname": "NAME_1",
|
||||
"create_time": gtime.Now().String(),
|
||||
}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
return errors.New("error")
|
||||
})
|
||||
t.AssertNE(err, nil)
|
||||
|
||||
if value, err := db.Table(table).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(value.String(), "name_1")
|
||||
}
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
err := db.Transaction(func(tx *gdb.TX) error {
|
||||
if _, err := tx.Replace(table, g.Map{
|
||||
"id": 1,
|
||||
"passport": "USER_1",
|
||||
"password": "PASS_1",
|
||||
"nickname": "NAME_1",
|
||||
"create_time": gtime.Now().String(),
|
||||
}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
t.Assert(err, nil)
|
||||
|
||||
if value, err := db.Table(table).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(value.String(), "NAME_1")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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")
|
||||
@ -265,7 +265,7 @@ func Test_HGetAll(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")
|
||||
@ -281,3 +281,39 @@ func Test_HGetAll(t *testing.T) {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
@ -58,6 +58,10 @@ func StackWithFilters(filters []string, skip ...int) string {
|
||||
pc, file, line, ok = runtime.Caller(i)
|
||||
}
|
||||
if ok {
|
||||
// Filter empty file.
|
||||
if file == "" {
|
||||
continue
|
||||
}
|
||||
// GOROOT filter.
|
||||
if goRootForFilter != "" &&
|
||||
len(file) >= len(goRootForFilter) &&
|
||||
|
||||
@ -19,9 +19,11 @@ import (
|
||||
//
|
||||
// Note that it returns error if given <level> is invalid.
|
||||
func Gzip(data []byte, level ...int) ([]byte, error) {
|
||||
var writer *gzip.Writer
|
||||
var buf bytes.Buffer
|
||||
var err error
|
||||
var (
|
||||
writer *gzip.Writer
|
||||
buf bytes.Buffer
|
||||
err error
|
||||
)
|
||||
if len(level) > 0 {
|
||||
writer, err = gzip.NewWriterLevel(&buf, level[0])
|
||||
if err != nil {
|
||||
@ -41,8 +43,10 @@ func Gzip(data []byte, level ...int) ([]byte, error) {
|
||||
|
||||
// GzipFile compresses the file <src> to <dst> using gzip algorithm.
|
||||
func GzipFile(src, dst string, level ...int) error {
|
||||
var writer *gzip.Writer
|
||||
var err error
|
||||
var (
|
||||
writer *gzip.Writer
|
||||
err error
|
||||
)
|
||||
srcFile, err := gfile.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@ -24,25 +24,88 @@ func Test_ZipPath(t *testing.T) {
|
||||
dstPath := gdebug.TestDataPath("zip", "zip.zip")
|
||||
|
||||
t.Assert(gfile.Exists(dstPath), false)
|
||||
err := gcompress.ZipPath(srcPath, dstPath)
|
||||
t.Assert(gcompress.ZipPath(srcPath, dstPath), nil)
|
||||
t.Assert(gfile.Exists(dstPath), true)
|
||||
defer gfile.Remove(dstPath)
|
||||
|
||||
// unzip to temporary dir.
|
||||
tempDirPath := gfile.TempDir(gtime.TimestampNanoStr())
|
||||
t.Assert(gfile.Mkdir(tempDirPath), nil)
|
||||
t.Assert(gcompress.UnZipFile(dstPath, tempDirPath), nil)
|
||||
defer gfile.Remove(tempDirPath)
|
||||
|
||||
t.Assert(
|
||||
gfile.GetContents(gfile.Join(tempDirPath, "1.txt")),
|
||||
gfile.GetContents(srcPath),
|
||||
)
|
||||
})
|
||||
// multiple files
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
srcPath1 = gdebug.TestDataPath("zip", "path1", "1.txt")
|
||||
srcPath2 = gdebug.TestDataPath("zip", "path2", "2.txt")
|
||||
dstPath = gfile.TempDir(gtime.TimestampNanoStr(), "zip.zip")
|
||||
)
|
||||
if p := gfile.Dir(dstPath); !gfile.Exists(p) {
|
||||
t.Assert(gfile.Mkdir(p), nil)
|
||||
}
|
||||
|
||||
t.Assert(gfile.Exists(dstPath), false)
|
||||
err := gcompress.ZipPath(srcPath1+","+srcPath2, dstPath)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(gfile.Exists(dstPath), true)
|
||||
defer gfile.Remove(dstPath)
|
||||
|
||||
// unzip to another temporary dir.
|
||||
tempDirPath := gfile.TempDir(gtime.TimestampNanoStr())
|
||||
err = gfile.Mkdir(tempDirPath)
|
||||
t.Assert(err, nil)
|
||||
|
||||
t.Assert(gfile.Mkdir(tempDirPath), nil)
|
||||
err = gcompress.UnZipFile(dstPath, tempDirPath)
|
||||
t.Assert(err, nil)
|
||||
defer gfile.Remove(tempDirPath)
|
||||
|
||||
t.Assert(
|
||||
gfile.GetContents(gfile.Join(tempDirPath, "1.txt")),
|
||||
gfile.GetContents(gfile.Join(srcPath, "path1", "1.txt")),
|
||||
gfile.GetContents(srcPath1),
|
||||
)
|
||||
t.Assert(
|
||||
gfile.GetContents(gfile.Join(tempDirPath, "2.txt")),
|
||||
gfile.GetContents(srcPath2),
|
||||
)
|
||||
})
|
||||
// directory
|
||||
// one dir and one file.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
srcPath1 = gdebug.TestDataPath("zip", "path1")
|
||||
srcPath2 = gdebug.TestDataPath("zip", "path2", "2.txt")
|
||||
dstPath = gfile.TempDir(gtime.TimestampNanoStr(), "zip.zip")
|
||||
)
|
||||
if p := gfile.Dir(dstPath); !gfile.Exists(p) {
|
||||
t.Assert(gfile.Mkdir(p), nil)
|
||||
}
|
||||
|
||||
t.Assert(gfile.Exists(dstPath), false)
|
||||
err := gcompress.ZipPath(srcPath1+","+srcPath2, dstPath)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(gfile.Exists(dstPath), true)
|
||||
defer gfile.Remove(dstPath)
|
||||
|
||||
// unzip to another temporary dir.
|
||||
tempDirPath := gfile.TempDir(gtime.TimestampNanoStr())
|
||||
t.Assert(gfile.Mkdir(tempDirPath), nil)
|
||||
err = gcompress.UnZipFile(dstPath, tempDirPath)
|
||||
t.Assert(err, nil)
|
||||
defer gfile.Remove(tempDirPath)
|
||||
|
||||
t.Assert(
|
||||
gfile.GetContents(gfile.Join(tempDirPath, "path1", "1.txt")),
|
||||
gfile.GetContents(gfile.Join(srcPath1, "1.txt")),
|
||||
)
|
||||
t.Assert(
|
||||
gfile.GetContents(gfile.Join(tempDirPath, "2.txt")),
|
||||
gfile.GetContents(srcPath2),
|
||||
)
|
||||
})
|
||||
// directory.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
srcPath := gdebug.TestDataPath("zip")
|
||||
dstPath := gdebug.TestDataPath("zip", "zip.zip")
|
||||
@ -75,13 +138,14 @@ func Test_ZipPath(t *testing.T) {
|
||||
gfile.GetContents(gfile.Join(srcPath, "path2", "2.txt")),
|
||||
)
|
||||
})
|
||||
// multiple paths joined using char ','
|
||||
// multiple directory paths joined using char ','.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
srcPath := gdebug.TestDataPath("zip")
|
||||
srcPath1 := gdebug.TestDataPath("zip", "path1")
|
||||
srcPath2 := gdebug.TestDataPath("zip", "path2")
|
||||
dstPath := gdebug.TestDataPath("zip", "zip.zip")
|
||||
|
||||
var (
|
||||
srcPath = gdebug.TestDataPath("zip")
|
||||
srcPath1 = gdebug.TestDataPath("zip", "path1")
|
||||
srcPath2 = gdebug.TestDataPath("zip", "path2")
|
||||
dstPath = gdebug.TestDataPath("zip", "zip.zip")
|
||||
)
|
||||
pwd := gfile.Pwd()
|
||||
err := gfile.Chdir(srcPath)
|
||||
defer gfile.Chdir(pwd)
|
||||
@ -116,10 +180,11 @@ func Test_ZipPath(t *testing.T) {
|
||||
|
||||
func Test_ZipPathWriter(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
srcPath := gdebug.TestDataPath("zip")
|
||||
srcPath1 := gdebug.TestDataPath("zip", "path1")
|
||||
srcPath2 := gdebug.TestDataPath("zip", "path2")
|
||||
|
||||
var (
|
||||
srcPath = gdebug.TestDataPath("zip")
|
||||
srcPath1 = gdebug.TestDataPath("zip", "path1")
|
||||
srcPath2 = gdebug.TestDataPath("zip", "path2")
|
||||
)
|
||||
pwd := gfile.Pwd()
|
||||
err := gfile.Chdir(srcPath)
|
||||
defer gfile.Chdir(pwd)
|
||||
|
||||
@ -10,16 +10,12 @@ import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/internal/fileinfo"
|
||||
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
)
|
||||
|
||||
// ZipPath compresses <paths> to <dest> using zip compressing algorithm.
|
||||
@ -85,11 +81,13 @@ func doZipPathWriter(path string, exclude string, zipWriter *zip.Writer, prefix
|
||||
headerPrefix = prefix[0]
|
||||
}
|
||||
headerPrefix = strings.TrimRight(headerPrefix, "\\/")
|
||||
if len(headerPrefix) > 0 && gfile.IsDir(path) {
|
||||
headerPrefix += "/"
|
||||
}
|
||||
if headerPrefix == "" {
|
||||
headerPrefix = gfile.Basename(path)
|
||||
if gfile.IsDir(path) {
|
||||
if len(headerPrefix) > 0 {
|
||||
headerPrefix += "/"
|
||||
} else {
|
||||
headerPrefix = gfile.Basename(path)
|
||||
}
|
||||
|
||||
}
|
||||
headerPrefix = strings.Replace(headerPrefix, "//", "/", -1)
|
||||
for _, file := range files {
|
||||
@ -97,23 +95,15 @@ func doZipPathWriter(path string, exclude string, zipWriter *zip.Writer, prefix
|
||||
intlog.Printf(`exclude file path: %s`, file)
|
||||
continue
|
||||
}
|
||||
err := zipFile(file, headerPrefix+gfile.Dir(file[len(path):]), zipWriter)
|
||||
dir := gfile.Dir(file[len(path):])
|
||||
if dir == "." {
|
||||
dir = ""
|
||||
}
|
||||
err := zipFile(file, headerPrefix+dir, zipWriter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Add prefix to zip archive.
|
||||
path = headerPrefix
|
||||
for {
|
||||
err := zipFileVirtual(fileinfo.New(gfile.Basename(path), 0, os.ModeDir, time.Now()), path, zipWriter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if path == "/" || !strings.Contains(path, "/") {
|
||||
break
|
||||
}
|
||||
path = gfile.Dir(path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -203,14 +193,23 @@ func zipFile(path string, prefix string, zw *zip.Writer) error {
|
||||
return nil
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
info, err := file.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
header, err := createFileHeader(info, prefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
header.Name += "/"
|
||||
} else {
|
||||
header.Method = zip.Deflate
|
||||
}
|
||||
|
||||
writer, err := zw.CreateHeader(header)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -223,23 +222,12 @@ func zipFile(path string, prefix string, zw *zip.Writer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func zipFileVirtual(info os.FileInfo, path string, zw *zip.Writer) error {
|
||||
header, err := createFileHeader(info, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
header.Name = path
|
||||
if _, err := zw.CreateHeader(header); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createFileHeader(info os.FileInfo, prefix string) (*zip.FileHeader, error) {
|
||||
header, err := zip.FileInfoHeader(info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(prefix) > 0 {
|
||||
prefix = strings.Replace(prefix, `\`, `/`, -1)
|
||||
prefix = strings.TrimRight(prefix, `/`)
|
||||
|
||||
@ -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++ {
|
||||
|
||||
@ -35,8 +35,8 @@ func (j *Json) IsNil() bool {
|
||||
// It returns all values of current Json object if <pattern> is given empty or string ".".
|
||||
// It returns nil if no value found by <pattern>.
|
||||
//
|
||||
// We can also access slice item by its index number in <pattern>,
|
||||
// eg: "items.name.first", "list.10".
|
||||
// We can also access slice item by its index number in <pattern> like:
|
||||
// "list.10", "array.0.name", "array.0.1.id".
|
||||
//
|
||||
// It returns a default value specified by <def> if value for <pattern> is not found.
|
||||
func (j *Json) Get(pattern string, def ...interface{}) interface{} {
|
||||
@ -78,8 +78,7 @@ func (j *Json) GetVars(pattern string, def ...interface{}) []*gvar.Var {
|
||||
return gvar.New(j.Get(pattern, def...)).Vars()
|
||||
}
|
||||
|
||||
// GetMap retrieves the value by specified <pattern>,
|
||||
// and converts it to map[string]interface{}.
|
||||
// GetMap retrieves and returns the value by specified <pattern> as map[string]interface{}.
|
||||
func (j *Json) GetMap(pattern string, def ...interface{}) map[string]interface{} {
|
||||
result := j.Get(pattern, def...)
|
||||
if result != nil {
|
||||
@ -88,8 +87,7 @@ func (j *Json) GetMap(pattern string, def ...interface{}) map[string]interface{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetMapStrStr retrieves the value by specified <pattern>,
|
||||
// and converts it to map[string]string.
|
||||
// GetMapStrStr retrieves and returns the value by specified <pattern> as map[string]string.
|
||||
func (j *Json) GetMapStrStr(pattern string, def ...interface{}) map[string]string {
|
||||
result := j.Get(pattern, def...)
|
||||
if result != nil {
|
||||
@ -98,6 +96,15 @@ func (j *Json) GetMapStrStr(pattern string, def ...interface{}) map[string]strin
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetMaps retrieves and returns the value by specified <pattern> as []map[string]interface{}.
|
||||
func (j *Json) GetMaps(pattern string, def ...interface{}) []map[string]interface{} {
|
||||
result := j.Get(pattern, def...)
|
||||
if result != nil {
|
||||
return gconv.Maps(result)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetJson gets the value by specified <pattern>,
|
||||
// and converts it to a un-concurrent-safe Json object.
|
||||
func (j *Json) GetJson(pattern string, def ...interface{}) *Json {
|
||||
@ -323,25 +330,28 @@ func (j *Json) GetStructsDeep(pattern string, pointer interface{}, mapping ...ma
|
||||
}
|
||||
|
||||
// GetMapToMap retrieves the value by specified <pattern> and converts it specified map variable.
|
||||
// The parameter of <pointer> should be type of *map.
|
||||
// See gconv.MapToMap.
|
||||
func (j *Json) GetMapToMap(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.MapToMap(j.Get(pattern), pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetMapToMapDeep retrieves the value by specified <pattern> and converts it specified map
|
||||
// variable recursively. The parameter of <pointer> should be type of *map.
|
||||
// variable recursively.
|
||||
// See gconv.MapToMapDeep.
|
||||
func (j *Json) GetMapToMapDeep(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.MapToMapDeep(j.Get(pattern), pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetMapToMaps retrieves the value by specified <pattern> and converts it specified map slice
|
||||
// variable. The parameter of <pointer> should be type of []map/*map.
|
||||
// variable.
|
||||
// See gconv.MapToMaps.
|
||||
func (j *Json) GetMapToMaps(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.MapToMaps(j.Get(pattern), pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetMapToMapsDeep retrieves the value by specified <pattern> and converts it specified map slice
|
||||
// variable recursively. The parameter of <pointer> should be type of []map/*map.
|
||||
// variable recursively.
|
||||
// See gconv.MapToMapsDeep.
|
||||
func (j *Json) GetMapToMapsDeep(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.MapToMapsDeep(j.Get(pattern), pointer, mapping...)
|
||||
}
|
||||
|
||||
@ -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",
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -5,21 +5,28 @@
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package errors provides simple functions to manipulate errors.
|
||||
//
|
||||
// Very note that, this package is quite a base package, which should not import extra
|
||||
// packages except standard packages, to avoid cycle imports.
|
||||
package gerror
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ApiStack is the interface for Stack feature.
|
||||
type ApiStack interface {
|
||||
Error() string // It should be en error.
|
||||
Stack() string
|
||||
}
|
||||
|
||||
// ApiCause is the interface for Cause feature.
|
||||
type ApiCause interface {
|
||||
Error() string // It should be en error.
|
||||
Cause() error
|
||||
}
|
||||
|
||||
// New returns an error that formats as the given text.
|
||||
// New creates and returns an error which is formatted from given text.
|
||||
func New(text string) error {
|
||||
if text == "" {
|
||||
return nil
|
||||
@ -30,7 +37,19 @@ func New(text string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Newf returns an error that formats as the given text.
|
||||
// NewSkip creates and returns an error which is formatted from given text.
|
||||
// The parameter <skip> specifies the stack callers skipped amount.
|
||||
func NewSkip(skip int, text string) error {
|
||||
if text == "" {
|
||||
return nil
|
||||
}
|
||||
return &Error{
|
||||
stack: callers(skip),
|
||||
text: text,
|
||||
}
|
||||
}
|
||||
|
||||
// Newf returns an error that formats as the given format and args.
|
||||
func Newf(format string, args ...interface{}) error {
|
||||
if format == "" {
|
||||
return nil
|
||||
@ -41,6 +60,18 @@ func Newf(format string, args ...interface{}) error {
|
||||
}
|
||||
}
|
||||
|
||||
// NewfSkip returns an error that formats as the given format and args.
|
||||
// The parameter <skip> specifies the stack callers skipped amount.
|
||||
func NewfSkip(skip int, format string, args ...interface{}) error {
|
||||
if format == "" {
|
||||
return nil
|
||||
}
|
||||
return &Error{
|
||||
stack: callers(skip),
|
||||
text: fmt.Sprintf(format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap wraps error with text.
|
||||
// It returns nil if given err is nil.
|
||||
func Wrap(err error, text string) error {
|
||||
@ -56,7 +87,7 @@ func Wrap(err error, text string) error {
|
||||
|
||||
// Wrapf returns an error annotating err with a stack trace
|
||||
// at the point Wrapf is called, and the format specifier.
|
||||
// It returns nil if given err is nil.
|
||||
// It returns nil if given <err> is nil.
|
||||
func Wrapf(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
@ -68,7 +99,7 @@ func Wrapf(err error, format string, args ...interface{}) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Cause returns the root cause error.
|
||||
// Cause returns the root cause error of <err>.
|
||||
func Cause(err error) error {
|
||||
if err != nil {
|
||||
if e, ok := err.(ApiCause); ok {
|
||||
@ -79,7 +110,7 @@ func Cause(err error) error {
|
||||
}
|
||||
|
||||
// Stack returns the stack callers as string.
|
||||
// It returns an empty string id the <err> does not support stacks.
|
||||
// It returns an empty string if the <err> does not support stacks.
|
||||
func Stack(err error) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
|
||||
@ -9,7 +9,6 @@ package gerror
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"io"
|
||||
"runtime"
|
||||
"strings"
|
||||
@ -94,7 +93,7 @@ func (err *Error) Format(s fmt.State, verb rune) {
|
||||
}
|
||||
|
||||
// Stack returns the stack callers as string.
|
||||
// It returns an empty string id the <err> does not support stacks.
|
||||
// It returns an empty string if the <err> does not support stacks.
|
||||
func (err *Error) Stack() string {
|
||||
if err == nil {
|
||||
return ""
|
||||
@ -131,15 +130,6 @@ func formatSubStack(st stack, buffer *bytes.Buffer) {
|
||||
if strings.Contains(file, gFILTER_KEY) {
|
||||
continue
|
||||
}
|
||||
// Avoid GF stacks if not in GF development.
|
||||
if !intlog.IsEnabled() {
|
||||
if strings.Contains(file, "github.com/gogf/gf/") {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(file, "github.com/gogf/gf@") {
|
||||
continue
|
||||
}
|
||||
}
|
||||
// Avoid stack string like "<autogenerated>"
|
||||
if strings.Contains(file, "<") {
|
||||
continue
|
||||
|
||||
@ -15,8 +15,14 @@ const (
|
||||
gMAX_STACK_DEPTH = 32
|
||||
)
|
||||
|
||||
func callers() stack {
|
||||
var pcs [gMAX_STACK_DEPTH]uintptr
|
||||
n := runtime.Callers(3, pcs[:])
|
||||
return pcs[0:n]
|
||||
// callers returns the stack callers.
|
||||
func callers(skip ...int) stack {
|
||||
var (
|
||||
pcs [gMAX_STACK_DEPTH]uintptr
|
||||
n = 3
|
||||
)
|
||||
if len(skip) > 0 {
|
||||
n += skip[0]
|
||||
}
|
||||
return pcs[:runtime.Callers(n, pcs[:])]
|
||||
}
|
||||
|
||||
@ -15,10 +15,6 @@ import (
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
func interfaceNil() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func nilError() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
7
go.mod
7
go.mod
@ -1,20 +1,19 @@
|
||||
module github.com/gogf/gf
|
||||
|
||||
go 1.13
|
||||
go 1.11
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/clbanning/mxj v1.8.4
|
||||
github.com/fatih/structs v1.1.0
|
||||
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 {
|
||||
@ -70,20 +72,23 @@ func New(options ...Options) *Manager {
|
||||
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,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 empty provides checks for empty variables.
|
||||
// Package empty provides functions for checking empty variables.
|
||||
package empty
|
||||
|
||||
import (
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
// Package structs provides functions for struct conversion.
|
||||
package structs
|
||||
|
||||
import "github.com/fatih/structs"
|
||||
import "github.com/gqcn/structs"
|
||||
|
||||
// Field is alias of structs.Field.
|
||||
type Field struct {
|
||||
|
||||
@ -9,7 +9,7 @@ package structs
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/fatih/structs"
|
||||
"github.com/gqcn/structs"
|
||||
)
|
||||
|
||||
// MapField retrieves struct field as map[name/tag]*Field from <pointer>, and returns the map.
|
||||
@ -22,15 +22,19 @@ import (
|
||||
//
|
||||
// Note that it only retrieves the exported attributes with first letter up-case from struct.
|
||||
func MapField(pointer interface{}, priority []string, recursive bool) map[string]*Field {
|
||||
fieldMap := make(map[string]*Field)
|
||||
fields := ([]*structs.Field)(nil)
|
||||
var (
|
||||
fields []*structs.Field
|
||||
fieldMap = make(map[string]*Field)
|
||||
)
|
||||
if v, ok := pointer.(reflect.Value); ok {
|
||||
fields = structs.Fields(v.Interface())
|
||||
} else {
|
||||
fields = structs.Fields(pointer)
|
||||
}
|
||||
tag := ""
|
||||
name := ""
|
||||
var (
|
||||
tag = ""
|
||||
name = ""
|
||||
)
|
||||
for _, field := range fields {
|
||||
name = field.Name()
|
||||
// Only retrieve exported attributes.
|
||||
|
||||
@ -9,7 +9,7 @@ package structs
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/fatih/structs"
|
||||
"github.com/gqcn/structs"
|
||||
)
|
||||
|
||||
// TagFields retrieves struct tags as []*Field from <pointer>, and returns it.
|
||||
@ -31,8 +31,10 @@ func doTagFields(pointer interface{}, priority []string, recursive bool, tagMap
|
||||
if v, ok := pointer.(reflect.Value); ok {
|
||||
fields = structs.Fields(v.Interface())
|
||||
} else {
|
||||
rv := reflect.ValueOf(pointer)
|
||||
kind := rv.Kind()
|
||||
var (
|
||||
rv = reflect.ValueOf(pointer)
|
||||
kind = rv.Kind()
|
||||
)
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
@ -45,8 +47,10 @@ func doTagFields(pointer interface{}, priority []string, recursive bool, tagMap
|
||||
fields = structs.Fields(pointer)
|
||||
}
|
||||
}
|
||||
tag := ""
|
||||
name := ""
|
||||
var (
|
||||
tag = ""
|
||||
name = ""
|
||||
)
|
||||
tagFields := make([]*Field, 0)
|
||||
for _, field := range fields {
|
||||
name = field.Name()
|
||||
@ -72,8 +76,10 @@ func doTagFields(pointer interface{}, priority []string, recursive bool, tagMap
|
||||
})
|
||||
}
|
||||
if recursive {
|
||||
rv := reflect.ValueOf(field.Value())
|
||||
kind := rv.Kind()
|
||||
var (
|
||||
rv = reflect.ValueOf(field.Value())
|
||||
kind = rv.Kind()
|
||||
)
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
|
||||
@ -6,9 +6,3 @@
|
||||
|
||||
// Package ghttp provides powerful http server and simple client implements.
|
||||
package ghttp
|
||||
|
||||
var (
|
||||
// paramTagPriority is the priority tag array for request parameter
|
||||
// to struct field mapping.
|
||||
paramTagPriority = []string{"param", "params", "p"}
|
||||
)
|
||||
|
||||
@ -43,7 +43,6 @@ func NewClient() *Client {
|
||||
DisableKeepAlives: true,
|
||||
},
|
||||
},
|
||||
ctx: context.Background(),
|
||||
header: make(map[string]string),
|
||||
cookies: make(map[string]string),
|
||||
}
|
||||
|
||||
118
net/ghttp/ghttp_client_dump.go
Normal file
118
net/ghttp/ghttp_client_dump.go
Normal file
@ -0,0 +1,118 @@
|
||||
// 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 ghttp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"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
|
||||
`
|
||||
|
||||
// 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) {
|
||||
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)
|
||||
}
|
||||
|
||||
// getResponseBody returns the text of the response body.
|
||||
func getResponseBody(resp *http.Response) string {
|
||||
contentType := resp.Header.Get("Content-Type")
|
||||
if !ifDumpBody(contentType) {
|
||||
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)
|
||||
}
|
||||
|
||||
// RawRequest returns the raw content of the request.
|
||||
func (r *ClientResponse) RawRequest() string {
|
||||
// ClientResponse can be nil.
|
||||
if r == nil {
|
||||
return ""
|
||||
}
|
||||
if r.request == nil {
|
||||
return ""
|
||||
}
|
||||
// DumpRequestOut writes more request headers than DumpRequest, such as User-Agent.
|
||||
bs, err := httputil.DumpRequestOut(r.request, false)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
dumpTextFormat,
|
||||
"REQUEST ",
|
||||
gconv.UnsafeBytesToStr(bs),
|
||||
getRequestBody(r.request),
|
||||
)
|
||||
}
|
||||
|
||||
// RawResponse returns the raw content of the response.
|
||||
func (r *ClientResponse) RawResponse() string {
|
||||
// ClientResponse can be nil.
|
||||
if r == nil || r.Response == nil {
|
||||
return ""
|
||||
}
|
||||
bs, err := httputil.DumpResponse(r.Response, false)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
dumpTextFormat,
|
||||
"RESPONSE",
|
||||
gconv.UnsafeBytesToStr(bs),
|
||||
getResponseBody(r.Response),
|
||||
)
|
||||
}
|
||||
|
||||
// Raw returns the raw text of the request and the response.
|
||||
func (r *ClientResponse) Raw() string {
|
||||
return fmt.Sprintf("%s\n%s", r.RawRequest(), r.RawResponse())
|
||||
}
|
||||
@ -11,10 +11,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/encoding/gparser"
|
||||
"github.com/gogf/gf/text/gregex"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
@ -22,6 +18,11 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/encoding/gparser"
|
||||
"github.com/gogf/gf/text/gregex"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
)
|
||||
|
||||
@ -156,7 +157,8 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien
|
||||
if err = writer.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if req, err = http.NewRequestWithContext(c.ctx, method, url, buffer); err != nil {
|
||||
|
||||
if req, err = http.NewRequest(method, url, buffer); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
@ -164,9 +166,7 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien
|
||||
} else {
|
||||
// Normal request.
|
||||
paramBytes := []byte(param)
|
||||
if req, err = http.NewRequestWithContext(
|
||||
c.ctx, method, url, bytes.NewReader(paramBytes),
|
||||
); err != nil {
|
||||
if req, err = http.NewRequest(method, url, bytes.NewReader(paramBytes)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
if v, ok := c.header["Content-Type"]; ok {
|
||||
@ -183,6 +183,10 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien
|
||||
}
|
||||
}
|
||||
}
|
||||
// Context.
|
||||
if c.ctx != nil {
|
||||
req = req.WithContext(c.ctx)
|
||||
}
|
||||
// Custom header.
|
||||
if len(c.header) > 0 {
|
||||
for k, v := range c.header {
|
||||
@ -211,27 +215,28 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien
|
||||
if len(c.authUser) > 0 {
|
||||
req.SetBasicAuth(c.authUser, c.authPass)
|
||||
}
|
||||
// Sending request.
|
||||
var r *http.Response
|
||||
// do not return nil even if the request fails
|
||||
resp = &ClientResponse{}
|
||||
for {
|
||||
if r, err = c.Do(req); err != nil {
|
||||
if resp.Response, err = c.Do(req); err != nil {
|
||||
if c.retryCount > 0 {
|
||||
c.retryCount--
|
||||
time.Sleep(c.retryInterval)
|
||||
} else {
|
||||
return nil, err
|
||||
// we need a copy of the request when the request fails.
|
||||
resp.request = req
|
||||
return resp, err
|
||||
}
|
||||
} else {
|
||||
resp.request = resp.Request
|
||||
break
|
||||
}
|
||||
}
|
||||
resp = &ClientResponse{
|
||||
Response: r,
|
||||
}
|
||||
|
||||
// Auto saving cookie content.
|
||||
if c.browserMode {
|
||||
now := time.Now()
|
||||
for _, v := range r.Cookies() {
|
||||
for _, v := range resp.Response.Cookies() {
|
||||
if v.Expires.UnixNano() < now.UnixNano() {
|
||||
delete(c.cookies, v.Name)
|
||||
} else {
|
||||
|
||||
@ -7,27 +7,24 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
// ClientResponse is the struct for client request response.
|
||||
type ClientResponse struct {
|
||||
*http.Response
|
||||
request *http.Request
|
||||
cookies map[string]string
|
||||
}
|
||||
|
||||
// initCookie initializes the cookie map attribute of ClientResponse.
|
||||
func (r *ClientResponse) initCookie() {
|
||||
if r.cookies == nil {
|
||||
now := time.Now()
|
||||
r.cookies = make(map[string]string)
|
||||
for _, v := range r.Cookies() {
|
||||
if v.Expires.UnixNano() < now.UnixNano() {
|
||||
continue
|
||||
}
|
||||
r.cookies[v.Name] = v.Value
|
||||
}
|
||||
}
|
||||
@ -51,7 +48,7 @@ func (r *ClientResponse) GetCookieMap() map[string]string {
|
||||
|
||||
// ReadAll retrieves and returns the response content as []byte.
|
||||
func (r *ClientResponse) ReadAll() []byte {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
body, err := ioutil.ReadAll(r.Response.Body)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
@ -66,5 +63,5 @@ func (r *ClientResponse) ReadAllString() string {
|
||||
// Close closes the response when it will never be used.
|
||||
func (r *ClientResponse) Close() error {
|
||||
r.Response.Close = true
|
||||
return r.Body.Close()
|
||||
return r.Response.Body.Close()
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/encoding/gurl"
|
||||
@ -54,12 +55,20 @@ func BuildParams(params interface{}, noUrlEncode ...bool) (encodedParamStr strin
|
||||
// niceCallFunc calls function <f> with exception capture logic.
|
||||
func niceCallFunc(f func()) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
switch err {
|
||||
if e := recover(); e != nil {
|
||||
switch e {
|
||||
case gEXCEPTION_EXIT, gEXCEPTION_EXIT_ALL:
|
||||
return
|
||||
default:
|
||||
panic(err)
|
||||
if _, ok := e.(gerror.ApiStack); ok {
|
||||
// It's already an error that has stack info.
|
||||
panic(e)
|
||||
} else {
|
||||
// Create a new error with stack info.
|
||||
// Note that there's a skip pointing the start stacktrace
|
||||
// of the real error point.
|
||||
panic(gerror.NewfSkip(1, "%v", e))
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
@ -10,11 +10,12 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"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 +76,19 @@ 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 (
|
||||
agent = request.UserAgent()
|
||||
address = request.RemoteAddr
|
||||
cookie = request.Header.Get("Cookie")
|
||||
)
|
||||
//intlog.Print(agent, address, cookie)
|
||||
return guid.S([]byte(agent), []byte(address), []byte(cookie))
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return request
|
||||
}
|
||||
|
||||
|
||||
@ -7,11 +7,10 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
)
|
||||
|
||||
@ -122,7 +121,15 @@ func (m *Middleware) Next() {
|
||||
loop = false
|
||||
}
|
||||
}, func(exception interface{}) {
|
||||
m.request.error = gerror.Newf("%v", exception)
|
||||
if e, ok := exception.(gerror.ApiStack); ok {
|
||||
// It's already an error that has stack info.
|
||||
m.request.error = e.(error)
|
||||
} else {
|
||||
// Create a new error with stack info.
|
||||
// Note that there's a skip pointing the start stacktrace
|
||||
// of the real error point.
|
||||
m.request.error = gerror.NewfSkip(1, "%v", exception)
|
||||
}
|
||||
m.request.Response.WriteStatus(http.StatusInternalServerError, exception)
|
||||
loop = false
|
||||
})
|
||||
|
||||
@ -20,6 +20,7 @@ import (
|
||||
"github.com/gogf/gf/util/gvalid"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -28,17 +29,62 @@ var (
|
||||
xmlHeaderBytes = []byte("<?xml")
|
||||
)
|
||||
|
||||
// Parse calls r.GetStruct to convert the parameters, which are sent from client,
|
||||
// to given struct, and then calls gvalid.CheckStruct validating the struct according
|
||||
// Parse is the most commonly used function, which converts request parameters to struct or struct
|
||||
// slice. It also automatically validates the struct or every element of the struct slice according
|
||||
// to the validation tag of the struct.
|
||||
//
|
||||
// See r.GetStruct, gvalid.CheckStruct.
|
||||
// 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 {
|
||||
if err := r.GetStruct(pointer); err != nil {
|
||||
return err
|
||||
var (
|
||||
reflectVal1 = reflect.ValueOf(pointer)
|
||||
reflectKind1 = reflectVal1.Kind()
|
||||
)
|
||||
if reflectKind1 != reflect.Ptr {
|
||||
return fmt.Errorf(
|
||||
"parameter should be type of *struct/**struct/*[]struct/*[]*struct, but got: %v",
|
||||
reflectKind1,
|
||||
)
|
||||
}
|
||||
if err := gvalid.CheckStruct(pointer, nil); err != nil {
|
||||
return err
|
||||
var (
|
||||
reflectVal2 = reflectVal1.Elem()
|
||||
reflectKind2 = reflectVal2.Kind()
|
||||
)
|
||||
switch reflectKind2 {
|
||||
// Single struct, post content like:
|
||||
// {"id":1, "name":"john"}
|
||||
case reflect.Ptr, reflect.Struct:
|
||||
// Conversion.
|
||||
if err := r.GetStruct(pointer); err != nil {
|
||||
return err
|
||||
}
|
||||
// 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.
|
||||
j, err := gjson.LoadContent(r.GetBody())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := j.GetStructs(".", pointer); err != nil {
|
||||
return err
|
||||
}
|
||||
for i := 0; i < reflectVal2.Len(); i++ {
|
||||
if err := gvalid.CheckStruct(reflectVal2.Index(i), nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -278,13 +324,15 @@ func (r *Request) parseForm() {
|
||||
for name, values := range r.PostForm {
|
||||
// Invalid parameter name.
|
||||
// Only allow chars of: '\w', '[', ']', '-'.
|
||||
if !gregex.IsMatchString(`^[\w\-\[\]]+$`, name) {
|
||||
if len(r.PostForm) == 1 {
|
||||
// It might be JSON/XML content.
|
||||
r.bodyContent = gconv.UnsafeStrToBytes(name + strings.Join(values, " "))
|
||||
if !gregex.IsMatchString(`^[\w\-\[\]]+$`, name) && len(r.PostForm) == 1 {
|
||||
// It might be JSON/XML content.
|
||||
if s := gstr.Trim(name + strings.Join(values, " ")); len(s) > 0 {
|
||||
if s[0] == '{' && s[len(s)-1] == '}' || s[0] == '<' && s[len(s)-1] == '>' {
|
||||
r.bodyContent = gconv.UnsafeStrToBytes(s)
|
||||
params = ""
|
||||
break
|
||||
}
|
||||
}
|
||||
params = ""
|
||||
break
|
||||
}
|
||||
if len(values) == 1 {
|
||||
if len(params) > 0 {
|
||||
@ -308,8 +356,10 @@ func (r *Request) parseForm() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if r.formMap, err = gstr.Parse(params); err != nil {
|
||||
panic(err)
|
||||
if params != "" {
|
||||
if r.formMap, err = gstr.Parse(params); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if r.formMap == nil {
|
||||
|
||||
@ -8,7 +8,6 @@ package ghttp
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/internal/structs"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
@ -190,13 +189,7 @@ func (r *Request) GetFormMapStrVar(kvMap ...map[string]interface{}) map[string]*
|
||||
// The optional parameter <mapping> is used to specify the key to attribute mapping.
|
||||
func (r *Request) GetFormStruct(pointer interface{}, mapping ...map[string]string) error {
|
||||
r.parseForm()
|
||||
tagMap := structs.TagMapName(pointer, paramTagPriority, true)
|
||||
if len(mapping) > 0 {
|
||||
for k, v := range mapping[0] {
|
||||
tagMap[k] = v
|
||||
}
|
||||
}
|
||||
return gconv.StructDeep(r.formMap, pointer, tagMap)
|
||||
return gconv.StructDeep(r.formMap, pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetFormToStruct is alias of GetFormStruct. See GetFormStruct.
|
||||
|
||||
@ -8,7 +8,6 @@ package ghttp
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/internal/structs"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
@ -205,13 +204,7 @@ func (r *Request) GetPostMapStrVar(kvMap ...map[string]interface{}) map[string]*
|
||||
//
|
||||
// Deprecated.
|
||||
func (r *Request) GetPostStruct(pointer interface{}, mapping ...map[string]string) error {
|
||||
tagMap := structs.TagMapName(pointer, paramTagPriority, true)
|
||||
if len(mapping) > 0 {
|
||||
for k, v := range mapping[0] {
|
||||
tagMap[k] = v
|
||||
}
|
||||
}
|
||||
return gconv.StructDeep(r.GetPostMap(), pointer, tagMap)
|
||||
return gconv.StructDeep(r.GetPostMap(), pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetPostToStruct is alias of GetQueryStruct. See GetPostStruct.
|
||||
|
||||
@ -9,7 +9,6 @@ package ghttp
|
||||
import (
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
|
||||
"github.com/gogf/gf/internal/structs"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
@ -194,13 +193,7 @@ func (r *Request) GetQueryMapStrVar(kvMap ...map[string]interface{}) map[string]
|
||||
// attribute mapping.
|
||||
func (r *Request) GetQueryStruct(pointer interface{}, mapping ...map[string]string) error {
|
||||
r.parseQuery()
|
||||
tagMap := structs.TagMapName(pointer, paramTagPriority, true)
|
||||
if len(mapping) > 0 {
|
||||
for k, v := range mapping[0] {
|
||||
tagMap[k] = v
|
||||
}
|
||||
}
|
||||
return gconv.StructDeep(r.GetQueryMap(), pointer, tagMap)
|
||||
return gconv.StructDeep(r.GetQueryMap(), pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetQueryToStruct is alias of GetQueryStruct. See GetQueryStruct.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user