437 lines
10 KiB
Go
437 lines
10 KiB
Go
/*
|
|
* Copyright 2022 CloudWeGo Authors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package util
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
"path/filepath"
|
|
"reflect"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"golib.gaore.com/GaoreGo/gr_hz/util/logs"
|
|
)
|
|
|
|
func CopyStringSlice(from, to *[]string) {
|
|
n := len(*from)
|
|
m := len(*to)
|
|
if n > m {
|
|
n = m
|
|
}
|
|
for i := 0; i < n; i++ {
|
|
(*to)[i] = (*from)[i]
|
|
}
|
|
*to = (*to)[:n]
|
|
}
|
|
|
|
func CopyString2StringMap(from, to map[string]string) {
|
|
for k := range to {
|
|
delete(to, k)
|
|
}
|
|
for k, v := range from {
|
|
to[k] = v
|
|
}
|
|
}
|
|
|
|
func PackArgs(c interface{}) (res []string, err error) {
|
|
t := reflect.TypeOf(c)
|
|
v := reflect.ValueOf(c)
|
|
if reflect.TypeOf(c).Kind() == reflect.Ptr {
|
|
t = t.Elem()
|
|
v = v.Elem()
|
|
}
|
|
if t.Kind() != reflect.Struct {
|
|
return nil, errors.New("passed c must be struct or pointer of struct")
|
|
}
|
|
|
|
for i := 0; i < t.NumField(); i++ {
|
|
f := t.Field(i)
|
|
x := v.Field(i)
|
|
n := f.Name
|
|
|
|
if x.IsZero() {
|
|
continue
|
|
}
|
|
|
|
switch x.Kind() {
|
|
case reflect.Bool:
|
|
if x.Bool() == false {
|
|
continue
|
|
}
|
|
res = append(res, n+"="+fmt.Sprint(x.Bool()))
|
|
case reflect.String:
|
|
if x.String() == "" {
|
|
continue
|
|
}
|
|
res = append(res, n+"="+x.String())
|
|
case reflect.Slice:
|
|
if x.Len() == 0 {
|
|
continue
|
|
}
|
|
ft := f.Type.Elem()
|
|
if ft.Kind() != reflect.String {
|
|
return nil, fmt.Errorf("slice field %v must be '[]string', err: %v", f.Name, err.Error())
|
|
}
|
|
var ss []string
|
|
for i := 0; i < x.Len(); i++ {
|
|
ss = append(ss, x.Index(i).String())
|
|
}
|
|
res = append(res, n+"="+strings.Join(ss, ";"))
|
|
case reflect.Map:
|
|
if x.Len() == 0 {
|
|
continue
|
|
}
|
|
fk := f.Type.Key()
|
|
if fk.Kind() != reflect.String {
|
|
return nil, fmt.Errorf("map field %v must be 'map[string]string', err: %v", f.Name, err.Error())
|
|
}
|
|
fv := f.Type.Elem()
|
|
if fv.Kind() != reflect.String {
|
|
return nil, fmt.Errorf("map field %v must be 'map[string]string', err: %v", f.Name, err.Error())
|
|
}
|
|
var sk []string
|
|
it := x.MapRange()
|
|
for it.Next() {
|
|
sk = append(sk, it.Key().String()+"="+it.Value().String())
|
|
}
|
|
res = append(res, n+"="+strings.Join(sk, ";"))
|
|
default:
|
|
return nil, fmt.Errorf("unsupported field type: %+v, err: %v", f, err.Error())
|
|
}
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func UnpackArgs(args []string, c interface{}) error {
|
|
m, err := MapForm(args)
|
|
if err != nil {
|
|
return fmt.Errorf("unmarshal args failed, err: %v", err.Error())
|
|
}
|
|
|
|
t := reflect.TypeOf(c).Elem()
|
|
v := reflect.ValueOf(c).Elem()
|
|
if t.Kind() != reflect.Struct {
|
|
return errors.New("passed c must be struct or pointer of struct")
|
|
}
|
|
|
|
for i := 0; i < t.NumField(); i++ {
|
|
f := t.Field(i)
|
|
x := v.Field(i)
|
|
n := f.Name
|
|
values, ok := m[n]
|
|
if !ok || len(values) == 0 || values[0] == "" {
|
|
continue
|
|
}
|
|
switch x.Kind() {
|
|
case reflect.Bool:
|
|
if len(values) != 1 {
|
|
return fmt.Errorf("field %s can't be assigned multi values: %v", n, values)
|
|
}
|
|
x.SetBool(values[0] == "true")
|
|
case reflect.String:
|
|
if len(values) != 1 {
|
|
return fmt.Errorf("field %s can't be assigned multi values: %v", n, values)
|
|
}
|
|
x.SetString(values[0])
|
|
case reflect.Slice:
|
|
if len(values) != 1 {
|
|
return fmt.Errorf("field %s can't be assigned multi values: %v", n, values)
|
|
}
|
|
ss := strings.Split(values[0], ";")
|
|
if x.Type().Elem().Kind() == reflect.Int {
|
|
n := reflect.MakeSlice(x.Type(), len(ss), len(ss))
|
|
for i, s := range ss {
|
|
val, err := strconv.ParseInt(s, 10, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
n.Index(i).SetInt(val)
|
|
}
|
|
x.Set(n)
|
|
} else {
|
|
for _, s := range ss {
|
|
val := reflect.Append(x, reflect.ValueOf(s))
|
|
x.Set(val)
|
|
}
|
|
}
|
|
case reflect.Map:
|
|
if len(values) != 1 {
|
|
return fmt.Errorf("field %s can't be assigned multi values: %v", n, values)
|
|
}
|
|
ss := strings.Split(values[0], ";")
|
|
out := make(map[string]string, len(ss))
|
|
for _, s := range ss {
|
|
sk := strings.SplitN(s, "=", 2)
|
|
if len(sk) != 2 {
|
|
return fmt.Errorf("map filed %v invalid key-value pair '%v'", n, s)
|
|
}
|
|
out[sk[0]] = sk[1]
|
|
}
|
|
x.Set(reflect.ValueOf(out))
|
|
default:
|
|
return fmt.Errorf("field %s has unsupported type %+v", n, f.Type)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func MapForm(input []string) (map[string][]string, error) {
|
|
out := make(map[string][]string, len(input))
|
|
|
|
for _, str := range input {
|
|
parts := strings.SplitN(str, "=", 2)
|
|
if len(parts) != 2 {
|
|
return nil, fmt.Errorf("invalid argument: '%s'", str)
|
|
}
|
|
key, val := parts[0], parts[1]
|
|
out[key] = append(out[key], val)
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
func GetFirstKV(m map[string][]string) (string, []string) {
|
|
for k, v := range m {
|
|
return k, v
|
|
}
|
|
return "", nil
|
|
}
|
|
|
|
func ToCamelCase(name string) string {
|
|
return CamelString(name)
|
|
}
|
|
|
|
func ToSnakeCase(name string) string {
|
|
return SnakeString(name)
|
|
}
|
|
|
|
// unifyPath will convert "\" to "/" in path if the os is windows
|
|
func unifyPath(path string) string {
|
|
if IsWindows() {
|
|
path = strings.ReplaceAll(path, "\\", "/")
|
|
}
|
|
return path
|
|
}
|
|
|
|
// BaseName get base name for path. ex: "github.com/p.s.m" => "p.s.m"
|
|
func BaseName(include, subFixToTrim string) string {
|
|
include = unifyPath(include)
|
|
subFixToTrim = unifyPath(subFixToTrim)
|
|
last := include
|
|
if id := strings.LastIndex(last, "/"); id >= 0 && id < len(last)-1 {
|
|
last = last[id+1:]
|
|
}
|
|
if !strings.HasSuffix(last, subFixToTrim) {
|
|
return last
|
|
}
|
|
return last[:len(last)-len(subFixToTrim)]
|
|
}
|
|
|
|
func BaseNameAndTrim(include string) string {
|
|
include = unifyPath(include)
|
|
last := include
|
|
if id := strings.LastIndex(last, "/"); id >= 0 && id < len(last)-1 {
|
|
last = last[id+1:]
|
|
}
|
|
|
|
if id := strings.LastIndex(last, "."); id != -1 {
|
|
last = last[:id]
|
|
}
|
|
return last
|
|
}
|
|
|
|
func SplitPackageName(pkg, subFixToTrim string) string {
|
|
pkg = unifyPath(pkg)
|
|
subFixToTrim = unifyPath(subFixToTrim)
|
|
last := SplitPackage(pkg, subFixToTrim)
|
|
if id := strings.LastIndex(last, "/"); id >= 0 && id < len(last)-1 {
|
|
last = last[id+1:]
|
|
}
|
|
return last
|
|
}
|
|
|
|
func SplitPackage(pkg, subFixToTrim string) string {
|
|
pkg = unifyPath(pkg)
|
|
subFixToTrim = unifyPath(subFixToTrim)
|
|
last := strings.TrimSuffix(pkg, subFixToTrim)
|
|
if id := strings.LastIndex(last, "/"); id >= 0 && id < len(last)-1 {
|
|
last = last[id+1:]
|
|
}
|
|
return strings.ReplaceAll(last, ".", "/")
|
|
}
|
|
|
|
func PathToImport(path, subFix string) string {
|
|
path = strings.TrimSuffix(path, subFix)
|
|
// path = RelativePath(path)
|
|
return strings.ReplaceAll(path, string(filepath.Separator), "/")
|
|
}
|
|
|
|
func ImportToPath(path, subFix string) string {
|
|
// path = RelativePath(path)
|
|
return strings.ReplaceAll(path, "/", string(filepath.Separator)) + subFix
|
|
}
|
|
|
|
func ImportToPathAndConcat(path, subFix string) string {
|
|
path = strings.TrimSuffix(path, subFix)
|
|
path = strings.ReplaceAll(path, "/", string(filepath.Separator))
|
|
if i := strings.LastIndex(path, string(filepath.Separator)); i >= 0 && i < len(path)-1 && strings.Contains(path[i+1:], ".") {
|
|
base := strings.ReplaceAll(path[i+1:], ".", "_")
|
|
dir := path[:i]
|
|
return dir + string(filepath.Separator) + base
|
|
}
|
|
return path
|
|
}
|
|
|
|
func ToVarName(paths []string) string {
|
|
ps := strings.Join(paths, "__")
|
|
input := []byte(url.PathEscape(ps))
|
|
out := make([]byte, 0, len(input))
|
|
for i := 0; i < len(input); i++ {
|
|
c := input[i]
|
|
if c == ':' || c == '*' {
|
|
continue
|
|
}
|
|
if (c >= '0' && c <= '9' && i != 0) || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_') {
|
|
out = append(out, c)
|
|
} else {
|
|
out = append(out, '_')
|
|
}
|
|
}
|
|
|
|
return string(out)
|
|
}
|
|
|
|
func SplitGoTags(input string) []string {
|
|
out := make([]string, 0, 4)
|
|
ns := len(input)
|
|
|
|
flag := false
|
|
prev := 0
|
|
i := 0
|
|
for i = 0; i < ns; i++ {
|
|
c := input[i]
|
|
if c == '"' {
|
|
flag = !flag
|
|
}
|
|
if !flag && c == ' ' {
|
|
if prev < i {
|
|
out = append(out, input[prev:i])
|
|
}
|
|
prev = i + 1
|
|
}
|
|
}
|
|
if i != 0 && prev < i {
|
|
out = append(out, input[prev:i])
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
func SubPackage(mod, dir string) string {
|
|
if dir == "" {
|
|
return mod
|
|
}
|
|
return mod + "/" + PathToImport(dir, "")
|
|
}
|
|
|
|
func SubDir(root, subPkg string) string {
|
|
if root == "" {
|
|
return ImportToPath(subPkg, "")
|
|
}
|
|
return filepath.Join(root, ImportToPath(subPkg, ""))
|
|
}
|
|
|
|
var (
|
|
uniquePackageName = map[string]bool{}
|
|
uniqueMiddlewareName = map[string]bool{}
|
|
uniqueHandlerPackageName = map[string]bool{}
|
|
)
|
|
|
|
// GetPackageUniqueName can get a non-repeating variable name for package alias
|
|
func GetPackageUniqueName(name string) (string, error) {
|
|
name, err := getUniqueName(name, uniquePackageName)
|
|
if err != nil {
|
|
return "", fmt.Errorf("can not generate unique name for package '%s', err: %v", name, err)
|
|
}
|
|
|
|
return name, nil
|
|
}
|
|
|
|
// GetMiddlewareUniqueName can get a non-repeating variable name for middleware name
|
|
func GetMiddlewareUniqueName(name string) (string, error) {
|
|
name, err := getUniqueName(name, uniqueMiddlewareName)
|
|
if err != nil {
|
|
return "", fmt.Errorf("can not generate routing group for path '%s', err: %v", name, err)
|
|
}
|
|
|
|
return name, nil
|
|
}
|
|
|
|
func GetHandlerPackageUniqueName(name string) (string, error) {
|
|
name, err := getUniqueName(name, uniqueHandlerPackageName)
|
|
if err != nil {
|
|
return "", fmt.Errorf("can not generate unique handler package name: '%s', err: %v", name, err)
|
|
}
|
|
|
|
return name, nil
|
|
}
|
|
|
|
// getUniqueName can get a non-repeating variable name
|
|
func getUniqueName(name string, uniqueNameSet map[string]bool) (string, error) {
|
|
uniqueName := name
|
|
if _, exist := uniqueNameSet[uniqueName]; exist {
|
|
for i := 0; i < 10000; i++ {
|
|
uniqueName = uniqueName + fmt.Sprintf("%d", i)
|
|
if _, exist := uniqueNameSet[uniqueName]; !exist {
|
|
logs.Infof("There is a package name with the same name, change %s to %s", name, uniqueName)
|
|
break
|
|
}
|
|
uniqueName = name
|
|
if i == 9999 {
|
|
return "", fmt.Errorf("there is too many same package for %s", name)
|
|
}
|
|
}
|
|
}
|
|
uniqueNameSet[uniqueName] = true
|
|
|
|
return uniqueName, nil
|
|
}
|
|
|
|
func SubPackageDir(path string) string {
|
|
index := strings.LastIndex(path, "/")
|
|
if index == -1 {
|
|
return ""
|
|
}
|
|
return path[:index]
|
|
}
|
|
|
|
var validFuncReg = regexp.MustCompile("[_0-9a-zA-Z]")
|
|
|
|
// ToGoFuncName converts a string to a function naming style for go
|
|
func ToGoFuncName(s string) string {
|
|
ss := []byte(s)
|
|
for i := range ss {
|
|
if !validFuncReg.Match([]byte{s[i]}) {
|
|
ss[i] = '_'
|
|
}
|
|
}
|
|
return string(ss)
|
|
}
|