Merge pull request #3015 from opencloud-eu/dependabot/go_modules/github.com/gookit/config/v2-2.2.8

build(deps): bump github.com/gookit/config/v2 from 2.2.7 to 2.2.8
This commit is contained in:
Ralf Haferkamp
2026-06-25 08:48:01 +02:00
committed by GitHub
23 changed files with 234 additions and 94 deletions

6
go.mod
View File

@@ -41,7 +41,7 @@ require (
github.com/google/go-cmp v0.7.0
github.com/google/go-tika v0.3.1
github.com/google/uuid v1.6.0
github.com/gookit/config/v2 v2.2.7
github.com/gookit/config/v2 v2.2.8
github.com/gorilla/mux v1.8.1
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0
github.com/invopop/validation v0.8.0
@@ -229,7 +229,7 @@ require (
github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.2.1 // indirect
github.com/goccy/go-json v0.10.6 // indirect
github.com/goccy/go-yaml v1.18.0 // indirect
github.com/goccy/go-yaml v1.19.2 // indirect
github.com/gofrs/flock v0.13.0 // indirect
github.com/gofrs/uuid v4.4.0+incompatible // indirect
github.com/gogo/protobuf v1.3.2 // indirect
@@ -240,7 +240,7 @@ require (
github.com/google/go-tpm v0.9.8 // indirect
github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 // indirect
github.com/google/renameio/v2 v2.0.2 // indirect
github.com/gookit/goutil v0.7.4 // indirect
github.com/gookit/goutil v0.7.5 // indirect
github.com/gorilla/handlers v1.5.1 // indirect
github.com/gorilla/schema v1.4.1 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect

12
go.sum
View File

@@ -480,8 +480,8 @@ github.com/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk=
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
@@ -588,10 +588,10 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gookit/config/v2 v2.2.7 h1:P58/uENzkDp7r7Hp8YSZxOhZ/F5a5Y/AzyhDUkQYa9A=
github.com/gookit/config/v2 v2.2.7/go.mod h1:QST99HmkZXXD/HkZmOm1OXpgdAnc6Rl9syGl+u62Pi8=
github.com/gookit/goutil v0.7.4 h1:OWgUngToNz+bPlX5aP+EMG31DraEU63uvKMwwT3vseM=
github.com/gookit/goutil v0.7.4/go.mod h1:vJS9HXctYTCLtCsZot5L5xF+O1oR17cDYO9R0HxBmnU=
github.com/gookit/config/v2 v2.2.8 h1:TX6CypFUj9mZeDxpigerUG2P27u/BDM2l6UUIj0G8rA=
github.com/gookit/config/v2 v2.2.8/go.mod h1:keuITIN2+4SBT3osc3LqqG3xTWaDEbftScDYCbcLwJQ=
github.com/gookit/goutil v0.7.5 h1:FXLTq+hVniw7UVMnr2i371yXqslgVpXqXszvXCJdEH8=
github.com/gookit/goutil v0.7.5/go.mod h1:vJS9HXctYTCLtCsZot5L5xF+O1oR17cDYO9R0HxBmnU=
github.com/gookit/ini/v2 v2.3.2 h1:W6tzOGE6zOLQelH2xhcH8BIBZPtnEpJgQ+J6SsAKBSw=
github.com/gookit/ini/v2 v2.3.2/go.mod h1:StKSqY5niArRwYBS8Z71+iWUt5ow47qt359sS9YQLYY=
github.com/gophercloud/gophercloud v0.15.1-0.20210202035223-633d73521055/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4=

View File

@@ -1450,16 +1450,25 @@ func (n *MappingValueNode) toString() string {
}
return fmt.Sprintf("%s%s: %s", space, n.Key.String(), value)
} else if keyIndentLevel < valueIndentLevel && !n.IsFlowStyle {
valueStr := n.Value.String()
// For flow-style values indented on the next line, we need to add the proper indentation
if m, ok := n.Value.(*MappingNode); ok && m.IsFlowStyle {
valueIndent := strings.Repeat(" ", n.Value.GetToken().Position.Column-1)
valueStr = valueIndent + valueStr
} else if s, ok := n.Value.(*SequenceNode); ok && s.IsFlowStyle {
valueIndent := strings.Repeat(" ", n.Value.GetToken().Position.Column-1)
valueStr = valueIndent + valueStr
}
if keyComment != nil {
return fmt.Sprintf(
"%s%s: %s\n%s",
space,
n.Key.stringWithoutComment(),
keyComment.String(),
n.Value.String(),
valueStr,
)
}
return fmt.Sprintf("%s%s:\n%s", space, n.Key.String(), n.Value.String())
return fmt.Sprintf("%s%s:\n%s", space, n.Key.String(), valueStr)
} else if m, ok := n.Value.(*MappingNode); ok && (m.IsFlowStyle || len(m.Values) == 0) {
return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String())
} else if s, ok := n.Value.(*SequenceNode); ok && (s.IsFlowStyle || len(s.Values) == 0) {
@@ -1614,7 +1623,11 @@ func (n *SequenceNode) flowStyleString() string {
for _, value := range n.Values {
values = append(values, value.String())
}
return fmt.Sprintf("[%s]", strings.Join(values, ", "))
seqText := fmt.Sprintf("[%s]", strings.Join(values, ", "))
if n.Comment != nil {
return addCommentString(seqText, n.Comment)
}
return seqText
}
func (n *SequenceNode) blockStyleString() string {

View File

@@ -40,6 +40,7 @@ type Decoder struct {
isResolvedReference bool
validator StructValidator
disallowUnknownField bool
allowedFieldPrefixes []string
allowDuplicateMapKey bool
useOrderedMap bool
useJSONUnmarshaler bool
@@ -287,7 +288,9 @@ func (d *Decoder) addSequenceNodeCommentToMap(node *ast.SequenceNode) {
texts = append(texts, comment.Token.Value)
}
if len(texts) != 0 {
d.addCommentToMap(node.Values[0].GetPath(), HeadComment(texts...))
if len(node.Values) != 0 {
d.addCommentToMap(node.Values[0].GetPath(), HeadComment(texts...))
}
}
}
}
@@ -462,8 +465,10 @@ func (d *Decoder) nodeToValue(ctx context.Context, node ast.Node) (any, error) {
}
return v.Interface(), nil
}
aliasName := n.Value.GetToken().Value
return nil, errors.ErrSyntax(fmt.Sprintf("could not find alias %q", aliasName), n.Value.GetToken())
if node, exists := d.anchorNodeMap[text]; exists {
return d.nodeToValue(ctx, node)
}
return nil, errors.ErrSyntax(fmt.Sprintf("could not find alias %q", text), n.Value.GetToken())
case *ast.LiteralNode:
return n.Value.GetValue(), nil
case *ast.MappingKeyNode:
@@ -1446,12 +1451,21 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N
// Unknown fields are expected (they could be fields from the parent struct).
if len(unknownFields) != 0 && d.disallowUnknownField && src.GetToken() != nil {
for key, node := range unknownFields {
return errors.ErrUnknownField(fmt.Sprintf(`unknown field "%s"`, key), node.GetToken())
var ok bool
for _, prefix := range d.allowedFieldPrefixes {
if strings.HasPrefix(key, prefix) {
ok = true
break
}
}
if !ok {
return errors.ErrUnknownField(fmt.Sprintf(`unknown field "%s"`, key), node.GetToken())
}
}
}
if d.validator != nil {
if err := d.validator.Struct(dst.Addr().Interface()); err != nil {
if err := d.validator.Struct(dst.Interface()); err != nil {
ev := reflect.ValueOf(err)
if ev.Type().Kind() == reflect.Slice {
for i := 0; i < ev.Len(); i++ {
@@ -1740,14 +1754,11 @@ func (d *Decoder) decodeMap(ctx context.Context, dst reflect.Value, src ast.Node
return err
}
} else {
keyVal, err := d.nodeToValue(ctx, key)
keyVal, err := d.createDecodedNewValue(ctx, keyType, reflect.Value{}, key)
if err != nil {
return err
}
k = reflect.ValueOf(keyVal)
if k.IsValid() && k.Type().ConvertibleTo(keyType) {
k = k.Convert(keyType)
}
k = keyVal
}
if k.IsValid() {

View File

@@ -712,9 +712,15 @@ func (e *Encoder) encodeMap(ctx context.Context, value reflect.Value, column int
anchorNode.Value = encoded
encoded = anchorNode
}
kn, err := e.encodeValue(ctx, reflect.ValueOf(key), column)
keyNode, ok := kn.(ast.MapKeyNode)
if !ok || err != nil {
keyNode = e.encodeString(fmt.Sprint(key), column)
}
node.Values = append(node.Values, ast.MappingValue(
nil,
e.encodeString(keyText, column),
keyNode,
encoded,
))
e.setSmartAnchor(vRef, keyText)

View File

@@ -351,8 +351,9 @@ func (f *Formatter) formatMapping(n *ast.MappingNode) string {
var ret string
if n.IsFlowStyle {
ret = f.origin(n.Start)
} else {
ret += f.formatCommentGroup(n.Comment)
}
ret += f.formatCommentGroup(n.Comment)
for _, value := range n.Values {
if value.CollectEntry != nil {
ret += f.origin(value.CollectEntry)
@@ -361,6 +362,7 @@ func (f *Formatter) formatMapping(n *ast.MappingNode) string {
}
if n.IsFlowStyle {
ret += f.origin(n.End)
ret += f.formatCommentGroup(n.Comment)
}
return ret
}
@@ -377,8 +379,7 @@ func (f *Formatter) formatSequence(n *ast.SequenceNode) string {
var ret string
if n.IsFlowStyle {
ret = f.origin(n.Start)
}
if n.Comment != nil {
} else {
// add head comment.
ret += f.formatCommentGroup(n.Comment)
}
@@ -387,6 +388,7 @@ func (f *Formatter) formatSequence(n *ast.SequenceNode) string {
}
if n.IsFlowStyle {
ret += f.origin(n.End)
ret += f.formatCommentGroup(n.Comment)
}
ret += f.formatCommentGroup(n.FootComment)
return ret

View File

@@ -69,6 +69,15 @@ func DisallowUnknownField() DecodeOption {
}
}
// AllowFieldPrefixes, when paired with [DisallowUnknownField], allows fields
// with the specified prefixes to bypass the unknown field check.
func AllowFieldPrefixes(prefixes ...string) DecodeOption {
return func(d *Decoder) error {
d.allowedFieldPrefixes = append(d.allowedFieldPrefixes, prefixes...)
return nil
}
}
// AllowDuplicateMapKey ignore syntax error when mapping keys that are duplicates.
func AllowDuplicateMapKey() DecodeOption {
return func(d *Decoder) error {

View File

@@ -426,6 +426,11 @@ func (p *parser) parseFlowMap(ctx *context) (*ast.MappingNode, error) {
if node.End == nil {
return nil, errors.ErrSyntax("could not find flow mapping end token '}'", node.Start)
}
// set line comment if exists. e.g.) } # comment
if err := setLineComment(ctx, node, ctx.currentToken()); err != nil {
return nil, err
}
ctx.goNext() // skip mapping end token.
return node, nil
}
@@ -1066,6 +1071,11 @@ func (p *parser) parseFlowSequence(ctx *context) (*ast.SequenceNode, error) {
if node.End == nil {
return nil, errors.ErrSyntax("sequence end token ']' not found", node.Start)
}
// set line comment if exists. e.g.) ] # comment
if err := setLineComment(ctx, node, ctx.currentToken()); err != nil {
return nil, err
}
ctx.goNext() // skip sequence end token.
return node, nil
}

View File

@@ -258,6 +258,10 @@ func (p *Path) Filter(target, v interface{}) error {
// FilterFile filter from ast.File by YAMLPath.
func (p *Path) FilterFile(f *ast.File) (ast.Node, error) {
for _, doc := range f.Docs {
// For simplicity, directives cannot be the target of operations
if doc.Body != nil && doc.Body.Type() == ast.DirectiveType {
continue
}
node, err := p.FilterNode(doc.Body)
if err != nil {
return nil, err
@@ -352,6 +356,10 @@ func (p *Path) ReplaceWithFile(dst *ast.File, src *ast.File) error {
// ReplaceNode replace ast.File with ast.Node.
func (p *Path) ReplaceWithNode(dst *ast.File, node ast.Node) error {
for _, doc := range dst.Docs {
// For simplicity, directives cannot be the target of operations
if doc.Body != nil && doc.Body.Type() == ast.DirectiveType {
continue
}
if node.Type() == ast.DocumentType {
node = node.(*ast.DocumentNode).Body
}
@@ -364,7 +372,7 @@ func (p *Path) ReplaceWithNode(dst *ast.File, node ast.Node) error {
// AnnotateSource add annotation to passed source ( see section 5.1 in README.md ).
func (p *Path) AnnotateSource(source []byte, colored bool) ([]byte, error) {
file, err := parser.ParseBytes([]byte(source), 0)
file, err := parser.ParseBytes(source, 0)
if err != nil {
return nil, err
}

View File

@@ -777,6 +777,15 @@ func (s *Scanner) scanComment(ctx *Context) bool {
func (s *Scanner) scanMultiLine(ctx *Context, c rune) error {
state := ctx.getMultiLineState()
ctx.addOriginBuf(c)
// normalize CR and CRLF to LF
if c == '\r' {
if ctx.nextChar() == '\n' {
ctx.addOriginBuf('\n')
s.progress(ctx, 1)
s.offset++
}
c = '\n'
}
if ctx.isEOS() {
if s.isFirstCharAtLine && c == ' ' {
state.addIndent(ctx, s.column)
@@ -1148,14 +1157,25 @@ func (s *Scanner) scanMultiLineHeaderOption(ctx *Context) error {
s.progress(ctx, 1) // skip '|' or '>' character
var progress int
var crlf bool
for idx, c := range ctx.src[ctx.idx:] {
progress = idx
ctx.addOriginBuf(c)
if s.isNewLineChar(c) {
nextIdx := ctx.idx + idx + 1
if c == '\r' && nextIdx < len(ctx.src) && ctx.src[nextIdx] == '\n' {
crlf = true
continue // process \n in the next iteration
}
break
}
}
value := strings.TrimRight(ctx.source(ctx.idx, ctx.idx+progress), " ")
endPos := ctx.idx + progress
if crlf {
// Exclude \r
endPos = endPos - 1
}
value := strings.TrimRight(ctx.source(ctx.idx, endPos), " ")
commentValueIndex := strings.Index(value, "#")
opt := value
if commentValueIndex > 0 {
@@ -1189,7 +1209,7 @@ func (s *Scanner) scanMultiLineHeaderOption(ctx *Context) error {
ctx.setFolded(s.lastDelimColumn, opt)
}
if commentIndex > 0 {
comment := string(value[commentValueIndex+1:])
comment := value[commentValueIndex+1:]
s.offset += len(headerBuf)
s.column += len(headerBuf)
ctx.addToken(token.Comment(comment, string(ctx.obuf[len(headerBuf):]), s.pos()))

View File

@@ -324,3 +324,34 @@ func RegisterCustomUnmarshalerContext[T any](unmarshaler func(context.Context, *
return unmarshaler(ctx, v.(*T), b)
}
}
// RawMessage is a raw encoded YAML value. It implements [BytesMarshaler] and
// [BytesUnmarshaler] and can be used to delay YAML decoding or precompute a YAML
// encoding.
// It also implements [json.Marshaler] and [json.Unmarshaler].
//
// This is similar to [json.RawMessage] in the stdlib.
type RawMessage []byte
func (m RawMessage) MarshalYAML() ([]byte, error) {
if m == nil {
return []byte("null"), nil
}
return m, nil
}
func (m *RawMessage) UnmarshalYAML(dt []byte) error {
if m == nil {
return errors.New("yaml.RawMessage: UnmarshalYAML on nil pointer")
}
*m = append((*m)[0:0], dt...)
return nil
}
func (m *RawMessage) UnmarshalJSON(b []byte) error {
return m.UnmarshalYAML(b)
}
func (m RawMessage) MarshalJSON() ([]byte, error) {
return YAMLToJSON(m)
}

View File

@@ -17,4 +17,5 @@
*.out
.DS_Store
vendor
#go.sum
#go.sum
.xenv.toml

View File

@@ -2,7 +2,6 @@
![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/gookit/config?style=flat-square)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/1e0f0ca096d94ffdab375234ec4167ee)](https://app.codacy.com/gh/gookit/config?utm_source=github.com&utm_medium=referral&utm_content=gookit/config&utm_campaign=Badge_Grade_Settings)
[![Build Status](https://travis-ci.org/gookit/config.svg?branch=master)](https://travis-ci.org/gookit/config)
[![Actions Status](https://github.com/gookit/config/workflows/Unit-Tests/badge.svg)](https://github.com/gookit/config/actions)
[![Coverage Status](https://coveralls.io/repos/github/gookit/config/badge.svg?branch=master)](https://coveralls.io/github/gookit/config?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/gookit/config)](https://goreportcard.com/report/github.com/gookit/config)
@@ -14,8 +13,9 @@
## Features
- Support multi format: `JSON`(default), `JSON5`, `INI`, `Properties`, `YAML`, `TOML`, `HCL`, `ENV`, `Flags`
- Support multi format: `JSON`(default), `JSON5`, `INI`, `Properties`, `YAML`, `TOML`, `ENV`, `Flags`
- `JSON` content support comments. will auto clear comments
- `HCL` need to import `github.com/hashicorp/hcl` for custom driver
- Other drivers are used on demand, not used will not be loaded into the application.
- Possibility to add custom driver for your specific format
- Support multi-file and multi-data loading
@@ -303,6 +303,9 @@ myConf := config.New("my-conf")
// create empty instance
myConf := config.NewEmpty("my-conf")
// default add options: ParseEnv, ParseDefault, ParseTime
myConf := config.NewGeneric("my-conf")
// create and with some options
myConf := config.NewWithOptions("my-conf", config.ParseEnv, config.ReadOnly)
```
@@ -516,13 +519,6 @@ go test -cover
go test -cover ./...
```
## Projects using config
Check out these projects, which use https://github.com/gookit/config :
- https://github.com/JanDeDobbeleer/oh-my-posh A prompt theme engine for any shell.
- [+ See More](https://pkg.go.dev/github.com/gookit/config?tab=importedby)
## Gookit packages
- [gookit/ini](https://github.com/gookit/ini) Go config management, use INI files

View File

@@ -2,7 +2,6 @@
![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/gookit/config?style=flat-square)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/1e0f0ca096d94ffdab375234ec4167ee)](https://app.codacy.com/gh/gookit/config?utm_source=github.com&utm_medium=referral&utm_content=gookit/config&utm_campaign=Badge_Grade_Settings)
[![Build Status](https://travis-ci.org/gookit/config.svg?branch=master)](https://travis-ci.org/gookit/config)
[![Actions Status](https://github.com/gookit/config/workflows/Unit-Tests/badge.svg)](https://github.com/gookit/config/actions)
[![Coverage Status](https://coveralls.io/repos/github/gookit/config/badge.svg?branch=master)](https://coveralls.io/github/gookit/config?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/gookit/config)](https://goreportcard.com/report/github.com/gookit/config)
@@ -16,6 +15,7 @@
- 支持多种格式: `JSON`(默认), `JSON5`, `INI`, `Properties`, `YAML`, `TOML`, `HCL`, `ENV`, `Flags`
- `JSON` 内容支持注释,可以设置解析时清除注释
- `HCL` 需要手动引入 `github.com/hashicorp/hcl` 添加自定义驱动
- 其他驱动都是按需使用,不使用的不会加载编译到应用中
- 支持多个文件、多数据加载
- 支持从 OS ENV 变量数据加载配置
@@ -296,6 +296,9 @@ myConf := config.New("my-conf")
// create empty instance
myConf := config.NewEmpty("my-conf")
// default add options: ParseEnv, ParseDefault, ParseTime
myConf := config.NewGeneric("my-conf")
// create and with some options
myConf := config.NewWithOptions("my-conf", config.ParseEnv, config.ReadOnly)
```
@@ -507,13 +510,6 @@ go test -cover
go test -cover ./...
```
## 使用Config的项目
看看这些使用了 https://github.com/gookit/config 的项目:
- https://github.com/JanDeDobbeleer/oh-my-posh A prompt theme engine for any shell.
- [+ See More](https://pkg.go.dev/github.com/gookit/config?tab=importedby)
## Gookit 工具包
- [gookit/ini](https://github.com/gookit/ini) INI配置读取管理支持多文件加载数据覆盖合并, 解析ENV变量, 解析变量引用

View File

@@ -2,4 +2,4 @@
- remote `etcd` `consul`
- watch changed config files and reload
- [x] set default value on binding struct. use tag `defalut`
- [x] set default value on binding struct. use tag `default`

View File

@@ -15,6 +15,7 @@ import (
"dario.cat/mergo"
"github.com/gookit/goutil/errorx"
"github.com/gookit/goutil/fsutil"
"github.com/gookit/goutil/strutil"
)
// LoadFiles load one or multi files, will fire OnLoadData event
@@ -55,6 +56,30 @@ func (c *Config) LoadExists(sourceFiles ...string) (err error) {
return
}
// FileFilterFn for check file is should load.
type FileFilterFn func(file string, c *Config) (shouldLoad bool, format string)
// LoadFilesByFilter load one or multi files by give filter checked, will fire OnLoadData event
func LoadFilesByFilter(configFiles []string, filter FileFilterFn) error {
return dc.LoadFilesByFilter(configFiles, filter)
}
// LoadFilesByFilter load one or multi files by give filter checked, will fire OnLoadData event
// - `filter` return format can be emtpy, will auto detect it by file extension
func (c *Config) LoadFilesByFilter(configFiles []string, filter FileFilterFn) (err error) {
for _, file := range configFiles {
shouldLoad, format := filter(file, c)
if !shouldLoad {
continue
}
if err = c.loadFile(file, false, format); err != nil {
return
}
}
return
}
// LoadRemote load config data from remote URL.
func LoadRemote(format, url string) error { return dc.LoadRemote(format, url) }
@@ -127,6 +152,25 @@ func (c *Config) LoadOSEnvs(nameToKeyMap map[string]string) {
c.fireHook(OnLoadData)
}
// LoadOSEnvByFilter load OS ENVs by custom fitler func. eg: use for load ENV by prefix.
func LoadOSEnvByFilter(filterFn func(key string) (loadIt bool, cfgKey string)) { dc.LoadOSEnvByFilter(filterFn) }
// LoadOSEnvByFilter load OS ENVs by custom fitler func. eg: use for load ENV by prefix.
//
// - `filterFn` return cfgKey can be empty, will use key instead.
func (c *Config) LoadOSEnvByFilter(filterFn func(key string) (loadIt bool, cfgKey string)) {
for _, str := range os.Environ() {
key, val := strutil.SplitKV(str, "=")
if loadIt, cfgKey := filterFn(key); loadIt {
if cfgKey == "" {
cfgKey = strings.ToLower(key)
}
_ = c.Set(cfgKey, val)
}
}
}
// support bound types for CLI flags vars
var validTypes = map[string]int{
"int": 1,
@@ -445,6 +489,7 @@ func (c *Config) ReloadFiles() (err error) {
}
// load config file, will fire OnLoadData event
// - loadExist=false will return error on file not exists
func (c *Config) loadFile(file string, loadExist bool, format string) (err error) {
fd, err := os.Open(file)
if err != nil {

View File

@@ -63,21 +63,22 @@ func (c *Config) Exists(key string, findByPath ...bool) (ok bool) {
}
case []int: // is array(is from Set)
i, err := strconv.Atoi(k)
// check slice index
if err != nil || len(typeData) < i {
if err != nil || i >= len(typeData) {
return false
}
item = typeData[i]
case []string: // is array(is from Set)
i, err := strconv.Atoi(k)
if err != nil || len(typeData) < i {
if err != nil || i >= len(typeData) {
return false
}
item = typeData[i]
case []any: // is array(load from file)
i, err := strconv.Atoi(k)
if err != nil || len(typeData) < i {
if err != nil || i >= len(typeData) {
return false
}
item = typeData[i]
default: // error
return false
}
@@ -213,7 +214,7 @@ func (c *Config) GetValue(key string, findByPath ...bool) (value any, ok bool) {
i, err := strconv.Atoi(k)
// check slice index
if err != nil || len(typeData) < i {
if err != nil || len(typeData) <= i {
ok = false
c.addError(err)
return
@@ -222,7 +223,7 @@ func (c *Config) GetValue(key string, findByPath ...bool) (value any, ok bool) {
item = typeData[i]
case []string: // is array(is from Set)
i, err := strconv.Atoi(k)
if err != nil || len(typeData) < i {
if err != nil || len(typeData) <= i {
ok = false
c.addError(err)
return
@@ -231,7 +232,7 @@ func (c *Config) GetValue(key string, findByPath ...bool) (value any, ok bool) {
item = typeData[i]
case []any: // is array(load from file)
i, err := strconv.Atoi(k)
if err != nil || len(typeData) < i {
if err != nil || len(typeData) <= i {
ok = false
c.addError(err)
return

View File

@@ -32,7 +32,7 @@ func ValDecodeHookFunc(parseEnv, parseTime bool) mapstructure.DecodeHookFunc {
}
// feat: support parse time or duration string. eg: 10s
if parseTime && str[0] > '0' && str[0] <= '9' {
if parseTime && str[0] >= '0' && str[0] <= '9' {
return reflects.ToTimeOrDuration(str, t)
}
return str, nil

View File

@@ -83,7 +83,7 @@ func IsSymlink(path string) bool {
return fi.Mode()&os.ModeSymlink != 0
}
// IsAbsPath is abs path.
// IsAbsPath is abs path check
func IsAbsPath(aPath string) bool {
if len(aPath) > 0 {
if aPath[0] == '/' {
@@ -94,6 +94,17 @@ func IsAbsPath(aPath string) bool {
return false
}
// IsAbsPath2 is abs path check. if start withs / OR ~, will direct return true.
func IsAbsPath2(aPath string) bool {
if len(aPath) > 0 {
if aPath[0] == '/' || aPath[0] == '~' {
return true
}
return filepath.IsAbs(aPath)
}
return false
}
// IsEmptyDir reports whether the named directory is empty.
func IsEmptyDir(dirPath string) bool {
f, err := os.Open(dirPath)

View File

@@ -96,6 +96,16 @@ func IsLower(s string) bool {
return true
}
// IsAllASCII 判断字符串是否全为可打印 ASCII无中文等多字节字符
func IsAllASCII(s string) bool {
for i := 0; i < len(s); i++ {
if s[i] > 0x7E || s[i] < 0x20 {
return false
}
}
return true
}
// StrPos alias of the strings.Index
func StrPos(s, sub string) int { return strings.Index(s, sub) }

View File

@@ -1,10 +1,7 @@
package strutil
import (
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"fmt"
"math/big"
"strings"
@@ -92,30 +89,3 @@ func ShortUUID() string {
}
return fmt.Sprintf("%08x", b)
}
//
// ----------------------- hash password -----------------------------
//
// HashPasswd for quick hash an input password string
func HashPasswd(pwd, key string) string {
hm := hmac.New(sha256.New, []byte(key))
hm.Write([]byte(pwd))
return hex.EncodeToString(hm.Sum(nil))
}
// VerifyPasswd for quick verify input password is valid
//
// - pwdMAC from db or config, generated by HashPasswd()
func VerifyPasswd(pwdMAC, pwd, key string) bool {
decBts, err := hex.DecodeString(pwdMAC)
if err != nil {
return false
}
hm := hmac.New(sha256.New, []byte(key))
hm.Write([]byte(pwd))
return hmac.Equal(decBts, hm.Sum(nil))
}

View File

@@ -1,8 +1,8 @@
package termenv
func init() {
// terminal supports color OR noColor=true: Don't need to enable virtual process
if colorLevel != TermColorNone || noColor {
// needVTP=false OR noColor=true: Don't need to enable virtual process
if !needVTP || noColor {
return
}

8
vendor/modules.txt vendored
View File

@@ -668,7 +668,7 @@ github.com/goccy/go-json/internal/encoder/vm_color_indent
github.com/goccy/go-json/internal/encoder/vm_indent
github.com/goccy/go-json/internal/errors
github.com/goccy/go-json/internal/runtime
# github.com/goccy/go-yaml v1.18.0
# github.com/goccy/go-yaml v1.19.2
## explicit; go 1.21.0
github.com/goccy/go-yaml
github.com/goccy/go-yaml/ast
@@ -735,11 +735,11 @@ github.com/google/renameio/v2
# github.com/google/uuid v1.6.0
## explicit
github.com/google/uuid
# github.com/gookit/config/v2 v2.2.7
## explicit; go 1.19
# github.com/gookit/config/v2 v2.2.8
## explicit; go 1.21.0
github.com/gookit/config/v2
github.com/gookit/config/v2/yaml
# github.com/gookit/goutil v0.7.4
# github.com/gookit/goutil v0.7.5
## explicit; go 1.19
github.com/gookit/goutil
github.com/gookit/goutil/arrutil