mirror of
https://gitee.com/johng/gf
synced 2026-06-06 02:25:47 +08:00
9
.github/ISSUE_TEMPLATE.MD
vendored
9
.github/ISSUE_TEMPLATE.MD
vendored
@ -1,12 +1,11 @@
|
||||
<!-- Please answer these questions before submitting your issue. Thanks! -->
|
||||
|
||||
<!-- 为高效处理您的疑问,如果觉得是BUG类问题,请您务必提供可复现该问题的最小可运行代码! -->
|
||||
<!-- 为高效处理您的疑问,如果觉得是BUG类问题,请您务必提供可复现该问题的最小可运行代码! -->
|
||||
<!-- 为高效处理您的疑问,如果觉得是BUG类问题,请您务必提供可复现该问题的最小可运行代码! -->
|
||||
<!-- 为高效处理您的疑问,如果觉得是BUG类问题,请您务必提供可复现该问题的最小可运行代码!否则issue可能会被延期处理! -->
|
||||
<!-- 为高效处理您的疑问,如果觉得是BUG类问题,请您务必提供可复现该问题的最小可运行代码!否则issue可能会被延期处理! -->
|
||||
<!-- 为高效处理您的疑问,如果觉得是BUG类问题,请您务必提供可复现该问题的最小可运行代码!否则issue可能会被延期处理! -->
|
||||
<!-- 重要的事情说三遍! -->
|
||||
|
||||
### 1. What version of `Go` and system type/arch are you using?
|
||||
|
||||
<!--
|
||||
Please paste the output of command `go version` from your terminal.
|
||||
What expect to see is like: `go 1.12, linux/amd64`
|
||||
@ -14,7 +13,6 @@ What expect to see is like: `go 1.12, linux/amd64`
|
||||
|
||||
|
||||
### 2. What version of `GoFrame` are you using?
|
||||
|
||||
<!-- You can find the GF version from your `go.mod`, or from the `version.go` in `GF` -->
|
||||
|
||||
|
||||
@ -23,7 +21,6 @@ What expect to see is like: `go 1.12, linux/amd64`
|
||||
|
||||
|
||||
### 4. What did you do?
|
||||
|
||||
<!--
|
||||
If possible, provide a copy of shortest codes for reproducing the error.
|
||||
A complete runnable program is best.
|
||||
|
||||
65
.github/workflows/cli.yml
vendored
Normal file
65
.github/workflows/cli.yml
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
name: GoFrame CLI Build Release
|
||||
|
||||
on:
|
||||
push:
|
||||
# Sequence of patterns matched against refs/tags
|
||||
tags:
|
||||
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
|
||||
|
||||
env:
|
||||
TZ: Asia/Shanghai
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build And Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Github Code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set Up Golang Environment
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.17
|
||||
|
||||
- name: Build CLI Binary
|
||||
run: |
|
||||
echo "Building linux amd64 binary..."
|
||||
cd cmd/gf
|
||||
GOOS=linux GOARCH=amd64 go build main.go
|
||||
chmod +x main
|
||||
./main install -y
|
||||
|
||||
- name: Build CLI Binary For All Platform
|
||||
run: |
|
||||
cd cmd/gf
|
||||
gf build main.go -n gf -a all -s all
|
||||
|
||||
- name: Move Files Before Release
|
||||
run: |
|
||||
cd cmd/gf/temp
|
||||
for OS in *;do for FILE in $OS/*;\
|
||||
do if [[ ${OS} =~ 'windows' ]];\
|
||||
then mv $FILE gf_$OS.exe && rm -rf $OS;\
|
||||
else mv $FILE gf_$OS && rm -rf $OS;\
|
||||
fi;done;done
|
||||
|
||||
- name: Create Github Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: GoFrame CLI Release ${{ github.ref }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
- name: Upload Release Asset
|
||||
id: upload-release-asset
|
||||
uses: alexellis/upload-assets@0.2.2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
asset_paths: '["cmd/gf/temp/gf_*"]'
|
||||
159
.github/workflows/gf.yml
vendored
Normal file
159
.github/workflows/gf.yml
vendored
Normal file
@ -0,0 +1,159 @@
|
||||
name: GoFrame Main CI
|
||||
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- feature/**
|
||||
- fix/**
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- feature/**
|
||||
- fix/**
|
||||
|
||||
env:
|
||||
GF_DEBUG: 0
|
||||
|
||||
|
||||
jobs:
|
||||
code-test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
|
||||
# Service containers to run with `code-test`
|
||||
services:
|
||||
redis:
|
||||
image : redis
|
||||
options: >-
|
||||
--health-cmd "redis-cli ping"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
# Maps tcp port 6379 on service container to the host
|
||||
- 6379:6379
|
||||
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
env:
|
||||
MYSQL_DATABASE : test
|
||||
MYSQL_ROOT_PASSWORD: 12345678
|
||||
ports:
|
||||
# Maps tcp port 3306 on service container to the host
|
||||
- 3306:3306
|
||||
|
||||
postgres:
|
||||
image: postgres:13
|
||||
env:
|
||||
POSTGRES_PASSWORD: 12345678
|
||||
POSTGRES_USER: root
|
||||
POSTGRES_DB: test
|
||||
TZ: Asia/Shanghai
|
||||
ports:
|
||||
- 5432:5432
|
||||
# Set health checks to wait until postgres has started
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
|
||||
mssql:
|
||||
image: mcmoe/mssqldocker:latest
|
||||
env:
|
||||
ACCEPT_EULA: Y
|
||||
SA_PASSWORD: LoremIpsum86
|
||||
MSSQL_DB: test
|
||||
MSSQL_USER: root
|
||||
MSSQL_PASSWORD: LoremIpsum86
|
||||
ports:
|
||||
- 1433:1433
|
||||
options: >-
|
||||
--health-cmd="/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P LoremIpsum86 -l 30 -Q \"SELECT 1\" || exit 1"
|
||||
--health-start-period 10s
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 10
|
||||
|
||||
clickhouse-server:
|
||||
image: yandex/clickhouse-server
|
||||
ports:
|
||||
- 9000:9000
|
||||
- 8123:8123
|
||||
- 9001:9001
|
||||
|
||||
|
||||
# strategy set
|
||||
strategy:
|
||||
matrix:
|
||||
go: ["1.15", "1.16", "1.17"]
|
||||
|
||||
|
||||
steps:
|
||||
- name: Set Up Timezone
|
||||
uses: szenius/set-timezone@v1.0
|
||||
with:
|
||||
timezoneLinux: "Asia/Shanghai"
|
||||
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set Up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go }}
|
||||
|
||||
- name: Before Script
|
||||
run: |
|
||||
find . -name "*.go" | xargs gofmt -w
|
||||
git diff --name-only --exit-code || if [ $? != 0 ]; then echo "Notice: gofmt check failed,please gofmt before pr." && exit 1; fi
|
||||
echo "gofmt check pass."
|
||||
sudo echo "127.0.0.1 local" | sudo tee -a /etc/hosts
|
||||
|
||||
- name: CLI Build & Test
|
||||
run: |
|
||||
cd cmd/gf
|
||||
go mod tidy
|
||||
go build ./...
|
||||
go test ./...
|
||||
|
||||
- name: Example Build & Test
|
||||
run: |
|
||||
cd example
|
||||
go mod tidy
|
||||
go build ./...
|
||||
go test ./...
|
||||
|
||||
- name: Contrib Build & Test
|
||||
run: |
|
||||
cd contrib
|
||||
for file in `find . -name go.mod`; do
|
||||
path=$(dirname $file)
|
||||
# Ignore oracle database driver build&test.
|
||||
if [ "oracle" = $(basename $path) ]; then
|
||||
continue 1
|
||||
fi
|
||||
cd $path
|
||||
go mod tidy
|
||||
go build ./...
|
||||
go test ./...
|
||||
cd -
|
||||
done
|
||||
|
||||
- name: Run i386 Arch Test
|
||||
run: |
|
||||
GOARCH=386 go test -v ./... || exit 1
|
||||
|
||||
- name: Run amd64 Arch Test
|
||||
run: |
|
||||
GOARCH=amd64 go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic
|
||||
|
||||
- name: Report Coverage
|
||||
uses: codecov/codecov-action@v2
|
||||
with:
|
||||
flags: go-${{ matrix.go }}
|
||||
|
||||
74
.github/workflows/go.yml
vendored
74
.github/workflows/go.yml
vendored
@ -1,74 +0,0 @@
|
||||
name: GoFrame CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
pull_request:
|
||||
branches: [master, develop]
|
||||
env:
|
||||
GF_DEBUG: 0
|
||||
|
||||
jobs:
|
||||
code-test:
|
||||
runs-on: ubuntu-latest
|
||||
# Service containers to run with `code-test`
|
||||
services:
|
||||
redis:
|
||||
image : redis
|
||||
options: >-
|
||||
--health-cmd "redis-cli ping"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
# Maps tcp port 6379 on service container to the host
|
||||
- 6379:6379
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
env:
|
||||
MYSQL_DATABASE : test
|
||||
MYSQL_ROOT_PASSWORD: 12345678
|
||||
ports:
|
||||
# Maps tcp port 3306 on service container to the host
|
||||
- 3306:3306
|
||||
|
||||
# strategy set
|
||||
strategy:
|
||||
matrix:
|
||||
go: ["1.15", "1.16", "1.17"]
|
||||
|
||||
steps:
|
||||
- name: Set Up Timezone
|
||||
uses: szenius/set-timezone@v1.0
|
||||
with:
|
||||
timezoneLinux: "Asia/Shanghai"
|
||||
|
||||
- name: Checkout Repositary
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set Up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go }}
|
||||
|
||||
- name: Before Script
|
||||
run: |
|
||||
date
|
||||
find . -name "*.go" | xargs gofmt -w
|
||||
git diff --name-only --exit-code || if [ $? != 0 ];then echo "Notice: gofmt check failed,please gofmt before pr." && exit 1; fi
|
||||
echo "gofmt check pass."
|
||||
sudo echo "127.0.0.1 local" | sudo tee -a /etc/hosts
|
||||
|
||||
- name: Run i386 Arch Test
|
||||
run: GOARCH=386 go test -v ./... || exit 1
|
||||
|
||||
- name: Run amd64 Arch Test
|
||||
run: GOARCH=amd64 go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic
|
||||
|
||||
- name: Report Coverage
|
||||
uses: codecov/codecov-action@v2
|
||||
with:
|
||||
flags: go-${{ matrix.go }}
|
||||
|
||||
30
.github/workflows/tag.yml
vendored
Normal file
30
.github/workflows/tag.yml
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
name: GoFrame AutoCreating SubMod Tags
|
||||
|
||||
on:
|
||||
push:
|
||||
# Sequence of patterns matched against refs/tags
|
||||
tags:
|
||||
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
|
||||
|
||||
env:
|
||||
TZ: Asia/Shanghai
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Auto Creating Tags
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Github Code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Auto Creating Tags
|
||||
run: |
|
||||
git config --global user.email "tagrobot@goframe.org"
|
||||
git config --global user.name "TagRobot"
|
||||
for file in `find contrib -name go.mod`; do
|
||||
tag=$(dirname $file)/$GITHUB_REF_NAME
|
||||
git tag $tag
|
||||
git push origin $tag
|
||||
done
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,7 +13,6 @@ pkg/
|
||||
bin/
|
||||
cbuild
|
||||
**/.DS_Store
|
||||
.vscode/
|
||||
.test/
|
||||
main
|
||||
gf
|
||||
48
README.MD
48
README.MD
@ -1,24 +1,42 @@
|
||||
# GoFrame
|
||||
<div align=center>
|
||||
<img src="https://goframe.org/statics/image/gf-head-large.png" width="100"/>
|
||||
|
||||
[](https://godoc.org/github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf/actions/workflows/go.yml)
|
||||
[](https://github.com/gogf/gf/actions/workflows/gf.yml)
|
||||
[](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[](https://codecov.io/gh/gogf/gf/branch/master)
|
||||
[](https://github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf)
|
||||
|
||||
</div>
|
||||
|
||||
`GoFrame` is a modular, powerful, high-performance and enterprise-class application development framework of Golang.
|
||||
|
||||
> 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`.
|
||||
# Features
|
||||
- modular, loosely coupled design
|
||||
- rich components, out-of-the-box
|
||||
- automatic codes generating for efficiency
|
||||
- simple and easy to use, detailed documentation
|
||||
- interface designed components, with high scalability
|
||||
- fully supported tracing and error stack feature
|
||||
- specially developed and powerful ORM component
|
||||
- robust engineering design specifications
|
||||
- convenient development CLI tool provide
|
||||
- OpenTelemetry observability features support
|
||||
- OpenAPIV3 documentation generating, automatically
|
||||
- much, much more...ready to explore?
|
||||
|
||||
# Installation
|
||||
```
|
||||
Enter your repo. directory and execute following command:
|
||||
|
||||
## primary module
|
||||
```bash
|
||||
go get -u -v github.com/gogf/gf/v2
|
||||
```
|
||||
suggested using `go.mod`:
|
||||
```
|
||||
require github.com/gogf/gf/v2 latest
|
||||
|
||||
## cli tool
|
||||
```bash
|
||||
go install github.com/gogf/gf/cmd/gf/v2
|
||||
```
|
||||
|
||||
# Limitation
|
||||
@ -31,24 +49,10 @@ golang version >= 1.15
|
||||
<img src="https://goframe.org/download/attachments/1114119/arch.png"/>
|
||||
</div>
|
||||
|
||||
# Packages
|
||||
1. **Primary Package**
|
||||
|
||||
The `gf` repository maintains some basic and most commonly used packages, keeping it as lightweight and simple as possible.
|
||||
|
||||
1. **Community Package**
|
||||
|
||||
The community packages are contributed and maintained by community members, which are hosted in `gogf` organization. Some of the community packages are separated from the `gf` repository, which are not of common usage or are with heavy dependencies.
|
||||
|
||||
# Performance
|
||||
|
||||
The `Web` component performance of `GoFrame`, please refer to third-party project: https://github.com/the-benchmarker/web-frameworks
|
||||
|
||||
|
||||
|
||||
# Documentation
|
||||
|
||||
* 中文官网: [https://goframe.org](https://goframe.org/display/gf)
|
||||
* Chinese Official Site(中文官网): [https://goframe.org](https://goframe.org/display/gf)
|
||||
* GoDoc API: [https://pkg.go.dev/github.com/gogf/gf](https://pkg.go.dev/github.com/gogf/gf)
|
||||
|
||||
|
||||
|
||||
21
cmd/gf/LICENSE
Normal file
21
cmd/gf/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 john@goframe.org https://goframe.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
18
cmd/gf/Makefile
Normal file
18
cmd/gf/Makefile
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
pack: pack.template-single pack.template-mono
|
||||
|
||||
pack.template-single:
|
||||
@rm -fr temp
|
||||
@mkdir temp || exit 0
|
||||
@cd temp && git clone https://github.com/gogf/template-single
|
||||
@rm -fr temp/template-single/.git
|
||||
@cd temp && gf pack template-single ../internal/packed/template-single.go -n=packed -y
|
||||
@rm -fr temp
|
||||
|
||||
pack.template-mono:
|
||||
@rm -fr temp
|
||||
@mkdir temp || exit 0
|
||||
@cd temp && git clone https://github.com/gogf/template-mono
|
||||
@rm -fr temp/template-mono/.git
|
||||
@cd temp && gf pack template-mono ../internal/packed/template-mono.go -n=packed -y
|
||||
@rm -fr temp
|
||||
69
cmd/gf/README.MD
Normal file
69
cmd/gf/README.MD
Normal file
@ -0,0 +1,69 @@
|
||||
# gf
|
||||
|
||||
`gf` is a powerful CLI tool for building [GoFrame](https://goframe.org) application with convenience.
|
||||
|
||||
|
||||
## 1. Install
|
||||
|
||||
## 1) PreCompiled Binary
|
||||
You can also install `gf` tool using pre-built binaries: https://github.com/gogf/gf/releases
|
||||
|
||||
1. `Mac` & `Linux`
|
||||
```shell
|
||||
wget -O gf https://github.com/gogf/gf/releases/latest/download/gf_$(go env GOOS)_$(go env GOARCH) && chmod +x gf && ./gf install -y && rm ./gf
|
||||
```
|
||||
> If you're using `zsh`, you might need rename your alias by command `alias gf=gf` to resolve the conflicts between `gf` and `git fetch`.
|
||||
|
||||
2. `Windows`
|
||||
|
||||
Manually download, execute it and then follow the instruction.
|
||||
|
||||
3. Database `sqlite` and `oracle` are not support in `gf gen` command in default as it needs `cgo` and `gcc`, you can manually make some changes to the source codes and do the building.
|
||||
|
||||
## 2) Manually Install
|
||||
|
||||
```shell
|
||||
git clone https://github.com/gogf/gf && cd gf/cmd/gf && go install
|
||||
```
|
||||
|
||||
|
||||
## 2. Commands
|
||||
```html
|
||||
$ gf
|
||||
USAGE
|
||||
gf COMMAND [OPTION]
|
||||
|
||||
COMMAND
|
||||
env show current Golang environment variables
|
||||
run running go codes with hot-compiled-like feature
|
||||
gen automatically generate go files for dao/do/entity/pb/pbentity
|
||||
tpl template parsing and building commands
|
||||
init create and initialize an empty GoFrame project
|
||||
pack packing any file/directory to a resource file, or a go file
|
||||
build cross-building go project for lots of platforms
|
||||
docker build docker image for current GoFrame project
|
||||
install install gf binary to system (might need root/admin permission)
|
||||
version show version information of current binary
|
||||
|
||||
OPTION
|
||||
-y, --yes all yes for all command without prompt ask
|
||||
-v, --version show version information of current binary
|
||||
-d, --debug show internal detailed debugging information
|
||||
-h, --help more information about this command
|
||||
|
||||
ADDITIONAL
|
||||
Use "gf COMMAND -h" for details about a command.
|
||||
```
|
||||
|
||||
## 3. FAQ
|
||||
|
||||
### 1). Command `gf run` returns `pipe: too many open files`
|
||||
|
||||
Please use `ulimit -n 65535` to enlarge your system configuration for max open files for current terminal shell session, and then `gf run`.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
20
cmd/gf/go.mod
Normal file
20
cmd/gf/go.mod
Normal file
@ -0,0 +1,20 @@
|
||||
module github.com/gogf/gf/cmd/gf/v2
|
||||
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.0.6
|
||||
github.com/gogf/gf/contrib/drivers/oracle/v2 v2.0.6
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.0.6
|
||||
github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.0.6
|
||||
github.com/gogf/gf/v2 v2.0.6
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/gogf/gf/contrib/drivers/mssql/v2 => ../../contrib/drivers/mssql/
|
||||
github.com/gogf/gf/contrib/drivers/oracle/v2 => ../../contrib/drivers/oracle/
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 => ../../contrib/drivers/pgsql/
|
||||
github.com/gogf/gf/contrib/drivers/sqlite/v2 => ../../contrib/drivers/sqlite/
|
||||
github.com/gogf/gf/v2 => ../../
|
||||
)
|
||||
165
cmd/gf/go.sum
Normal file
165
cmd/gf/go.sum
Normal file
@ -0,0 +1,165 @@
|
||||
github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=
|
||||
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E=
|
||||
github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisenkom/go-mssqldb v0.11.0 h1:9rHa233rhdOyrz2GcP9NM+gi2psgJZ4GWDpL/7ND8HI=
|
||||
github.com/denisenkom/go-mssqldb v0.11.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||
github.com/go-redis/redis/v8 v8.11.4 h1:kHoYkfZP6+pe04aFTnhDH6GDROa5yJdHJVNxV3F46Tg=
|
||||
github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M=
|
||||
github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0=
|
||||
github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk=
|
||||
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/longbridgeapp/sqlparser v0.3.1 h1:iWOZWGIFgQrJRgobLXUNJdvqGRpbVXkyKUKUA5CNJBE=
|
||||
github.com/longbridgeapp/sqlparser v0.3.1/go.mod h1:GIHaUq8zvYyHLCLMJJykx1CdM6LHtkUih/QaJXySSx4=
|
||||
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-oci8 v0.1.1 h1:aEUDxNAyDG0tv8CA3TArnDQNyc4EhnWlsfxRgDHABHM=
|
||||
github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-sqlite3 v1.14.10 h1:MLn+5bFRlWMGoSRmJour3CL1w/qL96mvipqpwQW/Sfk=
|
||||
github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c=
|
||||
github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opentelemetry.io/otel v1.0.0 h1:qTTn6x71GVBvoafHK/yaRUmFzI4LcONZD0/kXxl5PHI=
|
||||
go.opentelemetry.io/otel v1.0.0/go.mod h1:AjRVh9A5/5DE7S+mZtTR6t8vpKKryam+0lREnfmS4cg=
|
||||
go.opentelemetry.io/otel/sdk v1.0.0 h1:BNPMYUONPNbLneMttKSjQhOTlFLOD9U22HNG1KrIN2Y=
|
||||
go.opentelemetry.io/otel/sdk v1.0.0/go.mod h1:PCrDHlSy5x1kjezSdL37PhbFUMjrsLRshJ2zCzeXwbM=
|
||||
go.opentelemetry.io/otel/trace v1.0.0 h1:TSBr8GTEtKevYMG/2d21M989r5WJYVimhTHBKVEZuh4=
|
||||
go.opentelemetry.io/otel/trace v1.0.0/go.mod h1:PXTWqayeFUlJV1YDNhsJYB184+IvAH814St6o6ajzIs=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 h1:GLw7MR8AfAG2GmGcmVgObFOHXYypgGjnGno25RDwn3Y=
|
||||
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
64
cmd/gf/internal/cmd/cmd.go
Normal file
64
cmd/gf/internal/cmd/cmd.go
Normal file
@ -0,0 +1,64 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/service"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
|
||||
var (
|
||||
GF = cGF{}
|
||||
)
|
||||
|
||||
type cGF struct {
|
||||
g.Meta `name:"gf" ad:"{cGFAd}"`
|
||||
}
|
||||
|
||||
const (
|
||||
cGFAd = `
|
||||
ADDITIONAL
|
||||
Use "gf COMMAND -h" for details about a command.
|
||||
`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cGFAd`: cGFAd,
|
||||
})
|
||||
}
|
||||
|
||||
type cGFInput struct {
|
||||
g.Meta `name:"gf"`
|
||||
Yes bool `short:"y" name:"yes" brief:"all yes for all command without prompt ask" orphan:"true"`
|
||||
Version bool `short:"v" name:"version" brief:"show version information of current binary" orphan:"true"`
|
||||
Debug bool `short:"d" name:"debug" brief:"show internal detailed debugging information" orphan:"true"`
|
||||
}
|
||||
type cGFOutput struct{}
|
||||
|
||||
func (c cGF) Index(ctx context.Context, in cGFInput) (out *cGFOutput, err error) {
|
||||
// Version.
|
||||
if in.Version {
|
||||
_, err = Version.Index(ctx, cVersionInput{})
|
||||
return
|
||||
}
|
||||
// No argument or option, do installation checks.
|
||||
if !service.Install.IsInstalled() {
|
||||
mlog.Print("hi, it seams it's the first time you installing gf cli.")
|
||||
s := gcmd.Scanf("do you want to install gf binary to your system? [y/n]: ")
|
||||
if strings.EqualFold(s, "y") {
|
||||
if err = service.Install.Run(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
gcmd.Scan("press `Enter` to exit...")
|
||||
return
|
||||
}
|
||||
}
|
||||
// Print help content.
|
||||
gcmd.CommandFromCtx(ctx).Print()
|
||||
return
|
||||
}
|
||||
320
cmd/gf/internal/cmd/cmd_build.go
Normal file
320
cmd/gf/internal/cmd/cmd_build.go
Normal file
@ -0,0 +1,320 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/encoding/gbase64"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/genv"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
|
||||
var (
|
||||
Build = cBuild{
|
||||
nodeNameInConfigFile: "gfcli.build",
|
||||
packedGoFileName: "internal/packed/build_pack_data.go",
|
||||
}
|
||||
)
|
||||
|
||||
type cBuild struct {
|
||||
g.Meta `name:"build" brief:"{cBuildBrief}" dc:"{cBuildDc}" eg:"{cBuildEg}" ad:"{cBuildAd}"`
|
||||
nodeNameInConfigFile string // nodeNameInConfigFile is the node name for compiler configurations in configuration file.
|
||||
packedGoFileName string // packedGoFileName specifies the file name for packing common folders into one single go file.
|
||||
}
|
||||
|
||||
const (
|
||||
cBuildBrief = `cross-building go project for lots of platforms`
|
||||
cBuildEg = `
|
||||
gf build main.go
|
||||
gf build main.go --pack public,template
|
||||
gf build main.go --cgo
|
||||
gf build main.go -m none
|
||||
gf build main.go -n my-app -a all -s all
|
||||
gf build main.go -n my-app -a amd64,386 -s linux -p .
|
||||
gf build main.go -n my-app -v 1.0 -a amd64,386 -s linux,windows,darwin -p ./docker/bin
|
||||
`
|
||||
cBuildDc = `
|
||||
The "build" command is most commonly used command, which is designed as a powerful wrapper for
|
||||
"go build" command for convenience cross-compiling usage.
|
||||
It provides much more features for building binary:
|
||||
1. Cross-Compiling for many platforms and architectures.
|
||||
2. Configuration file support for compiling.
|
||||
3. Build-In Variables.
|
||||
`
|
||||
cBuildAd = `
|
||||
PLATFORMS
|
||||
darwin amd64,arm64
|
||||
freebsd 386,amd64,arm
|
||||
linux 386,amd64,arm,arm64,ppc64,ppc64le,mips,mipsle,mips64,mips64le
|
||||
netbsd 386,amd64,arm
|
||||
openbsd 386,amd64,arm
|
||||
windows 386,amd64
|
||||
`
|
||||
// https://golang.google.cn/doc/install/source
|
||||
cBuildPlatforms = `
|
||||
darwin amd64
|
||||
darwin arm64
|
||||
ios amd64
|
||||
ios arm64
|
||||
freebsd 386
|
||||
freebsd amd64
|
||||
freebsd arm
|
||||
linux 386
|
||||
linux amd64
|
||||
linux arm
|
||||
linux arm64
|
||||
linux ppc64
|
||||
linux ppc64le
|
||||
linux mips
|
||||
linux mipsle
|
||||
linux mips64
|
||||
linux mips64le
|
||||
netbsd 386
|
||||
netbsd amd64
|
||||
netbsd arm
|
||||
openbsd 386
|
||||
openbsd amd64
|
||||
openbsd arm
|
||||
windows 386
|
||||
windows amd64
|
||||
android arm
|
||||
dragonfly amd64
|
||||
plan9 386
|
||||
plan9 amd64
|
||||
solaris amd64
|
||||
`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cBuildBrief`: cBuildBrief,
|
||||
`cBuildDc`: cBuildDc,
|
||||
`cBuildEg`: cBuildEg,
|
||||
`cBuildAd`: cBuildAd,
|
||||
})
|
||||
}
|
||||
|
||||
type cBuildInput struct {
|
||||
g.Meta `name:"build" config:"gfcli.build"`
|
||||
File string `name:"FILE" arg:"true" brief:"building file path"`
|
||||
Name string `short:"n" name:"name" brief:"output binary name"`
|
||||
Version string `short:"v" name:"version" brief:"output binary version"`
|
||||
Arch string `short:"a" name:"arch" brief:"output binary architecture, multiple arch separated with ','"`
|
||||
System string `short:"s" name:"system" brief:"output binary system, multiple os separated with ','"`
|
||||
Output string `short:"o" name:"output" brief:"output binary path, used when building single binary file"`
|
||||
Path string `short:"p" name:"path" brief:"output binary directory path, default is './temp'" d:"./temp"`
|
||||
Extra string `short:"e" name:"extra" brief:"extra custom \"go build\" options"`
|
||||
Mod string `short:"m" name:"mod" brief:"like \"-mod\" option of \"go build\", use \"-m none\" to disable go module"`
|
||||
Cgo bool `short:"c" name:"cgo" brief:"enable or disable cgo feature, it's disabled in default" orphan:"true"`
|
||||
VarMap g.Map `short:"r" name:"varMap" brief:"custom built embedded variable into binary"`
|
||||
PackSrc string `short:"ps" name:"packSrc" brief:"pack one or more folders into one go file before building"`
|
||||
PackDst string `short:"pd" name:"packDst" brief:"temporary go file path for pack, this go file will be automatically removed after built" d:"internal/packed/build_pack_data.go"`
|
||||
ExitWhenError bool `short:"ew" name:"exitWhenError" brief:"exit building when any error occurs, default is false" orphan:"true"`
|
||||
}
|
||||
type cBuildOutput struct{}
|
||||
|
||||
func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, err error) {
|
||||
mlog.SetHeaderPrint(true)
|
||||
|
||||
mlog.Debugf(`build input: %+v`, in)
|
||||
// Necessary check.
|
||||
if gproc.SearchBinary("go") == "" {
|
||||
mlog.Fatalf(`command "go" not found in your environment, please install golang first to proceed this command`)
|
||||
}
|
||||
|
||||
var (
|
||||
parser = gcmd.ParserFromCtx(ctx)
|
||||
file = parser.GetArg(2).String()
|
||||
)
|
||||
if len(file) < 1 {
|
||||
// Check and use the main.go file.
|
||||
if gfile.Exists("main.go") {
|
||||
file = "main.go"
|
||||
} else {
|
||||
mlog.Fatal("build file path cannot be empty")
|
||||
}
|
||||
}
|
||||
if in.Name == "" {
|
||||
in.Name = gfile.Name(file)
|
||||
}
|
||||
if len(in.Name) < 1 || in.Name == "*" {
|
||||
mlog.Fatal("name cannot be empty")
|
||||
}
|
||||
if in.Mod != "" && in.Mod != "none" {
|
||||
mlog.Debugf(`mod is %s`, in.Mod)
|
||||
if in.Extra == "" {
|
||||
in.Extra = fmt.Sprintf(`-mod=%s`, in.Mod)
|
||||
} else {
|
||||
in.Extra = fmt.Sprintf(`-mod=%s %s`, in.Mod, in.Extra)
|
||||
}
|
||||
}
|
||||
if in.Extra != "" {
|
||||
in.Extra += " "
|
||||
}
|
||||
var (
|
||||
customSystems = gstr.SplitAndTrim(in.System, ",")
|
||||
customArches = gstr.SplitAndTrim(in.Arch, ",")
|
||||
)
|
||||
if len(in.Version) > 0 {
|
||||
in.Path += "/" + in.Version
|
||||
}
|
||||
// System and arch checks.
|
||||
var (
|
||||
spaceRegex = regexp.MustCompile(`\s+`)
|
||||
platformMap = make(map[string]map[string]bool)
|
||||
)
|
||||
for _, line := range strings.Split(strings.TrimSpace(cBuildPlatforms), "\n") {
|
||||
line = gstr.Trim(line)
|
||||
line = spaceRegex.ReplaceAllString(line, " ")
|
||||
var (
|
||||
array = strings.Split(line, " ")
|
||||
system = strings.TrimSpace(array[0])
|
||||
arch = strings.TrimSpace(array[1])
|
||||
)
|
||||
if platformMap[system] == nil {
|
||||
platformMap[system] = make(map[string]bool)
|
||||
}
|
||||
platformMap[system][arch] = true
|
||||
}
|
||||
// Auto packing.
|
||||
if in.PackSrc != "" {
|
||||
if in.PackDst == "" {
|
||||
mlog.Fatal(`parameter "packDst" should not be empty when "packSrc" is used`)
|
||||
}
|
||||
if gfile.Exists(in.PackDst) && !gfile.IsFile(in.PackDst) {
|
||||
mlog.Fatalf(`parameter "packDst" path "%s" should be type of file not directory`, in.PackDst)
|
||||
}
|
||||
if !gfile.Exists(in.PackDst) {
|
||||
// Remove the go file that is automatically packed resource.
|
||||
defer func() {
|
||||
_ = gfile.Remove(in.PackDst)
|
||||
mlog.Printf(`remove the automatically generated resource go file: %s`, in.PackDst)
|
||||
}()
|
||||
}
|
||||
packCmd := fmt.Sprintf(`gf pack %s %s`, in.PackSrc, in.PackDst)
|
||||
mlog.Print(packCmd)
|
||||
gproc.MustShellRun(packCmd)
|
||||
}
|
||||
|
||||
// Injected information by building flags.
|
||||
ldFlags := fmt.Sprintf(`-X 'github.com/gogf/gf/v2/os/gbuild.builtInVarStr=%v'`, c.getBuildInVarStr(in))
|
||||
|
||||
// start building
|
||||
mlog.Print("start building...")
|
||||
if in.Cgo {
|
||||
genv.MustSet("CGO_ENABLED", "1")
|
||||
} else {
|
||||
genv.MustSet("CGO_ENABLED", "0")
|
||||
}
|
||||
var (
|
||||
cmd = ""
|
||||
ext = ""
|
||||
)
|
||||
for system, item := range platformMap {
|
||||
cmd = ""
|
||||
ext = ""
|
||||
if len(customSystems) > 0 && customSystems[0] != "all" && !gstr.InArray(customSystems, system) {
|
||||
continue
|
||||
}
|
||||
for arch, _ := range item {
|
||||
if len(customArches) > 0 && customArches[0] != "all" && !gstr.InArray(customArches, arch) {
|
||||
continue
|
||||
}
|
||||
if len(customSystems) == 0 && len(customArches) == 0 {
|
||||
if runtime.GOOS == "windows" {
|
||||
ext = ".exe"
|
||||
}
|
||||
// Single binary building, output the binary to current working folder.
|
||||
output := ""
|
||||
if len(in.Output) > 0 {
|
||||
output = "-o " + in.Output + ext
|
||||
} else {
|
||||
output = "-o " + in.Name + ext
|
||||
}
|
||||
cmd = fmt.Sprintf(`go build %s -ldflags "%s" %s %s`, output, ldFlags, in.Extra, file)
|
||||
} else {
|
||||
// Cross-building, output the compiled binary to specified path.
|
||||
if system == "windows" {
|
||||
ext = ".exe"
|
||||
}
|
||||
genv.MustSet("GOOS", system)
|
||||
genv.MustSet("GOARCH", arch)
|
||||
cmd = fmt.Sprintf(
|
||||
`go build -o %s/%s/%s%s -ldflags "%s" %s%s`,
|
||||
in.Path, system+"_"+arch, in.Name, ext, ldFlags, in.Extra, file,
|
||||
)
|
||||
}
|
||||
mlog.Debug(cmd)
|
||||
// It's not necessary printing the complete command string.
|
||||
cmdShow, _ := gregex.ReplaceString(`\s+(-ldflags ".+?")\s+`, " ", cmd)
|
||||
mlog.Print(cmdShow)
|
||||
if result, err := gproc.ShellExec(cmd); err != nil {
|
||||
mlog.Printf(
|
||||
"failed to build, os:%s, arch:%s, error:\n%s\n\n%s\n",
|
||||
system, arch, gstr.Trim(result),
|
||||
`you may use command option "--debug" to enable debug info and check the details`,
|
||||
)
|
||||
if in.ExitWhenError {
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
mlog.Debug(gstr.Trim(result))
|
||||
}
|
||||
// single binary building.
|
||||
if len(customSystems) == 0 && len(customArches) == 0 {
|
||||
goto buildDone
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildDone:
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
|
||||
// getBuildInVarMapJson retrieves and returns the custom build-in variables in configuration
|
||||
// file as json.
|
||||
func (c cBuild) getBuildInVarStr(in cBuildInput) string {
|
||||
buildInVarMap := in.VarMap
|
||||
if buildInVarMap == nil {
|
||||
buildInVarMap = make(g.Map)
|
||||
}
|
||||
buildInVarMap["builtGit"] = c.getGitCommit()
|
||||
buildInVarMap["builtTime"] = gtime.Now().String()
|
||||
b, err := json.Marshal(buildInVarMap)
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
return gbase64.EncodeToString(b)
|
||||
}
|
||||
|
||||
// getGitCommit retrieves and returns the latest git commit hash string if present.
|
||||
func (c cBuild) getGitCommit() string {
|
||||
if gproc.SearchBinary("git") == "" {
|
||||
return ""
|
||||
}
|
||||
var (
|
||||
cmd = `git log -1 --format="%cd %H" --date=format:"%Y-%m-%d %H:%M:%S"`
|
||||
s, _ = gproc.ShellExec(cmd)
|
||||
)
|
||||
mlog.Debug(cmd)
|
||||
if s != "" {
|
||||
if !gstr.Contains(s, "fatal") {
|
||||
return gstr.Trim(s)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
152
cmd/gf/internal/cmd/cmd_docker.go
Normal file
152
cmd/gf/internal/cmd/cmd_docker.go
Normal file
@ -0,0 +1,152 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
|
||||
var (
|
||||
Docker = cDocker{}
|
||||
)
|
||||
|
||||
type cDocker struct {
|
||||
g.Meta `name:"docker" usage:"{cDockerUsage}" brief:"{cDockerBrief}" eg:"{cDockerEg}" dc:"{cDockerDc}"`
|
||||
}
|
||||
|
||||
const (
|
||||
cDockerUsage = `gf docker [MAIN] [OPTION]`
|
||||
cDockerBrief = `build docker image for current GoFrame project`
|
||||
cDockerEg = `
|
||||
gf docker
|
||||
gf docker -t hub.docker.com/john/image:tag
|
||||
gf docker -p -t hub.docker.com/john/image:tag
|
||||
gf docker main.go
|
||||
gf docker main.go -t hub.docker.com/john/image:tag
|
||||
gf docker main.go -t hub.docker.com/john/image:tag
|
||||
gf docker main.go -p -t hub.docker.com/john/image:tag
|
||||
`
|
||||
cDockerDc = `
|
||||
The "docker" command builds the GF project to a docker images.
|
||||
It runs "gf build" firstly to compile the project to binary file.
|
||||
It then runs "docker build" command automatically to generate the docker image.
|
||||
You should have docker installed, and there must be a Dockerfile in the root of the project.
|
||||
`
|
||||
cDockerMainBrief = `main file path for "gf build", it's "main.go" in default. empty string for no binary build`
|
||||
cDockerBuildBrief = `binary build options before docker image build, it's "-a amd64 -s linux" in default`
|
||||
cDockerFileBrief = `file path of the Dockerfile. it's "manifest/docker/Dockerfile" in default`
|
||||
cDockerShellBrief = `path of the shell file which is executed before docker build`
|
||||
cDockerPushBrief = `auto push the docker image to docker registry if "-t" option passed`
|
||||
cDockerTagNameBrief = `tag name for this docker, pattern like "image:tag". this option is required with TagPrefixes`
|
||||
cDockerTagPrefixesBrief = `tag prefixes for this docker, which are used for docker push. this option is required with TagName`
|
||||
cDockerExtraBrief = `extra build options passed to "docker image"`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cDockerUsage`: cDockerUsage,
|
||||
`cDockerBrief`: cDockerBrief,
|
||||
`cDockerEg`: cDockerEg,
|
||||
`cDockerDc`: cDockerDc,
|
||||
`cDockerMainBrief`: cDockerMainBrief,
|
||||
`cDockerFileBrief`: cDockerFileBrief,
|
||||
`cDockerShellBrief`: cDockerShellBrief,
|
||||
`cDockerBuildBrief`: cDockerBuildBrief,
|
||||
`cDockerPushBrief`: cDockerPushBrief,
|
||||
`cDockerTagNameBrief`: cDockerTagNameBrief,
|
||||
`cDockerTagPrefixesBrief`: cDockerTagPrefixesBrief,
|
||||
`cDockerExtraBrief`: cDockerExtraBrief,
|
||||
})
|
||||
}
|
||||
|
||||
type cDockerInput struct {
|
||||
g.Meta `name:"docker" config:"gfcli.docker"`
|
||||
Main string `name:"MAIN" arg:"true" brief:"{cDockerMainBrief}" d:"main.go"`
|
||||
File string `name:"file" short:"f" brief:"{cDockerFileBrief}" d:"manifest/docker/Dockerfile"`
|
||||
Shell string `name:"shell" short:"s" brief:"{cDockerShellBrief}" d:"manifest/docker/docker.sh"`
|
||||
Build string `name:"build" short:"b" brief:"{cDockerBuildBrief}" d:"-a amd64 -s linux"`
|
||||
TagName string `name:"tagName" short:"tn" brief:"{cDockerTagNameBrief}" v:"required-with:TagPrefixes"`
|
||||
TagPrefixes []string `name:"tagPrefixes" short:"tp" brief:"{cDockerTagPrefixesBrief}" v:"required-with:TagName"`
|
||||
Push bool `name:"push" short:"p" brief:"{cDockerPushBrief}" orphan:"true"`
|
||||
Extra string `name:"extra" short:"e" brief:"{cDockerExtraBrief}"`
|
||||
}
|
||||
type cDockerOutput struct{}
|
||||
|
||||
func (c cDocker) Index(ctx context.Context, in cDockerInput) (out *cDockerOutput, err error) {
|
||||
// Necessary check.
|
||||
if gproc.SearchBinary("docker") == "" {
|
||||
mlog.Fatalf(`command "docker" not found in your environment, please install docker first to proceed this command`)
|
||||
}
|
||||
|
||||
// Binary build.
|
||||
in.Build += " --exit"
|
||||
if in.Main != "" {
|
||||
if err = gproc.ShellRun(fmt.Sprintf(`gf build %s %s`, in.Main, in.Build)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Shell executing.
|
||||
if gfile.Exists(in.Shell) {
|
||||
if err = gproc.ShellRun(gfile.GetContents(in.Shell)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Docker build.
|
||||
var (
|
||||
dockerBuildOptions string
|
||||
dockerTags []string
|
||||
dockerTagBase string
|
||||
)
|
||||
if len(in.TagPrefixes) > 0 {
|
||||
for _, tagPrefix := range in.TagPrefixes {
|
||||
tagPrefix = gstr.TrimRight(tagPrefix, "/")
|
||||
dockerTags = append(dockerTags, fmt.Sprintf(`%s/%s`, tagPrefix, in.TagName))
|
||||
}
|
||||
}
|
||||
if len(dockerTags) == 0 {
|
||||
dockerTags = []string{""}
|
||||
}
|
||||
for i, dockerTag := range dockerTags {
|
||||
if i > 0 {
|
||||
err = gproc.ShellRun(fmt.Sprintf(`docker tag %s %s`, dockerTagBase, dockerTag))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
dockerTagBase = dockerTag
|
||||
dockerBuildOptions = ""
|
||||
if dockerTag != "" {
|
||||
dockerBuildOptions = fmt.Sprintf(`-t %s`, dockerTag)
|
||||
}
|
||||
if in.Extra != "" {
|
||||
dockerBuildOptions = fmt.Sprintf(`%s %s`, dockerBuildOptions, in.Extra)
|
||||
}
|
||||
err = gproc.ShellRun(fmt.Sprintf(`docker build -f %s . %s`, in.File, dockerBuildOptions))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Docker push.
|
||||
if !in.Push {
|
||||
return
|
||||
}
|
||||
for _, dockerTag := range dockerTags {
|
||||
if dockerTag == "" {
|
||||
continue
|
||||
}
|
||||
err = gproc.ShellRun(fmt.Sprintf(`docker push %s`, dockerTag))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
61
cmd/gf/internal/cmd/cmd_env.go
Normal file
61
cmd/gf/internal/cmd/cmd_env.go
Normal file
@ -0,0 +1,61 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
var (
|
||||
Env = cEnv{}
|
||||
)
|
||||
|
||||
type cEnv struct {
|
||||
g.Meta `name:"env" brief:"show current Golang environment variables"`
|
||||
}
|
||||
|
||||
type cEnvInput struct {
|
||||
g.Meta `name:"env"`
|
||||
}
|
||||
type cEnvOutput struct{}
|
||||
|
||||
func (c cEnv) Index(ctx context.Context, in cEnvInput) (out *cEnvOutput, err error) {
|
||||
result, err := gproc.ShellExec("go env")
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
if result == "" {
|
||||
mlog.Fatal(`retrieving Golang environment variables failed, did you install Golang?`)
|
||||
}
|
||||
var (
|
||||
lines = gstr.Split(result, "\n")
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
)
|
||||
array := make([][]string, 0)
|
||||
for _, line := range lines {
|
||||
line = gstr.Trim(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
if gstr.Pos(line, "set ") == 0 {
|
||||
line = line[4:]
|
||||
}
|
||||
match, _ := gregex.MatchString(`(.+?)=(.*)`, line)
|
||||
if len(match) < 3 {
|
||||
mlog.Fatalf(`invalid Golang environment variable: "%s"`, line)
|
||||
}
|
||||
array = append(array, []string{gstr.Trim(match[1]), gstr.Trim(match[2])})
|
||||
}
|
||||
tw := tablewriter.NewWriter(buffer)
|
||||
tw.SetColumnAlignment([]int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT})
|
||||
tw.AppendBulk(array)
|
||||
tw.Render()
|
||||
mlog.Print(buffer.String())
|
||||
return
|
||||
}
|
||||
30
cmd/gf/internal/cmd/cmd_gen.go
Normal file
30
cmd/gf/internal/cmd/cmd_gen.go
Normal file
@ -0,0 +1,30 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
|
||||
var (
|
||||
Gen = cGen{}
|
||||
)
|
||||
|
||||
type cGen struct {
|
||||
g.Meta `name:"gen" brief:"{cGenBrief}" dc:"{cGenDc}"`
|
||||
}
|
||||
|
||||
const (
|
||||
cGenBrief = `automatically generate go files for dao/do/entity/pb/pbentity`
|
||||
cGenDc = `
|
||||
The "gen" command is designed for multiple generating purposes.
|
||||
It's currently supporting generating go files for ORM models, protobuf and protobuf entity files.
|
||||
Please use "gf gen dao -h" for specified type help.
|
||||
`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cGenBrief`: cGenBrief,
|
||||
`cGenDc`: cGenDc,
|
||||
})
|
||||
}
|
||||
809
cmd/gf/internal/cmd/cmd_gen_dao.go
Normal file
809
cmd/gf/internal/cmd/cmd_gen_dao.go
Normal file
@ -0,0 +1,809 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
_ "github.com/gogf/gf/contrib/drivers/mssql/v2"
|
||||
_ "github.com/gogf/gf/contrib/drivers/pgsql/v2"
|
||||
_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
|
||||
//_ "github.com/gogf/gf/contrib/drivers/oracle/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
cGenDaoConfig = `gfcli.gen.dao`
|
||||
cGenDaoUsage = `gf gen dao [OPTION]`
|
||||
cGenDaoBrief = `automatically generate go files for dao/do/entity`
|
||||
cGenDaoEg = `
|
||||
gf gen dao
|
||||
gf gen dao -l "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
gf gen dao -p ./model -g user-center -t user,user_detail,user_login
|
||||
gf gen dao -r user_
|
||||
`
|
||||
|
||||
cGenDaoAd = `
|
||||
CONFIGURATION SUPPORT
|
||||
Options are also supported by configuration file.
|
||||
It's suggested using configuration file instead of command line arguments making producing.
|
||||
The configuration node name is "gfcli.gen.dao", which also supports multiple databases, for example(config.yaml):
|
||||
gfcli:
|
||||
gen:
|
||||
dao:
|
||||
- link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
tables: "order,products"
|
||||
jsonCase: "CamelLower"
|
||||
|
||||
- link: "mysql:root:12345678@tcp(127.0.0.1:3306)/primary"
|
||||
path: "./my-app"
|
||||
prefix: "primary_"
|
||||
tables: "user, userDetail"
|
||||
`
|
||||
cGenDaoBriefPath = `directory path for generated files`
|
||||
cGenDaoBriefLink = `database configuration, the same as the ORM configuration of GoFrame`
|
||||
cGenDaoBriefTables = `generate models only for given tables, multiple table names separated with ','`
|
||||
cGenDaoBriefTablesEx = `generate models excluding given tables, multiple table names separated with ','`
|
||||
cGenDaoBriefPrefix = `add prefix for all table of specified link/database tables`
|
||||
cGenDaoBriefRemovePrefix = `remove specified prefix of the table, multiple prefix separated with ','`
|
||||
cGenDaoBriefStdTime = `use time.Time from stdlib instead of gtime.Time for generated time/date fields of tables`
|
||||
cGenDaoBriefWithTime = `add created time for auto produced go files`
|
||||
cGenDaoBriefGJsonSupport = `use gJsonSupport to use *gjson.Json instead of string for generated json fields of tables`
|
||||
cGenDaoBriefImportPrefix = `custom import prefix for generated go files`
|
||||
cGenDaoBriefDaoPath = `directory path for storing generated dao files under path`
|
||||
cGenDaoBriefDoPath = `directory path for storing generated do files under path`
|
||||
cGenDaoBriefEntityPath = `directory path for storing generated entity files under path`
|
||||
cGenDaoBriefOverwriteDao = `overwrite all dao files both inside/outside internal folder`
|
||||
cGenDaoBriefModelFile = `custom file name for storing generated model content`
|
||||
cGenDaoBriefModelFileForDao = `custom file name generating model for DAO operations like Where/Data. It's empty in default`
|
||||
cGenDaoBriefDescriptionTag = `add comment to description tag for each field`
|
||||
cGenDaoBriefNoJsonTag = `no json tag will be added for each field`
|
||||
cGenDaoBriefNoModelComment = `no model comment will be added for each field`
|
||||
cGenDaoBriefGroup = `
|
||||
specifying the configuration group name of database for generated ORM instance,
|
||||
it's not necessary and the default value is "default"
|
||||
`
|
||||
cGenDaoBriefJsonCase = `
|
||||
generated json tag case for model struct, cases are as follows:
|
||||
| Case | Example |
|
||||
|---------------- |--------------------|
|
||||
| Camel | AnyKindOfString |
|
||||
| CamelLower | anyKindOfString | default
|
||||
| Snake | any_kind_of_string |
|
||||
| SnakeScreaming | ANY_KIND_OF_STRING |
|
||||
| SnakeFirstUpper | rgb_code_md5 |
|
||||
| Kebab | any-kind-of-string |
|
||||
| KebabScreaming | ANY-KIND-OF-STRING |
|
||||
`
|
||||
|
||||
tplVarTableName = `{TplTableName}`
|
||||
tplVarTableNameCamelCase = `{TplTableNameCamelCase}`
|
||||
tplVarTableNameCamelLowerCase = `{TplTableNameCamelLowerCase}`
|
||||
tplVarPackageImports = `{TplPackageImports}`
|
||||
tplVarImportPrefix = `{TplImportPrefix}`
|
||||
tplVarStructDefine = `{TplStructDefine}`
|
||||
tplVarColumnDefine = `{TplColumnDefine}`
|
||||
tplVarColumnNames = `{TplColumnNames}`
|
||||
tplVarGroupName = `{TplGroupName}`
|
||||
tplVarDatetimeStr = `{TplDatetimeStr}`
|
||||
)
|
||||
|
||||
var (
|
||||
createdAt = gtime.Now()
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cGenDaoConfig`: cGenDaoConfig,
|
||||
`cGenDaoUsage`: cGenDaoUsage,
|
||||
`cGenDaoBrief`: cGenDaoBrief,
|
||||
`cGenDaoEg`: cGenDaoEg,
|
||||
`cGenDaoAd`: cGenDaoAd,
|
||||
`cGenDaoBriefPath`: cGenDaoBriefPath,
|
||||
`cGenDaoBriefLink`: cGenDaoBriefLink,
|
||||
`cGenDaoBriefTables`: cGenDaoBriefTables,
|
||||
`cGenDaoBriefTablesEx`: cGenDaoBriefTablesEx,
|
||||
`cGenDaoBriefPrefix`: cGenDaoBriefPrefix,
|
||||
`cGenDaoBriefRemovePrefix`: cGenDaoBriefRemovePrefix,
|
||||
`cGenDaoBriefStdTime`: cGenDaoBriefStdTime,
|
||||
`cGenDaoBriefWithTime`: cGenDaoBriefWithTime,
|
||||
`cGenDaoBriefDaoPath`: cGenDaoBriefDaoPath,
|
||||
`cGenDaoBriefDoPath`: cGenDaoBriefDoPath,
|
||||
`cGenDaoBriefEntityPath`: cGenDaoBriefEntityPath,
|
||||
`cGenDaoBriefGJsonSupport`: cGenDaoBriefGJsonSupport,
|
||||
`cGenDaoBriefImportPrefix`: cGenDaoBriefImportPrefix,
|
||||
`cGenDaoBriefOverwriteDao`: cGenDaoBriefOverwriteDao,
|
||||
`cGenDaoBriefModelFile`: cGenDaoBriefModelFile,
|
||||
`cGenDaoBriefModelFileForDao`: cGenDaoBriefModelFileForDao,
|
||||
`cGenDaoBriefDescriptionTag`: cGenDaoBriefDescriptionTag,
|
||||
`cGenDaoBriefNoJsonTag`: cGenDaoBriefNoJsonTag,
|
||||
`cGenDaoBriefNoModelComment`: cGenDaoBriefNoModelComment,
|
||||
`cGenDaoBriefGroup`: cGenDaoBriefGroup,
|
||||
`cGenDaoBriefJsonCase`: cGenDaoBriefJsonCase,
|
||||
})
|
||||
}
|
||||
|
||||
type (
|
||||
cGenDaoInput struct {
|
||||
g.Meta `name:"dao" config:"{cGenDaoConfig}" usage:"{cGenDaoUsage}" brief:"{cGenDaoBrief}" eg:"{cGenDaoEg}" ad:"{cGenDaoAd}"`
|
||||
Path string `name:"path" short:"p" brief:"{cGenDaoBriefPath}" d:"internal"`
|
||||
Link string `name:"link" short:"l" brief:"{cGenDaoBriefLink}"`
|
||||
Tables string `name:"tables" short:"t" brief:"{cGenDaoBriefTables}"`
|
||||
TablesEx string `name:"tablesEx" short:"x" brief:"{cGenDaoBriefTablesEx}"`
|
||||
Group string `name:"group" short:"g" brief:"{cGenDaoBriefGroup}" d:"default"`
|
||||
Prefix string `name:"prefix" short:"f" brief:"{cGenDaoBriefPrefix}"`
|
||||
RemovePrefix string `name:"removePrefix" short:"r" brief:"{cGenDaoBriefRemovePrefix}"`
|
||||
JsonCase string `name:"jsonCase" short:"j" brief:"{cGenDaoBriefJsonCase}" d:"CamelLower"`
|
||||
ImportPrefix string `name:"importPrefix" short:"i" brief:"{cGenDaoBriefImportPrefix}"`
|
||||
DaoPath string `name:"daoPath" short:"d" brief:"{cGenDaoBriefDaoPath}" d:"dao"`
|
||||
DoPath string `name:"doPath" short:"o" brief:"{cGenDaoBriefDoPath}" d:"model/do"`
|
||||
EntityPath string `name:"entityPath" short:"e" brief:"{cGenDaoBriefEntityPath}" d:"model/entity"`
|
||||
StdTime bool `name:"stdTime" short:"s" brief:"{cGenDaoBriefStdTime}" orphan:"true"`
|
||||
WithTime bool `name:"withTime" short:"w" brief:"{cGenDaoBriefWithTime}" orphan:"true"`
|
||||
GJsonSupport bool `name:"gJsonSupport" short:"n" brief:"{cGenDaoBriefGJsonSupport}" orphan:"true"`
|
||||
OverwriteDao bool `name:"overwriteDao" short:"v" brief:"{cGenDaoBriefOverwriteDao}" orphan:"true"`
|
||||
DescriptionTag bool `name:"descriptionTag" short:"c" brief:"{cGenDaoBriefDescriptionTag}" orphan:"true"`
|
||||
NoJsonTag bool `name:"noJsonTag" short:"k" brief:"{cGenDaoBriefNoJsonTag" orphan:"true"`
|
||||
NoModelComment bool `name:"noModelComment" short:"m" brief:"{cGenDaoBriefNoModelComment}" orphan:"true"`
|
||||
}
|
||||
cGenDaoOutput struct{}
|
||||
|
||||
cGenDaoInternalInput struct {
|
||||
cGenDaoInput
|
||||
TableName string // TableName specifies the table name of the table.
|
||||
NewTableName string // NewTableName specifies the prefix-stripped name of the table.
|
||||
ModName string // ModName specifies the module name of current golang project, which is used for import purpose.
|
||||
}
|
||||
)
|
||||
|
||||
func (c cGen) Dao(ctx context.Context, in cGenDaoInput) (out *cGenDaoOutput, err error) {
|
||||
if g.Cfg().Available(ctx) {
|
||||
v := g.Cfg().MustGet(ctx, cGenDaoConfig)
|
||||
if v.IsSlice() {
|
||||
for i := 0; i < len(v.Interfaces()); i++ {
|
||||
doGenDaoForArray(ctx, i, in)
|
||||
}
|
||||
} else {
|
||||
doGenDaoForArray(ctx, -1, in)
|
||||
}
|
||||
} else {
|
||||
doGenDaoForArray(ctx, -1, in)
|
||||
}
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
|
||||
// doGenDaoForArray implements the "gen dao" command for configuration array.
|
||||
func doGenDaoForArray(ctx context.Context, index int, in cGenDaoInput) {
|
||||
var (
|
||||
err error
|
||||
db gdb.DB
|
||||
modName string // Go module name, eg: github.com/gogf/gf.
|
||||
)
|
||||
if index >= 0 {
|
||||
err = g.Cfg().MustGet(
|
||||
ctx,
|
||||
fmt.Sprintf(`%s.%d`, cGenDaoConfig, index),
|
||||
).Scan(&in)
|
||||
if err != nil {
|
||||
mlog.Fatalf(`invalid configuration of "%s": %+v`, cGenDaoConfig, err)
|
||||
}
|
||||
}
|
||||
if dirRealPath := gfile.RealPath(in.Path); dirRealPath == "" {
|
||||
mlog.Fatalf(`path "%s" does not exist`, in.Path)
|
||||
}
|
||||
removePrefixArray := gstr.SplitAndTrim(in.RemovePrefix, ",")
|
||||
if in.ImportPrefix == "" {
|
||||
if !gfile.Exists("go.mod") {
|
||||
mlog.Fatal("go.mod does not exist in current working directory")
|
||||
}
|
||||
var (
|
||||
goModContent = gfile.GetContents("go.mod")
|
||||
match, _ = gregex.MatchString(`^module\s+(.+)\s*`, goModContent)
|
||||
)
|
||||
if len(match) > 1 {
|
||||
modName = gstr.Trim(match[1])
|
||||
} else {
|
||||
mlog.Fatal("module name does not found in go.mod")
|
||||
}
|
||||
}
|
||||
|
||||
// It uses user passed database configuration.
|
||||
if in.Link != "" {
|
||||
var (
|
||||
tempGroup = gtime.TimestampNanoStr()
|
||||
match, _ = gregex.MatchString(`([a-z]+):(.+)`, in.Link)
|
||||
)
|
||||
if len(match) == 3 {
|
||||
gdb.AddConfigNode(tempGroup, gdb.ConfigNode{
|
||||
Type: gstr.Trim(match[1]),
|
||||
Link: gstr.Trim(match[2]),
|
||||
})
|
||||
if db, err = gdb.Instance(tempGroup); err != nil {
|
||||
mlog.Debugf(`database initialization failed: %+v`, err)
|
||||
}
|
||||
} else {
|
||||
mlog.Fatalf(`invalid database configuration: %s`, in.Link)
|
||||
}
|
||||
} else {
|
||||
db = g.DB(in.Group)
|
||||
}
|
||||
if db == nil {
|
||||
mlog.Fatal(`database initialization failed, may be invalid database configuration`)
|
||||
}
|
||||
|
||||
var tableNames []string
|
||||
if in.Tables != "" {
|
||||
tableNames = gstr.SplitAndTrim(in.Tables, ",")
|
||||
} else {
|
||||
tableNames, err = db.Tables(context.TODO())
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables failed: %+v", err)
|
||||
}
|
||||
}
|
||||
// Table excluding.
|
||||
if in.TablesEx != "" {
|
||||
array := garray.NewStrArrayFrom(tableNames)
|
||||
for _, v := range gstr.SplitAndTrim(in.TablesEx, ",") {
|
||||
array.RemoveValue(v)
|
||||
}
|
||||
tableNames = array.Slice()
|
||||
}
|
||||
|
||||
// Generating dao & model go files one by one according to given table name.
|
||||
newTableNames := make([]string, len(tableNames))
|
||||
for i, tableName := range tableNames {
|
||||
newTableName := tableName
|
||||
for _, v := range removePrefixArray {
|
||||
newTableName = gstr.TrimLeftStr(newTableName, v, 1)
|
||||
}
|
||||
newTableName = in.Prefix + newTableName
|
||||
newTableNames[i] = newTableName
|
||||
// Dao.
|
||||
generateDao(ctx, db, cGenDaoInternalInput{
|
||||
cGenDaoInput: in,
|
||||
TableName: tableName,
|
||||
NewTableName: newTableName,
|
||||
ModName: modName,
|
||||
})
|
||||
}
|
||||
// Do.
|
||||
generateDo(ctx, db, tableNames, newTableNames, cGenDaoInternalInput{
|
||||
cGenDaoInput: in,
|
||||
ModName: modName,
|
||||
})
|
||||
// Entity.
|
||||
generateEntity(ctx, db, tableNames, newTableNames, cGenDaoInternalInput{
|
||||
cGenDaoInput: in,
|
||||
ModName: modName,
|
||||
})
|
||||
}
|
||||
|
||||
// generateDaoContentFile generates the dao and model content of given table.
|
||||
func generateDao(ctx context.Context, db gdb.DB, in cGenDaoInternalInput) {
|
||||
// Generating table data preparing.
|
||||
fieldMap, err := db.TableFields(ctx, in.TableName)
|
||||
if err != nil {
|
||||
mlog.Fatalf(`fetching tables fields failed for table "%s": %+v`, in.TableName, err)
|
||||
}
|
||||
var (
|
||||
dirRealPath = gfile.RealPath(in.Path)
|
||||
dirPathDao = gfile.Join(in.Path, in.DaoPath)
|
||||
tableNameCamelCase = gstr.CaseCamel(in.NewTableName)
|
||||
tableNameCamelLowerCase = gstr.CaseCamelLower(in.NewTableName)
|
||||
tableNameSnakeCase = gstr.CaseSnake(in.NewTableName)
|
||||
importPrefix = in.ImportPrefix
|
||||
)
|
||||
if importPrefix == "" {
|
||||
if dirRealPath == "" {
|
||||
dirRealPath = in.Path
|
||||
importPrefix = dirRealPath
|
||||
importPrefix = gstr.Trim(dirRealPath, "./")
|
||||
} else {
|
||||
importPrefix = gstr.Replace(dirRealPath, gfile.Pwd(), "")
|
||||
}
|
||||
importPrefix = gstr.Replace(importPrefix, gfile.Separator, "/")
|
||||
importPrefix = gstr.Join(g.SliceStr{in.ModName, importPrefix, in.DaoPath}, "/")
|
||||
importPrefix, _ = gregex.ReplaceString(`\/{2,}`, `/`, gstr.Trim(importPrefix, "/"))
|
||||
} else {
|
||||
importPrefix = gstr.Join(g.SliceStr{importPrefix, in.DaoPath}, "/")
|
||||
}
|
||||
|
||||
fileName := gstr.Trim(tableNameSnakeCase, "-_.")
|
||||
if len(fileName) > 5 && fileName[len(fileName)-5:] == "_test" {
|
||||
// Add suffix to avoid the table name which contains "_test",
|
||||
// which would make the go file a testing file.
|
||||
fileName += "_table"
|
||||
}
|
||||
|
||||
// dao - index
|
||||
generateDaoIndex(in, tableNameCamelCase, tableNameCamelLowerCase, importPrefix, dirPathDao, fileName)
|
||||
|
||||
// dao - internal
|
||||
generateDaoInternal(in, tableNameCamelCase, tableNameCamelLowerCase, importPrefix, dirPathDao, fileName, fieldMap)
|
||||
}
|
||||
|
||||
func generateDo(ctx context.Context, db gdb.DB, tableNames, newTableNames []string, in cGenDaoInternalInput) {
|
||||
var (
|
||||
doDirPath = gfile.Join(in.Path, in.DoPath)
|
||||
)
|
||||
in.NoJsonTag = true
|
||||
in.DescriptionTag = false
|
||||
in.NoModelComment = false
|
||||
// Model content.
|
||||
for i, tableName := range tableNames {
|
||||
in.TableName = tableName
|
||||
fieldMap, err := db.TableFields(ctx, tableName)
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", in.TableName, err)
|
||||
}
|
||||
var (
|
||||
newTableName = newTableNames[i]
|
||||
doFilePath = gfile.Join(doDirPath, gstr.CaseSnake(newTableName)+".go")
|
||||
structDefinition = generateStructDefinition(generateStructDefinitionInput{
|
||||
cGenDaoInternalInput: in,
|
||||
StructName: gstr.CaseCamel(newTableName),
|
||||
FieldMap: fieldMap,
|
||||
IsDo: true,
|
||||
})
|
||||
)
|
||||
// replace all types to interface{}.
|
||||
structDefinition, _ = gregex.ReplaceStringFuncMatch(
|
||||
"([A-Z]\\w*?)\\s+([\\w\\*\\.]+?)\\s+(//)",
|
||||
structDefinition,
|
||||
func(match []string) string {
|
||||
// If the type is already a pointer/slice/map, it does nothing.
|
||||
if !gstr.HasPrefix(match[2], "*") && !gstr.HasPrefix(match[2], "[]") && !gstr.HasPrefix(match[2], "map") {
|
||||
return fmt.Sprintf(`%s interface{} %s`, match[1], match[3])
|
||||
}
|
||||
return match[0]
|
||||
},
|
||||
)
|
||||
modelContent := generateDoContent(
|
||||
in,
|
||||
tableName,
|
||||
gstr.CaseCamel(newTableName),
|
||||
structDefinition,
|
||||
)
|
||||
err = gfile.PutContents(doFilePath, strings.TrimSpace(modelContent))
|
||||
if err != nil {
|
||||
mlog.Fatalf(`writing content to "%s" failed: %v`, doFilePath, err)
|
||||
} else {
|
||||
utils.GoFmt(doFilePath)
|
||||
mlog.Print("generated:", doFilePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generateEntity(ctx context.Context, db gdb.DB, tableNames, newTableNames []string, in cGenDaoInternalInput) {
|
||||
var (
|
||||
entityDirPath = gfile.Join(in.Path, in.EntityPath)
|
||||
)
|
||||
|
||||
// Model content.
|
||||
for i, tableName := range tableNames {
|
||||
fieldMap, err := db.TableFields(ctx, tableName)
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", in.TableName, err)
|
||||
}
|
||||
var (
|
||||
newTableName = newTableNames[i]
|
||||
entityFilePath = gfile.Join(entityDirPath, gstr.CaseSnake(newTableName)+".go")
|
||||
entityContent = generateEntityContent(
|
||||
in,
|
||||
newTableName,
|
||||
gstr.CaseCamel(newTableName),
|
||||
generateStructDefinition(generateStructDefinitionInput{
|
||||
cGenDaoInternalInput: in,
|
||||
StructName: gstr.CaseCamel(newTableName),
|
||||
FieldMap: fieldMap,
|
||||
IsDo: false,
|
||||
}),
|
||||
)
|
||||
)
|
||||
err = gfile.PutContents(entityFilePath, strings.TrimSpace(entityContent))
|
||||
if err != nil {
|
||||
mlog.Fatalf("writing content to '%s' failed: %v", entityFilePath, err)
|
||||
} else {
|
||||
utils.GoFmt(entityFilePath)
|
||||
mlog.Print("generated:", entityFilePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getImportPartContent(source string, isDo bool) string {
|
||||
var (
|
||||
packageImportsArray = garray.NewStrArray()
|
||||
)
|
||||
|
||||
if isDo {
|
||||
packageImportsArray.Append(`"github.com/gogf/gf/v2/frame/g"`)
|
||||
}
|
||||
|
||||
// Time package recognition.
|
||||
if strings.Contains(source, "gtime.Time") {
|
||||
packageImportsArray.Append(`"github.com/gogf/gf/v2/os/gtime"`)
|
||||
} else if strings.Contains(source, "time.Time") {
|
||||
packageImportsArray.Append(`"time"`)
|
||||
}
|
||||
|
||||
// Json type.
|
||||
if strings.Contains(source, "gjson.Json") {
|
||||
packageImportsArray.Append(`"github.com/gogf/gf/v2/encoding/gjson"`)
|
||||
}
|
||||
|
||||
// Generate and write content to golang file.
|
||||
packageImportsStr := ""
|
||||
if packageImportsArray.Len() > 0 {
|
||||
packageImportsStr = fmt.Sprintf("import(\n%s\n)", packageImportsArray.Join("\n"))
|
||||
}
|
||||
return packageImportsStr
|
||||
}
|
||||
|
||||
func generateEntityContent(in cGenDaoInternalInput, tableName, tableNameCamelCase, structDefine string) string {
|
||||
entityContent := gstr.ReplaceByMap(consts.TemplateGenDaoEntityContent, g.MapStrStr{
|
||||
tplVarTableName: tableName,
|
||||
tplVarPackageImports: getImportPartContent(structDefine, false),
|
||||
tplVarTableNameCamelCase: tableNameCamelCase,
|
||||
tplVarStructDefine: structDefine,
|
||||
})
|
||||
entityContent = replaceDefaultVar(in, entityContent)
|
||||
return entityContent
|
||||
}
|
||||
|
||||
func generateDoContent(in cGenDaoInternalInput, tableName, tableNameCamelCase, structDefine string) string {
|
||||
doContent := gstr.ReplaceByMap(consts.TemplateGenDaoDoContent, g.MapStrStr{
|
||||
tplVarTableName: tableName,
|
||||
tplVarPackageImports: getImportPartContent(structDefine, true),
|
||||
tplVarTableNameCamelCase: tableNameCamelCase,
|
||||
tplVarStructDefine: structDefine,
|
||||
})
|
||||
doContent = replaceDefaultVar(in, doContent)
|
||||
return doContent
|
||||
}
|
||||
|
||||
func generateDaoIndex(in cGenDaoInternalInput, tableNameCamelCase, tableNameCamelLowerCase, importPrefix, dirPathDao, fileName string) {
|
||||
path := gfile.Join(dirPathDao, fileName+".go")
|
||||
if in.OverwriteDao || !gfile.Exists(path) {
|
||||
indexContent := gstr.ReplaceByMap(getTplDaoIndexContent(""), g.MapStrStr{
|
||||
tplVarImportPrefix: importPrefix,
|
||||
tplVarTableName: in.TableName,
|
||||
tplVarTableNameCamelCase: tableNameCamelCase,
|
||||
tplVarTableNameCamelLowerCase: tableNameCamelLowerCase,
|
||||
})
|
||||
indexContent = replaceDefaultVar(in, indexContent)
|
||||
if err := gfile.PutContents(path, strings.TrimSpace(indexContent)); err != nil {
|
||||
mlog.Fatalf("writing content to '%s' failed: %v", path, err)
|
||||
} else {
|
||||
utils.GoFmt(path)
|
||||
mlog.Print("generated:", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generateDaoInternal(
|
||||
in cGenDaoInternalInput,
|
||||
tableNameCamelCase, tableNameCamelLowerCase, importPrefix string,
|
||||
dirPathDao, fileName string,
|
||||
fieldMap map[string]*gdb.TableField,
|
||||
) {
|
||||
path := gfile.Join(dirPathDao, "internal", fileName+".go")
|
||||
modelContent := gstr.ReplaceByMap(getTplDaoInternalContent(""), g.MapStrStr{
|
||||
tplVarImportPrefix: importPrefix,
|
||||
tplVarTableName: in.TableName,
|
||||
tplVarGroupName: in.Group,
|
||||
tplVarTableNameCamelCase: tableNameCamelCase,
|
||||
tplVarTableNameCamelLowerCase: tableNameCamelLowerCase,
|
||||
tplVarColumnDefine: gstr.Trim(generateColumnDefinitionForDao(fieldMap)),
|
||||
tplVarColumnNames: gstr.Trim(generateColumnNamesForDao(fieldMap)),
|
||||
})
|
||||
modelContent = replaceDefaultVar(in, modelContent)
|
||||
if err := gfile.PutContents(path, strings.TrimSpace(modelContent)); err != nil {
|
||||
mlog.Fatalf("writing content to '%s' failed: %v", path, err)
|
||||
} else {
|
||||
utils.GoFmt(path)
|
||||
mlog.Print("generated:", path)
|
||||
}
|
||||
}
|
||||
|
||||
func replaceDefaultVar(in cGenDaoInternalInput, origin string) string {
|
||||
var tplDatetimeStr string
|
||||
if in.WithTime {
|
||||
tplDatetimeStr = fmt.Sprintf(`Created at %s`, createdAt.String())
|
||||
}
|
||||
return gstr.ReplaceByMap(origin, g.MapStrStr{
|
||||
tplVarDatetimeStr: tplDatetimeStr,
|
||||
})
|
||||
}
|
||||
|
||||
type generateStructDefinitionInput struct {
|
||||
cGenDaoInternalInput
|
||||
StructName string // Struct name.
|
||||
FieldMap map[string]*gdb.TableField // Table field map.
|
||||
IsDo bool // Is generating DTO struct.
|
||||
}
|
||||
|
||||
func generateStructDefinition(in generateStructDefinitionInput) string {
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
array := make([][]string, len(in.FieldMap))
|
||||
names := sortFieldKeyForDao(in.FieldMap)
|
||||
for index, name := range names {
|
||||
field := in.FieldMap[name]
|
||||
array[index] = generateStructFieldDefinition(field, in)
|
||||
}
|
||||
tw := tablewriter.NewWriter(buffer)
|
||||
tw.SetBorder(false)
|
||||
tw.SetRowLine(false)
|
||||
tw.SetAutoWrapText(false)
|
||||
tw.SetColumnSeparator("")
|
||||
tw.AppendBulk(array)
|
||||
tw.Render()
|
||||
stContent := buffer.String()
|
||||
// Let's do this hack of table writer for indent!
|
||||
stContent = gstr.Replace(stContent, " #", "")
|
||||
stContent = gstr.Replace(stContent, "` ", "`")
|
||||
stContent = gstr.Replace(stContent, "``", "")
|
||||
buffer.Reset()
|
||||
buffer.WriteString(fmt.Sprintf("type %s struct {\n", in.StructName))
|
||||
if in.IsDo {
|
||||
buffer.WriteString(fmt.Sprintf("g.Meta `orm:\"table:%s, do:true\"`\n", in.TableName))
|
||||
}
|
||||
buffer.WriteString(stContent)
|
||||
buffer.WriteString("}")
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// generateStructFieldForModel generates and returns the attribute definition for specified field.
|
||||
func generateStructFieldDefinition(field *gdb.TableField, in generateStructDefinitionInput) []string {
|
||||
var (
|
||||
typeName string
|
||||
jsonTag = getJsonTagFromCase(field.Name, in.JsonCase)
|
||||
)
|
||||
t, _ := gregex.ReplaceString(`\(.+\)`, "", field.Type)
|
||||
t = gstr.Split(gstr.Trim(t), " ")[0]
|
||||
t = gstr.ToLower(t)
|
||||
switch t {
|
||||
case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob":
|
||||
typeName = "[]byte"
|
||||
|
||||
case "bit", "int", "int2", "tinyint", "small_int", "smallint", "medium_int", "mediumint", "serial":
|
||||
if gstr.ContainsI(field.Type, "unsigned") {
|
||||
typeName = "uint"
|
||||
} else {
|
||||
typeName = "int"
|
||||
}
|
||||
|
||||
case "int4", "int8", "big_int", "bigint", "bigserial":
|
||||
if gstr.ContainsI(field.Type, "unsigned") {
|
||||
typeName = "uint64"
|
||||
} else {
|
||||
typeName = "int64"
|
||||
}
|
||||
|
||||
case "real":
|
||||
typeName = "float32"
|
||||
|
||||
case "float", "double", "decimal", "smallmoney", "numeric":
|
||||
typeName = "float64"
|
||||
|
||||
case "bool":
|
||||
typeName = "bool"
|
||||
|
||||
case "datetime", "timestamp", "date", "time":
|
||||
if in.StdTime {
|
||||
typeName = "time.Time"
|
||||
} else {
|
||||
typeName = "*gtime.Time"
|
||||
}
|
||||
case "json", "jsonb":
|
||||
if in.GJsonSupport {
|
||||
typeName = "*gjson.Json"
|
||||
} else {
|
||||
typeName = "string"
|
||||
}
|
||||
default:
|
||||
// Automatically detect its data type.
|
||||
switch {
|
||||
case strings.Contains(t, "int"):
|
||||
typeName = "int"
|
||||
case strings.Contains(t, "text") || strings.Contains(t, "char"):
|
||||
typeName = "string"
|
||||
case strings.Contains(t, "float") || strings.Contains(t, "double"):
|
||||
typeName = "float64"
|
||||
case strings.Contains(t, "bool"):
|
||||
typeName = "bool"
|
||||
case strings.Contains(t, "binary") || strings.Contains(t, "blob"):
|
||||
typeName = "[]byte"
|
||||
case strings.Contains(t, "date") || strings.Contains(t, "time"):
|
||||
if in.StdTime {
|
||||
typeName = "time.Time"
|
||||
} else {
|
||||
typeName = "*gtime.Time"
|
||||
}
|
||||
default:
|
||||
typeName = "string"
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
tagKey = "`"
|
||||
result = []string{
|
||||
" #" + gstr.CaseCamel(field.Name),
|
||||
" #" + typeName,
|
||||
}
|
||||
descriptionTag = gstr.Replace(formatComment(field.Comment), `"`, `\"`)
|
||||
)
|
||||
|
||||
result = append(result, " #"+fmt.Sprintf(tagKey+`json:"%s"`, jsonTag))
|
||||
result = append(result, " #"+fmt.Sprintf(`description:"%s"`+tagKey, descriptionTag))
|
||||
result = append(result, " #"+fmt.Sprintf(`// %s`, formatComment(field.Comment)))
|
||||
|
||||
for k, v := range result {
|
||||
if in.NoJsonTag {
|
||||
v, _ = gregex.ReplaceString(`json:".+"`, ``, v)
|
||||
}
|
||||
if !in.DescriptionTag {
|
||||
v, _ = gregex.ReplaceString(`description:".*"`, ``, v)
|
||||
}
|
||||
if in.NoModelComment {
|
||||
v, _ = gregex.ReplaceString(`//.+`, ``, v)
|
||||
}
|
||||
result[k] = v
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// formatComment formats the comment string to fit the golang code without any lines.
|
||||
func formatComment(comment string) string {
|
||||
comment = gstr.ReplaceByArray(comment, g.SliceStr{
|
||||
"\n", " ",
|
||||
"\r", " ",
|
||||
})
|
||||
comment = gstr.Replace(comment, `\n`, " ")
|
||||
comment = gstr.Trim(comment)
|
||||
return comment
|
||||
}
|
||||
|
||||
// generateColumnDefinitionForDao generates and returns the column names definition for specified table.
|
||||
func generateColumnDefinitionForDao(fieldMap map[string]*gdb.TableField) string {
|
||||
var (
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
array = make([][]string, len(fieldMap))
|
||||
names = sortFieldKeyForDao(fieldMap)
|
||||
)
|
||||
for index, name := range names {
|
||||
var (
|
||||
field = fieldMap[name]
|
||||
comment = gstr.Trim(gstr.ReplaceByArray(field.Comment, g.SliceStr{
|
||||
"\n", " ",
|
||||
"\r", " ",
|
||||
}))
|
||||
)
|
||||
array[index] = []string{
|
||||
" #" + gstr.CaseCamel(field.Name),
|
||||
" # " + "string",
|
||||
" #" + fmt.Sprintf(`// %s`, comment),
|
||||
}
|
||||
}
|
||||
tw := tablewriter.NewWriter(buffer)
|
||||
tw.SetBorder(false)
|
||||
tw.SetRowLine(false)
|
||||
tw.SetAutoWrapText(false)
|
||||
tw.SetColumnSeparator("")
|
||||
tw.AppendBulk(array)
|
||||
tw.Render()
|
||||
defineContent := buffer.String()
|
||||
// Let's do this hack of table writer for indent!
|
||||
defineContent = gstr.Replace(defineContent, " #", "")
|
||||
buffer.Reset()
|
||||
buffer.WriteString(defineContent)
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// generateColumnNamesForDao generates and returns the column names assignment content of column struct
|
||||
// for specified table.
|
||||
func generateColumnNamesForDao(fieldMap map[string]*gdb.TableField) string {
|
||||
var (
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
array = make([][]string, len(fieldMap))
|
||||
names = sortFieldKeyForDao(fieldMap)
|
||||
)
|
||||
for index, name := range names {
|
||||
field := fieldMap[name]
|
||||
array[index] = []string{
|
||||
" #" + gstr.CaseCamel(field.Name) + ":",
|
||||
fmt.Sprintf(` #"%s",`, field.Name),
|
||||
}
|
||||
}
|
||||
tw := tablewriter.NewWriter(buffer)
|
||||
tw.SetBorder(false)
|
||||
tw.SetRowLine(false)
|
||||
tw.SetAutoWrapText(false)
|
||||
tw.SetColumnSeparator("")
|
||||
tw.AppendBulk(array)
|
||||
tw.Render()
|
||||
namesContent := buffer.String()
|
||||
// Let's do this hack of table writer for indent!
|
||||
namesContent = gstr.Replace(namesContent, " #", "")
|
||||
buffer.Reset()
|
||||
buffer.WriteString(namesContent)
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func getTplDaoIndexContent(tplDaoIndexPath string) string {
|
||||
if tplDaoIndexPath != "" {
|
||||
return gfile.GetContents(tplDaoIndexPath)
|
||||
}
|
||||
return consts.TemplateDaoDaoIndexContent
|
||||
}
|
||||
|
||||
func getTplDaoInternalContent(tplDaoInternalPath string) string {
|
||||
if tplDaoInternalPath != "" {
|
||||
return gfile.GetContents(tplDaoInternalPath)
|
||||
}
|
||||
return consts.TemplateDaoDaoInternalContent
|
||||
}
|
||||
|
||||
// getJsonTagFromCase call gstr.Case* function to convert the s to specified case.
|
||||
func getJsonTagFromCase(str, caseStr string) string {
|
||||
switch gstr.ToLower(caseStr) {
|
||||
case gstr.ToLower("Camel"):
|
||||
return gstr.CaseCamel(str)
|
||||
|
||||
case gstr.ToLower("CamelLower"):
|
||||
return gstr.CaseCamelLower(str)
|
||||
|
||||
case gstr.ToLower("Kebab"):
|
||||
return gstr.CaseKebab(str)
|
||||
|
||||
case gstr.ToLower("KebabScreaming"):
|
||||
return gstr.CaseKebabScreaming(str)
|
||||
|
||||
case gstr.ToLower("Snake"):
|
||||
return gstr.CaseSnake(str)
|
||||
|
||||
case gstr.ToLower("SnakeFirstUpper"):
|
||||
return gstr.CaseSnakeFirstUpper(str)
|
||||
|
||||
case gstr.ToLower("SnakeScreaming"):
|
||||
return gstr.CaseSnakeScreaming(str)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func sortFieldKeyForDao(fieldMap map[string]*gdb.TableField) []string {
|
||||
names := make(map[int]string)
|
||||
for _, field := range fieldMap {
|
||||
names[field.Index] = field.Name
|
||||
}
|
||||
var (
|
||||
i = 0
|
||||
j = 0
|
||||
result = make([]string, len(names))
|
||||
)
|
||||
for {
|
||||
if len(names) == 0 {
|
||||
break
|
||||
}
|
||||
if val, ok := names[i]; ok {
|
||||
result[j] = val
|
||||
j++
|
||||
delete(names, i)
|
||||
}
|
||||
i++
|
||||
}
|
||||
return result
|
||||
}
|
||||
78
cmd/gf/internal/cmd/cmd_gen_pb.go
Normal file
78
cmd/gf/internal/cmd/cmd_gen_pb.go
Normal file
@ -0,0 +1,78 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/genv"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
)
|
||||
|
||||
type (
|
||||
cGenPbInput struct {
|
||||
g.Meta `name:"pb" brief:"parse proto files and generate protobuf go files"`
|
||||
}
|
||||
cGenPbOutput struct{}
|
||||
)
|
||||
|
||||
func (c cGen) Pb(ctx context.Context, in cGenPbInput) (out *cGenPbOutput, err error) {
|
||||
// Necessary check.
|
||||
if gproc.SearchBinary("protoc") == "" {
|
||||
mlog.Fatalf(`command "protoc" not found in your environment, please install protoc first to proceed this command`)
|
||||
}
|
||||
|
||||
// protocol fold checks.
|
||||
protoFolder := "protocol"
|
||||
if !gfile.Exists(protoFolder) {
|
||||
mlog.Fatalf(`proto files folder "%s" does not exist`, protoFolder)
|
||||
}
|
||||
// folder scanning.
|
||||
files, err := gfile.ScanDirFile(protoFolder, "*.proto", true)
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
if len(files) == 0 {
|
||||
mlog.Fatalf(`no proto files found in folder "%s"`, protoFolder)
|
||||
}
|
||||
dirSet := gset.NewStrSet()
|
||||
for _, file := range files {
|
||||
dirSet.Add(gfile.Dir(file))
|
||||
}
|
||||
var (
|
||||
servicePath = gfile.RealPath(".")
|
||||
goPathSrc = gfile.RealPath(gfile.Join(genv.Get("GOPATH").String(), "src"))
|
||||
)
|
||||
dirSet.Iterator(func(protoDirPath string) bool {
|
||||
parsingCommand := fmt.Sprintf(
|
||||
"protoc --gofast_out=plugins=grpc:. %s/*.proto -I%s",
|
||||
protoDirPath,
|
||||
servicePath,
|
||||
)
|
||||
if goPathSrc != "" {
|
||||
parsingCommand += " -I" + goPathSrc
|
||||
}
|
||||
mlog.Print(parsingCommand)
|
||||
if output, err := gproc.ShellExec(parsingCommand); err != nil {
|
||||
mlog.Print(output)
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
return true
|
||||
})
|
||||
// Custom replacement.
|
||||
//pbFolder := "protobuf"
|
||||
//_, _ = gfile.ScanDirFileFunc(pbFolder, "*.go", true, func(path string) string {
|
||||
// content := gfile.GetContents(path)
|
||||
// content = gstr.ReplaceByArray(content, g.SliceStr{
|
||||
// `gtime "gtime"`, `gtime "github.com/gogf/gf/v2/os/gtime"`,
|
||||
// })
|
||||
// _ = gfile.PutContents(path, content)
|
||||
// utils.GoFmt(path)
|
||||
// return path
|
||||
//})
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
409
cmd/gf/internal/cmd/cmd_gen_pbentity.go
Normal file
409
cmd/gf/internal/cmd/cmd_gen_pbentity.go
Normal file
@ -0,0 +1,409 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
const (
|
||||
cGenPbEntityConfig = `gfcli.gen.pbentity`
|
||||
cGenPbEntityBrief = `generate entity message files in protobuf3 format`
|
||||
cGenPbEntityEg = `
|
||||
gf gen pbentity
|
||||
gf gen pbentity -l "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
gf gen pbentity -p ./protocol/demos/entity -t user,user_detail,user_login
|
||||
gf gen pbentity -r user_
|
||||
`
|
||||
|
||||
cGenPbEntityAd = `
|
||||
CONFIGURATION SUPPORT
|
||||
Options are also supported by configuration file.
|
||||
It's suggested using configuration file instead of command line arguments making producing.
|
||||
The configuration node name is "gf.gen.pbentity", which also supports multiple databases, for example(config.yaml):
|
||||
gfcli:
|
||||
gen:
|
||||
- pbentity:
|
||||
link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
path: "protocol/demos/entity"
|
||||
tables: "order,products"
|
||||
package: "demos"
|
||||
- pbentity:
|
||||
link: "mysql:root:12345678@tcp(127.0.0.1:3306)/primary"
|
||||
path: "protocol/demos/entity"
|
||||
prefix: "primary_"
|
||||
tables: "user, userDetail"
|
||||
package: "demos"
|
||||
option: |
|
||||
option go_package = "protobuf/demos";
|
||||
option java_package = "protobuf/demos";
|
||||
option php_namespace = "protobuf/demos";
|
||||
`
|
||||
cGenPbEntityBriefPath = `directory path for generated files`
|
||||
cGenPbEntityBriefPackage = `package name for all entity proto files`
|
||||
cGenPbEntityBriefLink = `database configuration, the same as the ORM configuration of GoFrame`
|
||||
cGenPbEntityBriefTables = `generate models only for given tables, multiple table names separated with ','`
|
||||
cGenPbEntityBriefPrefix = `add specified prefix for all entity names and entity proto files`
|
||||
cGenPbEntityBriefRemovePrefix = `remove specified prefix of the table, multiple prefix separated with ','`
|
||||
cGenPbEntityBriefOption = `extra protobuf options`
|
||||
cGenPbEntityBriefGroup = `
|
||||
specifying the configuration group name of database for generated ORM instance,
|
||||
it's not necessary and the default value is "default"
|
||||
`
|
||||
|
||||
cGenPbEntityBriefNameCase = `
|
||||
case for message attribute names, default is "Camel":
|
||||
| Case | Example |
|
||||
|---------------- |--------------------|
|
||||
| Camel | AnyKindOfString |
|
||||
| CamelLower | anyKindOfString | default
|
||||
| Snake | any_kind_of_string |
|
||||
| SnakeScreaming | ANY_KIND_OF_STRING |
|
||||
| SnakeFirstUpper | rgb_code_md5 |
|
||||
| Kebab | any-kind-of-string |
|
||||
| KebabScreaming | ANY-KIND-OF-STRING |
|
||||
`
|
||||
|
||||
cGenPbEntityBriefJsonCase = `
|
||||
case for message json tag, cases are the same as "nameCase", default "CamelLower".
|
||||
set it to "none" to ignore json tag generating.
|
||||
`
|
||||
)
|
||||
|
||||
type (
|
||||
cGenPbEntityInput struct {
|
||||
g.Meta `name:"pbentity" config:"{cGenPbEntityConfig}" brief:"{cGenPbEntityBrief}" eg:"{cGenPbEntityEg}" ad:"{cGenPbEntityAd}"`
|
||||
Path string `name:"path" short:"p" brief:"{cGenPbEntityBriefPath}"`
|
||||
Package string `name:"package" short:"k" brief:"{cGenPbEntityBriefPackage}"`
|
||||
Link string `name:"link" short:"l" brief:"{cGenPbEntityBriefLink}"`
|
||||
Tables string `name:"tables" short:"t" brief:"{cGenPbEntityBriefTables}"`
|
||||
Prefix string `name:"prefix" short:"f" brief:"{cGenPbEntityBriefPrefix}"`
|
||||
RemovePrefix string `name:"removePrefix" short:"r" brief:"{cGenPbEntityBriefRemovePrefix}"`
|
||||
NameCase string `name:"nameCase" short:"n" brief:"{cGenPbEntityBriefNameCase}" d:"Camel"`
|
||||
JsonCase string `name:"jsonCase" short:"j" brief:"{cGenPbEntityBriefJsonCase}" d:"CamelLower"`
|
||||
Option string `name:"option" short:"o" brief:"{cGenPbEntityBriefOption}"`
|
||||
}
|
||||
cGenPbEntityOutput struct{}
|
||||
|
||||
cGenPbEntityInternalInput struct {
|
||||
cGenPbEntityInput
|
||||
TableName string // TableName specifies the table name of the table.
|
||||
NewTableName string // NewTableName specifies the prefix-stripped name of the table.
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cGenPbEntityConfig`: cGenPbEntityConfig,
|
||||
`cGenPbEntityBrief`: cGenPbEntityBrief,
|
||||
`cGenPbEntityEg`: cGenPbEntityEg,
|
||||
`cGenPbEntityAd`: cGenPbEntityAd,
|
||||
`cGenPbEntityBriefPath`: cGenPbEntityBriefPath,
|
||||
`cGenPbEntityBriefPackage`: cGenPbEntityBriefPackage,
|
||||
`cGenPbEntityBriefLink`: cGenPbEntityBriefLink,
|
||||
`cGenPbEntityBriefTables`: cGenPbEntityBriefTables,
|
||||
`cGenPbEntityBriefPrefix`: cGenPbEntityBriefPrefix,
|
||||
`cGenPbEntityBriefRemovePrefix`: cGenPbEntityBriefRemovePrefix,
|
||||
`cGenPbEntityBriefGroup`: cGenPbEntityBriefGroup,
|
||||
`cGenPbEntityBriefNameCase`: cGenPbEntityBriefNameCase,
|
||||
`cGenPbEntityBriefJsonCase`: cGenPbEntityBriefJsonCase,
|
||||
`cGenPbEntityBriefOption`: cGenPbEntityBriefOption,
|
||||
})
|
||||
}
|
||||
|
||||
func (c cGen) PbEntity(ctx context.Context, in cGenPbEntityInput) (out *cGenPbEntityOutput, err error) {
|
||||
var (
|
||||
config = g.Cfg()
|
||||
)
|
||||
if config.Available(ctx) {
|
||||
v := config.MustGet(ctx, cGenPbEntityConfig)
|
||||
if v.IsSlice() {
|
||||
for i := 0; i < len(v.Interfaces()); i++ {
|
||||
doGenPbEntityForArray(ctx, i, in)
|
||||
}
|
||||
} else {
|
||||
doGenPbEntityForArray(ctx, -1, in)
|
||||
}
|
||||
} else {
|
||||
doGenPbEntityForArray(ctx, -1, in)
|
||||
}
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
|
||||
func doGenPbEntityForArray(ctx context.Context, index int, in cGenPbEntityInput) {
|
||||
var (
|
||||
err error
|
||||
db gdb.DB
|
||||
)
|
||||
if index >= 0 {
|
||||
err = g.Cfg().MustGet(
|
||||
ctx,
|
||||
fmt.Sprintf(`%s.%d`, cGenDaoConfig, index),
|
||||
).Scan(&in)
|
||||
if err != nil {
|
||||
mlog.Fatalf(`invalid configuration of "%s": %+v`, cGenDaoConfig, err)
|
||||
}
|
||||
}
|
||||
if in.Package == "" {
|
||||
mlog.Fatal("package name should not be empty")
|
||||
}
|
||||
removePrefixArray := gstr.SplitAndTrim(in.RemovePrefix, ",")
|
||||
// It uses user passed database configuration.
|
||||
if in.Link != "" {
|
||||
var (
|
||||
tempGroup = gtime.TimestampNanoStr()
|
||||
match, _ = gregex.MatchString(`([a-z]+):(.+)`, in.Link)
|
||||
)
|
||||
if len(match) == 3 {
|
||||
gdb.AddConfigNode(tempGroup, gdb.ConfigNode{
|
||||
Type: gstr.Trim(match[1]),
|
||||
Link: gstr.Trim(match[2]),
|
||||
})
|
||||
db, _ = gdb.Instance(tempGroup)
|
||||
}
|
||||
} else {
|
||||
db = g.DB()
|
||||
}
|
||||
if db == nil {
|
||||
mlog.Fatal("database initialization failed")
|
||||
}
|
||||
|
||||
tableNames := ([]string)(nil)
|
||||
if in.Tables != "" {
|
||||
tableNames = gstr.SplitAndTrim(in.Tables, ",")
|
||||
} else {
|
||||
tableNames, err = db.Tables(context.TODO())
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables failed: \n %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, tableName := range tableNames {
|
||||
newTableName := tableName
|
||||
for _, v := range removePrefixArray {
|
||||
newTableName = gstr.TrimLeftStr(newTableName, v, 1)
|
||||
}
|
||||
generatePbEntityContentFile(ctx, db, cGenPbEntityInternalInput{
|
||||
cGenPbEntityInput: in,
|
||||
TableName: tableName,
|
||||
NewTableName: newTableName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// generatePbEntityContentFile generates the protobuf files for given table.
|
||||
func generatePbEntityContentFile(ctx context.Context, db gdb.DB, in cGenPbEntityInternalInput) {
|
||||
fieldMap, err := db.TableFields(ctx, in.TableName)
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", in.TableName, err)
|
||||
}
|
||||
// Change the `newTableName` if `Prefix` is given.
|
||||
newTableName := "Entity_" + in.Prefix + in.NewTableName
|
||||
var (
|
||||
tableNameCamelCase = gstr.CaseCamel(newTableName)
|
||||
tableNameSnakeCase = gstr.CaseSnake(newTableName)
|
||||
entityMessageDefine = generateEntityMessageDefinition(tableNameCamelCase, fieldMap, in)
|
||||
fileName = gstr.Trim(tableNameSnakeCase, "-_.")
|
||||
path = gfile.Join(in.Path, fileName+".proto")
|
||||
)
|
||||
entityContent := gstr.ReplaceByMap(getTplPbEntityContent(""), g.MapStrStr{
|
||||
"{PackageName}": in.Package,
|
||||
"{OptionContent}": in.Option,
|
||||
"{EntityMessage}": entityMessageDefine,
|
||||
})
|
||||
if err := gfile.PutContents(path, strings.TrimSpace(entityContent)); err != nil {
|
||||
mlog.Fatalf("writing content to '%s' failed: %v", path, err)
|
||||
} else {
|
||||
mlog.Print("generated:", path)
|
||||
}
|
||||
}
|
||||
|
||||
// generateEntityMessageDefinition generates and returns the message definition for specified table.
|
||||
func generateEntityMessageDefinition(entityName string, fieldMap map[string]*gdb.TableField, in cGenPbEntityInternalInput) string {
|
||||
var (
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
array = make([][]string, len(fieldMap))
|
||||
names = sortFieldKeyForPbEntity(fieldMap)
|
||||
)
|
||||
for index, name := range names {
|
||||
array[index] = generateMessageFieldForPbEntity(index+1, fieldMap[name], in)
|
||||
}
|
||||
tw := tablewriter.NewWriter(buffer)
|
||||
tw.SetBorder(false)
|
||||
tw.SetRowLine(false)
|
||||
tw.SetAutoWrapText(false)
|
||||
tw.SetColumnSeparator("")
|
||||
tw.AppendBulk(array)
|
||||
tw.Render()
|
||||
stContent := buffer.String()
|
||||
// Let's do this hack of table writer for indent!
|
||||
stContent = gstr.Replace(stContent, " #", "")
|
||||
buffer.Reset()
|
||||
buffer.WriteString(fmt.Sprintf("message %s {\n", entityName))
|
||||
buffer.WriteString(stContent)
|
||||
buffer.WriteString("}")
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// generateMessageFieldForPbEntity generates and returns the message definition for specified field.
|
||||
func generateMessageFieldForPbEntity(index int, field *gdb.TableField, in cGenPbEntityInternalInput) []string {
|
||||
var (
|
||||
typeName string
|
||||
comment string
|
||||
jsonTagStr string
|
||||
)
|
||||
t, _ := gregex.ReplaceString(`\(.+\)`, "", field.Type)
|
||||
t = gstr.Split(gstr.Trim(t), " ")[0]
|
||||
t = gstr.ToLower(t)
|
||||
switch t {
|
||||
case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob":
|
||||
typeName = "bytes"
|
||||
|
||||
case "bit", "int", "tinyint", "small_int", "smallint", "medium_int", "mediumint", "serial":
|
||||
if gstr.ContainsI(field.Type, "unsigned") {
|
||||
typeName = "uint32"
|
||||
} else {
|
||||
typeName = "int32"
|
||||
}
|
||||
|
||||
case "int8", "big_int", "bigint", "bigserial":
|
||||
if gstr.ContainsI(field.Type, "unsigned") {
|
||||
typeName = "uint64"
|
||||
} else {
|
||||
typeName = "int64"
|
||||
}
|
||||
|
||||
case "real":
|
||||
typeName = "float"
|
||||
|
||||
case "float", "double", "decimal", "smallmoney":
|
||||
typeName = "double"
|
||||
|
||||
case "bool":
|
||||
typeName = "bool"
|
||||
|
||||
case "datetime", "timestamp", "date", "time":
|
||||
typeName = "int64"
|
||||
|
||||
default:
|
||||
// Auto detecting type.
|
||||
switch {
|
||||
case strings.Contains(t, "int"):
|
||||
typeName = "int"
|
||||
case strings.Contains(t, "text") || strings.Contains(t, "char"):
|
||||
typeName = "string"
|
||||
case strings.Contains(t, "float") || strings.Contains(t, "double"):
|
||||
typeName = "double"
|
||||
case strings.Contains(t, "bool"):
|
||||
typeName = "bool"
|
||||
case strings.Contains(t, "binary") || strings.Contains(t, "blob"):
|
||||
typeName = "bytes"
|
||||
case strings.Contains(t, "date") || strings.Contains(t, "time"):
|
||||
typeName = "int64"
|
||||
default:
|
||||
typeName = "string"
|
||||
}
|
||||
}
|
||||
comment = gstr.ReplaceByArray(field.Comment, g.SliceStr{
|
||||
"\n", " ",
|
||||
"\r", " ",
|
||||
})
|
||||
comment = gstr.Trim(comment)
|
||||
comment = gstr.Replace(comment, `\n`, " ")
|
||||
comment, _ = gregex.ReplaceString(`\s{2,}`, ` `, comment)
|
||||
if jsonTagName := formatCase(field.Name, in.JsonCase); jsonTagName != "" {
|
||||
jsonTagStr = fmt.Sprintf(`[(gogoproto.jsontag) = "%s"]`, jsonTagName)
|
||||
// beautiful indent.
|
||||
if index < 10 {
|
||||
// 3 spaces
|
||||
jsonTagStr = " " + jsonTagStr
|
||||
} else if index < 100 {
|
||||
// 2 spaces
|
||||
jsonTagStr = " " + jsonTagStr
|
||||
} else {
|
||||
// 1 spaces
|
||||
jsonTagStr = " " + jsonTagStr
|
||||
}
|
||||
}
|
||||
return []string{
|
||||
" #" + typeName,
|
||||
" #" + formatCase(field.Name, in.NameCase),
|
||||
" #= " + gconv.String(index) + jsonTagStr + ";",
|
||||
" #" + fmt.Sprintf(`// %s`, comment),
|
||||
}
|
||||
}
|
||||
|
||||
func getTplPbEntityContent(tplEntityPath string) string {
|
||||
if tplEntityPath != "" {
|
||||
return gfile.GetContents(tplEntityPath)
|
||||
}
|
||||
return consts.TemplatePbEntityMessageContent
|
||||
}
|
||||
|
||||
// formatCase call gstr.Case* function to convert the s to specified case.
|
||||
func formatCase(str, caseStr string) string {
|
||||
switch gstr.ToLower(caseStr) {
|
||||
case gstr.ToLower("Camel"):
|
||||
return gstr.CaseCamel(str)
|
||||
|
||||
case gstr.ToLower("CamelLower"):
|
||||
return gstr.CaseCamelLower(str)
|
||||
|
||||
case gstr.ToLower("Kebab"):
|
||||
return gstr.CaseKebab(str)
|
||||
|
||||
case gstr.ToLower("KebabScreaming"):
|
||||
return gstr.CaseKebabScreaming(str)
|
||||
|
||||
case gstr.ToLower("Snake"):
|
||||
return gstr.CaseSnake(str)
|
||||
|
||||
case gstr.ToLower("SnakeFirstUpper"):
|
||||
return gstr.CaseSnakeFirstUpper(str)
|
||||
|
||||
case gstr.ToLower("SnakeScreaming"):
|
||||
return gstr.CaseSnakeScreaming(str)
|
||||
|
||||
case "none":
|
||||
return ""
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func sortFieldKeyForPbEntity(fieldMap map[string]*gdb.TableField) []string {
|
||||
names := make(map[int]string)
|
||||
for _, field := range fieldMap {
|
||||
names[field.Index] = field.Name
|
||||
}
|
||||
var (
|
||||
result = make([]string, len(names))
|
||||
i = 0
|
||||
j = 0
|
||||
)
|
||||
for {
|
||||
if len(names) == 0 {
|
||||
break
|
||||
}
|
||||
if val, ok := names[i]; ok {
|
||||
result[j] = val
|
||||
j++
|
||||
delete(names, i)
|
||||
}
|
||||
i++
|
||||
}
|
||||
return result
|
||||
}
|
||||
309
cmd/gf/internal/cmd/cmd_gen_service.go
Normal file
309
cmd/gf/internal/cmd/cmd_gen_service.go
Normal file
@ -0,0 +1,309 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type (
|
||||
cGenServiceInput struct {
|
||||
g.Meta `name:"service" config:"gfcli.gen.service" brief:"parse struct and associated functions from packages to generate service go file"`
|
||||
SrcFolder string `short:"s" name:"srcFolder" brief:"source folder path to be parsed. default: internal/logic" d:"internal/logic"`
|
||||
DstFolder string `short:"d" name:"dstFolder" brief:"destination folder path storing automatically generated go files. default: internal/service" d:"internal/service"`
|
||||
WatchFile string `short:"w" name:"watchFile" brief:"used in file watcher, it generates service go files only if given file is under srcFolder"`
|
||||
StPattern string `short:"a" name:"stPattern" brief:"regular expression matching struct name for generating service. default: s(\\w+)" d:"s(\\w+)"`
|
||||
Packages string `short:"p" name:"packages" brief:"produce go files only for given source packages, multiple packages joined with char ','"`
|
||||
ImportPrefix string `short:"i" name:"importPrefix" brief:"custom import prefix to calculate import path for generated go files"`
|
||||
OverWrite bool `short:"o" name:"overwrite" brief:"overwrite files that already exist in generating folder. default: true" d:"true" orphan:"true"`
|
||||
}
|
||||
cGenServiceOutput struct{}
|
||||
)
|
||||
|
||||
const (
|
||||
genServiceFileLockSeconds = 10
|
||||
)
|
||||
|
||||
func (c cGen) Service(ctx context.Context, in cGenServiceInput) (out *cGenServiceOutput, err error) {
|
||||
// File lock to avoid multiple processes.
|
||||
var (
|
||||
flockFilePath = gfile.Temp("gf.cli.gen.service.lock")
|
||||
flockContent = gfile.GetContents(flockFilePath)
|
||||
)
|
||||
if flockContent != "" {
|
||||
if gtime.Timestamp()-gconv.Int64(flockContent) < genServiceFileLockSeconds {
|
||||
// If another "gen service" process is running, it just exits.
|
||||
mlog.Debug(`another "gen service" process is running, exit`)
|
||||
return
|
||||
}
|
||||
}
|
||||
defer gfile.Remove(flockFilePath)
|
||||
_ = gfile.PutContents(flockFilePath, gtime.TimestampStr())
|
||||
|
||||
in.SrcFolder = gstr.TrimRight(in.SrcFolder, `\/`)
|
||||
in.SrcFolder = gstr.Replace(in.SrcFolder, "\\", "/")
|
||||
in.WatchFile = gstr.TrimRight(in.WatchFile, `\/`)
|
||||
in.WatchFile = gstr.Replace(in.WatchFile, "\\", "/")
|
||||
|
||||
// Watch file handling.
|
||||
if in.WatchFile != "" {
|
||||
// It works only if given WatchFile is in SrcFolder.
|
||||
var (
|
||||
watchFileDir = gfile.Dir(in.WatchFile)
|
||||
srcFolderDir = gfile.Dir(watchFileDir)
|
||||
)
|
||||
mlog.Debug("watchFileDir:", watchFileDir)
|
||||
mlog.Debug("logicFolderDir:", srcFolderDir)
|
||||
if !gstr.HasSuffix(srcFolderDir, in.SrcFolder) {
|
||||
mlog.Printf(`ignore watch file "%s", not in source path "%s"`, in.WatchFile, in.SrcFolder)
|
||||
return
|
||||
}
|
||||
var newWorkingDir = gfile.Dir(gfile.Dir(srcFolderDir))
|
||||
if err = gfile.Chdir(newWorkingDir); err != nil {
|
||||
mlog.Fatalf(`%+v`, err)
|
||||
}
|
||||
mlog.Debug("Chdir:", newWorkingDir)
|
||||
_ = gfile.Remove(flockFilePath)
|
||||
var command = fmt.Sprintf(
|
||||
`%s gen service -packages=%s`,
|
||||
gfile.SelfName(), gfile.Basename(watchFileDir),
|
||||
)
|
||||
err = gproc.ShellRun(command)
|
||||
return
|
||||
}
|
||||
|
||||
if !gfile.Exists(in.SrcFolder) {
|
||||
mlog.Fatalf(`source folder path "%s" does not exist`, in.SrcFolder)
|
||||
}
|
||||
|
||||
if in.ImportPrefix == "" {
|
||||
if !gfile.Exists("go.mod") {
|
||||
mlog.Fatal("ImportPrefix is empty and go.mod does not exist in current working directory")
|
||||
}
|
||||
var (
|
||||
goModContent = gfile.GetContents("go.mod")
|
||||
match, _ = gregex.MatchString(`^module\s+(.+)\s*`, goModContent)
|
||||
)
|
||||
if len(match) > 1 {
|
||||
in.ImportPrefix = fmt.Sprintf(`%s/%s`, gstr.Trim(match[1]), gstr.Replace(in.SrcFolder, `\`, `/`))
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
isDirty bool
|
||||
files []string
|
||||
fileContent string
|
||||
matches [][]string
|
||||
importSrcPackages []string
|
||||
inputPackages = gstr.SplitAndTrim(in.Packages, ",")
|
||||
dstPackageName = gstr.ToLower(gfile.Basename(in.DstFolder))
|
||||
)
|
||||
srcFolders, err := gfile.ScanDir(in.SrcFolder, "*", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, srcFolder := range srcFolders {
|
||||
if !gfile.IsDir(srcFolder) {
|
||||
continue
|
||||
}
|
||||
if files, err = gfile.ScanDir(srcFolder, "*.go", false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(files) == 0 {
|
||||
continue
|
||||
}
|
||||
var (
|
||||
// StructName => FunctionDefinitions
|
||||
srcPkgInterfaceMap = make(map[string]*garray.StrArray)
|
||||
srcPkgInterfaceFuncArray *garray.StrArray
|
||||
ok bool
|
||||
)
|
||||
for _, file := range files {
|
||||
fileContent = gfile.GetContents(file)
|
||||
matches, err = gregex.MatchAllString(`func \(\w+ (.+?)\) ([\s\S]+?) {`, fileContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, match := range matches {
|
||||
var (
|
||||
structMatch []string
|
||||
structName = gstr.Trim(match[1], "*")
|
||||
functionHead = gstr.Trim(gstr.Replace(match[2], "\n", ""))
|
||||
)
|
||||
// Xxx( ctx context.Context, req *v1.XxxReq,) -> Xxx(ctx context.Context, req *v1.XxxReq)
|
||||
functionHead = gstr.Replace(functionHead, `,)`, `)`)
|
||||
functionHead, _ = gregex.ReplaceString(`\(\s+`, `(`, functionHead)
|
||||
functionHead, _ = gregex.ReplaceString(`\s{2,}`, ` `, functionHead)
|
||||
if !gstr.IsLetterUpper(functionHead[0]) {
|
||||
continue
|
||||
}
|
||||
if structMatch, err = gregex.MatchString(in.StPattern, structName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(structMatch) < 1 {
|
||||
continue
|
||||
}
|
||||
structName = gstr.CaseCamel(structMatch[1])
|
||||
if srcPkgInterfaceFuncArray, ok = srcPkgInterfaceMap[structName]; !ok {
|
||||
srcPkgInterfaceMap[structName] = garray.NewStrArray()
|
||||
srcPkgInterfaceFuncArray = srcPkgInterfaceMap[structName]
|
||||
}
|
||||
// Remove package name calls of `dstPackageName` in produced codes.
|
||||
functionHead, _ = gregex.ReplaceString(fmt.Sprintf(`\*{0,1}%s\.`, dstPackageName), ``, functionHead)
|
||||
srcPkgInterfaceFuncArray.Append(functionHead)
|
||||
}
|
||||
}
|
||||
importSrcPackages = append(
|
||||
importSrcPackages,
|
||||
fmt.Sprintf(`%s/%s`, in.ImportPrefix, gfile.Basename(srcFolder)),
|
||||
)
|
||||
// Ignore source packages if input packages given.
|
||||
if len(inputPackages) > 0 && !gstr.InArray(inputPackages, gfile.Basename(srcFolder)) {
|
||||
mlog.Debugf(
|
||||
`ignore source package "%s" as it is not in desired packages: %+v`,
|
||||
gfile.Basename(srcFolder), inputPackages,
|
||||
)
|
||||
continue
|
||||
}
|
||||
// Generating go files for service.
|
||||
if ok, err = c.generateServiceFiles(in, srcPkgInterfaceMap, dstPackageName); err != nil {
|
||||
return
|
||||
}
|
||||
if ok {
|
||||
isDirty = true
|
||||
}
|
||||
}
|
||||
|
||||
if isDirty {
|
||||
// Generate initialization go file.
|
||||
if len(importSrcPackages) > 0 {
|
||||
if err = c.generateInitializationFile(in, importSrcPackages); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Go imports updating.
|
||||
mlog.Printf(`goimports go files in "%s", it may take seconds...`, in.DstFolder)
|
||||
utils.GoImports(in.DstFolder)
|
||||
|
||||
// Replica v1 to v2 for GoFrame.
|
||||
if err = c.replaceGeneratedServiceContentGFV2(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mlog.Printf(`gofmt go files in "%s"`, in.DstFolder)
|
||||
utils.GoFmt(in.DstFolder)
|
||||
}
|
||||
|
||||
mlog.Print(`done!`)
|
||||
return
|
||||
}
|
||||
|
||||
func (c cGen) generateServiceFiles(
|
||||
in cGenServiceInput, srcPkgInterfaceMap map[string]*garray.StrArray, dstPackageName string,
|
||||
) (ok bool, err error) {
|
||||
for structName, funcArray := range srcPkgInterfaceMap {
|
||||
var (
|
||||
filePath = gfile.Join(in.DstFolder, gstr.ToLower(structName)+".go")
|
||||
generatedContent = gstr.ReplaceByMap(consts.TemplateGenServiceContent, g.MapStrStr{
|
||||
"{StructName}": structName,
|
||||
"{PackageName}": dstPackageName,
|
||||
"{FuncDefinition}": funcArray.Join("\n\t"),
|
||||
})
|
||||
)
|
||||
if gfile.Exists(filePath) {
|
||||
if !in.OverWrite {
|
||||
mlog.Printf(`not overwrite, ignore generating service go file: %s`, filePath)
|
||||
continue
|
||||
}
|
||||
if !c.isToGenerateServiceGoFile(filePath, funcArray) {
|
||||
mlog.Printf(`not dirty, ignore generating service go file: %s`, filePath)
|
||||
continue
|
||||
}
|
||||
}
|
||||
ok = true
|
||||
mlog.Printf(`generating service go file: %s`, filePath)
|
||||
if err = gfile.PutContents(filePath, generatedContent); err != nil {
|
||||
return ok, err
|
||||
}
|
||||
}
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
// isToGenerateServiceGoFile checks and returns whether the service content dirty.
|
||||
func (c cGen) isToGenerateServiceGoFile(filePath string, funcArray *garray.StrArray) bool {
|
||||
if !utils.IsFileDoNotEdit(filePath) {
|
||||
mlog.Debugf(`ignore file as it is manually maintained: %s`, filePath)
|
||||
return false
|
||||
}
|
||||
var (
|
||||
fileContent = gfile.GetContents(filePath)
|
||||
generatedFuncArray = garray.NewSortedStrArrayFrom(funcArray.Slice())
|
||||
contentFuncArray = garray.NewSortedStrArray()
|
||||
)
|
||||
if fileContent == "" {
|
||||
return true
|
||||
}
|
||||
match, _ := gregex.MatchString(`interface\s+{([\s\S]+?)}`, fileContent)
|
||||
if len(match) != 2 {
|
||||
return false
|
||||
}
|
||||
contentFuncArray.Append(gstr.SplitAndTrim(match[1], "\n")...)
|
||||
if generatedFuncArray.Len() != contentFuncArray.Len() {
|
||||
return true
|
||||
}
|
||||
for i := 0; i < generatedFuncArray.Len(); i++ {
|
||||
if generatedFuncArray.At(i) != contentFuncArray.At(i) {
|
||||
mlog.Debugf(`dirty, %s != %s`, generatedFuncArray.At(i), contentFuncArray.At(i))
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c cGen) generateInitializationFile(in cGenServiceInput, importSrcPackages []string) (err error) {
|
||||
var (
|
||||
srcPackageName = gstr.ToLower(gfile.Basename(in.SrcFolder))
|
||||
srcFilePath = gfile.Join(in.SrcFolder, srcPackageName+".go")
|
||||
srcImports string
|
||||
generatedContent string
|
||||
)
|
||||
if !utils.IsFileDoNotEdit(srcFilePath) {
|
||||
mlog.Debugf(`ignore file as it is manually maintained: %s`, srcFilePath)
|
||||
return nil
|
||||
}
|
||||
for _, importSrcPackage := range importSrcPackages {
|
||||
srcImports += fmt.Sprintf(`%s_ "%s"%s`, "\t", importSrcPackage, "\n")
|
||||
}
|
||||
generatedContent = gstr.ReplaceByMap(consts.TemplateGenServiceLogicContent, g.MapStrStr{
|
||||
"{PackageName}": srcPackageName,
|
||||
"{Imports}": srcImports,
|
||||
})
|
||||
mlog.Printf(`generating init go file: %s`, srcFilePath)
|
||||
if err = gfile.PutContents(srcFilePath, generatedContent); err != nil {
|
||||
return err
|
||||
}
|
||||
utils.GoFmt(srcFilePath)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c cGen) replaceGeneratedServiceContentGFV2(in cGenServiceInput) (err error) {
|
||||
return gfile.ReplaceDirFunc(func(path, content string) string {
|
||||
if gstr.Contains(content, `"github.com/gogf/gf`) && !gstr.Contains(content, `"github.com/gogf/gf/v2`) {
|
||||
content = gstr.Replace(content, `"github.com/gogf/gf"`, `"github.com/gogf/gf/v2"`)
|
||||
content = gstr.Replace(content, `"github.com/gogf/gf/`, `"github.com/gogf/gf/v2/`)
|
||||
return content
|
||||
}
|
||||
return content
|
||||
}, in.DstFolder, "*.go", false)
|
||||
}
|
||||
101
cmd/gf/internal/cmd/cmd_init.go
Normal file
101
cmd/gf/internal/cmd/cmd_init.go
Normal file
@ -0,0 +1,101 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/allyes"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gres"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
|
||||
var (
|
||||
Init = cInit{}
|
||||
)
|
||||
|
||||
type cInit struct {
|
||||
g.Meta `name:"init" brief:"{cInitBrief}" eg:"{cInitEg}"`
|
||||
}
|
||||
|
||||
const (
|
||||
cInitRepoPrefix = `github.com/gogf/`
|
||||
cInitMonoRepo = `template-mono`
|
||||
cInitSingleRepo = `template-single`
|
||||
cInitBrief = `create and initialize an empty GoFrame project`
|
||||
cInitEg = `
|
||||
gf init my-project
|
||||
gf init my-mono-repo -m
|
||||
`
|
||||
cInitNameBrief = `
|
||||
name for the project. It will create a folder with NAME in current directory.
|
||||
The NAME will also be the module name for the project.
|
||||
`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cInitBrief`: cInitBrief,
|
||||
`cInitEg`: cInitEg,
|
||||
`cInitNameBrief`: cInitNameBrief,
|
||||
})
|
||||
}
|
||||
|
||||
type cInitInput struct {
|
||||
g.Meta `name:"init"`
|
||||
Name string `name:"NAME" arg:"true" v:"required" brief:"{cInitNameBrief}"`
|
||||
Mono bool `name:"mono" short:"m" brief:"initialize a mono-repo instead a single-repo" orphan:"true"`
|
||||
}
|
||||
type cInitOutput struct{}
|
||||
|
||||
func (c cInit) Index(ctx context.Context, in cInitInput) (out *cInitOutput, err error) {
|
||||
if !gfile.IsEmpty(in.Name) && !allyes.Check() {
|
||||
s := gcmd.Scanf(`the folder "%s" is not empty, files might be overwrote, continue? [y/n]: `, in.Name)
|
||||
if strings.EqualFold(s, "n") {
|
||||
return
|
||||
}
|
||||
}
|
||||
mlog.Print("initializing...")
|
||||
|
||||
// Create project folder and files.
|
||||
var (
|
||||
templateRepoName string
|
||||
)
|
||||
if in.Mono {
|
||||
templateRepoName = cInitMonoRepo
|
||||
} else {
|
||||
templateRepoName = cInitSingleRepo
|
||||
}
|
||||
err = gres.Export(templateRepoName, in.Name, gres.ExportOption{
|
||||
RemovePrefix: templateRepoName,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Replace template name to project name.
|
||||
err = gfile.ReplaceDir(
|
||||
cInitRepoPrefix+templateRepoName,
|
||||
gfile.Basename(gfile.RealPath(in.Name)),
|
||||
in.Name,
|
||||
"*",
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
mlog.Print("initialization done! ")
|
||||
if !in.Mono {
|
||||
enjoyCommand := `gf run main.go`
|
||||
if in.Name != "." {
|
||||
enjoyCommand = fmt.Sprintf(`cd %s && %s`, in.Name, enjoyCommand)
|
||||
}
|
||||
mlog.Printf(`you can now run "%s" to start your journey, enjoy!`, enjoyCommand)
|
||||
}
|
||||
return
|
||||
}
|
||||
26
cmd/gf/internal/cmd/cmd_install.go
Normal file
26
cmd/gf/internal/cmd/cmd_install.go
Normal file
@ -0,0 +1,26 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/service"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
var (
|
||||
Install = cInstall{}
|
||||
)
|
||||
|
||||
type cInstall struct {
|
||||
g.Meta `name:"install" brief:"install gf binary to system (might need root/admin permission)"`
|
||||
}
|
||||
|
||||
type cInstallInput struct {
|
||||
g.Meta `name:"install"`
|
||||
}
|
||||
type cInstallOutput struct{}
|
||||
|
||||
func (c cInstall) Index(ctx context.Context, in cInstallInput) (out *cInstallOutput, err error) {
|
||||
err = service.Install.Run(ctx)
|
||||
return
|
||||
}
|
||||
89
cmd/gf/internal/cmd/cmd_pack.go
Normal file
89
cmd/gf/internal/cmd/cmd_pack.go
Normal file
@ -0,0 +1,89 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/allyes"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gres"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
|
||||
var (
|
||||
Pack = cPack{}
|
||||
)
|
||||
|
||||
type cPack struct {
|
||||
g.Meta `name:"pack" usage:"{cPackUsage}" brief:"{cPackBrief}" eg:"{cPackEg}"`
|
||||
}
|
||||
|
||||
const (
|
||||
cPackUsage = `gf pack SRC DST`
|
||||
cPackBrief = `packing any file/directory to a resource file, or a go file`
|
||||
cPackEg = `
|
||||
gf pack public data.bin
|
||||
gf pack public,template data.bin
|
||||
gf pack public,template packed/data.go
|
||||
gf pack public,template,config packed/data.go
|
||||
gf pack public,template,config packed/data.go -n=packed -p=/var/www/my-app
|
||||
gf pack /var/www/public packed/data.go -n=packed
|
||||
`
|
||||
cPackSrcBrief = `source path for packing, which can be multiple source paths.`
|
||||
cPackDstBrief = `
|
||||
destination file path for packed file. if extension of the filename is ".go" and "-n" option is given,
|
||||
it enables packing SRC to go file, or else it packs SRC into a binary file.
|
||||
`
|
||||
cPackNameBrief = `package name for output go file, it's set as its directory name if no name passed`
|
||||
cPackPrefixBrief = `prefix for each file packed into the resource file`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cPackUsage`: cPackUsage,
|
||||
`cPackBrief`: cPackBrief,
|
||||
`cPackEg`: cPackEg,
|
||||
`cPackSrcBrief`: cPackSrcBrief,
|
||||
`cPackDstBrief`: cPackDstBrief,
|
||||
`cPackNameBrief`: cPackNameBrief,
|
||||
`cPackPrefixBrief`: cPackPrefixBrief,
|
||||
})
|
||||
}
|
||||
|
||||
type cPackInput struct {
|
||||
g.Meta `name:"pack"`
|
||||
Src string `name:"SRC" arg:"true" v:"required" brief:"{cPackSrcBrief}"`
|
||||
Dst string `name:"DST" arg:"true" v:"required" brief:"{cPackDstBrief}"`
|
||||
Name string `name:"name" short:"n" brief:"{cPackNameBrief}"`
|
||||
Prefix string `name:"prefix" short:"p" brief:"{cPackPrefixBrief}"`
|
||||
}
|
||||
type cPackOutput struct{}
|
||||
|
||||
func (c cPack) Index(ctx context.Context, in cPackInput) (out *cPackOutput, err error) {
|
||||
if gfile.Exists(in.Dst) && gfile.IsDir(in.Dst) {
|
||||
mlog.Fatalf("DST path '%s' cannot be a directory", in.Dst)
|
||||
}
|
||||
if !gfile.IsEmpty(in.Dst) && !allyes.Check() {
|
||||
s := gcmd.Scanf("path '%s' is not empty, files might be overwrote, continue? [y/n]: ", in.Dst)
|
||||
if strings.EqualFold(s, "n") {
|
||||
return
|
||||
}
|
||||
}
|
||||
if in.Name == "" && gfile.ExtName(in.Dst) == "go" {
|
||||
in.Name = gfile.Basename(gfile.Dir(in.Dst))
|
||||
}
|
||||
if in.Name != "" {
|
||||
if err = gres.PackToGoFile(in.Src, in.Dst, in.Name, in.Prefix); err != nil {
|
||||
mlog.Fatalf("pack failed: %v", err)
|
||||
}
|
||||
} else {
|
||||
if err = gres.PackToFile(in.Src, in.Dst, in.Prefix); err != nil {
|
||||
mlog.Fatalf("pack failed: %v", err)
|
||||
}
|
||||
}
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
162
cmd/gf/internal/cmd/cmd_run.go
Normal file
162
cmd/gf/internal/cmd/cmd_run.go
Normal file
@ -0,0 +1,162 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/container/gtype"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gfsnotify"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/os/gtimer"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
|
||||
var (
|
||||
Run = cRun{}
|
||||
)
|
||||
|
||||
type cRun struct {
|
||||
g.Meta `name:"run" usage:"{cRunUsage}" brief:"{cRunBrief}" eg:"{cRunEg}" dc:"{cRunDc}"`
|
||||
}
|
||||
|
||||
type cRunApp struct {
|
||||
File string // Go run file name.
|
||||
Path string // Directory storing built binary.
|
||||
Options string // Extra "go run" options.
|
||||
Args string // Custom arguments.
|
||||
}
|
||||
|
||||
const (
|
||||
cRunUsage = `gf run FILE [OPTION]`
|
||||
cRunBrief = `running go codes with hot-compiled-like feature`
|
||||
cRunEg = `
|
||||
gf run main.go
|
||||
gf run main.go --args "server -p 8080"
|
||||
gf run main.go -mod=vendor
|
||||
`
|
||||
cRunDc = `
|
||||
The "run" command is used for running go codes with hot-compiled-like feature,
|
||||
which compiles and runs the go codes asynchronously when codes change.
|
||||
`
|
||||
cRunFileBrief = `building file path.`
|
||||
cRunPathBrief = `output directory path for built binary file. it's "manifest/output" in default`
|
||||
cRunExtraBrief = `the same options as "go run"/"go build" except some options as follows defined`
|
||||
)
|
||||
|
||||
var (
|
||||
process *gproc.Process
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cRunUsage`: cRunUsage,
|
||||
`cRunBrief`: cRunBrief,
|
||||
`cRunEg`: cRunEg,
|
||||
`cRunDc`: cRunDc,
|
||||
`cRunFileBrief`: cRunFileBrief,
|
||||
`cRunPathBrief`: cRunPathBrief,
|
||||
`cRunExtraBrief`: cRunExtraBrief,
|
||||
})
|
||||
}
|
||||
|
||||
type (
|
||||
cRunInput struct {
|
||||
g.Meta `name:"run"`
|
||||
File string `name:"FILE" arg:"true" brief:"{cRunFileBrief}" v:"required"`
|
||||
Path string `name:"path" short:"p" brief:"{cRunPathBrief}" d:"./"`
|
||||
Extra string `name:"extra" short:"e" brief:"{cRunExtraBrief}"`
|
||||
}
|
||||
cRunOutput struct{}
|
||||
)
|
||||
|
||||
func (c cRun) Index(ctx context.Context, in cRunInput) (out *cRunOutput, err error) {
|
||||
// Necessary check.
|
||||
if gproc.SearchBinary("go") == "" {
|
||||
mlog.Fatalf(`command "go" not found in your environment, please install golang first to proceed this command`)
|
||||
}
|
||||
|
||||
app := &cRunApp{
|
||||
File: in.File,
|
||||
Path: in.Path,
|
||||
Options: in.Extra,
|
||||
}
|
||||
dirty := gtype.NewBool()
|
||||
_, err = gfsnotify.Add(gfile.RealPath("."), func(event *gfsnotify.Event) {
|
||||
if gfile.ExtName(event.Path) != "go" {
|
||||
return
|
||||
}
|
||||
// Variable `dirty` is used for running the changes only one in one second.
|
||||
if !dirty.Cas(false, true) {
|
||||
return
|
||||
}
|
||||
// With some delay in case of multiple code changes in very short interval.
|
||||
gtimer.SetTimeout(ctx, 1500*gtime.MS, func(ctx context.Context) {
|
||||
defer dirty.Set(false)
|
||||
mlog.Printf(`go file changes: %s`, event.String())
|
||||
app.Run()
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
go app.Run()
|
||||
select {}
|
||||
}
|
||||
|
||||
func (app *cRunApp) Run() {
|
||||
// Rebuild and run the codes.
|
||||
renamePath := ""
|
||||
mlog.Printf("build: %s", app.File)
|
||||
outputPath := gfile.Join(app.Path, gfile.Name(app.File))
|
||||
if runtime.GOOS == "windows" {
|
||||
outputPath += ".exe"
|
||||
if gfile.Exists(outputPath) {
|
||||
renamePath = outputPath + "~"
|
||||
if err := gfile.Rename(outputPath, renamePath); err != nil {
|
||||
mlog.Print(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// In case of `pipe: too many open files` error.
|
||||
// Build the app.
|
||||
buildCommand := fmt.Sprintf(
|
||||
`go build -o %s %s %s`,
|
||||
outputPath,
|
||||
app.Options,
|
||||
app.File,
|
||||
)
|
||||
mlog.Print(buildCommand)
|
||||
result, err := gproc.ShellExec(buildCommand)
|
||||
if err != nil {
|
||||
mlog.Printf("build error: \n%s%s", result, err.Error())
|
||||
return
|
||||
}
|
||||
// Kill the old process if build successfully.
|
||||
if process != nil {
|
||||
if err := process.Kill(); err != nil {
|
||||
mlog.Debugf("kill process error: %s", err.Error())
|
||||
//return
|
||||
}
|
||||
}
|
||||
// Run the binary file.
|
||||
runCommand := fmt.Sprintf(`%s %s`, outputPath, app.Args)
|
||||
mlog.Print(runCommand)
|
||||
if runtime.GOOS == "windows" {
|
||||
// Special handling for windows platform.
|
||||
// DO NOT USE "cmd /c" command.
|
||||
process = gproc.NewProcess(outputPath, gstr.SplitAndTrim(" ", app.Args))
|
||||
} else {
|
||||
process = gproc.NewProcessCmd(outputPath, gstr.SplitAndTrim(" ", app.Args))
|
||||
}
|
||||
if pid, err := process.Start(); err != nil {
|
||||
mlog.Printf("build running error: %s", err.Error())
|
||||
} else {
|
||||
mlog.Printf("build running pid: %d", pid)
|
||||
}
|
||||
}
|
||||
167
cmd/gf/internal/cmd/cmd_tpl.go
Normal file
167
cmd/gf/internal/cmd/cmd_tpl.go
Normal file
@ -0,0 +1,167 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
"github.com/gogf/gf/v2/util/gutil"
|
||||
)
|
||||
|
||||
var (
|
||||
Tpl = cTpl{}
|
||||
)
|
||||
|
||||
type cTpl struct {
|
||||
g.Meta `name:"tpl" brief:"{cTplBrief}" dc:"{cTplDc}"`
|
||||
}
|
||||
|
||||
const (
|
||||
cTplBrief = `template parsing and building commands`
|
||||
cTplDc = `
|
||||
The "tpl" command is used for template parsing and building purpose.
|
||||
It can parse either template file or folder with multiple types of values support,
|
||||
like json/xml/yaml/toml/ini.
|
||||
`
|
||||
cTplParseBrief = `parse either template file or folder with multiple types of values`
|
||||
cTplParseEg = `
|
||||
gf tpl parse -p ./template -v values.json -r
|
||||
gf tpl parse -p ./template -v values.json -n *.tpl -r
|
||||
gf tpl parse -p ./template -v values.json -d '${,}}' -r
|
||||
gf tpl parse -p ./template -v values.json -o ./template.parsed
|
||||
`
|
||||
cTplSupportValuesFilePattern = `*.json,*.xml,*.yaml,*.yml,*.toml,*.ini`
|
||||
)
|
||||
|
||||
type (
|
||||
cTplParseInput struct {
|
||||
g.Meta `name:"parse" brief:"{cTplParseBrief}" eg:"{cTplParseEg}"`
|
||||
Path string `name:"path" short:"p" brief:"template file or folder path" v:"required"`
|
||||
Pattern string `name:"pattern" short:"n" brief:"template file pattern when path is a folder, default is:*" d:"*"`
|
||||
Recursive bool `name:"recursive" short:"c" brief:"recursively parsing files if path is folder, default is:true" d:"true"`
|
||||
Values string `name:"values" short:"v" brief:"template values file/folder, support file types like: json/xml/yaml/toml/ini" v:"required"`
|
||||
Output string `name:"output" short:"o" brief:"output file/folder path"`
|
||||
Delimiters string `name:"delimiters" short:"d" brief:"delimiters for template content parsing, default is:{{,}}" d:"{{,}}"`
|
||||
Replace bool `name:"replace" short:"r" brief:"replace original files" orphan:"true"`
|
||||
}
|
||||
cTplParseOutput struct{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cTplBrief`: cTplBrief,
|
||||
`cTplDc`: cTplDc,
|
||||
`cTplParseEg`: cTplParseEg,
|
||||
`cTplParseBrief`: cTplParseBrief,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *cTpl) Parse(ctx context.Context, in cTplParseInput) (out *cTplParseOutput, err error) {
|
||||
if in.Output == "" && in.Replace == false {
|
||||
return nil, gerror.New(`parameter output and replace should not be both empty`)
|
||||
}
|
||||
delimiters := gstr.SplitAndTrim(in.Delimiters, ",")
|
||||
mlog.Debugf("delimiters input:%s, parsed:%#v", in.Delimiters, delimiters)
|
||||
if len(delimiters) != 2 {
|
||||
return nil, gerror.Newf(`invalid delimiters: %s`, in.Delimiters)
|
||||
}
|
||||
g.View().SetDelimiters(delimiters[0], delimiters[1])
|
||||
valuesMap, err := c.loadValues(ctx, in.Values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(valuesMap) == 0 {
|
||||
return nil, gerror.Newf(`empty values loaded from values file/folder "%s"`, in.Values)
|
||||
}
|
||||
err = c.parsePath(ctx, valuesMap, in)
|
||||
if err == nil {
|
||||
mlog.Print("done!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *cTpl) parsePath(ctx context.Context, values g.Map, in cTplParseInput) (err error) {
|
||||
if !gfile.Exists(in.Path) {
|
||||
return gerror.Newf(`path "%s" does not exist`, in.Path)
|
||||
}
|
||||
var (
|
||||
path string
|
||||
files []string
|
||||
relativePath string
|
||||
outputPath string
|
||||
)
|
||||
path = gfile.RealPath(in.Path)
|
||||
if gfile.IsDir(path) {
|
||||
files, err = gfile.ScanDirFile(path, in.Pattern, in.Recursive)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, file := range files {
|
||||
relativePath = gstr.Replace(file, path, "")
|
||||
if in.Output != "" {
|
||||
outputPath = gfile.Join(in.Output, relativePath)
|
||||
}
|
||||
if err = c.parseFile(ctx, file, outputPath, values, in); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if in.Output != "" {
|
||||
outputPath = in.Output
|
||||
}
|
||||
err = c.parseFile(ctx, path, outputPath, values, in)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *cTpl) parseFile(ctx context.Context, file string, output string, values g.Map, in cTplParseInput) (err error) {
|
||||
output = gstr.ReplaceByMap(output, g.MapStrStr{
|
||||
`\\`: `\`,
|
||||
`//`: `/`,
|
||||
})
|
||||
content, err := g.View().Parse(ctx, file, values)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if output != "" {
|
||||
mlog.Printf(`parse file "%s" to "%s"`, file, output)
|
||||
return gfile.PutContents(output, content)
|
||||
}
|
||||
if in.Replace {
|
||||
mlog.Printf(`parse and replace file "%s"`, file)
|
||||
return gfile.PutContents(file, content)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *cTpl) loadValues(ctx context.Context, valuesPath string) (data g.Map, err error) {
|
||||
if !gfile.Exists(valuesPath) {
|
||||
return nil, gerror.Newf(`values file/folder "%s" does not exist`, valuesPath)
|
||||
}
|
||||
var j *gjson.Json
|
||||
if gfile.IsDir(valuesPath) {
|
||||
var valueFiles []string
|
||||
valueFiles, err = gfile.ScanDirFile(valuesPath, cTplSupportValuesFilePattern, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data = make(g.Map)
|
||||
for _, file := range valueFiles {
|
||||
if j, err = gjson.Load(file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gutil.MapMerge(data, j.Map())
|
||||
}
|
||||
return
|
||||
}
|
||||
if j, err = gjson.Load(valuesPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data = j.Map()
|
||||
return
|
||||
}
|
||||
87
cmd/gf/internal/cmd/cmd_version.go
Normal file
87
cmd/gf/internal/cmd/cmd_version.go
Normal file
@ -0,0 +1,87 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gbuild"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
var (
|
||||
Version = cVersion{}
|
||||
)
|
||||
|
||||
type cVersion struct {
|
||||
g.Meta `name:"version" brief:"show version information of current binary"`
|
||||
}
|
||||
|
||||
type cVersionInput struct {
|
||||
g.Meta `name:"version"`
|
||||
}
|
||||
type cVersionOutput struct{}
|
||||
|
||||
func (c cVersion) Index(ctx context.Context, in cVersionInput) (*cVersionOutput, error) {
|
||||
info := gbuild.Info()
|
||||
if info.Git == "" {
|
||||
info.Git = "none"
|
||||
}
|
||||
mlog.Printf(`GoFrame CLI Tool %s, https://goframe.org`, gf.VERSION)
|
||||
gfVersion, err := c.getGFVersionOfCurrentProject()
|
||||
if err != nil {
|
||||
gfVersion = err.Error()
|
||||
} else {
|
||||
gfVersion = gfVersion + " in current go.mod"
|
||||
}
|
||||
mlog.Printf(`GoFrame Version: %s`, gfVersion)
|
||||
mlog.Printf(`CLI Installed At: %s`, gfile.SelfPath())
|
||||
if info.GoFrame == "" {
|
||||
mlog.Print(`Current is a custom installed version, no installation information.`)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mlog.Print(gstr.Trim(fmt.Sprintf(`
|
||||
CLI Built Detail:
|
||||
Go Version: %s
|
||||
GF Version: %s
|
||||
Git Commit: %s
|
||||
Build Time: %s
|
||||
`, info.Golang, info.GoFrame, info.Git, info.Time)))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// getGFVersionOfCurrentProject checks and returns the GoFrame version current project using.
|
||||
func (c cVersion) getGFVersionOfCurrentProject() (string, error) {
|
||||
goModPath := gfile.Join(gfile.Pwd(), "go.mod")
|
||||
if gfile.Exists(goModPath) {
|
||||
lines := gstr.SplitAndTrim(gfile.GetContents(goModPath), "\n")
|
||||
for _, line := range lines {
|
||||
line = gstr.Trim(line)
|
||||
// Version 1.
|
||||
match, err := gregex.MatchString(`^github\.com/gogf/gf\s+(.+)$`, line)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(match) <= 1 {
|
||||
// Version > 1.
|
||||
match, err = gregex.MatchString(`^github\.com/gogf/gf/v\d\s+(.+)$`, line)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
if len(match) > 1 {
|
||||
return gstr.Trim(match[1]), nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", gerror.New("cannot find goframe requirement in go.mod")
|
||||
} else {
|
||||
return "", gerror.New("cannot find go.mod")
|
||||
}
|
||||
}
|
||||
7
cmd/gf/internal/consts/consts.go
Normal file
7
cmd/gf/internal/consts/consts.go
Normal file
@ -0,0 +1,7 @@
|
||||
package consts
|
||||
|
||||
const (
|
||||
// DoNotEditKey is used in generated files,
|
||||
// which marks the files will be overwritten by CLI tool.
|
||||
DoNotEditKey = `DO NOT EDIT`
|
||||
)
|
||||
107
cmd/gf/internal/consts/consts_gen_dao_template_dao.go
Normal file
107
cmd/gf/internal/consts/consts_gen_dao_template_dao.go
Normal file
@ -0,0 +1,107 @@
|
||||
package consts
|
||||
|
||||
const TemplateDaoDaoIndexContent = `
|
||||
// =================================================================================
|
||||
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
|
||||
// =================================================================================
|
||||
|
||||
package dao
|
||||
|
||||
import (
|
||||
"{TplImportPrefix}/internal"
|
||||
)
|
||||
|
||||
// internal{TplTableNameCamelCase}Dao is internal type for wrapping internal DAO implements.
|
||||
type internal{TplTableNameCamelCase}Dao = *internal.{TplTableNameCamelCase}Dao
|
||||
|
||||
// {TplTableNameCamelLowerCase}Dao is the data access object for table {TplTableName}.
|
||||
// You can define custom methods on it to extend its functionality as you wish.
|
||||
type {TplTableNameCamelLowerCase}Dao struct {
|
||||
internal{TplTableNameCamelCase}Dao
|
||||
}
|
||||
|
||||
var (
|
||||
// {TplTableNameCamelCase} is globally public accessible object for table {TplTableName} operations.
|
||||
{TplTableNameCamelCase} = {TplTableNameCamelLowerCase}Dao{
|
||||
internal.New{TplTableNameCamelCase}Dao(),
|
||||
}
|
||||
)
|
||||
|
||||
// Fill with you ideas below.
|
||||
|
||||
`
|
||||
|
||||
const TemplateDaoDaoInternalContent = `
|
||||
// ==========================================================================
|
||||
// Code generated by GoFrame CLI tool. DO NOT EDIT. {TplDatetimeStr}
|
||||
// ==========================================================================
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// {TplTableNameCamelCase}Dao is the data access object for table {TplTableName}.
|
||||
type {TplTableNameCamelCase}Dao struct {
|
||||
table string // table is the underlying table name of the DAO.
|
||||
group string // group is the database configuration group name of current DAO.
|
||||
columns {TplTableNameCamelCase}Columns // columns contains all the column names of Table for convenient usage.
|
||||
}
|
||||
|
||||
// {TplTableNameCamelCase}Columns defines and stores column names for table {TplTableName}.
|
||||
type {TplTableNameCamelCase}Columns struct {
|
||||
{TplColumnDefine}
|
||||
}
|
||||
|
||||
// {TplTableNameCamelLowerCase}Columns holds the columns for table {TplTableName}.
|
||||
var {TplTableNameCamelLowerCase}Columns = {TplTableNameCamelCase}Columns{
|
||||
{TplColumnNames}
|
||||
}
|
||||
|
||||
// New{TplTableNameCamelCase}Dao creates and returns a new DAO object for table data access.
|
||||
func New{TplTableNameCamelCase}Dao() *{TplTableNameCamelCase}Dao {
|
||||
return &{TplTableNameCamelCase}Dao{
|
||||
group: "{TplGroupName}",
|
||||
table: "{TplTableName}",
|
||||
columns: {TplTableNameCamelLowerCase}Columns,
|
||||
}
|
||||
}
|
||||
|
||||
// DB retrieves and returns the underlying raw database management object of current DAO.
|
||||
func (dao *{TplTableNameCamelCase}Dao) DB() gdb.DB {
|
||||
return g.DB(dao.group)
|
||||
}
|
||||
|
||||
// Table returns the table name of current dao.
|
||||
func (dao *{TplTableNameCamelCase}Dao) Table() string {
|
||||
return dao.table
|
||||
}
|
||||
|
||||
// Columns returns all column names of current dao.
|
||||
func (dao *{TplTableNameCamelCase}Dao) Columns() {TplTableNameCamelCase}Columns {
|
||||
return dao.columns
|
||||
}
|
||||
|
||||
// Group returns the configuration group name of database of current dao.
|
||||
func (dao *{TplTableNameCamelCase}Dao) Group() string {
|
||||
return dao.group
|
||||
}
|
||||
|
||||
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
|
||||
func (dao *{TplTableNameCamelCase}Dao) Ctx(ctx context.Context) *gdb.Model {
|
||||
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
|
||||
}
|
||||
|
||||
// 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 (dao *{TplTableNameCamelCase}Dao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) {
|
||||
return dao.Ctx(ctx).Transaction(ctx, f)
|
||||
}
|
||||
`
|
||||
14
cmd/gf/internal/consts/consts_gen_dao_template_do.go
Normal file
14
cmd/gf/internal/consts/consts_gen_dao_template_do.go
Normal file
@ -0,0 +1,14 @@
|
||||
package consts
|
||||
|
||||
const TemplateGenDaoDoContent = `
|
||||
// =================================================================================
|
||||
// Code generated by GoFrame CLI tool. DO NOT EDIT. {TplDatetimeStr}
|
||||
// =================================================================================
|
||||
|
||||
package do
|
||||
|
||||
{TplPackageImports}
|
||||
|
||||
// {TplTableNameCamelCase} is the golang structure of table {TplTableName} for DAO operations like Where/Data.
|
||||
{TplStructDefine}
|
||||
`
|
||||
14
cmd/gf/internal/consts/consts_gen_dao_template_entity.go
Normal file
14
cmd/gf/internal/consts/consts_gen_dao_template_entity.go
Normal file
@ -0,0 +1,14 @@
|
||||
package consts
|
||||
|
||||
const TemplateGenDaoEntityContent = `
|
||||
// =================================================================================
|
||||
// Code generated by GoFrame CLI tool. DO NOT EDIT. {TplDatetimeStr}
|
||||
// =================================================================================
|
||||
|
||||
package entity
|
||||
|
||||
{TplPackageImports}
|
||||
|
||||
// {TplTableNameCamelCase} is the golang structure for table {TplTableName}.
|
||||
{TplStructDefine}
|
||||
`
|
||||
17
cmd/gf/internal/consts/consts_gen_pbentity_template.go
Normal file
17
cmd/gf/internal/consts/consts_gen_pbentity_template.go
Normal file
@ -0,0 +1,17 @@
|
||||
package consts
|
||||
|
||||
const TemplatePbEntityMessageContent = `
|
||||
// ==========================================================================
|
||||
// Code generated by GoFrame CLI tool. DO NOT EDIT.
|
||||
// ==========================================================================
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package {PackageName};
|
||||
|
||||
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
|
||||
|
||||
{OptionContent}
|
||||
|
||||
{EntityMessage}
|
||||
`
|
||||
26
cmd/gf/internal/consts/consts_gen_service_template.go
Normal file
26
cmd/gf/internal/consts/consts_gen_service_template.go
Normal file
@ -0,0 +1,26 @@
|
||||
package consts
|
||||
|
||||
const TemplateGenServiceContent = `
|
||||
// ==========================================================================
|
||||
// Code generated by GoFrame CLI tool. DO NOT EDIT.
|
||||
// ==========================================================================
|
||||
|
||||
package {PackageName}
|
||||
|
||||
type I{StructName} interface {
|
||||
{FuncDefinition}
|
||||
}
|
||||
|
||||
var local{StructName} I{StructName}
|
||||
|
||||
func {StructName}() I{StructName} {
|
||||
if local{StructName} == nil {
|
||||
panic("implement not found for interface I{StructName}, forgot register?")
|
||||
}
|
||||
return local{StructName}
|
||||
}
|
||||
|
||||
func Register{StructName}(i I{StructName}) {
|
||||
local{StructName} = i
|
||||
}
|
||||
`
|
||||
13
cmd/gf/internal/consts/consts_gen_service_template_logic.go
Normal file
13
cmd/gf/internal/consts/consts_gen_service_template_logic.go
Normal file
@ -0,0 +1,13 @@
|
||||
package consts
|
||||
|
||||
const TemplateGenServiceLogicContent = `
|
||||
// ==========================================================================
|
||||
// Code generated by GoFrame CLI tool. DO NOT EDIT.
|
||||
// ==========================================================================
|
||||
|
||||
package {PackageName}
|
||||
|
||||
import(
|
||||
{Imports}
|
||||
)
|
||||
`
|
||||
1
cmd/gf/internal/packed/packed.go
Normal file
1
cmd/gf/internal/packed/packed.go
Normal file
@ -0,0 +1 @@
|
||||
package packed
|
||||
9
cmd/gf/internal/packed/template-mono.go
Normal file
9
cmd/gf/internal/packed/template-mono.go
Normal file
File diff suppressed because one or more lines are too long
9
cmd/gf/internal/packed/template-single.go
Normal file
9
cmd/gf/internal/packed/template-single.go
Normal file
File diff suppressed because one or more lines are too long
235
cmd/gf/internal/service/install.go
Normal file
235
cmd/gf/internal/service/install.go
Normal file
@ -0,0 +1,235 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/allyes"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/genv"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var (
|
||||
Install = serviceInstall{}
|
||||
)
|
||||
|
||||
type serviceInstall struct{}
|
||||
|
||||
type serviceInstallAvailablePath struct {
|
||||
dirPath string
|
||||
filePath string
|
||||
writable bool
|
||||
installed bool
|
||||
}
|
||||
|
||||
func (s serviceInstall) Run(ctx context.Context) (err error) {
|
||||
// Ask where to install.
|
||||
paths := s.getAvailablePaths()
|
||||
if len(paths) <= 0 {
|
||||
mlog.Printf("no path detected, you can manually install gf by copying the binary to path folder.")
|
||||
return
|
||||
}
|
||||
mlog.Printf("I found some installable paths for you(from $PATH): ")
|
||||
mlog.Printf(" %2s | %8s | %9s | %s", "Id", "Writable", "Installed", "Path")
|
||||
|
||||
// Print all paths status and determine the default selectedID value.
|
||||
var (
|
||||
selectedID = -1
|
||||
newPaths []serviceInstallAvailablePath
|
||||
pathSet = gset.NewStrSet() // Used for repeated items filtering.
|
||||
)
|
||||
for _, path := range paths {
|
||||
if !pathSet.AddIfNotExist(path.dirPath) {
|
||||
continue
|
||||
}
|
||||
newPaths = append(newPaths, path)
|
||||
}
|
||||
paths = newPaths
|
||||
for id, path := range paths {
|
||||
mlog.Printf(" %2d | %8t | %9t | %s", id, path.writable, path.installed, path.dirPath)
|
||||
if selectedID == -1 {
|
||||
// Use the previously installed path as the most priority choice.
|
||||
if path.installed {
|
||||
selectedID = id
|
||||
}
|
||||
}
|
||||
}
|
||||
// If there's no previously installed path, use the first writable path.
|
||||
if selectedID == -1 {
|
||||
// Order by choosing priority.
|
||||
commonPaths := garray.NewStrArrayFrom(g.SliceStr{
|
||||
s.getGoPathBin(),
|
||||
`/usr/local/bin`,
|
||||
`/usr/bin`,
|
||||
`/usr/sbin`,
|
||||
`C:\Windows`,
|
||||
`C:\Windows\system32`,
|
||||
`C:\Go\bin`,
|
||||
`C:\Program Files`,
|
||||
`C:\Program Files (x86)`,
|
||||
})
|
||||
// Check the common installation directories.
|
||||
commonPaths.Iterator(func(k int, v string) bool {
|
||||
for id, aPath := range paths {
|
||||
if strings.EqualFold(aPath.dirPath, v) {
|
||||
selectedID = id
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
if selectedID == -1 {
|
||||
selectedID = 0
|
||||
}
|
||||
}
|
||||
|
||||
if allyes.Check() {
|
||||
// Use the default selectedID.
|
||||
mlog.Printf("please choose one installation destination [default %d]: %d", selectedID, selectedID)
|
||||
} else {
|
||||
for {
|
||||
// Get input and update selectedID.
|
||||
var (
|
||||
inputID int
|
||||
input = gcmd.Scanf("please choose one installation destination [default %d]: ", selectedID)
|
||||
)
|
||||
if input != "" {
|
||||
inputID = gconv.Int(input)
|
||||
}
|
||||
// Check if out of range.
|
||||
if inputID >= len(paths) || inputID < 0 {
|
||||
mlog.Printf("invalid install destination Id: %d", inputID)
|
||||
continue
|
||||
}
|
||||
selectedID = inputID
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Get selected destination path.
|
||||
dstPath := paths[selectedID]
|
||||
|
||||
// Install the new binary.
|
||||
err = gfile.CopyFile(gfile.SelfPath(), dstPath.filePath)
|
||||
if err != nil {
|
||||
mlog.Printf("install gf binary to '%s' failed: %v", dstPath.dirPath, err)
|
||||
mlog.Printf("you can manually install gf by copying the binary to folder: %s", dstPath.dirPath)
|
||||
} else {
|
||||
mlog.Printf("gf binary is successfully installed to: %s", dstPath.dirPath)
|
||||
}
|
||||
|
||||
// Uninstall the old binary.
|
||||
for _, path := range paths {
|
||||
// Do not delete myself.
|
||||
if path.filePath != "" && path.filePath != dstPath.filePath && gfile.SelfPath() != path.filePath {
|
||||
_ = gfile.Remove(path.filePath)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// IsInstalled checks and returns whether the binary is installed.
|
||||
func (s serviceInstall) IsInstalled() bool {
|
||||
paths := s.getAvailablePaths()
|
||||
for _, aPath := range paths {
|
||||
if aPath.installed {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// getGoPathBinFilePath retrieves ad returns the GOPATH/bin path for binary.
|
||||
func (s serviceInstall) getGoPathBin() string {
|
||||
if goPath := genv.Get(`GOPATH`).String(); goPath != "" {
|
||||
return gfile.Join(goPath, "bin")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// getAvailablePaths returns the installation paths data for the binary.
|
||||
func (s serviceInstall) getAvailablePaths() []serviceInstallAvailablePath {
|
||||
var (
|
||||
folderPaths []serviceInstallAvailablePath
|
||||
binaryFileName = "gf" + gfile.Ext(gfile.SelfPath())
|
||||
)
|
||||
// $GOPATH/bin
|
||||
if goPathBin := s.getGoPathBin(); goPathBin != "" {
|
||||
folderPaths = s.checkAndAppendToAvailablePath(
|
||||
folderPaths, goPathBin, binaryFileName,
|
||||
)
|
||||
}
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
darwinInstallationCheckPaths := []string{"/usr/local/bin"}
|
||||
for _, v := range darwinInstallationCheckPaths {
|
||||
folderPaths = s.checkAndAppendToAvailablePath(
|
||||
folderPaths, v, binaryFileName,
|
||||
)
|
||||
}
|
||||
fallthrough
|
||||
|
||||
default:
|
||||
// Search and find the writable directory path.
|
||||
envPath := genv.Get("PATH", genv.Get("Path").String()).String()
|
||||
if gstr.Contains(envPath, ";") {
|
||||
// windows.
|
||||
for _, v := range gstr.SplitAndTrim(envPath, ";") {
|
||||
if v == "." {
|
||||
continue
|
||||
}
|
||||
folderPaths = s.checkAndAppendToAvailablePath(
|
||||
folderPaths, v, binaryFileName,
|
||||
)
|
||||
}
|
||||
} else if gstr.Contains(envPath, ":") {
|
||||
// *nix.
|
||||
for _, v := range gstr.SplitAndTrim(envPath, ":") {
|
||||
if v == "." {
|
||||
continue
|
||||
}
|
||||
folderPaths = s.checkAndAppendToAvailablePath(
|
||||
folderPaths, v, binaryFileName,
|
||||
)
|
||||
}
|
||||
} else if envPath != "" {
|
||||
folderPaths = s.checkAndAppendToAvailablePath(
|
||||
folderPaths, envPath, binaryFileName,
|
||||
)
|
||||
} else {
|
||||
folderPaths = s.checkAndAppendToAvailablePath(
|
||||
folderPaths, "/usr/local/bin", binaryFileName,
|
||||
)
|
||||
}
|
||||
}
|
||||
return folderPaths
|
||||
}
|
||||
|
||||
// checkAndAppendToAvailablePath checks if `path` is writable and already installed.
|
||||
// It adds the `path` to `folderPaths` if it is writable or already installed, or else it ignores the `path`.
|
||||
func (s serviceInstall) checkAndAppendToAvailablePath(folderPaths []serviceInstallAvailablePath, dirPath string, binaryFileName string) []serviceInstallAvailablePath {
|
||||
var (
|
||||
filePath = gfile.Join(dirPath, binaryFileName)
|
||||
writable = gfile.IsWritable(dirPath)
|
||||
installed = gfile.Exists(filePath)
|
||||
)
|
||||
if !writable && !installed {
|
||||
return folderPaths
|
||||
}
|
||||
return append(
|
||||
folderPaths,
|
||||
serviceInstallAvailablePath{
|
||||
dirPath: dirPath,
|
||||
writable: writable,
|
||||
filePath: filePath,
|
||||
installed: installed,
|
||||
})
|
||||
}
|
||||
22
cmd/gf/internal/utility/allyes/allyes.go
Normal file
22
cmd/gf/internal/utility/allyes/allyes.go
Normal file
@ -0,0 +1,22 @@
|
||||
package allyes
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/genv"
|
||||
)
|
||||
|
||||
const (
|
||||
EnvName = "GF_CLI_ALL_YES"
|
||||
)
|
||||
|
||||
// Init initializes the package manually.
|
||||
func Init() {
|
||||
if gcmd.GetOpt("y") != nil {
|
||||
genv.MustSet(EnvName, "1")
|
||||
}
|
||||
}
|
||||
|
||||
// Check checks whether option allow all yes for command.
|
||||
func Check() bool {
|
||||
return genv.Get(EnvName).String() == "1"
|
||||
}
|
||||
66
cmd/gf/internal/utility/mlog/mlog.go
Normal file
66
cmd/gf/internal/utility/mlog/mlog.go
Normal file
@ -0,0 +1,66 @@
|
||||
package mlog
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/genv"
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
)
|
||||
|
||||
const (
|
||||
headerPrintEnvName = "GF_CLI_MLOG_HEADER"
|
||||
)
|
||||
|
||||
var (
|
||||
ctx = context.TODO()
|
||||
logger = glog.New()
|
||||
)
|
||||
|
||||
func init() {
|
||||
logger.SetStack(false)
|
||||
if genv.Get(headerPrintEnvName).String() == "1" {
|
||||
logger.SetHeaderPrint(true)
|
||||
} else {
|
||||
logger.SetHeaderPrint(false)
|
||||
}
|
||||
if gcmd.GetOpt("debug") != nil || gcmd.GetOpt("gf.debug") != nil {
|
||||
logger.SetDebug(true)
|
||||
} else {
|
||||
logger.SetDebug(false)
|
||||
}
|
||||
}
|
||||
|
||||
// SetHeaderPrint enables/disables header printing to stdout.
|
||||
func SetHeaderPrint(enabled bool) {
|
||||
logger.SetHeaderPrint(enabled)
|
||||
if enabled {
|
||||
_ = genv.Set(headerPrintEnvName, "1")
|
||||
} else {
|
||||
_ = genv.Set(headerPrintEnvName, "0")
|
||||
}
|
||||
}
|
||||
|
||||
func Print(v ...interface{}) {
|
||||
logger.Print(ctx, v...)
|
||||
}
|
||||
|
||||
func Printf(format string, v ...interface{}) {
|
||||
logger.Printf(ctx, format, v...)
|
||||
}
|
||||
|
||||
func Fatal(v ...interface{}) {
|
||||
logger.Fatal(ctx, v...)
|
||||
}
|
||||
|
||||
func Fatalf(format string, v ...interface{}) {
|
||||
logger.Fatalf(ctx, format, v...)
|
||||
}
|
||||
|
||||
func Debug(v ...interface{}) {
|
||||
logger.Debug(ctx, v...)
|
||||
}
|
||||
|
||||
func Debugf(format string, v ...interface{}) {
|
||||
logger.Debugf(ctx, format, v...)
|
||||
}
|
||||
48
cmd/gf/internal/utility/utils/utils.go
Normal file
48
cmd/gf/internal/utility/utils/utils.go
Normal file
@ -0,0 +1,48 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
var (
|
||||
gofmtPath = gproc.SearchBinaryPath("gofmt") // gofmtPath is the binary path of command `gofmt`.
|
||||
goimportsPath = gproc.SearchBinaryPath("goimports") // gofmtPath is the binary path of command `goimports`.
|
||||
)
|
||||
|
||||
// GoFmt formats the source file using command `gofmt -w -s PATH`.
|
||||
func GoFmt(path string) {
|
||||
if gofmtPath == "" {
|
||||
mlog.Fatal(`command "gofmt" not found`)
|
||||
}
|
||||
var command = fmt.Sprintf(`%s -w %s`, gofmtPath, path)
|
||||
result, err := gproc.ShellExec(command)
|
||||
if err != nil {
|
||||
mlog.Fatalf(`error executing command "%s": %s`, command, result)
|
||||
}
|
||||
}
|
||||
|
||||
// GoImports formats the source file using command `goimports -w PATH`.
|
||||
func GoImports(path string) {
|
||||
if goimportsPath == "" {
|
||||
mlog.Fatal(`command "goimports" not found`)
|
||||
}
|
||||
var command = fmt.Sprintf(`%s -w %s`, goimportsPath, path)
|
||||
result, err := gproc.ShellExec(command)
|
||||
if err != nil {
|
||||
mlog.Fatalf(`error executing command "%s": %s`, command, result)
|
||||
}
|
||||
}
|
||||
|
||||
// IsFileDoNotEdit checks and returns whether file contains `do not edit` key.
|
||||
func IsFileDoNotEdit(filePath string) bool {
|
||||
if !gfile.Exists(filePath) {
|
||||
return true
|
||||
}
|
||||
return gstr.Contains(gfile.GetContents(filePath), consts.DoNotEditKey)
|
||||
}
|
||||
86
cmd/gf/main.go
Normal file
86
cmd/gf/main.go
Normal file
@ -0,0 +1,86 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "github.com/gogf/gf/cmd/gf/v2/internal/packed"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcfg"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/cmd"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/allyes"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
const (
|
||||
cliFolderName = `hack`
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer func() {
|
||||
if exception := recover(); exception != nil {
|
||||
if err, ok := exception.(error); ok {
|
||||
mlog.Print(err.Error())
|
||||
} else {
|
||||
panic(exception)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// CLI configuration.
|
||||
if path, _ := gfile.Search(cliFolderName); path != "" {
|
||||
if adapter, ok := g.Cfg().GetAdapter().(*gcfg.AdapterFile); ok {
|
||||
if err := adapter.SetPath(path); err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// zsh alias "git fetch" conflicts checks.
|
||||
handleZshAlias()
|
||||
|
||||
// -y option checks.
|
||||
allyes.Init()
|
||||
|
||||
var (
|
||||
ctx = gctx.New()
|
||||
)
|
||||
command, err := gcmd.NewFromObject(cmd.GF)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = command.AddObject(
|
||||
cmd.Env,
|
||||
cmd.Run,
|
||||
cmd.Gen,
|
||||
cmd.Tpl,
|
||||
cmd.Init,
|
||||
cmd.Pack,
|
||||
cmd.Build,
|
||||
cmd.Docker,
|
||||
cmd.Install,
|
||||
cmd.Version,
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
command.Run(ctx)
|
||||
}
|
||||
|
||||
// zsh alias "git fetch" conflicts checks.
|
||||
func handleZshAlias() {
|
||||
if home, err := gfile.Home(); err == nil {
|
||||
zshPath := gfile.Join(home, ".zshrc")
|
||||
if gfile.Exists(zshPath) {
|
||||
var (
|
||||
aliasCommand = `alias gf=gf`
|
||||
content = gfile.GetContents(zshPath)
|
||||
)
|
||||
if !gstr.Contains(content, aliasCommand) {
|
||||
_ = gfile.PutContentsAppend(zshPath, "\n"+aliasCommand+"\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
cmd/gf/test/testdata/tpls/tpl1.yaml
vendored
Normal file
2
cmd/gf/test/testdata/tpls/tpl1.yaml
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
server:
|
||||
address: {{.server.address}}
|
||||
1
cmd/gf/test/testdata/tpls/tpl2.sql
vendored
Normal file
1
cmd/gf/test/testdata/tpls/tpl2.sql
vendored
Normal file
@ -0,0 +1 @@
|
||||
insert into {{.sql.table}}
|
||||
8
cmd/gf/test/testdata/values.json
vendored
Normal file
8
cmd/gf/test/testdata/values.json
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"server": {
|
||||
"address": "https://goframe.org"
|
||||
},
|
||||
"sql":{
|
||||
"table": "table_name"
|
||||
}
|
||||
}
|
||||
@ -503,16 +503,25 @@ func (a *Array) Search(value interface{}) int {
|
||||
// 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++ {
|
||||
for j := i + 1; j < len(a.array); {
|
||||
if a.array[i] == a.array[j] {
|
||||
a.array = append(a.array[:j], a.array[j+1:]...)
|
||||
} else {
|
||||
j++
|
||||
}
|
||||
}
|
||||
defer a.mu.Unlock()
|
||||
if len(a.array) == 0 {
|
||||
return a
|
||||
}
|
||||
a.mu.Unlock()
|
||||
var (
|
||||
ok bool
|
||||
temp interface{}
|
||||
uniqueSet = make(map[interface{}]struct{})
|
||||
uniqueArray = make([]interface{}, 0, len(a.array))
|
||||
)
|
||||
for i := 0; i < len(a.array); i++ {
|
||||
temp = a.array[i]
|
||||
if _, ok = uniqueSet[temp]; ok {
|
||||
continue
|
||||
}
|
||||
uniqueSet[temp] = struct{}{}
|
||||
uniqueArray = append(uniqueArray, temp)
|
||||
}
|
||||
a.array = uniqueArray
|
||||
return a
|
||||
}
|
||||
|
||||
@ -711,6 +720,9 @@ func (a *Array) IteratorDesc(f func(k int, v interface{}) bool) {
|
||||
|
||||
// String returns current array as a string, which implements like json.Marshal does.
|
||||
func (a *Array) String() string {
|
||||
if a == nil {
|
||||
return ""
|
||||
}
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
|
||||
@ -514,16 +514,25 @@ func (a *IntArray) Search(value int) int {
|
||||
// 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++ {
|
||||
for j := i + 1; j < len(a.array); {
|
||||
if a.array[i] == a.array[j] {
|
||||
a.array = append(a.array[:j], a.array[j+1:]...)
|
||||
} else {
|
||||
j++
|
||||
}
|
||||
}
|
||||
defer a.mu.Unlock()
|
||||
if len(a.array) == 0 {
|
||||
return a
|
||||
}
|
||||
a.mu.Unlock()
|
||||
var (
|
||||
ok bool
|
||||
temp int
|
||||
uniqueSet = make(map[int]struct{})
|
||||
uniqueArray = make([]int, 0, len(a.array))
|
||||
)
|
||||
for i := 0; i < len(a.array); i++ {
|
||||
temp = a.array[i]
|
||||
if _, ok = uniqueSet[temp]; ok {
|
||||
continue
|
||||
}
|
||||
uniqueSet[temp] = struct{}{}
|
||||
uniqueArray = append(uniqueArray, temp)
|
||||
}
|
||||
a.array = uniqueArray
|
||||
return a
|
||||
}
|
||||
|
||||
@ -722,6 +731,9 @@ func (a *IntArray) IteratorDesc(f func(k int, v int) bool) {
|
||||
|
||||
// String returns current array as a string, which implements like json.Marshal does.
|
||||
func (a *IntArray) String() string {
|
||||
if a == nil {
|
||||
return ""
|
||||
}
|
||||
return "[" + a.Join(",") + "]"
|
||||
}
|
||||
|
||||
|
||||
@ -516,16 +516,25 @@ func (a *StrArray) Search(value string) int {
|
||||
// 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++ {
|
||||
for j := i + 1; j < len(a.array); {
|
||||
if a.array[i] == a.array[j] {
|
||||
a.array = append(a.array[:j], a.array[j+1:]...)
|
||||
} else {
|
||||
j++
|
||||
}
|
||||
}
|
||||
defer a.mu.Unlock()
|
||||
if len(a.array) == 0 {
|
||||
return a
|
||||
}
|
||||
a.mu.Unlock()
|
||||
var (
|
||||
ok bool
|
||||
temp string
|
||||
uniqueSet = make(map[string]struct{})
|
||||
uniqueArray = make([]string, 0, len(a.array))
|
||||
)
|
||||
for i := 0; i < len(a.array); i++ {
|
||||
temp = a.array[i]
|
||||
if _, ok = uniqueSet[temp]; ok {
|
||||
continue
|
||||
}
|
||||
uniqueSet[temp] = struct{}{}
|
||||
uniqueArray = append(uniqueArray, temp)
|
||||
}
|
||||
a.array = uniqueArray
|
||||
return a
|
||||
}
|
||||
|
||||
@ -724,6 +733,9 @@ func (a *StrArray) IteratorDesc(f func(k int, v string) bool) {
|
||||
|
||||
// String returns current array as a string, which implements like json.Marshal does.
|
||||
func (a *StrArray) String() string {
|
||||
if a == nil {
|
||||
return ""
|
||||
}
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
|
||||
@ -36,9 +36,9 @@ type SortedArray struct {
|
||||
// NewSortedArray creates and returns an empty sorted array.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety, which is false in default.
|
||||
// The parameter `comparator` used to compare values to sort in array,
|
||||
// if it returns value < 0, means v1 < v2; the v1 will be inserted before v2;
|
||||
// if it returns value = 0, means v1 = v2; the v1 will be replaced by v2;
|
||||
// if it returns value > 0, means v1 > v2; the v1 will be inserted after v2;
|
||||
// if it returns value < 0, means `a` < `b`; the `a` will be inserted before `b`;
|
||||
// if it returns value = 0, means `a` = `b`; the `a` will be replaced by `b`;
|
||||
// if it returns value > 0, means `a` > `b`; the `a` will be inserted after `b`;
|
||||
func NewSortedArray(comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
|
||||
return NewSortedArraySize(0, comparator, safe...)
|
||||
}
|
||||
@ -653,6 +653,9 @@ func (a *SortedArray) IteratorDesc(f func(k int, v interface{}) bool) {
|
||||
|
||||
// String returns current array as a string, which implements like json.Marshal does.
|
||||
func (a *SortedArray) String() string {
|
||||
if a == nil {
|
||||
return ""
|
||||
}
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
|
||||
@ -646,6 +646,9 @@ func (a *SortedIntArray) IteratorDesc(f func(k int, v int) bool) {
|
||||
|
||||
// String returns current array as a string, which implements like json.Marshal does.
|
||||
func (a *SortedIntArray) String() string {
|
||||
if a == nil {
|
||||
return ""
|
||||
}
|
||||
return "[" + a.Join(",") + "]"
|
||||
}
|
||||
|
||||
|
||||
@ -648,6 +648,9 @@ func (a *SortedStrArray) IteratorDesc(f func(k int, v string) bool) {
|
||||
|
||||
// String returns current array as a string, which implements like json.Marshal does.
|
||||
func (a *SortedStrArray) String() string {
|
||||
if a == nil {
|
||||
return ""
|
||||
}
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
|
||||
@ -576,7 +576,7 @@ func TestArray_Json(t *testing.T) {
|
||||
|
||||
var a3 garray.Array
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// value.
|
||||
@ -595,7 +595,7 @@ func TestArray_Json(t *testing.T) {
|
||||
|
||||
var a3 garray.Array
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// pointer
|
||||
@ -609,11 +609,11 @@ func TestArray_Json(t *testing.T) {
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
})
|
||||
@ -628,11 +628,11 @@ func TestArray_Json(t *testing.T) {
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
})
|
||||
@ -720,7 +720,7 @@ func TestArray_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"array": g.Slice{1, 2, 3},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
|
||||
})
|
||||
|
||||
@ -619,7 +619,7 @@ func TestIntArray_Json(t *testing.T) {
|
||||
|
||||
var a3 garray.IntArray
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array value
|
||||
@ -637,7 +637,7 @@ func TestIntArray_Json(t *testing.T) {
|
||||
|
||||
var a3 garray.IntArray
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array pointer
|
||||
@ -651,11 +651,11 @@ func TestIntArray_Json(t *testing.T) {
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
})
|
||||
@ -670,11 +670,11 @@ func TestIntArray_Json(t *testing.T) {
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
})
|
||||
@ -752,7 +752,7 @@ func TestIntArray_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"array": []byte(`[1,2,3]`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
|
||||
})
|
||||
@ -763,7 +763,7 @@ func TestIntArray_UnmarshalValue(t *testing.T) {
|
||||
// "name": "john",
|
||||
// "array": g.Slice{1, 2, 3},
|
||||
// }, &v)
|
||||
// t.Assert(err, nil)
|
||||
// t.AssertNil(err)
|
||||
// t.Assert(v.Name, "john")
|
||||
// t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
|
||||
// })
|
||||
|
||||
@ -619,7 +619,7 @@ func TestStrArray_Json(t *testing.T) {
|
||||
|
||||
var a3 garray.StrArray
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array value
|
||||
@ -637,7 +637,7 @@ func TestStrArray_Json(t *testing.T) {
|
||||
|
||||
var a3 garray.StrArray
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array pointer
|
||||
@ -651,11 +651,11 @@ func TestStrArray_Json(t *testing.T) {
|
||||
"Scores": []string{"A+", "A", "A"},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
})
|
||||
@ -670,11 +670,11 @@ func TestStrArray_Json(t *testing.T) {
|
||||
"Scores": []string{"A+", "A", "A"},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
})
|
||||
@ -751,7 +751,7 @@ func TestStrArray_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"array": []byte(`["1","2","3"]`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.SliceStr{"1", "2", "3"})
|
||||
})
|
||||
@ -762,7 +762,7 @@ func TestStrArray_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"array": g.SliceStr{"1", "2", "3"},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.SliceStr{"1", "2", "3"})
|
||||
})
|
||||
|
||||
@ -661,7 +661,7 @@ func TestSortedArray_Json(t *testing.T) {
|
||||
|
||||
var a3 garray.SortedArray
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
t.Assert(a3.Interfaces(), s1)
|
||||
})
|
||||
@ -681,7 +681,7 @@ func TestSortedArray_Json(t *testing.T) {
|
||||
|
||||
var a3 garray.SortedArray
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
t.Assert(a3.Interfaces(), s1)
|
||||
})
|
||||
@ -696,11 +696,11 @@ func TestSortedArray_Json(t *testing.T) {
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.AssertNE(user.Scores, nil)
|
||||
t.Assert(user.Scores.Len(), 3)
|
||||
@ -732,11 +732,11 @@ func TestSortedArray_Json(t *testing.T) {
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.AssertNE(user.Scores, nil)
|
||||
t.Assert(user.Scores.Len(), 3)
|
||||
@ -830,7 +830,7 @@ func TestSortedArray_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"array": []byte(`[2,3,1]`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
|
||||
})
|
||||
@ -841,7 +841,7 @@ func TestSortedArray_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"array": g.Slice{2, 3, 1},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
|
||||
})
|
||||
|
||||
@ -561,7 +561,7 @@ func TestSortedIntArray_Json(t *testing.T) {
|
||||
|
||||
var a3 garray.SortedIntArray
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array value
|
||||
@ -580,7 +580,7 @@ func TestSortedIntArray_Json(t *testing.T) {
|
||||
|
||||
var a3 garray.SortedIntArray
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array pointer
|
||||
@ -594,11 +594,11 @@ func TestSortedIntArray_Json(t *testing.T) {
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, []int{98, 99, 100})
|
||||
})
|
||||
@ -613,11 +613,11 @@ func TestSortedIntArray_Json(t *testing.T) {
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, []int{98, 99, 100})
|
||||
})
|
||||
@ -695,7 +695,7 @@ func TestSortedIntArray_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"array": []byte(`[2,3,1]`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
|
||||
})
|
||||
@ -706,7 +706,7 @@ func TestSortedIntArray_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"array": g.Slice{2, 3, 1},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
|
||||
})
|
||||
|
||||
@ -583,7 +583,7 @@ func TestSortedStrArray_Json(t *testing.T) {
|
||||
|
||||
var a3 garray.SortedStrArray
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
t.Assert(a3.Interfaces(), s1)
|
||||
})
|
||||
@ -604,7 +604,7 @@ func TestSortedStrArray_Json(t *testing.T) {
|
||||
|
||||
var a3 garray.SortedStrArray
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
t.Assert(a3.Interfaces(), s1)
|
||||
})
|
||||
@ -619,11 +619,11 @@ func TestSortedStrArray_Json(t *testing.T) {
|
||||
"Scores": []string{"A+", "A", "A"},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, []string{"A", "A", "A+"})
|
||||
})
|
||||
@ -638,11 +638,11 @@ func TestSortedStrArray_Json(t *testing.T) {
|
||||
"Scores": []string{"A+", "A", "A"},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
user := new(User)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, []string{"A", "A", "A+"})
|
||||
})
|
||||
@ -719,7 +719,7 @@ func TestSortedStrArray_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"array": []byte(`["1","3","2"]`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.SliceStr{"1", "2", "3"})
|
||||
})
|
||||
@ -730,7 +730,7 @@ func TestSortedStrArray_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"array": g.SliceStr{"1", "3", "2"},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.SliceStr{"1", "2", "3"})
|
||||
})
|
||||
|
||||
@ -503,11 +503,14 @@ func (l *List) Join(glue string) string {
|
||||
|
||||
// String returns current list as a string.
|
||||
func (l *List) String() string {
|
||||
if l == nil {
|
||||
return ""
|
||||
}
|
||||
return "[" + l.Join(",") + "]"
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (l *List) MarshalJSON() ([]byte, error) {
|
||||
func (l List) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(l.FrontAll())
|
||||
}
|
||||
|
||||
|
||||
@ -709,20 +709,20 @@ func TestList_Json(t *testing.T) {
|
||||
a := []interface{}{"a", "b", "c"}
|
||||
l := New()
|
||||
b, err := json.Marshal(a)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
err = json.UnmarshalUseNumber(b, l)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(l.FrontAll(), a)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var l List
|
||||
a := []interface{}{"a", "b", "c"}
|
||||
b, err := json.Marshal(a)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
err = json.UnmarshalUseNumber(b, &l)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(l.FrontAll(), a)
|
||||
})
|
||||
}
|
||||
@ -739,7 +739,7 @@ func TestList_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"list": []byte(`[1,2,3]`),
|
||||
}, &tlist)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(tlist.Name, "john")
|
||||
t.Assert(tlist.List.FrontAll(), []interface{}{1, 2, 3})
|
||||
})
|
||||
@ -750,7 +750,7 @@ func TestList_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"list": []interface{}{1, 2, 3},
|
||||
}, &tlist)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(tlist.Name, "john")
|
||||
t.Assert(tlist.List.FrontAll(), []interface{}{1, 2, 3})
|
||||
})
|
||||
|
||||
@ -456,12 +456,15 @@ func (m *AnyAnyMap) Merge(other *AnyAnyMap) {
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *AnyAnyMap) String() string {
|
||||
if m == nil {
|
||||
return ""
|
||||
}
|
||||
b, _ := m.MarshalJSON()
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *AnyAnyMap) MarshalJSON() ([]byte, error) {
|
||||
func (m AnyAnyMap) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(gconv.Map(m.Map()))
|
||||
}
|
||||
|
||||
|
||||
@ -455,12 +455,15 @@ func (m *IntAnyMap) Merge(other *IntAnyMap) {
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *IntAnyMap) String() string {
|
||||
if m == nil {
|
||||
return ""
|
||||
}
|
||||
b, _ := m.MarshalJSON()
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *IntAnyMap) MarshalJSON() ([]byte, error) {
|
||||
func (m IntAnyMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return json.Marshal(m.data)
|
||||
|
||||
@ -426,12 +426,15 @@ func (m *IntIntMap) Merge(other *IntIntMap) {
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *IntIntMap) String() string {
|
||||
if m == nil {
|
||||
return ""
|
||||
}
|
||||
b, _ := m.MarshalJSON()
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *IntIntMap) MarshalJSON() ([]byte, error) {
|
||||
func (m IntIntMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return json.Marshal(m.data)
|
||||
|
||||
@ -426,12 +426,15 @@ func (m *IntStrMap) Merge(other *IntStrMap) {
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *IntStrMap) String() string {
|
||||
if m == nil {
|
||||
return ""
|
||||
}
|
||||
b, _ := m.MarshalJSON()
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *IntStrMap) MarshalJSON() ([]byte, error) {
|
||||
func (m IntStrMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return json.Marshal(m.data)
|
||||
|
||||
@ -451,12 +451,15 @@ func (m *StrAnyMap) Merge(other *StrAnyMap) {
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *StrAnyMap) String() string {
|
||||
if m == nil {
|
||||
return ""
|
||||
}
|
||||
b, _ := m.MarshalJSON()
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *StrAnyMap) MarshalJSON() ([]byte, error) {
|
||||
func (m StrAnyMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return json.Marshal(m.data)
|
||||
|
||||
@ -430,12 +430,15 @@ func (m *StrIntMap) Merge(other *StrIntMap) {
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *StrIntMap) String() string {
|
||||
if m == nil {
|
||||
return ""
|
||||
}
|
||||
b, _ := m.MarshalJSON()
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *StrIntMap) MarshalJSON() ([]byte, error) {
|
||||
func (m StrIntMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return json.Marshal(m.data)
|
||||
|
||||
@ -429,12 +429,15 @@ func (m *StrStrMap) Merge(other *StrStrMap) {
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *StrStrMap) String() string {
|
||||
if m == nil {
|
||||
return ""
|
||||
}
|
||||
b, _ := m.MarshalJSON()
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *StrStrMap) MarshalJSON() ([]byte, error) {
|
||||
func (m StrStrMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return json.Marshal(m.data)
|
||||
|
||||
@ -7,6 +7,9 @@
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/glist"
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
@ -15,6 +18,13 @@ import (
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// ListMap is a map that preserves insertion-order.
|
||||
//
|
||||
// It is backed by a hash table to store values and doubly-linked list to store ordering.
|
||||
//
|
||||
// Structure is not thread safe.
|
||||
//
|
||||
// Reference: http://en.wikipedia.org/wiki/Associative_array
|
||||
type ListMap struct {
|
||||
mu rwmutex.RWMutex
|
||||
data map[interface{}]*glist.Element
|
||||
@ -58,7 +68,7 @@ func (m *ListMap) IteratorAsc(f func(key interface{}, value interface{}) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
if m.list != nil {
|
||||
node := (*gListMapNode)(nil)
|
||||
var node *gListMapNode
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
return f(node.key, node.value)
|
||||
@ -72,7 +82,7 @@ func (m *ListMap) IteratorDesc(f func(key interface{}, value interface{}) bool)
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
if m.list != nil {
|
||||
node := (*gListMapNode)(nil)
|
||||
var node *gListMapNode
|
||||
m.list.IteratorDesc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
return f(node.key, node.value)
|
||||
@ -146,8 +156,10 @@ func (m *ListMap) MapStrAny() map[string]interface{} {
|
||||
func (m *ListMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
if m.list != nil {
|
||||
keys := make([]interface{}, 0)
|
||||
node := (*gListMapNode)(nil)
|
||||
var (
|
||||
keys = make([]interface{}, 0)
|
||||
node *gListMapNode
|
||||
)
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
if empty.IsEmpty(node.value) {
|
||||
@ -495,7 +507,7 @@ func (m *ListMap) Merge(other *ListMap) {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
node := (*gListMapNode)(nil)
|
||||
var node *gListMapNode
|
||||
other.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
if e, ok := m.data[node.key]; !ok {
|
||||
@ -509,13 +521,34 @@ func (m *ListMap) Merge(other *ListMap) {
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *ListMap) String() string {
|
||||
if m == nil {
|
||||
return ""
|
||||
}
|
||||
b, _ := m.MarshalJSON()
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *ListMap) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(gconv.Map(m.Map()))
|
||||
func (m ListMap) MarshalJSON() (jsonBytes []byte, err error) {
|
||||
if m.data == nil {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
buffer.WriteByte('{')
|
||||
m.Iterator(func(key, value interface{}) bool {
|
||||
valueBytes, valueJsonErr := json.Marshal(value)
|
||||
if valueJsonErr != nil {
|
||||
err = valueJsonErr
|
||||
return false
|
||||
}
|
||||
if buffer.Len() > 1 {
|
||||
buffer.WriteByte(',')
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf(`"%v":%s`, key, valueBytes))
|
||||
return true
|
||||
})
|
||||
buffer.WriteByte('}')
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
|
||||
@ -8,6 +8,7 @@ package gmap_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
@ -195,7 +196,7 @@ func ExampleListMap_Sets() {
|
||||
m.Sets(addMap)
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// May Output:
|
||||
// {"key1":"val1","key2":"val2","key3":"val3"}
|
||||
}
|
||||
|
||||
@ -562,12 +563,10 @@ func ExampleListMap_String() {
|
||||
|
||||
func ExampleListMap_MarshalJSON() {
|
||||
var m gmap.ListMap
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
m.Set("k1", "v1")
|
||||
m.Set("k2", "v2")
|
||||
m.Set("k3", "v3")
|
||||
m.Set("k4", "v4")
|
||||
|
||||
bytes, err := json.Marshal(&m)
|
||||
if err == nil {
|
||||
|
||||
@ -8,6 +8,7 @@ package gmap_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/util/gutil"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
@ -277,7 +278,7 @@ func ExampleNewListMapFrom() {
|
||||
n := gmap.NewListMapFrom(m.Map(), true)
|
||||
fmt.Println(n)
|
||||
|
||||
// Output:
|
||||
// May Output:
|
||||
// {"key1":"var1","key2":"var2"}
|
||||
// {"key1":"var1","key2":"var2"}
|
||||
}
|
||||
@ -290,7 +291,7 @@ func ExampleNewTreeMap() {
|
||||
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// May Output:
|
||||
// map[key1:var1 key2:var2]
|
||||
}
|
||||
|
||||
@ -305,7 +306,7 @@ func ExampleNewTreeMapFrom() {
|
||||
n := gmap.NewListMapFrom(m.Map(), true)
|
||||
fmt.Println(n.Map())
|
||||
|
||||
// Output:
|
||||
// May Output:
|
||||
// map[key1:var1 key2:var2]
|
||||
// map[key1:var1 key2:var2]
|
||||
}
|
||||
|
||||
@ -253,11 +253,11 @@ func Test_AnyAnyMap_Json(t *testing.T) {
|
||||
"k2": "v2",
|
||||
}
|
||||
b, err := json.Marshal(gconv.Map(data))
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
m := gmap.New()
|
||||
err = json.UnmarshalUseNumber(b, m)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
@ -267,11 +267,11 @@ func Test_AnyAnyMap_Json(t *testing.T) {
|
||||
"k2": "v2",
|
||||
}
|
||||
b, err := json.Marshal(gconv.Map(data))
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
var m gmap.Map
|
||||
err = json.UnmarshalUseNumber(b, &m)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
@ -342,7 +342,7 @@ func TestAnyAnyMap_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"map": []byte(`{"k1":"v1","k2":"v2"}`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get("k1"), "v1")
|
||||
@ -358,7 +358,7 @@ func TestAnyAnyMap_UnmarshalValue(t *testing.T) {
|
||||
"k2": "v2",
|
||||
},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get("k1"), "v1")
|
||||
|
||||
@ -242,11 +242,11 @@ func Test_IntAnyMap_Json(t *testing.T) {
|
||||
2: "v2",
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
m := gmap.NewIntAnyMap()
|
||||
err = json.UnmarshalUseNumber(b, m)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(m.Get(1), data[1])
|
||||
t.Assert(m.Get(2), data[2])
|
||||
})
|
||||
@ -317,7 +317,7 @@ func TestIntAnyMap_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"map": []byte(`{"1":"v1","2":"v2"}`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get(1), "v1")
|
||||
@ -333,7 +333,7 @@ func TestIntAnyMap_UnmarshalValue(t *testing.T) {
|
||||
2: "v2",
|
||||
},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get(1), "v1")
|
||||
|
||||
@ -248,11 +248,11 @@ func Test_IntIntMap_Json(t *testing.T) {
|
||||
2: 20,
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
m := gmap.NewIntIntMap()
|
||||
err = json.UnmarshalUseNumber(b, m)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(m.Get(1), data[1])
|
||||
t.Assert(m.Get(2), data[2])
|
||||
})
|
||||
@ -323,7 +323,7 @@ func TestIntIntMap_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"map": []byte(`{"1":1,"2":2}`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get(1), "1")
|
||||
@ -339,7 +339,7 @@ func TestIntIntMap_UnmarshalValue(t *testing.T) {
|
||||
2: 2,
|
||||
},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get(1), "1")
|
||||
|
||||
@ -246,11 +246,11 @@ func Test_IntStrMap_Json(t *testing.T) {
|
||||
2: "v2",
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
m := gmap.NewIntStrMap()
|
||||
err = json.UnmarshalUseNumber(b, m)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(m.Get(1), data[1])
|
||||
t.Assert(m.Get(2), data[2])
|
||||
})
|
||||
@ -321,7 +321,7 @@ func TestIntStrMap_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"map": []byte(`{"1":"v1","2":"v2"}`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get(1), "v1")
|
||||
@ -337,7 +337,7 @@ func TestIntStrMap_UnmarshalValue(t *testing.T) {
|
||||
2: "v2",
|
||||
},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get(1), "v1")
|
||||
|
||||
@ -240,11 +240,11 @@ func Test_StrAnyMap_Json(t *testing.T) {
|
||||
"k2": "v2",
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
m := gmap.NewStrAnyMap()
|
||||
err = json.UnmarshalUseNumber(b, m)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
@ -254,11 +254,11 @@ func Test_StrAnyMap_Json(t *testing.T) {
|
||||
"k2": "v2",
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
var m gmap.StrAnyMap
|
||||
err = json.UnmarshalUseNumber(b, &m)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
@ -329,7 +329,7 @@ func TestStrAnyMap_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"map": []byte(`{"k1":"v1","k2":"v2"}`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get("k1"), "v1")
|
||||
@ -345,7 +345,7 @@ func TestStrAnyMap_UnmarshalValue(t *testing.T) {
|
||||
"k2": "v2",
|
||||
},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get("k1"), "v1")
|
||||
|
||||
@ -244,11 +244,11 @@ func Test_StrIntMap_Json(t *testing.T) {
|
||||
"k2": 2,
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
m := gmap.NewStrIntMap()
|
||||
err = json.UnmarshalUseNumber(b, m)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
@ -258,11 +258,11 @@ func Test_StrIntMap_Json(t *testing.T) {
|
||||
"k2": 2,
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
var m gmap.StrIntMap
|
||||
err = json.UnmarshalUseNumber(b, &m)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
@ -333,7 +333,7 @@ func TestStrIntMap_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"map": []byte(`{"k1":1,"k2":2}`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get("k1"), 1)
|
||||
@ -349,7 +349,7 @@ func TestStrIntMap_UnmarshalValue(t *testing.T) {
|
||||
"k2": 2,
|
||||
},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get("k1"), 1)
|
||||
|
||||
@ -241,11 +241,11 @@ func Test_StrStrMap_Json(t *testing.T) {
|
||||
"k2": "v2",
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
m := gmap.NewStrStrMap()
|
||||
err = json.UnmarshalUseNumber(b, m)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
@ -255,11 +255,11 @@ func Test_StrStrMap_Json(t *testing.T) {
|
||||
"k2": "v2",
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
var m gmap.StrStrMap
|
||||
err = json.UnmarshalUseNumber(b, &m)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
@ -330,7 +330,7 @@ func TestStrStrMap_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"map": []byte(`{"k1":"v1","k2":"v2"}`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get("k1"), "v1")
|
||||
@ -346,7 +346,7 @@ func TestStrStrMap_UnmarshalValue(t *testing.T) {
|
||||
"k2": "v2",
|
||||
},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get("k1"), "v1")
|
||||
|
||||
@ -186,12 +186,12 @@ func Test_ListMap_Json(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
}
|
||||
m1 := gmap.NewListMapFrom(data)
|
||||
b1, err1 := json.Marshal(m1)
|
||||
t.AssertNil(err1)
|
||||
b2, err2 := json.Marshal(gconv.Map(data))
|
||||
t.Assert(err1, err2)
|
||||
t.AssertNil(err2)
|
||||
t.Assert(b1, b2)
|
||||
})
|
||||
// Unmarshal
|
||||
@ -201,11 +201,11 @@ func Test_ListMap_Json(t *testing.T) {
|
||||
"k2": "v2",
|
||||
}
|
||||
b, err := json.Marshal(gconv.Map(data))
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
m := gmap.NewListMap()
|
||||
err = json.UnmarshalUseNumber(b, m)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
@ -216,16 +216,37 @@ func Test_ListMap_Json(t *testing.T) {
|
||||
"k2": "v2",
|
||||
}
|
||||
b, err := json.Marshal(gconv.Map(data))
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
var m gmap.ListMap
|
||||
err = json.UnmarshalUseNumber(b, &m)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ListMap_Json_Sequence(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewListMap()
|
||||
for i := 'z'; i >= 'a'; i-- {
|
||||
m.Set(string(i), i)
|
||||
}
|
||||
b, err := json.Marshal(m)
|
||||
t.AssertNil(err)
|
||||
t.Assert(b, `{"z":122,"y":121,"x":120,"w":119,"v":118,"u":117,"t":116,"s":115,"r":114,"q":113,"p":112,"o":111,"n":110,"m":109,"l":108,"k":107,"j":106,"i":105,"h":104,"g":103,"f":102,"e":101,"d":100,"c":99,"b":98,"a":97}`)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewListMap()
|
||||
for i := 'a'; i <= 'z'; i++ {
|
||||
m.Set(string(i), i)
|
||||
}
|
||||
b, err := json.Marshal(m)
|
||||
t.AssertNil(err)
|
||||
t.Assert(b, `{"a":97,"b":98,"c":99,"d":100,"e":101,"f":102,"g":103,"h":104,"i":105,"j":106,"k":107,"l":108,"m":109,"n":110,"o":111,"p":112,"q":113,"r":114,"s":115,"t":116,"u":117,"v":118,"w":119,"x":120,"y":121,"z":122}`)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ListMap_Pop(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewListMapFrom(g.MapAnyAny{
|
||||
@ -291,7 +312,7 @@ func TestListMap_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"map": []byte(`{"1":"v1","2":"v2"}`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get("1"), "v1")
|
||||
@ -307,7 +328,7 @@ func TestListMap_UnmarshalValue(t *testing.T) {
|
||||
2: "v2",
|
||||
},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get("1"), "v1")
|
||||
|
||||
@ -185,11 +185,11 @@ func Test_TreeMap_Json(t *testing.T) {
|
||||
"k2": "v2",
|
||||
}
|
||||
b, err := json.Marshal(gconv.Map(data))
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
m := gmap.NewTreeMap(gutil.ComparatorString)
|
||||
err = json.UnmarshalUseNumber(b, m)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
@ -199,11 +199,11 @@ func Test_TreeMap_Json(t *testing.T) {
|
||||
"k2": "v2",
|
||||
}
|
||||
b, err := json.Marshal(gconv.Map(data))
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
var m gmap.TreeMap
|
||||
err = json.UnmarshalUseNumber(b, &m)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
@ -221,7 +221,7 @@ func TestTreeMap_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"map": []byte(`{"k1":"v1","k2":"v2"}`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get("k1"), "v1")
|
||||
@ -237,7 +237,7 @@ func TestTreeMap_UnmarshalValue(t *testing.T) {
|
||||
"k2": "v2",
|
||||
},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get("k1"), "v1")
|
||||
|
||||
@ -109,7 +109,7 @@ func (p *Pool) Get() (interface{}, error) {
|
||||
if f.expireAt == 0 || f.expireAt > gtime.TimestampMilli() {
|
||||
return f.value, nil
|
||||
} else if p.ExpireFunc != nil {
|
||||
// TODO: move expire function calling asynchronously from `Get` operation.
|
||||
// TODO: move expire function calling asynchronously out from `Get` operation.
|
||||
p.ExpireFunc(f.value)
|
||||
}
|
||||
} else {
|
||||
@ -129,7 +129,7 @@ func (p *Pool) Size() int {
|
||||
|
||||
// Close closes the pool. If `p` has ExpireFunc,
|
||||
// then it automatically closes all items using this function before it's closed.
|
||||
// Commonly you do not need call this function manually.
|
||||
// Commonly you do not need to call this function manually.
|
||||
func (p *Pool) Close() {
|
||||
p.closed.Set(true)
|
||||
}
|
||||
|
||||
@ -171,10 +171,13 @@ func (r *Ring) Link(s *Ring) *Ring {
|
||||
//
|
||||
func (r *Ring) Unlink(n int) *Ring {
|
||||
r.mu.Lock()
|
||||
r.ring = r.ring.Unlink(n)
|
||||
resultRing := r.ring.Unlink(n)
|
||||
r.dirty.Set(true)
|
||||
r.mu.Unlock()
|
||||
return r
|
||||
resultGRing := New(resultRing.Len())
|
||||
resultGRing.ring = resultRing
|
||||
resultGRing.dirty.Set(true)
|
||||
return resultGRing
|
||||
}
|
||||
|
||||
// RLockIteratorNext iterates and locks reading forward
|
||||
|
||||
@ -8,6 +8,7 @@ package gring_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gring"
|
||||
)
|
||||
|
||||
@ -209,10 +210,10 @@ func ExampleRing_Unlink() {
|
||||
// Before Unlink, Cap: 10
|
||||
// Before Unlink, [0 9 8 7 6 5 4 3 2 1]
|
||||
// Before Unlink, [0 1 2 3 4 5 6 7 8 9]
|
||||
// After Unlink, Len: 7
|
||||
// After Unlink, Cap: 7
|
||||
// After Unlink, [1 7 6 5 4 3 2]
|
||||
// After Unlink, [1 2 3 4 5 6 7]
|
||||
// After Unlink, Len: 3
|
||||
// After Unlink, Cap: 3
|
||||
// After Unlink, [0 9 8]
|
||||
// After Unlink, [0 8 9]
|
||||
}
|
||||
|
||||
func ExampleRing_RLockIteratorNext() {
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
package gring_test
|
||||
|
||||
import (
|
||||
"container/ring"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gring"
|
||||
@ -137,3 +138,48 @@ func TestRing_Slice(t *testing.T) {
|
||||
t.Assert(array4, g.Slice{1, 5, 4, 3, 2})
|
||||
})
|
||||
}
|
||||
|
||||
// https://github.com/gogf/gf/issues/1394
|
||||
func Test_Issue1394(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
// gring.
|
||||
gRing := gring.New(10)
|
||||
for i := 0; i < 10; i++ {
|
||||
gRing.Put(i)
|
||||
}
|
||||
t.Logf("the length:%d", gRing.Len())
|
||||
gRingResult := gRing.Unlink(6)
|
||||
for i := 0; i < 10; i++ {
|
||||
t.Log(gRing.Val())
|
||||
gRing = gRing.Next()
|
||||
}
|
||||
t.Logf("the ring length:%d", gRing.Len())
|
||||
t.Logf("the result length:%d", gRingResult.Len())
|
||||
|
||||
// stdring
|
||||
stdRing := ring.New(10)
|
||||
for i := 0; i < 10; i++ {
|
||||
stdRing.Value = i
|
||||
stdRing = stdRing.Next()
|
||||
}
|
||||
t.Logf("the length:%d", stdRing.Len())
|
||||
stdRingResult := stdRing.Unlink(6)
|
||||
for i := 0; i < 10; i++ {
|
||||
t.Log(stdRing.Value)
|
||||
stdRing = stdRing.Next()
|
||||
}
|
||||
t.Logf("the ring length:%d", stdRing.Len())
|
||||
t.Logf("the result length:%d", stdRingResult.Len())
|
||||
|
||||
// Assertion.
|
||||
t.Assert(gRing.Len(), stdRing.Len())
|
||||
t.Assert(gRingResult.Len(), stdRingResult.Len())
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
t.Assert(stdRing.Value, gRing.Val())
|
||||
stdRing = stdRing.Next()
|
||||
gRing = gRing.Next()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@ -223,6 +223,9 @@ func (set *Set) Join(glue string) string {
|
||||
|
||||
// String returns items as a string, which implements like json.Marshal does.
|
||||
func (set *Set) String() string {
|
||||
if set == nil {
|
||||
return ""
|
||||
}
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
var (
|
||||
@ -467,7 +470,7 @@ func (set *Set) Walk(f func(item interface{}) interface{}) *Set {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (set *Set) MarshalJSON() ([]byte, error) {
|
||||
func (set Set) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(set.Slice())
|
||||
}
|
||||
|
||||
|
||||
@ -204,6 +204,9 @@ func (set *IntSet) Join(glue string) string {
|
||||
|
||||
// String returns items as a string, which implements like json.Marshal does.
|
||||
func (set *IntSet) String() string {
|
||||
if set == nil {
|
||||
return ""
|
||||
}
|
||||
return "[" + set.Join(",") + "]"
|
||||
}
|
||||
|
||||
@ -426,7 +429,7 @@ func (set *IntSet) Walk(f func(item int) int) *IntSet {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (set *IntSet) MarshalJSON() ([]byte, error) {
|
||||
func (set IntSet) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(set.Slice())
|
||||
}
|
||||
|
||||
|
||||
@ -69,7 +69,7 @@ func (set *StrSet) Add(item ...string) {
|
||||
}
|
||||
|
||||
// AddIfNotExist checks whether item exists in the set,
|
||||
// it adds the item to set and returns true if it does not exists in the set,
|
||||
// it adds the item to set and returns true if it does not exist in the set,
|
||||
// or else it does nothing and returns false.
|
||||
func (set *StrSet) AddIfNotExist(item string) bool {
|
||||
if !set.Contains(item) {
|
||||
@ -218,6 +218,9 @@ func (set *StrSet) Join(glue string) string {
|
||||
|
||||
// String returns items as a string, which implements like json.Marshal does.
|
||||
func (set *StrSet) String() string {
|
||||
if set == nil {
|
||||
return ""
|
||||
}
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
var (
|
||||
@ -454,7 +457,7 @@ func (set *StrSet) Walk(f func(item string) string) *StrSet {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (set *StrSet) MarshalJSON() ([]byte, error) {
|
||||
func (set StrSet) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(set.Slice())
|
||||
}
|
||||
|
||||
|
||||
@ -336,7 +336,7 @@ func TestSet_Json(t *testing.T) {
|
||||
|
||||
var a3 gset.Set
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a3.Contains("a"), true)
|
||||
t.Assert(a3.Contains("b"), true)
|
||||
t.Assert(a3.Contains("c"), true)
|
||||
@ -438,7 +438,7 @@ func TestSet_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"set": []byte(`["k1","k2","k3"]`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Set.Size(), 3)
|
||||
t.Assert(v.Set.Contains("k1"), true)
|
||||
@ -453,7 +453,7 @@ func TestSet_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"set": g.Slice{"k1", "k2", "k3"},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Set.Size(), 3)
|
||||
t.Assert(v.Set.Contains("k1"), true)
|
||||
|
||||
@ -368,7 +368,7 @@ func TestIntSet_Json(t *testing.T) {
|
||||
|
||||
var a3 gset.IntSet
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a2.Contains(1), true)
|
||||
t.Assert(a2.Contains(2), true)
|
||||
t.Assert(a2.Contains(3), true)
|
||||
@ -402,7 +402,7 @@ func TestIntSet_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"set": []byte(`[1,2,3]`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Set.Size(), 3)
|
||||
t.Assert(v.Set.Contains(1), true)
|
||||
@ -417,7 +417,7 @@ func TestIntSet_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"set": g.Slice{1, 2, 3},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Set.Size(), 3)
|
||||
t.Assert(v.Set.Contains(1), true)
|
||||
|
||||
@ -414,7 +414,7 @@ func TestStrSet_Json(t *testing.T) {
|
||||
|
||||
var a3 gset.StrSet
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a3.Contains("a"), true)
|
||||
t.Assert(a3.Contains("b"), true)
|
||||
t.Assert(a3.Contains("c"), true)
|
||||
@ -453,7 +453,7 @@ func TestStrSet_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"set": []byte(`["1","2","3"]`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Set.Size(), 3)
|
||||
t.Assert(v.Set.Contains("1"), true)
|
||||
@ -468,7 +468,7 @@ func TestStrSet_UnmarshalValue(t *testing.T) {
|
||||
"name": "john",
|
||||
"set": g.SliceStr{"1", "2", "3"},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Set.Size(), 3)
|
||||
t.Assert(v.Set.Contains("1"), true)
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
package gtree
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
@ -398,6 +399,9 @@ func (tree *AVLTree) Replace(data map[interface{}]interface{}) {
|
||||
|
||||
// String returns a string representation of container
|
||||
func (tree *AVLTree) String() string {
|
||||
if tree == nil {
|
||||
return ""
|
||||
}
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
str := ""
|
||||
@ -780,8 +784,26 @@ func output(node *AVLTreeNode, prefix string, isTail bool, str *string) {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (tree *AVLTree) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(tree.MapStrAny())
|
||||
func (tree AVLTree) MarshalJSON() (jsonBytes []byte, err error) {
|
||||
if tree.root == nil {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
buffer.WriteByte('{')
|
||||
tree.Iterator(func(key, value interface{}) bool {
|
||||
valueBytes, valueJsonErr := json.Marshal(value)
|
||||
if valueJsonErr != nil {
|
||||
err = valueJsonErr
|
||||
return false
|
||||
}
|
||||
if buffer.Len() > 1 {
|
||||
buffer.WriteByte(',')
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf(`"%v":%s`, key, valueBytes))
|
||||
return true
|
||||
})
|
||||
buffer.WriteByte('}')
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// getComparator returns the comparator if it's previously set,
|
||||
|
||||
@ -367,6 +367,9 @@ func (tree *BTree) Right() *BTreeEntry {
|
||||
|
||||
// String returns a string representation of container (for debugging purposes)
|
||||
func (tree *BTree) String() string {
|
||||
if tree == nil {
|
||||
return ""
|
||||
}
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
var buffer bytes.Buffer
|
||||
@ -575,10 +578,10 @@ func (tree *BTree) output(buffer *bytes.Buffer, node *BTreeNode, level int, isTa
|
||||
}
|
||||
if e < len(node.Entries) {
|
||||
if _, err := buffer.WriteString(strings.Repeat(" ", level)); err != nil {
|
||||
intlog.Error(context.TODO(), err)
|
||||
intlog.Errorf(context.TODO(), `%+v`, err)
|
||||
}
|
||||
if _, err := buffer.WriteString(fmt.Sprintf("%v", node.Entries[e].Key) + "\n"); err != nil {
|
||||
intlog.Error(context.TODO(), err)
|
||||
intlog.Errorf(context.TODO(), `%+v`, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -944,8 +947,26 @@ func (tree *BTree) deleteChild(node *BTreeNode, index int) {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (tree *BTree) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(tree.MapStrAny())
|
||||
func (tree BTree) MarshalJSON() (jsonBytes []byte, err error) {
|
||||
if tree.root == nil {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
buffer.WriteByte('{')
|
||||
tree.Iterator(func(key, value interface{}) bool {
|
||||
valueBytes, valueJsonErr := json.Marshal(value)
|
||||
if valueJsonErr != nil {
|
||||
err = valueJsonErr
|
||||
return false
|
||||
}
|
||||
if buffer.Len() > 1 {
|
||||
buffer.WriteByte(',')
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf(`"%v":%s`, key, valueBytes))
|
||||
return true
|
||||
})
|
||||
buffer.WriteByte('}')
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// getComparator returns the comparator if it's previously set,
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
package gtree
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
@ -622,6 +623,9 @@ func (tree *RedBlackTree) Replace(data map[interface{}]interface{}) {
|
||||
|
||||
// String returns a string representation of container.
|
||||
func (tree *RedBlackTree) String() string {
|
||||
if tree == nil {
|
||||
return ""
|
||||
}
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
str := ""
|
||||
@ -925,8 +929,26 @@ func (tree *RedBlackTree) nodeColor(node *RedBlackTreeNode) color {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (tree *RedBlackTree) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(gconv.Map(tree.Map()))
|
||||
func (tree RedBlackTree) MarshalJSON() (jsonBytes []byte, err error) {
|
||||
if tree.root == nil {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
buffer.WriteByte('{')
|
||||
tree.Iterator(func(key, value interface{}) bool {
|
||||
valueBytes, valueJsonErr := json.Marshal(value)
|
||||
if valueJsonErr != nil {
|
||||
err = valueJsonErr
|
||||
return false
|
||||
}
|
||||
if buffer.Len() > 1 {
|
||||
buffer.WriteByte(',')
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf(`"%v":%s`, key, valueBytes))
|
||||
return true
|
||||
})
|
||||
buffer.WriteByte('}')
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
|
||||
@ -78,7 +78,7 @@ func (v *Bool) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (v *Bool) MarshalJSON() ([]byte, error) {
|
||||
func (v Bool) MarshalJSON() ([]byte, error) {
|
||||
if v.Val() {
|
||||
return bytesTrue, nil
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ func (v *Byte) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (v *Byte) MarshalJSON() ([]byte, error) {
|
||||
func (v Byte) MarshalJSON() ([]byte, error) {
|
||||
return []byte(strconv.FormatUint(uint64(v.Val()), 10)), nil
|
||||
}
|
||||
|
||||
|
||||
@ -57,7 +57,7 @@ func (v *Bytes) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (v *Bytes) MarshalJSON() ([]byte, error) {
|
||||
func (v Bytes) MarshalJSON() ([]byte, error) {
|
||||
val := v.Val()
|
||||
dst := make([]byte, base64.StdEncoding.EncodedLen(len(val)))
|
||||
base64.StdEncoding.Encode(dst, val)
|
||||
|
||||
@ -72,7 +72,7 @@ func (v *Float32) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (v *Float32) MarshalJSON() ([]byte, error) {
|
||||
func (v Float32) MarshalJSON() ([]byte, error) {
|
||||
return []byte(strconv.FormatFloat(float64(v.Val()), 'g', -1, 32)), nil
|
||||
}
|
||||
|
||||
|
||||
@ -72,7 +72,7 @@ func (v *Float64) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (v *Float64) MarshalJSON() ([]byte, error) {
|
||||
func (v Float64) MarshalJSON() ([]byte, error) {
|
||||
return []byte(strconv.FormatFloat(v.Val(), 'g', -1, 64)), nil
|
||||
}
|
||||
|
||||
|
||||
@ -60,7 +60,7 @@ func (v *Int) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (v *Int) MarshalJSON() ([]byte, error) {
|
||||
func (v Int) MarshalJSON() ([]byte, error) {
|
||||
return []byte(strconv.Itoa(v.Val())), nil
|
||||
}
|
||||
|
||||
|
||||
@ -60,7 +60,7 @@ func (v *Int32) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (v *Int32) MarshalJSON() ([]byte, error) {
|
||||
func (v Int32) MarshalJSON() ([]byte, error) {
|
||||
return []byte(strconv.Itoa(int(v.Val()))), nil
|
||||
}
|
||||
|
||||
|
||||
@ -60,7 +60,7 @@ func (v *Int64) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (v *Int64) MarshalJSON() ([]byte, error) {
|
||||
func (v Int64) MarshalJSON() ([]byte, error) {
|
||||
return []byte(strconv.FormatInt(v.Val(), 10)), nil
|
||||
}
|
||||
|
||||
|
||||
@ -52,7 +52,7 @@ func (v *Interface) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (v *Interface) MarshalJSON() ([]byte, error) {
|
||||
func (v Interface) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(v.Val())
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user