mirror of
https://gitee.com/johng/gf
synced 2026-06-07 10:22:11 +08:00
Merge branch 'gogf:master' into master
This commit is contained in:
22
.github/workflows/gf.yml
vendored
22
.github/workflows/gf.yml
vendored
@ -122,16 +122,16 @@ jobs:
|
||||
cd cmd/gf
|
||||
go mod tidy
|
||||
go build ./...
|
||||
GOARCH=386 go test ./... || exit 1
|
||||
GOARCH=amd64 go test ./... -race -coverprofile=coverage.txt -covermode=atomic -coverpkg=./...,github.com/gogf/gf/... || exit 1
|
||||
GOARCH=386 go test -v ./... || exit 1
|
||||
GOARCH=amd64 go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic -coverpkg=./...,github.com/gogf/gf/... || exit 1
|
||||
|
||||
- name: Example Build & Test
|
||||
run: |
|
||||
cd example
|
||||
go mod tidy
|
||||
go build ./...
|
||||
GOARCH=386 go test ./... || exit 1
|
||||
GOARCH=amd64 go test ./... -race -coverprofile=coverage.txt -covermode=atomic -coverpkg=./...,github.com/gogf/gf/... || exit 1
|
||||
GOARCH=386 go test -v ./... || exit 1
|
||||
GOARCH=amd64 go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic -coverpkg=./...,github.com/gogf/gf/... || exit 1
|
||||
|
||||
- name: Contrib Build & Test
|
||||
run: |
|
||||
@ -145,8 +145,8 @@ jobs:
|
||||
cd $dirpath
|
||||
go mod tidy
|
||||
go build ./...
|
||||
GOARCH=386 go test ./... || exit 1
|
||||
GOARCH=amd64 go test ./... -race -coverprofile=coverage.txt -covermode=atomic -coverpkg=./...,github.com/gogf/gf/... || exit 1
|
||||
GOARCH=386 go test -v ./... || exit 1
|
||||
GOARCH=amd64 go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic -coverpkg=./...,github.com/gogf/gf/... || exit 1
|
||||
cd -
|
||||
done
|
||||
|
||||
@ -156,16 +156,6 @@ jobs:
|
||||
GOARCH=386 go test -v ./... || exit 1
|
||||
GOARCH=amd64 go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic -coverpkg=./...,github.com/gogf/gf/... || exit 1
|
||||
|
||||
# - name: Merge Coverage
|
||||
# run: |
|
||||
# for file in `find . -name coverage.txt`; do
|
||||
# # In case of recursively incremental selffile in root directory.
|
||||
# if [ "./coverage.txt" = $file ]; then
|
||||
# continue 1
|
||||
# fi
|
||||
# cat $file >> coverage.txt
|
||||
# done
|
||||
|
||||
- name: Report Coverage
|
||||
uses: codecov/codecov-action@v2
|
||||
with:
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -14,5 +14,4 @@ bin/
|
||||
cbuild
|
||||
**/.DS_Store
|
||||
.test/
|
||||
main
|
||||
gf
|
||||
cmd/gf/main
|
||||
|
||||
@ -3,6 +3,7 @@ package cmd
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
@ -93,8 +94,8 @@ func (c cDocker) Index(ctx context.Context, in cDockerInput) (out *cDockerOutput
|
||||
}
|
||||
|
||||
// Shell executing.
|
||||
if gfile.Exists(in.Shell) {
|
||||
if err = gproc.ShellRun(gfile.GetContents(in.Shell)); err != nil {
|
||||
if in.Shell != "" && gfile.Exists(in.Shell) {
|
||||
if err = c.exeDockerShell(in.Shell); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -150,3 +151,11 @@ func (c cDocker) Index(ctx context.Context, in cDockerInput) (out *cDockerOutput
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c cDocker) exeDockerShell(shellFilePath string) error {
|
||||
if gfile.ExtName(shellFilePath) == "sh" && runtime.GOOS == "windows" {
|
||||
mlog.Debugf(`ignore shell file "%s", as it cannot be run on windows system`, shellFilePath)
|
||||
return nil
|
||||
}
|
||||
return gproc.ShellRun(gfile.GetContents(shellFilePath))
|
||||
}
|
||||
|
||||
@ -22,13 +22,14 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
ContentTypeJson = `json`
|
||||
ContentTypeJs = `js`
|
||||
ContentTypeXml = `xml`
|
||||
ContentTypeIni = `ini`
|
||||
ContentTypeYaml = `yaml`
|
||||
ContentTypeYml = `yml`
|
||||
ContentTypeToml = `toml`
|
||||
ContentTypeJson = `json`
|
||||
ContentTypeJs = `js`
|
||||
ContentTypeXml = `xml`
|
||||
ContentTypeIni = `ini`
|
||||
ContentTypeYaml = `yaml`
|
||||
ContentTypeYml = `yml`
|
||||
ContentTypeToml = `toml`
|
||||
ContentTypeProperties = `properties`
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@ -8,6 +8,7 @@ package gjson
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/encoding/gini"
|
||||
"github.com/gogf/gf/v2/encoding/gproperties"
|
||||
"github.com/gogf/gf/v2/encoding/gtoml"
|
||||
"github.com/gogf/gf/v2/encoding/gxml"
|
||||
"github.com/gogf/gf/v2/encoding/gyaml"
|
||||
@ -197,3 +198,30 @@ func (j *Json) MustToIni() []byte {
|
||||
func (j *Json) MustToIniString() string {
|
||||
return string(j.MustToIni())
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// properties
|
||||
// ========================================================================
|
||||
// Toproperties json to properties
|
||||
func (j *Json) ToProperties() ([]byte, error) {
|
||||
return gproperties.Encode(j.Map())
|
||||
}
|
||||
|
||||
// TopropertiesString properties to string
|
||||
func (j *Json) ToPropertiesString() (string, error) {
|
||||
b, e := j.ToProperties()
|
||||
return string(b), e
|
||||
}
|
||||
|
||||
func (j *Json) MustToProperties() []byte {
|
||||
result, err := j.ToProperties()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// MustTopropertiesString
|
||||
func (j *Json) MustToPropertiesString() string {
|
||||
return string(j.MustToProperties())
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gini"
|
||||
"github.com/gogf/gf/v2/encoding/gproperties"
|
||||
"github.com/gogf/gf/v2/encoding/gtoml"
|
||||
"github.com/gogf/gf/v2/encoding/gxml"
|
||||
"github.com/gogf/gf/v2/encoding/gyaml"
|
||||
@ -174,6 +175,17 @@ func LoadToml(data interface{}, safe ...bool) (*Json, error) {
|
||||
return doLoadContentWithOptions(gconv.Bytes(data), option)
|
||||
}
|
||||
|
||||
// LoadProperties creates a Json object from given TOML format content.
|
||||
func LoadProperties(data interface{}, safe ...bool) (*Json, error) {
|
||||
option := Options{
|
||||
Type: ContentTypeProperties,
|
||||
}
|
||||
if len(safe) > 0 && safe[0] {
|
||||
option.Safe = true
|
||||
}
|
||||
return doLoadContentWithOptions(gconv.Bytes(data), option)
|
||||
}
|
||||
|
||||
// LoadContent creates a Json object from given content, it checks the data type of `content`
|
||||
// automatically, supporting data content type as follows:
|
||||
// JSON, XML, INI, YAML and TOML.
|
||||
@ -222,7 +234,8 @@ func IsValidDataType(dataType string) bool {
|
||||
ContentTypeYaml,
|
||||
ContentTypeYml,
|
||||
ContentTypeToml,
|
||||
ContentTypeIni:
|
||||
ContentTypeIni,
|
||||
ContentTypeProperties:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@ -288,6 +301,10 @@ func doLoadContentWithOptions(data []byte, options Options) (*Json, error) {
|
||||
if data, err = gini.ToJson(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case ContentTypeProperties:
|
||||
if data, err = gproperties.ToJson(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
default:
|
||||
err = gerror.NewCodef(
|
||||
@ -335,6 +352,8 @@ func checkDataType(content []byte) string {
|
||||
(gregex.IsMatch(`[\n\r]*[\s\t\w\-\."]+\s*=\s*".+"`, content) || gregex.IsMatch(`[\n\r]*[\s\t\w\-\."]+\s*=\s*\w+`, content)) {
|
||||
// Must contain "[xxx]" section.
|
||||
return ContentTypeIni
|
||||
} else if gregex.IsMatch(`[\n\r]*[\s\t\w\-\."]+\s*=\s*\w+`, content) {
|
||||
return ContentTypeProperties
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
|
||||
@ -182,6 +182,7 @@ func ExampleIsValidDataType() {
|
||||
fmt.Println(gjson.IsValidDataType("txt"))
|
||||
fmt.Println(gjson.IsValidDataType(""))
|
||||
fmt.Println(gjson.IsValidDataType(".json"))
|
||||
fmt.Println(gjson.IsValidDataType(".properties"))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
@ -192,6 +193,7 @@ func ExampleIsValidDataType() {
|
||||
// false
|
||||
// false
|
||||
// true
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleLoad_Xml() {
|
||||
@ -200,3 +202,16 @@ func ExampleLoad_Xml() {
|
||||
fmt.Println(j.Get("doc.name"))
|
||||
fmt.Println(j.Get("doc.score"))
|
||||
}
|
||||
|
||||
func ExampleLoad_Properties() {
|
||||
jsonFilePath := gtest.DataPath("properties", "data1.properties")
|
||||
j, _ := gjson.Load(jsonFilePath)
|
||||
fmt.Println(j.Get("pr.name"))
|
||||
fmt.Println(j.Get("pr.score"))
|
||||
fmt.Println(j.Get("pr.sex"))
|
||||
|
||||
//Output:
|
||||
// john
|
||||
// 100
|
||||
// 0
|
||||
}
|
||||
|
||||
@ -641,6 +641,80 @@ func ExampleJson_MustToIniString() {
|
||||
//Name=John
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Properties
|
||||
// ========================================================================
|
||||
func ExampleJson_ToProperties() {
|
||||
type BaseInfo struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
info := BaseInfo{
|
||||
Name: "John",
|
||||
Age: 18,
|
||||
}
|
||||
|
||||
j := gjson.New(info)
|
||||
pr, _ := j.ToProperties()
|
||||
fmt.Println(string(pr))
|
||||
|
||||
// May Output:
|
||||
// name = John
|
||||
// age = 18
|
||||
}
|
||||
|
||||
func ExampleJson_ToPropertiesString() {
|
||||
type BaseInfo struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
info := BaseInfo{
|
||||
Name: "John",
|
||||
}
|
||||
|
||||
j := gjson.New(info)
|
||||
pr, _ := j.ToPropertiesString()
|
||||
fmt.Println(pr)
|
||||
|
||||
// Output:
|
||||
// name = John
|
||||
}
|
||||
|
||||
func ExampleJson_MustToProperties() {
|
||||
type BaseInfo struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
info := BaseInfo{
|
||||
Name: "John",
|
||||
}
|
||||
|
||||
j := gjson.New(info)
|
||||
pr := j.MustToProperties()
|
||||
fmt.Println(string(pr))
|
||||
|
||||
// Output:
|
||||
// name = John
|
||||
}
|
||||
|
||||
func ExampleJson_MustToPropertiesString() {
|
||||
type BaseInfo struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
info := BaseInfo{
|
||||
Name: "John",
|
||||
}
|
||||
|
||||
j := gjson.New(info)
|
||||
pr := j.MustToPropertiesString()
|
||||
fmt.Println(pr)
|
||||
|
||||
// Output:
|
||||
// name = John
|
||||
}
|
||||
|
||||
func ExampleJson_MarshalJSON() {
|
||||
type BaseInfo struct {
|
||||
Name string
|
||||
|
||||
@ -361,3 +361,58 @@ gfcli:
|
||||
t.AssertNil(err)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Load_Properties(t *testing.T) {
|
||||
var data = `
|
||||
|
||||
#注释
|
||||
|
||||
|
||||
addr.ip = 127.0.0.1
|
||||
addr.port=9001
|
||||
addr.enable=true
|
||||
DBINFO.type=mysql
|
||||
DBINFO.user=root
|
||||
DBINFO.password=password
|
||||
|
||||
`
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
j, err := gjson.LoadContent(data)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
|
||||
t.Assert(j.Get("addr.ip").String(), "127.0.0.1")
|
||||
t.Assert(j.Get("addr.port").String(), "9001")
|
||||
t.Assert(j.Get("addr.enable").String(), "true")
|
||||
t.Assert(j.Get("DBINFO.type").String(), "mysql")
|
||||
t.Assert(j.Get("DBINFO.user").String(), "root")
|
||||
t.Assert(j.Get("DBINFO.password").String(), "password")
|
||||
|
||||
_, err = j.ToProperties()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
j, err := gjson.LoadProperties(data, true)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
|
||||
t.Assert(j.Get("addr.ip").String(), "127.0.0.1")
|
||||
t.Assert(j.Get("addr.port").String(), "9001")
|
||||
t.Assert(j.Get("addr.enable").String(), "true")
|
||||
t.Assert(j.Get("DBINFO.type").String(), "mysql")
|
||||
t.Assert(j.Get("DBINFO.user").String(), "root")
|
||||
t.Assert(j.Get("DBINFO.password").String(), "password")
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
errData := []byte("i\\u1 : 123456789")
|
||||
_, err := gjson.LoadContentType("properties", errData, true)
|
||||
t.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
3
encoding/gjson/testdata/properties/data1.properties
vendored
Normal file
3
encoding/gjson/testdata/properties/data1.properties
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
pr.name=john
|
||||
pr.score=100
|
||||
pr.sex=0
|
||||
137
encoding/gproperties/gproperties.go
Normal file
137
encoding/gproperties/gproperties.go
Normal file
@ -0,0 +1,137 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gproperties provides accessing and converting for .properties content.
|
||||
package gproperties
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/magiconair/properties"
|
||||
)
|
||||
|
||||
// Decode converts properties format to map.
|
||||
func Decode(data []byte) (res map[string]interface{}, err error) {
|
||||
res = make(map[string]interface{})
|
||||
pr, err := properties.Load(data, properties.UTF8)
|
||||
if err != nil || pr == nil {
|
||||
err = gerror.Wrapf(err, `Lib magiconair load Properties data failed.`)
|
||||
return nil, err
|
||||
}
|
||||
for _, key := range pr.Keys() {
|
||||
// ignore existence check: we know it's there
|
||||
value, _ := pr.Get(key)
|
||||
// recursively build nested maps
|
||||
path := strings.Split(key, ".")
|
||||
lastKey := strings.ToLower(path[len(path)-1])
|
||||
deepestMap := deepSearch(res, path[0:len(path)-1])
|
||||
|
||||
// set innermost value
|
||||
deepestMap[lastKey] = value
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Encode converts map to properties format.
|
||||
func Encode(data map[string]interface{}) (res []byte, err error) {
|
||||
pr := properties.NewProperties()
|
||||
|
||||
flattened := map[string]interface{}{}
|
||||
|
||||
flattened = flattenAndMergeMap(flattened, data, "", ".")
|
||||
|
||||
keys := make([]string, 0, len(flattened))
|
||||
|
||||
for key := range flattened {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, key := range keys {
|
||||
_, _, err := pr.Set(key, gconv.String(flattened[key]))
|
||||
if err != nil {
|
||||
err = gerror.Wrapf(err, `Sets the property key to the corresponding value failed.`)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
_, err = pr.Write(&buf, properties.UTF8)
|
||||
if err != nil {
|
||||
err = gerror.Wrapf(err, `Properties Write buf failed.`)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// ToJson convert .properties format to JSON.
|
||||
func ToJson(data []byte) (res []byte, err error) {
|
||||
prMap, err := Decode(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(prMap)
|
||||
}
|
||||
|
||||
// deepSearch scans deep maps, following the key indexes listed in the sequence "path".
|
||||
// The last value is expected to be another map, and is returned.
|
||||
func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
|
||||
for _, k := range path {
|
||||
m2, ok := m[k]
|
||||
if !ok {
|
||||
// intermediate key does not exist
|
||||
// => create it and continue from there
|
||||
m3 := make(map[string]interface{})
|
||||
m[k] = m3
|
||||
m = m3
|
||||
continue
|
||||
}
|
||||
m3, ok := m2.(map[string]interface{})
|
||||
if !ok {
|
||||
m3 = make(map[string]interface{})
|
||||
m[k] = m3
|
||||
}
|
||||
// continue search from here
|
||||
m = m3
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// flattenAndMergeMap recursively flattens the given map into a new map
|
||||
func flattenAndMergeMap(shadow map[string]interface{}, m map[string]interface{}, prefix string, delimiter string) map[string]interface{} {
|
||||
if shadow != nil && prefix != "" && shadow[prefix] != nil {
|
||||
return shadow
|
||||
}
|
||||
|
||||
var m2 map[string]interface{}
|
||||
if prefix != "" {
|
||||
prefix += delimiter
|
||||
}
|
||||
for k, val := range m {
|
||||
fullKey := prefix + k
|
||||
switch val.(type) {
|
||||
case map[string]interface{}:
|
||||
m2 = val.(map[string]interface{})
|
||||
case map[interface{}]interface{}:
|
||||
m2 = gconv.Map(val)
|
||||
default:
|
||||
// immediate value
|
||||
shadow[strings.ToLower(fullKey)] = val
|
||||
continue
|
||||
}
|
||||
// recursively merge to shadow map
|
||||
shadow = flattenAndMergeMap(shadow, m2, fullKey, delimiter)
|
||||
}
|
||||
return shadow
|
||||
}
|
||||
132
encoding/gproperties/gproperties_z_unit_test.go
Normal file
132
encoding/gproperties/gproperties_z_unit_test.go
Normal file
@ -0,0 +1,132 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gproperties_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/encoding/gproperties"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
)
|
||||
|
||||
var pStr string = `
|
||||
# 模板引擎目录
|
||||
viewpath = "/home/www/templates/"
|
||||
# redis数据库配置
|
||||
redis.disk = "127.0.0.1:6379,0"
|
||||
redis.cache = "127.0.0.1:6379,1"
|
||||
#SQL配置
|
||||
sql.mysql.0.type = mysql
|
||||
sql.mysql.0.ip = 127.0.0.1
|
||||
sql.mysql.0.user = root
|
||||
`
|
||||
var errorTests = []struct {
|
||||
input, msg string
|
||||
}{
|
||||
// unicode literals
|
||||
{"key\\u1 = value", "invalid unicode literal"},
|
||||
{"key\\u12 = value", "invalid unicode literal"},
|
||||
{"key\\u123 = value", "invalid unicode literal"},
|
||||
{"key\\u123g = value", "invalid unicode literal"},
|
||||
{"key\\u123", "invalid unicode literal"},
|
||||
|
||||
// circular references
|
||||
{"key=${key}", `circular reference in:\nkey=\$\{key\}`},
|
||||
{"key1=${key2}\nkey2=${key1}", `circular reference in:\n(key1=\$\{key2\}\nkey2=\$\{key1\}|key2=\$\{key1\}\nkey1=\$\{key2\})`},
|
||||
|
||||
// malformed expressions
|
||||
{"key=${ke", "malformed expression"},
|
||||
{"key=valu${ke", "malformed expression"},
|
||||
}
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := make(map[string]interface{})
|
||||
m["properties"] = pStr
|
||||
res, err := gproperties.Encode(m)
|
||||
if err != nil {
|
||||
t.Errorf("encode failed. %v", err)
|
||||
return
|
||||
}
|
||||
decodeMap, err := gproperties.Decode(res)
|
||||
if err != nil {
|
||||
t.Errorf("decode failed. %v", err)
|
||||
return
|
||||
}
|
||||
t.Assert(decodeMap["properties"], pStr)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
for _, v := range errorTests {
|
||||
_, err := gproperties.Decode(([]byte)(v.input))
|
||||
if err == nil {
|
||||
t.Errorf("encode should be failed. %v", err)
|
||||
return
|
||||
}
|
||||
t.AssertIN(`Lib magiconair load Properties data failed.`, strings.Split(err.Error(), ":"))
|
||||
}
|
||||
})
|
||||
}
|
||||
func TestEncode(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := make(map[string]interface{})
|
||||
m["properties"] = pStr
|
||||
res, err := gproperties.Encode(m)
|
||||
if err != nil {
|
||||
t.Errorf("encode failed. %v", err)
|
||||
return
|
||||
}
|
||||
decodeMap, err := gproperties.Decode(res)
|
||||
if err != nil {
|
||||
t.Errorf("decode failed. %v", err)
|
||||
return
|
||||
}
|
||||
t.Assert(decodeMap["properties"], pStr)
|
||||
})
|
||||
}
|
||||
|
||||
func TestToJson(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
res, err := gproperties.Encode(map[string]interface{}{
|
||||
"sql": g.Map{
|
||||
"userName": "admin",
|
||||
"password": "123456",
|
||||
},
|
||||
"user": "admin",
|
||||
"no": 123,
|
||||
})
|
||||
fmt.Print(string(res))
|
||||
jsonPr, err := gproperties.ToJson(res)
|
||||
if err != nil {
|
||||
t.Errorf("ToJson failed. %v", err)
|
||||
return
|
||||
}
|
||||
fmt.Print(string(jsonPr))
|
||||
|
||||
p := gjson.New(res)
|
||||
expectJson, err := p.ToJson()
|
||||
if err != nil {
|
||||
t.Errorf("parser ToJson failed. %v", err)
|
||||
return
|
||||
}
|
||||
t.Assert(jsonPr, expectJson)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
for _, v := range errorTests {
|
||||
_, err := gproperties.ToJson(([]byte)(v.input))
|
||||
if err == nil {
|
||||
t.Errorf("encode should be failed. %v", err)
|
||||
return
|
||||
}
|
||||
t.AssertIN(`Lib magiconair load Properties data failed.`, strings.Split(err.Error(), ":"))
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -13,10 +13,8 @@ import (
|
||||
|
||||
"github.com/gogf/gf/v2/os/gcfg"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gres"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/gutil"
|
||||
)
|
||||
|
||||
func Test_View(t *testing.T) {
|
||||
@ -60,12 +58,6 @@ func Test_View_Config(t *testing.T) {
|
||||
defer Config().GetAdapter().(*gcfg.AdapterFile).ClearContent()
|
||||
defer localInstances.Clear()
|
||||
|
||||
gres.Dump()
|
||||
gutil.Dump(Config().GetAdapter().(*gcfg.AdapterFile).GetPaths())
|
||||
gutil.Dump(Config().GetAdapter().(*gcfg.AdapterFile).GetFileName())
|
||||
gutil.Dump(Config().GetAdapter().(*gcfg.AdapterFile).GetContent())
|
||||
gutil.Dump(Config().Data(ctx))
|
||||
|
||||
view := View("test1")
|
||||
t.AssertNE(view, nil)
|
||||
err := view.AddPath(dirPath)
|
||||
|
||||
2
go.mod
2
go.mod
@ -10,6 +10,8 @@ require (
|
||||
github.com/go-redis/redis/v8 v8.11.4
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/grokify/html-strip-tags-go v0.0.1
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/magiconair/properties v1.8.6
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
go.opentelemetry.io/otel v1.7.0
|
||||
go.opentelemetry.io/otel/sdk v1.7.0
|
||||
|
||||
16
go.sum
16
go.sum
@ -4,6 +4,7 @@ github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cb
|
||||
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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
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=
|
||||
@ -45,6 +46,15 @@ github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad
|
||||
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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
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=
|
||||
@ -67,6 +77,8 @@ 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/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
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.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
@ -139,8 +151,10 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
||||
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/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
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=
|
||||
|
||||
@ -10,12 +10,13 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
_ "github.com/gogf/gf/v2/os/gres/testdata/data"
|
||||
|
||||
"github.com/gogf/gf/v2/debug/gdebug"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/i18n/gi18n"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gres"
|
||||
_ "github.com/gogf/gf/v2/os/gres/testdata/data"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
@ -133,6 +134,7 @@ func Test_Instance(t *testing.T) {
|
||||
// Default language is: en
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gi18n.Instance(gconv.String(gtime.TimestampNano()))
|
||||
m.SetPath("i18n-dir")
|
||||
t.Assert(m.T(context.Background(), "{#hello}{#world}"), "HelloWorld")
|
||||
})
|
||||
}
|
||||
|
||||
@ -71,9 +71,8 @@ func (r *Response) ReadAllString() string {
|
||||
|
||||
// Close closes the response when it will never be used.
|
||||
func (r *Response) Close() error {
|
||||
if r == nil || r.Response == nil || r.Response.Close {
|
||||
if r == nil || r.Response == nil {
|
||||
return nil
|
||||
}
|
||||
r.Response.Close = true
|
||||
return r.Response.Body.Close()
|
||||
}
|
||||
|
||||
@ -9,6 +9,9 @@ package gclient_test
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/net/gclient"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
@ -98,6 +101,49 @@ func init() {
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
}
|
||||
|
||||
func ExampleNew() {
|
||||
var (
|
||||
ctx = gctx.New()
|
||||
client = gclient.New()
|
||||
)
|
||||
|
||||
if r, err := client.Get(ctx, "http://127.0.0.1:8999/var/json"); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
defer r.Close()
|
||||
fmt.Println(r.ReadAllString())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {"id":1,"name":"john"}
|
||||
}
|
||||
|
||||
func ExampleNew_MultiConn_Recommend() {
|
||||
var (
|
||||
ctx = gctx.New()
|
||||
client = g.Client()
|
||||
)
|
||||
|
||||
// controls the maximum idle(keep-alive) connections to keep per-host
|
||||
client.Transport.(*http.Transport).MaxIdleConnsPerHost = 5
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
if r, err := client.Get(ctx, "http://127.0.0.1:8999/var/json"); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
fmt.Println(r.ReadAllString())
|
||||
r.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
//{"id":1,"name":"john"}
|
||||
//{"id":1,"name":"john"}
|
||||
//{"id":1,"name":"john"}
|
||||
//{"id":1,"name":"john"}
|
||||
//{"id":1,"name":"john"}
|
||||
}
|
||||
|
||||
func ExampleClient_Header() {
|
||||
var (
|
||||
url = "http://127.0.0.1:8999/header"
|
||||
|
||||
@ -8,7 +8,6 @@ package gcfg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
@ -207,10 +206,12 @@ func (c *AdapterFile) Available(ctx context.Context, fileName ...string) bool {
|
||||
} else {
|
||||
usedFileName = c.defaultName
|
||||
}
|
||||
if path, _ := c.GetFilePath(usedFileName); path != "" {
|
||||
// Custom configuration content exists.
|
||||
if c.GetContent(usedFileName) != "" {
|
||||
return true
|
||||
}
|
||||
if c.GetContent(usedFileName) != "" {
|
||||
// Configuration file exists in system path.
|
||||
if path, _ := c.GetFilePath(usedFileName); path != "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@ -258,7 +259,6 @@ func (c *AdapterFile) getJson(fileName ...string) (configJson *gjson.Json, err e
|
||||
return nil
|
||||
}
|
||||
if file := gres.Get(filePath); file != nil {
|
||||
fmt.Println("retrieve config content from gres:", filePath)
|
||||
content = string(file.Content())
|
||||
} else {
|
||||
content = gfile.GetContents(filePath)
|
||||
|
||||
@ -28,6 +28,7 @@ type Command struct {
|
||||
Examples string // Usage examples.
|
||||
Additional string // Additional info about this command, which will be appended to the end of help info.
|
||||
Strict bool // Strict parsing options, which means it returns error if invalid option given.
|
||||
CaseSensitive bool // CaseSensitive parsing options, which means it parses input options in case-sensitive way.
|
||||
Config string // Config node name, which also retrieves the values from config component along with command line.
|
||||
parent *Command // Parent command for internal usage.
|
||||
commands []*Command // Sub commands of this command.
|
||||
|
||||
@ -9,11 +9,14 @@ package gcmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/intlog"
|
||||
"github.com/gogf/gf/v2/internal/reflection"
|
||||
"github.com/gogf/gf/v2/internal/utils"
|
||||
"github.com/gogf/gf/v2/os/gstructs"
|
||||
@ -285,11 +288,17 @@ func newCommandFromMethod(object interface{}, method reflect.Value) (command *Co
|
||||
}
|
||||
// Construct input parameters.
|
||||
if len(data) > 0 {
|
||||
intlog.PrintFunc(ctx, func() string {
|
||||
return fmt.Sprintf(`input command data map: %s`, gjson.MustEncode(data))
|
||||
})
|
||||
if inputObject.Kind() == reflect.Ptr {
|
||||
err = gconv.Scan(data, inputObject.Interface())
|
||||
} else {
|
||||
err = gconv.Struct(data, inputObject.Addr().Interface())
|
||||
}
|
||||
intlog.PrintFunc(ctx, func() string {
|
||||
return fmt.Sprintf(`input object assigned data: %s`, gjson.MustEncode(inputObject.Interface()))
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -146,7 +146,10 @@ func (c *Command) reParse(ctx context.Context, parser *Parser) (*Parser, error)
|
||||
}
|
||||
supportedOptions[optionKey] = !arg.Orphan
|
||||
}
|
||||
parser, err := Parse(supportedOptions, c.Strict)
|
||||
parser, err := Parse(supportedOptions, ParserOption{
|
||||
CaseSensitive: c.CaseSensitive,
|
||||
Strict: c.Strict,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -21,9 +21,15 @@ import (
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
// ParserOption manages the parsing options.
|
||||
type ParserOption struct {
|
||||
CaseSensitive bool // Marks options parsing in case-sensitive way.
|
||||
Strict bool // Whether stops parsing and returns error if invalid option passed.
|
||||
}
|
||||
|
||||
// Parser for arguments.
|
||||
type Parser struct {
|
||||
strict bool // Whether stops parsing and returns error if invalid option passed.
|
||||
option ParserOption // Parse option.
|
||||
parsedArgs []string // As name described.
|
||||
parsedOptions map[string]string // As name described.
|
||||
passedOptions map[string]bool // User passed supported options, like: map[string]bool{"name,n":true}
|
||||
@ -47,7 +53,7 @@ func ParserFromCtx(ctx context.Context) *Parser {
|
||||
// the value item of `supportedOptions` indicates whether corresponding option name needs argument or not.
|
||||
//
|
||||
// The optional parameter `strict` specifies whether stops parsing and returns error if invalid option passed.
|
||||
func Parse(supportedOptions map[string]bool, strict ...bool) (*Parser, error) {
|
||||
func Parse(supportedOptions map[string]bool, option ...ParserOption) (*Parser, error) {
|
||||
if supportedOptions == nil {
|
||||
command.Init(os.Args...)
|
||||
return &Parser{
|
||||
@ -55,7 +61,7 @@ func Parse(supportedOptions map[string]bool, strict ...bool) (*Parser, error) {
|
||||
parsedOptions: GetOptAll(),
|
||||
}, nil
|
||||
}
|
||||
return ParseArgs(os.Args, supportedOptions, strict...)
|
||||
return ParseArgs(os.Args, supportedOptions, option...)
|
||||
}
|
||||
|
||||
// ParseArgs creates and returns a new Parser with given arguments and supported options.
|
||||
@ -64,7 +70,7 @@ func Parse(supportedOptions map[string]bool, strict ...bool) (*Parser, error) {
|
||||
// the value item of `supportedOptions` indicates whether corresponding option name needs argument or not.
|
||||
//
|
||||
// The optional parameter `strict` specifies whether stops parsing and returns error if invalid option passed.
|
||||
func ParseArgs(args []string, supportedOptions map[string]bool, strict ...bool) (*Parser, error) {
|
||||
func ParseArgs(args []string, supportedOptions map[string]bool, option ...ParserOption) (*Parser, error) {
|
||||
if supportedOptions == nil {
|
||||
command.Init(args...)
|
||||
return &Parser{
|
||||
@ -72,12 +78,12 @@ func ParseArgs(args []string, supportedOptions map[string]bool, strict ...bool)
|
||||
parsedOptions: GetOptAll(),
|
||||
}, nil
|
||||
}
|
||||
strictParsing := false
|
||||
if len(strict) > 0 {
|
||||
strictParsing = strict[0]
|
||||
var parserOption ParserOption
|
||||
if len(option) > 0 {
|
||||
parserOption = option[0]
|
||||
}
|
||||
parser := &Parser{
|
||||
strict: strictParsing,
|
||||
option: parserOption,
|
||||
parsedArgs: make([]string, 0),
|
||||
parsedOptions: make(map[string]string),
|
||||
passedOptions: supportedOptions,
|
||||
@ -118,7 +124,7 @@ func ParseArgs(args []string, supportedOptions map[string]bool, strict ...bool)
|
||||
}
|
||||
i++
|
||||
continue
|
||||
} else if parser.strict {
|
||||
} else if parser.option.Strict {
|
||||
return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `invalid option '%s'`, args[i])
|
||||
}
|
||||
}
|
||||
@ -159,8 +165,18 @@ func (p *Parser) parseOption(argument string) string {
|
||||
}
|
||||
|
||||
func (p *Parser) isOptionValid(name string) bool {
|
||||
_, ok := p.supportedOptions[name]
|
||||
return ok
|
||||
// Case-Sensitive.
|
||||
if p.option.CaseSensitive {
|
||||
_, ok := p.supportedOptions[name]
|
||||
return ok
|
||||
}
|
||||
// Case-InSensitive.
|
||||
for optionName, _ := range p.supportedOptions {
|
||||
if gstr.Equal(optionName, name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Parser) isOptionNeedArgument(name string) bool {
|
||||
|
||||
50
os/gcmd/gcmd_z_unit_feature_object3_test.go
Normal file
50
os/gcmd/gcmd_z_unit_feature_object3_test.go
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gcmd_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
)
|
||||
|
||||
type TestParamsCase struct {
|
||||
g.Meta `name:"root" root:"root"`
|
||||
}
|
||||
|
||||
type TestParamsCaseRootInput struct {
|
||||
g.Meta `name:"root"`
|
||||
Name string
|
||||
}
|
||||
type TestParamsCaseRootOutput struct {
|
||||
Content string
|
||||
}
|
||||
|
||||
func (c *TestParamsCase) Root(ctx context.Context, in TestParamsCaseRootInput) (out *TestParamsCaseRootOutput, err error) {
|
||||
out = &TestParamsCaseRootOutput{
|
||||
Content: in.Name,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Test_Command_ParamsCase(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var ctx = gctx.New()
|
||||
cmd, err := gcmd.NewFromObject(TestParamsCase{})
|
||||
t.AssertNil(err)
|
||||
|
||||
os.Args = []string{"root", "-name=john"}
|
||||
value, err := cmd.RunWithValueError(ctx)
|
||||
t.AssertNil(err)
|
||||
t.Assert(value, `{"Content":"john"}`)
|
||||
})
|
||||
}
|
||||
@ -19,7 +19,7 @@ import (
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
)
|
||||
|
||||
func Test_PackToGoFile(t *testing.T) {
|
||||
func Test_PackFolderToGoFile(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
srcPath = gtest.DataPath("files")
|
||||
@ -32,6 +32,24 @@ func Test_PackToGoFile(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_PackMultiFilesToGoFile(t *testing.T) {
|
||||
gres.Dump()
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
srcPath = gtest.DataPath("files")
|
||||
goFilePath = gfile.Temp(gtime.TimestampNanoStr(), "data.go")
|
||||
pkgName = "data"
|
||||
array, err = gfile.ScanDir(srcPath, "*", false)
|
||||
)
|
||||
t.AssertNil(err)
|
||||
err = gres.PackToGoFile(strings.Join(array, ","), goFilePath, pkgName)
|
||||
t.AssertNil(err)
|
||||
defer gfile.Remove(goFilePath)
|
||||
|
||||
t.AssertNil(gfile.CopyFile(goFilePath, gtest.DataPath("data/data.go")))
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Pack(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
@ -65,21 +83,6 @@ func Test_PackToFile(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_PackMulti(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
srcPath = gtest.DataPath("files")
|
||||
goFilePath = gfile.Temp(gtime.TimestampNanoStr(), "data.go")
|
||||
pkgName = "data"
|
||||
array, err = gfile.ScanDir(srcPath, "*", false)
|
||||
)
|
||||
t.AssertNil(err)
|
||||
err = gres.PackToGoFile(strings.Join(array, ","), goFilePath, pkgName)
|
||||
t.AssertNil(err)
|
||||
_ = gfile.Remove(goFilePath)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_PackWithPrefix1(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
@ -235,7 +238,7 @@ func Test_Export(t *testing.T) {
|
||||
gres.Dump()
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
src = `template`
|
||||
src = `template-res`
|
||||
dst = gfile.Temp(gtime.TimestampNanoStr())
|
||||
err = gres.Export(src, dst)
|
||||
)
|
||||
@ -245,15 +248,15 @@ func Test_Export(t *testing.T) {
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(files), 14)
|
||||
|
||||
name := `template/index.html`
|
||||
name := `template-res/index.html`
|
||||
t.Assert(gfile.GetContents(gfile.Join(dst, name)), gres.GetContent(name))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
src = `template`
|
||||
src = `template-res`
|
||||
dst = gfile.Temp(gtime.TimestampNanoStr())
|
||||
err = gres.Export(src, dst, gres.ExportOption{
|
||||
RemovePrefix: `template`,
|
||||
RemovePrefix: `template-res`,
|
||||
})
|
||||
)
|
||||
defer gfile.Remove(dst)
|
||||
@ -262,7 +265,7 @@ func Test_Export(t *testing.T) {
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(files), 13)
|
||||
|
||||
nameInRes := `template/index.html`
|
||||
nameInRes := `template-res/index.html`
|
||||
nameInSys := `index.html`
|
||||
t.Assert(gfile.GetContents(gfile.Join(dst, nameInSys)), gres.GetContent(nameInRes))
|
||||
})
|
||||
|
||||
2
os/gres/testdata/data/data.go
vendored
Normal file → Executable file
2
os/gres/testdata/data/data.go
vendored
Normal file → Executable file
File diff suppressed because one or more lines are too long
@ -82,8 +82,8 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
patternKeyForRequired = `required`
|
||||
patternKeyForIn = `in:`
|
||||
validationRuleKeyForRequired = `required`
|
||||
validationRuleKeyForIn = `in:`
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@ -80,8 +80,9 @@ func (oai *OpenApiV3) newParameterRefWithStructMethod(field gstructs.Field, path
|
||||
}
|
||||
|
||||
// Required check.
|
||||
if parameter.Schema.Value != nil && parameter.Schema.Value.Pattern != "" {
|
||||
if gset.NewStrSetFrom(gstr.Split(parameter.Schema.Value.Pattern, "|")).Contains(patternKeyForRequired) {
|
||||
if parameter.Schema.Value != nil && parameter.Schema.Value.ValidationRules != "" {
|
||||
validationRuleArray := gstr.Split(parameter.Schema.Value.ValidationRules, "|")
|
||||
if gset.NewStrSetFrom(validationRuleArray).Contains(validationRuleKeyForRequired) {
|
||||
parameter.Required = true
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,6 +60,7 @@ type Schema struct {
|
||||
AdditionalProperties *SchemaRef `json:"additionalProperties,omitempty"`
|
||||
Discriminator *Discriminator `json:"discriminator,omitempty"`
|
||||
XExtensions XExtensions `json:"-"`
|
||||
ValidationRules string `json:"-"`
|
||||
}
|
||||
|
||||
func (s Schema) MarshalJSON() ([]byte, error) {
|
||||
@ -183,9 +184,9 @@ func (oai *OpenApiV3) structToSchema(object interface{}) (*Schema, error) {
|
||||
}
|
||||
|
||||
schema.Properties.Iterator(func(key string, ref SchemaRef) bool {
|
||||
if ref.Value != nil && ref.Value.Pattern != "" {
|
||||
validationRuleSet := gset.NewStrSetFrom(gstr.Split(ref.Value.Pattern, "|"))
|
||||
if validationRuleSet.Contains(patternKeyForRequired) {
|
||||
if ref.Value != nil && ref.Value.ValidationRules != "" {
|
||||
validationRuleSet := gset.NewStrSetFrom(gstr.Split(ref.Value.ValidationRules, "|"))
|
||||
if validationRuleSet.Contains(validationRuleKeyForRequired) {
|
||||
schema.Required = append(schema.Required, key)
|
||||
}
|
||||
}
|
||||
@ -212,14 +213,14 @@ func (oai *OpenApiV3) tagMapToSchema(tagMap map[string]string, schema *Schema) e
|
||||
for _, tag := range gvalid.GetTags() {
|
||||
if validationTagValue, ok := tagMap[tag]; ok {
|
||||
_, validationRules, _ := gvalid.ParseTagValue(validationTagValue)
|
||||
schema.Pattern = validationRules
|
||||
schema.ValidationRules = validationRules
|
||||
// Enum checks.
|
||||
if len(schema.Enum) == 0 {
|
||||
for _, rule := range gstr.SplitAndTrim(validationRules, "|") {
|
||||
if gstr.HasPrefix(rule, patternKeyForIn) {
|
||||
if gstr.HasPrefix(rule, validationRuleKeyForIn) {
|
||||
var (
|
||||
isAllEnumNumber = true
|
||||
enumArray = gstr.SplitAndTrim(rule[len(patternKeyForIn):], ",")
|
||||
enumArray = gstr.SplitAndTrim(rule[len(validationRuleKeyForIn):], ",")
|
||||
)
|
||||
for _, enum := range enumArray {
|
||||
if !gstr.IsNumeric(enum) {
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
// XExtensions stores the `x-` custom extensions.
|
||||
type XExtensions map[string]interface{}
|
||||
type XExtensions map[string]string
|
||||
|
||||
func (oai *OpenApiV3) tagMapToXExtensions(tagMap map[string]string, extensions XExtensions) {
|
||||
for k, v := range tagMap {
|
||||
|
||||
@ -9,12 +9,18 @@ package gstr
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
"math"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
)
|
||||
|
||||
var (
|
||||
// octReg is the regular expression object for checks octal string.
|
||||
octReg = regexp.MustCompile(`\\[0-7]{3}`)
|
||||
)
|
||||
|
||||
// Chr return the ascii string of a number(0-255).
|
||||
@ -27,11 +33,6 @@ func Ord(char string) int {
|
||||
return int(char[0])
|
||||
}
|
||||
|
||||
var (
|
||||
// octReg is the regular expression object for checks octal string.
|
||||
octReg = regexp.MustCompile(`\\[0-7]{3}`)
|
||||
)
|
||||
|
||||
// OctStr converts string container octal string to its original string,
|
||||
// for example, to Chinese string.
|
||||
// Eg: `\346\200\241` -> 怡
|
||||
@ -175,7 +176,8 @@ func Nl2Br(str string, isXhtml ...bool) string {
|
||||
}
|
||||
|
||||
// WordWrap wraps a string to a given number of characters.
|
||||
// TODO: Enable cut parameter, see http://php.net/manual/en/function.wordwrap.php.
|
||||
// This function supports cut parameters of both english and chinese punctuations.
|
||||
// TODO: Enable custom cut parameter, see http://php.net/manual/en/function.wordwrap.php.
|
||||
func WordWrap(str string, width int, br string) string {
|
||||
if br == "" {
|
||||
br = "\n"
|
||||
@ -185,9 +187,11 @@ func WordWrap(str string, width int, br string) string {
|
||||
wordBuf, spaceBuf bytes.Buffer
|
||||
init = make([]byte, 0, len(str))
|
||||
buf = bytes.NewBuffer(init)
|
||||
strRunes = []rune(str)
|
||||
)
|
||||
for _, char := range []rune(str) {
|
||||
if char == '\n' {
|
||||
for _, char := range strRunes {
|
||||
switch {
|
||||
case char == '\n':
|
||||
if wordBuf.Len() == 0 {
|
||||
if current+spaceBuf.Len() > width {
|
||||
current = 0
|
||||
@ -205,7 +209,8 @@ func WordWrap(str string, width int, br string) string {
|
||||
}
|
||||
buf.WriteRune(char)
|
||||
current = 0
|
||||
} else if unicode.IsSpace(char) {
|
||||
|
||||
case unicode.IsSpace(char):
|
||||
if spaceBuf.Len() == 0 || wordBuf.Len() > 0 {
|
||||
current += spaceBuf.Len() + wordBuf.Len()
|
||||
spaceBuf.WriteTo(buf)
|
||||
@ -214,7 +219,18 @@ func WordWrap(str string, width int, br string) string {
|
||||
wordBuf.Reset()
|
||||
}
|
||||
spaceBuf.WriteRune(char)
|
||||
} else {
|
||||
|
||||
case isPunctuation(char):
|
||||
wordBuf.WriteRune(char)
|
||||
if spaceBuf.Len() == 0 || wordBuf.Len() > 0 {
|
||||
current += spaceBuf.Len() + wordBuf.Len()
|
||||
spaceBuf.WriteTo(buf)
|
||||
spaceBuf.Reset()
|
||||
wordBuf.WriteTo(buf)
|
||||
wordBuf.Reset()
|
||||
}
|
||||
|
||||
default:
|
||||
wordBuf.WriteRune(char)
|
||||
if current+spaceBuf.Len()+wordBuf.Len() > width && wordBuf.Len() < width {
|
||||
buf.WriteString(br)
|
||||
@ -234,3 +250,16 @@ func WordWrap(str string, width int, br string) string {
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func isPunctuation(char int32) bool {
|
||||
switch char {
|
||||
// English Punctuations.
|
||||
case ';', '.', ',', ':', '~':
|
||||
return true
|
||||
// Chinese Punctuations.
|
||||
case ';', ',', '。', ':', '?', '!', '…', '、':
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,8 +4,6 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go -bench=".*"
|
||||
|
||||
package gstr_test
|
||||
|
||||
import (
|
||||
@ -20,3 +18,25 @@ func Test_OctStr(t *testing.T) {
|
||||
t.Assert(gstr.OctStr(`\346\200\241`), "怡")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_WordWrap(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gstr.WordWrap("12 34", 2, "<br>"), "12<br>34")
|
||||
t.Assert(gstr.WordWrap("12 34", 2, "\n"), "12\n34")
|
||||
t.Assert(gstr.WordWrap("我爱 GF", 2, "\n"), "我爱\nGF")
|
||||
t.Assert(gstr.WordWrap("A very long woooooooooooooooooord. and something", 7, "<br>"),
|
||||
"A very<br>long<br>woooooooooooooooooord.<br>and<br>something")
|
||||
})
|
||||
// Chinese Punctuations.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
br = " "
|
||||
content = " DelRouteKeyIPv6 删除VPC内的服务的Route信息;和DelRouteIPv6接口相比,这个接口可以删除满足条件的多条RS\n"
|
||||
length = 120
|
||||
)
|
||||
wrappedContent := gstr.WordWrap(content, length, "\n"+br)
|
||||
t.Assert(wrappedContent, ` DelRouteKeyIPv6 删除VPC内的服务的Route信息;和DelRouteIPv6接口相比,
|
||||
这个接口可以删除满足条件的多条RS
|
||||
`)
|
||||
})
|
||||
}
|
||||
|
||||
@ -213,16 +213,6 @@ func Test_CountChars(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_WordWrap(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gstr.WordWrap("12 34", 2, "<br>"), "12<br>34")
|
||||
t.Assert(gstr.WordWrap("12 34", 2, "\n"), "12\n34")
|
||||
t.Assert(gstr.WordWrap("我爱 GF", 2, "\n"), "我爱\nGF")
|
||||
t.Assert(gstr.WordWrap("A very long woooooooooooooooooord. and something", 7, "<br>"),
|
||||
"A very<br>long<br>woooooooooooooooooord.<br>and<br>something")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_LenRune(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gstr.LenRune("1234"), 4)
|
||||
|
||||
Reference in New Issue
Block a user