chore: sub-store Go 重写项目初始化
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GetByPath retrieves a value from a nested map using dot-notation path.
|
||||
// e.g. GetByPath(node, "ws-opts.headers.Host")
|
||||
func GetByPath(input map[string]any, path string) any {
|
||||
keys := strings.Split(path, ".")
|
||||
var current any = input
|
||||
for _, key := range keys {
|
||||
if current == nil {
|
||||
return nil
|
||||
}
|
||||
m, ok := current.(map[string]any)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
current = m[key]
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
// SetByPath sets a value in a nested map using dot-notation path,
|
||||
// creating intermediate maps as needed.
|
||||
func SetByPath(input map[string]any, path string, value any) map[string]any {
|
||||
keys := strings.Split(path, ".")
|
||||
current := input
|
||||
for i := 0; i < len(keys)-1; i++ {
|
||||
key := keys[i]
|
||||
next, ok := current[key].(map[string]any)
|
||||
if !ok {
|
||||
next = map[string]any{}
|
||||
current[key] = next
|
||||
}
|
||||
current = next
|
||||
}
|
||||
current[keys[len(keys)-1]] = value
|
||||
return input
|
||||
}
|
||||
|
||||
func GetString(input map[string]any, path string) string {
|
||||
v := GetByPath(input, path)
|
||||
if s, ok := v.(string); ok {
|
||||
return s
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func GetInt(input map[string]any, path string) int {
|
||||
v := GetByPath(input, path)
|
||||
switch n := v.(type) {
|
||||
case int:
|
||||
return n
|
||||
case int64:
|
||||
return int(n)
|
||||
case float64:
|
||||
return int(n)
|
||||
case string:
|
||||
var i int
|
||||
fmt.Sscanf(n, "%d", &i)
|
||||
return i
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func GetBool(input map[string]any, path string) bool {
|
||||
v := GetByPath(input, path)
|
||||
if b, ok := v.(bool); ok {
|
||||
return b
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// DeepCopy produces a deep copy of a ProxyNode via JSON round-trip.
|
||||
func DeepCopy(node map[string]any) map[string]any {
|
||||
data, err := json.Marshal(node)
|
||||
if err != nil {
|
||||
return map[string]any{}
|
||||
}
|
||||
var copy map[string]any
|
||||
if err := json.Unmarshal(data, ©); err != nil {
|
||||
return map[string]any{}
|
||||
}
|
||||
return copy
|
||||
}
|
||||
|
||||
// StripUndefined removes keys with nil or empty-string values from a map.
|
||||
func StripUndefined(input map[string]any) map[string]any {
|
||||
result := make(map[string]any, len(input))
|
||||
for k, v := range input {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
if s, ok := v.(string); ok && s == "" {
|
||||
continue
|
||||
}
|
||||
result[k] = v
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// MergeDeep recursively merges `next` into `base`, returning a new map.
|
||||
func MergeDeep(base, next map[string]any) map[string]any {
|
||||
result := make(map[string]any, len(base))
|
||||
for k, v := range base {
|
||||
result[k] = v
|
||||
}
|
||||
for k, v := range next {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
if isPlainObject(v) && isPlainObject(result[k]) {
|
||||
result[k] = MergeDeep(result[k].(map[string]any), v.(map[string]any))
|
||||
} else {
|
||||
result[k] = v
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func isPlainObject(v any) bool {
|
||||
_, ok := v.(map[string]any)
|
||||
return ok
|
||||
}
|
||||
|
||||
// ToId normalizes a string into a valid record ID: lowercase, non-alphanum → dash, max 64.
|
||||
func ToId(input string) string {
|
||||
s := strings.TrimSpace(input)
|
||||
b := make([]byte, 0, len(s))
|
||||
prevDash := false
|
||||
for i := 0; i < len(s); i++ {
|
||||
c := s[i]
|
||||
if c >= 'A' && c <= 'Z' {
|
||||
c += 32
|
||||
}
|
||||
if (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_' {
|
||||
b = append(b, c)
|
||||
prevDash = false
|
||||
} else if c == '-' {
|
||||
if !prevDash {
|
||||
b = append(b, '-')
|
||||
prevDash = true
|
||||
}
|
||||
} else {
|
||||
if !prevDash && len(b) > 0 {
|
||||
b = append(b, '-')
|
||||
prevDash = true
|
||||
}
|
||||
}
|
||||
}
|
||||
// trim leading/trailing dashes
|
||||
result := strings.Trim(string(b), "-")
|
||||
if len(result) > 64 {
|
||||
result = result[:64]
|
||||
}
|
||||
if result == "" {
|
||||
return "item"
|
||||
}
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user