diff --git a/go.mod b/go.mod index 2a81d37bd..aecf328f6 100644 --- a/go.mod +++ b/go.mod @@ -40,7 +40,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.6 + github.com/gookit/config/v2 v2.2.7 github.com/gorilla/mux v1.8.1 github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 github.com/invopop/validation v0.8.0 @@ -203,6 +203,7 @@ require ( github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/go-test/deep v1.1.0 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/pool v0.2.1 // indirect @@ -219,8 +220,7 @@ require ( github.com/google/go-tpm v0.9.5 // indirect github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect github.com/google/renameio/v2 v2.0.0 // indirect - github.com/gookit/color v1.5.4 // indirect - github.com/gookit/goutil v0.6.18 // indirect + github.com/gookit/goutil v0.7.1 // 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 @@ -314,7 +314,6 @@ require ( github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect - github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect github.com/yashtewari/glob-intersection v0.2.0 // indirect go.etcd.io/etcd/api/v3 v3.6.4 // indirect diff --git a/go.sum b/go.sum index 1e5eeda2d..5fc3ce3fc 100644 --- a/go.sum +++ b/go.sum @@ -430,6 +430,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVruqWQJBtW6+bTBDTniY8yZum5rF3b5jw= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= @@ -551,14 +553,12 @@ 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/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= -github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= -github.com/gookit/config/v2 v2.2.6 h1:8ZbkSr3gnFg1En8za9X3vldnZca3y3C7kaBLGsdLghE= -github.com/gookit/config/v2 v2.2.6/go.mod h1:++APDf3Ebj6mjzW1ALkegvg1evQKyx4FpuQqQZ2s2WM= -github.com/gookit/goutil v0.6.18 h1:MUVj0G16flubWT8zYVicIuisUiHdgirPAkmnfD2kKgw= -github.com/gookit/goutil v0.6.18/go.mod h1:AY/5sAwKe7Xck+mEbuxj0n/bc3qwrGNe3Oeulln7zBA= -github.com/gookit/ini/v2 v2.2.3 h1:nSbN+x9OfQPcMObTFP+XuHt8ev6ndv/fWWqxFhPMu2E= -github.com/gookit/ini/v2 v2.2.3/go.mod h1:Vu6p7P7xcfmb8KYu3L0ek8bqu/Im63N81q208SCCZY4= +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.1 h1:AaFJPN9mrdeYBv8HOybri26EHGCC34WJVT7jUStGJsI= +github.com/gookit/goutil v0.7.1/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= github.com/gophercloud/gophercloud v0.16.0/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4= github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae/go.mod h1:wx8HMD8oQD0Ryhz6+6ykq75PJ79iPyEqYHfwZ4l7OsA= @@ -1141,8 +1141,6 @@ github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQ github.com/xhit/go-simple-mail/v2 v2.16.0 h1:ouGy/Ww4kuaqu2E2UrDw7SvLaziWTB60ICLkIkNVccA= github.com/xhit/go-simple-mail/v2 v2.16.0/go.mod h1:b7P5ygho6SYE+VIqpxA6QkYfv4teeyG4MKqB3utRu98= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= -github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yashtewari/glob-intersection v0.2.0 h1:8iuHdN88yYuCzCdjt0gDe+6bAhUwBeEWqThExu54RFg= diff --git a/vendor/github.com/go-viper/mapstructure/v2/.editorconfig b/vendor/github.com/go-viper/mapstructure/v2/.editorconfig new file mode 100644 index 000000000..faef0c91e --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/.editorconfig @@ -0,0 +1,21 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.go] +indent_style = tab + +[{Makefile,*.mk}] +indent_style = tab + +[*.nix] +indent_size = 2 + +[.golangci.yaml] +indent_size = 2 diff --git a/vendor/github.com/go-viper/mapstructure/v2/.gitignore b/vendor/github.com/go-viper/mapstructure/v2/.gitignore new file mode 100644 index 000000000..470e7ca2b --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/.gitignore @@ -0,0 +1,6 @@ +/.devenv/ +/.direnv/ +/.pre-commit-config.yaml +/bin/ +/build/ +/var/ diff --git a/vendor/github.com/go-viper/mapstructure/v2/.golangci.yaml b/vendor/github.com/go-viper/mapstructure/v2/.golangci.yaml new file mode 100644 index 000000000..bda962566 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/.golangci.yaml @@ -0,0 +1,48 @@ +version: "2" + +run: + timeout: 10m + +linters: + enable: + - govet + - ineffassign + # - misspell + - nolintlint + # - revive + + disable: + - errcheck + - staticcheck + - unused + + settings: + misspell: + locale: US + nolintlint: + allow-unused: false # report any unused nolint directives + require-specific: false # don't require nolint directives to be specific about which linter is being skipped + +formatters: + enable: + - gci + - gofmt + - gofumpt + - goimports + # - golines + + settings: + gci: + sections: + - standard + - default + - localmodule + gofmt: + simplify: true + rewrite-rules: + - pattern: interface{} + replacement: any + + exclusions: + paths: + - internal/ diff --git a/vendor/github.com/go-viper/mapstructure/v2/CHANGELOG.md b/vendor/github.com/go-viper/mapstructure/v2/CHANGELOG.md new file mode 100644 index 000000000..afd44e5f5 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/CHANGELOG.md @@ -0,0 +1,104 @@ +> [!WARNING] +> As of v2 of this library, change log can be found in GitHub releases. + +## 1.5.1 + +* Wrap errors so they're compatible with `errors.Is` and `errors.As` [GH-282] +* Fix map of slices not decoding properly in certain cases. [GH-266] + +## 1.5.0 + +* New option `IgnoreUntaggedFields` to ignore decoding to any fields + without `mapstructure` (or the configured tag name) set [GH-277] +* New option `ErrorUnset` which makes it an error if any fields + in a target struct are not set by the decoding process. [GH-225] +* New function `OrComposeDecodeHookFunc` to help compose decode hooks. [GH-240] +* Decoding to slice from array no longer crashes [GH-265] +* Decode nested struct pointers to map [GH-271] +* Fix issue where `,squash` was ignored if `Squash` option was set. [GH-280] +* Fix issue where fields with `,omitempty` would sometimes decode + into a map with an empty string key [GH-281] + +## 1.4.3 + +* Fix cases where `json.Number` didn't decode properly [GH-261] + +## 1.4.2 + +* Custom name matchers to support any sort of casing, formatting, etc. for + field names. [GH-250] +* Fix possible panic in ComposeDecodeHookFunc [GH-251] + +## 1.4.1 + +* Fix regression where `*time.Time` value would be set to empty and not be sent + to decode hooks properly [GH-232] + +## 1.4.0 + +* A new decode hook type `DecodeHookFuncValue` has been added that has + access to the full values. [GH-183] +* Squash is now supported with embedded fields that are struct pointers [GH-205] +* Empty strings will convert to 0 for all numeric types when weakly decoding [GH-206] + +## 1.3.3 + +* Decoding maps from maps creates a settable value for decode hooks [GH-203] + +## 1.3.2 + +* Decode into interface type with a struct value is supported [GH-187] + +## 1.3.1 + +* Squash should only squash embedded structs. [GH-194] + +## 1.3.0 + +* Added `",omitempty"` support. This will ignore zero values in the source + structure when encoding. [GH-145] + +## 1.2.3 + +* Fix duplicate entries in Keys list with pointer values. [GH-185] + +## 1.2.2 + +* Do not add unsettable (unexported) values to the unused metadata key + or "remain" value. [GH-150] + +## 1.2.1 + +* Go modules checksum mismatch fix + +## 1.2.0 + +* Added support to capture unused values in a field using the `",remain"` value + in the mapstructure tag. There is an example to showcase usage. +* Added `DecoderConfig` option to always squash embedded structs +* `json.Number` can decode into `uint` types +* Empty slices are preserved and not replaced with nil slices +* Fix panic that can occur in when decoding a map into a nil slice of structs +* Improved package documentation for godoc + +## 1.1.2 + +* Fix error when decode hook decodes interface implementation into interface + type. [GH-140] + +## 1.1.1 + +* Fix panic that can happen in `decodePtr` + +## 1.1.0 + +* Added `StringToIPHookFunc` to convert `string` to `net.IP` and `net.IPNet` [GH-133] +* Support struct to struct decoding [GH-137] +* If source map value is nil, then destination map value is nil (instead of empty) +* If source slice value is nil, then destination slice value is nil (instead of empty) +* If source pointer is nil, then destination pointer is set to nil (instead of + allocated zero value of type) + +## 1.0.0 + +* Initial tagged stable release. diff --git a/vendor/github.com/xo/terminfo/LICENSE b/vendor/github.com/go-viper/mapstructure/v2/LICENSE similarity index 88% rename from vendor/github.com/xo/terminfo/LICENSE rename to vendor/github.com/go-viper/mapstructure/v2/LICENSE index 197dadb12..f9c841a51 100644 --- a/vendor/github.com/xo/terminfo/LICENSE +++ b/vendor/github.com/go-viper/mapstructure/v2/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016 Anmol Sethi +Copyright (c) 2013 Mitchell Hashimoto Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -9,13 +9,13 @@ 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 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. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/go-viper/mapstructure/v2/README.md b/vendor/github.com/go-viper/mapstructure/v2/README.md new file mode 100644 index 000000000..45db71975 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/README.md @@ -0,0 +1,81 @@ +# mapstructure + +[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/go-viper/mapstructure/ci.yaml?style=flat-square)](https://github.com/go-viper/mapstructure/actions/workflows/ci.yaml) +[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/mod/github.com/go-viper/mapstructure/v2) +![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/go-viper/mapstructure?style=flat-square&color=61CFDD) +[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/go-viper/mapstructure/badge?style=flat-square)](https://deps.dev/go/github.com%252Fgo-viper%252Fmapstructure%252Fv2) + +mapstructure is a Go library for decoding generic map values to structures +and vice versa, while providing helpful error handling. + +This library is most useful when decoding values from some data stream (JSON, +Gob, etc.) where you don't _quite_ know the structure of the underlying data +until you read a part of it. You can therefore read a `map[string]interface{}` +and use this library to decode it into the proper underlying native Go +structure. + +## Installation + +```shell +go get github.com/go-viper/mapstructure/v2 +``` + +## Migrating from `github.com/mitchellh/mapstructure` + +[@mitchehllh](https://github.com/mitchellh) announced his intent to archive some of his unmaintained projects (see [here](https://gist.github.com/mitchellh/90029601268e59a29e64e55bab1c5bdc) and [here](https://github.com/mitchellh/mapstructure/issues/349)). This is a repository achieved the "blessed fork" status. + +You can migrate to this package by changing your import paths in your Go files to `github.com/go-viper/mapstructure/v2`. +The API is the same, so you don't need to change anything else. + +Here is a script that can help you with the migration: + +```shell +sed -i 's|github.com/mitchellh/mapstructure|github.com/go-viper/mapstructure/v2|g' $(find . -type f -name '*.go') +``` + +If you need more time to migrate your code, that is absolutely fine. + +Some of the latest fixes are backported to the v1 release branch of this package, so you can use the Go modules `replace` feature until you are ready to migrate: + +```shell +replace github.com/mitchellh/mapstructure => github.com/go-viper/mapstructure v1.6.0 +``` + +## Usage & Example + +For usage and examples see the [documentation](https://pkg.go.dev/mod/github.com/go-viper/mapstructure/v2). + +The `Decode` function has examples associated with it there. + +## But Why?! + +Go offers fantastic standard libraries for decoding formats such as JSON. +The standard method is to have a struct pre-created, and populate that struct +from the bytes of the encoded format. This is great, but the problem is if +you have configuration or an encoding that changes slightly depending on +specific fields. For example, consider this JSON: + +```json +{ + "type": "person", + "name": "Mitchell" +} +``` + +Perhaps we can't populate a specific structure without first reading +the "type" field from the JSON. We could always do two passes over the +decoding of the JSON (reading the "type" first, and the rest later). +However, it is much simpler to just decode this into a `map[string]interface{}` +structure, read the "type" key, then use something like this library +to decode it into the proper structure. + +## Credits + +Mapstructure was originally created by [@mitchellh](https://github.com/mitchellh). +This is a maintained fork of the original library. + +Read more about the reasons for the fork [here](https://github.com/mitchellh/mapstructure/issues/349). + +## License + +The project is licensed under the [MIT License](LICENSE). diff --git a/vendor/github.com/go-viper/mapstructure/v2/decode_hooks.go b/vendor/github.com/go-viper/mapstructure/v2/decode_hooks.go new file mode 100644 index 000000000..a852a0a04 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/decode_hooks.go @@ -0,0 +1,714 @@ +package mapstructure + +import ( + "encoding" + "errors" + "fmt" + "net" + "net/netip" + "net/url" + "reflect" + "strconv" + "strings" + "time" +) + +// typedDecodeHook takes a raw DecodeHookFunc (an any) and turns +// it into the proper DecodeHookFunc type, such as DecodeHookFuncType. +func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc { + // Create variables here so we can reference them with the reflect pkg + var f1 DecodeHookFuncType + var f2 DecodeHookFuncKind + var f3 DecodeHookFuncValue + + // Fill in the variables into this interface and the rest is done + // automatically using the reflect package. + potential := []any{f1, f2, f3} + + v := reflect.ValueOf(h) + vt := v.Type() + for _, raw := range potential { + pt := reflect.ValueOf(raw).Type() + if vt.ConvertibleTo(pt) { + return v.Convert(pt).Interface() + } + } + + return nil +} + +// cachedDecodeHook takes a raw DecodeHookFunc (an any) and turns +// it into a closure to be used directly +// if the type fails to convert we return a closure always erroring to keep the previous behaviour +func cachedDecodeHook(raw DecodeHookFunc) func(from reflect.Value, to reflect.Value) (any, error) { + switch f := typedDecodeHook(raw).(type) { + case DecodeHookFuncType: + return func(from reflect.Value, to reflect.Value) (any, error) { + return f(from.Type(), to.Type(), from.Interface()) + } + case DecodeHookFuncKind: + return func(from reflect.Value, to reflect.Value) (any, error) { + return f(from.Kind(), to.Kind(), from.Interface()) + } + case DecodeHookFuncValue: + return func(from reflect.Value, to reflect.Value) (any, error) { + return f(from, to) + } + default: + return func(from reflect.Value, to reflect.Value) (any, error) { + return nil, errors.New("invalid decode hook signature") + } + } +} + +// DecodeHookExec executes the given decode hook. This should be used +// since it'll naturally degrade to the older backwards compatible DecodeHookFunc +// that took reflect.Kind instead of reflect.Type. +func DecodeHookExec( + raw DecodeHookFunc, + from reflect.Value, to reflect.Value, +) (any, error) { + switch f := typedDecodeHook(raw).(type) { + case DecodeHookFuncType: + return f(from.Type(), to.Type(), from.Interface()) + case DecodeHookFuncKind: + return f(from.Kind(), to.Kind(), from.Interface()) + case DecodeHookFuncValue: + return f(from, to) + default: + return nil, errors.New("invalid decode hook signature") + } +} + +// ComposeDecodeHookFunc creates a single DecodeHookFunc that +// automatically composes multiple DecodeHookFuncs. +// +// The composed funcs are called in order, with the result of the +// previous transformation. +func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc { + cached := make([]func(from reflect.Value, to reflect.Value) (any, error), 0, len(fs)) + for _, f := range fs { + cached = append(cached, cachedDecodeHook(f)) + } + return func(f reflect.Value, t reflect.Value) (any, error) { + var err error + data := f.Interface() + + newFrom := f + for _, c := range cached { + data, err = c(newFrom, t) + if err != nil { + return nil, err + } + if v, ok := data.(reflect.Value); ok { + newFrom = v + } else { + newFrom = reflect.ValueOf(data) + } + } + + return data, nil + } +} + +// OrComposeDecodeHookFunc executes all input hook functions until one of them returns no error. In that case its value is returned. +// If all hooks return an error, OrComposeDecodeHookFunc returns an error concatenating all error messages. +func OrComposeDecodeHookFunc(ff ...DecodeHookFunc) DecodeHookFunc { + cached := make([]func(from reflect.Value, to reflect.Value) (any, error), 0, len(ff)) + for _, f := range ff { + cached = append(cached, cachedDecodeHook(f)) + } + return func(a, b reflect.Value) (any, error) { + var allErrs string + var out any + var err error + + for _, c := range cached { + out, err = c(a, b) + if err != nil { + allErrs += err.Error() + "\n" + continue + } + + return out, nil + } + + return nil, errors.New(allErrs) + } +} + +// StringToSliceHookFunc returns a DecodeHookFunc that converts +// string to []string by splitting on the given sep. +func StringToSliceHookFunc(sep string) DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.SliceOf(f) { + return data, nil + } + + raw := data.(string) + if raw == "" { + return []string{}, nil + } + + return strings.Split(raw, sep), nil + } +} + +// StringToWeakSliceHookFunc brings back the old (pre-v2) behavior of [StringToSliceHookFunc]. +// +// As of mapstructure v2.0.0 [StringToSliceHookFunc] checks if the return type is a string slice. +// This function removes that check. +func StringToWeakSliceHookFunc(sep string) DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Slice { + return data, nil + } + + raw := data.(string) + if raw == "" { + return []string{}, nil + } + + return strings.Split(raw, sep), nil + } +} + +// StringToTimeDurationHookFunc returns a DecodeHookFunc that converts +// strings to time.Duration. +func StringToTimeDurationHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(time.Duration(5)) { + return data, nil + } + + // Convert it by parsing + d, err := time.ParseDuration(data.(string)) + + return d, wrapTimeParseDurationError(err) + } +} + +// StringToTimeLocationHookFunc returns a DecodeHookFunc that converts +// strings to *time.Location. +func StringToTimeLocationHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(time.Local) { + return data, nil + } + d, err := time.LoadLocation(data.(string)) + + return d, wrapTimeParseLocationError(err) + } +} + +// StringToURLHookFunc returns a DecodeHookFunc that converts +// strings to *url.URL. +func StringToURLHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(&url.URL{}) { + return data, nil + } + + // Convert it by parsing + u, err := url.Parse(data.(string)) + + return u, wrapUrlError(err) + } +} + +// StringToIPHookFunc returns a DecodeHookFunc that converts +// strings to net.IP +func StringToIPHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(net.IP{}) { + return data, nil + } + + // Convert it by parsing + ip := net.ParseIP(data.(string)) + if ip == nil { + return net.IP{}, fmt.Errorf("failed parsing ip") + } + + return ip, nil + } +} + +// StringToIPNetHookFunc returns a DecodeHookFunc that converts +// strings to net.IPNet +func StringToIPNetHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(net.IPNet{}) { + return data, nil + } + + // Convert it by parsing + _, net, err := net.ParseCIDR(data.(string)) + return net, wrapNetParseError(err) + } +} + +// StringToTimeHookFunc returns a DecodeHookFunc that converts +// strings to time.Time. +func StringToTimeHookFunc(layout string) DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(time.Time{}) { + return data, nil + } + + // Convert it by parsing + ti, err := time.Parse(layout, data.(string)) + + return ti, wrapTimeParseError(err) + } +} + +// WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to +// the decoder. +// +// Note that this is significantly different from the WeaklyTypedInput option +// of the DecoderConfig. +func WeaklyTypedHook( + f reflect.Kind, + t reflect.Kind, + data any, +) (any, error) { + dataVal := reflect.ValueOf(data) + switch t { + case reflect.String: + switch f { + case reflect.Bool: + if dataVal.Bool() { + return "1", nil + } + return "0", nil + case reflect.Float32: + return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil + case reflect.Int: + return strconv.FormatInt(dataVal.Int(), 10), nil + case reflect.Slice: + dataType := dataVal.Type() + elemKind := dataType.Elem().Kind() + if elemKind == reflect.Uint8 { + return string(dataVal.Interface().([]uint8)), nil + } + case reflect.Uint: + return strconv.FormatUint(dataVal.Uint(), 10), nil + } + } + + return data, nil +} + +func RecursiveStructToMapHookFunc() DecodeHookFunc { + return func(f reflect.Value, t reflect.Value) (any, error) { + if f.Kind() != reflect.Struct { + return f.Interface(), nil + } + + var i any = struct{}{} + if t.Type() != reflect.TypeOf(&i).Elem() { + return f.Interface(), nil + } + + m := make(map[string]any) + t.Set(reflect.ValueOf(m)) + + return f.Interface(), nil + } +} + +// TextUnmarshallerHookFunc returns a DecodeHookFunc that applies +// strings to the UnmarshalText function, when the target type +// implements the encoding.TextUnmarshaler interface +func TextUnmarshallerHookFunc() DecodeHookFuncType { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String { + return data, nil + } + result := reflect.New(t).Interface() + unmarshaller, ok := result.(encoding.TextUnmarshaler) + if !ok { + return data, nil + } + str, ok := data.(string) + if !ok { + str = reflect.Indirect(reflect.ValueOf(&data)).Elem().String() + } + if err := unmarshaller.UnmarshalText([]byte(str)); err != nil { + return nil, err + } + return result, nil + } +} + +// StringToNetIPAddrHookFunc returns a DecodeHookFunc that converts +// strings to netip.Addr. +func StringToNetIPAddrHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(netip.Addr{}) { + return data, nil + } + + // Convert it by parsing + addr, err := netip.ParseAddr(data.(string)) + + return addr, wrapNetIPParseAddrError(err) + } +} + +// StringToNetIPAddrPortHookFunc returns a DecodeHookFunc that converts +// strings to netip.AddrPort. +func StringToNetIPAddrPortHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(netip.AddrPort{}) { + return data, nil + } + + // Convert it by parsing + addrPort, err := netip.ParseAddrPort(data.(string)) + + return addrPort, wrapNetIPParseAddrPortError(err) + } +} + +// StringToNetIPPrefixHookFunc returns a DecodeHookFunc that converts +// strings to netip.Prefix. +func StringToNetIPPrefixHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(netip.Prefix{}) { + return data, nil + } + + // Convert it by parsing + prefix, err := netip.ParsePrefix(data.(string)) + + return prefix, wrapNetIPParsePrefixError(err) + } +} + +// StringToBasicTypeHookFunc returns a DecodeHookFunc that converts +// strings to basic types. +// int8, uint8, int16, uint16, int32, uint32, int64, uint64, int, uint, float32, float64, bool, byte, rune, complex64, complex128 +func StringToBasicTypeHookFunc() DecodeHookFunc { + return ComposeDecodeHookFunc( + StringToInt8HookFunc(), + StringToUint8HookFunc(), + StringToInt16HookFunc(), + StringToUint16HookFunc(), + StringToInt32HookFunc(), + StringToUint32HookFunc(), + StringToInt64HookFunc(), + StringToUint64HookFunc(), + StringToIntHookFunc(), + StringToUintHookFunc(), + StringToFloat32HookFunc(), + StringToFloat64HookFunc(), + StringToBoolHookFunc(), + // byte and rune are aliases for uint8 and int32 respectively + // StringToByteHookFunc(), + // StringToRuneHookFunc(), + StringToComplex64HookFunc(), + StringToComplex128HookFunc(), + ) +} + +// StringToInt8HookFunc returns a DecodeHookFunc that converts +// strings to int8. +func StringToInt8HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Int8 { + return data, nil + } + + // Convert it by parsing + i64, err := strconv.ParseInt(data.(string), 0, 8) + return int8(i64), wrapStrconvNumError(err) + } +} + +// StringToUint8HookFunc returns a DecodeHookFunc that converts +// strings to uint8. +func StringToUint8HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Uint8 { + return data, nil + } + + // Convert it by parsing + u64, err := strconv.ParseUint(data.(string), 0, 8) + return uint8(u64), wrapStrconvNumError(err) + } +} + +// StringToInt16HookFunc returns a DecodeHookFunc that converts +// strings to int16. +func StringToInt16HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Int16 { + return data, nil + } + + // Convert it by parsing + i64, err := strconv.ParseInt(data.(string), 0, 16) + return int16(i64), wrapStrconvNumError(err) + } +} + +// StringToUint16HookFunc returns a DecodeHookFunc that converts +// strings to uint16. +func StringToUint16HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Uint16 { + return data, nil + } + + // Convert it by parsing + u64, err := strconv.ParseUint(data.(string), 0, 16) + return uint16(u64), wrapStrconvNumError(err) + } +} + +// StringToInt32HookFunc returns a DecodeHookFunc that converts +// strings to int32. +func StringToInt32HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Int32 { + return data, nil + } + + // Convert it by parsing + i64, err := strconv.ParseInt(data.(string), 0, 32) + return int32(i64), wrapStrconvNumError(err) + } +} + +// StringToUint32HookFunc returns a DecodeHookFunc that converts +// strings to uint32. +func StringToUint32HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Uint32 { + return data, nil + } + + // Convert it by parsing + u64, err := strconv.ParseUint(data.(string), 0, 32) + return uint32(u64), wrapStrconvNumError(err) + } +} + +// StringToInt64HookFunc returns a DecodeHookFunc that converts +// strings to int64. +func StringToInt64HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Int64 { + return data, nil + } + + // Convert it by parsing + i64, err := strconv.ParseInt(data.(string), 0, 64) + return int64(i64), wrapStrconvNumError(err) + } +} + +// StringToUint64HookFunc returns a DecodeHookFunc that converts +// strings to uint64. +func StringToUint64HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Uint64 { + return data, nil + } + + // Convert it by parsing + u64, err := strconv.ParseUint(data.(string), 0, 64) + return uint64(u64), wrapStrconvNumError(err) + } +} + +// StringToIntHookFunc returns a DecodeHookFunc that converts +// strings to int. +func StringToIntHookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Int { + return data, nil + } + + // Convert it by parsing + i64, err := strconv.ParseInt(data.(string), 0, 0) + return int(i64), wrapStrconvNumError(err) + } +} + +// StringToUintHookFunc returns a DecodeHookFunc that converts +// strings to uint. +func StringToUintHookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Uint { + return data, nil + } + + // Convert it by parsing + u64, err := strconv.ParseUint(data.(string), 0, 0) + return uint(u64), wrapStrconvNumError(err) + } +} + +// StringToFloat32HookFunc returns a DecodeHookFunc that converts +// strings to float32. +func StringToFloat32HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Float32 { + return data, nil + } + + // Convert it by parsing + f64, err := strconv.ParseFloat(data.(string), 32) + return float32(f64), wrapStrconvNumError(err) + } +} + +// StringToFloat64HookFunc returns a DecodeHookFunc that converts +// strings to float64. +func StringToFloat64HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Float64 { + return data, nil + } + + // Convert it by parsing + f64, err := strconv.ParseFloat(data.(string), 64) + return f64, wrapStrconvNumError(err) + } +} + +// StringToBoolHookFunc returns a DecodeHookFunc that converts +// strings to bool. +func StringToBoolHookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Bool { + return data, nil + } + + // Convert it by parsing + b, err := strconv.ParseBool(data.(string)) + return b, wrapStrconvNumError(err) + } +} + +// StringToByteHookFunc returns a DecodeHookFunc that converts +// strings to byte. +func StringToByteHookFunc() DecodeHookFunc { + return StringToUint8HookFunc() +} + +// StringToRuneHookFunc returns a DecodeHookFunc that converts +// strings to rune. +func StringToRuneHookFunc() DecodeHookFunc { + return StringToInt32HookFunc() +} + +// StringToComplex64HookFunc returns a DecodeHookFunc that converts +// strings to complex64. +func StringToComplex64HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Complex64 { + return data, nil + } + + // Convert it by parsing + c128, err := strconv.ParseComplex(data.(string), 64) + return complex64(c128), wrapStrconvNumError(err) + } +} + +// StringToComplex128HookFunc returns a DecodeHookFunc that converts +// strings to complex128. +func StringToComplex128HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Complex128 { + return data, nil + } + + // Convert it by parsing + c128, err := strconv.ParseComplex(data.(string), 128) + return c128, wrapStrconvNumError(err) + } +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/errors.go b/vendor/github.com/go-viper/mapstructure/v2/errors.go new file mode 100644 index 000000000..07d31c22a --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/errors.go @@ -0,0 +1,244 @@ +package mapstructure + +import ( + "errors" + "fmt" + "net" + "net/url" + "reflect" + "strconv" + "strings" + "time" +) + +// Error interface is implemented by all errors emitted by mapstructure. +// +// Use [errors.As] to check if an error implements this interface. +type Error interface { + error + + mapstructure() +} + +// DecodeError is a generic error type that holds information about +// a decoding error together with the name of the field that caused the error. +type DecodeError struct { + name string + err error +} + +func newDecodeError(name string, err error) *DecodeError { + return &DecodeError{ + name: name, + err: err, + } +} + +func (e *DecodeError) Name() string { + return e.name +} + +func (e *DecodeError) Unwrap() error { + return e.err +} + +func (e *DecodeError) Error() string { + return fmt.Sprintf("'%s' %s", e.name, e.err) +} + +func (*DecodeError) mapstructure() {} + +// ParseError is an error type that indicates a value could not be parsed +// into the expected type. +type ParseError struct { + Expected reflect.Value + Value any + Err error +} + +func (e *ParseError) Error() string { + return fmt.Sprintf("cannot parse value as '%s': %s", e.Expected.Type(), e.Err) +} + +func (*ParseError) mapstructure() {} + +// UnconvertibleTypeError is an error type that indicates a value could not be +// converted to the expected type. +type UnconvertibleTypeError struct { + Expected reflect.Value + Value any +} + +func (e *UnconvertibleTypeError) Error() string { + return fmt.Sprintf( + "expected type '%s', got unconvertible type '%s'", + e.Expected.Type(), + reflect.TypeOf(e.Value), + ) +} + +func (*UnconvertibleTypeError) mapstructure() {} + +func wrapStrconvNumError(err error) error { + if err == nil { + return nil + } + + if err, ok := err.(*strconv.NumError); ok { + return &strconvNumError{Err: err} + } + + return err +} + +type strconvNumError struct { + Err *strconv.NumError +} + +func (e *strconvNumError) Error() string { + return "strconv." + e.Err.Func + ": " + e.Err.Err.Error() +} + +func (e *strconvNumError) Unwrap() error { return e.Err } + +func wrapUrlError(err error) error { + if err == nil { + return nil + } + + if err, ok := err.(*url.Error); ok { + return &urlError{Err: err} + } + + return err +} + +type urlError struct { + Err *url.Error +} + +func (e *urlError) Error() string { + return fmt.Sprintf("%s", e.Err.Err) +} + +func (e *urlError) Unwrap() error { return e.Err } + +func wrapNetParseError(err error) error { + if err == nil { + return nil + } + + if err, ok := err.(*net.ParseError); ok { + return &netParseError{Err: err} + } + + return err +} + +type netParseError struct { + Err *net.ParseError +} + +func (e *netParseError) Error() string { + return "invalid " + e.Err.Type +} + +func (e *netParseError) Unwrap() error { return e.Err } + +func wrapTimeParseError(err error) error { + if err == nil { + return nil + } + + if err, ok := err.(*time.ParseError); ok { + return &timeParseError{Err: err} + } + + return err +} + +type timeParseError struct { + Err *time.ParseError +} + +func (e *timeParseError) Error() string { + if e.Err.Message == "" { + return fmt.Sprintf("parsing time as %q: cannot parse as %q", e.Err.Layout, e.Err.LayoutElem) + } + + return "parsing time " + e.Err.Message +} + +func (e *timeParseError) Unwrap() error { return e.Err } + +func wrapNetIPParseAddrError(err error) error { + if err == nil { + return nil + } + + if errMsg := err.Error(); strings.HasPrefix(errMsg, "ParseAddr") { + errPieces := strings.Split(errMsg, ": ") + + return fmt.Errorf("ParseAddr: %s", errPieces[len(errPieces)-1]) + } + + return err +} + +func wrapNetIPParseAddrPortError(err error) error { + if err == nil { + return nil + } + + errMsg := err.Error() + if strings.HasPrefix(errMsg, "invalid port ") { + return errors.New("invalid port") + } else if strings.HasPrefix(errMsg, "invalid ip:port ") { + return errors.New("invalid ip:port") + } + + return err +} + +func wrapNetIPParsePrefixError(err error) error { + if err == nil { + return nil + } + + if errMsg := err.Error(); strings.HasPrefix(errMsg, "netip.ParsePrefix") { + errPieces := strings.Split(errMsg, ": ") + + return fmt.Errorf("netip.ParsePrefix: %s", errPieces[len(errPieces)-1]) + } + + return err +} + +func wrapTimeParseDurationError(err error) error { + if err == nil { + return nil + } + + errMsg := err.Error() + if strings.HasPrefix(errMsg, "time: unknown unit ") { + return errors.New("time: unknown unit") + } else if strings.HasPrefix(errMsg, "time: ") { + idx := strings.LastIndex(errMsg, " ") + + return errors.New(errMsg[:idx]) + } + + return err +} + +func wrapTimeParseLocationError(err error) error { + if err == nil { + return nil + } + errMsg := err.Error() + if strings.Contains(errMsg, "unknown time zone") || strings.HasPrefix(errMsg, "time: unknown format") { + return fmt.Errorf("invalid time zone format: %w", err) + } + + return err +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/flake.lock b/vendor/github.com/go-viper/mapstructure/v2/flake.lock new file mode 100644 index 000000000..5e67bdd6b --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/flake.lock @@ -0,0 +1,294 @@ +{ + "nodes": { + "cachix": { + "inputs": { + "devenv": [ + "devenv" + ], + "flake-compat": [ + "devenv" + ], + "git-hooks": [ + "devenv" + ], + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1742042642, + "narHash": "sha256-D0gP8srrX0qj+wNYNPdtVJsQuFzIng3q43thnHXQ/es=", + "owner": "cachix", + "repo": "cachix", + "rev": "a624d3eaf4b1d225f918de8543ed739f2f574203", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "latest", + "repo": "cachix", + "type": "github" + } + }, + "devenv": { + "inputs": { + "cachix": "cachix", + "flake-compat": "flake-compat", + "git-hooks": "git-hooks", + "nix": "nix", + "nixpkgs": "nixpkgs_3" + }, + "locked": { + "lastModified": 1744876578, + "narHash": "sha256-8MTBj2REB8t29sIBLpxbR0+AEGJ7f+RkzZPAGsFd40c=", + "owner": "cachix", + "repo": "devenv", + "rev": "7ff7c351bba20d0615be25ecdcbcf79b57b85fe1", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1733328505, + "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "devenv", + "nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1712014858, + "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_2": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1743550720, + "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "c621e8422220273271f52058f618c94e405bb0f5", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "git-hooks": { + "inputs": { + "flake-compat": [ + "devenv" + ], + "gitignore": "gitignore", + "nixpkgs": [ + "devenv", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1742649964, + "narHash": "sha256-DwOTp7nvfi8mRfuL1escHDXabVXFGT1VlPD1JHrtrco=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "dcf5072734cb576d2b0c59b2ac44f5050b5eac82", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "devenv", + "git-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "libgit2": { + "flake": false, + "locked": { + "lastModified": 1697646580, + "narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=", + "owner": "libgit2", + "repo": "libgit2", + "rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5", + "type": "github" + }, + "original": { + "owner": "libgit2", + "repo": "libgit2", + "type": "github" + } + }, + "nix": { + "inputs": { + "flake-compat": [ + "devenv" + ], + "flake-parts": "flake-parts", + "libgit2": "libgit2", + "nixpkgs": "nixpkgs_2", + "nixpkgs-23-11": [ + "devenv" + ], + "nixpkgs-regression": [ + "devenv" + ], + "pre-commit-hooks": [ + "devenv" + ] + }, + "locked": { + "lastModified": 1741798497, + "narHash": "sha256-E3j+3MoY8Y96mG1dUIiLFm2tZmNbRvSiyN7CrSKuAVg=", + "owner": "domenkozar", + "repo": "nix", + "rev": "f3f44b2baaf6c4c6e179de8cbb1cc6db031083cd", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "devenv-2.24", + "repo": "nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1733212471, + "narHash": "sha256-M1+uCoV5igihRfcUKrr1riygbe73/dzNnzPsmaLCmpo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "55d15ad12a74eb7d4646254e13638ad0c4128776", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1743296961, + "narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1717432640, + "narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "88269ab3044128b7c2f4c7d68448b2fb50456870", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "release-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1733477122, + "narHash": "sha256-qamMCz5mNpQmgBwc8SB5tVMlD5sbwVIToVZtSxMph9s=", + "owner": "cachix", + "repo": "devenv-nixpkgs", + "rev": "7bd9e84d0452f6d2e63b6e6da29fe73fac951857", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "rolling", + "repo": "devenv-nixpkgs", + "type": "github" + } + }, + "nixpkgs_4": { + "locked": { + "lastModified": 1744536153, + "narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "flake-parts": "flake-parts_2", + "nixpkgs": "nixpkgs_4" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/flake.nix b/vendor/github.com/go-viper/mapstructure/v2/flake.nix new file mode 100644 index 000000000..3b116f426 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/flake.nix @@ -0,0 +1,46 @@ +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + devenv.url = "github:cachix/devenv"; + }; + + outputs = + inputs@{ flake-parts, ... }: + flake-parts.lib.mkFlake { inherit inputs; } { + imports = [ + inputs.devenv.flakeModule + ]; + + systems = [ + "x86_64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + + perSystem = + { pkgs, ... }: + rec { + devenv.shells = { + default = { + languages = { + go.enable = true; + }; + + pre-commit.hooks = { + nixpkgs-fmt.enable = true; + }; + + packages = with pkgs; [ + golangci-lint + ]; + + # https://github.com/cachix/devenv/issues/528#issuecomment-1556108767 + containers = pkgs.lib.mkForce { }; + }; + + ci = devenv.shells.default; + }; + }; + }; +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/internal/errors/errors.go b/vendor/github.com/go-viper/mapstructure/v2/internal/errors/errors.go new file mode 100644 index 000000000..d1c15e474 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/internal/errors/errors.go @@ -0,0 +1,11 @@ +package errors + +import "errors" + +func New(text string) error { + return errors.New(text) +} + +func As(err error, target interface{}) bool { + return errors.As(err, target) +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/internal/errors/join.go b/vendor/github.com/go-viper/mapstructure/v2/internal/errors/join.go new file mode 100644 index 000000000..d74e3a0b5 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/internal/errors/join.go @@ -0,0 +1,9 @@ +//go:build go1.20 + +package errors + +import "errors" + +func Join(errs ...error) error { + return errors.Join(errs...) +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/internal/errors/join_go1_19.go b/vendor/github.com/go-viper/mapstructure/v2/internal/errors/join_go1_19.go new file mode 100644 index 000000000..700b40229 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/internal/errors/join_go1_19.go @@ -0,0 +1,61 @@ +//go:build !go1.20 + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package errors + +// Join returns an error that wraps the given errors. +// Any nil error values are discarded. +// Join returns nil if every value in errs is nil. +// The error formats as the concatenation of the strings obtained +// by calling the Error method of each element of errs, with a newline +// between each string. +// +// A non-nil error returned by Join implements the Unwrap() []error method. +func Join(errs ...error) error { + n := 0 + for _, err := range errs { + if err != nil { + n++ + } + } + if n == 0 { + return nil + } + e := &joinError{ + errs: make([]error, 0, n), + } + for _, err := range errs { + if err != nil { + e.errs = append(e.errs, err) + } + } + return e +} + +type joinError struct { + errs []error +} + +func (e *joinError) Error() string { + // Since Join returns nil if every value in errs is nil, + // e.errs cannot be empty. + if len(e.errs) == 1 { + return e.errs[0].Error() + } + + b := []byte(e.errs[0].Error()) + for _, err := range e.errs[1:] { + b = append(b, '\n') + b = append(b, err.Error()...) + } + // At this point, b has at least one byte '\n'. + // return unsafe.String(&b[0], len(b)) + return string(b) +} + +func (e *joinError) Unwrap() []error { + return e.errs +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go b/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go new file mode 100644 index 000000000..7c35bce02 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go @@ -0,0 +1,1712 @@ +// Package mapstructure exposes functionality to convert one arbitrary +// Go type into another, typically to convert a map[string]any +// into a native Go structure. +// +// The Go structure can be arbitrarily complex, containing slices, +// other structs, etc. and the decoder will properly decode nested +// maps and so on into the proper structures in the native Go struct. +// See the examples to see what the decoder is capable of. +// +// The simplest function to start with is Decode. +// +// # Field Tags +// +// When decoding to a struct, mapstructure will use the field name by +// default to perform the mapping. For example, if a struct has a field +// "Username" then mapstructure will look for a key in the source value +// of "username" (case insensitive). +// +// type User struct { +// Username string +// } +// +// You can change the behavior of mapstructure by using struct tags. +// The default struct tag that mapstructure looks for is "mapstructure" +// but you can customize it using DecoderConfig. +// +// # Renaming Fields +// +// To rename the key that mapstructure looks for, use the "mapstructure" +// tag and set a value directly. For example, to change the "username" example +// above to "user": +// +// type User struct { +// Username string `mapstructure:"user"` +// } +// +// # Embedded Structs and Squashing +// +// Embedded structs are treated as if they're another field with that name. +// By default, the two structs below are equivalent when decoding with +// mapstructure: +// +// type Person struct { +// Name string +// } +// +// type Friend struct { +// Person +// } +// +// type Friend struct { +// Person Person +// } +// +// This would require an input that looks like below: +// +// map[string]any{ +// "person": map[string]any{"name": "alice"}, +// } +// +// If your "person" value is NOT nested, then you can append ",squash" to +// your tag value and mapstructure will treat it as if the embedded struct +// were part of the struct directly. Example: +// +// type Friend struct { +// Person `mapstructure:",squash"` +// } +// +// Now the following input would be accepted: +// +// map[string]any{ +// "name": "alice", +// } +// +// When decoding from a struct to a map, the squash tag squashes the struct +// fields into a single map. Using the example structs from above: +// +// Friend{Person: Person{Name: "alice"}} +// +// Will be decoded into a map: +// +// map[string]any{ +// "name": "alice", +// } +// +// DecoderConfig has a field that changes the behavior of mapstructure +// to always squash embedded structs. +// +// # Remainder Values +// +// If there are any unmapped keys in the source value, mapstructure by +// default will silently ignore them. You can error by setting ErrorUnused +// in DecoderConfig. If you're using Metadata you can also maintain a slice +// of the unused keys. +// +// You can also use the ",remain" suffix on your tag to collect all unused +// values in a map. The field with this tag MUST be a map type and should +// probably be a "map[string]any" or "map[any]any". +// See example below: +// +// type Friend struct { +// Name string +// Other map[string]any `mapstructure:",remain"` +// } +// +// Given the input below, Other would be populated with the other +// values that weren't used (everything but "name"): +// +// map[string]any{ +// "name": "bob", +// "address": "123 Maple St.", +// } +// +// # Omit Empty Values +// +// When decoding from a struct to any other value, you may use the +// ",omitempty" suffix on your tag to omit that value if it equates to +// the zero value, or a zero-length element. The zero value of all types is +// specified in the Go specification. +// +// For example, the zero type of a numeric type is zero ("0"). If the struct +// field value is zero and a numeric type, the field is empty, and it won't +// be encoded into the destination type. And likewise for the URLs field, if the +// slice is nil or empty, it won't be encoded into the destination type. +// +// type Source struct { +// Age int `mapstructure:",omitempty"` +// URLs []string `mapstructure:",omitempty"` +// } +// +// # Omit Zero Values +// +// When decoding from a struct to any other value, you may use the +// ",omitzero" suffix on your tag to omit that value if it equates to the zero +// value. The zero value of all types is specified in the Go specification. +// +// For example, the zero type of a numeric type is zero ("0"). If the struct +// field value is zero and a numeric type, the field is empty, and it won't +// be encoded into the destination type. And likewise for the URLs field, if the +// slice is nil, it won't be encoded into the destination type. +// +// Note that if the field is a slice, and it is empty but not nil, it will +// still be encoded into the destination type. +// +// type Source struct { +// Age int `mapstructure:",omitzero"` +// URLs []string `mapstructure:",omitzero"` +// } +// +// # Unexported fields +// +// Since unexported (private) struct fields cannot be set outside the package +// where they are defined, the decoder will simply skip them. +// +// For this output type definition: +// +// type Exported struct { +// private string // this unexported field will be skipped +// Public string +// } +// +// Using this map as input: +// +// map[string]any{ +// "private": "I will be ignored", +// "Public": "I made it through!", +// } +// +// The following struct will be decoded: +// +// type Exported struct { +// private: "" // field is left with an empty string (zero value) +// Public: "I made it through!" +// } +// +// # Other Configuration +// +// mapstructure is highly configurable. See the DecoderConfig struct +// for other features and options that are supported. +package mapstructure + +import ( + "encoding/json" + "fmt" + "reflect" + "sort" + "strconv" + "strings" + + "github.com/go-viper/mapstructure/v2/internal/errors" +) + +// DecodeHookFunc is the callback function that can be used for +// data transformations. See "DecodeHook" in the DecoderConfig +// struct. +// +// The type must be one of DecodeHookFuncType, DecodeHookFuncKind, or +// DecodeHookFuncValue. +// Values are a superset of Types (Values can return types), and Types are a +// superset of Kinds (Types can return Kinds) and are generally a richer thing +// to use, but Kinds are simpler if you only need those. +// +// The reason DecodeHookFunc is multi-typed is for backwards compatibility: +// we started with Kinds and then realized Types were the better solution, +// but have a promise to not break backwards compat so we now support +// both. +type DecodeHookFunc any + +// DecodeHookFuncType is a DecodeHookFunc which has complete information about +// the source and target types. +type DecodeHookFuncType func(reflect.Type, reflect.Type, any) (any, error) + +// DecodeHookFuncKind is a DecodeHookFunc which knows only the Kinds of the +// source and target types. +type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, any) (any, error) + +// DecodeHookFuncValue is a DecodeHookFunc which has complete access to both the source and target +// values. +type DecodeHookFuncValue func(from reflect.Value, to reflect.Value) (any, error) + +// DecoderConfig is the configuration that is used to create a new decoder +// and allows customization of various aspects of decoding. +type DecoderConfig struct { + // DecodeHook, if set, will be called before any decoding and any + // type conversion (if WeaklyTypedInput is on). This lets you modify + // the values before they're set down onto the resulting struct. The + // DecodeHook is called for every map and value in the input. This means + // that if a struct has embedded fields with squash tags the decode hook + // is called only once with all of the input data, not once for each + // embedded struct. + // + // If an error is returned, the entire decode will fail with that error. + DecodeHook DecodeHookFunc + + // If ErrorUnused is true, then it is an error for there to exist + // keys in the original map that were unused in the decoding process + // (extra keys). + ErrorUnused bool + + // If ErrorUnset is true, then it is an error for there to exist + // fields in the result that were not set in the decoding process + // (extra fields). This only applies to decoding to a struct. This + // will affect all nested structs as well. + ErrorUnset bool + + // AllowUnsetPointer, if set to true, will prevent fields with pointer types + // from being reported as unset, even if ErrorUnset is true and the field was + // not present in the input data. This allows pointer fields to be optional + // without triggering an error when they are missing. + AllowUnsetPointer bool + + // ZeroFields, if set to true, will zero fields before writing them. + // For example, a map will be emptied before decoded values are put in + // it. If this is false, a map will be merged. + ZeroFields bool + + // If WeaklyTypedInput is true, the decoder will make the following + // "weak" conversions: + // + // - bools to string (true = "1", false = "0") + // - numbers to string (base 10) + // - bools to int/uint (true = 1, false = 0) + // - strings to int/uint (base implied by prefix) + // - int to bool (true if value != 0) + // - string to bool (accepts: 1, t, T, TRUE, true, True, 0, f, F, + // FALSE, false, False. Anything else is an error) + // - empty array = empty map and vice versa + // - negative numbers to overflowed uint values (base 10) + // - slice of maps to a merged map + // - single values are converted to slices if required. Each + // element is weakly decoded. For example: "4" can become []int{4} + // if the target type is an int slice. + // + WeaklyTypedInput bool + + // Squash will squash embedded structs. A squash tag may also be + // added to an individual struct field using a tag. For example: + // + // type Parent struct { + // Child `mapstructure:",squash"` + // } + Squash bool + + // Metadata is the struct that will contain extra metadata about + // the decoding. If this is nil, then no metadata will be tracked. + Metadata *Metadata + + // Result is a pointer to the struct that will contain the decoded + // value. + Result any + + // The tag name that mapstructure reads for field names. This + // defaults to "mapstructure" + TagName string + + // The option of the value in the tag that indicates a field should + // be squashed. This defaults to "squash". + SquashTagOption string + + // IgnoreUntaggedFields ignores all struct fields without explicit + // TagName, comparable to `mapstructure:"-"` as default behaviour. + IgnoreUntaggedFields bool + + // MatchName is the function used to match the map key to the struct + // field name or tag. Defaults to `strings.EqualFold`. This can be used + // to implement case-sensitive tag values, support snake casing, etc. + MatchName func(mapKey, fieldName string) bool + + // DecodeNil, if set to true, will cause the DecodeHook (if present) to run + // even if the input is nil. This can be used to provide default values. + DecodeNil bool +} + +// A Decoder takes a raw interface value and turns it into structured +// data, keeping track of rich error information along the way in case +// anything goes wrong. Unlike the basic top-level Decode method, you can +// more finely control how the Decoder behaves using the DecoderConfig +// structure. The top-level Decode method is just a convenience that sets +// up the most basic Decoder. +type Decoder struct { + config *DecoderConfig + cachedDecodeHook func(from reflect.Value, to reflect.Value) (any, error) +} + +// Metadata contains information about decoding a structure that +// is tedious or difficult to get otherwise. +type Metadata struct { + // Keys are the keys of the structure which were successfully decoded + Keys []string + + // Unused is a slice of keys that were found in the raw value but + // weren't decoded since there was no matching field in the result interface + Unused []string + + // Unset is a slice of field names that were found in the result interface + // but weren't set in the decoding process since there was no matching value + // in the input + Unset []string +} + +// Decode takes an input structure and uses reflection to translate it to +// the output structure. output must be a pointer to a map or struct. +func Decode(input any, output any) error { + config := &DecoderConfig{ + Metadata: nil, + Result: output, + } + + decoder, err := NewDecoder(config) + if err != nil { + return err + } + + return decoder.Decode(input) +} + +// WeakDecode is the same as Decode but is shorthand to enable +// WeaklyTypedInput. See DecoderConfig for more info. +func WeakDecode(input, output any) error { + config := &DecoderConfig{ + Metadata: nil, + Result: output, + WeaklyTypedInput: true, + } + + decoder, err := NewDecoder(config) + if err != nil { + return err + } + + return decoder.Decode(input) +} + +// DecodeMetadata is the same as Decode, but is shorthand to +// enable metadata collection. See DecoderConfig for more info. +func DecodeMetadata(input any, output any, metadata *Metadata) error { + config := &DecoderConfig{ + Metadata: metadata, + Result: output, + } + + decoder, err := NewDecoder(config) + if err != nil { + return err + } + + return decoder.Decode(input) +} + +// WeakDecodeMetadata is the same as Decode, but is shorthand to +// enable both WeaklyTypedInput and metadata collection. See +// DecoderConfig for more info. +func WeakDecodeMetadata(input any, output any, metadata *Metadata) error { + config := &DecoderConfig{ + Metadata: metadata, + Result: output, + WeaklyTypedInput: true, + } + + decoder, err := NewDecoder(config) + if err != nil { + return err + } + + return decoder.Decode(input) +} + +// NewDecoder returns a new decoder for the given configuration. Once +// a decoder has been returned, the same configuration must not be used +// again. +func NewDecoder(config *DecoderConfig) (*Decoder, error) { + val := reflect.ValueOf(config.Result) + if val.Kind() != reflect.Ptr { + return nil, errors.New("result must be a pointer") + } + + val = val.Elem() + if !val.CanAddr() { + return nil, errors.New("result must be addressable (a pointer)") + } + + if config.Metadata != nil { + if config.Metadata.Keys == nil { + config.Metadata.Keys = make([]string, 0) + } + + if config.Metadata.Unused == nil { + config.Metadata.Unused = make([]string, 0) + } + + if config.Metadata.Unset == nil { + config.Metadata.Unset = make([]string, 0) + } + } + + if config.TagName == "" { + config.TagName = "mapstructure" + } + + if config.SquashTagOption == "" { + config.SquashTagOption = "squash" + } + + if config.MatchName == nil { + config.MatchName = strings.EqualFold + } + + result := &Decoder{ + config: config, + } + if config.DecodeHook != nil { + result.cachedDecodeHook = cachedDecodeHook(config.DecodeHook) + } + + return result, nil +} + +// Decode decodes the given raw interface to the target pointer specified +// by the configuration. +func (d *Decoder) Decode(input any) error { + err := d.decode("", input, reflect.ValueOf(d.config.Result).Elem()) + + // Retain some of the original behavior when multiple errors ocurr + var joinedErr interface{ Unwrap() []error } + if errors.As(err, &joinedErr) { + return fmt.Errorf("decoding failed due to the following error(s):\n\n%w", err) + } + + return err +} + +// isNil returns true if the input is nil or a typed nil pointer. +func isNil(input any) bool { + if input == nil { + return true + } + val := reflect.ValueOf(input) + return val.Kind() == reflect.Ptr && val.IsNil() +} + +// Decodes an unknown data type into a specific reflection value. +func (d *Decoder) decode(name string, input any, outVal reflect.Value) error { + var ( + inputVal = reflect.ValueOf(input) + outputKind = getKind(outVal) + decodeNil = d.config.DecodeNil && d.cachedDecodeHook != nil + ) + if isNil(input) { + // Typed nils won't match the "input == nil" below, so reset input. + input = nil + } + if input == nil { + // If the data is nil, then we don't set anything, unless ZeroFields is set + // to true. + if d.config.ZeroFields { + outVal.Set(reflect.Zero(outVal.Type())) + + if d.config.Metadata != nil && name != "" { + d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) + } + } + if !decodeNil { + return nil + } + } + if !inputVal.IsValid() { + if !decodeNil { + // If the input value is invalid, then we just set the value + // to be the zero value. + outVal.Set(reflect.Zero(outVal.Type())) + if d.config.Metadata != nil && name != "" { + d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) + } + return nil + } + // Hooks need a valid inputVal, so reset it to zero value of outVal type. + switch outputKind { + case reflect.Struct, reflect.Map: + var mapVal map[string]any + inputVal = reflect.ValueOf(mapVal) // create nil map pointer + case reflect.Slice, reflect.Array: + var sliceVal []any + inputVal = reflect.ValueOf(sliceVal) // create nil slice pointer + default: + inputVal = reflect.Zero(outVal.Type()) + } + } + + if d.cachedDecodeHook != nil { + // We have a DecodeHook, so let's pre-process the input. + var err error + input, err = d.cachedDecodeHook(inputVal, outVal) + if err != nil { + return newDecodeError(name, err) + } + } + if isNil(input) { + return nil + } + + var err error + addMetaKey := true + switch outputKind { + case reflect.Bool: + err = d.decodeBool(name, input, outVal) + case reflect.Interface: + err = d.decodeBasic(name, input, outVal) + case reflect.String: + err = d.decodeString(name, input, outVal) + case reflect.Int: + err = d.decodeInt(name, input, outVal) + case reflect.Uint: + err = d.decodeUint(name, input, outVal) + case reflect.Float32: + err = d.decodeFloat(name, input, outVal) + case reflect.Complex64: + err = d.decodeComplex(name, input, outVal) + case reflect.Struct: + err = d.decodeStruct(name, input, outVal) + case reflect.Map: + err = d.decodeMap(name, input, outVal) + case reflect.Ptr: + addMetaKey, err = d.decodePtr(name, input, outVal) + case reflect.Slice: + err = d.decodeSlice(name, input, outVal) + case reflect.Array: + err = d.decodeArray(name, input, outVal) + case reflect.Func: + err = d.decodeFunc(name, input, outVal) + default: + // If we reached this point then we weren't able to decode it + return newDecodeError(name, fmt.Errorf("unsupported type: %s", outputKind)) + } + + // If we reached here, then we successfully decoded SOMETHING, so + // mark the key as used if we're tracking metainput. + if addMetaKey && d.config.Metadata != nil && name != "" { + d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) + } + + return err +} + +// This decodes a basic type (bool, int, string, etc.) and sets the +// value to "data" of that type. +func (d *Decoder) decodeBasic(name string, data any, val reflect.Value) error { + if val.IsValid() && val.Elem().IsValid() { + elem := val.Elem() + + // If we can't address this element, then its not writable. Instead, + // we make a copy of the value (which is a pointer and therefore + // writable), decode into that, and replace the whole value. + copied := false + if !elem.CanAddr() { + copied = true + + // Make *T + copy := reflect.New(elem.Type()) + + // *T = elem + copy.Elem().Set(elem) + + // Set elem so we decode into it + elem = copy + } + + // Decode. If we have an error then return. We also return right + // away if we're not a copy because that means we decoded directly. + if err := d.decode(name, data, elem); err != nil || !copied { + return err + } + + // If we're a copy, we need to set te final result + val.Set(elem.Elem()) + return nil + } + + dataVal := reflect.ValueOf(data) + + // If the input data is a pointer, and the assigned type is the dereference + // of that exact pointer, then indirect it so that we can assign it. + // Example: *string to string + if dataVal.Kind() == reflect.Ptr && dataVal.Type().Elem() == val.Type() { + dataVal = reflect.Indirect(dataVal) + } + + if !dataVal.IsValid() { + dataVal = reflect.Zero(val.Type()) + } + + dataValType := dataVal.Type() + if !dataValType.AssignableTo(val.Type()) { + return newDecodeError(name, &UnconvertibleTypeError{ + Expected: val, + Value: data, + }) + } + + val.Set(dataVal) + return nil +} + +func (d *Decoder) decodeString(name string, data any, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataKind := getKind(dataVal) + + converted := true + switch { + case dataKind == reflect.String: + val.SetString(dataVal.String()) + case dataKind == reflect.Bool && d.config.WeaklyTypedInput: + if dataVal.Bool() { + val.SetString("1") + } else { + val.SetString("0") + } + case dataKind == reflect.Int && d.config.WeaklyTypedInput: + val.SetString(strconv.FormatInt(dataVal.Int(), 10)) + case dataKind == reflect.Uint && d.config.WeaklyTypedInput: + val.SetString(strconv.FormatUint(dataVal.Uint(), 10)) + case dataKind == reflect.Float32 && d.config.WeaklyTypedInput: + val.SetString(strconv.FormatFloat(dataVal.Float(), 'f', -1, 64)) + case dataKind == reflect.Slice && d.config.WeaklyTypedInput, + dataKind == reflect.Array && d.config.WeaklyTypedInput: + dataType := dataVal.Type() + elemKind := dataType.Elem().Kind() + switch elemKind { + case reflect.Uint8: + var uints []uint8 + if dataKind == reflect.Array { + uints = make([]uint8, dataVal.Len(), dataVal.Len()) + for i := range uints { + uints[i] = dataVal.Index(i).Interface().(uint8) + } + } else { + uints = dataVal.Interface().([]uint8) + } + val.SetString(string(uints)) + default: + converted = false + } + default: + converted = false + } + + if !converted { + return newDecodeError(name, &UnconvertibleTypeError{ + Expected: val, + Value: data, + }) + } + + return nil +} + +func (d *Decoder) decodeInt(name string, data any, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataKind := getKind(dataVal) + dataType := dataVal.Type() + + switch { + case dataKind == reflect.Int: + val.SetInt(dataVal.Int()) + case dataKind == reflect.Uint: + val.SetInt(int64(dataVal.Uint())) + case dataKind == reflect.Float32: + val.SetInt(int64(dataVal.Float())) + case dataKind == reflect.Bool && d.config.WeaklyTypedInput: + if dataVal.Bool() { + val.SetInt(1) + } else { + val.SetInt(0) + } + case dataKind == reflect.String && d.config.WeaklyTypedInput: + str := dataVal.String() + if str == "" { + str = "0" + } + + i, err := strconv.ParseInt(str, 0, val.Type().Bits()) + if err == nil { + val.SetInt(i) + } else { + return newDecodeError(name, &ParseError{ + Expected: val, + Value: data, + Err: wrapStrconvNumError(err), + }) + } + case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": + jn := data.(json.Number) + i, err := jn.Int64() + if err != nil { + return newDecodeError(name, &ParseError{ + Expected: val, + Value: data, + Err: err, + }) + } + val.SetInt(i) + default: + return newDecodeError(name, &UnconvertibleTypeError{ + Expected: val, + Value: data, + }) + } + + return nil +} + +func (d *Decoder) decodeUint(name string, data any, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataKind := getKind(dataVal) + dataType := dataVal.Type() + + switch { + case dataKind == reflect.Int: + i := dataVal.Int() + if i < 0 && !d.config.WeaklyTypedInput { + return newDecodeError(name, &ParseError{ + Expected: val, + Value: data, + Err: fmt.Errorf("%d overflows uint", i), + }) + } + val.SetUint(uint64(i)) + case dataKind == reflect.Uint: + val.SetUint(dataVal.Uint()) + case dataKind == reflect.Float32: + f := dataVal.Float() + if f < 0 && !d.config.WeaklyTypedInput { + return newDecodeError(name, &ParseError{ + Expected: val, + Value: data, + Err: fmt.Errorf("%f overflows uint", f), + }) + } + val.SetUint(uint64(f)) + case dataKind == reflect.Bool && d.config.WeaklyTypedInput: + if dataVal.Bool() { + val.SetUint(1) + } else { + val.SetUint(0) + } + case dataKind == reflect.String && d.config.WeaklyTypedInput: + str := dataVal.String() + if str == "" { + str = "0" + } + + i, err := strconv.ParseUint(str, 0, val.Type().Bits()) + if err == nil { + val.SetUint(i) + } else { + return newDecodeError(name, &ParseError{ + Expected: val, + Value: data, + Err: wrapStrconvNumError(err), + }) + } + case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": + jn := data.(json.Number) + i, err := strconv.ParseUint(string(jn), 0, 64) + if err != nil { + return newDecodeError(name, &ParseError{ + Expected: val, + Value: data, + Err: wrapStrconvNumError(err), + }) + } + val.SetUint(i) + default: + return newDecodeError(name, &UnconvertibleTypeError{ + Expected: val, + Value: data, + }) + } + + return nil +} + +func (d *Decoder) decodeBool(name string, data any, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataKind := getKind(dataVal) + + switch { + case dataKind == reflect.Bool: + val.SetBool(dataVal.Bool()) + case dataKind == reflect.Int && d.config.WeaklyTypedInput: + val.SetBool(dataVal.Int() != 0) + case dataKind == reflect.Uint && d.config.WeaklyTypedInput: + val.SetBool(dataVal.Uint() != 0) + case dataKind == reflect.Float32 && d.config.WeaklyTypedInput: + val.SetBool(dataVal.Float() != 0) + case dataKind == reflect.String && d.config.WeaklyTypedInput: + b, err := strconv.ParseBool(dataVal.String()) + if err == nil { + val.SetBool(b) + } else if dataVal.String() == "" { + val.SetBool(false) + } else { + return newDecodeError(name, &ParseError{ + Expected: val, + Value: data, + Err: wrapStrconvNumError(err), + }) + } + default: + return newDecodeError(name, &UnconvertibleTypeError{ + Expected: val, + Value: data, + }) + } + + return nil +} + +func (d *Decoder) decodeFloat(name string, data any, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataKind := getKind(dataVal) + dataType := dataVal.Type() + + switch { + case dataKind == reflect.Int: + val.SetFloat(float64(dataVal.Int())) + case dataKind == reflect.Uint: + val.SetFloat(float64(dataVal.Uint())) + case dataKind == reflect.Float32: + val.SetFloat(dataVal.Float()) + case dataKind == reflect.Bool && d.config.WeaklyTypedInput: + if dataVal.Bool() { + val.SetFloat(1) + } else { + val.SetFloat(0) + } + case dataKind == reflect.String && d.config.WeaklyTypedInput: + str := dataVal.String() + if str == "" { + str = "0" + } + + f, err := strconv.ParseFloat(str, val.Type().Bits()) + if err == nil { + val.SetFloat(f) + } else { + return newDecodeError(name, &ParseError{ + Expected: val, + Value: data, + Err: wrapStrconvNumError(err), + }) + } + case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": + jn := data.(json.Number) + i, err := jn.Float64() + if err != nil { + return newDecodeError(name, &ParseError{ + Expected: val, + Value: data, + Err: err, + }) + } + val.SetFloat(i) + default: + return newDecodeError(name, &UnconvertibleTypeError{ + Expected: val, + Value: data, + }) + } + + return nil +} + +func (d *Decoder) decodeComplex(name string, data any, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataKind := getKind(dataVal) + + switch { + case dataKind == reflect.Complex64: + val.SetComplex(dataVal.Complex()) + default: + return newDecodeError(name, &UnconvertibleTypeError{ + Expected: val, + Value: data, + }) + } + + return nil +} + +func (d *Decoder) decodeMap(name string, data any, val reflect.Value) error { + valType := val.Type() + valKeyType := valType.Key() + valElemType := valType.Elem() + + // By default we overwrite keys in the current map + valMap := val + + // If the map is nil or we're purposely zeroing fields, make a new map + if valMap.IsNil() || d.config.ZeroFields { + // Make a new map to hold our result + mapType := reflect.MapOf(valKeyType, valElemType) + valMap = reflect.MakeMap(mapType) + } + + dataVal := reflect.ValueOf(data) + + // Resolve any levels of indirection + for dataVal.Kind() == reflect.Pointer { + dataVal = reflect.Indirect(dataVal) + } + + // Check input type and based on the input type jump to the proper func + switch dataVal.Kind() { + case reflect.Map: + return d.decodeMapFromMap(name, dataVal, val, valMap) + + case reflect.Struct: + return d.decodeMapFromStruct(name, dataVal, val, valMap) + + case reflect.Array, reflect.Slice: + if d.config.WeaklyTypedInput { + return d.decodeMapFromSlice(name, dataVal, val, valMap) + } + + fallthrough + + default: + return newDecodeError(name, &UnconvertibleTypeError{ + Expected: val, + Value: data, + }) + } +} + +func (d *Decoder) decodeMapFromSlice(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error { + // Special case for BC reasons (covered by tests) + if dataVal.Len() == 0 { + val.Set(valMap) + return nil + } + + for i := 0; i < dataVal.Len(); i++ { + err := d.decode( + name+"["+strconv.Itoa(i)+"]", + dataVal.Index(i).Interface(), val) + if err != nil { + return err + } + } + + return nil +} + +func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error { + valType := val.Type() + valKeyType := valType.Key() + valElemType := valType.Elem() + + // Accumulate errors + var errs []error + + // If the input data is empty, then we just match what the input data is. + if dataVal.Len() == 0 { + if dataVal.IsNil() { + if !val.IsNil() { + val.Set(dataVal) + } + } else { + // Set to empty allocated value + val.Set(valMap) + } + + return nil + } + + for _, k := range dataVal.MapKeys() { + fieldName := name + "[" + k.String() + "]" + + // First decode the key into the proper type + currentKey := reflect.Indirect(reflect.New(valKeyType)) + if err := d.decode(fieldName, k.Interface(), currentKey); err != nil { + errs = append(errs, err) + continue + } + + // Next decode the data into the proper type + v := dataVal.MapIndex(k).Interface() + currentVal := reflect.Indirect(reflect.New(valElemType)) + if err := d.decode(fieldName, v, currentVal); err != nil { + errs = append(errs, err) + continue + } + + valMap.SetMapIndex(currentKey, currentVal) + } + + // Set the built up map to the value + val.Set(valMap) + + return errors.Join(errs...) +} + +func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error { + typ := dataVal.Type() + for i := 0; i < typ.NumField(); i++ { + // Get the StructField first since this is a cheap operation. If the + // field is unexported, then ignore it. + f := typ.Field(i) + if f.PkgPath != "" { + continue + } + + // Next get the actual value of this field and verify it is assignable + // to the map value. + v := dataVal.Field(i) + if !v.Type().AssignableTo(valMap.Type().Elem()) { + return newDecodeError( + name+"."+f.Name, + fmt.Errorf("cannot assign type %q to map value field of type %q", v.Type(), valMap.Type().Elem()), + ) + } + + tagValue := f.Tag.Get(d.config.TagName) + keyName := f.Name + + if tagValue == "" && d.config.IgnoreUntaggedFields { + continue + } + + // If Squash is set in the config, we squash the field down. + squash := d.config.Squash && v.Kind() == reflect.Struct && f.Anonymous + + v = dereferencePtrToStructIfNeeded(v, d.config.TagName) + + // Determine the name of the key in the map + if index := strings.Index(tagValue, ","); index != -1 { + if tagValue[:index] == "-" { + continue + } + // If "omitempty" is specified in the tag, it ignores empty values. + if strings.Index(tagValue[index+1:], "omitempty") != -1 && isEmptyValue(v) { + continue + } + + // If "omitzero" is specified in the tag, it ignores zero values. + if strings.Index(tagValue[index+1:], "omitzero") != -1 && v.IsZero() { + continue + } + + // If "squash" is specified in the tag, we squash the field down. + squash = squash || strings.Contains(tagValue[index+1:], d.config.SquashTagOption) + if squash { + // When squashing, the embedded type can be a pointer to a struct. + if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct { + v = v.Elem() + } + + // The final type must be a struct + if v.Kind() != reflect.Struct { + return newDecodeError( + name+"."+f.Name, + fmt.Errorf("cannot squash non-struct type %q", v.Type()), + ) + } + } else { + if strings.Index(tagValue[index+1:], "remain") != -1 { + if v.Kind() != reflect.Map { + return newDecodeError( + name+"."+f.Name, + fmt.Errorf("error remain-tag field with invalid type: %q", v.Type()), + ) + } + + ptr := v.MapRange() + for ptr.Next() { + valMap.SetMapIndex(ptr.Key(), ptr.Value()) + } + continue + } + } + if keyNameTagValue := tagValue[:index]; keyNameTagValue != "" { + keyName = keyNameTagValue + } + } else if len(tagValue) > 0 { + if tagValue == "-" { + continue + } + keyName = tagValue + } + + switch v.Kind() { + // this is an embedded struct, so handle it differently + case reflect.Struct: + x := reflect.New(v.Type()) + x.Elem().Set(v) + + vType := valMap.Type() + vKeyType := vType.Key() + vElemType := vType.Elem() + mType := reflect.MapOf(vKeyType, vElemType) + vMap := reflect.MakeMap(mType) + + // Creating a pointer to a map so that other methods can completely + // overwrite the map if need be (looking at you decodeMapFromMap). The + // indirection allows the underlying map to be settable (CanSet() == true) + // where as reflect.MakeMap returns an unsettable map. + addrVal := reflect.New(vMap.Type()) + reflect.Indirect(addrVal).Set(vMap) + + err := d.decode(keyName, x.Interface(), reflect.Indirect(addrVal)) + if err != nil { + return err + } + + // the underlying map may have been completely overwritten so pull + // it indirectly out of the enclosing value. + vMap = reflect.Indirect(addrVal) + + if squash { + for _, k := range vMap.MapKeys() { + valMap.SetMapIndex(k, vMap.MapIndex(k)) + } + } else { + valMap.SetMapIndex(reflect.ValueOf(keyName), vMap) + } + + default: + valMap.SetMapIndex(reflect.ValueOf(keyName), v) + } + } + + if val.CanAddr() { + val.Set(valMap) + } + + return nil +} + +func (d *Decoder) decodePtr(name string, data any, val reflect.Value) (bool, error) { + // If the input data is nil, then we want to just set the output + // pointer to be nil as well. + isNil := data == nil + if !isNil { + switch v := reflect.Indirect(reflect.ValueOf(data)); v.Kind() { + case reflect.Chan, + reflect.Func, + reflect.Interface, + reflect.Map, + reflect.Ptr, + reflect.Slice: + isNil = v.IsNil() + } + } + if isNil { + if !val.IsNil() && val.CanSet() { + nilValue := reflect.New(val.Type()).Elem() + val.Set(nilValue) + } + + return true, nil + } + + // Create an element of the concrete (non pointer) type and decode + // into that. Then set the value of the pointer to this type. + valType := val.Type() + valElemType := valType.Elem() + if val.CanSet() { + realVal := val + if realVal.IsNil() || d.config.ZeroFields { + realVal = reflect.New(valElemType) + } + + if err := d.decode(name, data, reflect.Indirect(realVal)); err != nil { + return false, err + } + + val.Set(realVal) + } else { + if err := d.decode(name, data, reflect.Indirect(val)); err != nil { + return false, err + } + } + return false, nil +} + +func (d *Decoder) decodeFunc(name string, data any, val reflect.Value) error { + // Create an element of the concrete (non pointer) type and decode + // into that. Then set the value of the pointer to this type. + dataVal := reflect.Indirect(reflect.ValueOf(data)) + if val.Type() != dataVal.Type() { + return newDecodeError(name, &UnconvertibleTypeError{ + Expected: val, + Value: data, + }) + } + val.Set(dataVal) + return nil +} + +func (d *Decoder) decodeSlice(name string, data any, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataValKind := dataVal.Kind() + valType := val.Type() + valElemType := valType.Elem() + sliceType := reflect.SliceOf(valElemType) + + // If we have a non array/slice type then we first attempt to convert. + if dataValKind != reflect.Array && dataValKind != reflect.Slice { + if d.config.WeaklyTypedInput { + switch { + // Slice and array we use the normal logic + case dataValKind == reflect.Slice, dataValKind == reflect.Array: + break + + // Empty maps turn into empty slices + case dataValKind == reflect.Map: + if dataVal.Len() == 0 { + val.Set(reflect.MakeSlice(sliceType, 0, 0)) + return nil + } + // Create slice of maps of other sizes + return d.decodeSlice(name, []any{data}, val) + + case dataValKind == reflect.String && valElemType.Kind() == reflect.Uint8: + return d.decodeSlice(name, []byte(dataVal.String()), val) + + // All other types we try to convert to the slice type + // and "lift" it into it. i.e. a string becomes a string slice. + default: + // Just re-try this function with data as a slice. + return d.decodeSlice(name, []any{data}, val) + } + } + + return newDecodeError(name, + fmt.Errorf("source data must be an array or slice, got %s", dataValKind)) + } + + // If the input value is nil, then don't allocate since empty != nil + if dataValKind != reflect.Array && dataVal.IsNil() { + return nil + } + + valSlice := val + if valSlice.IsNil() || d.config.ZeroFields { + // Make a new slice to hold our result, same size as the original data. + valSlice = reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len()) + } else if valSlice.Len() > dataVal.Len() { + valSlice = valSlice.Slice(0, dataVal.Len()) + } + + // Accumulate any errors + var errs []error + + for i := 0; i < dataVal.Len(); i++ { + currentData := dataVal.Index(i).Interface() + for valSlice.Len() <= i { + valSlice = reflect.Append(valSlice, reflect.Zero(valElemType)) + } + currentField := valSlice.Index(i) + + fieldName := name + "[" + strconv.Itoa(i) + "]" + if err := d.decode(fieldName, currentData, currentField); err != nil { + errs = append(errs, err) + } + } + + // Finally, set the value to the slice we built up + val.Set(valSlice) + + return errors.Join(errs...) +} + +func (d *Decoder) decodeArray(name string, data any, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataValKind := dataVal.Kind() + valType := val.Type() + valElemType := valType.Elem() + arrayType := reflect.ArrayOf(valType.Len(), valElemType) + + valArray := val + + if isComparable(valArray) && valArray.Interface() == reflect.Zero(valArray.Type()).Interface() || d.config.ZeroFields { + // Check input type + if dataValKind != reflect.Array && dataValKind != reflect.Slice { + if d.config.WeaklyTypedInput { + switch { + // Empty maps turn into empty arrays + case dataValKind == reflect.Map: + if dataVal.Len() == 0 { + val.Set(reflect.Zero(arrayType)) + return nil + } + + // All other types we try to convert to the array type + // and "lift" it into it. i.e. a string becomes a string array. + default: + // Just re-try this function with data as a slice. + return d.decodeArray(name, []any{data}, val) + } + } + + return newDecodeError(name, + fmt.Errorf("source data must be an array or slice, got %s", dataValKind)) + + } + if dataVal.Len() > arrayType.Len() { + return newDecodeError(name, + fmt.Errorf("expected source data to have length less or equal to %d, got %d", arrayType.Len(), dataVal.Len())) + } + + // Make a new array to hold our result, same size as the original data. + valArray = reflect.New(arrayType).Elem() + } + + // Accumulate any errors + var errs []error + + for i := 0; i < dataVal.Len(); i++ { + currentData := dataVal.Index(i).Interface() + currentField := valArray.Index(i) + + fieldName := name + "[" + strconv.Itoa(i) + "]" + if err := d.decode(fieldName, currentData, currentField); err != nil { + errs = append(errs, err) + } + } + + // Finally, set the value to the array we built up + val.Set(valArray) + + return errors.Join(errs...) +} + +func (d *Decoder) decodeStruct(name string, data any, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + + // If the type of the value to write to and the data match directly, + // then we just set it directly instead of recursing into the structure. + if dataVal.Type() == val.Type() { + val.Set(dataVal) + return nil + } + + dataValKind := dataVal.Kind() + switch dataValKind { + case reflect.Map: + return d.decodeStructFromMap(name, dataVal, val) + + case reflect.Struct: + // Not the most efficient way to do this but we can optimize later if + // we want to. To convert from struct to struct we go to map first + // as an intermediary. + + // Make a new map to hold our result + mapType := reflect.TypeOf((map[string]any)(nil)) + mval := reflect.MakeMap(mapType) + + // Creating a pointer to a map so that other methods can completely + // overwrite the map if need be (looking at you decodeMapFromMap). The + // indirection allows the underlying map to be settable (CanSet() == true) + // where as reflect.MakeMap returns an unsettable map. + addrVal := reflect.New(mval.Type()) + + reflect.Indirect(addrVal).Set(mval) + if err := d.decodeMapFromStruct(name, dataVal, reflect.Indirect(addrVal), mval); err != nil { + return err + } + + result := d.decodeStructFromMap(name, reflect.Indirect(addrVal), val) + return result + + default: + return newDecodeError(name, + fmt.Errorf("expected a map or struct, got %q", dataValKind)) + } +} + +func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) error { + dataValType := dataVal.Type() + if kind := dataValType.Key().Kind(); kind != reflect.String && kind != reflect.Interface { + return newDecodeError(name, + fmt.Errorf("needs a map with string keys, has %q keys", kind)) + } + + dataValKeys := make(map[reflect.Value]struct{}) + dataValKeysUnused := make(map[any]struct{}) + for _, dataValKey := range dataVal.MapKeys() { + dataValKeys[dataValKey] = struct{}{} + dataValKeysUnused[dataValKey.Interface()] = struct{}{} + } + + targetValKeysUnused := make(map[any]struct{}) + + var errs []error + + // This slice will keep track of all the structs we'll be decoding. + // There can be more than one struct if there are embedded structs + // that are squashed. + structs := make([]reflect.Value, 1, 5) + structs[0] = val + + // Compile the list of all the fields that we're going to be decoding + // from all the structs. + type field struct { + field reflect.StructField + val reflect.Value + } + + // remainField is set to a valid field set with the "remain" tag if + // we are keeping track of remaining values. + var remainField *field + + fields := []field{} + for len(structs) > 0 { + structVal := structs[0] + structs = structs[1:] + + structType := structVal.Type() + + for i := 0; i < structType.NumField(); i++ { + fieldType := structType.Field(i) + fieldVal := structVal.Field(i) + if fieldVal.Kind() == reflect.Ptr && fieldVal.Elem().Kind() == reflect.Struct { + // Handle embedded struct pointers as embedded structs. + fieldVal = fieldVal.Elem() + } + + // If "squash" is specified in the tag, we squash the field down. + squash := d.config.Squash && fieldVal.Kind() == reflect.Struct && fieldType.Anonymous + remain := false + + // We always parse the tags cause we're looking for other tags too + tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",") + for _, tag := range tagParts[1:] { + if tag == d.config.SquashTagOption { + squash = true + break + } + + if tag == "remain" { + remain = true + break + } + } + + if squash { + switch fieldVal.Kind() { + case reflect.Struct: + structs = append(structs, fieldVal) + case reflect.Interface: + if !fieldVal.IsNil() { + structs = append(structs, fieldVal.Elem().Elem()) + } + default: + errs = append(errs, newDecodeError( + name+"."+fieldType.Name, + fmt.Errorf("unsupported type for squash: %s", fieldVal.Kind()), + )) + } + continue + } + + // Build our field + if remain { + remainField = &field{fieldType, fieldVal} + } else { + // Normal struct field, store it away + fields = append(fields, field{fieldType, fieldVal}) + } + } + } + + // for fieldType, field := range fields { + for _, f := range fields { + field, fieldValue := f.field, f.val + fieldName := field.Name + + tagValue := field.Tag.Get(d.config.TagName) + if tagValue == "" && d.config.IgnoreUntaggedFields { + continue + } + tagValue = strings.SplitN(tagValue, ",", 2)[0] + if tagValue != "" { + fieldName = tagValue + } + + rawMapKey := reflect.ValueOf(fieldName) + rawMapVal := dataVal.MapIndex(rawMapKey) + if !rawMapVal.IsValid() { + // Do a slower search by iterating over each key and + // doing case-insensitive search. + for dataValKey := range dataValKeys { + mK, ok := dataValKey.Interface().(string) + if !ok { + // Not a string key + continue + } + + if d.config.MatchName(mK, fieldName) { + rawMapKey = dataValKey + rawMapVal = dataVal.MapIndex(dataValKey) + break + } + } + + if !rawMapVal.IsValid() { + // There was no matching key in the map for the value in + // the struct. Remember it for potential errors and metadata. + if !(d.config.AllowUnsetPointer && fieldValue.Kind() == reflect.Ptr) { + targetValKeysUnused[fieldName] = struct{}{} + } + continue + } + } + + if !fieldValue.IsValid() { + // This should never happen + panic("field is not valid") + } + + // If we can't set the field, then it is unexported or something, + // and we just continue onwards. + if !fieldValue.CanSet() { + continue + } + + // Delete the key we're using from the unused map so we stop tracking + delete(dataValKeysUnused, rawMapKey.Interface()) + + // If the name is empty string, then we're at the root, and we + // don't dot-join the fields. + if name != "" { + fieldName = name + "." + fieldName + } + + if err := d.decode(fieldName, rawMapVal.Interface(), fieldValue); err != nil { + errs = append(errs, err) + } + } + + // If we have a "remain"-tagged field and we have unused keys then + // we put the unused keys directly into the remain field. + if remainField != nil && len(dataValKeysUnused) > 0 { + // Build a map of only the unused values + remain := map[any]any{} + for key := range dataValKeysUnused { + remain[key] = dataVal.MapIndex(reflect.ValueOf(key)).Interface() + } + + // Decode it as-if we were just decoding this map onto our map. + if err := d.decodeMap(name, remain, remainField.val); err != nil { + errs = append(errs, err) + } + + // Set the map to nil so we have none so that the next check will + // not error (ErrorUnused) + dataValKeysUnused = nil + } + + if d.config.ErrorUnused && len(dataValKeysUnused) > 0 { + keys := make([]string, 0, len(dataValKeysUnused)) + for rawKey := range dataValKeysUnused { + keys = append(keys, rawKey.(string)) + } + sort.Strings(keys) + + errs = append(errs, newDecodeError( + name, + fmt.Errorf("has invalid keys: %s", strings.Join(keys, ", ")), + )) + } + + if d.config.ErrorUnset && len(targetValKeysUnused) > 0 { + keys := make([]string, 0, len(targetValKeysUnused)) + for rawKey := range targetValKeysUnused { + keys = append(keys, rawKey.(string)) + } + sort.Strings(keys) + + errs = append(errs, newDecodeError( + name, + fmt.Errorf("has unset fields: %s", strings.Join(keys, ", ")), + )) + } + + if err := errors.Join(errs...); err != nil { + return err + } + + // Add the unused keys to the list of unused keys if we're tracking metadata + if d.config.Metadata != nil { + for rawKey := range dataValKeysUnused { + key := rawKey.(string) + if name != "" { + key = name + "." + key + } + + d.config.Metadata.Unused = append(d.config.Metadata.Unused, key) + } + for rawKey := range targetValKeysUnused { + key := rawKey.(string) + if name != "" { + key = name + "." + key + } + + d.config.Metadata.Unset = append(d.config.Metadata.Unset, key) + } + } + + return nil +} + +func isEmptyValue(v reflect.Value) bool { + switch getKind(v) { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + } + return false +} + +func getKind(val reflect.Value) reflect.Kind { + kind := val.Kind() + + switch { + case kind >= reflect.Int && kind <= reflect.Int64: + return reflect.Int + case kind >= reflect.Uint && kind <= reflect.Uint64: + return reflect.Uint + case kind >= reflect.Float32 && kind <= reflect.Float64: + return reflect.Float32 + case kind >= reflect.Complex64 && kind <= reflect.Complex128: + return reflect.Complex64 + default: + return kind + } +} + +func isStructTypeConvertibleToMap(typ reflect.Type, checkMapstructureTags bool, tagName string) bool { + for i := 0; i < typ.NumField(); i++ { + f := typ.Field(i) + if f.PkgPath == "" && !checkMapstructureTags { // check for unexported fields + return true + } + if checkMapstructureTags && f.Tag.Get(tagName) != "" { // check for mapstructure tags inside + return true + } + } + return false +} + +func dereferencePtrToStructIfNeeded(v reflect.Value, tagName string) reflect.Value { + if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { + return v + } + deref := v.Elem() + derefT := deref.Type() + if isStructTypeConvertibleToMap(derefT, true, tagName) { + return deref + } + return v +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/reflect_go1_19.go b/vendor/github.com/go-viper/mapstructure/v2/reflect_go1_19.go new file mode 100644 index 000000000..d0913fff6 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/reflect_go1_19.go @@ -0,0 +1,44 @@ +//go:build !go1.20 + +package mapstructure + +import "reflect" + +func isComparable(v reflect.Value) bool { + k := v.Kind() + switch k { + case reflect.Invalid: + return false + + case reflect.Array: + switch v.Type().Elem().Kind() { + case reflect.Interface, reflect.Array, reflect.Struct: + for i := 0; i < v.Type().Len(); i++ { + // if !v.Index(i).Comparable() { + if !isComparable(v.Index(i)) { + return false + } + } + return true + } + return v.Type().Comparable() + + case reflect.Interface: + // return v.Elem().Comparable() + return isComparable(v.Elem()) + + case reflect.Struct: + for i := 0; i < v.NumField(); i++ { + return false + + // if !v.Field(i).Comparable() { + if !isComparable(v.Field(i)) { + return false + } + } + return true + + default: + return v.Type().Comparable() + } +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/reflect_go1_20.go b/vendor/github.com/go-viper/mapstructure/v2/reflect_go1_20.go new file mode 100644 index 000000000..f8255a1b1 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/reflect_go1_20.go @@ -0,0 +1,10 @@ +//go:build go1.20 + +package mapstructure + +import "reflect" + +// TODO: remove once we drop support for Go <1.20 +func isComparable(v reflect.Value) bool { + return v.Comparable() +} diff --git a/vendor/github.com/gookit/color/.gitignore b/vendor/github.com/gookit/color/.gitignore deleted file mode 100644 index 5efa5e3f0..000000000 --- a/vendor/github.com/gookit/color/.gitignore +++ /dev/null @@ -1,20 +0,0 @@ -*.log -*.swp -.idea -*.patch -### Go template -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib - -# Test binary, build with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out -.DS_Store -app -demo diff --git a/vendor/github.com/gookit/color/.nojekyll b/vendor/github.com/gookit/color/.nojekyll deleted file mode 100644 index e69de29bb..000000000 diff --git a/vendor/github.com/gookit/color/LICENSE b/vendor/github.com/gookit/color/LICENSE deleted file mode 100644 index d839cdc1a..000000000 --- a/vendor/github.com/gookit/color/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 inhere - -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. \ No newline at end of file diff --git a/vendor/github.com/gookit/color/README.md b/vendor/github.com/gookit/color/README.md deleted file mode 100644 index 77d50ca3b..000000000 --- a/vendor/github.com/gookit/color/README.md +++ /dev/null @@ -1,580 +0,0 @@ -# CLI Color - -![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/gookit/color?style=flat-square) -[![Actions Status](https://github.com/gookit/color/workflows/action-tests/badge.svg)](https://github.com/gookit/color/actions) -[![Codacy Badge](https://app.codacy.com/project/badge/Grade/7fef8d74c1d64afc99ce0f2c6d3f8af1)](https://www.codacy.com/gh/gookit/color/dashboard?utm_source=github.com&utm_medium=referral&utm_content=gookit/color&utm_campaign=Badge_Grade) -[![GoDoc](https://godoc.org/github.com/gookit/color?status.svg)](https://pkg.go.dev/github.com/gookit/color?tab=overview) -[![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/gookit/color)](https://github.com/gookit/color) -[![Build Status](https://travis-ci.org/gookit/color.svg?branch=master)](https://travis-ci.org/gookit/color) -[![Coverage Status](https://coveralls.io/repos/github/gookit/color/badge.svg?branch=master)](https://coveralls.io/github/gookit/color?branch=master) -[![Go Report Card](https://goreportcard.com/badge/github.com/gookit/color)](https://goreportcard.com/report/github.com/gookit/color) - -A command-line color library with 16/256/True color support, universal API methods and Windows support. - -> **[中文说明](README.zh-CN.md)** - -Basic color preview: - -![basic-color](_examples/images/basic-color2.png) - -Now, 256 colors and RGB colors have also been supported to work in Windows CMD and PowerShell: - -![color-on-cmd-pwsh](_examples/images/color-on-cmd-pwsh.jpg) - -## Features - - - Simple to use, zero dependencies - - Supports rich color output: 16-color (4-bit), 256-color (8-bit), true color (24-bit, RGB) - - 16-color output is the most commonly used and most widely supported, working on any Windows version - - Since `v1.2.4` **the 256-color (8-bit), true color (24-bit) support windows CMD and PowerShell** - - See [this gist](https://gist.github.com/XVilka/8346728) for information on true color support - - Support converts `HEX` `HSL` value to RGB color - - Generic API methods: `Print`, `Printf`, `Println`, `Sprint`, `Sprintf` - - Supports HTML tag-style color rendering, such as `message text`. - - In addition to using built-in tags, it also supports custom color attributes - - Custom color attributes support the use of 16 color names, 256 color values, rgb color values and hex color values - - Support working on Windows `cmd` and `powerShell` terminal - - Basic colors: `Bold`, `Black`, `White`, `Gray`, `Red`, `Green`, `Yellow`, `Blue`, `Magenta`, `Cyan` - - Additional styles: `Info`, `Note`, `Light`, `Error`, `Danger`, `Notice`, `Success`, `Comment`, `Primary`, `Warning`, `Question`, `Secondary` - - Support by set `NO_COLOR` for disable color or use `FORCE_COLOR` for force open color render. - - Support Rgb, 256, 16 color conversion - -## GoDoc - - - [godoc for gopkg](https://pkg.go.dev/gopkg.in/gookit/color.v1) - - [godoc for github](https://pkg.go.dev/github.com/gookit/color) - -## Install - -```bash -go get github.com/gookit/color -``` - -## Quick start - -```go -package main - -import ( - "fmt" - - "github.com/gookit/color" -) - -func main() { - // quick use package func - color.Redp("Simple to use color") - color.Redln("Simple to use color") - color.Greenp("Simple to use color\n") - color.Cyanln("Simple to use color") - color.Yellowln("Simple to use color") - - // quick use like fmt.Print* - color.Red.Println("Simple to use color") - color.Green.Print("Simple to use color\n") - color.Cyan.Printf("Simple to use %s\n", "color") - color.Yellow.Printf("Simple to use %s\n", "color") - - // use like func - red := color.FgRed.Render - green := color.FgGreen.Render - fmt.Printf("%s line %s library\n", red("Command"), green("color")) - - // custom color - color.New(color.FgWhite, color.BgBlack).Println("custom color style") - - // can also: - color.Style{color.FgCyan, color.OpBold}.Println("custom color style") - - // internal theme/style: - color.Info.Tips("message") - color.Info.Prompt("message") - color.Info.Println("message") - color.Warn.Println("message") - color.Error.Println("message") - - // use style tag - color.Print("hello, welcome\n") - // Custom label attr: Supports the use of 16 color names, 256 color values, rgb color values and hex color values - color.Println("hello, welcome") - - // apply a style tag - color.Tag("info").Println("info style text") - - // prompt message - color.Info.Prompt("prompt style message") - color.Warn.Prompt("prompt style message") - - // tips message - color.Info.Tips("tips style message") - color.Warn.Tips("tips style message") -} -``` - -Run demo: `go run ./_examples/demo.go` - -![colored-out](_examples/images/color-demo.jpg) - -## Basic/16 color - -Supported on any Windows version. Provide generic API methods: `Print`, `Printf`, `Println`, `Sprint`, `Sprintf` - -```go -color.Bold.Println("bold message") -color.Cyan.Println("yellow message") -color.Yellow.Println("yellow message") -color.Magenta.Println("yellow message") - -// Only use foreground color -color.FgCyan.Printf("Simple to use %s\n", "color") -// Only use background color -color.BgRed.Printf("Simple to use %s\n", "color") -``` - -Run demo: `go run ./_examples/color_16.go` - -![basic-color](_examples/images/basic-color.png) - -### Custom build color - -```go -// Full custom: foreground, background, option -myStyle := color.New(color.FgWhite, color.BgBlack, color.OpBold) -myStyle.Println("custom color style") - -// can also: -color.Style{color.FgCyan, color.OpBold}.Println("custom color style") -``` - -custom set console settings: - -```go -// set console color -color.Set(color.FgCyan) - -// print message -fmt.Print("message") - -// reset console settings -color.Reset() -``` - -### Additional styles - -provide generic API methods: `Print`, `Printf`, `Println`, `Sprint`, `Sprintf` - -print message use defined style: - -```go -color.Info.Println("Info message") -color.Notice.Println("Notice message") -color.Error.Println("Error message") -// ... -``` - -Run demo: `go run ./_examples/theme_basic.go` - -![theme-basic](_examples/images/theme-basic.png) - -**Tips style** - -```go -color.Info.Tips("Info tips message") -color.Notice.Tips("Notice tips message") -color.Error.Tips("Error tips message") -color.Secondary.Tips("Secondary tips message") -``` - -Run demo: `go run ./_examples/theme_tips.go` - -![theme-tips](_examples/images/theme-tips.png) - -**Prompt Style** - -```go -color.Info.Prompt("Info prompt message") -color.Notice.Prompt("Notice prompt message") -color.Error.Prompt("Error prompt message") -// ... -``` - -Run demo: `go run ./_examples/theme_prompt.go` - -![theme-prompt](_examples/images/theme-prompt.png) - -**Block Style** - -```go -color.Danger.Block("Danger block message") -color.Warn.Block("Warn block message") -// ... -``` - -Run demo: `go run ./_examples/theme_block.go` - -![theme-block](_examples/images/theme-block.png) - -## 256-color usage - -> 256 colors support Windows CMD, PowerShell environment after `v1.2.4` - -### Set the foreground or background color - -- `color.C256(val uint8, isBg ...bool) Color256` - -```go -c := color.C256(132) // fg color -c.Println("message") -c.Printf("format %s", "message") - -c := color.C256(132, true) // bg color -c.Println("message") -c.Printf("format %s", "message") -``` - -### 256-color style - -Can be used to set foreground and background colors at the same time. - -- `S256(fgAndBg ...uint8) *Style256` - -```go -s := color.S256(32, 203) -s.Println("message") -s.Printf("format %s", "message") -``` - -with options: - -```go -s := color.S256(32, 203) -s.SetOpts(color.Opts{color.OpBold}) - -s.Println("style with options") -s.Printf("style with %s\n", "options") -``` - -Run demo: `go run ./_examples/color_256.go` - -![color-tags](_examples/images/color-256.png) - -## RGB/True color - -> RGB colors support Windows `CMD`, `PowerShell` environment after `v1.2.4` - -**Preview:** - -> Run demo: `Run demo: go run ./_examples/color_rgb.go` - -![color-rgb](_examples/images/color-rgb.png) - -example: - -```go -color.RGB(30, 144, 255).Println("message. use RGB number") - -color.HEX("#1976D2").Println("blue-darken") -color.HEX("#D50000", true).Println("red-accent. use HEX style") - -color.RGBStyleFromString("213,0,0").Println("red-accent. use RGB number") -color.HEXStyle("eee", "D50000").Println("deep-purple color") -``` - -### Set the foreground or background color - -- `color.RGB(r, g, b uint8, isBg ...bool) RGBColor` - -```go -c := color.RGB(30,144,255) // fg color -c.Println("message") -c.Printf("format %s", "message") - -c := color.RGB(30,144,255, true) // bg color -c.Println("message") -c.Printf("format %s", "message") -``` - -Create a style from an hexadecimal color string: - -- `color.HEX(hex string, isBg ...bool) RGBColor` - -```go -c := color.HEX("ccc") // can also: "cccccc" "#cccccc" -c.Println("message") -c.Printf("format %s", "message") - -c = color.HEX("aabbcc", true) // as bg color -c.Println("message") -c.Printf("format %s", "message") -``` - -### RGB color style - -Can be used to set the foreground and background colors at the same time. - -- `color.NewRGBStyle(fg RGBColor, bg ...RGBColor) *RGBStyle` - -```go -s := color.NewRGBStyle(RGB(20, 144, 234), RGB(234, 78, 23)) -s.Println("message") -s.Printf("format %s", "message") -``` - -Create a style from an hexadecimal color string: - -- `color.HEXStyle(fg string, bg ...string) *RGBStyle` - -```go -s := color.HEXStyle("11aa23", "eee") -s.Println("message") -s.Printf("format %s", "message") -``` - -with options: - -```go -s := color.HEXStyle("11aa23", "eee") -s.SetOpts(color.Opts{color.OpBold}) - -s.Println("style with options") -s.Printf("style with %s\n", "options") -``` - -## HTML-like tag usage - -`Print,Printf,Println` functions support auto parse and render color tags. - -```go - text := ` - gookit/color: - A command-line - color library with 256-color - and True-color support, - universal API methods - and Windows support. -` - color.Print(text) -``` - -Preview, code please see [_examples/demo_tag.go](_examples/demo_tag.go): - -![demo_tag](_examples/images/demo_tag.png) - -**Tag formats:** - -- Use built in tags: `CONTENT` e.g: `message` -- Custom tag attributes: `CONTENT` e.g: `wel` - -> **Supported** on Windows `cmd.exe` `PowerShell`. - -Examples: - -```go -// use style tag -color.Print("hello, welcome") -color.Println("hello") -color.Println("hello") -color.Println("hello") - -// custom color attributes -color.Print("hello, welcome\n") - -// Custom label attr: Supports the use of 16 color names, 256 color values, rgb color values and hex color values -color.Println("hello, welcome") -``` - -### Tag attributes - -tag attributes format: - -```text -attr format: - // VALUE please see var: FgColors, BgColors, AllOptions - "fg=VALUE;bg=VALUE;op=VALUE" - -16 color: - "fg=yellow" - "bg=red" - "op=bold,underscore" // option is allow multi value - "fg=white;bg=blue;op=bold" - "fg=white;op=bold,underscore" - -256 color: - "fg=167" - "fg=167;bg=23" - "fg=167;bg=23;op=bold" - -True color: - // hex - "fg=fc1cac" - "fg=fc1cac;bg=c2c3c4" - // r,g,b - "fg=23,45,214" - "fg=23,45,214;bg=109,99,88" -``` - -> tag attributes parse please see `func ParseCodeFromAttr()` - -### Built-in tags - -Built-in tags please see var `colorTags` in [color_tag.go](color_tag.go) - -```go -// use style tag -color.Print("hello, welcome") -color.Println("hello") -color.Println("hello") -``` - -Run demo: `go run ./_examples/color_tag.go` - -![color-tags](_examples/images/color-tags.png) - -**Use `color.Tag` build message**: - -```go -// set a style tag -color.Tag("info").Print("info style text") -color.Tag("info").Printf("%s style text", "info") -color.Tag("info").Println("info style text") -``` - -## Color convert - -Supports conversion between Rgb, 256, 16 colors, `Rgb <=> 256 <=> 16` - -```go -basic := color.Red -basic.Println("basic color") - -c256 := color.Red.C256() -c256.Println("256 color") -c256.C16().Println("basic color") - -rgb := color.Red.RGB() -rgb.Println("rgb color") -rgb.C256().Println("256 color") -``` - -### Convert utils - -`color` has many built-in color conversion utility functions. - -```go -func Basic2hex(val uint8) string - -func Bg2Fg(val uint8) uint8 -func Fg2Bg(val uint8) uint8 - -func C256ToRgb(val uint8) (rgb []uint8) -func C256ToRgbV1(val uint8) (rgb []uint8) - -func Hex2basic(hex string, asBg ...bool) uint8 -func Hex2rgb(hex string) []int -func HexToRGB(hex string) []int -func HexToRgb(hex string) (rgb []int) - -func HslIntToRgb(h, s, l int) (rgb []uint8) -func HslToRgb(h, s, l float64) (rgb []uint8) -func HsvToRgb(h, s, v int) (rgb []uint8) - -func Rgb2ansi(r, g, b uint8, isBg bool) uint8 -func Rgb2basic(r, g, b uint8, isBg bool) uint8 -func Rgb2hex(rgb []int) string -func Rgb2short(r, g, b uint8) uint8 -func RgbTo256(r, g, b uint8) uint8 -func RgbTo256Table() map[string]uint8 -func RgbToAnsi(r, g, b uint8, isBg bool) uint8 -func RgbToHex(rgb []int) string -func RgbToHsl(r, g, b uint8) []float64 -func RgbToHslInt(r, g, b uint8) []int -``` - -**Convert to `RGBColor`**: - -- `func RGBFromSlice(rgb []uint8, isBg ...bool) RGBColor` -- `func RGBFromString(rgb string, isBg ...bool) RGBColor` -- `func HEX(hex string, isBg ...bool) RGBColor` -- `func HSL(h, s, l float64, isBg ...bool) RGBColor` -- `func HSLInt(h, s, l int, isBg ...bool) RGBColor` - -## Util functions - -There are some useful functions reference - -- `Disable()` disable color render -- `SetOutput(io.Writer)` custom set the colored text output writer -- `ForceOpenColor()` force open color render -- `Colors2code(colors ...Color) string` Convert colors to code. return like "32;45;3" -- `ClearCode(str string) string` Use for clear color codes -- `ClearTag(s string) string` clear all color html-tag for a string -- `IsConsole(w io.Writer)` Determine whether w is one of stderr, stdout, stdin - -> More useful func please see https://pkg.go.dev/github.com/gookit/color - -### Detect color level - -`color` automatically checks the color levels supported by the current environment. - -```go -// Level is the color level supported by a terminal. -type Level = terminfo.ColorLevel - -// terminal color available level alias of the terminfo.ColorLevel* -const ( - LevelNo = terminfo.ColorLevelNone // not support color. - Level16 = terminfo.ColorLevelBasic // basic - 3/4 bit color supported - Level256 = terminfo.ColorLevelHundreds // hundreds - 8-bit color supported - LevelRgb = terminfo.ColorLevelMillions // millions - (24 bit)true color supported -) -``` - -- `func SupportColor() bool` Whether the current environment supports color output -- `func Support256Color() bool` Whether the current environment supports 256-color output -- `func SupportTrueColor() bool` Whether the current environment supports (RGB)True-color output -- `func TermColorLevel() Level` Get the currently supported color level - - -## Projects using color - -Check out these projects, which use https://github.com/gookit/color : - -- https://github.com/Delta456/box-cli-maker Make Highly Customized Boxes for your CLI -- https://github.com/flipped-aurora/gin-vue-admin 基于gin+vue搭建的(中)后台系统框架 -- https://github.com/JanDeDobbeleer/oh-my-posh A prompt theme engine for any shell. -- https://github.com/jesseduffield/lazygit Simple terminal UI for git commands -- https://github.com/olivia-ai/olivia 💁‍♀️Your new best friend powered by an artificial neural network -- https://github.com/pterm/pterm PTerm is a modern Go module to beautify console output. Featuring charts, progressbars, tables, trees, etc. -- https://github.com/securego/gosec Golang security checker -- https://github.com/TNK-Studio/lazykube ⎈ The lazier way to manage kubernetes. -- [+ See More](https://pkg.go.dev/github.com/gookit/color?tab=importedby) - -## Gookit packages - - - [gookit/ini](https://github.com/gookit/ini) Go config management, use INI files - - [gookit/rux](https://github.com/gookit/rux) Simple and fast request router for golang HTTP - - [gookit/gcli](https://github.com/gookit/gcli) build CLI application, tool library, running CLI commands - - [gookit/slog](https://github.com/gookit/slog) Concise and extensible go log library - - [gookit/event](https://github.com/gookit/event) Lightweight event manager and dispatcher implements by Go - - [gookit/cache](https://github.com/gookit/cache) Generic cache use and cache manager for golang. support File, Memory, Redis, Memcached. - - [gookit/config](https://github.com/gookit/config) Go config management. support JSON, YAML, TOML, INI, HCL, ENV and Flags - - [gookit/color](https://github.com/gookit/color) A command-line color library with true color support, universal API methods and Windows support - - [gookit/filter](https://github.com/gookit/filter) Provide filtering, sanitizing, and conversion of golang data - - [gookit/validate](https://github.com/gookit/validate) Use for data validation and filtering. support Map, Struct, Form data - - [gookit/goutil](https://github.com/gookit/goutil) Some utils for the Go: string, array/slice, map, format, cli, env, filesystem, test and more - - More, please see https://github.com/gookit - -## See also - - - [inhere/console](https://github.com/inhere/php-console) - - [xo/terminfo](https://github.com/xo/terminfo) - - [beego/bee](https://github.com/beego/bee) - - [issue9/term](https://github.com/issue9/term) - - [muesli/termenv](https://github.com/muesli/termenv) - - [ANSI escape code](https://en.wikipedia.org/wiki/ANSI_escape_code) - - [Standard ANSI color map](https://conemu.github.io/en/AnsiEscapeCodes.html#Standard_ANSI_color_map) - - [Terminal Colors](https://gist.github.com/XVilka/8346728) - -## License - -[MIT](/LICENSE) diff --git a/vendor/github.com/gookit/color/README.zh-CN.md b/vendor/github.com/gookit/color/README.zh-CN.md deleted file mode 100644 index 192a89c5a..000000000 --- a/vendor/github.com/gookit/color/README.zh-CN.md +++ /dev/null @@ -1,591 +0,0 @@ -# CLI Color - -![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/gookit/color?style=flat-square) -[![Actions Status](https://github.com/gookit/color/workflows/action-tests/badge.svg)](https://github.com/gookit/color/actions) -[![Codacy Badge](https://app.codacy.com/project/badge/Grade/7fef8d74c1d64afc99ce0f2c6d3f8af1)](https://www.codacy.com/gh/gookit/color/dashboard?utm_source=github.com&utm_medium=referral&utm_content=gookit/color&utm_campaign=Badge_Grade) -[![GoDoc](https://godoc.org/github.com/gookit/color?status.svg)](https://pkg.go.dev/github.com/gookit/color?tab=overview) -[![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/gookit/color)](https://github.com/gookit/color) -[![Build Status](https://travis-ci.org/gookit/color.svg?branch=master)](https://travis-ci.org/gookit/color) -[![Coverage Status](https://coveralls.io/repos/github/gookit/color/badge.svg?branch=master)](https://coveralls.io/github/gookit/color?branch=master) -[![Go Report Card](https://goreportcard.com/badge/github.com/gookit/color)](https://goreportcard.com/report/github.com/gookit/color) - -Golang下的命令行色彩使用库, 拥有丰富的色彩(16/256/True)渲染输出,通用的API方法,兼容Windows系统 - -> **[EN README](README.md)** - -基本颜色预览: - -![basic-color](_examples/images/basic-color2.png) - -现在,256色和RGB色彩也已经支持windows CMD和PowerShell中工作: - -![color-on-cmd-pwsh](_examples/images/color-on-cmd-pwsh.jpg) - -## 功能特色 - - - 使用简单方便 - - 支持丰富的颜色输出, 16色(4bit),256色(8bit),RGB色彩(24bit, RGB) - - 16色(4bit)是最常用和支持最广的,支持Windows `cmd.exe` - - 自 `v1.2.4` 起 **256色(8bit),RGB色彩(24bit)均支持Windows CMD和PowerShell终端** - - 请查看 [this gist](https://gist.github.com/XVilka/8346728) 了解支持RGB色彩的终端 - - 支持转换 `HEX` `HSL` 等为RGB色彩 - - 提供通用的API方法:`Print` `Printf` `Println` `Sprint` `Sprintf` - - 同时支持html标签式的颜色渲染,除了使用内置标签,同时支持自定义颜色属性 - - 例如: `this an message text` 标签内部文本将会渲染对应色彩 - - 自定义颜色属性: 支持使用16色彩名称,256色彩值,rgb色彩值以及hex色彩值 - - 基础色彩: `Bold` `Black` `White` `Gray` `Red` `Green` `Yellow` `Blue` `Magenta` `Cyan` - - 扩展风格: `Info` `Note` `Light` `Error` `Danger` `Notice` `Success` `Comment` `Primary` `Warning` `Question` `Secondary` - - 支持通过设置环境变量 `NO_COLOR` 来禁用色彩,或者使用 `FORCE_COLOR` 来强制使用色彩渲染. - - 支持 Rgb, 256, 16 色彩之间的互相转换 - - 支持Linux、Mac,同时兼容Windows系统环境 - -## GoDoc - - - [godoc for gopkg](https://pkg.go.dev/gopkg.in/gookit/color.v1) - - [godoc for github](https://pkg.go.dev/github.com/gookit/color) - -## 安装 - -```bash -go get github.com/gookit/color -``` - -## 快速开始 - -如下,引入当前包就可以快速的使用 - -```go -package main - -import ( - "fmt" - - "github.com/gookit/color" -) - -func main() { - // 简单快速的使用,跟 fmt.Print* 类似 - color.Redp("Simple to use color") - color.Redln("Simple to use color") - color.Greenp("Simple to use color\n") - color.Cyanln("Simple to use color") - color.Yellowln("Simple to use color") - - // 简单快速的使用,跟 fmt.Print* 类似 - color.Red.Println("Simple to use color") - color.Green.Print("Simple to use color\n") - color.Cyan.Printf("Simple to use %s\n", "color") - color.Yellow.Printf("Simple to use %s\n", "color") - - // use like func - red := color.FgRed.Render - green := color.FgGreen.Render - fmt.Printf("%s line %s library\n", red("Command"), green("color")) - - // 自定义颜色 - color.New(color.FgWhite, color.BgBlack).Println("custom color style") - - // 也可以: - color.Style{color.FgCyan, color.OpBold}.Println("custom color style") - - // internal style: - color.Info.Println("message") - color.Warn.Println("message") - color.Error.Println("message") - - // 使用内置颜色标签 - color.Print("hello, welcome\n") - // 自定义标签: 支持使用16色彩名称,256色彩值,rgb色彩值以及hex色彩值 - color.Println("hello, welcome") - - // apply a style tag - color.Tag("info").Println("info style text") - - // prompt message - color.Info.Prompt("prompt style message") - color.Warn.Prompt("prompt style message") - - // tips message - color.Info.Tips("tips style message") - color.Warn.Tips("tips style message") -} -``` - -> 运行 demo: `go run ./_examples/demo.go` - -![colored-out](_examples/images/color-demo.jpg) - -## 基础颜色(16-color) - -提供通用的API方法:`Print` `Printf` `Println` `Sprint` `Sprintf` - -> 支持在windows `cmd.exe` `powerShell` 等终端使用 - -```go -color.Bold.Println("bold message") -color.Black.Println("bold message") -color.White.Println("bold message") -// ... - -// Only use foreground color -color.FgCyan.Printf("Simple to use %s\n", "color") -// Only use background color -color.BgRed.Printf("Simple to use %s\n", "color") -``` - -> 运行demo: `go run ./_examples/color_16.go` - -![basic-color](_examples/images/basic-color.png) - -### 构建风格 - -```go -// 仅设置前景色 -color.FgCyan.Printf("Simple to use %s\n", "color") -// 仅设置背景色 -color.BgRed.Printf("Simple to use %s\n", "color") - -// 完全自定义: 前景色 背景色 选项 -style := color.New(color.FgWhite, color.BgBlack, color.OpBold) -style.Println("custom color style") - -// 也可以: -color.Style{color.FgCyan, color.OpBold}.Println("custom color style") -``` - -直接设置控制台属性: - -```go -// 设置console颜色 -color.Set(color.FgCyan) - -// 输出信息 -fmt.Print("message") - -// 重置console颜色 -color.Reset() -``` - -> 当然,color已经内置丰富的色彩风格支持 - -### 扩展风格方法 - -提供通用的API方法:`Print` `Printf` `Println` `Sprint` `Sprintf` - -> 支持在windows `cmd.exe` `powerShell` 等终端使用 - -基础使用: - -```go -// print message -color.Info.Println("Info message") -color.Note.Println("Note message") -color.Notice.Println("Notice message") -// ... -``` - -Run demo: `go run ./_examples/theme_basic.go` - -![theme-basic](_examples/images/theme-basic.png) - -**简约提示风格** - -```go -color.Info.Tips("Info tips message") -color.Notice.Tips("Notice tips message") -color.Error.Tips("Error tips message") -// ... -``` - -Run demo: `go run ./_examples/theme_tips.go` - -![theme-tips](_examples/images/theme-tips.png) - -**着重提示风格** - -```go -color.Info.Prompt("Info prompt message") -color.Error.Prompt("Error prompt message") -color.Danger.Prompt("Danger prompt message") -``` - -Run demo: `go run ./_examples/theme_prompt.go` - -![theme-prompt](_examples/images/theme-prompt.png) - -**强调提示风格** - -```go -color.Warn.Block("Warn block message") -color.Debug.Block("Debug block message") -color.Question.Block("Question block message") -``` - -Run demo: `go run ./_examples/theme_block.go` - -![theme-block](_examples/images/theme-block.png) - -## 256 色彩使用 - -> 256色彩在 `v1.2.4` 后支持Windows CMD,PowerShell 环境 - -### 使用前景或后景色 - -- `color.C256(val uint8, isBg ...bool) Color256` - -```go -c := color.C256(132) // fg color -c.Println("message") -c.Printf("format %s", "message") - -c := color.C256(132, true) // bg color -c.Println("message") -c.Printf("format %s", "message") -``` - -### 使用256 色彩风格 - -> 可同时设置前景和背景色 - -- `color.S256(fgAndBg ...uint8) *Style256` - -```go -s := color.S256(32, 203) -s.Println("message") -s.Printf("format %s", "message") -``` - -可以同时添加选项设置: - -```go -s := color.S256(32, 203) -s.SetOpts(color.Opts{color.OpBold}) - -s.Println("style with options") -s.Printf("style with %s\n", "options") -``` - -> 运行 demo: `go run ./_examples/color_256.go` - -![color-tags](_examples/images/color-256.png) - -## RGB/True色彩使用 - -> RGB色彩在 `v1.2.4` 后支持 Windows `CMD`, `PowerShell` 环境 - -**效果预览:** - -> 运行 demo: `Run demo: go run ./_examples/color_rgb.go` - -![color-rgb](_examples/images/color-rgb.png) - -代码示例: - -```go -color.RGB(30, 144, 255).Println("message. use RGB number") - -color.HEX("#1976D2").Println("blue-darken") -color.HEX("#D50000", true).Println("red-accent. use HEX style") - -color.RGBStyleFromString("213,0,0").Println("red-accent. use RGB number") -color.HEXStyle("eee", "D50000").Println("deep-purple color") -``` - -### 使用前景或后景色 - -- `color.RGB(r, g, b uint8, isBg ...bool) RGBColor` - -```go -c := color.RGB(30,144,255) // fg color -c.Println("message") -c.Printf("format %s", "message") - -c := color.RGB(30,144,255, true) // bg color -c.Println("message") -c.Printf("format %s", "message") -``` - -- `color.HEX(hex string, isBg ...bool) RGBColor` 从16进制颜色创建 - -```go -c := color.HEX("ccc") // 也可以写为: "cccccc" "#cccccc" -c.Println("message") -c.Printf("format %s", "message") - -c = color.HEX("aabbcc", true) // as bg color -c.Println("message") -c.Printf("format %s", "message") -``` - -### 使用RGB风格 - -> TIP: 可同时设置前景和背景色 - -- `color.NewRGBStyle(fg RGBColor, bg ...RGBColor) *RGBStyle` - -```go -s := color.NewRGBStyle(RGB(20, 144, 234), RGB(234, 78, 23)) -s.Println("message") -s.Printf("format %s", "message") -``` - -- `color.HEXStyle(fg string, bg ...string) *RGBStyle` 从16进制颜色创建 - -```go -s := color.HEXStyle("11aa23", "eee") -s.Println("message") -s.Printf("format %s", "message") -``` - -- 可以同时添加选项设置: - -```go -s := color.HEXStyle("11aa23", "eee") -s.SetOpts(color.Opts{color.OpBold}) - -s.Println("style with options") -s.Printf("style with %s\n", "options") -``` - -## 使用颜色标签 - -`Print,Printf,Println` 等方法支持自动解析并渲染 HTML 风格的颜色标签 - -> **支持** 在windows `cmd.exe` `PowerShell` 使用 - -简单示例: - -```go - text := ` - gookit/color: - A command-line - color library with 256-color - and True-color support, - universal API methods - and Windows support. -` - color.Print(text) -``` - -输出效果, 示例代码请看 [_examples/demo_tag.go](_examples/demo_tag.go): - -![demo_tag](_examples/images/demo_tag.png) - -**颜色标签格式:** - -- 直接使用内置风格标签: `CONTENT` e.g: `message` -- 自定义标签属性: `CONTENT` e.g: `wel` - -使用内置的颜色标签,可以非常方便简单的构建自己需要的任何格式 - -> 同时支持自定义颜色属性: 支持使用16色彩名称,256色彩值,rgb色彩值以及hex色彩值 - -```go -// 使用内置的 color tag -color.Print("hello, welcome") -color.Println("hello") -color.Println("hello") -color.Println("hello") - -// 自定义颜色属性 -color.Print("hello, welcome\n") - -// 自定义颜色属性: 支持使用16色彩名称,256色彩值,rgb色彩值以及hex色彩值 -color.Println("hello, welcome") -``` - -### 自定义标签属性 - -标签属性格式: - -```text -attr format: - // VALUE please see var: FgColors, BgColors, AllOptions - "fg=VALUE;bg=VALUE;op=VALUE" - -16 color: - "fg=yellow" - "bg=red" - "op=bold,underscore" // option is allow multi value - "fg=white;bg=blue;op=bold" - "fg=white;op=bold,underscore" - -256 color: - "fg=167" - "fg=167;bg=23" - "fg=167;bg=23;op=bold" - -True color: - // hex - "fg=fc1cac" - "fg=fc1cac;bg=c2c3c4" - // r,g,b - "fg=23,45,214" - "fg=23,45,214;bg=109,99,88" -``` - -> tag attributes parse please see `func ParseCodeFromAttr()` - -### 内置标签 - -内置标签请参见变量 `colorTags` 定义, 源文件 [color_tag.go](color_tag.go) - -```go -// use style tag -color.Print("hello, welcome") -color.Println("hello") -color.Println("hello") -``` - -> 运行 demo: `go run ./_examples/color_tag.go` - -![color-tags](_examples/images/color-tags.png) - -**使用 `color.Tag` 包装标签**: - -可以使用通用的输出API方法,给后面输出的文本信息加上给定的颜色风格标签 - -```go -// set a style tag -color.Tag("info").Print("info style text") -color.Tag("info").Printf("%s style text", "info") -color.Tag("info").Println("info style text") -``` - -## 颜色转换 - -支持 Rgb, 256, 16 色彩之间的互相转换 `Rgb <=> 256 <=> 16` - -```go -basic := color.Red -basic.Println("basic color") - -c256 := color.Red.C256() -c256.Println("256 color") -c256.C16().Println("basic color") - -rgb := color.Red.RGB() -rgb.Println("rgb color") -rgb.C256().Println("256 color") -``` - -### 颜色转换方法 - -`color` 内置了许多颜色转换工具方法 - -```go -func Basic2hex(val uint8) string - -func Bg2Fg(val uint8) uint8 -func Fg2Bg(val uint8) uint8 - -func C256ToRgb(val uint8) (rgb []uint8) -func C256ToRgbV1(val uint8) (rgb []uint8) - -func Hex2basic(hex string, asBg ...bool) uint8 -func Hex2rgb(hex string) []int -func HexToRGB(hex string) []int -func HexToRgb(hex string) (rgb []int) - -func HslIntToRgb(h, s, l int) (rgb []uint8) -func HslToRgb(h, s, l float64) (rgb []uint8) -func HsvToRgb(h, s, v int) (rgb []uint8) - -func Rgb2ansi(r, g, b uint8, isBg bool) uint8 -func Rgb2basic(r, g, b uint8, isBg bool) uint8 -func Rgb2hex(rgb []int) string -func Rgb2short(r, g, b uint8) uint8 -func RgbTo256(r, g, b uint8) uint8 -func RgbTo256Table() map[string]uint8 -func RgbToAnsi(r, g, b uint8, isBg bool) uint8 -func RgbToHex(rgb []int) string -func RgbToHsl(r, g, b uint8) []float64 -func RgbToHslInt(r, g, b uint8) []int -``` - -**转换为 `RGBColor`**: - -- `func RGBFromSlice(rgb []uint8, isBg ...bool) RGBColor` -- `func RGBFromString(rgb string, isBg ...bool) RGBColor` -- `func HEX(hex string, isBg ...bool) RGBColor` -- `func HSL(h, s, l float64, isBg ...bool) RGBColor` -- `func HSLInt(h, s, l int, isBg ...bool) RGBColor` - -## 工具方法参考 - -一些有用的工具方法参考 - -- `Disable()` 禁用颜色渲染输出 -- `SetOutput(io.Writer)` 自定义设置渲染后的彩色文本输出位置 -- `ForceOpenColor()` 强制开启颜色渲染 -- `ClearCode(str string) string` Use for clear color codes -- `Colors2code(colors ...Color) string` Convert colors to code. return like "32;45;3" -- `ClearTag(s string) string` clear all color html-tag for a string -- `IsConsole(w io.Writer)` Determine whether w is one of stderr, stdout, stdin -- 更多请查看文档 https://pkg.go.dev/github.com/gookit/color - -### 检测支持的颜色级别 - -`color` 会自动检查当前环境支持的颜色级别 - -```go -// Level is the color level supported by a terminal. -type Level = terminfo.ColorLevel - -// terminal color available level alias of the terminfo.ColorLevel* -const ( - LevelNo = terminfo.ColorLevelNone // not support color. - Level16 = terminfo.ColorLevelBasic // basic - 3/4 bit color supported - Level256 = terminfo.ColorLevelHundreds // hundreds - 8-bit color supported - LevelRgb = terminfo.ColorLevelMillions // millions - (24 bit)true color supported -) -``` - -- `func SupportColor() bool` 当前环境是否支持色彩输出 -- `func Support256Color() bool` 当前环境是否支持256色彩输出 -- `func SupportTrueColor() bool` 当前环境是否支持(RGB)True色彩输出 -- `func TermColorLevel() Level` 获取当前支持的颜色级别 - -## 使用Color的项目 - -看看这些使用了 https://github.com/gookit/color 的项目: - -- https://github.com/Delta456/box-cli-maker Make Highly Customized Boxes for your CLI -- https://github.com/flipped-aurora/gin-vue-admin 基于gin+vue搭建的(中)后台系统框架 -- https://github.com/JanDeDobbeleer/oh-my-posh A prompt theme engine for any shell. -- https://github.com/jesseduffield/lazygit Simple terminal UI for git commands -- https://github.com/olivia-ai/olivia 💁‍♀️Your new best friend powered by an artificial neural network -- https://github.com/pterm/pterm PTerm is a modern Go module to beautify console output. Featuring charts, progressbars, tables, trees, etc. -- https://github.com/securego/gosec Golang security checker -- https://github.com/TNK-Studio/lazykube ⎈ The lazier way to manage kubernetes. -- [+ See More](https://pkg.go.dev/github.com/gookit/color?tab=importedby) - -## Gookit 工具包 - - - [gookit/ini](https://github.com/gookit/ini) INI配置读取管理,支持多文件加载,数据覆盖合并, 解析ENV变量, 解析变量引用 - - [gookit/rux](https://github.com/gookit/rux) Simple and fast request router for golang HTTP - - [gookit/gcli](https://github.com/gookit/gcli) Go的命令行应用,工具库,运行CLI命令,支持命令行色彩,用户交互,进度显示,数据格式化显示 - - [gookit/slog](https://github.com/gookit/slog) 简洁易扩展的go日志库 - - [gookit/event](https://github.com/gookit/event) Go实现的轻量级的事件管理、调度程序库, 支持设置监听器的优先级, 支持对一组事件进行监听 - - [gookit/cache](https://github.com/gookit/cache) 通用的缓存使用包装库,通过包装各种常用的驱动,来提供统一的使用API - - [gookit/config](https://github.com/gookit/config) Go应用配置管理,支持多种格式(JSON, YAML, TOML, INI, HCL, ENV, Flags),多文件加载,远程文件加载,数据合并 - - [gookit/color](https://github.com/gookit/color) CLI 控制台颜色渲染工具库, 拥有简洁的使用API,支持16色,256色,RGB色彩渲染输出 - - [gookit/filter](https://github.com/gookit/filter) 提供对Golang数据的过滤,净化,转换 - - [gookit/validate](https://github.com/gookit/validate) Go通用的数据验证与过滤库,使用简单,内置大部分常用验证、过滤器 - - [gookit/goutil](https://github.com/gookit/goutil) Go 的一些工具函数,格式化,特殊处理,常用信息获取等 - - 更多请查看 https://github.com/gookit - -## 参考项目 - - - [inhere/console](https://github.com/inhere/php-console) - - [muesli/termenv](https://github.com/muesli/termenv) - - [xo/terminfo](https://github.com/xo/terminfo) - - [beego/bee](https://github.com/beego/bee) - - [issue9/term](https://github.com/issue9/term) - - [ANSI转义序列](https://zh.wikipedia.org/wiki/ANSI转义序列) - - [Standard ANSI color map](https://conemu.github.io/en/AnsiEscapeCodes.html#Standard_ANSI_color_map) - - [Terminal Colors](https://gist.github.com/XVilka/8346728) - -## License - -MIT diff --git a/vendor/github.com/gookit/color/any.go b/vendor/github.com/gookit/color/any.go deleted file mode 100644 index 8bf31c181..000000000 --- a/vendor/github.com/gookit/color/any.go +++ /dev/null @@ -1,6 +0,0 @@ -//go:build !go1.18 -// +build !go1.18 - -package color - -type any = interface{} diff --git a/vendor/github.com/gookit/color/color.go b/vendor/github.com/gookit/color/color.go deleted file mode 100644 index 22de1b045..000000000 --- a/vendor/github.com/gookit/color/color.go +++ /dev/null @@ -1,251 +0,0 @@ -/* -Package color is command line color library. -Support rich color rendering output, universal API method, compatible with Windows system - -Source code and other details for the project are available at GitHub: - - https://github.com/gookit/color - -More usage please see README and tests. -*/ -package color - -import ( - "fmt" - "io" - "os" - "regexp" - "strings" - - "github.com/xo/terminfo" -) - -// color render templates -// -// ESC 操作的表示: -// -// "\033"(Octal 8进制) = "\x1b"(Hexadecimal 16进制) = 27 (10进制) -const ( - // StartSet chars - StartSet = "\x1b[" - // ResetSet close all properties. - ResetSet = "\x1b[0m" - // SettingTpl string. - SettingTpl = "\x1b[%sm" - // FullColorTpl for build color code - FullColorTpl = "\x1b[%sm%s\x1b[0m" - // CodeSuffix string for color code. - CodeSuffix = "[0m" -) - -// CodeExpr regex to clear color codes eg "\033[1;36mText\x1b[0m" -const CodeExpr = `\033\[[\d;?]+m` - -var ( - // Enable switch color render and display - // - // NOTICE: - // if ENV: NO_COLOR is not empty, will disable color render. - Enable = os.Getenv("NO_COLOR") == "" - // RenderTag render HTML tag on call color.Xprint, color.PrintX - RenderTag = true - // debug mode for development. - // - // set env: - // COLOR_DEBUG_MODE=on - // or: - // COLOR_DEBUG_MODE=on go run ./_examples/envcheck.go - debugMode = os.Getenv("COLOR_DEBUG_MODE") == "on" - // inner errors record on detect color level - innerErrs []error - // output the default io.Writer message print - output io.Writer = os.Stdout - // mark current env, It's like in `cmd.exe` - // if not in windows, it's always False. - isLikeInCmd bool - // the color support level for current terminal - // needVTP - need enable VTP, only for Windows OS - colorLevel, needVTP = detectTermColorLevel() - // match color codes - codeRegex = regexp.MustCompile(CodeExpr) - // mark current env is support color. - // Always: isLikeInCmd != supportColor - // supportColor = IsSupportColor() -) - -// TermColorLevel Get the currently supported color level -func TermColorLevel() Level { - return colorLevel -} - -// SupportColor Whether the current environment supports color output -func SupportColor() bool { - return colorLevel > terminfo.ColorLevelNone -} - -// Support16Color on the current ENV -// func Support16Color() bool { -// return colorLevel > terminfo.ColorLevelNone -// } - -// Support256Color Whether the current environment supports 256-color output -func Support256Color() bool { - return colorLevel > terminfo.ColorLevelBasic -} - -// SupportTrueColor Whether the current environment supports (RGB)True-color output -func SupportTrueColor() bool { - return colorLevel > terminfo.ColorLevelHundreds -} - -/************************************************************* - * global settings - *************************************************************/ - -// Set console color attributes -func Set(colors ...Color) (int, error) { - code := Colors2code(colors...) - err := SetTerminal(code) - return 0, err -} - -// Reset reset console color attributes -func Reset() (int, error) { - err := ResetTerminal() - return 0, err -} - -// Disable disable color output -func Disable() bool { - oldVal := Enable - Enable = false - return oldVal -} - -// NotRenderTag on call color.Xprint, color.PrintX -func NotRenderTag() { - RenderTag = false -} - -// SetOutput set default colored text output -func SetOutput(w io.Writer) { - output = w -} - -// ResetOutput reset output -func ResetOutput() { - output = os.Stdout -} - -// ResetOptions reset all package option setting -func ResetOptions() { - RenderTag = true - Enable = true - output = os.Stdout -} - -// ForceSetColorLevel force open color render -func ForceSetColorLevel(level terminfo.ColorLevel) terminfo.ColorLevel { - oldLevelVal := colorLevel - colorLevel = level - return oldLevelVal -} - -// ForceColor force open color render -func ForceColor() terminfo.ColorLevel { - return ForceOpenColor() -} - -// ForceOpenColor force open color render -func ForceOpenColor() terminfo.ColorLevel { - // TODO should set level to ? - return ForceSetColorLevel(terminfo.ColorLevelMillions) -} - -// IsLikeInCmd check result -// -// Deprecated: please don't use -func IsLikeInCmd() bool { - return isLikeInCmd -} - -// InnerErrs info -func InnerErrs() []error { - return innerErrs -} - -/************************************************************* - * render color code - *************************************************************/ - -// RenderCode render message by color code. -// -// Usage: -// -// msg := RenderCode("3;32;45", "some", "message") -func RenderCode(code string, args ...any) string { - var message string - if ln := len(args); ln == 0 { - return "" - } - - message = fmt.Sprint(args...) - if len(code) == 0 { - return message - } - - // disabled OR not support color - if !Enable || !SupportColor() { - return ClearCode(message) - } - - // return fmt.Sprintf(FullColorTpl, code, message) - return StartSet + code + "m" + message + ResetSet -} - -// RenderWithSpaces Render code with spaces. -// If the number of args is > 1, a space will be added between the args -func RenderWithSpaces(code string, args ...any) string { - msg := formatArgsForPrintln(args) - if len(code) == 0 { - return msg - } - - // disabled OR not support color - if !Enable || !SupportColor() { - return ClearCode(msg) - } - - return StartSet + code + "m" + msg + ResetSet -} - -// RenderString render a string with color code. -// -// Usage: -// -// msg := RenderString("3;32;45", "a message") -func RenderString(code string, str string) string { - if len(code) == 0 || str == "" { - return str - } - - // disabled OR not support color - if !Enable || !SupportColor() { - return ClearCode(str) - } - - // return fmt.Sprintf(FullColorTpl, code, str) - return StartSet + code + "m" + str + ResetSet -} - -// ClearCode clear color codes. -// -// eg: -// -// "\033[36;1mText\x1b[0m" -> "Text" -func ClearCode(str string) string { - if !strings.Contains(str, CodeSuffix) { - return str - } - return codeRegex.ReplaceAllString(str, "") -} diff --git a/vendor/github.com/gookit/color/color_256.go b/vendor/github.com/gookit/color/color_256.go deleted file mode 100644 index 79ae5f8df..000000000 --- a/vendor/github.com/gookit/color/color_256.go +++ /dev/null @@ -1,303 +0,0 @@ -package color - -import ( - "fmt" - "strconv" - "strings" -) - -/* -from wikipedia, 256 color: - ESC[ … 38;5; … m选择前景色 - ESC[ … 48;5; … m选择背景色 - 0- 7:标准颜色(同 ESC[30–37m) - 8- 15:高强度颜色(同 ESC[90–97m) - 16-231:6 × 6 × 6 立方(216色): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5) - 232-255:从黑到白的24阶灰度色 -*/ - -// tpl for 8 bit 256 color(`2^8`) -// -// format: -// -// ESC[ … 38;5; … m // 选择前景色 -// ESC[ … 48;5; … m // 选择背景色 -// -// example: -// -// fg "\x1b[38;5;242m" -// bg "\x1b[48;5;208m" -// both "\x1b[38;5;242;48;5;208m" -// -// links: -// -// https://zh.wikipedia.org/wiki/ANSI%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97#8位 -const ( - TplFg256 = "38;5;%d" - TplBg256 = "48;5;%d" - Fg256Pfx = "38;5;" - Bg256Pfx = "48;5;" -) - -/************************************************************* - * 8bit(256) Color: Bit8Color Color256 - *************************************************************/ - -// Color256 256 color (8 bit), uint8 range at 0 - 255. -// Support 256 color on windows CMD, PowerShell -// -// 颜色值使用10进制和16进制都可 0x98 = 152 -// -// The color consists of two uint8: -// -// 0: color value -// 1: color type; Fg=0, Bg=1, >1: unset value -// -// example: -// -// fg color: [152, 0] -// bg color: [152, 1] -// -// lint warn - Name starts with package name -type Color256 [2]uint8 -type Bit8Color = Color256 // alias - -var emptyC256 = Color256{1: 99} - -// Bit8 create a color256 -func Bit8(val uint8, isBg ...bool) Color256 { - return C256(val, isBg...) -} - -// C256 create a color256 -func C256(val uint8, isBg ...bool) Color256 { - bc := Color256{val} - - // mark is bg color - if len(isBg) > 0 && isBg[0] { - bc[1] = AsBg - } - - return bc -} - -// Set terminal by 256 color code -func (c Color256) Set() error { - return SetTerminal(c.String()) -} - -// Reset terminal. alias of the ResetTerminal() -func (c Color256) Reset() error { - return ResetTerminal() -} - -// Print print message -func (c Color256) Print(a ...any) { - doPrintV2(c.String(), fmt.Sprint(a...)) -} - -// Printf format and print message -func (c Color256) Printf(format string, a ...any) { - doPrintV2(c.String(), fmt.Sprintf(format, a...)) -} - -// Println print message with newline -func (c Color256) Println(a ...any) { - doPrintlnV2(c.String(), a) -} - -// Sprint returns rendered message -func (c Color256) Sprint(a ...any) string { - return RenderCode(c.String(), a...) -} - -// Sprintf returns format and rendered message -func (c Color256) Sprintf(format string, a ...any) string { - return RenderString(c.String(), fmt.Sprintf(format, a...)) -} - -// C16 convert color-256 to 16 color. -func (c Color256) C16() Color { - return c.Basic() -} - -// Basic convert color-256 to basic 16 color. -func (c Color256) Basic() Color { - return Color(c[0]) // TODO -} - -// RGB convert color-256 to RGB color. -func (c Color256) RGB() RGBColor { - return RGBFromSlice(C256ToRgb(c[0]), c[1] == AsBg) -} - -// RGBColor convert color-256 to RGB color. -func (c Color256) RGBColor() RGBColor { - return c.RGB() -} - -// Value return color value -func (c Color256) Value() uint8 { - return c[0] -} - -// Code convert to color code string. eg: "12" -func (c Color256) Code() string { - return strconv.Itoa(int(c[0])) -} - -// FullCode convert to color code string with prefix. eg: "38;5;12" -func (c Color256) FullCode() string { - return c.String() -} - -// String convert to color code string with prefix. eg: "38;5;12" -func (c Color256) String() string { - if c[1] == AsFg { // 0 is Fg - return Fg256Pfx + strconv.Itoa(int(c[0])) - } - - if c[1] == AsBg { // 1 is Bg - return Bg256Pfx + strconv.Itoa(int(c[0])) - } - return "" // empty -} - -// IsFg color -func (c Color256) IsFg() bool { return c[1] == AsFg } - -// ToFg 256 color -func (c Color256) ToFg() Color256 { - c[1] = AsFg - return c -} - -// IsBg color -func (c Color256) IsBg() bool { return c[1] == AsBg } - -// ToBg 256 color -func (c Color256) ToBg() Color256 { - c[1] = AsBg - return c -} - -// IsEmpty value -func (c Color256) IsEmpty() bool { return c[1] > 1 } - -/************************************************************* - * 8bit(256) Style - *************************************************************/ - -// Style256 definition -// -// 前/背景色 -// 都是由两位uint8组成, 第一位是色彩值; -// 第二位与 Bit8Color 不一样的是,在这里表示是否设置了值 0 未设置 !=0 已设置 -type Style256 struct { - // Name of the style - Name string - // color options of the style - opts Opts - // fg and bg color - fg, bg Color256 -} - -// S256 create a color256 style -// -// Usage: -// -// s := color.S256() -// s := color.S256(132) // fg -// s := color.S256(132, 203) // fg and bg -func S256(fgAndBg ...uint8) *Style256 { - s := &Style256{} - vl := len(fgAndBg) - if vl > 0 { // with fg - s.fg = Color256{fgAndBg[0], 1} - - if vl > 1 { // and with bg - s.bg = Color256{fgAndBg[1], 1} - } - } - - return s -} - -// Set fg and bg color value, can also with color options -func (s *Style256) Set(fgVal, bgVal uint8, opts ...Color) *Style256 { - s.fg = Color256{fgVal, 1} - s.bg = Color256{bgVal, 1} - s.opts.Add(opts...) - return s -} - -// SetBg set bg color value -func (s *Style256) SetBg(bgVal uint8) *Style256 { - s.bg = Color256{bgVal, 1} - return s -} - -// SetFg set fg color value -func (s *Style256) SetFg(fgVal uint8) *Style256 { - s.fg = Color256{fgVal, 1} - return s -} - -// SetOpts set options -func (s *Style256) SetOpts(opts Opts) *Style256 { - s.opts = opts - return s -} - -// AddOpts add options -func (s *Style256) AddOpts(opts ...Color) *Style256 { - s.opts.Add(opts...) - return s -} - -// Print message -func (s *Style256) Print(a ...any) { - doPrintV2(s.String(), fmt.Sprint(a...)) -} - -// Printf format and print message -func (s *Style256) Printf(format string, a ...any) { - doPrintV2(s.String(), fmt.Sprintf(format, a...)) -} - -// Println print message with newline -func (s *Style256) Println(a ...any) { - doPrintlnV2(s.String(), a) -} - -// Sprint returns rendered message -func (s *Style256) Sprint(a ...any) string { - return RenderCode(s.Code(), a...) -} - -// Sprintf returns format and rendered message -func (s *Style256) Sprintf(format string, a ...any) string { - return RenderString(s.Code(), fmt.Sprintf(format, a...)) -} - -// Code convert to color code string -func (s *Style256) Code() string { - return s.String() -} - -// String convert to color code string -func (s *Style256) String() string { - var ss []string - if s.fg[1] > 0 { - ss = append(ss, Fg256Pfx+strconv.FormatInt(int64(s.fg[0]), 10)) - } - - if s.bg[1] > 0 { - ss = append(ss, Bg256Pfx+strconv.FormatInt(int64(s.bg[0]), 10)) - } - - if s.opts.IsValid() { - ss = append(ss, s.opts.String()) - } - return strings.Join(ss, ";") -} diff --git a/vendor/github.com/gookit/color/color_rgb.go b/vendor/github.com/gookit/color/color_rgb.go deleted file mode 100644 index bc129b715..000000000 --- a/vendor/github.com/gookit/color/color_rgb.go +++ /dev/null @@ -1,443 +0,0 @@ -package color - -import ( - "fmt" - "strconv" - "strings" -) - -// 24 bit RGB color -// RGB: -// -// R 0-255 G 0-255 B 0-255 -// R 00-FF G 00-FF B 00-FF (16进制) -// -// Format: -// -// ESC[ … 38;2;;; … m // Select RGB foreground color -// ESC[ … 48;2;;; … m // Choose RGB background color -// -// links: -// -// https://zh.wikipedia.org/wiki/ANSI%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97#24位 -// -// example: -// -// fg: \x1b[38;2;30;144;255mMESSAGE\x1b[0m -// bg: \x1b[48;2;30;144;255mMESSAGE\x1b[0m -// both: \x1b[38;2;233;90;203;48;2;30;144;255mMESSAGE\x1b[0m -const ( - TplFgRGB = "38;2;%d;%d;%d" - TplBgRGB = "48;2;%d;%d;%d" - FgRGBPfx = "38;2;" - BgRGBPfx = "48;2;" -) - -// mark color is fg or bg. -const ( - AsFg uint8 = iota - AsBg -) - -/************************************************************* - * RGB Color(Bit24Color, TrueColor) - *************************************************************/ - -// RGBColor definition. -// Support RGB color on Windows CMD, PowerShell -// -// The first to third digits represent the color value. -// The last digit represents the foreground(0), background(1), >1 is unset value -// -// Usage: -// -// // 0, 1, 2 is R,G,B. -// // 3rd: Fg=0, Bg=1, >1: unset value -// RGBColor{30,144,255, 0} -// RGBColor{30,144,255, 1} -type RGBColor [4]uint8 - -// create an empty RGBColor -var emptyRGBColor = RGBColor{3: 99} - -// RGB color create. -// -// Usage: -// -// c := RGB(30,144,255) -// c := RGB(30,144,255, true) -// c.Print("message") -func RGB(r, g, b uint8, isBg ...bool) RGBColor { - rgb := RGBColor{r, g, b} - if len(isBg) > 0 && isBg[0] { - rgb[3] = AsBg - } - - return rgb -} - -// Rgb alias of the RGB() -func Rgb(r, g, b uint8, isBg ...bool) RGBColor { return RGB(r, g, b, isBg...) } - -// Bit24 alias of the RGB() -func Bit24(r, g, b uint8, isBg ...bool) RGBColor { return RGB(r, g, b, isBg...) } - -// RgbFromInt create instance from int r,g,b value -func RgbFromInt(r, g, b int, isBg ...bool) RGBColor { - return RGB(uint8(r), uint8(g), uint8(b), isBg...) -} - -// RgbFromInts create instance from []int r,g,b value -func RgbFromInts(rgb []int, isBg ...bool) RGBColor { - return RGB(uint8(rgb[0]), uint8(rgb[1]), uint8(rgb[2]), isBg...) -} - -// HEX create RGB color from a HEX color string. -// -// Usage: -// -// c := HEX("ccc") // rgb: [204 204 204] -// c := HEX("aabbcc") // rgb: [170 187 204] -// c := HEX("#aabbcc") -// c := HEX("0xaabbcc") -// c.Print("message") -func HEX(hex string, isBg ...bool) RGBColor { - if rgb := HexToRgb(hex); len(rgb) > 0 { - return RGB(uint8(rgb[0]), uint8(rgb[1]), uint8(rgb[2]), isBg...) - } - - // mark is empty - return emptyRGBColor -} - -// Hex alias of the HEX() -func Hex(hex string, isBg ...bool) RGBColor { return HEX(hex, isBg...) } - -// RGBFromHEX quick RGBColor from hex string, alias of HEX() -func RGBFromHEX(hex string, isBg ...bool) RGBColor { return HEX(hex, isBg...) } - -// HSL create RGB color from a hsl value. -// more see HslToRgb() -func HSL(h, s, l float64, isBg ...bool) RGBColor { - rgb := HslToRgb(h, s, l) - return RGB(rgb[0], rgb[1], rgb[2], isBg...) -} - -// Hsl alias of the HSL() -func Hsl(h, s, l float64, isBg ...bool) RGBColor { return HSL(h, s, l, isBg...) } - -// HSLInt create RGB color from a hsl int value. -// more see HslIntToRgb() -func HSLInt(h, s, l int, isBg ...bool) RGBColor { - rgb := HslIntToRgb(h, s, l) - return RGB(rgb[0], rgb[1], rgb[2], isBg...) -} - -// HslInt alias of the HSLInt() -func HslInt(h, s, l int, isBg ...bool) RGBColor { return HSLInt(h, s, l, isBg...) } - -// RGBFromSlice quick RGBColor from slice -func RGBFromSlice(rgb []uint8, isBg ...bool) RGBColor { - return RGB(rgb[0], rgb[1], rgb[2], isBg...) -} - -// RGBFromString create RGB color from a string. -// Support use color name in the {namedRgbMap} -// -// Usage: -// -// c := RGBFromString("170,187,204") -// c.Print("message") -// -// c := RGBFromString("brown") -// c.Print("message with color brown") -func RGBFromString(rgb string, isBg ...bool) RGBColor { - // use color name in the {namedRgbMap} - if rgbVal, ok := namedRgbMap[rgb]; ok { - rgb = rgbVal - } - - // use rgb string. - ss := stringToArr(rgb, ",") - if len(ss) != 3 { - return emptyRGBColor - } - - var ar [3]uint8 - for i, val := range ss { - iv, err := strconv.Atoi(val) - if err != nil || !isValidUint8(iv) { - return emptyRGBColor - } - - ar[i] = uint8(iv) - } - - return RGB(ar[0], ar[1], ar[2], isBg...) -} - -// Set terminal by rgb/true color code -func (c RGBColor) Set() error { - return SetTerminal(c.String()) -} - -// Reset terminal. alias of the ResetTerminal() -func (c RGBColor) Reset() error { - return ResetTerminal() -} - -// Print print message -func (c RGBColor) Print(a ...any) { - doPrintV2(c.String(), fmt.Sprint(a...)) -} - -// Printf format and print message -func (c RGBColor) Printf(format string, a ...any) { - doPrintV2(c.String(), fmt.Sprintf(format, a...)) -} - -// Println print message with newline -func (c RGBColor) Println(a ...any) { - doPrintlnV2(c.String(), a) -} - -// Sprint returns rendered message -func (c RGBColor) Sprint(a ...any) string { - return RenderCode(c.String(), a...) -} - -// Sprintf returns format and rendered message -func (c RGBColor) Sprintf(format string, a ...any) string { - return RenderString(c.String(), fmt.Sprintf(format, a...)) -} - -// Values to RGB values -func (c RGBColor) Values() []int { - return []int{int(c[0]), int(c[1]), int(c[2])} -} - -// Code to color code string without prefix. eg: "204;123;56" -func (c RGBColor) Code() string { - return fmt.Sprintf("%d;%d;%d", c[0], c[1], c[2]) -} - -// Hex color rgb to hex string. as in "ff0080". -func (c RGBColor) Hex() string { - return fmt.Sprintf("%02x%02x%02x", c[0], c[1], c[2]) -} - -// RgbString to color code string without prefix. eg: "204,123,56" -func (c RGBColor) RgbString() string { - return fmt.Sprintf("%d,%d,%d", c[0], c[1], c[2]) -} - -// FullCode to color code string with prefix -func (c RGBColor) FullCode() string { - return c.String() -} - -// String to color code string with prefix. eg: "38;2;204;123;56" -func (c RGBColor) String() string { - if c[3] == AsFg { - return fmt.Sprintf(TplFgRGB, c[0], c[1], c[2]) - } - - if c[3] == AsBg { - return fmt.Sprintf(TplBgRGB, c[0], c[1], c[2]) - } - - // c[3] > 1 is empty - return "" -} - -// ToBg convert to background color -func (c RGBColor) ToBg() RGBColor { - c[3] = AsBg - return c -} - -// ToFg convert to foreground color -func (c RGBColor) ToFg() RGBColor { - c[3] = AsFg - return c -} - -// IsEmpty value -func (c RGBColor) IsEmpty() bool { - return c[3] > AsBg -} - -// IsValid value -// func (c RGBColor) IsValid() bool { -// return c[3] <= AsBg -// } - -// C256 returns the closest approximate 256 (8 bit) color -func (c RGBColor) C256() Color256 { - return C256(RgbTo256(c[0], c[1], c[2]), c[3] == AsBg) -} - -// Basic returns the closest approximate 16 (4 bit) color -func (c RGBColor) Basic() Color { - // return Color(RgbToAnsi(c[0], c[1], c[2], c[3] == AsBg)) - return Color(Rgb2basic(c[0], c[1], c[2], c[3] == AsBg)) -} - -// Color returns the closest approximate 16 (4 bit) color -func (c RGBColor) Color() Color { return c.Basic() } - -// C16 returns the closest approximate 16 (4 bit) color -func (c RGBColor) C16() Color { return c.Basic() } - -/************************************************************* - * RGB Style - *************************************************************/ - -// RGBStyle supports set foreground and background color -// -// All are composed of 4 digits uint8, the first three digits are the color value; -// The last bit is different from RGBColor, here it indicates whether the value is set. -// -// 1 Has been set -// ^1 Not set -type RGBStyle struct { - // Name of the style - Name string - // color options of the style - opts Opts - // fg and bg color - fg, bg RGBColor -} - -// NewRGBStyle create a RGBStyle. -func NewRGBStyle(fg RGBColor, bg ...RGBColor) *RGBStyle { - s := &RGBStyle{} - if len(bg) > 0 { - s.SetBg(bg[0]) - } - - return s.SetFg(fg) -} - -// HEXStyle create a RGBStyle from HEX color string. -// -// Usage: -// -// s := HEXStyle("aabbcc", "eee") -// s.Print("message") -func HEXStyle(fg string, bg ...string) *RGBStyle { - s := &RGBStyle{} - if len(bg) > 0 { - s.SetBg(HEX(bg[0])) - } - - if len(fg) > 0 { - s.SetFg(HEX(fg)) - } - return s -} - -// RGBStyleFromString create a RGBStyle from color value string. -// -// Usage: -// -// s := RGBStyleFromString("170,187,204", "70,87,4") -// s.Print("message") -func RGBStyleFromString(fg string, bg ...string) *RGBStyle { - s := &RGBStyle{} - if len(bg) > 0 { - s.SetBg(RGBFromString(bg[0])) - } - - return s.SetFg(RGBFromString(fg)) -} - -// Set fg and bg color, can also with color options -func (s *RGBStyle) Set(fg, bg RGBColor, opts ...Color) *RGBStyle { - return s.SetFg(fg).SetBg(bg).SetOpts(opts) -} - -// SetFg set fg color -func (s *RGBStyle) SetFg(fg RGBColor) *RGBStyle { - fg[3] = 1 // add fixed value, mark is valid - s.fg = fg - return s -} - -// SetBg set bg color -func (s *RGBStyle) SetBg(bg RGBColor) *RGBStyle { - bg[3] = 1 // add fixed value, mark is valid - s.bg = bg - return s -} - -// SetOpts set color options -func (s *RGBStyle) SetOpts(opts Opts) *RGBStyle { - s.opts = opts - return s -} - -// AddOpts add options -func (s *RGBStyle) AddOpts(opts ...Color) *RGBStyle { - s.opts.Add(opts...) - return s -} - -// Print print message -func (s *RGBStyle) Print(a ...any) { - doPrintV2(s.String(), fmt.Sprint(a...)) -} - -// Printf format and print message -func (s *RGBStyle) Printf(format string, a ...any) { - doPrintV2(s.String(), fmt.Sprintf(format, a...)) -} - -// Println print message with newline -func (s *RGBStyle) Println(a ...any) { - doPrintlnV2(s.String(), a) -} - -// Sprint returns rendered message -func (s *RGBStyle) Sprint(a ...any) string { - return RenderCode(s.String(), a...) -} - -// Sprintf returns format and rendered message -func (s *RGBStyle) Sprintf(format string, a ...any) string { - return RenderString(s.String(), fmt.Sprintf(format, a...)) -} - -// Code convert to color code string -func (s *RGBStyle) Code() string { - return s.String() -} - -// FullCode convert to color code string -func (s *RGBStyle) FullCode() string { - return s.String() -} - -// String convert to color code string -func (s *RGBStyle) String() string { - var ss []string - // last value ensure is enable. - if s.fg[3] == 1 { - ss = append(ss, fmt.Sprintf(TplFgRGB, s.fg[0], s.fg[1], s.fg[2])) - } - - if s.bg[3] == 1 { - ss = append(ss, fmt.Sprintf(TplBgRGB, s.bg[0], s.bg[1], s.bg[2])) - } - - if s.opts.IsValid() { - ss = append(ss, s.opts.String()) - } - - return strings.Join(ss, ";") -} - -// IsEmpty style -func (s *RGBStyle) IsEmpty() bool { - return s.fg[3] != 1 && s.bg[3] != 1 -} diff --git a/vendor/github.com/gookit/color/color_tag.go b/vendor/github.com/gookit/color/color_tag.go deleted file mode 100644 index 1d2b9d3fe..000000000 --- a/vendor/github.com/gookit/color/color_tag.go +++ /dev/null @@ -1,567 +0,0 @@ -package color - -import ( - "fmt" - "regexp" - "strings" -) - -// output colored text like use html tag. (not support windows cmd) -const ( - // MatchExpr regex to match color tags - // - // Notice: golang 不支持反向引用. 即不支持使用 \1 引用第一个匹配 ([a-z=;]+) - // MatchExpr = `<([a-z=;]+)>(.*?)<\/\1>` - // 所以调整一下 统一使用 `` 来结束标签,例如 "some text" - // - // allow custom attrs, eg: "content" - // (?s:...) s - 让 "." 匹配换行 - MatchExpr = `<([0-9a-zA-Z_=,;]+)>(?s:(.*?))<\/>` - - // AttrExpr regex to match custom color attributes - // eg: "content" - AttrExpr = `(fg|bg|op)[\s]*=[\s]*([0-9a-zA-Z,]+);?` - - // StripExpr regex used for removing color tags - // StripExpr = `<[\/]?[a-zA-Z=;]+>` - // 随着上面的做一些调整 - StripExpr = `<[\/]?[0-9a-zA-Z_=,;]*>` -) - -var ( - attrRegex = regexp.MustCompile(AttrExpr) - matchRegex = regexp.MustCompile(MatchExpr) - stripRegex = regexp.MustCompile(StripExpr) -) - -/************************************************************* - * internal defined color tags - *************************************************************/ - -// There are internal defined fg color tags -// -// Usage: -// -// content text -// -// @notice 加 0 在前面是为了防止之前的影响到现在的设置 -var colorTags = map[string]string{ - // basic tags - "red": "0;31", - "red1": "1;31", // with bold - "redB": "1;31", - "red_b": "1;31", - "blue": "0;34", - "blue1": "1;34", // with bold - "blueB": "1;34", - "blue_b": "1;34", - "cyan": "0;36", - "cyan1": "1;36", // with bold - "cyanB": "1;36", - "cyan_b": "1;36", - "green": "0;32", - "green1": "1;32", // with bold - "greenB": "1;32", - "green_b": "1;32", - "black": "0;30", - "white": "1;37", - "default": "0;39", // no color - "normal": "0;39", // no color - "brown": "0;33", // #A52A2A - "yellow": "0;33", - "ylw0": "0;33", - "yellowB": "1;33", // with bold - "ylw1": "1;33", - "ylwB": "1;33", - "magenta": "0;35", - "mga": "0;35", // short name - "magentaB": "1;35", // with bold - "magenta1": "1;35", - "mgb": "1;35", - "mga1": "1;35", - "mgaB": "1;35", - - // light/hi tags - - "gray": "0;90", - "darkGray": "0;90", - "dark_gray": "0;90", - "lightYellow": "0;93", - "light_yellow": "0;93", - "hiYellow": "0;93", - "hi_yellow": "0;93", - "hiYellowB": "1;93", // with bold - "hi_yellow_b": "1;93", - "lightMagenta": "0;95", - "light_magenta": "0;95", - "hiMagenta": "0;95", - "hi_magenta": "0;95", - "lightMagenta1": "1;95", // with bold - "hiMagentaB": "1;95", // with bold - "hi_magenta_b": "1;95", - "lightRed": "0;91", - "light_red": "0;91", - "hiRed": "0;91", - "hi_red": "0;91", - "lightRedB": "1;91", // with bold - "light_red_b": "1;91", - "hi_red_b": "1;91", - "lightGreen": "0;92", - "light_green": "0;92", - "hiGreen": "0;92", - "hi_green": "0;92", - "lightGreenB": "1;92", - "light_green_b": "1;92", - "hi_green_b": "1;92", - "lightBlue": "0;94", - "light_blue": "0;94", - "hiBlue": "0;94", - "hi_blue": "0;94", - "lightBlueB": "1;94", - "light_blue_b": "1;94", - "hi_blue_b": "1;94", - "lightCyan": "0;96", - "light_cyan": "0;96", - "hiCyan": "0;96", - "hi_cyan": "0;96", - "lightCyanB": "1;96", - "light_cyan_b": "1;96", - "hi_cyan_b": "1;96", - "lightWhite": "0;97;40", - "light_white": "0;97;40", - - // option - "bold": "1", - "b": "1", - "italic": "3", - "i": "3", // italic - "underscore": "4", - "us": "4", // short name for 'underscore' - "blink": "5", - "fb": "6", // fast blink - "reverse": "7", - "st": "9", // strikethrough - - // alert tags, like bootstrap's alert - "suc": "1;32", // same "green" and "bold" - "success": "1;32", - "info": "0;32", // same "green", - "comment": "0;33", // same "brown" - "note": "36;1", - "notice": "36;4", - "warn": "0;1;33", - "warning": "0;30;43", - "primary": "0;34", - "danger": "1;31", // same "red" but add bold - "err": "97;41", - "error": "97;41", // fg light white; bg red -} - -/************************************************************* - * internal defined tag attributes - *************************************************************/ - -// built-in attributes for fg,bg 16-colors and op codes. -var ( - attrFgs = map[string]string{ - // basic colors - - "black": FgBlack.Code(), - "red": "31", - "green": "32", - "brown": "33", // #A52A2A - "yellow": "33", - "ylw": "33", - "blue": "34", - "cyan": "36", - "magenta": "35", - "mga": "35", - "white": FgWhite.Code(), - "default": "39", // no color - "normal": "39", // no color - - // light/hi colors - - "darkGray": FgDarkGray.Code(), - "dark_gray": "90", - "gray": "90", - "lightYellow": "93", - "light_yellow": "93", - "hiYellow": "93", - "hi_yellow": "93", - "lightMagenta": "95", - "light_magenta": "95", - "hiMagenta": "95", - "hi_magenta": "95", - "hi_mga": "95", - "lightRed": "91", - "light_red": "91", - "hiRed": "91", - "hi_red": "91", - "lightGreen": "92", - "light_green": "92", - "hiGreen": "92", - "hi_green": "92", - "lightBlue": "94", - "light_blue": "94", - "hiBlue": "94", - "hi_blue": "94", - "lightCyan": "96", - "light_cyan": "96", - "hiCyan": "96", - "hi_cyan": "96", - "lightWhite": "97", - "light_white": "97", - } - - attrBgs = map[string]string{ - // basic colors - - "black": BgBlack.Code(), - "red": "41", - "green": "42", - "brown": "43", // #A52A2A - "yellow": "43", - "ylw": "43", - "blue": "44", - "cyan": "46", - "magenta": "45", - "mga": "45", - "white": FgWhite.Code(), - "default": "49", // no color - "normal": "49", // no color - - // light/hi colors - - "darkGray": BgDarkGray.Code(), - "dark_gray": "100", - "gray": "100", - "lightYellow": "103", - "light_yellow": "103", - "hiYellow": "103", - "hi_yellow": "103", - "lightMagenta": "105", - "light_magenta": "105", - "hiMagenta": "105", - "hi_magenta": "105", - "hi_mga": "105", - "lightRed": "101", - "light_red": "101", - "hiRed": "101", - "hi_red": "101", - "lightGreen": "102", - "light_green": "102", - "hiGreen": "102", - "hi_green": "102", - "lightBlue": "104", - "light_blue": "104", - "hiBlue": "104", - "hi_blue": "104", - "lightCyan": "106", - "light_cyan": "106", - "hiCyan": "106", - "hi_cyan": "106", - "lightWhite": BgLightWhite.Code(), - "light_white": "107", - } - - attrOpts = map[string]string{ - "reset": OpReset.Code(), - "bold": OpBold.Code(), - "b": OpBold.Code(), - "fuzzy": OpFuzzy.Code(), - "italic": OpItalic.Code(), - "i": OpItalic.Code(), - "underscore": OpUnderscore.Code(), - "us": OpUnderscore.Code(), - "u": OpUnderscore.Code(), - "blink": OpBlink.Code(), - "fastblink": OpFastBlink.Code(), - "fb": OpFastBlink.Code(), - "reverse": OpReverse.Code(), - "concealed": OpConcealed.Code(), - "strikethrough": OpStrikethrough.Code(), - "st": OpStrikethrough.Code(), - } -) - -/************************************************************* - * parse color tags - *************************************************************/ - -var ( - tagParser = TagParser{} - // regex for match color 256 code - rxNumStr = regexp.MustCompile("^[0-9]{1,3}$") - rxHexCode = regexp.MustCompile("^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$") -) - -// TagParser struct -type TagParser struct { - disable bool -} - -// NewTagParser create -func NewTagParser() *TagParser { - return &TagParser{} -} - -// func (tp *TagParser) Disable() *TagParser { -// tp.disable = true -// return tp -// } - -// ParseByEnv parse given string. will check package setting. -func (tp *TagParser) ParseByEnv(str string) string { - // disable handler TAG - if !RenderTag { - return str - } - - // disable OR not support color - if !Enable || !SupportColor() { - return ClearTag(str) - } - return tp.Parse(str) -} - -// Parse given string, replace color tag and return rendered string -// -// Use built in tags: -// -// CONTENT -// // e.g: `message` -// -// Custom tag attributes: -// -// `CONTENT` -// // e.g: `wel` -func (tp *TagParser) Parse(str string) string { - // not contains color tag - if !strings.Contains(str, "") { - return str - } - - // find color tags by regex. str eg: "content" - matched := matchRegex.FindAllStringSubmatch(str, -1) - - // item: 0 full text 1 tag name 2 tag content - for _, item := range matched { - full, tag, body := item[0], item[1], item[2] - - // use defined color tag name: "content" -> tag: "info" - if !strings.ContainsRune(tag, '=') { - if code := colorTags[tag]; len(code) > 0 { - str = strings.Replace(str, full, RenderString(code, body), 1) - } else if code, ok := namedRgbMap[tag]; ok { - code = strings.Replace(code, ",", ";", -1) - now := RenderString(FgRGBPfx+code, body) - str = strings.Replace(str, full, now, 1) - } - continue - } - - // custom color in tag - // - basic: "fg=white;bg=blue;op=bold" - if code := ParseCodeFromAttr(tag); len(code) > 0 { - str = strings.Replace(str, full, RenderString(code, body), 1) - } - } - - return str -} - -// ReplaceTag parse string, replace color tag and return rendered string -func ReplaceTag(str string) string { - return tagParser.ParseByEnv(str) -} - -// ParseCodeFromAttr parse color attributes. -// -// attr format: -// -// // VALUE please see var: FgColors, BgColors, AllOptions -// "fg=VALUE;bg=VALUE;op=VALUE" -// -// 16 color: -// -// "fg=yellow" -// "bg=red" -// "op=bold,underscore" // option is allow multi value -// "fg=white;bg=blue;op=bold" -// "fg=white;op=bold,underscore" -// -// 256 color: -// -// "fg=167" -// "fg=167;bg=23" -// "fg=167;bg=23;op=bold" -// -// True color: -// -// // hex -// "fg=fc1cac" -// "fg=fc1cac;bg=c2c3c4" -// // r,g,b -// "fg=23,45,214" -// "fg=23,45,214;bg=109,99,88" -func ParseCodeFromAttr(attr string) (code string) { - if !strings.ContainsRune(attr, '=') { - return - } - - attr = strings.Trim(attr, ";=,") - if len(attr) == 0 { - return - } - - var codes []string - matched := attrRegex.FindAllStringSubmatch(attr, -1) - - for _, item := range matched { - pos, val := item[1], item[2] - switch pos { - case "fg": - if code, ok := attrFgs[val]; ok { // attr fg - codes = append(codes, code) - } else if code := rgbHex256toCode(val, false); code != "" { - codes = append(codes, code) - } - case "bg": - if code, ok := attrBgs[val]; ok { // attr bg - codes = append(codes, code) - } else if code := rgbHex256toCode(val, true); code != "" { - codes = append(codes, code) - } - case "op": // options allow multi value - if strings.Contains(val, ",") { - ns := strings.Split(val, ",") - for _, n := range ns { - if code, ok := attrOpts[n]; ok { // attr ops - codes = append(codes, code) - } - } - } else if code, ok := attrOpts[val]; ok { - codes = append(codes, code) - } - } - } - - return strings.Join(codes, ";") -} - -func rgbHex256toCode(val string, isBg bool) (code string) { - if len(val) == 6 && rxHexCode.MatchString(val) { // hex: "fc1cac" - code = HEX(val, isBg).String() - } else if strings.ContainsRune(val, ',') { // rgb: "231,178,161" - code = strings.Replace(val, ",", ";", -1) - if isBg { - code = BgRGBPfx + code - } else { - code = FgRGBPfx + code - } - } else if len(val) < 4 && rxNumStr.MatchString(val) { // 256 code - if isBg { - code = Bg256Pfx + val - } else { - code = Fg256Pfx + val - } - } - return -} - -// ClearTag clear all tag for a string -func ClearTag(s string) string { - if !strings.Contains(s, "") { - return s - } - return stripRegex.ReplaceAllString(s, "") -} - -/************************************************************* - * helper methods - *************************************************************/ - -// GetTagCode get color code by tag name -func GetTagCode(name string) string { return colorTags[name] } - -// ApplyTag for messages -func ApplyTag(tag string, a ...any) string { - return RenderCode(GetTagCode(tag), a...) -} - -// WrapTag wrap a tag for a string "content" -func WrapTag(s string, tag string) string { - if s == "" || tag == "" { - return s - } - return fmt.Sprintf("<%s>%s", tag, s) -} - -// GetColorTags get all internal color tags -func GetColorTags() map[string]string { - return colorTags -} - -// IsDefinedTag is defined tag name -func IsDefinedTag(name string) bool { - _, ok := colorTags[name] - return ok -} - -/************************************************************* - * Tag extra - *************************************************************/ - -// Tag value is a defined style name -// Usage: -// -// Tag("info").Println("message") -type Tag string - -// Print messages -func (tg Tag) Print(a ...any) { - name := string(tg) - str := fmt.Sprint(a...) - - if stl := GetStyle(name); !stl.IsEmpty() { - stl.Print(str) - } else { - doPrintV2(GetTagCode(name), str) - } -} - -// Printf format and print messages -func (tg Tag) Printf(format string, a ...any) { - name := string(tg) - str := fmt.Sprintf(format, a...) - - if stl := GetStyle(name); !stl.IsEmpty() { - stl.Print(str) - } else { - doPrintV2(GetTagCode(name), str) - } -} - -// Println messages line -func (tg Tag) Println(a ...any) { - name := string(tg) - if stl := GetStyle(name); !stl.IsEmpty() { - stl.Println(a...) - } else { - doPrintlnV2(GetTagCode(name), a) - } -} - -// Sprint render messages -func (tg Tag) Sprint(a ...any) string { - return RenderCode(GetTagCode(string(tg)), a...) -} - -// Sprintf format and render messages -func (tg Tag) Sprintf(format string, a ...any) string { - tag := string(tg) - str := fmt.Sprintf(format, a...) - - return RenderString(GetTagCode(tag), str) -} diff --git a/vendor/github.com/gookit/color/convert.go b/vendor/github.com/gookit/color/convert.go deleted file mode 100644 index c71035360..000000000 --- a/vendor/github.com/gookit/color/convert.go +++ /dev/null @@ -1,966 +0,0 @@ -package color - -import ( - "fmt" - "math" - "sort" - "strconv" - "strings" -) - -// values from https://github.com/go-terminfo/terminfo -// var ( -// RgbaBlack = image_color.RGBA{0, 0, 0, 255} -// Red = color.RGBA{205, 0, 0, 255} -// Green = color.RGBA{0, 205, 0, 255} -// Orange = color.RGBA{205, 205, 0, 255} -// Blue = color.RGBA{0, 0, 238, 255} -// Magenta = color.RGBA{205, 0, 205, 255} -// Cyan = color.RGBA{0, 205, 205, 255} -// LightGrey = color.RGBA{229, 229, 229, 255} -// -// DarkGrey = color.RGBA{127, 127, 127, 255} -// LightRed = color.RGBA{255, 0, 0, 255} -// LightGreen = color.RGBA{0, 255, 0, 255} -// Yellow = color.RGBA{255, 255, 0, 255} -// LightBlue = color.RGBA{92, 92, 255, 255} -// LightMagenta = color.RGBA{255, 0, 255, 255} -// LightCyan = color.RGBA{0, 255, 255, 255} -// White = color.RGBA{255, 255, 255, 255} -// ) - -var ( - // ---------- basic(16) <=> 256 color convert ---------- - basicTo256Map = map[uint8]uint8{ - 30: 0, // black 000000 - 31: 160, // red c51e14 - 32: 34, // green 1dc121 - 33: 184, // yellow c7c329 - 34: 20, // blue 0a2fc4 - 35: 170, // magenta c839c5 - 36: 44, // cyan 20c5c6 - 37: 188, // white c7c7c7 - 90: 59, // lightBlack 686868 - 91: 203, // lightRed fd6f6b - 92: 83, // lightGreen 67f86f - 93: 227, // lightYellow fffa72 - 94: 69, // lightBlue 6a76fb - 95: 213, // lightMagenta fd7cfc - 96: 87, // lightCyan 68fdfe - 97: 15, // lightWhite ffffff - } - - // ---------- basic(16) <=> RGB color convert ---------- - // refer from Hyper app - // Tip: only keep foreground color, background color need convert to foreground color for convert to RGB - basic2hexMap = map[uint8]string{ - 30: "000000", // black - 31: "c51e14", // red - 32: "1dc121", // green - 33: "c7c329", // yellow - 34: "0a2fc4", // blue - 35: "c839c5", // magenta - 36: "20c5c6", // cyan - 37: "c7c7c7", // white - // - don't add bg color, convert to fg color for convert to RGB - // 40: "000000", // black - // 41: "c51e14", // red - // 42: "1dc121", // green - // 43: "c7c329", // yellow - // 44: "0a2fc4", // blue - // 45: "c839c5", // magenta - // 46: "20c5c6", // cyan - // 47: "c7c7c7", // white - 90: "686868", // lightBlack/darkGray - 91: "fd6f6b", // lightRed - 92: "67f86f", // lightGreen - 93: "fffa72", // lightYellow - 94: "6a76fb", // lightBlue - 95: "fd7cfc", // lightMagenta - 96: "68fdfe", // lightCyan - 97: "ffffff", // lightWhite - // - don't add bg color - // 100: "686868", // lightBlack/darkGray - // 101: "fd6f6b", // lightRed - // 102: "67f86f", // lightGreen - // 103: "fffa72", // lightYellow - // 104: "6a76fb", // lightBlue - // 105: "fd7cfc", // lightMagenta - // 106: "68fdfe", // lightCyan - // 107: "ffffff", // lightWhite - } - // will convert data from basic2hexMap - hex2basicMap = initHex2basicMap() - - // ---------- 256 <=> RGB color convert ---------- - // adapted from https://gist.github.com/MicahElliott/719710 - - c256ToHexMap = init256ToHexMap() - - // rgb to 256 color look-up table - // RGB hex => 256 code - hexTo256Table = map[string]uint8{ - // Primary 3-bit (8 colors). Unique representation! - "000000": 0, - "800000": 1, - "008000": 2, - "808000": 3, - "000080": 4, - "800080": 5, - "008080": 6, - "c0c0c0": 7, - - // Equivalent "bright" versions of original 8 colors. - "808080": 8, - "ff0000": 9, - "00ff00": 10, - "ffff00": 11, - "0000ff": 12, - "ff00ff": 13, - "00ffff": 14, - "ffffff": 15, - - // values commented out below are duplicates from the prior sections - - // Strictly ascending. - // "000000": 16, - "000001": 16, // up: avoid key conflicts, value + 1 - "00005f": 17, - "000087": 18, - "0000af": 19, - "0000d7": 20, - // "0000ff": 21, - "0000fe": 21, // up: avoid key conflicts, value - 1 - "005f00": 22, - "005f5f": 23, - "005f87": 24, - "005faf": 25, - "005fd7": 26, - "005fff": 27, - "008700": 28, - "00875f": 29, - "008787": 30, - "0087af": 31, - "0087d7": 32, - "0087ff": 33, - "00af00": 34, - "00af5f": 35, - "00af87": 36, - "00afaf": 37, - "00afd7": 38, - "00afff": 39, - "00d700": 40, - "00d75f": 41, - "00d787": 42, - "00d7af": 43, - "00d7d7": 44, - "00d7ff": 45, - // "00ff00": 46, - "00ff01": 46, // up: avoid key conflicts, value + 1 - "00ff5f": 47, - "00ff87": 48, - "00ffaf": 49, - "00ffd7": 50, - // "00ffff": 51, - "00fffe": 51, // up: avoid key conflicts, value - 1 - "5f0000": 52, - "5f005f": 53, - "5f0087": 54, - "5f00af": 55, - "5f00d7": 56, - "5f00ff": 57, - "5f5f00": 58, - "5f5f5f": 59, - "5f5f87": 60, - "5f5faf": 61, - "5f5fd7": 62, - "5f5fff": 63, - "5f8700": 64, - "5f875f": 65, - "5f8787": 66, - "5f87af": 67, - "5f87d7": 68, - "5f87ff": 69, - "5faf00": 70, - "5faf5f": 71, - "5faf87": 72, - "5fafaf": 73, - "5fafd7": 74, - "5fafff": 75, - "5fd700": 76, - "5fd75f": 77, - "5fd787": 78, - "5fd7af": 79, - "5fd7d7": 80, - "5fd7ff": 81, - "5fff00": 82, - "5fff5f": 83, - "5fff87": 84, - "5fffaf": 85, - "5fffd7": 86, - "5fffff": 87, - "870000": 88, - "87005f": 89, - "870087": 90, - "8700af": 91, - "8700d7": 92, - "8700ff": 93, - "875f00": 94, - "875f5f": 95, - "875f87": 96, - "875faf": 97, - "875fd7": 98, - "875fff": 99, - "878700": 100, - "87875f": 101, - "878787": 102, - "8787af": 103, - "8787d7": 104, - "8787ff": 105, - "87af00": 106, - "87af5f": 107, - "87af87": 108, - "87afaf": 109, - "87afd7": 110, - "87afff": 111, - "87d700": 112, - "87d75f": 113, - "87d787": 114, - "87d7af": 115, - "87d7d7": 116, - "87d7ff": 117, - "87ff00": 118, - "87ff5f": 119, - "87ff87": 120, - "87ffaf": 121, - "87ffd7": 122, - "87ffff": 123, - "af0000": 124, - "af005f": 125, - "af0087": 126, - "af00af": 127, - "af00d7": 128, - "af00ff": 129, - "af5f00": 130, - "af5f5f": 131, - "af5f87": 132, - "af5faf": 133, - "af5fd7": 134, - "af5fff": 135, - "af8700": 136, - "af875f": 137, - "af8787": 138, - "af87af": 139, - "af87d7": 140, - "af87ff": 141, - "afaf00": 142, - "afaf5f": 143, - "afaf87": 144, - "afafaf": 145, - "afafd7": 146, - "afafff": 147, - "afd700": 148, - "afd75f": 149, - "afd787": 150, - "afd7af": 151, - "afd7d7": 152, - "afd7ff": 153, - "afff00": 154, - "afff5f": 155, - "afff87": 156, - "afffaf": 157, - "afffd7": 158, - "afffff": 159, - "d70000": 160, - "d7005f": 161, - "d70087": 162, - "d700af": 163, - "d700d7": 164, - "d700ff": 165, - "d75f00": 166, - "d75f5f": 167, - "d75f87": 168, - "d75faf": 169, - "d75fd7": 170, - "d75fff": 171, - "d78700": 172, - "d7875f": 173, - "d78787": 174, - "d787af": 175, - "d787d7": 176, - "d787ff": 177, - "d7af00": 178, - "d7af5f": 179, - "d7af87": 180, - "d7afaf": 181, - "d7afd7": 182, - "d7afff": 183, - "d7d700": 184, - "d7d75f": 185, - "d7d787": 186, - "d7d7af": 187, - "d7d7d7": 188, - "d7d7ff": 189, - "d7ff00": 190, - "d7ff5f": 191, - "d7ff87": 192, - "d7ffaf": 193, - "d7ffd7": 194, - "d7ffff": 195, - // "ff0000": 196, - "ff0001": 196, // up: avoid key conflicts, value + 1 - "ff005f": 197, - "ff0087": 198, - "ff00af": 199, - "ff00d7": 200, - // "ff00ff": 201, - "ff00fe": 201, // up: avoid key conflicts, value - 1 - "ff5f00": 202, - "ff5f5f": 203, - "ff5f87": 204, - "ff5faf": 205, - "ff5fd7": 206, - "ff5fff": 207, - "ff8700": 208, - "ff875f": 209, - "ff8787": 210, - "ff87af": 211, - "ff87d7": 212, - "ff87ff": 213, - "ffaf00": 214, - "ffaf5f": 215, - "ffaf87": 216, - "ffafaf": 217, - "ffafd7": 218, - "ffafff": 219, - "ffd700": 220, - "ffd75f": 221, - "ffd787": 222, - "ffd7af": 223, - "ffd7d7": 224, - "ffd7ff": 225, - // "ffff00": 226, - "ffff01": 226, // up: avoid key conflicts, value + 1 - "ffff5f": 227, - "ffff87": 228, - "ffffaf": 229, - "ffffd7": 230, - // "ffffff": 231, - "fffffe": 231, // up: avoid key conflicts, value - 1 - - // Gray-scale range. - "080808": 232, - "121212": 233, - "1c1c1c": 234, - "262626": 235, - "303030": 236, - "3a3a3a": 237, - "444444": 238, - "4e4e4e": 239, - "585858": 240, - "626262": 241, - "6c6c6c": 242, - "767676": 243, - // "808080": 244, - "808081": 244, // up: avoid key conflicts, value + 1 - "8a8a8a": 245, - "949494": 246, - "9e9e9e": 247, - "a8a8a8": 248, - "b2b2b2": 249, - "bcbcbc": 250, - "c6c6c6": 251, - "d0d0d0": 252, - "dadada": 253, - "e4e4e4": 254, - "eeeeee": 255, - } - - incs = []uint8{0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff} -) - -func initHex2basicMap() map[string]uint8 { - h2b := make(map[string]uint8, len(basic2hexMap)) - // ini data map - for u, s := range basic2hexMap { - h2b[s] = u - } - return h2b -} - -func init256ToHexMap() map[uint8]string { - c256toh := make(map[uint8]string, len(hexTo256Table)) - // ini data map - for hex, c256 := range hexTo256Table { - c256toh[c256] = hex - } - return c256toh -} - -// RgbTo256Table mapping data -func RgbTo256Table() map[string]uint8 { - return hexTo256Table -} - -// Colors2code convert colors to code. return like "32;45;3" -func Colors2code(colors ...Color) string { - if len(colors) == 0 { - return "" - } - - var codes []string - for _, color := range colors { - codes = append(codes, color.String()) - } - - return strings.Join(codes, ";") -} - -/************************************************************* - * HEX code <=> RGB/True color code - *************************************************************/ - -// Hex2rgb alias of the HexToRgb() -func Hex2rgb(hex string) []int { return HexToRgb(hex) } - -// HexToRGB alias of the HexToRgb() -func HexToRGB(hex string) []int { return HexToRgb(hex) } - -// HexToRgb convert hex color string to RGB numbers -// -// Usage: -// -// rgb := HexToRgb("ccc") // rgb: [204 204 204] -// rgb := HexToRgb("aabbcc") // rgb: [170 187 204] -// rgb := HexToRgb("#aabbcc") // rgb: [170 187 204] -// rgb := HexToRgb("0xad99c0") // rgb: [170 187 204] -func HexToRgb(hex string) (rgb []int) { - hex = strings.TrimSpace(hex) - if hex == "" { - return - } - - // like from css. eg "#ccc" "#ad99c0" - if hex[0] == '#' { - hex = hex[1:] - } - - hex = strings.ToLower(hex) - switch len(hex) { - case 3: // "ccc" - hex = string([]byte{hex[0], hex[0], hex[1], hex[1], hex[2], hex[2]}) - case 8: // "0xad99c0" - hex = strings.TrimPrefix(hex, "0x") - } - - // recheck - if len(hex) != 6 { - return - } - - // convert string to int64 - if i64, err := strconv.ParseInt(hex, 16, 32); err == nil { - color := int(i64) - // parse int - rgb = make([]int, 3) - rgb[0] = color >> 16 - rgb[1] = (color & 0x00FF00) >> 8 - rgb[2] = color & 0x0000FF - } - return -} - -// Rgb2hex alias of the RgbToHex() -func Rgb2hex(rgb []int) string { return RgbToHex(rgb) } - -// RgbToHex convert RGB-code to hex-code -// -// Usage: -// -// hex := RgbToHex([]int{170, 187, 204}) // hex: "aabbcc" -func RgbToHex(rgb []int) string { - hexNodes := make([]string, len(rgb)) - - for _, v := range rgb { - hexNodes = append(hexNodes, strconv.FormatInt(int64(v), 16)) - } - return strings.Join(hexNodes, "") -} - -/************************************************************* - * 4bit(16) color <=> RGB/True color - *************************************************************/ - -// BasicToHex convert basic color to hex string. -func BasicToHex(val uint8) string { - val = Bg2Fg(val) - return basic2hexMap[val] -} - -// Basic2hex convert basic color to hex string. -func Basic2hex(val uint8) string { - return BasicToHex(val) -} - -// Hex2basic convert hex string to basic color code. -func Hex2basic(hex string, asBg ...bool) uint8 { - val := hex2basicMap[hex] - - if len(asBg) > 0 && asBg[0] { - return Fg2Bg(val) - } - return val -} - -// Rgb2basic alias of the RgbToAnsi() -func Rgb2basic(r, g, b uint8, isBg bool) uint8 { - // is basic color, direct use static map data. - hex := RgbToHex([]int{int(r), int(g), int(b)}) - if val, ok := hex2basicMap[hex]; ok { - if isBg { - return val + 10 - } - return val - } - - return RgbToAnsi(r, g, b, isBg) -} - -// Rgb2ansi convert RGB-code to 16-code, alias of the RgbToAnsi() -func Rgb2ansi(r, g, b uint8, isBg bool) uint8 { - return RgbToAnsi(r, g, b, isBg) -} - -// RgbToAnsi convert RGB-code to 16-code -// refer https://github.com/radareorg/radare2/blob/master/libr/cons/rgb.c#L249-L271 -func RgbToAnsi(r, g, b uint8, isBg bool) uint8 { - var bright, c, k uint8 - base := compareVal(isBg, BgBase, FgBase) - - // eco bright-specific - if r == 0x80 && g == 0x80 && b == 0x80 { // 0x80=128 - bright = 53 - } else if r == 0xff || g == 0xff || b == 0xff { // 0xff=255 - bright = 60 - } // else bright = 0 - - if r == g && g == b { - // 0x7f=127 - // r = (r > 0x7f) ? 1 : 0; - r = compareVal(r > 0x7f, 1, 0) - g = compareVal(g > 0x7f, 1, 0) - b = compareVal(b > 0x7f, 1, 0) - } else { - k = (r + g + b) / 3 - - // r = (r >= k) ? 1 : 0; - r = compareVal(r >= k, 1, 0) - g = compareVal(g >= k, 1, 0) - b = compareVal(b >= k, 1, 0) - } - - // c = (r ? 1 : 0) + (g ? (b ? 6 : 2) : (b ? 4 : 0)) - c = compareVal(r > 0, 1, 0) - - if g > 0 { - c += compareVal(b > 0, 6, 2) - } else { - c += compareVal(b > 0, 4, 0) - } - return base + bright + c -} - -/************************************************************* - * 8bit(256) color <=> RGB/True color - *************************************************************/ - -// Rgb2short convert RGB-code to 256-code -func Rgb2short(r, g, b uint8) uint8 { - return RgbTo256(r, g, b) -} - -// RgbTo256 convert RGB-code to 256-code -func RgbTo256(r, g, b uint8) uint8 { - res := make([]uint8, 3) - for partI, part := range [3]uint8{r, g, b} { - i := 0 - for i < len(incs)-1 { - s, b := incs[i], incs[i+1] // smaller, bigger - if s <= part && part <= b { - s1 := math.Abs(float64(s) - float64(part)) - b1 := math.Abs(float64(b) - float64(part)) - var closest uint8 - if s1 < b1 { - closest = s - } else { - closest = b - } - res[partI] = closest - break - } - i++ - } - } - hex := fmt.Sprintf("%02x%02x%02x", res[0], res[1], res[2]) - equiv := hexTo256Table[hex] - return equiv -} - -// C256ToRgb convert an 256 color code to RGB numbers -func C256ToRgb(val uint8) (rgb []uint8) { - hex := c256ToHexMap[val] - // convert to rgb code - rgbInts := Hex2rgb(hex) - - return []uint8{ - uint8(rgbInts[0]), - uint8(rgbInts[1]), - uint8(rgbInts[2]), - } -} - -// C256ToRgbV1 convert an 256 color code to RGB numbers -// refer https://github.com/torvalds/linux/commit/cec5b2a97a11ade56a701e83044d0a2a984c67b4 -func C256ToRgbV1(val uint8) (rgb []uint8) { - var r, g, b uint8 - if val < 8 { // Standard colours. - // r = val&1 ? 0xaa : 0x00; - r = compareVal(val&1 == 1, 0xaa, 0x00) - g = compareVal(val&2 == 2, 0xaa, 0x00) - b = compareVal(val&4 == 4, 0xaa, 0x00) - } else if val < 16 { - // r = val & 1 ? 0xff : 0x55; - r = compareVal(val&1 == 1, 0xff, 0x55) - g = compareVal(val&2 == 2, 0xff, 0x55) - b = compareVal(val&4 == 4, 0xff, 0x55) - } else if val < 232 { /* 6x6x6 colour cube. */ - r = (val - 16) / 36 * 85 / 2 - g = (val - 16) / 6 % 6 * 85 / 2 - b = (val - 16) % 6 * 85 / 2 - } else { /* Grayscale ramp. */ - nv := uint8(int(val)*10 - 2312) - // set value - r, g, b = nv, nv, nv - } - - return []uint8{r, g, b} -} - -/************************************************************** - * HSL color <=> RGB/True color - ************************************************************ - * h,s,l = Hue, Saturation, Lightness - * - * refers - * http://en.wikipedia.org/wiki/HSL_color_space - * https://www.w3.org/TR/css-color-3/#hsl-color - * https://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - * https://github.com/less/less.js/blob/master/packages/less/src/less/functions/color.js - * https://github.com/d3/d3-color/blob/v3.0.1/README.md#hsl - * - * examples: - * color: hsl(0, 100%, 50%) // red - * color: hsl(120, 100%, 50%) // lime - * color: hsl(120, 100%, 25%) // dark green - * color: hsl(120, 100%, 75%) // light green - * color: hsl(120, 75%, 75%) // pastel green, and so on - */ - -// HslIntToRgb Converts an HSL color value to RGB -// Assumes h: 0-360, s: 0-100, l: 0-100 -// returns r, g, and b in the set [0, 255]. -// -// Usage: -// -// HslIntToRgb(0, 100, 50) // red -// HslIntToRgb(120, 100, 50) // lime -// HslIntToRgb(120, 100, 25) // dark green -// HslIntToRgb(120, 100, 75) // light green -func HslIntToRgb(h, s, l int) (rgb []uint8) { - return HslToRgb(float64(h)/360, float64(s)/100, float64(l)/100) -} - -// HslToRgb Converts an HSL color value to RGB. Conversion formula -// adapted from http://en.wikipedia.org/wiki/HSL_color_space. -// Assumes h, s, and l are contained in the set [0, 1] -// returns r, g, and b in the set [0, 255]. -// -// Usage: -// -// rgbVals := HslToRgb(0, 1, 0.5) // red -func HslToRgb(h, s, l float64) (rgb []uint8) { - var r, g, b float64 - - if s == 0 { // achromatic - r, g, b = l, l, l - } else { - var hue2rgb = func(p, q, t float64) float64 { - if t < 0.0 { - t += 1 - } - if t > 1.0 { - t -= 1 - } - - if t < 1.0/6.0 { - return p + (q-p)*6.0*t - } - - if t < 1.0/2.0 { - return q - } - - if t < 2.0/3.0 { - return p + (q-p)*(2.0/3.0-t)*6.0 - } - return p - } - - // q = l < 0.5 ? l * (1 + s) : l + s - l*s - var q float64 - if l < 0.5 { - q = l * (1.0 + s) - } else { - q = l + s - l*s - } - - var p = 2.0*l - q - - r = hue2rgb(p, q, h+1.0/3.0) - g = hue2rgb(p, q, h) - b = hue2rgb(p, q, h-1.0/3.0) - } - - // return []uint8{uint8(r * 255), uint8(g * 255), uint8(b * 255)} - return []uint8{ - uint8(math.Round(r * 255)), - uint8(math.Round(g * 255)), - uint8(math.Round(b * 255)), - } -} - -// RgbToHslInt Converts an RGB color value to HSL. Conversion formula -// Assumes r, g, and b are contained in the set [0, 255] and -// returns [h,s,l] h: 0-360, s: 0-100, l: 0-100. -func RgbToHslInt(r, g, b uint8) []int { - f64s := RgbToHsl(r, g, b) - - return []int{int(f64s[0] * 360), int(f64s[1] * 100), int(f64s[2] * 100)} -} - -// RgbToHsl Converts an RGB color value to HSL. Conversion formula -// -// adapted from http://en.wikipedia.org/wiki/HSL_color_space. -// -// Assumes r, g, and b are contained in the set [0, 255] and -// returns h, s, and l in the set [0, 1]. -func RgbToHsl(r, g, b uint8) []float64 { - // to float64 - fr, fg, fb := float64(r), float64(g), float64(b) - // percentage - pr, pg, pb := float64(r)/255.0, float64(g)/255.0, float64(b)/255.0 - - ps := []float64{pr, pg, pb} - sort.Float64s(ps) - - min, max := ps[0], ps[2] - // max := math.Max(math.Max(pr, pg), pb) - // min := math.Min(math.Min(pr, pg), pb) - mid := (max + min) / 2 - - h, s, l := mid, mid, mid - if max == min { - h, s = 0, 0 // achromatic - } else { - var d = max - min - // s = l > 0.5 ? d / (2 - max - min) : d / (max + min) - s = compareF64Val(l > 0.5, d/(2-max-min), d/(max+min)) - - switch max { - case fr: - // h = (g - b) / d + (g < b ? 6 : 0) - h = (fg - fb) / d - h += compareF64Val(g < b, 6, 0) - case fg: - h = (fb-fr)/d + 2 - case fb: - h = (fr-fg)/d + 4 - } - - h /= 6 - } - - return []float64{h, s, l} -} - -/************************************************************** - * HSV color <=> RGB/True color - ************************************************************ - * h,s,l = Hue, Saturation, Value(Brightness) - * - * refers - * https://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - * https://github.com/less/less.js/blob/master/packages/less/src/less/functions/color.js - * https://github.com/d3/d3-color/blob/v3.0.1/README.md#hsl - */ - -// HsvToRgb Converts an HSL color value to RGB. Conversion formula -// adapted from https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB -// Assumes h: 0-360, s: 0-100, l: 0-100 -// returns r, g, and b in the set [0, 255]. -func HsvToRgb(h, s, v int) (rgb []uint8) { - // TODO ... - return -} - -// Named rgb colors -// https://www.w3.org/TR/css-color-3/#svg-color -var namedRgbMap = map[string]string{ - "aliceblue": "240,248,255", // #F0F8FF - "antiquewhite": "250,235,215", // #FAEBD7 - "aqua": "0,255,255", // #00FFFF - "aquamarine": "127,255,212", // #7FFFD4 - "azure": "240,255,255", // #F0FFFF - "beige": "245,245,220", // #F5F5DC - "bisque": "255,228,196", // #FFE4C4 - "black": "0,0,0", // #000000 - "blanchedalmond": "255,235,205", // #FFEBCD - "blue": "0,0,255", // #0000FF - "blueviolet": "138,43,226", // #8A2BE2 - "brown": "165,42,42", // #A52A2A - "burlywood": "222,184,135", // #DEB887 - "cadetblue": "95,158,160", // #5F9EA0 - "chartreuse": "127,255,0", // #7FFF00 - "chocolate": "210,105,30", // #D2691E - "coral": "255,127,80", // #FF7F50 - "cornflowerblue": "100,149,237", // #6495ED - "cornsilk": "255,248,220", // #FFF8DC - "crimson": "220,20,60", // #DC143C - "cyan": "0,255,255", // #00FFFF - "darkblue": "0,0,139", // #00008B - "darkcyan": "0,139,139", // #008B8B - "darkgoldenrod": "184,134,11", // #B8860B - "darkgray": "169,169,169", // #A9A9A9 - "darkgreen": "0,100,0", // #006400 - "darkgrey": "169,169,169", // #A9A9A9 - "darkkhaki": "189,183,107", // #BDB76B - "darkmagenta": "139,0,139", // #8B008B - "darkolivegreen": "85,107,47", // #556B2F - "darkorange": "255,140,0", // #FF8C00 - "darkorchid": "153,50,204", // #9932CC - "darkred": "139,0,0", // #8B0000 - "darksalmon": "233,150,122", // #E9967A - "darkseagreen": "143,188,143", // #8FBC8F - "darkslateblue": "72,61,139", // #483D8B - "darkslategray": "47,79,79", // #2F4F4F - "darkslategrey": "47,79,79", // #2F4F4F - "darkturquoise": "0,206,209", // #00CED1 - "darkviolet": "148,0,211", // #9400D3 - "deeppink": "255,20,147", // #FF1493 - "deepskyblue": "0,191,255", // #00BFFF - "dimgray": "105,105,105", // #696969 - "dimgrey": "105,105,105", // #696969 - "dodgerblue": "30,144,255", // #1E90FF - "firebrick": "178,34,34", // #B22222 - "floralwhite": "255,250,240", // #FFFAF0 - "forestgreen": "34,139,34", // #228B22 - "fuchsia": "255,0,255", // #FF00FF - "gainsboro": "220,220,220", // #DCDCDC - "ghostwhite": "248,248,255", // #F8F8FF - "gold": "255,215,0", // #FFD700 - "goldenrod": "218,165,32", // #DAA520 - "gray": "128,128,128", // #808080 - "green": "0,128,0", // #008000 - "greenyellow": "173,255,47", // #ADFF2F - "grey": "128,128,128", // #808080 - "honeydew": "240,255,240", // #F0FFF0 - "hotpink": "255,105,180", // #FF69B4 - "indianred": "205,92,92", // #CD5C5C - "indigo": "75,0,130", // #4B0082 - "ivory": "255,255,240", // #FFFFF0 - "khaki": "240,230,140", // #F0E68C - "lavender": "230,230,250", // #E6E6FA - "lavenderblush": "255,240,245", // #FFF0F5 - "lawngreen": "124,252,0", // #7CFC00 - "lemonchiffon": "255,250,205", // #FFFACD - "lightblue": "173,216,230", // #ADD8E6 - "lightcoral": "240,128,128", // #F08080 - "lightcyan": "224,255,255", // #E0FFFF - "lightgoldenrodyellow": "250,250,210", // #FAFAD2 - "lightgray": "211,211,211", // #D3D3D3 - "lightgreen": "144,238,144", // #90EE90 - "lightgrey": "211,211,211", // #D3D3D3 - "lightpink": "255,182,193", // #FFB6C1 - "lightsalmon": "255,160,122", // #FFA07A - "lightseagreen": "32,178,170", // #20B2AA - "lightskyblue": "135,206,250", // #87CEFA - "lightslategray": "119,136,153", // #778899 - "lightslategrey": "119,136,153", // #778899 - "lightsteelblue": "176,196,222", // #B0C4DE - "lightyellow": "255,255,224", // #FFFFE0 - "lime": "0,255,0", // #00FF00 - "limegreen": "50,205,50", // #32CD32 - "linen": "250,240,230", // #FAF0E6 - "magenta": "255,0,255", // #FF00FF - "maroon": "128,0,0", // #800000 - "mediumaquamarine": "102,205,170", // #66CDAA - "mediumblue": "0,0,205", // #0000CD - "mediumorchid": "186,85,211", // #BA55D3 - "mediumpurple": "147,112,219", // #9370DB - "mediumseagreen": "60,179,113", // #3CB371 - "mediumslateblue": "123,104,238", // #7B68EE - "mediumspringgreen": "0,250,154", // #00FA9A - "mediumturquoise": "72,209,204", // #48D1CC - "mediumvioletred": "199,21,133", // #C71585 - "midnightblue": "25,25,112", // #191970 - "mintcream": "245,255,250", // #F5FFFA - "mistyrose": "255,228,225", // #FFE4E1 - "moccasin": "255,228,181", // #FFE4B5 - "navajowhite": "255,222,173", // #FFDEAD - "navy": "0,0,128", // #000080 - "oldlace": "253,245,230", // #FDF5E6 - "olive": "128,128,0", // #808000 - "olivedrab": "107,142,35", // #6B8E23 - "orange": "255,165,0", // #FFA500 - "orangered": "255,69,0", // #FF4500 - "orchid": "218,112,214", // #DA70D6 - "palegoldenrod": "238,232,170", // #EEE8AA - "palegreen": "152,251,152", // #98FB98 - "paleturquoise": "175,238,238", // #AFEEEE - "palevioletred": "219,112,147", // #DB7093 - "papayawhip": "255,239,213", // #FFEFD5 - "peachpuff": "255,218,185", // #FFDAB9 - "peru": "205,133,63", // #CD853F - "pink": "255,192,203", // #FFC0CB - "plum": "221,160,221", // #DDA0DD - "powderblue": "176,224,230", // #B0E0E6 - "purple": "128,0,128", // #800080 - "red": "255,0,0", // #FF0000 - "rosybrown": "188,143,143", // #BC8F8F - "royalblue": "65,105,225", // #4169E1 - "saddlebrown": "139,69,19", // #8B4513 - "salmon": "250,128,114", // #FA8072 - "sandybrown": "244,164,96", // #F4A460 - "seagreen": "46,139,87", // #2E8B57 - "seashell": "255,245,238", // #FFF5EE - "sienna": "160,82,45", // #A0522D - "silver": "192,192,192", // #C0C0C0 - "skyblue": "135,206,235", // #87CEEB - "slateblue": "106,90,205", // #6A5ACD - "slategray": "112,128,144", // #708090 - "slategrey": "112,128,144", // #708090 - "snow": "255,250,250", // #FFFAFA - "springgreen": "0,255,127", // #00FF7F - "steelblue": "70,130,180", // #4682B4 - "tan": "210,180,140", // #D2B48C - "teal": "0,128,128", // #008080 - "thistle": "216,191,216", // #D8BFD8 - "tomato": "255,99,71", // #FF6347 - "turquoise": "64,224,208", // #40E0D0 - "violet": "238,130,238", // #EE82EE - "wheat": "245,222,179", // #F5DEB3 - "white": "255,255,255", // #FFFFFF - "whitesmoke": "245,245,245", // #F5F5F5 - "yellow": "255,255,0", // #FFFF00 - "yellowgreen": "154,205,50", // #9ACD32 -} diff --git a/vendor/github.com/gookit/color/detect_env.go b/vendor/github.com/gookit/color/detect_env.go deleted file mode 100644 index 7fd8d81e2..000000000 --- a/vendor/github.com/gookit/color/detect_env.go +++ /dev/null @@ -1,291 +0,0 @@ -package color - -import ( - "io" - "os" - "runtime" - "strconv" - "strings" - "syscall" - - "github.com/xo/terminfo" -) - -// Level is the color level supported by a terminal. -type Level = terminfo.ColorLevel - -// terminal color available level alias of the terminfo.ColorLevel* -const ( - LevelNo = terminfo.ColorLevelNone // not support color. - Level16 = terminfo.ColorLevelBasic // basic - 3/4 bit color supported - Level256 = terminfo.ColorLevelHundreds // hundreds - 8-bit color supported - LevelRgb = terminfo.ColorLevelMillions // millions - (24 bit)true color supported -) - -/************************************************************* - * helper methods for detect color supports - *************************************************************/ - -// DetectColorLevel for current env -// -// NOTICE: The method will detect terminal info each times, -// if only want to get current color level, please direct call SupportColor() or TermColorLevel() -func DetectColorLevel() Level { - level, _ := detectTermColorLevel() - return level -} - -// detect terminal color support level -// -// refer https://github.com/Delta456/box-cli-maker -func detectTermColorLevel() (level Level, needVTP bool) { - // on windows WSL: - // - runtime.GOOS == "Linux" - // - support true-color - // env: - // WSL_DISTRO_NAME=Debian - if val := os.Getenv("WSL_DISTRO_NAME"); val != "" { - // detect WSL as it has True Color support - if detectWSL() { - debugf("True Color support on WSL environment") - return terminfo.ColorLevelMillions, false - } - } - - isWin := runtime.GOOS == "windows" - termVal := os.Getenv("TERM") - - // on TERM=screen: not support true-color - if termVal != "screen" { - // On JetBrains Terminal - // - support true-color - // env: - // TERMINAL_EMULATOR=JetBrains-JediTerm - val := os.Getenv("TERMINAL_EMULATOR") - if val == "JetBrains-JediTerm" { - debugf("True Color support on JetBrains-JediTerm, is win: %v", isWin) - return terminfo.ColorLevelMillions, isWin - } - } - - // level, err = terminfo.ColorLevelFromEnv() - level = detectColorLevelFromEnv(termVal, isWin) - debugf("color level by detectColorLevelFromEnv: %s", level.String()) - - // fallback: simple detect by TERM value string. - if level == terminfo.ColorLevelNone { - debugf("level none - fallback check special term color support") - // on Windows: enable VTP as it has True Color support - level, needVTP = detectSpecialTermColor(termVal) - } - return -} - -// detectColorFromEnv returns the color level COLORTERM, FORCE_COLOR, -// TERM_PROGRAM, or determined from the TERM environment variable. -// -// refer the terminfo.ColorLevelFromEnv() -// https://en.wikipedia.org/wiki/Terminfo -func detectColorLevelFromEnv(termVal string, isWin bool) Level { - // check for overriding environment variables - colorTerm, termProg, forceColor := os.Getenv("COLORTERM"), os.Getenv("TERM_PROGRAM"), os.Getenv("FORCE_COLOR") - switch { - case strings.Contains(colorTerm, "truecolor") || strings.Contains(colorTerm, "24bit"): - if termVal == "screen" { // on TERM=screen: not support true-color - return terminfo.ColorLevelHundreds - } - return terminfo.ColorLevelMillions - case colorTerm != "" || forceColor != "": - return terminfo.ColorLevelBasic - case termProg == "Apple_Terminal": - return terminfo.ColorLevelHundreds - case termProg == "Terminus" || termProg == "Hyper": - if termVal == "screen" { // on TERM=screen: not support true-color - return terminfo.ColorLevelHundreds - } - return terminfo.ColorLevelMillions - case termProg == "iTerm.app": - if termVal == "screen" { // on TERM=screen: not support true-color - return terminfo.ColorLevelHundreds - } - - // check iTerm version - ver := os.Getenv("TERM_PROGRAM_VERSION") - if ver != "" { - i, err := strconv.Atoi(strings.Split(ver, ".")[0]) - if err != nil { - saveInternalError(terminfo.ErrInvalidTermProgramVersion) - // return terminfo.ColorLevelNone - return terminfo.ColorLevelHundreds - } - if i == 3 { - return terminfo.ColorLevelMillions - } - } - return terminfo.ColorLevelHundreds - } - - // otherwise determine from TERM's max_colors capability - if !isWin && termVal != "" { - debugf("TERM=%s - check color level by load terminfo file", termVal) - ti, err := terminfo.Load(termVal) - if err != nil { - saveInternalError(err) - return terminfo.ColorLevelNone - } - - debugf("the loaded term info file is: %s", ti.File) - v, ok := ti.Nums[terminfo.MaxColors] - switch { - case !ok || v <= 16: - return terminfo.ColorLevelNone - case ok && v >= 256: - return terminfo.ColorLevelHundreds - } - return terminfo.ColorLevelBasic - } - - // no TERM env value. default return none level - return terminfo.ColorLevelNone - // return terminfo.ColorLevelBasic -} - -var detectedWSL bool -var wslContents string - -// https://github.com/Microsoft/WSL/issues/423#issuecomment-221627364 -func detectWSL() bool { - if !detectedWSL { - detectedWSL = true - - b := make([]byte, 1024) - // `cat /proc/version` - // on mac: - // !not the file! - // on linux(debian,ubuntu,alpine): - // Linux version 4.19.121-linuxkit (root@18b3f92ade35) (gcc version 9.2.0 (Alpine 9.2.0)) #1 SMP Thu Jan 21 15:36:34 UTC 2021 - // on win git bash, conEmu: - // MINGW64_NT-10.0-19042 version 3.1.7-340.x86_64 (@WIN-N0G619FD3UK) (gcc version 9.3.0 (GCC) ) 2020-10-23 13:08 UTC - // on WSL: - // Linux version 4.4.0-19041-Microsoft (Microsoft@Microsoft.com) (gcc version 5.4.0 (GCC) ) #488-Microsoft Mon Sep 01 13:43:00 PST 2020 - f, err := os.Open("/proc/version") - if err == nil { - _, _ = f.Read(b) // ignore error - if err = f.Close(); err != nil { - saveInternalError(err) - } - - wslContents = string(b) - return strings.Contains(wslContents, "Microsoft") - } - } - return false -} - -/* -// refer -// https://github.com/Delta456/box-cli-maker/blob/7b5a1ad8a016ce181e7d8b05e24b54ff60b4b38a/detect_unix.go#L27-L45 -// detect WSL as it has True Color support -func isWSL() bool { - // on windows WSL: - // - runtime.GOOS == "Linux" - // - support true-color - // WSL_DISTRO_NAME=Debian - if val := os.Getenv("WSL_DISTRO_NAME"); val == "" { - return false - } - - // `cat /proc/sys/kernel/osrelease` - // on mac: - // !not the file! - // on linux: - // 4.19.121-linuxkit - // on WSL Output: - // 4.4.0-19041-Microsoft - wsl, err := ioutil.ReadFile("/proc/sys/kernel/osrelease") - if err != nil { - saveInternalError(err) - return false - } - - // it gives "Microsoft" for WSL and "microsoft" for WSL 2 - // it supports True-color - content := strings.ToLower(string(wsl)) - return strings.Contains(content, "microsoft") -} -*/ - -/************************************************************* - * helper methods for check env - *************************************************************/ - -// IsWindows OS env -func IsWindows() bool { - return runtime.GOOS == "windows" -} - -// IsConsole Determine whether w is one of stderr, stdout, stdin -func IsConsole(w io.Writer) bool { - o, ok := w.(*os.File) - if !ok { - return false - } - - fd := o.Fd() - - // fix: cannot use 'o == os.Stdout' to compare - return fd == uintptr(syscall.Stdout) || fd == uintptr(syscall.Stdin) || fd == uintptr(syscall.Stderr) -} - -// IsMSys msys(MINGW64) environment, does not necessarily support color -func IsMSys() bool { - // like "MSYSTEM=MINGW64" - return len(os.Getenv("MSYSTEM")) > 0 -} - -// IsSupportColor check current console is support color. -// -// NOTICE: The method will detect terminal info each times, -// if only want get current color level, please direct call SupportColor() or TermColorLevel() -func IsSupportColor() bool { - return IsSupport16Color() -} - -// IsSupport16Color check current console is support color. -// -// NOTICE: The method will detect terminal info each times, -// if only want get current color level, please direct call SupportColor() or TermColorLevel() -func IsSupport16Color() bool { - level, _ := detectTermColorLevel() - return level > terminfo.ColorLevelNone -} - -// IsSupport256Color render check -// -// NOTICE: The method will detect terminal info each times, -// if only want to get current color level, please direct call SupportColor() or TermColorLevel() -func IsSupport256Color() bool { - level, _ := detectTermColorLevel() - return level > terminfo.ColorLevelBasic -} - -// IsSupportRGBColor check. alias of the IsSupportTrueColor() -// -// NOTICE: The method will detect terminal info each times, -// if only want get current color level, please direct call SupportColor() or TermColorLevel() -func IsSupportRGBColor() bool { - return IsSupportTrueColor() -} - -// IsSupportTrueColor render check. -// -// NOTICE: The method will detect terminal info each times, -// if only want get current color level, please direct call SupportColor() or TermColorLevel() -// -// ENV: -// "COLORTERM=truecolor" -// "COLORTERM=24bit" -func IsSupportTrueColor() bool { - level, _ := detectTermColorLevel() - return level > terminfo.ColorLevelHundreds -} diff --git a/vendor/github.com/gookit/color/detect_nonwin.go b/vendor/github.com/gookit/color/detect_nonwin.go deleted file mode 100644 index 26f10908e..000000000 --- a/vendor/github.com/gookit/color/detect_nonwin.go +++ /dev/null @@ -1,49 +0,0 @@ -//go:build !windows -// +build !windows - -// The method in the file has no effect -// Only for compatibility with non-Windows systems - -package color - -import ( - "strings" - "syscall" - - "github.com/xo/terminfo" -) - -// detect special term color support -func detectSpecialTermColor(termVal string) (Level, bool) { - if termVal == "" { - return terminfo.ColorLevelNone, false - } - - debugf("terminfo check fail - fallback detect color by check TERM value") - - // on TERM=screen: - // - support 256, not support true-color. test on macOS - if termVal == "screen" { - return terminfo.ColorLevelHundreds, false - } - - if strings.Contains(termVal, "256color") { - return terminfo.ColorLevelHundreds, false - } - - if strings.Contains(termVal, "xterm") { - return terminfo.ColorLevelHundreds, false - // return terminfo.ColorLevelBasic, false - } - - // return terminfo.ColorLevelNone, nil - return terminfo.ColorLevelBasic, false -} - -// IsTerminal returns true if the given file descriptor is a terminal. -// -// Usage: -// IsTerminal(os.Stdout.Fd()) -func IsTerminal(fd uintptr) bool { - return fd == uintptr(syscall.Stdout) || fd == uintptr(syscall.Stdin) || fd == uintptr(syscall.Stderr) -} diff --git a/vendor/github.com/gookit/color/detect_windows.go b/vendor/github.com/gookit/color/detect_windows.go deleted file mode 100644 index df538b215..000000000 --- a/vendor/github.com/gookit/color/detect_windows.go +++ /dev/null @@ -1,250 +0,0 @@ -//go:build windows -// +build windows - -// Display color on Windows -// -// refer: -// -// golang.org/x/sys/windows -// golang.org/x/crypto/ssh/terminal -// https://docs.microsoft.com/en-us/windows/console -package color - -import ( - "os" - "syscall" - "unsafe" - - "github.com/xo/terminfo" - "golang.org/x/sys/windows" -) - -// related docs -// https://docs.microsoft.com/zh-cn/windows/console/console-virtual-terminal-sequences -// https://docs.microsoft.com/zh-cn/windows/console/console-virtual-terminal-sequences#samples -var ( - // isMSys bool - kernel32 *syscall.LazyDLL - - procGetConsoleMode *syscall.LazyProc - procSetConsoleMode *syscall.LazyProc -) - -func init() { - if !SupportColor() { - isLikeInCmd = true - return - } - - // if disabled. - if !Enable { - return - } - - // if at Windows's ConEmu, Cmder, putty ... terminals not need VTP - - // -------- try force enable colors on windows terminal ------- - tryEnableVTP(needVTP) - - // fetch console screen buffer info - // err := getConsoleScreenBufferInfo(uintptr(syscall.Stdout), &defScreenInfo) -} - -// try force enable colors on Windows terminal -func tryEnableVTP(enable bool) bool { - if !enable { - return false - } - - debugf("True-Color by enable VirtualTerminalProcessing on windows") - - initKernel32Proc() - - // enable colors on Windows terminal - if tryEnableOnCONOUT() { - return true - } - - return tryEnableOnStdout() -} - -func initKernel32Proc() { - if kernel32 != nil { - return - } - - // load related Windows dll - // https://docs.microsoft.com/en-us/windows/console/setconsolemode - kernel32 = syscall.NewLazyDLL("kernel32.dll") - - procGetConsoleMode = kernel32.NewProc("GetConsoleMode") - procSetConsoleMode = kernel32.NewProc("SetConsoleMode") -} - -func tryEnableOnCONOUT() bool { - outHandle, err := syscall.Open("CONOUT$", syscall.O_RDWR, 0) - if err != nil { - saveInternalError(err) - return false - } - - err = EnableVirtualTerminalProcessing(outHandle, true) - if err != nil { - saveInternalError(err) - return false - } - - return true -} - -func tryEnableOnStdout() bool { - // try direct open syscall.Stdout - err := EnableVirtualTerminalProcessing(syscall.Stdout, true) - if err != nil { - saveInternalError(err) - return false - } - - return true -} - -// Get the Windows Version and Build Number -var ( - winVersion, _, buildNumber = windows.RtlGetNtVersionNumbers() -) - -// refer -// -// https://github.com/Delta456/box-cli-maker/blob/7b5a1ad8a016ce181e7d8b05e24b54ff60b4b38a/detect_windows.go#L30-L57 -// https://github.com/gookit/color/issues/25#issuecomment-738727917 -// -// detects the color level supported on Windows: cmd, powerShell -func detectSpecialTermColor(termVal string) (tl Level, needVTP bool) { - if os.Getenv("ConEmuANSI") == "ON" { - debugf("support True Color by ConEmuANSI=ON") - // ConEmuANSI is "ON" for generic ANSI support - // but True Color option is enabled by default - // I am just assuming that people wouldn't have disabled it - // Even if it is not enabled then ConEmu will auto round off - // accordingly - return terminfo.ColorLevelMillions, false - } - - // Before Windows 10 Build Number 10586, console never supported ANSI Colors - if buildNumber < 10586 || winVersion < 10 { - // Detect if using ANSICON on older systems - if os.Getenv("ANSICON") != "" { - conVersion := os.Getenv("ANSICON_VER") - // 8-bit Colors were only supported after v1.81 release - if conVersion >= "181" { - return terminfo.ColorLevelHundreds, false - } - return terminfo.ColorLevelBasic, false - } - - return terminfo.ColorLevelNone, false - } - - // True Color is not available before build 14931 so fallback to 8-bit color. - if buildNumber < 14931 { - return terminfo.ColorLevelHundreds, true - } - - // Windows 10 build 14931 is the first release that supports 16m/TrueColor - debugf("support True Color on windows version is >= build 14931") - return terminfo.ColorLevelMillions, true -} - -/************************************************************* - * render full color code on Windows(8,16,24bit color) - *************************************************************/ - -// docs https://docs.microsoft.com/zh-cn/windows/console/getconsolemode#parameters -const ( - // equals to docs page's ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 - EnableVirtualTerminalProcessingMode uint32 = 0x4 -) - -// EnableVirtualTerminalProcessing Enable virtual terminal processing -// -// ref from github.com/konsorten/go-windows-terminal-sequences -// doc https://docs.microsoft.com/zh-cn/windows/console/console-virtual-terminal-sequences#samples -// -// Usage: -// -// err := EnableVirtualTerminalProcessing(syscall.Stdout, true) -// // support print color text -// err = EnableVirtualTerminalProcessing(syscall.Stdout, false) -func EnableVirtualTerminalProcessing(stream syscall.Handle, enable bool) error { - var mode uint32 - // Check if it is currently in the terminal - // err := syscall.GetConsoleMode(syscall.Stdout, &mode) - err := syscall.GetConsoleMode(stream, &mode) - if err != nil { - // fmt.Println("EnableVirtualTerminalProcessing", err) - return err - } - - if enable { - mode |= EnableVirtualTerminalProcessingMode - } else { - mode &^= EnableVirtualTerminalProcessingMode - } - - ret, _, err := procSetConsoleMode.Call(uintptr(stream), uintptr(mode)) - if ret == 0 { - return err - } - - return nil -} - -// renderColorCodeOnCmd enable cmd color render. -// func renderColorCodeOnCmd(fn func()) { -// err := EnableVirtualTerminalProcessing(syscall.Stdout, true) -// // if is not in terminal, will clear color tag. -// if err != nil { -// // panic(err) -// fn() -// return -// } -// -// // force open color render -// old := ForceOpenColor() -// fn() -// // revert color setting -// supportColor = old -// -// err = EnableVirtualTerminalProcessing(syscall.Stdout, false) -// if err != nil { -// panic(err) -// } -// } - -/************************************************************* - * render simple color code on Windows - *************************************************************/ - -// IsTty returns true if the given file descriptor is a terminal. -func IsTty(fd uintptr) bool { - initKernel32Proc() - - var st uint32 - r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) - return r != 0 && e == 0 -} - -// IsTerminal returns true if the given file descriptor is a terminal. -// -// Usage: -// -// fd := os.Stdout.Fd() -// fd := uintptr(syscall.Stdout) // for Windows -// IsTerminal(fd) -func IsTerminal(fd uintptr) bool { - initKernel32Proc() - - var st uint32 - r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) - return r != 0 && e == 0 -} diff --git a/vendor/github.com/gookit/color/index.html b/vendor/github.com/gookit/color/index.html deleted file mode 100644 index 19e8e9a50..000000000 --- a/vendor/github.com/gookit/color/index.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - Color - A command-line color library with true color support, universal API methods and Windows support. - - -
- - - - diff --git a/vendor/github.com/gookit/color/printer.go b/vendor/github.com/gookit/color/printer.go deleted file mode 100644 index 985a0b624..000000000 --- a/vendor/github.com/gookit/color/printer.go +++ /dev/null @@ -1,133 +0,0 @@ -package color - -import "fmt" - -/************************************************************* - * colored message Printer - *************************************************************/ - -// PrinterFace interface -type PrinterFace interface { - fmt.Stringer - Sprint(a ...any) string - Sprintf(format string, a ...any) string - Print(a ...any) - Printf(format string, a ...any) - Println(a ...any) -} - -// Printer a generic color message printer. -// -// Usage: -// -// p := &Printer{Code: "32;45;3"} -// p.Print("message") -type Printer struct { - // NoColor disable color. - NoColor bool - // Code color code string. eg "32;45;3" - Code string -} - -// NewPrinter instance -func NewPrinter(colorCode string) *Printer { - return &Printer{Code: colorCode} -} - -// String returns color code string. eg: "32;45;3" -func (p *Printer) String() string { - // panic("implement me") - return p.Code -} - -// Sprint returns rendering colored messages -func (p *Printer) Sprint(a ...any) string { - return RenderCode(p.String(), a...) -} - -// Sprintf returns format and rendering colored messages -func (p *Printer) Sprintf(format string, a ...any) string { - return RenderString(p.String(), fmt.Sprintf(format, a...)) -} - -// Print rendering colored messages -func (p *Printer) Print(a ...any) { - doPrintV2(p.String(), fmt.Sprint(a...)) -} - -// Printf format and rendering colored messages -func (p *Printer) Printf(format string, a ...any) { - doPrintV2(p.String(), fmt.Sprintf(format, a...)) -} - -// Println rendering colored messages with newline -func (p *Printer) Println(a ...any) { - doPrintlnV2(p.Code, a) -} - -// IsEmpty color code -func (p *Printer) IsEmpty() bool { - return p.Code == "" -} - -/************************************************************* - * SimplePrinter struct - *************************************************************/ - -// SimplePrinter use for quick use color print on inject to struct -type SimplePrinter struct{} - -// Print message -func (s *SimplePrinter) Print(v ...any) { - Print(v...) -} - -// Printf message -func (s *SimplePrinter) Printf(format string, v ...any) { - Printf(format, v...) -} - -// Println message -func (s *SimplePrinter) Println(v ...any) { - Println(v...) -} - -// Successf message -func (s *SimplePrinter) Successf(format string, a ...any) { - Success.Printf(format, a...) -} - -// Successln message -func (s *SimplePrinter) Successln(a ...any) { - Success.Println(a...) -} - -// Infof message -func (s *SimplePrinter) Infof(format string, a ...any) { - Info.Printf(format, a...) -} - -// Infoln message -func (s *SimplePrinter) Infoln(a ...any) { - Info.Println(a...) -} - -// Warnf message -func (s *SimplePrinter) Warnf(format string, a ...any) { - Warn.Printf(format, a...) -} - -// Warnln message -func (s *SimplePrinter) Warnln(a ...any) { - Warn.Println(a...) -} - -// Errorf message -func (s *SimplePrinter) Errorf(format string, a ...any) { - Error.Printf(format, a...) -} - -// Errorln message -func (s *SimplePrinter) Errorln(a ...any) { - Error.Println(a...) -} diff --git a/vendor/github.com/gookit/color/style.go b/vendor/github.com/gookit/color/style.go deleted file mode 100644 index 353d39f10..000000000 --- a/vendor/github.com/gookit/color/style.go +++ /dev/null @@ -1,324 +0,0 @@ -package color - -import ( - "fmt" - "strings" -) - -/************************************************************* - * 16 color Style - *************************************************************/ - -// Style a 16 color style. can add: fg color, bg color, color options -// -// Example: -// -// color.Style{color.FgGreen}.Print("message") -type Style []Color - -// New create a custom style -// -// Usage: -// -// color.New(color.FgGreen).Print("message") -// equals to: -// color.Style{color.FgGreen}.Print("message") -func New(colors ...Color) Style { - return colors -} - -// Save to global styles map -func (s Style) Save(name string) { - AddStyle(name, s) -} - -// Add to global styles map -func (s *Style) Add(cs ...Color) { - *s = append(*s, cs...) -} - -// Render colored text -// -// Usage: -// -// color.New(color.FgGreen).Render("text") -// color.New(color.FgGreen, color.BgBlack, color.OpBold).Render("text") -func (s Style) Render(a ...any) string { - return RenderCode(s.String(), a...) -} - -// Renderln render text with newline. -// like Println, will add spaces for each argument -// -// Usage: -// -// color.New(color.FgGreen).Renderln("text", "more") -// color.New(color.FgGreen, color.BgBlack, color.OpBold).Render("text", "more") -func (s Style) Renderln(a ...any) string { - return RenderWithSpaces(s.String(), a...) -} - -// Sprint is alias of the 'Render' -func (s Style) Sprint(a ...any) string { - return RenderCode(s.String(), a...) -} - -// Sprintf format and render message. -func (s Style) Sprintf(format string, a ...any) string { - return RenderString(s.String(), fmt.Sprintf(format, a...)) -} - -// Print render and Print text -func (s Style) Print(a ...any) { - doPrintV2(s.String(), fmt.Sprint(a...)) -} - -// Printf render and print text -func (s Style) Printf(format string, a ...any) { - doPrintV2(s.Code(), fmt.Sprintf(format, a...)) -} - -// Println render and print text line -func (s Style) Println(a ...any) { - doPrintlnV2(s.String(), a) -} - -// Code convert to code string. returns like "32;45;3" -func (s Style) Code() string { - return s.String() -} - -// String convert to code string. returns like "32;45;3" -func (s Style) String() string { - return Colors2code(s...) -} - -// IsEmpty style -func (s Style) IsEmpty() bool { - return len(s) == 0 -} - -/************************************************************* - * Theme(extended Style) - *************************************************************/ - -// Theme definition. extends from Style -type Theme struct { - // Name theme name - Name string - // Style for the theme - Style -} - -// NewTheme instance -func NewTheme(name string, style Style) *Theme { - return &Theme{name, style} -} - -// Save to themes map -func (t *Theme) Save() { - AddTheme(t.Name, t.Style) -} - -// Tips use name as title, only apply style for name -func (t *Theme) Tips(format string, a ...any) { - // only apply style for name - t.Print(strings.ToUpper(t.Name) + ": ") - Printf(format+"\n", a...) -} - -// Prompt use name as title, and apply style for message -func (t *Theme) Prompt(format string, a ...any) { - title := strings.ToUpper(t.Name) + ":" - t.Println(title, fmt.Sprintf(format, a...)) -} - -// Block like Prompt, but will wrap a empty line -func (t *Theme) Block(format string, a ...any) { - title := strings.ToUpper(t.Name) + ":\n" - - t.Println(title, fmt.Sprintf(format, a...)) -} - -/************************************************************* - * Theme: internal themes - *************************************************************/ - -// internal themes(like bootstrap style) -// Usage: -// -// color.Info.Print("message") -// color.Info.Printf("a %s message", "test") -// color.Warn.Println("message") -// color.Error.Println("message") -var ( - // Info color style - Info = &Theme{"info", Style{OpReset, FgGreen}} - // Note color style - Note = &Theme{"note", Style{OpBold, FgLightCyan}} - // Warn color style - Warn = &Theme{"warning", Style{OpBold, FgYellow}} - // Light color style - Light = &Theme{"light", Style{FgLightWhite, BgBlack}} - // Error color style - Error = &Theme{"error", Style{FgLightWhite, BgRed}} - // Danger color style - Danger = &Theme{"danger", Style{OpBold, FgRed}} - // Debug color style - Debug = &Theme{"debug", Style{OpReset, FgCyan}} - // Notice color style - Notice = &Theme{"notice", Style{OpBold, FgCyan}} - // Comment color style - Comment = &Theme{"comment", Style{OpReset, FgYellow}} - // Success color style - Success = &Theme{"success", Style{OpBold, FgGreen}} - // Primary color style - Primary = &Theme{"primary", Style{OpReset, FgBlue}} - // Question color style - Question = &Theme{"question", Style{OpReset, FgMagenta}} - // Secondary color style - Secondary = &Theme{"secondary", Style{FgDarkGray}} -) - -// Themes internal defined themes. -// Usage: -// -// color.Themes["info"].Println("message") -var Themes = map[string]*Theme{ - "info": Info, - "note": Note, - "light": Light, - "error": Error, - - "debug": Debug, - "danger": Danger, - "notice": Notice, - "success": Success, - "comment": Comment, - "primary": Primary, - "warning": Warn, - - "question": Question, - "secondary": Secondary, -} - -// AddTheme add a theme and style -func AddTheme(name string, style Style) { - Themes[name] = NewTheme(name, style) - Styles[name] = style -} - -// GetTheme get defined theme by name -func GetTheme(name string) *Theme { - return Themes[name] -} - -/************************************************************* - * internal styles - *************************************************************/ - -// Styles internal defined styles, like bootstrap styles. -// Usage: -// -// color.Styles["info"].Println("message") -var Styles = map[string]Style{ - "info": {OpReset, FgGreen}, - "note": {OpBold, FgLightCyan}, - "light": {FgLightWhite, BgRed}, - "error": {FgLightWhite, BgRed}, - - "danger": {OpBold, FgRed}, - "notice": {OpBold, FgCyan}, - "success": {OpBold, FgGreen}, - "comment": {OpReset, FgMagenta}, - "primary": {OpReset, FgBlue}, - "warning": {OpBold, FgYellow}, - - "question": {OpReset, FgMagenta}, - "secondary": {FgDarkGray}, -} - -// some style name alias -var styleAliases = map[string]string{ - "err": "error", - "suc": "success", - "warn": "warning", -} - -// AddStyle add a style -func AddStyle(name string, s Style) { - Styles[name] = s -} - -// GetStyle get defined style by name -func GetStyle(name string) Style { - if s, ok := Styles[name]; ok { - return s - } - - if realName, ok := styleAliases[name]; ok { - return Styles[realName] - } - - // empty style - return New() -} - -/************************************************************* - * color scheme - *************************************************************/ - -// Scheme struct -type Scheme struct { - Name string - Styles map[string]Style -} - -// NewScheme create new Scheme -func NewScheme(name string, styles map[string]Style) *Scheme { - return &Scheme{Name: name, Styles: styles} -} - -// NewDefaultScheme create an defuault color Scheme -func NewDefaultScheme(name string) *Scheme { - return NewScheme(name, map[string]Style{ - "info": {OpReset, FgGreen}, - "warn": {OpBold, FgYellow}, - "error": {FgLightWhite, BgRed}, - }) -} - -// Style get by name -func (s *Scheme) Style(name string) Style { - return s.Styles[name] -} - -// Infof message print -func (s *Scheme) Infof(format string, a ...any) { - s.Styles["info"].Printf(format, a...) -} - -// Infoln message print -func (s *Scheme) Infoln(v ...any) { - s.Styles["info"].Println(v...) -} - -// Warnf message print -func (s *Scheme) Warnf(format string, a ...any) { - s.Styles["warn"].Printf(format, a...) -} - -// Warnln message print -func (s *Scheme) Warnln(v ...any) { - s.Styles["warn"].Println(v...) -} - -// Errorf message print -func (s *Scheme) Errorf(format string, a ...any) { - s.Styles["error"].Printf(format, a...) -} - -// Errorln message print -func (s *Scheme) Errorln(v ...any) { - s.Styles["error"].Println(v...) -} diff --git a/vendor/github.com/gookit/color/utils.go b/vendor/github.com/gookit/color/utils.go deleted file mode 100644 index b6920f6dc..000000000 --- a/vendor/github.com/gookit/color/utils.go +++ /dev/null @@ -1,209 +0,0 @@ -package color - -import ( - "fmt" - "io" - "log" - "strings" -) - -// SetTerminal by given code. -func SetTerminal(code string) error { - if !Enable || !SupportColor() { - return nil - } - - _, err := fmt.Fprintf(output, SettingTpl, code) - return err -} - -// ResetTerminal terminal setting. -func ResetTerminal() error { - if !Enable || !SupportColor() { - return nil - } - - _, err := fmt.Fprint(output, ResetSet) - return err -} - -/************************************************************* - * print methods(will auto parse color tags) - *************************************************************/ - -// Print render color tag and print messages -func Print(a ...any) { - Fprint(output, a...) -} - -// Printf format and print messages -func Printf(format string, a ...any) { - Fprintf(output, format, a...) -} - -// Println messages with new line -func Println(a ...any) { - Fprintln(output, a...) -} - -// Fprint print rendered messages to writer -// -// Notice: will ignore print error -func Fprint(w io.Writer, a ...any) { - _, err := fmt.Fprint(w, Render(a...)) - saveInternalError(err) -} - -// Fprintf print format and rendered messages to writer. -// Notice: will ignore print error -func Fprintf(w io.Writer, format string, a ...any) { - str := fmt.Sprintf(format, a...) - _, err := fmt.Fprint(w, ReplaceTag(str)) - saveInternalError(err) -} - -// Fprintln print rendered messages line to writer -// Notice: will ignore print error -func Fprintln(w io.Writer, a ...any) { - str := formatArgsForPrintln(a) - _, err := fmt.Fprintln(w, ReplaceTag(str)) - saveInternalError(err) -} - -// Lprint passes colored messages to a log.Logger for printing. -// Notice: should be goroutine safe -func Lprint(l *log.Logger, a ...any) { - l.Print(Render(a...)) -} - -// Render parse color tags, return rendered string. -// -// Usage: -// -// text := Render("hello world!") -// fmt.Println(text) -func Render(a ...any) string { - if len(a) == 0 { - return "" - } - return ReplaceTag(fmt.Sprint(a...)) -} - -// Sprint parse color tags, return rendered string -func Sprint(a ...any) string { - if len(a) == 0 { - return "" - } - return ReplaceTag(fmt.Sprint(a...)) -} - -// Sprintf format and return rendered string -func Sprintf(format string, a ...any) string { - return ReplaceTag(fmt.Sprintf(format, a...)) -} - -// String alias of the ReplaceTag -func String(s string) string { return ReplaceTag(s) } - -// Text alias of the ReplaceTag -func Text(s string) string { return ReplaceTag(s) } - -// Uint8sToInts convert []uint8 to []int -// func Uint8sToInts(u8s []uint8 ) []int { -// ints := make([]int, len(u8s)) -// for i, u8 := range u8s { -// ints[i] = int(u8) -// } -// return ints -// } - -/************************************************************* - * helper methods for print - *************************************************************/ - -// new implementation, support render full color code on pwsh.exe, cmd.exe -func doPrintV2(code, str string) { - _, err := fmt.Fprint(output, RenderString(code, str)) - saveInternalError(err) -} - -// new implementation, support render full color code on pwsh.exe, cmd.exe -func doPrintlnV2(code string, args []any) { - str := formatArgsForPrintln(args) - _, err := fmt.Fprintln(output, RenderString(code, str)) - saveInternalError(err) -} - -// use Println, will add spaces for each arg -func formatArgsForPrintln(args []any) (message string) { - if ln := len(args); ln == 0 { - message = "" - } else if ln == 1 { - message = fmt.Sprint(args[0]) - } else { - message = fmt.Sprintln(args...) - // clear last "\n" - message = message[:len(message)-1] - } - return -} - -/************************************************************* - * helper methods - *************************************************************/ - -// is on debug mode -// func isDebugMode() bool { -// return debugMode == "on" -// } - -func debugf(f string, v ...any) { - if debugMode { - fmt.Print("COLOR_DEBUG: ") - fmt.Printf(f, v...) - fmt.Println() - } -} - -// equals: return ok ? val1 : val2 -func isValidUint8(val int) bool { - return val >= 0 && val < 256 -} - -// equals: return ok ? val1 : val2 -func compareVal(ok bool, val1, val2 uint8) uint8 { - if ok { - return val1 - } - return val2 -} - -// equals: return ok ? val1 : val2 -func compareF64Val(ok bool, val1, val2 float64) float64 { - if ok { - return val1 - } - return val2 -} - -func saveInternalError(err error) { - if err != nil { - debugf("inner error: %s", err.Error()) - innerErrs = append(innerErrs, err) - } -} - -func stringToArr(str, sep string) (arr []string) { - str = strings.TrimSpace(str) - if str == "" { - return - } - - ss := strings.Split(str, sep) - for _, val := range ss { - if val = strings.TrimSpace(val); val != "" { - arr = append(arr, val) - } - } - return -} diff --git a/vendor/github.com/gookit/config/v2/README.md b/vendor/github.com/gookit/config/v2/README.md index 5a6287550..0b1a6391f 100644 --- a/vendor/github.com/gookit/config/v2/README.md +++ b/vendor/github.com/gookit/config/v2/README.md @@ -421,7 +421,10 @@ config.WithOptions(func(opt *Options) { ### Options: Parse default -Support parse default value by struct tag `default` +Support parse default value by struct tag `default`, and support parse fields in sub struct. + +> **NOTE**⚠️ If you want to parse a sub-struct, you need to set the `default:""` flag on the parent struct, +> otherwise the fields of the sub-struct will not be resolved. ```go // add option: config.ParseDefault diff --git a/vendor/github.com/gookit/config/v2/README.zh-CN.md b/vendor/github.com/gookit/config/v2/README.zh-CN.md index 420813c21..06cd7329c 100644 --- a/vendor/github.com/gookit/config/v2/README.zh-CN.md +++ b/vendor/github.com/gookit/config/v2/README.zh-CN.md @@ -26,7 +26,7 @@ - `Readonly` 支持设置配置数据只读 - `EnableCache` 支持设置配置数据缓存 - `ParseEnv` 支持获取时自动解析string值里的ENV变量(`shell: ${SHELL}` -> `shell: /bin/zsh`) - - `ParseDefault` 支持在绑定数据到结构体时解析默认值 (tag: `default:"def_value"`, 配合ParseEnv也支持ENV变量) + - `ParseDefault` 支持在绑定数据到结构体时解析默认值 (tag: `default:"def_value"`, 配合`ParseEnv`也支持ENV变量) - `ParseTime` 支持绑定数据到struct时自动转换 `10s`,`2m` 为 `time.Duration` - 完整选项设置请查看 `config.Options` - 支持将全部或部分配置数据绑定到结构体 `config.BindStruct("key", &s)` @@ -414,7 +414,9 @@ config.WithOptions(func(opt *Options) { ### 选项: 解析默认值 -NEW: 支持通过结构标签 `default` 解析并设置默认值 +NEW: 支持通过结构标签 `default` 解析并设置默认值,支持嵌套解析处理。 + +> 注意 ⚠️ 如果想要解析子结构体字段,需要对父结构体设置 `default:""` 标记,否则不会解析子结构体的字段。 ```go // add option: config.ParseDefault diff --git a/vendor/github.com/gookit/config/v2/export.go b/vendor/github.com/gookit/config/v2/export.go index 7f20b6179..e33716a71 100644 --- a/vendor/github.com/gookit/config/v2/export.go +++ b/vendor/github.com/gookit/config/v2/export.go @@ -7,8 +7,8 @@ import ( "io" "os" + "github.com/go-viper/mapstructure/v2" "github.com/gookit/goutil/structs" - "github.com/mitchellh/mapstructure" ) // Decode all config data to the dst ptr @@ -37,17 +37,13 @@ func (c *Config) Decode(dst any) error { func MapStruct(key string, dst any) error { return dc.MapStruct(key, dst) } // MapStruct alias method of the 'Structure' -func (c *Config) MapStruct(key string, dst any) error { - return c.Structure(key, dst) -} +func (c *Config) MapStruct(key string, dst any) error { return c.Structure(key, dst) } // BindStruct alias method of the 'Structure' func BindStruct(key string, dst any) error { return dc.BindStruct(key, dst) } // BindStruct alias method of the 'Structure' -func (c *Config) BindStruct(key string, dst any) error { - return c.Structure(key, dst) -} +func (c *Config) BindStruct(key string, dst any) error { return c.Structure(key, dst) } // MapOnExists mapping data to the dst structure only on key exists. func MapOnExists(key string, dst any) error { @@ -63,7 +59,6 @@ func (c *Config) MapOnExists(key string, dst any) error { if err != nil && err == ErrNotFound { return nil } - return err } @@ -86,6 +81,7 @@ func (c *Config) Structure(key string, dst any) (err error) { if c.opts.ParseDefault { err = structs.InitDefaults(dst, func(opt *structs.InitOptions) { opt.ParseEnv = c.opts.ParseEnv + opt.ParseTime = c.opts.ParseTime // add ParseTime support on parse default value }) } return @@ -116,6 +112,7 @@ func (c *Config) Structure(key string, dst any) (err error) { if c.opts.ParseDefault { err = structs.InitDefaults(dst, func(opt *structs.InitOptions) { opt.ParseEnv = c.opts.ParseEnv + opt.ParseTime = c.opts.ParseTime }) } return err diff --git a/vendor/github.com/gookit/config/v2/options.go b/vendor/github.com/gookit/config/v2/options.go index 09a1ee286..f2e89e29f 100644 --- a/vendor/github.com/gookit/config/v2/options.go +++ b/vendor/github.com/gookit/config/v2/options.go @@ -4,8 +4,8 @@ import ( "strings" "dario.cat/mergo" + "github.com/go-viper/mapstructure/v2" "github.com/gookit/goutil" - "github.com/mitchellh/mapstructure" ) // there are some event names for config data changed. @@ -33,12 +33,15 @@ type Options struct { // ParseDefault tag on binding data to struct. default: false // // - tag: default + // + // NOTE: If you want to parse a substruct, you need to set the `default:""` flag on the struct, + // otherwise the fields that will not resolve to it will not be resolved. ParseDefault bool // Readonly config is readonly. default: false Readonly bool // EnableCache enable config data cache. default: false EnableCache bool - // ParseKey support key path, allow find value by key path. default: true + // ParseKey support key path, allow finding value by key path. default: true // // - eg: 'key.sub' will find `map[key]sub` ParseKey bool diff --git a/vendor/github.com/gookit/config/v2/util.go b/vendor/github.com/gookit/config/v2/util.go index 459fa573b..aca699f74 100644 --- a/vendor/github.com/gookit/config/v2/util.go +++ b/vendor/github.com/gookit/config/v2/util.go @@ -4,10 +4,10 @@ import ( "os" "reflect" "strings" - "time" + "github.com/go-viper/mapstructure/v2" "github.com/gookit/goutil/envutil" - "github.com/mitchellh/mapstructure" + "github.com/gookit/goutil/reflects" ) // ValDecodeHookFunc returns a mapstructure.DecodeHookFunc @@ -31,15 +31,9 @@ func ValDecodeHookFunc(parseEnv, parseTime bool) mapstructure.DecodeHookFunc { return str, nil } - // start char is number(1-9) - if str[0] > '0' && str[0] <= '9' { - // parse time string. eg: 10s - if parseTime && t.Kind() == reflect.Int64 { - dur, err := time.ParseDuration(str) - if err == nil { - return dur, nil - } - } + // feat: support parse time or duration string. eg: 10s + if parseTime && str[0] > '0' && str[0] <= '9' { + return reflects.ToTimeOrDuration(str, t) } return str, nil } diff --git a/vendor/github.com/gookit/goutil/README.md b/vendor/github.com/gookit/goutil/README.md index fa85d8961..60ae33df5 100644 --- a/vendor/github.com/gookit/goutil/README.md +++ b/vendor/github.com/gookit/goutil/README.md @@ -7,56 +7,63 @@ [![Coverage Status](https://coveralls.io/repos/github/gookit/goutil/badge.svg?branch=master)](https://coveralls.io/github/gookit/goutil?branch=master) [![Go Reference](https://pkg.go.dev/badge/github.com/gookit/goutil.svg)](https://pkg.go.dev/github.com/gookit/goutil) -💪 Useful utils(**700+**) package for the Go: int, string, array/slice, map, error, time, format, CLI, ENV, filesystem, system, testing and more. +💪 Useful utils(**800+**) package for the Go: int, string, array/slice, map, error, time, format, CLI, ENV, filesystem, system, testing and more. > **[中文说明](README.zh-CN.md)** -**Basic packages:** +## Packages + +### Basic packages - [`arrutil`](arrutil): Array/Slice util functions. eg: check, convert, formatting, enum, collections -- [`cliutil`](cliutil) Command-line util functions. eg: colored print, read input, exec command -- [`envutil`](envutil) ENV util for current runtime env information. eg: get one, get info, parse var -- [`fmtutil`](fmtutil) Format data util functions. eg: data, size, time -- [`fsutil`](fsutil) Filesystem util functions, quick create, read and write file. eg: file and dir check, operate -- [`goinfo`](goinfo) provide some standard util functions for go. -- [`jsonutil`](jsonutil) Provide some util functions for quick read, write, encode, decode JSON data. +- [`byteutil`](byteutil): Provide some common bytes util functions. eg: convert, check and more - [`maputil`](maputil) Map data util functions. eg: convert, sub-value get, simple merge - [`mathutil`](mathutil) Math(int, number) util functions. eg: convert, math calc, random -- [`netutil`](netutil) Network util functions. eg: Ip, IpV4, IpV6, Mac, Port, Hostname, etc. - [`reflects`](reflects) Provide extends reflect util functions. - [`structs`](structs) Provide some extends util functions for struct. eg: tag parse, struct data init - [`strutil`](strutil) String util functions. eg: bytes, check, convert, encode, format and more - [`sysutil`](sysutil) System util functions. eg: sysenv, exec, user, process +- [`cliutil`](cliutil) Command-line util functions. eg: colored print, read input, exec command +- [`envutil`](envutil) ENV util for current runtime env information. eg: get one, get info, parse var +- [`fsutil`](fsutil) Filesystem util functions, quick create, read and write file. eg: file and dir check, operate +- [`jsonutil`](jsonutil) Provide some util functions for quick read, write, encode, decode JSON data. -**Extra packages:** +### Debug & Test & Errors + +- [`dump`](dump): GO value printing tool. print slice, map will auto wrap each element and display the call location +- [`errorx`](errorx) Provide an enhanced error implements for go, allow with stacktrace and wrap another error. +- [`assert`](testutil/assert) Provides commonly asserts functions for help testing +- [`testutil`](testutil) Test help util functions. eg: http test, mock ENV value +- [`fakeobj`](x/fakeobj) provides a fake object for testing. such as fake fs.File, fs.FileInfo, fs.DirEntry etc. + +### Extra Tools packages - [`cflag`](cflag): Wraps and extends go `flag.FlagSet` to build simple command line applications -- cli util: - - [cmdline](cliutil/cmdline) Provide cmdline parse, args build to cmdline -- [`dump`](dump): GO value printing tool. print slice, map will auto wrap each element and display the call location -- [`encodes`](encodes): Provide some encoding/decoding, hash, crypto util functions. eg: base64, hex, etc. -- [`errorx`](errorx) Provide an enhanced error implements for go, allow with stacktrace and wrap another error. -- file util: - - [`finder`](fsutil/finder) Provides a simple and convenient filedir lookup function, supports filtering, excluding, matching, ignoring, etc. -- net util: - - [httpreq](netutil/httpreq) An easier-to-use HTTP client that wraps http.Client, and with some http utils. -- string util: - - [textscan](strutil/textscan) Implemented a parser that quickly scans and analyzes text content. It can be used to parse INI, Properties and other formats - - [textutil](strutil/textutil) Provide some extensions text handle util functions. eg: text replace, etc. -- [syncs](syncs) Provides synchronization primitives util functions. -- system util: - - [clipboard](sysutil/clipboard) Provide a simple clipboard read and write operations. - - [cmdr](sysutil/cmdr) Provide for quick build and run a cmd, batch run multi cmd tasks - - [process](sysutil/process) Provide some process handle util functions. -- [`testutil`](testutil) Test help util functions. eg: http test, mock ENV value - - [assert](testutil/assert) Provides commonly asserts functions for help testing - - [fakeobj](testutil/fakeobj) provides a fake object for testing. such as fake fs.File, fs.FileInfo, fs.DirEntry etc. +- [`ccolor`](x/ccolor): Simple command-line color output library that uses ANSI color codes to output text with colors. - [`timex`](timex) Provides an enhanced time.Time implementation. Add more commonly used functional methods + - Provides datetime format parsing like `Y-m-d H:i:s` - such as: DayStart(), DayAfter(), DayAgo(), DateFormat() and more. +- [httpreq](netutil/httpreq) An easier-to-use HTTP client that wraps http.Client, and with some http utils. +- [syncs](syncs) Provides synchronization primitives util functions. + +**More ...** + +- [`cmdline`](cliutil/cmdline) Provide cmdline parse, args build to cmdline +- [`encodes`](encodes): Provide some encoding/decoding, hash, crypto util functions. eg: base64, hex, etc. +- [`finder`](x/finder) Provides a simple and convenient file/dir lookup function, supports filtering, excluding, matching, ignoring, etc. +- [`netutil`](netutil) Network util functions. eg: Ip, IpV4, IpV6, Mac, Port, Hostname, etc. +- [textutil](strutil/textutil) Provide some extensions text handle util functions. eg: text replace, etc. +- [textscan](strutil/textscan) Implemented a parser that quickly scans and analyzes text content. It can be used to parse INI, Properties and other formats +- [`cmdr`](sysutil/cmdr) Provide for quick build and run a cmd, batch run multi cmd tasks +- [`clipboard`](x/clipboard) Provide a simple clipboard read and write operations. +- [`process`](sysutil/process) Provide some process handle util functions. +- [`fmtutil`](x/fmtutil) Format data util functions. eg: data, size, time +- [`goinfo`](x/goinfo) provide some standard util functions for go. ## Go Doc -Please see [Go doc](https://pkg.go.dev/github.com/gookit/goutil) +Please see [Go doc](https://pkg.go.dev/github.com/gookit/goutil). +Wiki docs on [DeepWiki - gookit/goutil](https://deepwiki.com/gookit/goutil) ## Install @@ -166,8 +173,9 @@ func Remove[T comdef.Compared](ls []T, val T) []T func Filter[T any](ls []T, filter ...comdef.MatchFunc[T]) []T func Map[T any, V any](list []T, mapFn MapFn[T, V]) []V func Column[T any, V any](list []T, mapFn func(obj T) (val V, find bool)) []V -func Unique[T ~string | comdef.XintOrFloat](list []T) []T -func IndexOf[T ~string | comdef.XintOrFloat](val T, list []T) int +func Unique[T comdef.NumberOrString](list []T) []T +func IndexOf[T comdef.NumberOrString](val T, list []T) int +func FirstOr[T any](list []T, defVal ...T) T // source at arrutil/strings.go func StringsToAnys(ss []string) []any func StringsToSlice(ss []string) []any @@ -212,6 +220,7 @@ ss, err := arrutil.ToStrings([]int{1, 2}) // ss: []string{"1", "2"} func NewBuffer() *Buffer // source at byteutil/byteutil.go func Md5(src any) []byte +func Md5Sum(src any) []byte func ShortMd5(src any) []byte func Random(length int) ([]byte, error) func FirstLine(bs []byte) []byte @@ -221,6 +230,7 @@ func SafeCut(bs []byte, sep byte) (before, after []byte) func SafeCuts(bs []byte, sep []byte) (before, after []byte) // source at byteutil/check.go func IsNumChar(c byte) bool +func IsAlphaChar(c byte) bool // source at byteutil/conv.go func StrOrErr(bs []byte, err error) (string, error) func SafeString(bs []byte, err error) string @@ -242,7 +252,7 @@ func NewChanPool(chSize int, width int, capWidth int) *ChanPool ```go // source at cflag/app.go func NewApp(fns ...func(app *App)) *App -func NewCmd(name, desc string) *Cmd +func NewCmd(name, desc string, runFunc ...func(c *Cmd) error) *Cmd // source at cflag/cflag.go func SetDebug(open bool) func New(fns ...func(c *CFlags)) *CFlags @@ -275,7 +285,7 @@ func ReplaceShorts(args []string, shortsMap map[string]string) []string `cflag` usage please see [cflag/README.md](cflag/README.md) -### CLI/Console +### CLI Utils > Package `github.com/gookit/goutil/cliutil` @@ -371,7 +381,7 @@ Build line: ./myapp -a val0 -m "this is message" arg0 > More, please see [./cliutil/README](cliutil/README.md) -### Dumper +### Var Dumper > Package `github.com/gookit/goutil/dump` @@ -446,20 +456,6 @@ Preview: ![](dump/_examples/preview-nested-struct.png) -### Encodes - -> Package `github.com/gookit/goutil/encodes` - -```go -// source at encodes/encodes.go -func B32Encode(str string) string -func B32Decode(str string) string -func B64Encode(str string) string -func B64EncodeBytes(src []byte) []byte -func B64Decode(str string) string -func B64DecodeBytes(str []byte) []byte -``` - ### ENV/Environment > Package `github.com/gookit/goutil/envutil` @@ -471,14 +467,16 @@ func ParseOrErr(val string) (string, error) func ParseValue(val string) string func VarParse(val string) string func ParseEnvValue(val string) string -func SetEnvMap(mp map[string]string) -func SetEnvs(kvPairs ...string) -func UnsetEnvs(keys ...string) +func SplitText2map(text string) map[string]string +func SplitLineToKv(line string) (string, string) // source at envutil/get.go func Getenv(name string, def ...string) string +func MustGet(name string) string func GetInt(name string, def ...int) int func GetBool(name string, def ...bool) bool +func GetOne(names []string, defVal ...string) string func GetMulti(names ...string) map[string]string +func OnExist(name string, fn func(val string)) bool func EnvPaths() []string func EnvMap() map[string]string func Environ() map[string]string @@ -490,7 +488,6 @@ func IsWindows() bool func IsMac() bool func IsLinux() bool func IsMSys() bool -func IsWSL() bool func IsTerminal(fd uintptr) bool func StdIsTerminal() bool func IsConsole(out io.Writer) bool @@ -499,6 +496,12 @@ func IsSupportColor() bool func IsSupport256Color() bool func IsSupportTrueColor() bool func IsGithubActions() bool +// source at envutil/set.go +func SetEnvMap(mp map[string]string) +func SetEnvs(kvPairs ...string) +func UnsetEnvs(keys ...string) +func LoadText(text string) +func LoadString(line string) bool ``` #### ENV Util Usage @@ -562,7 +565,8 @@ func Err(msg string) error func Raw(msg string) error func Ef(tpl string, vars ...any) error func Errf(tpl string, vars ...any) error -func Rawf(tpl string, vars ...any) error +func Rf(tpl string, vs ...any) error +func Rawf(tpl string, vs ...any) error func Cause(err error) error func Unwrap(err error) error func Previous(err error) error @@ -651,24 +655,6 @@ runtime.goexit() ``` -### Format Utils - -> Package `github.com/gookit/goutil/fmtutil` - -```go -// source at fmtutil/fmtutil.go -func StringOrJSON(v any) ([]byte, error) -// source at fmtutil/format.go -func DataSize(size uint64) string -func SizeToString(size uint64) string -func StringToByte(sizeStr string) uint64 -func ParseByte(sizeStr string) uint64 -func PrettyJSON(v any) (string, error) -func ArgsWithSpaces(vs []any) (message string) -// source at fmtutil/time.go -func HowLongAgo(sec int64) string -``` - ### File System > Package `github.com/gookit/goutil/fsutil` @@ -688,7 +674,7 @@ func PathMatch(pattern, s string) bool func NewEntry(fPath string, ent fs.DirEntry) Entry func NewFileInfo(fPath string, info fs.FileInfo) FileInfo // source at fsutil/find.go -func FilePathInDirs(file string, dirs ...string) string +func FilePathInDirs(fPath string, dirs ...string) string func FirstExists(paths ...string) string func FirstExistsDir(paths ...string) string func FirstExistsFile(paths ...string) string @@ -707,20 +693,25 @@ func ExcludeDotFile(_ string, ent fs.DirEntry) bool func ExcludeSuffix(ss ...string) FilterFunc func ApplyFilters(fPath string, ent fs.DirEntry, filters []FilterFunc) bool func FindInDir(dir string, handleFn HandleFunc, filters ...FilterFunc) (e error) +func FileInDirs(paths []string, names ...string) string // source at fsutil/fsutil.go func JoinPaths(elem ...string) string -func JoinSubPaths(basePath string, elem ...string) string +func JoinPaths3(basePath, secPath string, elems ...string) string +func JoinSubPaths(basePath string, elems ...string) string func SlashPath(path string) string func UnixPath(path string) string func ToAbsPath(p string) string +func Must2(_ any, err error) // source at fsutil/info.go -func DirPath(fpath string) string -func Dir(fpath string) string -func PathName(fpath string) string -func Name(fpath string) string -func FileExt(fpath string) string -func Extname(fpath string) string -func Suffix(fpath string) string +func DirPath(fPath string) string +func Dir(fPath string) string +func PathName(fPath string) string +func PathNoExt(fPath string) string +func Name(fPath string) string +func NameNoExt(fPath string) string +func FileExt(fPath string) string +func Extname(fPath string) string +func Suffix(fPath string) string func Expand(pathStr string) string func ExpandPath(pathStr string) string func ResolvePath(pathStr string) string @@ -781,6 +772,7 @@ func OSTempDir(pattern string) (string, error) func TempDir(dir, pattern string) (string, error) func MustSave(filePath string, data any, optFns ...OpenOptionFunc) func SaveFile(filePath string, data any, optFns ...OpenOptionFunc) error +func WriteData(filePath string, data any, fileFlag ...int) (int, error) func PutContents(filePath string, data any, fileFlag ...int) (int, error) func WriteFile(filePath string, data any, perm os.FileMode, fileFlag ...int) error func WriteOSFile(f *os.File, data any) (n int, err error) @@ -819,27 +811,6 @@ func main() { ``` -### Go Info - -> Package `github.com/gookit/goutil/goinfo` - -```go -// source at goinfo/gofunc.go -func FuncName(fn any) string -func CutFuncName(fullFcName string) (pkgPath, shortFnName string) -func PkgName(fullFcName string) string -func GoodFuncName(name string) bool -// source at goinfo/goinfo.go -func GoVersion() string -func ParseGoVersion(line string) (*GoInfo, error) -func OsGoInfo() (*GoInfo, error) -// source at goinfo/stack.go -func GetCallStacks(all bool) []byte -func GetCallerInfo(skip int) string -func SimpleCallersInfo(skip, num int) []string -func GetCallersInfo(skip, max int) []string -``` - ### JSON Utils > Package `github.com/gookit/goutil/jsonutil` @@ -855,6 +826,7 @@ func EncodeUnescapeHTML(v any) ([]byte, error) func Decode(bts []byte, ptr any) error func DecodeString(str string, ptr any) error func DecodeReader(r io.Reader, ptr any) error +func DecodeFile(file string, ptr any) error // source at jsonutil/jsonutil.go func WriteFile(filePath string, data any) error func WritePretty(filePath string, data any) error @@ -880,9 +852,14 @@ func HasOneKey(mp any, keys ...any) (ok bool, key any) func HasAllKeys(mp any, keys ...any) (ok bool, noKey any) // source at maputil/convert.go func KeyToLower(src map[string]string) map[string]string +func AnyToStrMap(src any) map[string]string func ToStringMap(src map[string]any) map[string]string +func ToL2StringMap(groupsMap map[string]any) map[string]map[string]string func CombineToSMap(keys, values []string) SMap func CombineToMap[K comdef.SortedType, V any](keys []K, values []V) map[K]V +func SliceToSMap(kvPairs ...string) map[string]string +func SliceToMap(kvPairs ...any) map[string]any +func SliceToTypeMap[T any](valFunc func(any) T, kvPairs ...any) map[string]T func ToAnyMap(mp any) map[string]any func TryAnyMap(mp any) (map[string]any, error) func HTTPQueryString(data map[string]any) string @@ -901,14 +878,21 @@ func GetFromAny(path string, data any) (val any, ok bool) func GetByPath(path string, mp map[string]any) (val any, ok bool) func GetByPathKeys(mp map[string]any, keys []string) (val any, ok bool) func Keys(mp any) (keys []string) +func TypedKeys[K comdef.SimpleType, V any](mp map[K]V) (keys []K) +func FirstKey[T any](mp map[string]T) string func Values(mp any) (values []any) +func TypedValues[K comdef.SimpleType, V any](mp map[K]V) (values []V) func EachAnyMap(mp any, fn func(key string, val any)) +func EachTypedMap[K comdef.SimpleType, V any](mp map[K]V, fn func(key K, val V)) // source at maputil/maputil.go func SimpleMerge(src, dst map[string]any) map[string]any +func Merge1level(mps ...map[string]any) map[string]any func DeepMerge(src, dst map[string]any, deep int) map[string]any func MergeSMap(src, dst map[string]string, ignoreCase bool) map[string]string +func MergeStrMap(src, dst map[string]string) map[string]string func MergeStringMap(src, dst map[string]string, ignoreCase bool) map[string]string func MergeMultiSMap(mps ...map[string]string) map[string]string +func MergeL2StrMap(mps ...map[string]map[string]string) map[string]map[string]string func FilterSMap(sm map[string]string) map[string]string func MakeByPath(path string, val any) (mp map[string]any) func MakeByKeys(keys []string, val any) (mp map[string]any) @@ -922,21 +906,23 @@ func SetByKeys(mp *map[string]any, keys []string, val any) (err error) > Package `github.com/gookit/goutil/mathutil` ```go +// source at mathutil/calc.go +func Abs[T comdef.Int](val T) T // source at mathutil/check.go func IsNumeric(c byte) bool func Compare(first, second any, op string) bool func CompInt[T comdef.Xint](first, second T, op string) (ok bool) func CompInt64(first, second int64, op string) bool func CompFloat[T comdef.Float](first, second T, op string) (ok bool) -func CompValue[T comdef.XintOrFloat](first, second T, op string) (ok bool) -func InRange[T comdef.IntOrFloat](val, min, max T) bool -func OutRange[T comdef.IntOrFloat](val, min, max T) bool +func CompValue[T comdef.Number](first, second T, op string) (ok bool) +func InRange[T comdef.Number](val, min, max T) bool +func OutRange[T comdef.Number](val, min, max T) bool func InUintRange[T comdef.Uint](val, min, max T) bool // source at mathutil/compare.go -func Min[T comdef.XintOrFloat](x, y T) T -func Max[T comdef.XintOrFloat](x, y T) T -func SwapMin[T comdef.XintOrFloat](x, y T) (T, T) -func SwapMax[T comdef.XintOrFloat](x, y T) (T, T) +func Min[T comdef.Number](x, y T) T +func Max[T comdef.Number](x, y T) T +func SwapMin[T comdef.Number](x, y T) (T, T) +func SwapMax[T comdef.Number](x, y T) (T, T) func MaxInt(x, y int) int func SwapMaxInt(x, y int) (int, int) func MaxI64(x, y int64) int64 @@ -1011,15 +997,15 @@ func ToStringWith(in any, optFns ...comfunc.ConvOptionFn) (string, error) func DataSize(size uint64) string func HowLongAgo(sec int64) string // source at mathutil/mathutil.go -func OrElse[T comdef.XintOrFloat](val, defVal T) T -func ZeroOr[T comdef.XintOrFloat](val, defVal T) T -func LessOr[T comdef.XintOrFloat](val, max, devVal T) T -func LteOr[T comdef.XintOrFloat](val, max, devVal T) T -func GreaterOr[T comdef.XintOrFloat](val, min, defVal T) T -func GteOr[T comdef.XintOrFloat](val, min, defVal T) T -func Mul[T1, T2 comdef.XintOrFloat](a T1, b T2) float64 +func OrElse[T comdef.Number](val, defVal T) T +func ZeroOr[T comdef.Number](val, defVal T) T +func LessOr[T comdef.Number](val, max, devVal T) T +func LteOr[T comdef.Number](val, max, devVal T) T +func GreaterOr[T comdef.Number](val, min, defVal T) T +func GteOr[T comdef.Number](val, min, defVal T) T +func Mul[T1, T2 comdef.Number](a T1, b T2) float64 func MulF2i(a, b float64) int -func Div[T1, T2 comdef.XintOrFloat](a T1, b T2) float64 +func Div[T1, T2 comdef.Number](a T1, b T2) float64 func DivInt[T comdef.Integer](a, b T) int func DivF2i(a, b float64) int func Percent(val, total int) float64 @@ -1028,8 +1014,6 @@ func RandomInt(min, max int) int func RandInt(min, max int) int func RandIntWithSeed(min, max int, seed int64) int func RandomIntWithSeed(min, max int, seed int64) int -// source at mathutil/value.go -func New[T comdef.IntOrFloat](v T) *Num[T] ``` ### Reflects @@ -1038,6 +1022,8 @@ func New[T comdef.IntOrFloat](v T) *Num[T] ```go // source at reflects/check.go +func IsTimeType(t reflect.Type) bool +func IsDurationType(t reflect.Type) bool func HasChild(v reflect.Value) bool func IsArrayOrSlice(k reflect.Kind) bool func IsSimpleKind(k reflect.Kind) bool @@ -1046,6 +1032,7 @@ func IsIntLike(k reflect.Kind) bool func IsIntx(k reflect.Kind) bool func IsUintX(k reflect.Kind) bool func IsNil(v reflect.Value) bool +func IsValidPtr(v reflect.Value) bool func CanBeNil(typ reflect.Type) bool func IsFunc(val any) bool func IsEqual(src, dst any) bool @@ -1056,12 +1043,13 @@ func BaseTypeVal(v reflect.Value) (value any, err error) func ToBaseVal(v reflect.Value) (value any, err error) func ConvToType(val any, typ reflect.Type) (rv reflect.Value, err error) func ValueByType(val any, typ reflect.Type) (rv reflect.Value, err error) -func ValueByKind(val any, kind reflect.Kind) (rv reflect.Value, err error) -func ConvToKind(val any, kind reflect.Kind) (rv reflect.Value, err error) +func ValueByKind(val any, kind reflect.Kind) (reflect.Value, error) +func ConvToKind(val any, kind reflect.Kind, fallback ...ConvFunc) (rv reflect.Value, err error) func ConvSlice(oldSlRv reflect.Value, newElemTyp reflect.Type) (rv reflect.Value, err error) func String(rv reflect.Value) string func ToString(rv reflect.Value) (str string, err error) func ValToString(rv reflect.Value, defaultAsErr bool) (str string, err error) +func ToTimeOrDuration(str string, typ reflect.Type) (any, error) // source at reflects/func.go func NewFunc(fn any) *FuncX func Call2(fn reflect.Value, args []reflect.Value) (reflect.Value, error) @@ -1069,8 +1057,9 @@ func Call(fn reflect.Value, args []reflect.Value, opt *CallOpt) ([]reflect.Value func SafeCall2(fun reflect.Value, args []reflect.Value) (val reflect.Value, err error) func SafeCall(fun reflect.Value, args []reflect.Value) (ret []reflect.Value, err error) // source at reflects/map.go -func EachMap(mp reflect.Value, fn func(key, val reflect.Value)) -func EachStrAnyMap(mp reflect.Value, fn func(key string, val any)) +func TryAnyMap(mp reflect.Value) (map[string]any, error) +func EachMap(mp reflect.Value, fn func(key, val reflect.Value)) (err error) +func EachStrAnyMap(mp reflect.Value, fn func(key string, val any)) error func FlatMap(rv reflect.Value, fn FlatFunc) // source at reflects/slice.go func MakeSliceByElem(elTyp reflect.Type, len, cap int) reflect.Value @@ -1097,32 +1086,6 @@ func Wrap(rv reflect.Value) Value func ValueOf(v any) Value ``` -### Stdio - -> Package `github.com/gookit/goutil/stdio` - -```go -// source at stdio/ioutil.go -func QuietFprint(w io.Writer, a ...any) -func QuietFprintf(w io.Writer, tpl string, vs ...any) -func QuietFprintln(w io.Writer, a ...any) -func QuietWriteString(w io.Writer, ss ...string) -// source at stdio/stdio.go -func DiscardReader(src io.Reader) -func ReadString(r io.Reader) string -func MustReadReader(r io.Reader) []byte -func NewIOReader(in any) io.Reader -func NewScanner(in any) *bufio.Scanner -func WriteByte(b byte) -func WriteBytes(bs []byte) -func WritelnBytes(bs []byte) -func WriteString(s string) -func Writeln(s string) -// source at stdio/writer.go -func WrapW(w io.Writer) *WriteWrapper -func NewWriteWrapper(w io.Writer) *WriteWrapper -``` - ### Structs > Package `github.com/gookit/goutil/structs` @@ -1139,8 +1102,10 @@ func TryToSMap(st any, optFns ...MapOptFunc) (map[string]string, error) func MustToSMap(st any, optFns ...MapOptFunc) map[string]string func ToString(st any, optFns ...MapOptFunc) string func WithMapTagName(tagName string) MapOptFunc +func WithUserFunc(fn CustomUserFunc) MapOptFunc func MergeAnonymous(opt *MapOptions) func ExportPrivate(opt *MapOptions) +func WithIgnoreEmpty(opt *MapOptions) func StructToMap(st any, optFns ...MapOptFunc) (map[string]any, error) // source at structs/copy.go func MapStruct(srcSt, dstSt any) @@ -1171,6 +1136,8 @@ func WrapValue(rv reflect.Value) *Wrapper // source at structs/writer.go func NewWriter(ptr any) *Wrapper func WithParseDefault(opt *SetOptions) +func WithBeforeSetFn(fn BeforeSetFunc) SetOptFunc +func BindData(ptr any, data map[string]any, optFns ...SetOptFunc) error func SetValues(ptr any, data map[string]any, optFns ...SetOptFunc) error ``` @@ -1180,10 +1147,12 @@ func SetValues(ptr any, data map[string]any, optFns ...SetOptFunc) error ```go // source at strutil/bytes.go -func NewBuffer() *Buffer +func NewBuffer(initSize ...int) *Buffer func NewByteChanPool(maxSize, width, capWidth int) *ByteChanPool // source at strutil/check.go func IsNumChar(c byte) bool +func IsInt(s string) bool +func IsFloat(s string) bool func IsNumeric(s string) bool func IsAlphabet(char uint8) bool func IsAlphaNum(c uint8) bool @@ -1195,8 +1164,10 @@ func IContains(s, sub string) bool func ContainsByte(s string, c byte) bool func ContainsOne(s string, subs []string) bool func HasOneSub(s string, subs []string) bool +func IContainsOne(s string, subs []string) bool func ContainsAll(s string, subs []string) bool func HasAllSubs(s string, subs []string) bool +func IContainsAll(s string, subs []string) bool func IsStartsOf(s string, prefixes []string) bool func HasOnePrefix(s string, prefixes []string) bool func HasPrefix(s string, prefix string) bool @@ -1214,6 +1185,7 @@ func IsSymbol(r rune) bool func HasEmpty(ss ...string) bool func IsAllEmpty(ss ...string) bool func IsVersion(s string) bool +func IsVarName(s string) bool func Compare(s1, s2, op string) bool func VersionCompare(v1, v2, op string) bool func SimpleMatch(s string, keywords []string) bool @@ -1231,6 +1203,7 @@ func Quote(s string) string func Unquote(s string) string func Join(sep string, ss ...string) string func JoinList(sep string, ss []string) string +func JoinComma(ss []string) string func JoinAny(sep string, parts ...any) string func Implode(sep string, ss ...string) string func String(val any) (string, error) @@ -1330,6 +1303,7 @@ func IndentBytes(b, prefix []byte) []byte func MicroTimeID() string func MicroTimeHexID() string func MTimeHexID() string +func MTimeBase36() string func MTimeBaseID(toBase int) string func DatetimeNo(prefix string) string func DateSN(prefix string) string @@ -1338,6 +1312,8 @@ func DateSNV2(prefix string, extBase ...int) string func Md5(src any) string func MD5(src any) string func GenMd5(src any) string +func Md5Simple(src any) string +func Md5Base62(src any) string func Md5Bytes(src any) []byte func ShortMd5(src any) string func HashPasswd(pwd, key string) string @@ -1387,6 +1363,7 @@ func RunesWidth(rs []rune) (w int) func Truncate(s string, w int, tail string) string func TextTruncate(s string, w int, tail string) string func Utf8Truncate(s string, w int, tail string) string +func Chunk[T ~string](s T, size int) []T func TextSplit(s string, w int) []string func Utf8Split(s string, w int) []string func TextWrap(s string, w int) string @@ -1408,6 +1385,7 @@ func SplitNValid(s, sep string, n int) (ss []string) func SplitN(s, sep string, n int) (ss []string) func SplitTrimmed(s, sep string) (ss []string) func SplitNTrimmed(s, sep string, n int) (ss []string) +func SplitByWhitespace(s string) []string func Substr(s string, pos, length int) string func SplitInlineComment(val string, strict ...bool) (string, string) func FirstLine(output string) string @@ -1432,12 +1410,12 @@ func SubstrCount(s, substr string, params ...uint64) (int, error) ```go // source at syncs/chan.go -func WaitCloseSignals(onClose func(sig os.Signal)) func Go(f func() error) error // source at syncs/group.go func NewCtxErrGroup(ctx context.Context, limit ...int) (*ErrGroup, context.Context) func NewErrGroup(limit ...int) *ErrGroup // source at syncs/signal.go +func WaitCloseSignals(onClose func(sig os.Signal), sigCh ...chan os.Signal) func SignalHandler(ctx context.Context, signals ...os.Signal) (execute func() error, interrupt func(error)) ``` @@ -1453,15 +1431,14 @@ func QuickExec(cmdLine string, workDir ...string) (string, error) func ExecLine(cmdLine string, workDir ...string) (string, error) func ExecCmd(binName string, args []string, workDir ...string) (string, error) func ShellExec(cmdLine string, shells ...string) (string, error) -// source at sysutil/stack.go -func CallersInfos(skip, num int, filters ...func(file string, fc *runtime.Func) bool) []*CallerInfo // source at sysutil/sysenv.go func IsMSys() bool +func IsWSL() bool func IsConsole(out io.Writer) bool func IsTerminal(fd uintptr) bool func StdIsTerminal() bool func Hostname() string -func CurrentShell(onlyName bool) (path string) +func CurrentShell(onlyName bool, fallbackShell ...string) string func HasShellEnv(shell string) bool func IsShellSpecialVar(c uint8) bool func FindExecutable(binName string) (string, error) @@ -1471,11 +1448,13 @@ func Getenv(name string, def ...string) string func Environ() map[string]string func EnvMapWith(newEnv map[string]string) map[string]string func EnvPaths() []string -func SearchPath(keywords string, limit int) []string +func ToEnvPATH(paths []string) string +func SearchPath(keywords string, limit int, opts ...SearchPathOption) []string // source at sysutil/sysgo.go func GoVersion() string func ParseGoVersion(line string) (*GoInfo, error) func OsGoInfo() (*GoInfo, error) +func CallersInfos(skip, num int, filters ...goinfo.CallerFilterFunc) []*CallerInfo // source at sysutil/sysutil.go func Workdir() string func BinDir() string @@ -1484,16 +1463,16 @@ func BinFile() string func Open(fileOrURL string) error func OpenBrowser(fileOrURL string) error func OpenFile(path string) error -// source at sysutil/sysutil_nonwin.go -func Kill(pid int, signal syscall.Signal) error -func ProcessExists(pid int) bool -// source at sysutil/sysutil_unix.go +// source at sysutil/sysutil_linux.go func IsWin() bool func IsWindows() bool func IsMac() bool func IsDarwin() bool func IsLinux() bool func OpenURL(URL string) error +// source at sysutil/sysutil_nonwin.go +func Kill(pid int, signal syscall.Signal) error +func ProcessExists(pid int) bool // source at sysutil/user.go func MustFindUser(uname string) *user.User func LoginUser() *user.User @@ -1501,12 +1480,13 @@ func CurrentUser() *user.User func UHomeDir() string func UserHomeDir() string func HomeDir() string -func UserDir(subPath string) string -func UserCacheDir(subPath string) string -func UserConfigDir(subPath string) string +func UserDir(subPaths ...string) string +func UserCacheDir(subPaths ...string) string +func UserConfigDir(subPaths ...string) string func ExpandPath(path string) string func ExpandHome(path string) string // source at sysutil/user_nonwin.go +func IsAdmin() bool func ChangeUserByName(newUname string) error func ChangeUserUidGid(newUID int, newGid int) error func ChangeUserUIDGid(newUID int, newGid int) (err error) @@ -1524,15 +1504,18 @@ func MockEnvValue(key, val string, fn func(nv string)) func MockEnvValues(kvMap map[string]string, fn func()) func MockOsEnvByText(envText string, fn func()) func MockOsEnv(mp map[string]string, fn func()) +func SetOsEnvs(mp map[string]string) string +func RemoveTmpEnvs(tmpKey string) func ClearOSEnv() func RevertOSEnv() +func RunOnCleanEnv(runFn func()) func MockCleanOsEnv(mp map[string]string, fn func()) // source at testutil/httpmock.go -func NewHttpRequest(method, path string, data *MD) *http.Request func NewHTTPRequest(method, path string, data *MD) *http.Request func MockRequest(h http.Handler, method, path string, data *MD) *httptest.ResponseRecorder +func MockHttpServer() *EchoServer func TestMain(m *testing.M) -func NewEchoServer() *httptest.Server +func NewEchoServer() *EchoServer func BuildEchoReply(r *http.Request) *EchoReply func ParseRespToReply(w *http.Response) *EchoReply func ParseBodyToReply(bd io.ReadCloser) *EchoReply @@ -1548,7 +1531,7 @@ func SetTimeLocalUTC() func RestoreTimeLocal() // source at testutil/writer.go func NewTestWriter() *TestWriter -func NewDirEnt(fpath string, isDir ...bool) *fakeobj.DirEntry +func NewDirEnt(fPath string, isDir ...bool) *fakeobj.DirEntry ``` ### Timex @@ -1752,17 +1735,14 @@ Testing in docker: ```shell cd goutil -docker run -ti -v $(pwd):/go/work golang:1.18 -root@xx:/go/work# go test ./... + +docker run -ti -v $(pwd):/go/goutil -e GOPROXY=https://goproxy.cn,direct golang:1.23 +# on Windows +docker run -ti -v "${PWD}:/go/goutil" -e GOPROXY=https://goproxy.cn,direct golang:1.23 + +root@xx:/go/goutil# go test ./... ``` -## Related - -- https://github.com/duke-git/lancet -- https://github.com/samber/lo -- https://github.com/zyedidia/generic -- https://github.com/thoas/go-funk - ## Gookit packages - [gookit/ini](https://github.com/gookit/ini) Go config management, use INI files @@ -1778,6 +1758,13 @@ root@xx:/go/work# go test ./... - [gookit/goutil](https://github.com/gookit/goutil) Some utils for the Go: string, array/slice, map, format, cli, env, filesystem, test and more - More, please see https://github.com/gookit +## Related + +- https://github.com/duke-git/lancet +- https://github.com/samber/lo +- https://github.com/zyedidia/generic +- https://github.com/thoas/go-funk + ## License [MIT](LICENSE) diff --git a/vendor/github.com/gookit/goutil/README.zh-CN.md b/vendor/github.com/gookit/goutil/README.zh-CN.md index 9cffe7199..ee6d10db0 100644 --- a/vendor/github.com/gookit/goutil/README.zh-CN.md +++ b/vendor/github.com/gookit/goutil/README.zh-CN.md @@ -7,55 +7,63 @@ [![Coverage Status](https://coveralls.io/repos/github/gookit/goutil/badge.svg?branch=master)](https://coveralls.io/github/gookit/goutil?branch=master) [![Go Reference](https://pkg.go.dev/badge/github.com/gookit/goutil.svg)](https://pkg.go.dev/github.com/gookit/goutil) -`goutil` Go 常用功能的扩展工具库(**700+**)。包含:数字,byte, 字符串,slice/数组,Map,结构体,反射,文本,文件,错误,时间日期,测试,特殊处理,格式化,常用信息获取等等。 +`goutil` Go 常用功能的扩展工具库(**800+**)。包含:数字,byte, 字符串,slice/数组,Map,结构体,反射,文本,文件,错误,时间日期,测试,特殊处理,格式化,常用信息获取等等。 > **[EN README](README.md)** -**基础工具包** +## 工具包说明 + +### 基础工具包 - [`arrutil`](arrutil) array/slice 相关操作的函数工具包 如:类型转换,元素检查等等 -- [`cliutil`](cliutil) CLI 的一些工具函数包. eg: read input, exec command - - [cmdline](cliutil/cmdline) 提供 cmdline 解析,args 构建到 cmdline -- [`envutil`](envutil) ENV 信息获取判断工具包. eg: get one, get info, parse var -- [`fmtutil`](fmtutil) 格式化数据工具函数 eg:数据size -- [`fsutil`](fsutil) 文件系统操作相关的工具函数包. eg: file and dir check, operate -- [`goinfo`](goinfo) 提供一些与Go info, runtime 相关的工具函数。 -- [`jsonutil`](jsonutil) 一些用于快速读取、写入、编码、解码 JSON 数据的实用函数。 +- [`byteutil`](byteutil): 提供一些常用的 byte 操作函数工具包. eg: convert, check and more - [`maputil`](maputil) map 相关操作的函数工具包. eg: convert, sub-value get, simple merge - [`mathutil`](mathutil) int/number 相关操作的函数工具包. eg: convert, math calc, random -- [`netutil`](netutil) Network util functions. eg: Ip, IpV4, IpV6, Mac, Port, Hostname, etc. - [`reflects`](reflects) 提供一些扩展性的反射使用工具函数. - [`structs`](structs) 为 struct 提供一些扩展 util 函数。 eg: tag parse, struct data - [`strutil`](strutil) string 相关操作的函数工具包. eg: bytes, check, convert, encode, format and more +- [`cliutil`](cliutil) CLI 的一些工具函数包. eg: read input, exec command +- [`envutil`](envutil) ENV 信息获取判断工具包. eg: get one, get info, parse var +- [`fsutil`](fsutil) 文件系统操作相关的工具函数包. eg: file and dir check, operate +- [`jsonutil`](jsonutil) 一些用于快速读取、写入、编码、解码 JSON 数据的实用函数。 - [`sysutil`](sysutil) system 相关操作的函数工具包. eg: sysenv, exec, user, process -**扩展工具包** +### Debug & Test & Errors -- [`cflag`](./cflag): 包装和扩展 go `flag.FlagSet` 以方便快速的构建简单的命令行应用程序 - [`dump`](./dump) GO变量打印工具,打印 slice, map 会自动换行显示每个元素,同时会显示打印调用位置 -- [`encodes`](encodes): Provide some encoding/decoding, hash, crypto util functions. eg: base64, hex, etc. - [`errorx`](./errorx) 为 go 提供增强的错误实现,允许带有堆栈跟踪信息和包装另一个错误。 -- [`finder`](./fsutil/finder) 提供简单方便的file/dir查找功能,支持过滤、排除、匹配、忽略等。 -- netutil 子包: - - `netutil/httpreq` 包装 http.Client 实现的更加易于使用的HTTP客户端, 和一些 http 工具函数 -- strutil 子包: - - [textscan](strutil/textscan) 实现了一个快速扫描和分析文本内容的解析器. 可用于解析 INI, Properties 等格式内容 - - [textutil](strutil/textutil) 提供一些常用的扩展文本处理功能函数。 -- [syncs](syncs) 提供一些同步、协程、信号相关的工具函数. -- sysutil 子包: - - [clipboard](sysutil/clipboard) 提供简单的剪贴板读写操作工具库 - - [cmdr](sysutil/cmdr) 提供快速构建和运行一个cmd,批量运行多个cmd任务 - - [process](sysutil/process) 提供一些进程操作相关的实用功能。 - [`testutil`](testutil) test help 相关操作的函数工具包. eg: http test, mock ENV value - - [assert](testutil/assert) 用于帮助测试的断言函数工具包,方便编写单元测试。 - - [fakeobj](testutil/fakeobj) 提供一些接口的假的实现,用于模拟测试. 例如 fs.File, fs.FileInfo, fs.DirEntry 等等. +- [`assert`](testutil/assert) 用于帮助测试的断言函数工具包,方便编写单元测试。 +- [`fakeobj`](x/fakeobj) 提供一些接口的MOCK的实现,用于模拟测试. 例如 fs.File, fs.FileInfo, fs.DirEntry 等等. + +### 扩展工具包 + +- [`cflag`](cflag): 包装和扩展 go `flag.FlagSet` 以方便快速的构建简单的命令行应用程序 +- [`ccolor`](x/ccolor): 简单的命令行颜色输出库,它使用 ANSI 颜色代码来输出带有颜色的文本。 +- [`syncs`](syncs) 提供一些同步、协程、信号相关的工具函数. +- [`httpreq`](netutil/httpreq) 包装 http.Client 实现的更加易于使用的HTTP客户端, 和一些 http 工具函数 +- [`clipboard`](sysutil/clipboard) 提供简单的OS剪贴板读写操作工具库 - [`timex`](timex) 提供增强的 time.Time 实现。添加更多常用的功能方法 - 提供类似 `Y-m-d H:i:s` 的日期时间格式解析处理 - 常用时间方法。例如: DayStart(), DayAfter(), DayAgo(), DateFormat() 等等 +**更多 ...** + +- [`netutil`](netutil) Network util functions. eg: Ip, IpV4, IpV6, Mac, Port, Hostname, etc. +- [`cmdline`](cliutil/cmdline) 提供 cmdline 解析,args 构建到 cmdline +- [`encodes`](x/encodes): Provide some encoding/decoding, hash, crypto util functions. eg: base64, hex, etc. +- [`finder`](x/finder) 提供简单方便的file/dir查找功能,支持过滤、排除、匹配、忽略等。 +- [textscan](strutil/textscan) 实现了一个快速扫描和分析文本内容的解析器. 可用于解析 INI, Properties 等格式内容 +- [textutil](strutil/textutil) 提供一些常用的扩展文本处理功能函数。 +- [cmdr](sysutil/cmdr) 提供快速构建和运行一个cmd,批量运行多个cmd任务 +- [process](sysutil/process) 提供一些进程操作相关的实用功能。 +- [`fmtutil`](x/fmtutil) 格式化数据工具函数 eg:数据size +- [`goinfo`](x/goinfo) 提供一些与Go info, runtime 相关的工具函数。 + ## GoDoc - [Godoc for github](https://pkg.go.dev/github.com/gookit/goutil) +- Wiki docs on [DeepWiki - gookit/goutil](https://deepwiki.com/gookit/goutil) ## 获取 @@ -95,7 +103,7 @@ dump.Print(somevar, somevar2, ...) ![preview-nested-struct](dump/_examples/preview-nested-struct.png) -## Packages +## Packages Details ### Array and Slice @@ -165,8 +173,9 @@ func Remove[T comdef.Compared](ls []T, val T) []T func Filter[T any](ls []T, filter ...comdef.MatchFunc[T]) []T func Map[T any, V any](list []T, mapFn MapFn[T, V]) []V func Column[T any, V any](list []T, mapFn func(obj T) (val V, find bool)) []V -func Unique[T ~string | comdef.XintOrFloat](list []T) []T -func IndexOf[T ~string | comdef.XintOrFloat](val T, list []T) int +func Unique[T comdef.NumberOrString](list []T) []T +func IndexOf[T comdef.NumberOrString](val T, list []T) int +func FirstOr[T any](list []T, defVal ...T) T // source at arrutil/strings.go func StringsToAnys(ss []string) []any func StringsToSlice(ss []string) []any @@ -211,6 +220,7 @@ ss, err := arrutil.ToStrings([]int{1, 2}) // ss: []string{"1", "2"} func NewBuffer() *Buffer // source at byteutil/byteutil.go func Md5(src any) []byte +func Md5Sum(src any) []byte func ShortMd5(src any) []byte func Random(length int) ([]byte, error) func FirstLine(bs []byte) []byte @@ -220,6 +230,7 @@ func SafeCut(bs []byte, sep byte) (before, after []byte) func SafeCuts(bs []byte, sep []byte) (before, after []byte) // source at byteutil/check.go func IsNumChar(c byte) bool +func IsAlphaChar(c byte) bool // source at byteutil/conv.go func StrOrErr(bs []byte, err error) (string, error) func SafeString(bs []byte, err error) string @@ -241,7 +252,7 @@ func NewChanPool(chSize int, width int, capWidth int) *ChanPool ```go // source at cflag/app.go func NewApp(fns ...func(app *App)) *App -func NewCmd(name, desc string) *Cmd +func NewCmd(name, desc string, runFunc ...func(c *Cmd) error) *Cmd // source at cflag/cflag.go func SetDebug(open bool) func New(fns ...func(c *CFlags)) *CFlags @@ -274,7 +285,7 @@ func ReplaceShorts(args []string, shortsMap map[string]string) []string `cflag` 使用说明请看 [cflag/README.zh-CN.md](cflag/README.zh-CN.md) -### CLI/Console +### CLI Utils > Package `github.com/gookit/goutil/cliutil` @@ -370,7 +381,7 @@ Build line: ./myapp -a val0 -m "this is message" arg0 > More, please see [./cliutil/README](cliutil/README.md) -### Dumper +### Var Dumper > Package `github.com/gookit/goutil/dump` @@ -445,20 +456,6 @@ Preview: ![](dump/_examples/preview-nested-struct.png) -### Encodes - -> Package `github.com/gookit/goutil/encodes` - -```go -// source at encodes/encodes.go -func B32Encode(str string) string -func B32Decode(str string) string -func B64Encode(str string) string -func B64EncodeBytes(src []byte) []byte -func B64Decode(str string) string -func B64DecodeBytes(str []byte) []byte -``` - ### ENV/Environment > Package `github.com/gookit/goutil/envutil` @@ -470,14 +467,16 @@ func ParseOrErr(val string) (string, error) func ParseValue(val string) string func VarParse(val string) string func ParseEnvValue(val string) string -func SetEnvMap(mp map[string]string) -func SetEnvs(kvPairs ...string) -func UnsetEnvs(keys ...string) +func SplitText2map(text string) map[string]string +func SplitLineToKv(line string) (string, string) // source at envutil/get.go func Getenv(name string, def ...string) string +func MustGet(name string) string func GetInt(name string, def ...int) int func GetBool(name string, def ...bool) bool +func GetOne(names []string, defVal ...string) string func GetMulti(names ...string) map[string]string +func OnExist(name string, fn func(val string)) bool func EnvPaths() []string func EnvMap() map[string]string func Environ() map[string]string @@ -489,7 +488,6 @@ func IsWindows() bool func IsMac() bool func IsLinux() bool func IsMSys() bool -func IsWSL() bool func IsTerminal(fd uintptr) bool func StdIsTerminal() bool func IsConsole(out io.Writer) bool @@ -498,6 +496,12 @@ func IsSupportColor() bool func IsSupport256Color() bool func IsSupportTrueColor() bool func IsGithubActions() bool +// source at envutil/set.go +func SetEnvMap(mp map[string]string) +func SetEnvs(kvPairs ...string) +func UnsetEnvs(keys ...string) +func LoadText(text string) +func LoadString(line string) bool ``` #### ENV Util Usage @@ -563,7 +567,8 @@ func Err(msg string) error func Raw(msg string) error func Ef(tpl string, vars ...any) error func Errf(tpl string, vars ...any) error -func Rawf(tpl string, vars ...any) error +func Rf(tpl string, vs ...any) error +func Rawf(tpl string, vs ...any) error func Cause(err error) error func Unwrap(err error) error func Previous(err error) error @@ -652,24 +657,6 @@ runtime.goexit() ``` -### Format Utils - -> Package `github.com/gookit/goutil/fmtutil` - -```go -// source at fmtutil/fmtutil.go -func StringOrJSON(v any) ([]byte, error) -// source at fmtutil/format.go -func DataSize(size uint64) string -func SizeToString(size uint64) string -func StringToByte(sizeStr string) uint64 -func ParseByte(sizeStr string) uint64 -func PrettyJSON(v any) (string, error) -func ArgsWithSpaces(vs []any) (message string) -// source at fmtutil/time.go -func HowLongAgo(sec int64) string -``` - ### File System > Package `github.com/gookit/goutil/fsutil` @@ -689,7 +676,7 @@ func PathMatch(pattern, s string) bool func NewEntry(fPath string, ent fs.DirEntry) Entry func NewFileInfo(fPath string, info fs.FileInfo) FileInfo // source at fsutil/find.go -func FilePathInDirs(file string, dirs ...string) string +func FilePathInDirs(fPath string, dirs ...string) string func FirstExists(paths ...string) string func FirstExistsDir(paths ...string) string func FirstExistsFile(paths ...string) string @@ -708,20 +695,25 @@ func ExcludeDotFile(_ string, ent fs.DirEntry) bool func ExcludeSuffix(ss ...string) FilterFunc func ApplyFilters(fPath string, ent fs.DirEntry, filters []FilterFunc) bool func FindInDir(dir string, handleFn HandleFunc, filters ...FilterFunc) (e error) +func FileInDirs(paths []string, names ...string) string // source at fsutil/fsutil.go func JoinPaths(elem ...string) string -func JoinSubPaths(basePath string, elem ...string) string +func JoinPaths3(basePath, secPath string, elems ...string) string +func JoinSubPaths(basePath string, elems ...string) string func SlashPath(path string) string func UnixPath(path string) string func ToAbsPath(p string) string +func Must2(_ any, err error) // source at fsutil/info.go -func DirPath(fpath string) string -func Dir(fpath string) string -func PathName(fpath string) string -func Name(fpath string) string -func FileExt(fpath string) string -func Extname(fpath string) string -func Suffix(fpath string) string +func DirPath(fPath string) string +func Dir(fPath string) string +func PathName(fPath string) string +func PathNoExt(fPath string) string +func Name(fPath string) string +func NameNoExt(fPath string) string +func FileExt(fPath string) string +func Extname(fPath string) string +func Suffix(fPath string) string func Expand(pathStr string) string func ExpandPath(pathStr string) string func ResolvePath(pathStr string) string @@ -782,6 +774,7 @@ func OSTempDir(pattern string) (string, error) func TempDir(dir, pattern string) (string, error) func MustSave(filePath string, data any, optFns ...OpenOptionFunc) func SaveFile(filePath string, data any, optFns ...OpenOptionFunc) error +func WriteData(filePath string, data any, fileFlag ...int) (int, error) func PutContents(filePath string, data any, fileFlag ...int) (int, error) func WriteFile(filePath string, data any, perm os.FileMode, fileFlag ...int) error func WriteOSFile(f *os.File, data any) (n int, err error) @@ -820,27 +813,6 @@ func main() { ``` -### Go Info - -> Package `github.com/gookit/goutil/goinfo` - -```go -// source at goinfo/gofunc.go -func FuncName(fn any) string -func CutFuncName(fullFcName string) (pkgPath, shortFnName string) -func PkgName(fullFcName string) string -func GoodFuncName(name string) bool -// source at goinfo/goinfo.go -func GoVersion() string -func ParseGoVersion(line string) (*GoInfo, error) -func OsGoInfo() (*GoInfo, error) -// source at goinfo/stack.go -func GetCallStacks(all bool) []byte -func GetCallerInfo(skip int) string -func SimpleCallersInfo(skip, num int) []string -func GetCallersInfo(skip, max int) []string -``` - ### JSON Utils > Package `github.com/gookit/goutil/jsonutil` @@ -856,6 +828,7 @@ func EncodeUnescapeHTML(v any) ([]byte, error) func Decode(bts []byte, ptr any) error func DecodeString(str string, ptr any) error func DecodeReader(r io.Reader, ptr any) error +func DecodeFile(file string, ptr any) error // source at jsonutil/jsonutil.go func WriteFile(filePath string, data any) error func WritePretty(filePath string, data any) error @@ -881,9 +854,14 @@ func HasOneKey(mp any, keys ...any) (ok bool, key any) func HasAllKeys(mp any, keys ...any) (ok bool, noKey any) // source at maputil/convert.go func KeyToLower(src map[string]string) map[string]string +func AnyToStrMap(src any) map[string]string func ToStringMap(src map[string]any) map[string]string +func ToL2StringMap(groupsMap map[string]any) map[string]map[string]string func CombineToSMap(keys, values []string) SMap func CombineToMap[K comdef.SortedType, V any](keys []K, values []V) map[K]V +func SliceToSMap(kvPairs ...string) map[string]string +func SliceToMap(kvPairs ...any) map[string]any +func SliceToTypeMap[T any](valFunc func(any) T, kvPairs ...any) map[string]T func ToAnyMap(mp any) map[string]any func TryAnyMap(mp any) (map[string]any, error) func HTTPQueryString(data map[string]any) string @@ -902,14 +880,21 @@ func GetFromAny(path string, data any) (val any, ok bool) func GetByPath(path string, mp map[string]any) (val any, ok bool) func GetByPathKeys(mp map[string]any, keys []string) (val any, ok bool) func Keys(mp any) (keys []string) +func TypedKeys[K comdef.SimpleType, V any](mp map[K]V) (keys []K) +func FirstKey[T any](mp map[string]T) string func Values(mp any) (values []any) +func TypedValues[K comdef.SimpleType, V any](mp map[K]V) (values []V) func EachAnyMap(mp any, fn func(key string, val any)) +func EachTypedMap[K comdef.SimpleType, V any](mp map[K]V, fn func(key K, val V)) // source at maputil/maputil.go func SimpleMerge(src, dst map[string]any) map[string]any +func Merge1level(mps ...map[string]any) map[string]any func DeepMerge(src, dst map[string]any, deep int) map[string]any func MergeSMap(src, dst map[string]string, ignoreCase bool) map[string]string +func MergeStrMap(src, dst map[string]string) map[string]string func MergeStringMap(src, dst map[string]string, ignoreCase bool) map[string]string func MergeMultiSMap(mps ...map[string]string) map[string]string +func MergeL2StrMap(mps ...map[string]map[string]string) map[string]map[string]string func FilterSMap(sm map[string]string) map[string]string func MakeByPath(path string, val any) (mp map[string]any) func MakeByKeys(keys []string, val any) (mp map[string]any) @@ -923,21 +908,23 @@ func SetByKeys(mp *map[string]any, keys []string, val any) (err error) > Package `github.com/gookit/goutil/mathutil` ```go +// source at mathutil/calc.go +func Abs[T comdef.Int](val T) T // source at mathutil/check.go func IsNumeric(c byte) bool func Compare(first, second any, op string) bool func CompInt[T comdef.Xint](first, second T, op string) (ok bool) func CompInt64(first, second int64, op string) bool func CompFloat[T comdef.Float](first, second T, op string) (ok bool) -func CompValue[T comdef.XintOrFloat](first, second T, op string) (ok bool) -func InRange[T comdef.IntOrFloat](val, min, max T) bool -func OutRange[T comdef.IntOrFloat](val, min, max T) bool +func CompValue[T comdef.Number](first, second T, op string) (ok bool) +func InRange[T comdef.Number](val, min, max T) bool +func OutRange[T comdef.Number](val, min, max T) bool func InUintRange[T comdef.Uint](val, min, max T) bool // source at mathutil/compare.go -func Min[T comdef.XintOrFloat](x, y T) T -func Max[T comdef.XintOrFloat](x, y T) T -func SwapMin[T comdef.XintOrFloat](x, y T) (T, T) -func SwapMax[T comdef.XintOrFloat](x, y T) (T, T) +func Min[T comdef.Number](x, y T) T +func Max[T comdef.Number](x, y T) T +func SwapMin[T comdef.Number](x, y T) (T, T) +func SwapMax[T comdef.Number](x, y T) (T, T) func MaxInt(x, y int) int func SwapMaxInt(x, y int) (int, int) func MaxI64(x, y int64) int64 @@ -1012,15 +999,15 @@ func ToStringWith(in any, optFns ...comfunc.ConvOptionFn) (string, error) func DataSize(size uint64) string func HowLongAgo(sec int64) string // source at mathutil/mathutil.go -func OrElse[T comdef.XintOrFloat](val, defVal T) T -func ZeroOr[T comdef.XintOrFloat](val, defVal T) T -func LessOr[T comdef.XintOrFloat](val, max, devVal T) T -func LteOr[T comdef.XintOrFloat](val, max, devVal T) T -func GreaterOr[T comdef.XintOrFloat](val, min, defVal T) T -func GteOr[T comdef.XintOrFloat](val, min, defVal T) T -func Mul[T1, T2 comdef.XintOrFloat](a T1, b T2) float64 +func OrElse[T comdef.Number](val, defVal T) T +func ZeroOr[T comdef.Number](val, defVal T) T +func LessOr[T comdef.Number](val, max, devVal T) T +func LteOr[T comdef.Number](val, max, devVal T) T +func GreaterOr[T comdef.Number](val, min, defVal T) T +func GteOr[T comdef.Number](val, min, defVal T) T +func Mul[T1, T2 comdef.Number](a T1, b T2) float64 func MulF2i(a, b float64) int -func Div[T1, T2 comdef.XintOrFloat](a T1, b T2) float64 +func Div[T1, T2 comdef.Number](a T1, b T2) float64 func DivInt[T comdef.Integer](a, b T) int func DivF2i(a, b float64) int func Percent(val, total int) float64 @@ -1029,8 +1016,6 @@ func RandomInt(min, max int) int func RandInt(min, max int) int func RandIntWithSeed(min, max int, seed int64) int func RandomIntWithSeed(min, max int, seed int64) int -// source at mathutil/value.go -func New[T comdef.IntOrFloat](v T) *Num[T] ``` ### Reflects @@ -1039,6 +1024,8 @@ func New[T comdef.IntOrFloat](v T) *Num[T] ```go // source at reflects/check.go +func IsTimeType(t reflect.Type) bool +func IsDurationType(t reflect.Type) bool func HasChild(v reflect.Value) bool func IsArrayOrSlice(k reflect.Kind) bool func IsSimpleKind(k reflect.Kind) bool @@ -1047,6 +1034,7 @@ func IsIntLike(k reflect.Kind) bool func IsIntx(k reflect.Kind) bool func IsUintX(k reflect.Kind) bool func IsNil(v reflect.Value) bool +func IsValidPtr(v reflect.Value) bool func CanBeNil(typ reflect.Type) bool func IsFunc(val any) bool func IsEqual(src, dst any) bool @@ -1057,12 +1045,13 @@ func BaseTypeVal(v reflect.Value) (value any, err error) func ToBaseVal(v reflect.Value) (value any, err error) func ConvToType(val any, typ reflect.Type) (rv reflect.Value, err error) func ValueByType(val any, typ reflect.Type) (rv reflect.Value, err error) -func ValueByKind(val any, kind reflect.Kind) (rv reflect.Value, err error) -func ConvToKind(val any, kind reflect.Kind) (rv reflect.Value, err error) +func ValueByKind(val any, kind reflect.Kind) (reflect.Value, error) +func ConvToKind(val any, kind reflect.Kind, fallback ...ConvFunc) (rv reflect.Value, err error) func ConvSlice(oldSlRv reflect.Value, newElemTyp reflect.Type) (rv reflect.Value, err error) func String(rv reflect.Value) string func ToString(rv reflect.Value) (str string, err error) func ValToString(rv reflect.Value, defaultAsErr bool) (str string, err error) +func ToTimeOrDuration(str string, typ reflect.Type) (any, error) // source at reflects/func.go func NewFunc(fn any) *FuncX func Call2(fn reflect.Value, args []reflect.Value) (reflect.Value, error) @@ -1070,8 +1059,9 @@ func Call(fn reflect.Value, args []reflect.Value, opt *CallOpt) ([]reflect.Value func SafeCall2(fun reflect.Value, args []reflect.Value) (val reflect.Value, err error) func SafeCall(fun reflect.Value, args []reflect.Value) (ret []reflect.Value, err error) // source at reflects/map.go -func EachMap(mp reflect.Value, fn func(key, val reflect.Value)) -func EachStrAnyMap(mp reflect.Value, fn func(key string, val any)) +func TryAnyMap(mp reflect.Value) (map[string]any, error) +func EachMap(mp reflect.Value, fn func(key, val reflect.Value)) (err error) +func EachStrAnyMap(mp reflect.Value, fn func(key string, val any)) error func FlatMap(rv reflect.Value, fn FlatFunc) // source at reflects/slice.go func MakeSliceByElem(elTyp reflect.Type, len, cap int) reflect.Value @@ -1098,32 +1088,6 @@ func Wrap(rv reflect.Value) Value func ValueOf(v any) Value ``` -### Stdio - -> Package `github.com/gookit/goutil/stdio` - -```go -// source at stdio/ioutil.go -func QuietFprint(w io.Writer, a ...any) -func QuietFprintf(w io.Writer, tpl string, vs ...any) -func QuietFprintln(w io.Writer, a ...any) -func QuietWriteString(w io.Writer, ss ...string) -// source at stdio/stdio.go -func DiscardReader(src io.Reader) -func ReadString(r io.Reader) string -func MustReadReader(r io.Reader) []byte -func NewIOReader(in any) io.Reader -func NewScanner(in any) *bufio.Scanner -func WriteByte(b byte) -func WriteBytes(bs []byte) -func WritelnBytes(bs []byte) -func WriteString(s string) -func Writeln(s string) -// source at stdio/writer.go -func WrapW(w io.Writer) *WriteWrapper -func NewWriteWrapper(w io.Writer) *WriteWrapper -``` - ### Structs > Package `github.com/gookit/goutil/structs` @@ -1140,8 +1104,10 @@ func TryToSMap(st any, optFns ...MapOptFunc) (map[string]string, error) func MustToSMap(st any, optFns ...MapOptFunc) map[string]string func ToString(st any, optFns ...MapOptFunc) string func WithMapTagName(tagName string) MapOptFunc +func WithUserFunc(fn CustomUserFunc) MapOptFunc func MergeAnonymous(opt *MapOptions) func ExportPrivate(opt *MapOptions) +func WithIgnoreEmpty(opt *MapOptions) func StructToMap(st any, optFns ...MapOptFunc) (map[string]any, error) // source at structs/copy.go func MapStruct(srcSt, dstSt any) @@ -1172,6 +1138,8 @@ func WrapValue(rv reflect.Value) *Wrapper // source at structs/writer.go func NewWriter(ptr any) *Wrapper func WithParseDefault(opt *SetOptions) +func WithBeforeSetFn(fn BeforeSetFunc) SetOptFunc +func BindData(ptr any, data map[string]any, optFns ...SetOptFunc) error func SetValues(ptr any, data map[string]any, optFns ...SetOptFunc) error ``` @@ -1181,10 +1149,12 @@ func SetValues(ptr any, data map[string]any, optFns ...SetOptFunc) error ```go // source at strutil/bytes.go -func NewBuffer() *Buffer +func NewBuffer(initSize ...int) *Buffer func NewByteChanPool(maxSize, width, capWidth int) *ByteChanPool // source at strutil/check.go func IsNumChar(c byte) bool +func IsInt(s string) bool +func IsFloat(s string) bool func IsNumeric(s string) bool func IsAlphabet(char uint8) bool func IsAlphaNum(c uint8) bool @@ -1196,8 +1166,10 @@ func IContains(s, sub string) bool func ContainsByte(s string, c byte) bool func ContainsOne(s string, subs []string) bool func HasOneSub(s string, subs []string) bool +func IContainsOne(s string, subs []string) bool func ContainsAll(s string, subs []string) bool func HasAllSubs(s string, subs []string) bool +func IContainsAll(s string, subs []string) bool func IsStartsOf(s string, prefixes []string) bool func HasOnePrefix(s string, prefixes []string) bool func HasPrefix(s string, prefix string) bool @@ -1215,6 +1187,7 @@ func IsSymbol(r rune) bool func HasEmpty(ss ...string) bool func IsAllEmpty(ss ...string) bool func IsVersion(s string) bool +func IsVarName(s string) bool func Compare(s1, s2, op string) bool func VersionCompare(v1, v2, op string) bool func SimpleMatch(s string, keywords []string) bool @@ -1232,6 +1205,7 @@ func Quote(s string) string func Unquote(s string) string func Join(sep string, ss ...string) string func JoinList(sep string, ss []string) string +func JoinComma(ss []string) string func JoinAny(sep string, parts ...any) string func Implode(sep string, ss ...string) string func String(val any) (string, error) @@ -1331,6 +1305,7 @@ func IndentBytes(b, prefix []byte) []byte func MicroTimeID() string func MicroTimeHexID() string func MTimeHexID() string +func MTimeBase36() string func MTimeBaseID(toBase int) string func DatetimeNo(prefix string) string func DateSN(prefix string) string @@ -1339,6 +1314,8 @@ func DateSNV2(prefix string, extBase ...int) string func Md5(src any) string func MD5(src any) string func GenMd5(src any) string +func Md5Simple(src any) string +func Md5Base62(src any) string func Md5Bytes(src any) []byte func ShortMd5(src any) string func HashPasswd(pwd, key string) string @@ -1388,6 +1365,7 @@ func RunesWidth(rs []rune) (w int) func Truncate(s string, w int, tail string) string func TextTruncate(s string, w int, tail string) string func Utf8Truncate(s string, w int, tail string) string +func Chunk[T ~string](s T, size int) []T func TextSplit(s string, w int) []string func Utf8Split(s string, w int) []string func TextWrap(s string, w int) string @@ -1409,6 +1387,7 @@ func SplitNValid(s, sep string, n int) (ss []string) func SplitN(s, sep string, n int) (ss []string) func SplitTrimmed(s, sep string) (ss []string) func SplitNTrimmed(s, sep string, n int) (ss []string) +func SplitByWhitespace(s string) []string func Substr(s string, pos, length int) string func SplitInlineComment(val string, strict ...bool) (string, string) func FirstLine(output string) string @@ -1433,12 +1412,12 @@ func SubstrCount(s, substr string, params ...uint64) (int, error) ```go // source at syncs/chan.go -func WaitCloseSignals(onClose func(sig os.Signal)) func Go(f func() error) error // source at syncs/group.go func NewCtxErrGroup(ctx context.Context, limit ...int) (*ErrGroup, context.Context) func NewErrGroup(limit ...int) *ErrGroup // source at syncs/signal.go +func WaitCloseSignals(onClose func(sig os.Signal), sigCh ...chan os.Signal) func SignalHandler(ctx context.Context, signals ...os.Signal) (execute func() error, interrupt func(error)) ``` @@ -1454,15 +1433,14 @@ func QuickExec(cmdLine string, workDir ...string) (string, error) func ExecLine(cmdLine string, workDir ...string) (string, error) func ExecCmd(binName string, args []string, workDir ...string) (string, error) func ShellExec(cmdLine string, shells ...string) (string, error) -// source at sysutil/stack.go -func CallersInfos(skip, num int, filters ...func(file string, fc *runtime.Func) bool) []*CallerInfo // source at sysutil/sysenv.go func IsMSys() bool +func IsWSL() bool func IsConsole(out io.Writer) bool func IsTerminal(fd uintptr) bool func StdIsTerminal() bool func Hostname() string -func CurrentShell(onlyName bool) (path string) +func CurrentShell(onlyName bool, fallbackShell ...string) string func HasShellEnv(shell string) bool func IsShellSpecialVar(c uint8) bool func FindExecutable(binName string) (string, error) @@ -1472,11 +1450,13 @@ func Getenv(name string, def ...string) string func Environ() map[string]string func EnvMapWith(newEnv map[string]string) map[string]string func EnvPaths() []string -func SearchPath(keywords string, limit int) []string +func ToEnvPATH(paths []string) string +func SearchPath(keywords string, limit int, opts ...SearchPathOption) []string // source at sysutil/sysgo.go func GoVersion() string func ParseGoVersion(line string) (*GoInfo, error) func OsGoInfo() (*GoInfo, error) +func CallersInfos(skip, num int, filters ...goinfo.CallerFilterFunc) []*CallerInfo // source at sysutil/sysutil.go func Workdir() string func BinDir() string @@ -1485,16 +1465,16 @@ func BinFile() string func Open(fileOrURL string) error func OpenBrowser(fileOrURL string) error func OpenFile(path string) error -// source at sysutil/sysutil_nonwin.go -func Kill(pid int, signal syscall.Signal) error -func ProcessExists(pid int) bool -// source at sysutil/sysutil_unix.go +// source at sysutil/sysutil_linux.go func IsWin() bool func IsWindows() bool func IsMac() bool func IsDarwin() bool func IsLinux() bool func OpenURL(URL string) error +// source at sysutil/sysutil_nonwin.go +func Kill(pid int, signal syscall.Signal) error +func ProcessExists(pid int) bool // source at sysutil/user.go func MustFindUser(uname string) *user.User func LoginUser() *user.User @@ -1502,12 +1482,13 @@ func CurrentUser() *user.User func UHomeDir() string func UserHomeDir() string func HomeDir() string -func UserDir(subPath string) string -func UserCacheDir(subPath string) string -func UserConfigDir(subPath string) string +func UserDir(subPaths ...string) string +func UserCacheDir(subPaths ...string) string +func UserConfigDir(subPaths ...string) string func ExpandPath(path string) string func ExpandHome(path string) string // source at sysutil/user_nonwin.go +func IsAdmin() bool func ChangeUserByName(newUname string) error func ChangeUserUidGid(newUID int, newGid int) error func ChangeUserUIDGid(newUID int, newGid int) (err error) @@ -1525,15 +1506,18 @@ func MockEnvValue(key, val string, fn func(nv string)) func MockEnvValues(kvMap map[string]string, fn func()) func MockOsEnvByText(envText string, fn func()) func MockOsEnv(mp map[string]string, fn func()) +func SetOsEnvs(mp map[string]string) string +func RemoveTmpEnvs(tmpKey string) func ClearOSEnv() func RevertOSEnv() +func RunOnCleanEnv(runFn func()) func MockCleanOsEnv(mp map[string]string, fn func()) // source at testutil/httpmock.go -func NewHttpRequest(method, path string, data *MD) *http.Request func NewHTTPRequest(method, path string, data *MD) *http.Request func MockRequest(h http.Handler, method, path string, data *MD) *httptest.ResponseRecorder +func MockHttpServer() *EchoServer func TestMain(m *testing.M) -func NewEchoServer() *httptest.Server +func NewEchoServer() *EchoServer func BuildEchoReply(r *http.Request) *EchoReply func ParseRespToReply(w *http.Response) *EchoReply func ParseBodyToReply(bd io.ReadCloser) *EchoReply @@ -1549,7 +1533,7 @@ func SetTimeLocalUTC() func RestoreTimeLocal() // source at testutil/writer.go func NewTestWriter() *TestWriter -func NewDirEnt(fpath string, isDir ...bool) *fakeobj.DirEntry +func NewDirEnt(fPath string, isDir ...bool) *fakeobj.DirEntry ``` ### Timex @@ -1749,13 +1733,6 @@ go test -v -run ^TestErr$ go test -v -run ^TestErr$ ./testutil/assert/... ``` -## Related - -- https://github.com/duke-git/lancet -- https://github.com/samber/lo -- https://github.com/zyedidia/generic -- https://github.com/thoas/go-funk - ## Gookit packages - [gookit/ini](https://github.com/gookit/ini) Go config management, use INI files @@ -1771,6 +1748,13 @@ go test -v -run ^TestErr$ ./testutil/assert/... - [gookit/goutil](https://github.com/gookit/goutil) Some utils for the Go: string, array/slice, map, format, cli, env, filesystem, test and more - More, please see https://github.com/gookit +## Related + +- https://github.com/duke-git/lancet +- https://github.com/samber/lo +- https://github.com/zyedidia/generic +- https://github.com/thoas/go-funk + ## License [MIT](LICENSE) diff --git a/vendor/github.com/gookit/goutil/arrutil/README.zh-CN.md b/vendor/github.com/gookit/goutil/arrutil/README.zh-CN.md new file mode 100644 index 000000000..fcacd208a --- /dev/null +++ b/vendor/github.com/gookit/goutil/arrutil/README.zh-CN.md @@ -0,0 +1,50 @@ +## ArrUtil + +`arrutil` 是一个用于操作数组和切片的工具包,提供了丰富的功能来简化 Go 语言中数组和切片的处理。 + +## Install + +```shell +go get github.com/gookit/goutil/arrutil +``` + +## Go docs + +- [Go docs](https://pkg.go.dev/github.com/gookit/goutil/arrutil) + +## 基本功能 + +主要包括以下功能: + +1. **数组/切片的基本操作**: + - `RandomOne`:从数组或切片中随机获取一个元素。 + - `Reverse`:反转数组或切片中的元素顺序。 + +2. **检查和查找**: + - `Contains` 和 `HasValue`:检查数组或切片是否包含特定值。 + - `InStrings` 和 `StringsHas`:检查字符串切片中是否包含特定字符串。 + - `IntsHas` 和 `Int64sHas`:检查整数切片中是否包含特定整数值。 + - `Find` 和 `FindOrDefault`:根据谓词函数查找元素,如果没有找到则返回默认值。 + +3. **集合操作**: + - `Union`:计算两个切片的并集。 + - `Intersects`:计算两个切片的交集。 + - `Excepts` 和 `Differences`:计算两个切片的差集。 + - `TwowaySearch`:在切片中双向搜索特定元素。 + +4. **转换和格式化**: + - `ToInt64s` 和 `ToStrings`:将任意类型的切片转换为整数或字符串切片。 + - `JoinSlice` 和 `JoinStrings`:将切片中的元素连接成一个字符串。 + - `FormatIndent`:将数组或切片格式化为带有缩进的字符串。 + +5. **排序和过滤**: + - `Sort`:对切片进行排序。 + - `Filter`:根据条件过滤切片中的元素。 + - `Remove`:从切片中移除特定元素。 + +6. **其他实用功能**: + - `Unique`:去除切片中的重复元素。 + - `FirstOr`:获取切片的第一个元素,如果切片为空则返回默认值。 + +这些功能使得在 Go 语言中处理数组和切片变得更加方便和高效。无论是进行数据处理、集合运算还是字符串操作,`arrutil` 都提供了一系列简洁且易于使用的函数来帮助开发者完成任务。 + diff --git a/vendor/github.com/gookit/goutil/arrutil/convert.go b/vendor/github.com/gookit/goutil/arrutil/convert.go index 17ce8f6e7..9b7fe30f0 100644 --- a/vendor/github.com/gookit/goutil/arrutil/convert.go +++ b/vendor/github.com/gookit/goutil/arrutil/convert.go @@ -126,7 +126,7 @@ func SliceToInt64s(arr []any) []int64 { } /************************************************************* - * convert func for anys + * convert func for any-slice *************************************************************/ // AnyToSlice convert any(allow: array,slice) to []any @@ -159,22 +159,28 @@ func MustToStrings(arr any) []string { // ToStrings convert any(allow: array,slice) to []string func ToStrings(arr any) (ret []string, err error) { - rv := reflect.ValueOf(arr) - if rv.Kind() == reflect.String { - return []string{rv.String()}, nil + // try direct convert + switch typVal := arr.(type) { + case string: + return []string{typVal}, nil + case []string: + return typVal, nil + case []any: + return SliceToStrings(typVal), nil } + // try use reflect to convert + rv := reflect.ValueOf(arr) if rv.Kind() != reflect.Slice && rv.Kind() != reflect.Array { err = ErrInvalidType return } for i := 0; i < rv.Len(); i++ { - str, err := strutil.ToString(rv.Index(i).Interface()) - if err != nil { - return []string{}, err + str, err1 := strutil.ToString(rv.Index(i).Interface()) + if err1 != nil { + return nil, err1 } - ret = append(ret, str) } return @@ -182,11 +188,6 @@ func ToStrings(arr any) (ret []string, err error) { // SliceToStrings safe convert []any to []string func SliceToStrings(arr []any) []string { - return QuietStrings(arr) -} - -// QuietStrings safe convert []any to []string -func QuietStrings(arr []any) []string { ss := make([]string, len(arr)) for i, v := range arr { ss[i] = strutil.SafeString(v) @@ -194,6 +195,9 @@ func QuietStrings(arr []any) []string { return ss } +// QuietStrings safe convert []any to []string +func QuietStrings(arr []any) []string { return SliceToStrings(arr) } + // ConvType convert type of slice elements to new type slice, by the given newElemTyp type. // // Supports conversion between []string, []intX, []uintX, []floatX. diff --git a/vendor/github.com/gookit/goutil/arrutil/process.go b/vendor/github.com/gookit/goutil/arrutil/process.go index 05d960f6b..dc72c9df6 100644 --- a/vendor/github.com/gookit/goutil/arrutil/process.go +++ b/vendor/github.com/gookit/goutil/arrutil/process.go @@ -74,7 +74,7 @@ func Column[T any, V any](list []T, mapFn func(obj T) (val V, find bool)) []V { } // Unique value in the given slice data. -func Unique[T ~string | comdef.XintOrFloat](list []T) []T { +func Unique[T comdef.NumberOrString](list []T) []T { if len(list) < 2 { return list } @@ -92,7 +92,7 @@ func Unique[T ~string | comdef.XintOrFloat](list []T) []T { } // IndexOf value in given slice. -func IndexOf[T ~string | comdef.XintOrFloat](val T, list []T) int { +func IndexOf[T comdef.NumberOrString](val T, list []T) int { for i, v := range list { if v == val { return i @@ -100,3 +100,16 @@ func IndexOf[T ~string | comdef.XintOrFloat](val T, list []T) int { } return -1 } + +// FirstOr get first value of slice, if slice is empty, return the default value. +func FirstOr[T any](list []T, defVal ...T) T { + if len(list) > 0 { + return list[0] + } + + if len(defVal) > 0 { + return defVal[0] + } + var zero T + return zero +} diff --git a/vendor/github.com/gookit/goutil/arrutil/strings.go b/vendor/github.com/gookit/goutil/arrutil/strings.go index 55968a41f..78167afb0 100644 --- a/vendor/github.com/gookit/goutil/arrutil/strings.go +++ b/vendor/github.com/gookit/goutil/arrutil/strings.go @@ -47,6 +47,10 @@ func StringsTryInts(ss []string) (ints []int, err error) { // StringsUnique unique string slice func StringsUnique(ss []string) []string { + if len(ss) == 0 { + return ss + } + var unique []string for _, s := range ss { if !StringsContains(unique, s) { diff --git a/vendor/github.com/gookit/goutil/byteutil/README.md b/vendor/github.com/gookit/goutil/byteutil/README.md index 89147dc7c..05c473f7d 100644 --- a/vendor/github.com/gookit/goutil/byteutil/README.md +++ b/vendor/github.com/gookit/goutil/byteutil/README.md @@ -1,6 +1,6 @@ # Bytes Util -Provide some commonly bytes util functions. +Provide some common bytes util functions. ## Install diff --git a/vendor/github.com/gookit/goutil/byteutil/byteutil.go b/vendor/github.com/gookit/goutil/byteutil/byteutil.go index 6f3044d25..32ffacf9e 100644 --- a/vendor/github.com/gookit/goutil/byteutil/byteutil.go +++ b/vendor/github.com/gookit/goutil/byteutil/byteutil.go @@ -13,6 +13,14 @@ import ( // Md5 Generate a 32-bit md5 bytes func Md5(src any) []byte { + bs := Md5Sum(src) + dst := make([]byte, hex.EncodedLen(len(bs))) + hex.Encode(dst, bs) + return dst +} + +// Md5Sum Generate a md5 bytes +func Md5Sum(src any) []byte { h := md5.New() switch val := src.(type) { @@ -24,16 +32,11 @@ func Md5(src any) []byte { h.Write([]byte(fmt.Sprint(src))) } - bs := h.Sum(nil) // cap(bs) == 16 - dst := make([]byte, hex.EncodedLen(len(bs))) - hex.Encode(dst, bs) - return dst + return h.Sum(nil) // cap(bs) == 16 } -// ShortMd5 Generate a 16-bit md5 bytes. remove first 8 and last 8 bytes from 32-bit md5. -func ShortMd5(src any) []byte { - return Md5(src)[8:24] -} +// ShortMd5 Generate a 16-bit md5 bytes. remove the first 8 and last 8 bytes from 32-bit md5. +func ShortMd5(src any) []byte { return Md5(src)[8:24] } // Random bytes generate func Random(length int) ([]byte, error) { diff --git a/vendor/github.com/gookit/goutil/byteutil/check.go b/vendor/github.com/gookit/goutil/byteutil/check.go index 7be9e4b4b..f4d2e5ba6 100644 --- a/vendor/github.com/gookit/goutil/byteutil/check.go +++ b/vendor/github.com/gookit/goutil/byteutil/check.go @@ -2,3 +2,9 @@ package byteutil // IsNumChar returns true if the given character is a numeric, otherwise false. func IsNumChar(c byte) bool { return c >= '0' && c <= '9' } + +// IsAlphaChar returns true if the given character is a alphabet, otherwise false. +func IsAlphaChar(c byte) bool { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') +} + diff --git a/vendor/github.com/gookit/goutil/check.go b/vendor/github.com/gookit/goutil/check.go deleted file mode 100644 index ba362f4b9..000000000 --- a/vendor/github.com/gookit/goutil/check.go +++ /dev/null @@ -1,86 +0,0 @@ -package goutil - -import ( - "reflect" - - "github.com/gookit/goutil/internal/checkfn" - "github.com/gookit/goutil/reflects" -) - -// IsNil value check -func IsNil(v any) bool { - if v == nil { - return true - } - return reflects.IsNil(reflect.ValueOf(v)) -} - -// IsZero value check, alias of the IsEmpty() -var IsZero = IsEmpty - -// IsEmpty value check -func IsEmpty(v any) bool { - if v == nil { - return true - } - return reflects.IsEmpty(reflect.ValueOf(v)) -} - -// Alias of the IsEmptyReal() -var IsZeroReal = IsEmptyReal - -// IsEmptyReal checks for empty given value and also real empty value if the passed value is a pointer -func IsEmptyReal(v any) bool { - if v == nil { - return true - } - return reflects.IsEmptyReal(reflect.ValueOf(v)) -} - -// IsFunc value -func IsFunc(val any) bool { - if val == nil { - return false - } - return reflect.TypeOf(val).Kind() == reflect.Func -} - -// IsEqual determines if two objects are considered equal. -// -// TIP: cannot compare function type -func IsEqual(src, dst any) bool { - if src == nil || dst == nil { - return src == dst - } - - // cannot compare function type - if IsFunc(src) || IsFunc(dst) { - return false - } - return reflects.IsEqual(src, dst) -} - -// Contains try loop over the data check if the data includes the element. -// alias of the IsContains -// -// TIP: only support types: string, map, array, slice -// -// map - check key exists -// string - check sub-string exists -// array,slice - check sub-element exists -func Contains(data, elem any) bool { - _, found := checkfn.Contains(data, elem) - return found -} - -// IsContains try loop over the data check if the data includes the element. -// -// TIP: only support types: string, map, array, slice -// -// map - check key exists -// string - check sub-string exists -// array,slice - check sub-element exists -func IsContains(data, elem any) bool { - _, found := checkfn.Contains(data, elem) - return found -} diff --git a/vendor/github.com/gookit/goutil/cliutil/cmdline/builder.go b/vendor/github.com/gookit/goutil/cliutil/cmdline/builder.go index fac12bc9a..2d06122d0 100644 --- a/vendor/github.com/gookit/goutil/cliutil/cmdline/builder.go +++ b/vendor/github.com/gookit/goutil/cliutil/cmdline/builder.go @@ -3,6 +3,7 @@ package cmdline import ( "strings" + "github.com/gookit/goutil/internal/comfunc" "github.com/gookit/goutil/strutil" ) @@ -24,6 +25,12 @@ func NewBuilder(binFile string, args ...string) *LineBuilder { return b } +// ResetGet value, will reset after get. +func (b *LineBuilder) ResetGet() string { + defer b.Reset() + return b.String() +} + // AddArg to builder func (b *LineBuilder) AddArg(arg string) { _, _ = b.WriteString(arg) @@ -51,35 +58,10 @@ func (b *LineBuilder) AddAny(args ...any) { // WriteString arg string to the builder, will auto quote special string. // refer strconv.Quote() func (b *LineBuilder) WriteString(a string) (int, error) { - var quote byte - if pos := strings.IndexByte(a, '"'); pos > -1 { - quote = '\'' - // fix: a = `--pretty=format:"one two three"` - if pos > 0 && a[len(a)-1] == '"' { - quote = 0 - } - } else if pos := strings.IndexByte(a, '\''); pos > -1 { - quote = '"' - // fix: a = "--pretty=format:'one two three'" - if pos > 0 && a[len(a)-1] == '\'' { - quote = 0 - } - } else if a == "" || strings.ContainsRune(a, ' ') { - quote = '"' - } - // add sep on not-first write. if b.Len() != 0 { _ = b.WriteByte(' ') } - // no quote char OR not need quote - if quote == 0 { - return b.Builder.WriteString(a) - } - - _ = b.WriteByte(quote) // add start quote - n, err := b.Builder.WriteString(a) - _ = b.WriteByte(quote) // add end quote - return n, err + return b.Builder.WriteString(comfunc.ShellQuote(a)) } diff --git a/vendor/github.com/gookit/goutil/cliutil/cmdline/cmdline.go b/vendor/github.com/gookit/goutil/cliutil/cmdline/cmdline.go index d048720fe..b17fea9ed 100644 --- a/vendor/github.com/gookit/goutil/cliutil/cmdline/cmdline.go +++ b/vendor/github.com/gookit/goutil/cliutil/cmdline/cmdline.go @@ -1,12 +1,15 @@ // Package cmdline provide quick build and parse cmd line string. package cmdline +import "github.com/gookit/goutil/internal/comfunc" + // LineBuild build command line string by given args. func LineBuild(binFile string, args []string) string { return NewBuilder(binFile, args...).String() } // ParseLine input command line text. alias of the StringToOSArgs() -func ParseLine(line string) []string { - return NewParser(line).Parse() -} +func ParseLine(line string) []string { return NewParser(line).Parse() } + +// Quote string in shell command env +func Quote(s string) string { return comfunc.ShellQuote(s) } diff --git a/vendor/github.com/gookit/goutil/comdef/comdef.go b/vendor/github.com/gookit/goutil/comdef/comdef.go index d1e0ef238..f61d17076 100644 --- a/vendor/github.com/gookit/goutil/comdef/comdef.go +++ b/vendor/github.com/gookit/goutil/comdef/comdef.go @@ -1,14 +1,6 @@ // Package comdef provide some common type or constant definitions package comdef -type ( - // MarshalFunc define - MarshalFunc func(v any) ([]byte, error) - - // UnmarshalFunc define - UnmarshalFunc func(bts []byte, ptr any) error -) - // ToTypeFunc convert value to defined type type ToTypeFunc[T any] func(any) (T, error) diff --git a/vendor/github.com/gookit/goutil/comdef/formatter.go b/vendor/github.com/gookit/goutil/comdef/formatter.go index 7bd713ad8..08651f08a 100644 --- a/vendor/github.com/gookit/goutil/comdef/formatter.go +++ b/vendor/github.com/gookit/goutil/comdef/formatter.go @@ -4,7 +4,7 @@ import ( "bytes" "io" - "github.com/gookit/goutil/stdio" + "github.com/gookit/goutil/x/stdio" ) // DataFormatter interface @@ -14,6 +14,13 @@ type DataFormatter interface { } // BaseFormatter struct +// +// Usage: +// +// type YourFormatter struct { +// comdef.BaseFormatter +// } +// // implement the DataFormatter interface... type BaseFormatter struct { ow ByteStringWriter // Out formatted to the writer @@ -41,7 +48,7 @@ func (f *BaseFormatter) SetOutput(out io.Writer) { f.Out = out } -// BsWriter build and get +// BsWriter warp the Out, build a ByteStringWriter func (f *BaseFormatter) BsWriter() ByteStringWriter { if f.ow == nil { if f.Out == nil { @@ -52,6 +59,5 @@ func (f *BaseFormatter) BsWriter() ByteStringWriter { f.ow = stdio.NewWriteWrapper(f.Out) } } - return f.ow } diff --git a/vendor/github.com/gookit/goutil/comdef/interface.go b/vendor/github.com/gookit/goutil/comdef/interface.go index 3f3fb8145..630882abf 100644 --- a/vendor/github.com/gookit/goutil/comdef/interface.go +++ b/vendor/github.com/gookit/goutil/comdef/interface.go @@ -29,6 +29,9 @@ type Float64able interface { Float64() (float64, error) } +// MapFunc definition +type MapFunc func(val any) (any, error) + // // // Matcher type diff --git a/vendor/github.com/gookit/goutil/comdef/serializer.go b/vendor/github.com/gookit/goutil/comdef/serializer.go new file mode 100644 index 000000000..04888e1e2 --- /dev/null +++ b/vendor/github.com/gookit/goutil/comdef/serializer.go @@ -0,0 +1,27 @@ +package comdef + +type ( + // MarshalFunc define + MarshalFunc func(v any) ([]byte, error) + + // UnmarshalFunc define + UnmarshalFunc func(bts []byte, ptr any) error +) + +// Serializer interface definition +type Serializer interface { + Serialize(v any) ([]byte, error) + Deserialize(data []byte, v any) error +} + +// GoSerializer interface definition +type GoSerializer interface { + Marshal(v any) ([]byte, error) + Unmarshal(v []byte, ptr any) error +} + +// Codec interface definition +type Codec interface { + Decode(blob []byte, v any) (err error) + Encode(v any) (out []byte, err error) +} diff --git a/vendor/github.com/gookit/goutil/comdef/types.go b/vendor/github.com/gookit/goutil/comdef/types.go index 02991cbdb..1907d288c 100644 --- a/vendor/github.com/gookit/goutil/comdef/types.go +++ b/vendor/github.com/gookit/goutil/comdef/types.go @@ -25,16 +25,28 @@ type Float interface { ~float32 | ~float64 } -// IntOrFloat interface type. all int and float types +// IntOrFloat interface type. all int and float types, but NOT uint types type IntOrFloat interface { Int | Float } -// XintOrFloat interface type. all int, uint and float types +// Number interface type. contains all int, uint and float types +type Number interface { + Int | Uint | Float +} + +// XintOrFloat interface type. all int, uint and float types. alias of Number +// +// Deprecated: use Number instead. type XintOrFloat interface { Int | Uint | Float } +// NumberOrString interface type for (x)int, float, ~string types +type NumberOrString interface { + Int | Uint | Float | ~string +} + // SortedType interface type. same of constraints.Ordered // // it can be ordered, that supports the operators < <= >= >. @@ -67,3 +79,12 @@ type SimpleType interface { type ScalarType interface { Int | Uint | Float | ~string | ~bool } + +// StrMap is alias of map[string]string +type StrMap map[string]string + +// AnyMap is alias of map[string]any +type AnyMap map[string]any + +// L2StrMap is alias of map[string]map[string]string +type L2StrMap map[string]map[string]string diff --git a/vendor/github.com/gookit/goutil/conv.go b/vendor/github.com/gookit/goutil/conv.go index b0a0c069c..f9e8047a0 100644 --- a/vendor/github.com/gookit/goutil/conv.go +++ b/vendor/github.com/gookit/goutil/conv.go @@ -20,20 +20,16 @@ func Bool(v any) bool { } // ToBool try to convert type to bool -func ToBool(v any) (bool, error) { - return comfunc.ToBool(v) -} +func ToBool(v any) (bool, error) { return comfunc.ToBool(v) } -// String always convert value to string, will ignore error +// String func. always converts value to string, will ignore error func String(v any) string { s, _ := strutil.AnyToString(v, false) return s } // ToString convert value to string, will return error on fail. -func ToString(v any) (string, error) { - return strutil.AnyToString(v, true) -} +func ToString(v any) (string, error) { return strutil.AnyToString(v, true) } // Int convert value to int func Int(v any) int { @@ -42,9 +38,7 @@ func Int(v any) int { } // ToInt try to convert value to int -func ToInt(v any) (int, error) { - return mathutil.ToInt(v) -} +func ToInt(v any) (int, error) { return mathutil.ToInt(v) } // Int64 convert value to int64 func Int64(v any) int64 { @@ -53,9 +47,7 @@ func Int64(v any) int64 { } // ToInt64 try to convert value to int64 -func ToInt64(v any) (int64, error) { - return mathutil.ToInt64(v) -} +func ToInt64(v any) (int64, error) { return mathutil.ToInt64(v) } // Uint convert value to uint func Uint(v any) uint { @@ -64,9 +56,7 @@ func Uint(v any) uint { } // ToUint try to convert value to uint -func ToUint(v any) (uint, error) { - return mathutil.ToUint(v) -} +func ToUint(v any) (uint, error) { return mathutil.ToUint(v) } // Uint64 convert value to uint64 func Uint64(v any) uint64 { @@ -75,14 +65,10 @@ func Uint64(v any) uint64 { } // ToUint64 try to convert value to uint64 -func ToUint64(v any) (uint64, error) { - return mathutil.ToUint64(v) -} +func ToUint64(v any) (uint64, error) { return mathutil.ToUint64(v) } // BoolString convert bool to string -func BoolString(bl bool) string { - return strconv.FormatBool(bl) -} +func BoolString(bl bool) string { return strconv.FormatBool(bl) } // BaseTypeVal convert custom type or intX,uintX,floatX to generic base type. // @@ -136,7 +122,7 @@ func ConvOrDefault(val any, kind reflect.Kind, defVal any) any { // // Examples: // -// val, err := ToKind("123", reflect.Int) // 123 +// val, err := ToKind("123", reflect.Int) // 123 func ToKind(val any, kind reflect.Kind, fbFunc func(val any) (any, error)) (newVal any, err error) { switch kind { case reflect.Int: diff --git a/vendor/github.com/gookit/goutil/envutil/envutil.go b/vendor/github.com/gookit/goutil/envutil/envutil.go index 9a446fde1..bd433d234 100644 --- a/vendor/github.com/gookit/goutil/envutil/envutil.go +++ b/vendor/github.com/gookit/goutil/envutil/envutil.go @@ -3,7 +3,9 @@ package envutil import ( "os" + "strings" + "github.com/gookit/goutil/internal/comfunc" "github.com/gookit/goutil/internal/varexpr" ) @@ -42,36 +44,23 @@ func ParseValue(val string) string { } // VarParse alias of the ParseValue -func VarParse(val string) string { - return varexpr.SafeParse(val) -} +func VarParse(val string) string { return varexpr.SafeParse(val) } // ParseEnvValue alias of the ParseValue -func ParseEnvValue(val string) string { - return varexpr.SafeParse(val) +func ParseEnvValue(val string) string { return varexpr.SafeParse(val) } + +// SplitText2map parse ENV text to map. Can use to parse .env file contents. +func SplitText2map(text string) map[string]string { + envMp, _ := comfunc.ParseEnvLines(text, comfunc.ParseEnvLineOption{ + SkipOnErrorLine: true, + }) + return envMp } -// SetEnvMap set multi ENV(string-map) to os -func SetEnvMap(mp map[string]string) { - for key, value := range mp { - _ = os.Setenv(key, value) - } -} - -// SetEnvs set multi k-v ENV pairs to os -func SetEnvs(kvPairs ...string) { - if len(kvPairs)%2 == 1 { - panic("envutil.SetEnvs: odd argument count") - } - - for i := 0; i < len(kvPairs); i += 2 { - _ = os.Setenv(kvPairs[i], kvPairs[i+1]) - } -} - -// UnsetEnvs from os -func UnsetEnvs(keys ...string) { - for _, key := range keys { - _ = os.Unsetenv(key) +// SplitLineToKv parse ENV line to k-v. eg: 'DEBUG=true' => ['DEBUG', 'true'] +func SplitLineToKv(line string) (string, string) { + if line = strings.TrimSpace(line); line == "" { + return "", "" } + return comfunc.SplitLineToKv(line, "=") } diff --git a/vendor/github.com/gookit/goutil/envutil/get.go b/vendor/github.com/gookit/goutil/envutil/get.go index 4cd9d76ea..d3be8a855 100644 --- a/vendor/github.com/gookit/goutil/envutil/get.go +++ b/vendor/github.com/gookit/goutil/envutil/get.go @@ -4,9 +4,9 @@ import ( "os" "path/filepath" - "github.com/gookit/goutil/basefn" "github.com/gookit/goutil/internal/comfunc" "github.com/gookit/goutil/strutil" + "github.com/gookit/goutil/x/basefn" ) // Getenv get ENV value by key name, can with default value @@ -18,6 +18,14 @@ func Getenv(name string, def ...string) string { return val } +// MustGet get ENV value by key name, if not exists or empty, will panic +func MustGet(name string) string { + if val := os.Getenv(name); val != "" { + return val + } + panic("ENV key '" + name + "' not exists") +} + // GetInt get int ENV value by key name, can with default value func GetInt(name string, def ...int) int { if val := os.Getenv(name); val != "" { @@ -34,6 +42,20 @@ func GetBool(name string, def ...bool) bool { return basefn.FirstOr(def, false) } +// GetOne get one not empty ENV value by input names. +func GetOne(names []string, defVal ...string) string { + for _, name := range names { + if val := os.Getenv(name); val != "" { + return val + } + } + + if len(defVal) > 0 { + return defVal[0] + } + return "" +} + // GetMulti ENV values by input names. func GetMulti(names ...string) map[string]string { valMap := make(map[string]string, len(names)) @@ -46,6 +68,15 @@ func GetMulti(names ...string) map[string]string { return valMap } +// OnExist check ENV value by key name, if exists call fn +func OnExist(name string, fn func(val string)) bool { + if val := os.Getenv(name); val != "" { + fn(val) + return true + } + return false +} + // EnvPaths get and split $PATH to []string func EnvPaths() []string { return filepath.SplitList(os.Getenv("PATH")) diff --git a/vendor/github.com/gookit/goutil/envutil/info.go b/vendor/github.com/gookit/goutil/envutil/info.go index 5a66150f3..b18cb4485 100644 --- a/vendor/github.com/gookit/goutil/envutil/info.go +++ b/vendor/github.com/gookit/goutil/envutil/info.go @@ -36,33 +36,6 @@ func IsMSys() bool { return sysutil.IsMSys() } -var detectedWSL bool -var detectedWSLContents string - -// IsWSL system env -// https://github.com/Microsoft/WSL/issues/423#issuecomment-221627364 -func IsWSL() bool { - if !detectedWSL { - b := make([]byte, 1024) - // `cat /proc/version` - // on mac: - // !not the file! - // on linux(debian,ubuntu,alpine): - // Linux version 4.19.121-linuxkit (root@18b3f92ade35) (gcc version 9.2.0 (Alpine 9.2.0)) #1 SMP Thu Jan 21 15:36:34 UTC 2021 - // on win git bash, conEmu: - // MINGW64_NT-10.0-19042 version 3.1.7-340.x86_64 (@WIN-N0G619FD3UK) (gcc version 9.3.0 (GCC) ) 2020-10-23 13:08 UTC - // on WSL: - // Linux version 4.4.0-19041-Microsoft (Microsoft@Microsoft.com) (gcc version 5.4.0 (GCC) ) #488-Microsoft Mon Sep 01 13:43:00 PST 2020 - f, err := os.Open("/proc/version") - if err == nil { - _, _ = f.Read(b) // ignore error - f.Close() - detectedWSLContents = string(b) - } - detectedWSL = true - } - return strings.Contains(detectedWSLContents, "Microsoft") -} // IsTerminal isatty check // @@ -114,7 +87,7 @@ var specialColorTerms = map[string]bool{ // // Supported: // -// linux, mac, or windows's ConEmu, Cmder, putty, git-bash.exe +// linux, mac, or Windows's ConEmu, Cmder, putty, git-bash.exe // // Not support: // diff --git a/vendor/github.com/gookit/goutil/envutil/set.go b/vendor/github.com/gookit/goutil/envutil/set.go new file mode 100644 index 000000000..b317155f5 --- /dev/null +++ b/vendor/github.com/gookit/goutil/envutil/set.go @@ -0,0 +1,52 @@ +package envutil + +import ( + "os" + + "github.com/gookit/goutil/internal/comfunc" +) + +// SetEnvMap set multi ENV(string-map) to os +func SetEnvMap(mp map[string]string) { + for key, value := range mp { + _ = os.Setenv(key, value) + } +} + +// SetEnvs set multi k-v ENV pairs to os +func SetEnvs(kvPairs ...string) { + if len(kvPairs)%2 == 1 { + panic("envutil.SetEnvs: odd argument count") + } + + for i := 0; i < len(kvPairs); i += 2 { + _ = os.Setenv(kvPairs[i], kvPairs[i+1]) + } +} + +// UnsetEnvs from os +func UnsetEnvs(keys ...string) { + for _, key := range keys { + _ = os.Unsetenv(key) + } +} + +// LoadText parse multiline text to ENV. Can use to load .env file contents. +// +// Usage: +// envutil.LoadText(fsutil.ReadFile(".env")) +func LoadText(text string) { + envMp := SplitText2map(text) + for key, value := range envMp { + _ = os.Setenv(key, value) + } +} + +// LoadString set line to ENV. e.g.: KEY=VALUE +func LoadString(line string) bool { + k, v := comfunc.SplitLineToKv(line, "=") + if len(k) > 0 { + return os.Setenv(k, v) == nil + } + return false +} diff --git a/vendor/github.com/gookit/goutil/errorx/util.go b/vendor/github.com/gookit/goutil/errorx/util.go index 83acf8c75..7ac7766d0 100644 --- a/vendor/github.com/gookit/goutil/errorx/util.go +++ b/vendor/github.com/gookit/goutil/errorx/util.go @@ -6,34 +6,25 @@ import ( ) // E new a raw go error. alias of errors.New() -func E(msg string) error { - return errors.New(msg) -} +func E(msg string) error { return errors.New(msg) } // Err new a raw go error. alias of errors.New() -func Err(msg string) error { - return errors.New(msg) -} +func Err(msg string) error { return errors.New(msg) } // Raw new a raw go error. alias of errors.New() -func Raw(msg string) error { - return errors.New(msg) -} +func Raw(msg string) error { return errors.New(msg) } -// Ef new a raw go error. alias of errors.New() -func Ef(tpl string, vars ...any) error { - return fmt.Errorf(tpl, vars...) -} +// Ef new a raw go error. alias of fmt.Errorf +func Ef(tpl string, vars ...any) error { return fmt.Errorf(tpl, vars...) } -// Errf new a raw go error. alias of errors.New() -func Errf(tpl string, vars ...any) error { - return fmt.Errorf(tpl, vars...) -} +// Errf new a raw go error. alias of fmt.Errorf +func Errf(tpl string, vars ...any) error { return fmt.Errorf(tpl, vars...) } -// Rawf new a raw go error. alias of errors.New() -func Rawf(tpl string, vars ...any) error { - return fmt.Errorf(tpl, vars...) -} +// Rf new a raw go error. alias of fmt.Errorf +func Rf(tpl string, vs ...any) error { return fmt.Errorf(tpl, vs...) } + +// Rawf new a raw go error. alias of fmt.Errorf +func Rawf(tpl string, vs ...any) error { return fmt.Errorf(tpl, vs...) } /************************************************************* * helper func for error @@ -74,7 +65,7 @@ func IsErrorX(err error) (ok bool) { return } -// ToErrorX convert check +// ToErrorX convert check. like errors.As() func ToErrorX(err error) (ex *ErrorX, ok bool) { ex, ok = err.(*ErrorX) return diff --git a/vendor/github.com/gookit/goutil/fsutil/README.md b/vendor/github.com/gookit/goutil/fsutil/README.md index 0515b6d5e..a4f114635 100644 --- a/vendor/github.com/gookit/goutil/fsutil/README.md +++ b/vendor/github.com/gookit/goutil/fsutil/README.md @@ -14,6 +14,8 @@ go get github.com/gookit/goutil/fsutil ## Find files +More see [./finder](./finder) + ```go // find all files in dir fsutil.FindInDir("./", func(filePath string, de fs.DirEntry) error { diff --git a/vendor/github.com/gookit/goutil/fsutil/define.go b/vendor/github.com/gookit/goutil/fsutil/define.go index 09bb232c9..fc253da0f 100644 --- a/vendor/github.com/gookit/goutil/fsutil/define.go +++ b/vendor/github.com/gookit/goutil/fsutil/define.go @@ -12,16 +12,16 @@ const ( MimeSniffLen = 512 ) -// NameMatchFunc name match func, alias of comdef.StringMatchFunc +// NameMatchFunc name matches func, alias of comdef.StringMatchFunc type NameMatchFunc = comdef.StringMatchFunc -// PathMatchFunc path match func. alias of comdef.StringMatchFunc +// PathMatchFunc path matches func. alias of comdef.StringMatchFunc type PathMatchFunc = comdef.StringMatchFunc // Entry extends fs.DirEntry, add some useful methods type Entry interface { fs.DirEntry - // Path get file/dir full path. eg: "/path/to/file.go" + // Path gets file/dir full path. eg: "/path/to/file.go" Path() string // Info get file info. like fs.DirEntry.Info(), but will cache result. Info() (fs.FileInfo, error) @@ -42,12 +42,12 @@ func NewEntry(fPath string, ent fs.DirEntry) Entry { } } -// Path get full file/dir path. eg: "/path/to/file.go" +// Path gets full file/dir path. eg: "/path/to/file.go" func (e *entry) Path() string { return e.path } -// Info get file info, will cache result +// Info gets file info, will cache result func (e *entry) Info() (fs.FileInfo, error) { if e.stat == nil { e.stat, e.sErr = e.DirEntry.Info() @@ -63,7 +63,7 @@ func (e *entry) String() string { // FileInfo extends fs.FileInfo, add some useful methods type FileInfo interface { fs.FileInfo - // Path get file full path. eg: "/path/to/file.go" + // Path gets file full path. eg: "/path/to/file.go" Path() string } @@ -80,7 +80,7 @@ func NewFileInfo(fPath string, info fs.FileInfo) FileInfo { } } -// Path get file full path. eg: "/path/to/file.go" +// Path gets file full path. eg: "/path/to/file.go" func (fi *fileInfo) Path() string { return fi.fullPath } diff --git a/vendor/github.com/gookit/goutil/fsutil/find.go b/vendor/github.com/gookit/goutil/fsutil/find.go index 1f563f059..4dadc4307 100644 --- a/vendor/github.com/gookit/goutil/fsutil/find.go +++ b/vendor/github.com/gookit/goutil/fsutil/find.go @@ -11,19 +11,19 @@ import ( "github.com/gookit/goutil/strutil" ) -// FilePathInDirs get full file path in dirs. +// FilePathInDirs get full file path in dirs. return empty string if not found. // // Params: // - file: can be relative path, file name, full path. // - dirs: dir paths -func FilePathInDirs(file string, dirs ...string) string { - file = comfunc.ExpandHome(file) - if FileExists(file) { - return file +func FilePathInDirs(fPath string, dirs ...string) string { + fPath = comfunc.ExpandHome(fPath) + if FileExists(fPath) { + return fPath } for _, dirPath := range dirs { - fPath := JoinSubPaths(dirPath, file) + fPath := JoinSubPaths(dirPath, fPath) if FileExists(fPath) { return fPath } @@ -102,12 +102,12 @@ func SearchNameUpx(dirPath, name string) (string, bool) { // WalkDir walks the file tree rooted at root, calling fn for each file or // directory in the tree, including root. // -// TIP: will recursively find in sub dirs. +// TIP: will recursively found in sub dirs. func WalkDir(dir string, fn fs.WalkDirFunc) error { return filepath.WalkDir(dir, fn) } -// Glob find files by glob path pattern. alias of filepath.Glob() +// Glob finds files by glob path pattern. alias of filepath.Glob() // and support filter matched files by name. // // Usage: @@ -132,8 +132,6 @@ func Glob(pattern string, fls ...NameMatchFunc) []string { } // GlobWithFunc find files by glob path pattern, then handle matched file -// -// - TIP: will be not find in subdir. func GlobWithFunc(pattern string, fn func(filePath string) error) (err error) { files, err := filepath.Glob(pattern) if err != nil { @@ -207,7 +205,7 @@ func ApplyFilters(fPath string, ent fs.DirEntry, filters []FilterFunc) bool { // FindInDir code refer the go pkg: path/filepath.glob() // -// - TIP: will be not find in subdir. +// - TIP: will be not found in sub-dir. // // filters: return false will skip the file. func FindInDir(dir string, handleFn HandleFunc, filters ...FilterFunc) (e error) { @@ -235,9 +233,22 @@ func FindInDir(dir string, handleFn HandleFunc, filters ...FilterFunc) (e error) continue } - if err := handleFn(filePath, ent); err != nil { - return err + if err1 := handleFn(filePath, ent); err1 != nil { + return err1 } } return nil } + +// FileInDirs returns the first file path in the given dirs. +func FileInDirs(paths []string, names ...string) string { + for _, pathDir := range paths { + for _, name := range names { + file := pathDir + "/" + name + if IsFile(file) { + return file + } + } + } + return "" +} diff --git a/vendor/github.com/gookit/goutil/fsutil/fsutil.go b/vendor/github.com/gookit/goutil/fsutil/fsutil.go index a55e04485..4bd7f1b20 100644 --- a/vendor/github.com/gookit/goutil/fsutil/fsutil.go +++ b/vendor/github.com/gookit/goutil/fsutil/fsutil.go @@ -6,8 +6,8 @@ import ( "path/filepath" "strings" - "github.com/gookit/goutil/basefn" "github.com/gookit/goutil/internal/comfunc" + "github.com/gookit/goutil/x/basefn" ) // PathSep alias of os.PathSeparator diff --git a/vendor/github.com/gookit/goutil/fsutil/info.go b/vendor/github.com/gookit/goutil/fsutil/info.go index e7c23ee12..c843bc792 100644 --- a/vendor/github.com/gookit/goutil/fsutil/info.go +++ b/vendor/github.com/gookit/goutil/fsutil/info.go @@ -3,39 +3,66 @@ package fsutil import ( "os" "path/filepath" + "strings" "github.com/gookit/goutil/internal/comfunc" ) -// DirPath get dir path from filepath, without last name. -func DirPath(fpath string) string { return filepath.Dir(fpath) } +// DirPath get dir path from filepath, without a last name. +func DirPath(fPath string) string { return filepath.Dir(fPath) } -// Dir get dir path from filepath, without last name. -func Dir(fpath string) string { return filepath.Dir(fpath) } +// Dir get dir path from filepath, without a last name. +func Dir(fPath string) string { return filepath.Dir(fPath) } -// PathName get file/dir name from full path -func PathName(fpath string) string { return filepath.Base(fpath) } +// PathName get file/dir name from a full path +func PathName(fPath string) string { return filepath.Base(fPath) } + +// PathNoExt get path from full path, without ext. +// +// eg: path/to/main.go => "path/to/main" +func PathNoExt(fPath string) string { + ext := filepath.Ext(fPath) + if el := len(ext); el > 0 { + return fPath[:len(fPath)-el] + } + return fPath +} // Name get file/dir name from full path. // -// eg: path/to/main.go => main.go -func Name(fpath string) string { - if fpath == "" { +// eg: path/to/main.go => "main.go" +func Name(fPath string) string { + if fPath == "" { return "" } - return filepath.Base(fpath) + return filepath.Base(fPath) +} + +// NameNoExt get file name from a full path, without an ext. +// +// eg: path/to/main.go => "main" +func NameNoExt(fPath string) string { + if fPath == "" { + return "" + } + + fName := filepath.Base(fPath) + if pos := strings.LastIndexByte(fName, '.'); pos > 0 { + return fName[:pos] + } + return fName } // FileExt get filename ext. alias of filepath.Ext() // // eg: path/to/main.go => ".go" -func FileExt(fpath string) string { return filepath.Ext(fpath) } +func FileExt(fPath string) string { return filepath.Ext(fPath) } // Extname get filename ext. alias of filepath.Ext() // // eg: path/to/main.go => "go" -func Extname(fpath string) string { - if ext := filepath.Ext(fpath); len(ext) > 0 { +func Extname(fPath string) string { + if ext := filepath.Ext(fPath); len(ext) > 0 { return ext[1:] } return "" @@ -44,14 +71,14 @@ func Extname(fpath string) string { // Suffix get filename ext. alias of filepath.Ext() // // eg: path/to/main.go => ".go" -func Suffix(fpath string) string { return filepath.Ext(fpath) } +func Suffix(fPath string) string { return filepath.Ext(fPath) } -// Expand will parse first `~` as user home dir path. +// Expand will parse first `~` to user home dir path. func Expand(pathStr string) string { return comfunc.ExpandHome(pathStr) } -// ExpandPath will parse `~` as user home dir path. +// ExpandPath will parse `~` to user home dir path. func ExpandPath(pathStr string) string { return comfunc.ExpandHome(pathStr) } diff --git a/vendor/github.com/gookit/goutil/fsutil/mime.go b/vendor/github.com/gookit/goutil/fsutil/mime.go index b509960a8..0ea6855b7 100644 --- a/vendor/github.com/gookit/goutil/fsutil/mime.go +++ b/vendor/github.com/gookit/goutil/fsutil/mime.go @@ -6,7 +6,7 @@ import ( "os" ) -// DetectMime detect file mime type. alias of MimeType() +// DetectMime detect a file mime type. alias of MimeType() func DetectMime(path string) string { return MimeType(path) } diff --git a/vendor/github.com/gookit/goutil/fsutil/operate.go b/vendor/github.com/gookit/goutil/fsutil/operate.go index 2fe9877f9..1d68e4c53 100644 --- a/vendor/github.com/gookit/goutil/fsutil/operate.go +++ b/vendor/github.com/gookit/goutil/fsutil/operate.go @@ -9,7 +9,7 @@ import ( "path/filepath" "strings" - "github.com/gookit/goutil/basefn" + "github.com/gookit/goutil/x/basefn" ) // Mkdir alias of os.MkdirAll() @@ -17,7 +17,7 @@ func Mkdir(dirPath string, perm os.FileMode) error { return os.MkdirAll(dirPath, perm) } -// MkDirs batch make multi dirs at once +// MkDirs batch makes multi dirs at once func MkDirs(perm os.FileMode, dirPaths ...string) error { for _, dirPath := range dirPaths { if err := os.MkdirAll(dirPath, perm); err != nil { @@ -27,7 +27,7 @@ func MkDirs(perm os.FileMode, dirPaths ...string) error { return nil } -// MkSubDirs batch make multi sub-dirs at once +// MkSubDirs batch makes multi sub-dirs at once func MkSubDirs(perm os.FileMode, parentDir string, subDirs ...string) error { for _, dirName := range subDirs { dirPath := parentDir + "/" + dirName @@ -38,7 +38,7 @@ func MkSubDirs(perm os.FileMode, parentDir string, subDirs ...string) error { return nil } -// MkParentDir quick create parent dir for given path. +// MkParentDir quickly create parent dir for a given path. func MkParentDir(fpath string) error { dirPath := filepath.Dir(fpath) if !IsDir(dirPath) { @@ -232,10 +232,10 @@ func SafeRemoveAll(path string) { _ = os.RemoveAll(path) } -// RmIfExist removes the named file or (empty) directory on exists. +// RmIfExist removes the named file or (empty) directory on existing. func RmIfExist(fPath string) error { return DeleteIfExist(fPath) } -// DeleteIfExist removes the named file or (empty) directory on exists. +// DeleteIfExist removes the named file or (empty) directory on existing. func DeleteIfExist(fPath string) error { if PathExists(fPath) { return os.Remove(fPath) @@ -243,10 +243,10 @@ func DeleteIfExist(fPath string) error { return nil } -// RmFileIfExist removes the named file on exists. +// RmFileIfExist removes the named file on existing. func RmFileIfExist(fPath string) error { return DeleteIfFileExist(fPath) } -// DeleteIfFileExist removes the named file on exists. +// DeleteIfFileExist removes the named file on existing. func DeleteIfFileExist(fPath string) error { if IsFile(fPath) { return os.Remove(fPath) diff --git a/vendor/github.com/gookit/goutil/fsutil/opread.go b/vendor/github.com/gookit/goutil/fsutil/opread.go index 7a8dc0c7e..13babadff 100644 --- a/vendor/github.com/gookit/goutil/fsutil/opread.go +++ b/vendor/github.com/gookit/goutil/fsutil/opread.go @@ -7,7 +7,7 @@ import ( "os" "text/scanner" - "github.com/gookit/goutil/basefn" + "github.com/gookit/goutil/x/basefn" ) // NewIOReader instance by input file path or io.Reader diff --git a/vendor/github.com/gookit/goutil/fsutil/opwrite.go b/vendor/github.com/gookit/goutil/fsutil/opwrite.go index 37b3ab663..305d8ce8b 100644 --- a/vendor/github.com/gookit/goutil/fsutil/opwrite.go +++ b/vendor/github.com/gookit/goutil/fsutil/opwrite.go @@ -4,7 +4,7 @@ import ( "io" "os" - "github.com/gookit/goutil/basefn" + "github.com/gookit/goutil/x/basefn" ) // ************************************************************ @@ -74,9 +74,14 @@ func SaveFile(filePath string, data any, optFns ...OpenOptionFunc) error { return WriteFile(filePath, data, opt.Perm, opt.Flag) } -// PutContents create file and write contents to file at once. +// WriteData Quick write any data to file, alias of PutContents +func WriteData(filePath string, data any, fileFlag ...int) (int, error) { + return PutContents(filePath, data, fileFlag...) +} + +// PutContents create file and write contents to file at once. Will auto create dir // -// data type allow: string, []byte, io.Reader. will auto create dir. +// data type allows: string, []byte, io.Reader // // Tip: file flag default is FsCWTFlags (override write) // @@ -92,9 +97,9 @@ func PutContents(filePath string, data any, fileFlag ...int) (int, error) { return WriteOSFile(f, data) } -// WriteFile create file and write contents to file, can set perm for file. +// WriteFile create file and write contents to file, can set perm for a file. // -// data type allow: string, []byte, io.Reader +// data type allows: string, []byte, io.Reader // // Tip: file flag default is FsCWTFlags (override write) // @@ -114,7 +119,7 @@ func WriteFile(filePath string, data any, perm os.FileMode, fileFlag ...int) err // WriteOSFile write data to give os.File, then close file. // -// data type allow: string, []byte, io.Reader +// data type allows: string, []byte, io.Reader func WriteOSFile(f *os.File, data any) (n int, err error) { switch typData := data.(type) { case []byte: diff --git a/vendor/github.com/gookit/goutil/func.go b/vendor/github.com/gookit/goutil/func.go index a68a41881..4f30222e2 100644 --- a/vendor/github.com/gookit/goutil/func.go +++ b/vendor/github.com/gookit/goutil/func.go @@ -23,6 +23,14 @@ func CallOn(cond bool, fn ErrFunc) error { return nil } +// IfElseFn call okFunc() on condition is true, else call elseFn() +func IfElseFn(cond bool, okFn, elseFn ErrFunc) error { + if cond { + return okFn() + } + return elseFn() +} + // CallOrElse call okFunc() on condition is true, else call elseFn() func CallOrElse(cond bool, okFn, elseFn ErrFunc) error { if cond { @@ -43,7 +51,7 @@ func SafeRun(fn func()) (err error) { } }() fn() - return nil + return err } // SafeRunWithError sync run a func with error. diff --git a/vendor/github.com/gookit/goutil/goutil.go b/vendor/github.com/gookit/goutil/goutil.go index a6d327b34..c0e92e6a5 100644 --- a/vendor/github.com/gookit/goutil/goutil.go +++ b/vendor/github.com/gookit/goutil/goutil.go @@ -1,13 +1,16 @@ -// Package goutil 💪 Useful utils for Go: int, string, array/slice, map, error, time, format, CLI, ENV, filesystem, +// Package goutil 💪 Useful utils for Go: byte, int, string, array/slice, map, struct, reflect, error, time, format, CLI, ENV, filesystem, // system, testing, debug and more. package goutil import ( "fmt" + "reflect" - "github.com/gookit/goutil/basefn" - "github.com/gookit/goutil/goinfo" + "github.com/gookit/goutil/internal/checkfn" + "github.com/gookit/goutil/reflects" "github.com/gookit/goutil/structs" + "github.com/gookit/goutil/x/basefn" + "github.com/gookit/goutil/x/goinfo" ) // Value alias of structs.Value @@ -18,7 +21,7 @@ func Panicf(format string, v ...any) { panic(fmt.Sprintf(format, v...)) } -// PanicIf if cond = true, panics with error message +// PanicIf if cond = true, panics with an error message func PanicIf(cond bool, fmtAndArgs ...any) { basefn.PanicIf(cond, fmtAndArgs...) } @@ -74,21 +77,6 @@ func Must[T any](v T, err error) T { return v } -// FuncName get func name -func FuncName(f any) string { - return goinfo.FuncName(f) -} - -// PkgName get current package name. alias of goinfo.PkgName() -// -// Usage: -// -// funcName := goutil.FuncName(fn) -// pgkName := goutil.PkgName(funcName) -func PkgName(funcName string) string { - return goinfo.PkgName(funcName) -} - // ErrOnFail return input error on cond is false, otherwise return nil func ErrOnFail(cond bool, err error) error { return OrError(cond, err) @@ -102,7 +90,7 @@ func OrError(cond bool, err error) error { return nil } -// OrValue get +// OrValue get. like: if cond { okVal } else { elVal } func OrValue[T any](cond bool, okVal, elVal T) T { if cond { return okVal @@ -117,3 +105,105 @@ func OrReturn[T any](cond bool, okFn, elseFn func() T) T { } return elseFn() } + +// +// ------------------------- check functions ------------------------- +// + +// IsNil value check +func IsNil(v any) bool { + if v == nil { + return true + } + return reflects.IsNil(reflect.ValueOf(v)) +} + +// IsZero value check, alias of the IsEmpty() +var IsZero = IsEmpty + +// IsEmpty value check +func IsEmpty(v any) bool { + if v == nil { + return true + } + return reflects.IsEmpty(reflect.ValueOf(v)) +} + +// IsZeroReal Alias of the IsEmptyReal() +var IsZeroReal = IsEmptyReal + +// IsEmptyReal checks for empty given value and also real empty value if the passed value is a pointer +func IsEmptyReal(v any) bool { + if v == nil { + return true + } + return reflects.IsEmptyReal(reflect.ValueOf(v)) +} + +// IsFunc value +func IsFunc(val any) bool { + if val == nil { + return false + } + return reflect.TypeOf(val).Kind() == reflect.Func +} + +// IsEqual determines if two objects are considered equal. +// +// TIP: cannot compare a function type +func IsEqual(src, dst any) bool { + if src == nil || dst == nil { + return src == dst + } + + // cannot compare a function type + if IsFunc(src) || IsFunc(dst) { + return false + } + return reflects.IsEqual(src, dst) +} + +// Contains try loop over the data check if the data includes the element. +// alias of the IsContains +// +// TIP: only support types: string, map, array, slice +// +// map - check key exists +// string - check sub-string exists +// array,slice - check sub-element exists +func Contains(data, elem any) bool { + _, found := checkfn.Contains(data, elem) + return found +} + +// IsContains try loop over the data check if the data includes the element. +// +// TIP: only support types: string, map, array, slice +// +// map - check key exists +// string - check sub-string exists +// array,slice - check sub-element exists +func IsContains(data, elem any) bool { + _, found := checkfn.Contains(data, elem) + return found +} + +// +// ------------------------- goinfo functions ------------------------- +// + +// FuncName get func name +func FuncName(f any) string { + return goinfo.FuncName(f) +} + +// PkgName get the current package name. alias of goinfo.PkgName() +// +// Usage: +// +// funcName := goutil.FuncName(fn) +// pgkName := goutil.PkgName(funcName) +func PkgName(funcName string) string { + return goinfo.PkgName(funcName) +} + diff --git a/vendor/github.com/gookit/goutil/group.go b/vendor/github.com/gookit/goutil/group.go index 0038d3d31..c2b2feecf 100644 --- a/vendor/github.com/gookit/goutil/group.go +++ b/vendor/github.com/gookit/goutil/group.go @@ -11,12 +11,12 @@ import ( // are part of the same overall task. type ErrGroup = syncs.ErrGroup -// NewCtxErrGroup instance +// NewCtxErrGroup instance. use for batch run tasks, can with context. func NewCtxErrGroup(ctx context.Context, limit ...int) (*ErrGroup, context.Context) { return syncs.NewCtxErrGroup(ctx, limit...) } -// NewErrGroup instance +// NewErrGroup instance. use for batch run tasks func NewErrGroup(limit ...int) *ErrGroup { return syncs.NewErrGroup(limit...) } @@ -48,7 +48,6 @@ func (p *QuickRun) Add(fns ...RunFn) *QuickRun { func (p *QuickRun) Run() error { for i, fn := range p.fns { p.ctx.Set("index", i) - if err := fn(p.ctx); err != nil { return err } diff --git a/vendor/github.com/gookit/goutil/internal/checkfn/check.go b/vendor/github.com/gookit/goutil/internal/checkfn/check.go index cb0cd5957..e85f23154 100644 --- a/vendor/github.com/gookit/goutil/internal/checkfn/check.go +++ b/vendor/github.com/gookit/goutil/internal/checkfn/check.go @@ -123,3 +123,8 @@ var numReg = regexp.MustCompile(`^[-+]?\d*\.?\d+$`) // IsNumeric returns true if the given string is a numeric, otherwise false. func IsNumeric(s string) bool { return numReg.MatchString(s) } + +// IsHttpURL check input is http/https url +func IsHttpURL(s string) bool { + return strings.HasPrefix(s, "http://") || strings.HasPrefix(s, "https://") +} diff --git a/vendor/github.com/gookit/goutil/internal/checkfn/sysenv.go b/vendor/github.com/gookit/goutil/internal/checkfn/sysenv.go new file mode 100644 index 000000000..16cbe02e5 --- /dev/null +++ b/vendor/github.com/gookit/goutil/internal/checkfn/sysenv.go @@ -0,0 +1,40 @@ +package checkfn + +import ( + "os" + "strings" +) + +var detectedWSL bool +var detectedWSLContents string + +// IsWSL system env +// https://github.com/Microsoft/WSL/issues/423#issuecomment-221627364 +func IsWSL() bool { + // ENV: + // WSL_DISTRO_NAME=Debian + if len(os.Getenv("WSL_DISTRO_NAME")) > 0 { + return true + } + + if !detectedWSL { + b := make([]byte, 1024) + // `cat /proc/version` + // on mac: + // !not the file! + // on linux(debian,ubuntu,alpine): + // Linux version 4.19.121-linuxkit (root@18b3f92ade35) (gcc version 9.2.0 (Alpine 9.2.0)) #1 SMP Thu Jan 21 15:36:34 UTC 2021 + // on win git bash, conEmu: + // MINGW64_NT-10.0-19042 version 3.1.7-340.x86_64 (@WIN-N0G619FD3UK) (gcc version 9.3.0 (GCC) ) 2020-10-23 13:08 UTC + // on WSL: + // Linux version 4.4.0-19041-Microsoft (Microsoft@Microsoft.com) (gcc version 5.4.0 (GCC) ) #488-Microsoft Mon Sep 01 13:43:00 PST 2020 + f, err := os.Open("/proc/version") + if err == nil { + _, _ = f.Read(b) // ignore error + f.Close() + detectedWSLContents = string(b) + } + detectedWSL = true + } + return strings.Contains(detectedWSLContents, "Microsoft") +} diff --git a/vendor/github.com/gookit/goutil/internal/comfunc/README.md b/vendor/github.com/gookit/goutil/internal/comfunc/README.md index 3057ef9e9..d71a48c4b 100644 --- a/vendor/github.com/gookit/goutil/internal/comfunc/README.md +++ b/vendor/github.com/gookit/goutil/internal/comfunc/README.md @@ -1,3 +1,3 @@ -# common func for internal use +# Common func for internal use - don't depend on other external packages \ No newline at end of file diff --git a/vendor/github.com/gookit/goutil/internal/comfunc/cli_func.go b/vendor/github.com/gookit/goutil/internal/comfunc/cli_func.go new file mode 100644 index 000000000..bded2ced0 --- /dev/null +++ b/vendor/github.com/gookit/goutil/internal/comfunc/cli_func.go @@ -0,0 +1,93 @@ +package comfunc + +import ( + "fmt" + "strings" +) + +// Cmdline build +func Cmdline(args []string, binName ...string) string { + b := new(strings.Builder) + + if len(binName) > 0 { + b.WriteString(binName[0]) + b.WriteByte(' ') + } + + for i, a := range args { + if i > 0 { + b.WriteByte(' ') + } + + if strings.ContainsRune(a, '"') { + b.WriteString(fmt.Sprintf(`'%s'`, a)) + } else if a == "" || strings.ContainsRune(a, '\'') || strings.ContainsRune(a, ' ') { + b.WriteString(fmt.Sprintf(`"%s"`, a)) + } else { + b.WriteString(a) + } + } + return b.String() +} + +// ShellQuote quote a string on contains ', ", SPACE. refer strconv.Quote() +func ShellQuote(a string) string { + if a == "" { + return `""` + } + + // use quote char + var quote byte + + // has double quote + if pos := strings.IndexByte(a, '"'); pos > -1 { + if !checkNeedQuote(a, pos, '"') { + return a + } + + quote = '\'' + } else if pos := strings.IndexByte(a, '\''); pos > -1 { + // single quote + if !checkNeedQuote(a, pos, '\'') { + return a + } + quote = '"' + } else if strings.IndexByte(a, ' ') > -1 { + quote = '"' + } + + // no quote char OR not need quote + if quote == 0 { + return a + } + return fmt.Sprintf("%c%s%c", quote, a, quote) +} + +func checkNeedQuote(a string, pos int, char byte) bool { + // end with char. eg: " + lastIsQ := a[len(a)-1] == char + + // start with char. eg: " + if pos == 0 { + if lastIsQ { + return false + } + + if pos1 := strings.IndexByte(a[pos+1:], char); pos1 > -1 { + // eg: `"one two" three four` + lastS := a[pos1+pos+1:] + if !strings.ContainsRune(lastS, ' ') { + return false + } + } + } else { + startS := a[:pos] + + // eg: `--one="two three"` + if lastIsQ && strings.IndexByte(startS, ' ') == -1 { + return false + } + } + + return true +} diff --git a/vendor/github.com/gookit/goutil/internal/comfunc/cmdline.go b/vendor/github.com/gookit/goutil/internal/comfunc/cmdline.go deleted file mode 100644 index b9b474a9d..000000000 --- a/vendor/github.com/gookit/goutil/internal/comfunc/cmdline.go +++ /dev/null @@ -1,31 +0,0 @@ -package comfunc - -import ( - "fmt" - "strings" -) - -// Cmdline build -func Cmdline(args []string, binName ...string) string { - b := new(strings.Builder) - - if len(binName) > 0 { - b.WriteString(binName[0]) - b.WriteByte(' ') - } - - for i, a := range args { - if i > 0 { - b.WriteByte(' ') - } - - if strings.ContainsRune(a, '"') { - b.WriteString(fmt.Sprintf(`'%s'`, a)) - } else if a == "" || strings.ContainsRune(a, '\'') || strings.ContainsRune(a, ' ') { - b.WriteString(fmt.Sprintf(`"%s"`, a)) - } else { - b.WriteString(a) - } - } - return b.String() -} diff --git a/vendor/github.com/gookit/goutil/internal/comfunc/comfunc.go b/vendor/github.com/gookit/goutil/internal/comfunc/comfunc.go index 5b2ad153a..c02005cd2 100644 --- a/vendor/github.com/gookit/goutil/internal/comfunc/comfunc.go +++ b/vendor/github.com/gookit/goutil/internal/comfunc/comfunc.go @@ -1,12 +1,8 @@ package comfunc import ( - "fmt" "os" - "regexp" - "strconv" "strings" - "time" ) // Environ like os.Environ, but will returns key-value map[string]string data. @@ -24,78 +20,3 @@ func Environ() map[string]string { } return envMap } - -var ( - // TIP: extend unit d,w. eg: "1d", "2w" - // time.ParseDuration() is max support hour "h". - durStrReg = regexp.MustCompile(`^(-?\d+)(ns|us|µs|ms|s|m|h|d|w)$`) - - // match long duration string. eg: "1hour", "2hours", "3minutes", "4mins", "5days", "1weeks", "1month" - // time.ParseDuration() is not supported. - durStrRegL = regexp.MustCompile(`^(-?\d+)([hdmsw][a-zA-Z]{2,8})$`) -) - -// IsDuration check the string is a duration string. -func IsDuration(s string) bool { - if s == "0" || durStrReg.MatchString(s) { - return true - } - return durStrRegL.MatchString(s) -} - -// ToDuration parses a duration string. such as "300ms", "-1.5h" or "2h45m". -// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". -// -// Diff of time.ParseDuration: -// - support extend unit d, w at the end of string. such as "1d", "2w". -// - support long string unit at end. such as "1hour", "2hours", "3minutes", "4mins", "5days", "1weeks". -// -// If the string is not a valid duration string, it will return an error. -func ToDuration(s string) (time.Duration, error) { - ln := len(s) - if ln == 0 { - return 0, fmt.Errorf("empty duration string") - } - - s = strings.ToLower(s) - if s == "0" { - return 0, nil - } - - // extend unit d,w, time.ParseDuration() is not supported. eg: "1d", "2w" - if lastUnit := s[ln-1]; lastUnit == 'd' { - s = s + "ay" - } else if lastUnit == 'w' { - s = s + "eek" - } - - // long unit, time.ParseDuration() is not supported. eg: "-3sec" => [3sec -3 sec] - ss := durStrRegL.FindStringSubmatch(s) - if len(ss) == 3 { - num, unit := ss[1], ss[2] - - // convert to short unit - switch unit { - case "month", "months": - // max unit is hour, so need convert by 24 * 30 * n - n, _ := strconv.Atoi(num) - s = strconv.Itoa(n*24*30) + "h" - case "week", "weeks": - // max unit is hour, so need convert by 24 * 7 * n - n, _ := strconv.Atoi(num) - s = strconv.Itoa(n*24*7) + "h" - case "day", "days": - // max unit is hour, so need convert by 24 * n - n, _ := strconv.Atoi(num) - s = strconv.Itoa(n*24) + "h" - case "hour", "hours": - s = num + "h" - case "min", "mins", "minute", "minutes": - s = num + "m" - case "sec", "secs", "second", "seconds": - s = num + "s" - } - } - - return time.ParseDuration(s) -} diff --git a/vendor/github.com/gookit/goutil/internal/comfunc/str_func.go b/vendor/github.com/gookit/goutil/internal/comfunc/str_func.go new file mode 100644 index 000000000..240328080 --- /dev/null +++ b/vendor/github.com/gookit/goutil/internal/comfunc/str_func.go @@ -0,0 +1,95 @@ +package comfunc + +import ( + "fmt" + "strings" +) + +var commentsPrefixes = []string{"#", ";", "//"} + +// ParseEnvLineOption parse env line options +type ParseEnvLineOption struct { + // NotInlineComments dont parse inline comments. + // - default: false. will parse inline comments + NotInlineComments bool + // SkipOnErrorLine skip error line, continue parse next line + // - False: return error, clear parsed map + SkipOnErrorLine bool +} + +// ParseEnvLines parse simple multiline k-v string to a string-map. +// Can use to parse simple INI or DOTENV file contents. +// +// NOTE: +// +// - It's like INI/ENV format contents. +// - Support comments line starts with: "#", ";", "//" +// - Support inline comments split with: " #" eg: name=tom # a comments +// - DON'T support submap parse. +func ParseEnvLines(text string, opt ParseEnvLineOption) (mp map[string]string, err error) { + lines := strings.Split(text, "\n") + ln := len(lines) + if ln == 0 { + return + } + + strMap := make(map[string]string, ln) + + for _, line := range lines { + if line = strings.TrimSpace(line); line == "" { + continue + } + + // skip comments line + if line[0] == '#' || line[0] == ';' || strings.HasPrefix(line, "//") { + continue + } + + // invalid line + if strings.IndexByte(line, '=') < 1 { + if opt.SkipOnErrorLine { + continue + } + + strMap = nil + err = fmt.Errorf("invalid line contents: must match `KEY=VAL`(line: %s)", line) + return + } + + key, value := SplitLineToKv(line, "=") + + // check and remove inline comments + if !opt.NotInlineComments { + if pos := strings.Index(value, " #"); pos > 0 { + value = strings.TrimRight(value[0:pos], " \t") + } + } + + strMap[key] = value + } + + return strMap, nil +} + +// SplitLineToKv parse string line to k-v. eg: +// +// 'DEBUG=true' => ['DEBUG', 'true'] +// +// NOTE: line must contain '=', allow: 'ENV_KEY=' +func SplitLineToKv(line, sep string) (string, string) { + nodes := strings.SplitN(line, sep, 2) + envKey := strings.TrimSpace(nodes[0]) + + // key cannot be empty + if envKey == "" { + return "", "" + } + + if len(nodes) < 2 { + if strings.Contains(line, sep) { + return envKey, "" + } + return "", "" + } + return envKey, strings.TrimSpace(nodes[1]) +} diff --git a/vendor/github.com/gookit/goutil/internal/comfunc/sysfunc.go b/vendor/github.com/gookit/goutil/internal/comfunc/sysfunc.go index aef90e947..4f43d3ad1 100644 --- a/vendor/github.com/gookit/goutil/internal/comfunc/sysfunc.go +++ b/vendor/github.com/gookit/goutil/internal/comfunc/sysfunc.go @@ -1,7 +1,6 @@ package comfunc import ( - "bytes" "os" "os/exec" "path/filepath" @@ -51,6 +50,11 @@ func ExecCmd(binName string, args []string, workDir ...string) (string, error) { return string(bs), err } +var ( + cmdList = []string{"cmd", "cmd.exe"} + pwshList = []string{"powershell", "powershell.exe", "pwsh", "pwsh.exe"} +) + // ShellExec exec command by shell // cmdLine e.g. "ls -al" func ShellExec(cmdLine string, shells ...string) (string, error) { @@ -60,48 +64,63 @@ func ShellExec(cmdLine string, shells ...string) (string, error) { shell = shells[0] } - var out bytes.Buffer - cmd := exec.Command(shell, "-c", cmdLine) - cmd.Stdout = &out - - if err := cmd.Run(); err != nil { - return "", err - } - return out.String(), nil + bs, err := cmd.Output() + return string(bs), err } -// curShell cache -var curShell string +// curShellCache value +var curShellCache string // CurrentShell get current used shell env file. // -// eg "/bin/zsh" "/bin/bash". -// if onlyName=true, will return "zsh", "bash" -func CurrentShell(onlyName bool) (binPath string) { +// return like: "/bin/zsh" "/bin/bash". if onlyName=true, will return "zsh", "bash" +func CurrentShell(onlyName bool, fallbackShell ...string) (binPath string) { var err error - if curShell == "" { - binPath = os.Getenv("SHELL") + + fbShell := "" + if len(fallbackShell) > 0 { + fbShell = fallbackShell[0] + } + + if curShellCache == "" { + // 检查父进程名称 + parentProcess := os.Getenv("GOPROCESS") + if parentProcess != "" { + return parentProcess + } + + binPath = os.Getenv("SHELL") // 适用于 Unix-like 系统 if len(binPath) == 0 { + // TODO check on Windows binPath, err = ShellExec("echo $SHELL") if err != nil { - return "" + return fbShell } } binPath = strings.TrimSpace(binPath) // cache result - curShell = binPath + curShellCache = binPath } else { - binPath = curShell + binPath = curShellCache } if onlyName && len(binPath) > 0 { binPath = filepath.Base(binPath) + } else if len(binPath) == 0 { + binPath = fbShell } return } +func checkWinCurrentShell() string { + // 在 Windows 上,可以检查 COMSPEC 环境变量 + comSpec := os.Getenv("COMSPEC") + // 没法检查 pwsh, 返回的还是 cmd + return comSpec +} + // HasShellEnv has shell env check. // // Usage: diff --git a/vendor/github.com/gookit/goutil/internal/comfunc/time_func.go b/vendor/github.com/gookit/goutil/internal/comfunc/time_func.go new file mode 100644 index 000000000..bcfbc158c --- /dev/null +++ b/vendor/github.com/gookit/goutil/internal/comfunc/time_func.go @@ -0,0 +1,149 @@ +package comfunc + +import ( + "fmt" + "math" + "regexp" + "strconv" + "strings" + "time" +) + +var ( + // check is duration string. TIP: extend unit d,w. eg: "1d", "2w" + // + // time.ParseDuration() is max support hour "h". + durStrReg = regexp.MustCompile(`^-?([0-9]+(?:\.[0-9]*)?(ns|us|µs|ms|s|m|h|d|w))+$`) + + // check long duration string. 验证整体格式是否符合 + // + // eg: "1hour", "2hours", "3minutes", "4mins", "5days", "1weeks", "1month" + // + // time.ParseDuration() is not support long unit. + durStrRegL = regexp.MustCompile(`^-?([0-9]+(?:\.[0-9]*)?[nuµsmhdw][a-zA-Z]{0,8})+$`) + // use for parse duration string. see ToDuration() + // + // NOTE: 解析时,不能加最后的 `+` 会导致只匹配了最后一组 时间单位 + durStrRegL2 = regexp.MustCompile(`-?([0-9]+(?:\.[0-9]*)?)([nuµsmhdw][a-z]{0,8})`) +) + +// IsDuration check the string is a duration string. +func IsDuration(s string) bool { + if s == "0" || durStrReg.MatchString(s) { + return true + } + return durStrRegL.MatchString(s) +} + +// ToDuration parses a duration string. such as "300ms", "-1.5h" or "2h45m". +// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". +// +// Diff of time.ParseDuration: +// - support extends unit d, w at the end of string. such as "1d", "2w". +// - support extends unit: month, week, day +// - support long string unit at the end. such as "1hour", "2hours", "3minutes", "4mins", "5days", "1weeks". +// +// If the string is not a valid duration string, it will return an error. +func ToDuration(s string) (time.Duration, error) { + ln := len(s) + if ln == 0 { + return 0, fmt.Errorf("empty duration string") + } + + s = strings.ToLower(s) + if s == "0" { + return 0, nil + } + + // check duration string is valid + if !durStrRegL.MatchString(s) { + return 0, fmt.Errorf("invalid duration string: %s", s) + } + + // if ln < 4 AND end != d|w, directly call time.ParseDuration() + if ln < 4 && s[ln-1] != 'd' && s[ln-1] != 'w' { + return time.ParseDuration(s) + } + + // time.ParseDuration() is not support long unit. + ssList := durStrRegL2.FindAllStringSubmatch(s, -1) + // fmt.Println(ssList) + bts := make([]byte, 0, ln) + if s[0] == '-' { + bts = append(bts, '-') + } + + // only one element. eg: "1day" + if len(ssList) == 1 { + bts = parseLongUnit(ssList[0], bts) + } else { + // more than one element. eg: "1day2hour3min" + for _, ss := range ssList { + if len(ss) == 3 { + bts = parseLongUnit(ss, bts) + } + } + } + + return time.ParseDuration(string(bts)) +} + +// convert to short unit +func parseLongUnit(ss []string, bts []byte) []byte { + // eg: "3sec" -> ss=[3sec, -3, sec] + num, unit := ss[1], ss[2] + switch unit { + case "month", "months": + // time lib max unit is hour, so need convert by 24 * 30*n + bts = appendNumToBytes(bts, num, 24*30) + bts = append(bts, 'h') + case "w", "week", "weeks": + // time lib max unit is hour, so need convert by 24 * 7*n + bts = appendNumToBytes(bts, num, 24*7) + bts = append(bts, 'h') + case "d", "day", "days": + // time lib max unit is hour, so need convert by 24*n + bts = appendNumToBytes(bts, num, 24) + bts = append(bts, 'h') + case "hour", "hours": + bts = append(bts, num...) + bts = append(bts, 'h') + case "min", "mins", "minute", "minutes": + bts = append(bts, num...) + bts = append(bts, 'm') + case "sec", "secs", "second", "seconds": + bts = append(bts, num...) + bts = append(bts, 's') + default: + first := ss[0] + + // '-' has been added on ToDuration() + if first[0] == '-' { + bts = append(bts, first[1:]...) + } else { + bts = append(bts, first...) + } + } + + return bts +} + +func appendNumToBytes(bts []byte, num string, multiple int) []byte { + if strings.ContainsRune(num, '.') { + f, _ := strconv.ParseFloat(num, 64) // is float number + val := f * float64(multiple) + + // 使用 Float 保留两位小数 -> 会始终有两位小数,即使是N.00 + // bts = strconv.AppendFloat(bts, val, 'f', 2, 64) + + // 四舍五入到两位小数 + rounded := math.Round(val*100) / 100 + // 使用 AppendFloat 自动去除末尾的 .0 或 .00 + bts = strconv.AppendFloat(bts, rounded, 'f', -1, 64) + } else { + n, _ := strconv.Atoi(num) + bts = strconv.AppendInt(bts, int64(n*multiple), 10) + } + + return bts +} diff --git a/vendor/github.com/gookit/goutil/internal/varexpr/varexpr.go b/vendor/github.com/gookit/goutil/internal/varexpr/varexpr.go index bc701008d..b78a256b5 100644 --- a/vendor/github.com/gookit/goutil/internal/varexpr/varexpr.go +++ b/vendor/github.com/gookit/goutil/internal/varexpr/varexpr.go @@ -62,11 +62,15 @@ var std = New() // ${var_name} Only var name // ${var_name | default} With default value // ${var_name | ?error} With error on value is empty. +// +// see Parser.Parse func Parse(val string) (string, error) { return std.Parse(val) } // SafeParse parse ENV var value from input string, support default value. +// +// see Parser.Parse func SafeParse(val string) string { s, _ := std.Parse(val) return s @@ -100,6 +104,7 @@ func New(optFns ...ParseOptFn) *Parser { // ${var_name} Only var name // ${var_name | default} With default value // ${var_name | ?error} With error on value is empty. +// ${VAR_NAME1}/path/${VAR_NAME2} Allow multi var name. func (p *Parser) Parse(val string) (newVal string, err error) { if p.Regexp == nil { p.useDefaultRegex() diff --git a/vendor/github.com/gookit/goutil/jsonutil/encoding.go b/vendor/github.com/gookit/goutil/jsonutil/encoding.go index da05a3246..b921a4215 100644 --- a/vendor/github.com/gookit/goutil/jsonutil/encoding.go +++ b/vendor/github.com/gookit/goutil/jsonutil/encoding.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "io" + "os" ) // MustString encode data to json string, will panic on error @@ -62,3 +63,13 @@ func DecodeString(str string, ptr any) error { func DecodeReader(r io.Reader, ptr any) error { return json.NewDecoder(r).Decode(ptr) } + +// DecodeFile decode JSON from file, bind data to ptr. +func DecodeFile(file string, ptr any) error { + bs, err := os.ReadFile(file) + if err != nil { + return err + } + + return json.Unmarshal(bs, ptr) +} \ No newline at end of file diff --git a/vendor/github.com/gookit/goutil/maputil/convert.go b/vendor/github.com/gookit/goutil/maputil/convert.go index 13d4b0cf3..fd0a17d4a 100644 --- a/vendor/github.com/gookit/goutil/maputil/convert.go +++ b/vendor/github.com/gookit/goutil/maputil/convert.go @@ -11,8 +11,20 @@ import ( "github.com/gookit/goutil/strutil" ) +// alias functions +var ( + // ToStrMap convert map[string]any to map[string]string + ToStrMap = ToStringMap + // ToL2StrMap convert map[string]any to map[string]map[string]string + ToL2StrMap = ToL2StringMap +) + // KeyToLower convert keys to lower case. func KeyToLower(src map[string]string) map[string]string { + if len(src) == 0 { + return src + } + newMp := make(map[string]string, len(src)) for k, v := range src { k = strings.ToLower(k) @@ -21,7 +33,22 @@ func KeyToLower(src map[string]string) map[string]string { return newMp } -// ToStringMap convert map[string]any to map[string]string +// AnyToStrMap try convert any(map[string]any, map[string]string) to map[string]string +func AnyToStrMap(src any) map[string]string { + if src == nil { + return nil + } + + if m, ok := src.(map[string]string); ok { + return m + } + if m, ok := src.(map[string]any); ok { + return ToStringMap(m) + } + return nil +} + +// ToStringMap simple convert map[string]any to map[string]string func ToStringMap(src map[string]any) map[string]string { strMp := make(map[string]string, len(src)) for k, v := range src { @@ -30,7 +57,25 @@ func ToStringMap(src map[string]any) map[string]string { return strMp } -// CombineToSMap combine two string-slice to SMap(map[string]string) +// ToL2StringMap convert map[string]any to map[string]map[string]string +func ToL2StringMap(groupsMap map[string]any) map[string]map[string]string { + if len(groupsMap) == 0 { + return nil + } + + l2sMap := make(map[string]map[string]string, len(groupsMap)) + + for k, v := range groupsMap { + if mp, ok := v.(map[string]any); ok { + l2sMap[k] = ToStringMap(mp) + } else if smp, ok := v.(map[string]string); ok { + l2sMap[k] = smp + } + } + return l2sMap +} + +// CombineToSMap combine two string-slices to SMap(map[string]string) func CombineToSMap(keys, values []string) SMap { return arrutil.CombineToSMap(keys, values) } @@ -40,6 +85,54 @@ func CombineToMap[K comdef.SortedType, V any](keys []K, values []V) map[K]V { return arrutil.CombineToMap(keys, values) } +// SliceToSMap convert string k-v pairs slice to map[string]string +// - eg: []string{k1,v1,k2,v2} -> map[string]string{k1:v1, k2:v2} +func SliceToSMap(kvPairs ...string) map[string]string { + ln := len(kvPairs) + // check kvPairs length must be even + if ln == 0 || ln%2 != 0 { + return nil + } + + sMap := make(map[string]string, ln/2) + for i := 0; i < ln; i += 2 { + sMap[kvPairs[i]] = kvPairs[i+1] + } + return sMap +} + +// SliceToMap convert any k-v pairs slice to map[string]any +func SliceToMap(kvPairs ...any) map[string]any { + ln := len(kvPairs) + // check kvPairs length must be even + if ln == 0 || ln%2 != 0 { + return nil + } + + mp := make(map[string]any, ln/2) + for i := 0; i < ln; i += 2 { + kStr := strutil.SafeString(kvPairs[i]) + mp[kStr] = kvPairs[i+1] + } + return mp +} + +// SliceToTypeMap convert k-v pairs slice to map[string]T +func SliceToTypeMap[T any](valFunc func(any) T, kvPairs ...any) map[string]T { + ln := len(kvPairs) + // check kvPairs length must be even + if ln == 0 || ln%2 != 0 { + return nil + } + + mp := make(map[string]T, ln/2) + for i := 0; i < ln; i += 2 { + kStr := strutil.SafeString(kvPairs[i]) + mp[kStr] = valFunc(kvPairs[i+1]) + } + return mp +} + // ToAnyMap convert map[TYPE1]TYPE2 to map[string]any func ToAnyMap(mp any) map[string]any { amp, _ := TryAnyMap(mp) @@ -51,15 +144,23 @@ func TryAnyMap(mp any) (map[string]any, error) { if aMp, ok := mp.(map[string]any); ok { return aMp, nil } + if sMp, ok := mp.(map[string]string); ok { + anyMp := make(map[string]any, len(sMp)) + for k, v := range sMp { + anyMp[k] = v + } + return anyMp, nil + } rv := reflect.Indirect(reflect.ValueOf(mp)) if rv.Kind() != reflect.Map { - return nil, errors.New("input is not a map value") + return nil, errors.New("input is not a map value type") } anyMp := make(map[string]any, rv.Len()) for _, key := range rv.MapKeys() { - anyMp[key.String()] = rv.MapIndex(key).Interface() + keyStr := strutil.SafeString(key.Interface()) + anyMp[keyStr] = rv.MapIndex(key).Interface() } return anyMp, nil } @@ -125,9 +226,7 @@ func ToString(mp map[string]any) string { } // ToString2 simple and quickly convert a map to string. -func ToString2(mp any) string { - return NewFormatter(mp).Format() -} +func ToString2(mp any) string { return NewFormatter(mp).Format() } // FormatIndent format map data to string with newline and indent. func FormatIndent(mp any, indent string) string { diff --git a/vendor/github.com/gookit/goutil/maputil/data.go b/vendor/github.com/gookit/goutil/maputil/data.go index b284d8892..71e227ad9 100644 --- a/vendor/github.com/gookit/goutil/maputil/data.go +++ b/vendor/github.com/gookit/goutil/maputil/data.go @@ -4,6 +4,7 @@ import ( "strings" "github.com/gookit/goutil/arrutil" + "github.com/gookit/goutil/internal/comfunc" "github.com/gookit/goutil/mathutil" "github.com/gookit/goutil/strutil" ) @@ -25,37 +26,10 @@ func (d Data) IsEmpty() bool { return len(d) == 0 } -// Value get from the data map -func (d Data) Value(key string) (any, bool) { - val, ok := d.GetByPath(key) - return val, ok -} - -// Get value from the data map. -// Supports dot syntax to get deep values. eg: top.sub -func (d Data) Get(key string) any { - if val, ok := d.GetByPath(key); ok { - return val - } - return nil -} - -// GetByPath get value from the data map by path. eg: top.sub -// Supports dot syntax to get deep values. -func (d Data) GetByPath(path string) (any, bool) { - if val, ok := d[path]; ok { - return val, true - } - - // is key path. - if strings.ContainsRune(path, '.') { - val, ok := GetByPath(path, d) - if ok { - return val, true - } - } - return nil, false -} +// +// endregion +// region T: set value(s) +// // Set value to the data map func (d Data) Set(key string, val any) { @@ -65,7 +39,7 @@ func (d Data) Set(key string, val any) { // SetByPath sets a value in the map. // Supports dot syntax to set deep values. // -// For example: +// Example: // // d.SetByPath("name.first", "Mat") func (d Data) SetByPath(path string, value any) error { @@ -78,7 +52,7 @@ func (d Data) SetByPath(path string, value any) error { // SetByKeys sets a value in the map by path keys. // Supports dot syntax to set deep values. // -// For example: +// Example: // // d.SetByKeys([]string{"name", "first"}, "Mat") func (d Data) SetByKeys(keys []string, value any) error { @@ -102,6 +76,61 @@ func (d Data) SetByKeys(keys []string, value any) error { // return SetByKeys((*map[string]any)(d), keys, value) } +// +// endregion +// region T: read value(s) +// + +// Value get from the data map +func (d Data) Value(key string) (any, bool) { + val, ok := d.GetByPath(key) + return val, ok +} + +// Get value from the data map. +// Supports dot syntax to get deep values. eg: top.sub +func (d Data) Get(key string) any { + if val, ok := d.GetByPath(key); ok { + return val + } + return nil +} + +// One get value from the data by multi paths. will return first founded value +func (d Data) One(keys ...string) any { + if val, ok := d.TryOne(keys...); ok { + return val + } + return nil +} + +// TryOne get value from the data by multi paths. will return first founded value +func (d Data) TryOne(keys ...string) (any, bool) { + for _, path := range keys { + if val, ok := d.GetByPath(path); ok { + return val, true + } + } + return nil, false +} + +// GetByPath get value from the data map by path. eg: top.sub +// Supports dot syntax to get deep values. +func (d Data) GetByPath(path string) (any, bool) { + if val, ok := d[path]; ok { + return val, true + } + + // is a key path. + if strings.ContainsRune(path, '.') { + val, ok := GetByPath(path, d) + if ok { + return val, true + } + } + return nil, false +} + // Default get value from the data map with default value func (d Data) Default(key string, def any) any { if val, ok := d.GetByPath(key); ok { @@ -142,10 +171,20 @@ func (d Data) Uint64(key string) uint64 { return 0 } -// Str value get by key +// Str value gets by key func (d Data) Str(key string) string { if val, ok := d.GetByPath(key); ok { - return strutil.QuietString(val) + return strutil.SafeString(val) + } + return "" +} + +// StrOne value gets by multi keys, will return first value +func (d Data) StrOne(keys ...string) string { + for _, key := range keys { + if val, ok := d.GetByPath(key); ok { + return strutil.SafeString(val) + } } return "" } @@ -156,45 +195,46 @@ func (d Data) Bool(key string) bool { if !ok { return false } - - switch tv := val.(type) { - case string: - return strutil.QuietBool(tv) - case bool: - return tv - default: - return false - } + return comfunc.Bool(val) } -// Strings get []string value -func (d Data) Strings(key string) []string { - val, ok := d.GetByPath(key) - if !ok { - return nil - } - - switch typVal := val.(type) { - case string: - return []string{typVal} - case []string: - return typVal - case []any: - return arrutil.SliceToStrings(typVal) - default: - return nil +// BoolOne value gets from multi keys, return first value +func (d Data) BoolOne(keys ...string) bool { + for _, key := range keys { + if val, ok := d.GetByPath(key); ok { + return comfunc.Bool(val) + } } + return false } -// StrSplit get strings by split key value -func (d Data) StrSplit(key, sep string) []string { - if val, ok := d.GetByPath(key); ok { - return strings.Split(strutil.QuietString(val), sep) +// StringsOne get []string value by multi keys, return first founded value +func (d Data) StringsOne(keys ...string) []string { + for _, key := range keys { + if val, ok := d.GetByPath(key); ok { + return arrutil.AnyToStrings(val) + } } return nil } -// StringsByStr value get by key +// Strings get []string value by key +func (d Data) Strings(key string) []string { + if val, ok := d.GetByPath(key); ok { + return arrutil.AnyToStrings(val) + } + return nil +} + +// StrSplit get strings by split string value +func (d Data) StrSplit(key, sep string) []string { + if val, ok := d.GetByPath(key); ok { + return strings.Split(strutil.SafeString(val), sep) + } + return nil +} + +// StringsByStr value gets by key, will split string value by "," func (d Data) StringsByStr(key string) []string { return d.StrSplit(key, ",") } @@ -221,23 +261,40 @@ func (d Data) StringMap(key string) map[string]string { } } -// Sub get sub value as new Data +// Sub get sub value(map[string]any) as new Data func (d Data) Sub(key string) Data { if val, ok := d.GetByPath(key); ok { - if sub, ok := val.(map[string]any); ok { - return sub - } + return d.toAnyMap(val) } return nil } +// AnyMap get sub value as map[string]any +func (d Data) AnyMap(key string) map[string]any { + if val, ok := d.GetByPath(key); ok { + return d.toAnyMap(val) + } + return nil +} + +// AnyMap get sub value as map[string]any +func (d Data) toAnyMap(val any) map[string]any { + switch tv := val.(type) { + case map[string]string: + return ToAnyMap(tv) + case map[string]any: + return tv + default: + return nil + } +} + // Slice get []any value from data map func (d Data) Slice(key string) ([]any, error) { val, ok := d.GetByPath(key) if !ok { return nil, nil } - return arrutil.AnyToSlice(val) } diff --git a/vendor/github.com/gookit/goutil/maputil/get.go b/vendor/github.com/gookit/goutil/maputil/get.go index 9859fa7bf..21c00e3fa 100644 --- a/vendor/github.com/gookit/goutil/maputil/get.go +++ b/vendor/github.com/gookit/goutil/maputil/get.go @@ -202,6 +202,14 @@ func TypedKeys[K comdef.SimpleType, V any](mp map[K]V) (keys []K) { return } +// FirstKey returns the first key of the given map. +func FirstKey[T any](mp map[string]T) string { + for key := range mp { + return key + } + return "" +} + // Values get all values from the given map. func Values(mp any) (values []any) { rv := reflect.Indirect(reflect.ValueOf(mp)) diff --git a/vendor/github.com/gookit/goutil/maputil/maputil.go b/vendor/github.com/gookit/goutil/maputil/maputil.go index 48a26dbd4..8acd64d40 100644 --- a/vendor/github.com/gookit/goutil/maputil/maputil.go +++ b/vendor/github.com/gookit/goutil/maputil/maputil.go @@ -58,6 +58,11 @@ func MergeSMap(src, dst map[string]string, ignoreCase bool) map[string]string { return MergeStringMap(src, dst, ignoreCase) } +// MergeStrMap simple merge two string map. merge src to dst map +func MergeStrMap(src, dst map[string]string) map[string]string { + return MergeStringMap(src, dst, false) +} + // MergeStringMap simple merge two string map. merge src to dst map func MergeStringMap(src, dst map[string]string, ignoreCase bool) map[string]string { if len(src) == 0 { @@ -87,6 +92,25 @@ func MergeMultiSMap(mps ...map[string]string) map[string]string { return newMp } +// MergeL2StrMap merge multi level2 string-map data. The back map covers the front. +func MergeL2StrMap(mps ...map[string]map[string]string) map[string]map[string]string { + newMp := make(map[string]map[string]string) + for _, mp := range mps { + for k, v := range mp { + // merge level 2 value + if oldV, ok := newMp[k]; ok { + for k1, v1 := range v { + oldV[k1] = v1 + } + newMp[k] = oldV + } else { + newMp[k] = v + } + } + } + return newMp +} + // FilterSMap filter empty elem for the string map. func FilterSMap(sm map[string]string) map[string]string { for key, val := range sm { diff --git a/vendor/github.com/gookit/goutil/maputil/setval.go b/vendor/github.com/gookit/goutil/maputil/setval.go index 210181fc8..184d3297e 100644 --- a/vendor/github.com/gookit/goutil/maputil/setval.go +++ b/vendor/github.com/gookit/goutil/maputil/setval.go @@ -171,7 +171,7 @@ func setMapByKeys(rv reflect.Value, keys []string, nv reflect.Value) (err error) if isSlice { // key is slice index - if strutil.IsNumeric(key) { + if strutil.IsInt(key) { idx, _ = strconv.Atoi(key) } @@ -231,7 +231,7 @@ func setMapByKeys(rv reflect.Value, keys []string, nv reflect.Value) (err error) // next key is index number. nxtKey := keys[i+1] - if strutil.IsNumeric(nxtKey) { + if strutil.IsInt(nxtKey) { idx, _ = strconv.Atoi(nxtKey) sliLen := tmpV.Len() wantLen := idx + 1 @@ -275,7 +275,7 @@ func setMapByKeys(rv reflect.Value, keys []string, nv reflect.Value) (err error) } break - } else if isSlice && strutil.IsNumeric(key) { // (E). slice from ptr slice + } else if isSlice && strutil.IsInt(key) { // (E). slice from ptr slice idx, _ = strconv.Atoi(key) sliLen := rv.Len() wantLen := idx + 1 diff --git a/vendor/github.com/gookit/goutil/maputil/smap.go b/vendor/github.com/gookit/goutil/maputil/smap.go index 8905825b5..6fc767a64 100644 --- a/vendor/github.com/gookit/goutil/maputil/smap.go +++ b/vendor/github.com/gookit/goutil/maputil/smap.go @@ -5,22 +5,23 @@ import ( "github.com/gookit/goutil/strutil" ) -// SMap is alias of map[string]string -type SMap map[string]string +// SMap and StrMap is alias of map[string]string +type SMap = StrMap +type StrMap map[string]string // IsEmpty of the data map -func (m SMap) IsEmpty() bool { +func (m StrMap) IsEmpty() bool { return len(m) == 0 } // Has key on the data map -func (m SMap) Has(key string) bool { +func (m StrMap) Has(key string) bool { _, ok := m[key] return ok } // HasValue on the data map -func (m SMap) HasValue(val string) bool { +func (m StrMap) HasValue(val string) bool { for _, v := range m { if v == val { return true @@ -30,25 +31,25 @@ func (m SMap) HasValue(val string) bool { } // Load data to the map -func (m SMap) Load(data map[string]string) { +func (m StrMap) Load(data map[string]string) { for k, v := range data { m[k] = v } } // Set value to the data map -func (m SMap) Set(key string, val any) { +func (m StrMap) Set(key string, val any) { m[key] = strutil.MustString(val) } // Value get from the data map -func (m SMap) Value(key string) (string, bool) { +func (m StrMap) Value(key string) (string, bool) { val, ok := m[key] return val, ok } // Default get value by key. if not found, return defVal -func (m SMap) Default(key, defVal string) string { +func (m StrMap) Default(key, defVal string) string { if val, ok := m[key]; ok { return val } @@ -56,41 +57,51 @@ func (m SMap) Default(key, defVal string) string { } // Get value by key -func (m SMap) Get(key string) string { +func (m StrMap) Get(key string) string { return m[key] } // Int value get -func (m SMap) Int(key string) int { +func (m StrMap) Int(key string) int { if val, ok := m[key]; ok { - return mathutil.QuietInt(val) + return mathutil.SafeInt(val) } return 0 } // Int64 value get -func (m SMap) Int64(key string) int64 { +func (m StrMap) Int64(key string) int64 { if val, ok := m[key]; ok { - return mathutil.QuietInt64(val) + return mathutil.SafeInt64(val) } return 0 } // Str value get -func (m SMap) Str(key string) string { +func (m StrMap) Str(key string) string { return m[key] } +// StrOne get first founded value by keys +func (m SMap) StrOne(keys ...string) string { + for _, key := range keys { + if val, ok := m[key]; ok { + return val + } + } + return "" +} + // Bool value get -func (m SMap) Bool(key string) bool { +func (m StrMap) Bool(key string) bool { if val, ok := m[key]; ok { - return strutil.QuietBool(val) + return strutil.SafeBool(val) } return false } // Ints value to []int -func (m SMap) Ints(key string) []int { +func (m StrMap) Ints(key string) []int { if val, ok := m[key]; ok { return strutil.Ints(val, ValSepStr) } @@ -98,7 +109,7 @@ func (m SMap) Ints(key string) []int { } // Strings value to []string -func (m SMap) Strings(key string) (ss []string) { +func (m StrMap) Strings(key string) (ss []string) { if val, ok := m[key]; ok { return strutil.ToSlice(val, ValSepStr) } @@ -106,21 +117,21 @@ func (m SMap) Strings(key string) (ss []string) { } // IfExist key, then call the fn with value. -func (m SMap) IfExist(key string, fn func(val string)) { +func (m StrMap) IfExist(key string, fn func(val string)) { if val, ok := m[key]; ok { fn(val) } } // IfValid value is not empty, then call the fn -func (m SMap) IfValid(key string, fn func(val string)) { +func (m StrMap) IfValid(key string, fn func(val string)) { if val, ok := m[key]; ok && val != "" { fn(val) } } // Keys of the string-map -func (m SMap) Keys() []string { +func (m StrMap) Keys() []string { keys := make([]string, 0, len(m)) for k := range m { keys = append(keys, k) @@ -129,7 +140,7 @@ func (m SMap) Keys() []string { } // Values of the string-map -func (m SMap) Values() []string { +func (m StrMap) Values() []string { ss := make([]string, 0, len(m)) for _, v := range m { ss = append(ss, v) @@ -138,7 +149,7 @@ func (m SMap) Values() []string { } // ToKVPairs slice convert. eg: {k1:v1,k2:v2} => {k1,v1,k2,v2} -func (m SMap) ToKVPairs() []string { +func (m StrMap) ToKVPairs() []string { pairs := make([]string, 0, len(m)*2) for k, v := range m { pairs = append(pairs, k, v) @@ -147,6 +158,6 @@ func (m SMap) ToKVPairs() []string { } // String data to string -func (m SMap) String() string { +func (m StrMap) String() string { return ToString2(m) } diff --git a/vendor/github.com/gookit/goutil/maputil/smap_l2.go b/vendor/github.com/gookit/goutil/maputil/smap_l2.go new file mode 100644 index 000000000..b841b91f5 --- /dev/null +++ b/vendor/github.com/gookit/goutil/maputil/smap_l2.go @@ -0,0 +1,51 @@ +package maputil + +import "github.com/gookit/goutil/strutil" + +// L2StrMap is alias of map[string]map[string]string +type L2StrMap map[string]map[string]string + +// Load data, merge new data to old +func (m L2StrMap) Load(mp map[string]map[string]string) { + for k, v := range mp { + if oldV, ok := m[k]; ok { + for k1, v1 := range v { + oldV[k1] = v1 + } + m[k] = oldV + } else { + m[k] = v + } + } +} + +// Value get by key path. eg: "top.sub" +func (m L2StrMap) Value(key string) (val string, ok bool) { + top, sub, found := strutil.Cut(key, KeySepStr) + if !found { + return "", false + } + + if vals, ok1 := m[top]; ok1 { + val, ok = vals[sub] + return + } + return "", false +} + +// Get value by key path. eg: "top.sub" +func (m L2StrMap) Get(key string) string { + val, _ := m.Value(key) + return val +} + +// Exists check key path exists. eg: "top.sub" +func (m L2StrMap) Exists(key string) bool { + _, ok := m.Value(key) + return ok +} + +// StrMap get by top key. eg: "top" +func (m L2StrMap) StrMap(top string) StrMap { + return m[top] +} diff --git a/vendor/github.com/gookit/goutil/mathutil/check.go b/vendor/github.com/gookit/goutil/mathutil/check.go index bd876031e..ed1d11957 100644 --- a/vendor/github.com/gookit/goutil/mathutil/check.go +++ b/vendor/github.com/gookit/goutil/mathutil/check.go @@ -56,7 +56,7 @@ func CompFloat[T comdef.Float](first, second T, op string) (ok bool) { } // CompValue compare intX,uintX,floatX value. returns `first op(=,!=,<,<=,>,>=) second` -func CompValue[T comdef.XintOrFloat](first, second T, op string) (ok bool) { +func CompValue[T comdef.Number](first, second T, op string) (ok bool) { switch op { case "<", "lt": ok = first < second @@ -75,12 +75,12 @@ func CompValue[T comdef.XintOrFloat](first, second T, op string) (ok bool) { } // InRange check if val in int/float range [min, max] -func InRange[T comdef.IntOrFloat](val, min, max T) bool { +func InRange[T comdef.Number](val, min, max T) bool { return val >= min && val <= max } // OutRange check if val not in int/float range [min, max] -func OutRange[T comdef.IntOrFloat](val, min, max T) bool { +func OutRange[T comdef.Number](val, min, max T) bool { return val < min || val > max } diff --git a/vendor/github.com/gookit/goutil/mathutil/compare.go b/vendor/github.com/gookit/goutil/mathutil/compare.go index f18e28a5c..c805a8325 100644 --- a/vendor/github.com/gookit/goutil/mathutil/compare.go +++ b/vendor/github.com/gookit/goutil/mathutil/compare.go @@ -7,7 +7,7 @@ import ( ) // Min compare two value and return max value -func Min[T comdef.XintOrFloat](x, y T) T { +func Min[T comdef.Number](x, y T) T { if x < y { return x } @@ -15,7 +15,7 @@ func Min[T comdef.XintOrFloat](x, y T) T { } // Max compare two value and return max value -func Max[T comdef.XintOrFloat](x, y T) T { +func Max[T comdef.Number](x, y T) T { if x > y { return x } @@ -23,7 +23,7 @@ func Max[T comdef.XintOrFloat](x, y T) T { } // SwapMin compare and always return [min, max] value -func SwapMin[T comdef.XintOrFloat](x, y T) (T, T) { +func SwapMin[T comdef.Number](x, y T) (T, T) { if x < y { return x, y } @@ -31,7 +31,7 @@ func SwapMin[T comdef.XintOrFloat](x, y T) (T, T) { } // SwapMax compare and always return [max, min] value -func SwapMax[T comdef.XintOrFloat](x, y T) (T, T) { +func SwapMax[T comdef.Number](x, y T) (T, T) { if x > y { return x, y } diff --git a/vendor/github.com/gookit/goutil/mathutil/mathutil.go b/vendor/github.com/gookit/goutil/mathutil/mathutil.go index 4831ceb1d..1a32e5023 100644 --- a/vendor/github.com/gookit/goutil/mathutil/mathutil.go +++ b/vendor/github.com/gookit/goutil/mathutil/mathutil.go @@ -8,12 +8,12 @@ import ( ) // OrElse return default value on val is zero, else return val -func OrElse[T comdef.XintOrFloat](val, defVal T) T { +func OrElse[T comdef.Number](val, defVal T) T { return ZeroOr(val, defVal) } // ZeroOr return default value on val is zero, else return val -func ZeroOr[T comdef.XintOrFloat](val, defVal T) T { +func ZeroOr[T comdef.Number](val, defVal T) T { if val != 0 { return val } @@ -27,7 +27,7 @@ func ZeroOr[T comdef.XintOrFloat](val, defVal T) T { // LessOr(11, 10, 1) // 1 // LessOr(2, 10, 1) // 2 // LessOr(10, 10, 1) // 1 -func LessOr[T comdef.XintOrFloat](val, max, devVal T) T { +func LessOr[T comdef.Number](val, max, devVal T) T { if val < max { return val } @@ -41,7 +41,7 @@ func LessOr[T comdef.XintOrFloat](val, max, devVal T) T { // LteOr(11, 10, 1) // 11 // LteOr(2, 10, 1) // 2 // LteOr(10, 10, 1) // 10 -func LteOr[T comdef.XintOrFloat](val, max, devVal T) T { +func LteOr[T comdef.Number](val, max, devVal T) T { if val <= max { return val } @@ -54,7 +54,7 @@ func LteOr[T comdef.XintOrFloat](val, max, devVal T) T { // // GreaterOr(23, 0, 2) // 23 // GreaterOr(0, 0, 2) // 2 -func GreaterOr[T comdef.XintOrFloat](val, min, defVal T) T { +func GreaterOr[T comdef.Number](val, min, defVal T) T { if val > min { return val } @@ -67,15 +67,15 @@ func GreaterOr[T comdef.XintOrFloat](val, min, defVal T) T { // // GteOr(23, 0, 2) // 23 // GteOr(0, 0, 2) // 0 -func GteOr[T comdef.XintOrFloat](val, min, defVal T) T { +func GteOr[T comdef.Number](val, min, defVal T) T { if val >= min { return val } return defVal } -// Mul computes the a*b value, rounding the result. -func Mul[T1, T2 comdef.XintOrFloat](a T1, b T2) float64 { +// Mul computes the `a*b` value, rounding the result. +func Mul[T1, T2 comdef.Number](a T1, b T2) float64 { return math.Round(SafeFloat(a) * SafeFloat(b)) } @@ -84,8 +84,8 @@ func MulF2i(a, b float64) int { return int(math.Round(a * b)) } -// Div computes the a/b value, result use round handle. -func Div[T1, T2 comdef.XintOrFloat](a T1, b T2) float64 { +// Div computes the `a/b` value, result uses a round handle. +func Div[T1, T2 comdef.Number](a T1, b T2) float64 { return math.Round(SafeFloat(a) / SafeFloat(b)) } @@ -100,7 +100,7 @@ func DivF2i(a, b float64) int { return int(math.Round(a / b)) } -// Percent returns a values percent of the total +// Percent returns a value percentage of the total func Percent(val, total int) float64 { if total == 0 { return float64(0) diff --git a/vendor/github.com/gookit/goutil/reflects/check.go b/vendor/github.com/gookit/goutil/reflects/check.go index 7b4815ba9..361c028cd 100644 --- a/vendor/github.com/gookit/goutil/reflects/check.go +++ b/vendor/github.com/gookit/goutil/reflects/check.go @@ -5,6 +5,24 @@ import ( "reflect" ) +// IsTimeType check is or alias of time.Time type +func IsTimeType(t reflect.Type) bool { + if t == nil || t.Kind() != reflect.Struct { + return false + } + // t == timeType - 无法判断自定义类型 + return t == timeType || t.ConvertibleTo(timeType) +} + +// IsDurationType check is or alias of time.Duration type +func IsDurationType(t reflect.Type) bool { + if t == nil || t.Kind() != reflect.Int64 { + return false + } + // t == durationType - 无法判断自定义类型 + return t == durationType || t.ConvertibleTo(durationType) +} + // HasChild type check. eg: array, slice, map, struct func HasChild(v reflect.Value) bool { switch v.Kind() { @@ -15,7 +33,7 @@ func HasChild(v reflect.Value) bool { } } -// IsArrayOrSlice check. eg: array, slice +// IsArrayOrSlice type check. eg: array, slice func IsArrayOrSlice(k reflect.Kind) bool { return k == reflect.Slice || k == reflect.Array } @@ -70,8 +88,9 @@ func CanBeNil(typ reflect.Type) bool { return true case reflect.Struct: return typ == reflectValueType + default: + return false } - return false } // IsFunc value @@ -84,7 +103,7 @@ func IsFunc(val any) bool { // IsEqual determines if two objects are considered equal. // -// TIP: cannot compare function type +// TIP: cannot compare a function type func IsEqual(src, dst any) bool { if src == nil || dst == nil { return src == dst @@ -109,7 +128,7 @@ func IsEqual(src, dst any) bool { // IsZero reflect value check, alias of the IsEmpty() var IsZero = IsEmpty -// IsEmpty reflect value check +// IsEmpty reflect value check. if is ptr, check if is nil func IsEmpty(v reflect.Value) bool { switch v.Kind() { case reflect.Invalid: @@ -140,7 +159,7 @@ var IsEmptyValue = IsEmptyReal // // Note: // -// Difference the IsEmpty(), if value is ptr or interface, will check real elem. +// Difference the IsEmpty(), if value is ptr or interface, will check real elem. // // From src/pkg/encoding/json/encode.go. func IsEmptyReal(v reflect.Value) bool { diff --git a/vendor/github.com/gookit/goutil/reflects/conv.go b/vendor/github.com/gookit/goutil/reflects/conv.go index c95dcfe21..16c67e279 100644 --- a/vendor/github.com/gookit/goutil/reflects/conv.go +++ b/vendor/github.com/gookit/goutil/reflects/conv.go @@ -50,44 +50,47 @@ func ConvToType(val any, typ reflect.Type) (rv reflect.Value, err error) { // ValueByType create reflect.Value by give reflect.Type func ValueByType(val any, typ reflect.Type) (rv reflect.Value, err error) { - // handle kind: string, bool, intX, uintX, floatX - if typ.Kind() == reflect.String || typ.Kind() <= reflect.Float64 { - return ConvToKind(val, typ.Kind()) - } - var ok bool var newRv reflect.Value if newRv, ok = val.(reflect.Value); !ok { newRv = reflect.ValueOf(val) } - // try auto convert slice type - if IsArrayOrSlice(newRv.Kind()) && IsArrayOrSlice(typ.Kind()) { - return ConvSlice(newRv, typ.Elem()) + // fix: check newRv is valid + if !newRv.IsValid() { + return rv, comdef.ErrConvType } - // check type. like map + // check the same type. like map if newRv.Type() == typ { return newRv, nil } + // handle kind: string, bool, intX, uintX, floatX + if typ.Kind() == reflect.String || typ.Kind() <= reflect.Float64 { + return ConvToKind(val, typ.Kind()) + } + + // try the auto convert slice type + if IsArrayOrSlice(newRv.Kind()) && IsArrayOrSlice(typ.Kind()) { + return ConvSlice(newRv, typ.Elem()) + } + err = comdef.ErrConvType return } // ValueByKind convert and create reflect.Value by give reflect.Kind -func ValueByKind(val any, kind reflect.Kind) (rv reflect.Value, err error) { - return ConvToKind(val, kind) -} +func ValueByKind(val any, kind reflect.Kind) (reflect.Value, error) { return ConvToKind(val, kind) } // ConvToKind convert and create reflect.Value by give reflect.Kind // // TIPs: // // Only support kind: string, bool, intX, uintX, floatX -func ConvToKind(val any, kind reflect.Kind) (rv reflect.Value, err error) { - if rv, ok := val.(reflect.Value); ok { - val = rv.Interface() +func ConvToKind(val any, kind reflect.Kind, fallback ...ConvFunc) (rv reflect.Value, err error) { + if rv1, ok := val.(reflect.Value); ok { + val = rv1.Interface() } switch kind { @@ -185,6 +188,12 @@ func ConvToKind(val any, kind reflect.Kind) (rv reflect.Value, err error) { err = err1 } default: + // call fallback func + if len(fallback) > 0 && fallback[0] != nil { + rv, err = fallback[0](val, kind) + } else { + err = comdef.ErrConvType + } err = comdef.ErrConvType } return @@ -254,3 +263,31 @@ func ValToString(rv reflect.Value, defaultAsErr bool) (str string, err error) { } return } + +// ToTimeOrDuration convert string to time.Time or time.Duration type +// +// If the target type is not match, return the input string. +func ToTimeOrDuration(str string, typ reflect.Type) (any, error) { + // datetime, time, duration string should not greater than 64 + if len(str) > 64 { + return str, nil + } + var anyVal any = str + + // time.Time date string + if len(str) > 5 && IsTimeType(typ) { + ttVal, err := strutil.ToTime(str) + if err != nil { + return nil, err + } + anyVal = ttVal + } else if IsDurationType(typ) { + dVal, err := strutil.ToDuration(str) + if err != nil { + return nil, err + } + anyVal = dVal + } + + return anyVal, nil +} diff --git a/vendor/github.com/gookit/goutil/reflects/func.go b/vendor/github.com/gookit/goutil/reflects/func.go index 408d21f0e..4e68c8a85 100644 --- a/vendor/github.com/gookit/goutil/reflects/func.go +++ b/vendor/github.com/gookit/goutil/reflects/func.go @@ -5,7 +5,7 @@ import ( "fmt" "reflect" - "github.com/gookit/goutil/basefn" + "github.com/gookit/goutil/x/basefn" ) // FuncX wrap a go func. represent a function @@ -86,7 +86,7 @@ func (f *FuncX) Call(args ...any) ([]any, error) { // The function must return 1 result, or 2 results, the second of which is an error. // // - Only support func with 1 or 2 return values: (val) OR (val, err) -// - Will check args and try convert input args to func args type. +// - Will check args and try to convert input args to func args type. func (f *FuncX) Call2(args ...any) (any, error) { // convert args to []reflect.Value argRvs := make([]reflect.Value, len(args)) diff --git a/vendor/github.com/gookit/goutil/reflects/map.go b/vendor/github.com/gookit/goutil/reflects/map.go index b63b9479a..924b24f67 100644 --- a/vendor/github.com/gookit/goutil/reflects/map.go +++ b/vendor/github.com/gookit/goutil/reflects/map.go @@ -1,27 +1,39 @@ package reflects import ( + "errors" "reflect" "strconv" ) +// TryAnyMap convert map[TYPE1]TYPE2 to map[string]any +func TryAnyMap(mp reflect.Value) (map[string]any, error) { + saMap := make(map[string]any) + err := EachStrAnyMap(mp, func(key string, val any) { + saMap[key] = val + }) + + return saMap, err +} + // EachMap process any map data -func EachMap(mp reflect.Value, fn func(key, val reflect.Value)) { +func EachMap(mp reflect.Value, fn func(key, val reflect.Value)) (err error) { if fn == nil { return } if mp.Kind() != reflect.Map { - panic("only allow map value data") + return errors.New("EachMap: only allow map value data") } for _, key := range mp.MapKeys() { fn(key, mp.MapIndex(key)) } + return } // EachStrAnyMap process any map data as string key and any value -func EachStrAnyMap(mp reflect.Value, fn func(key string, val any)) { - EachMap(mp, func(key, val reflect.Value) { +func EachStrAnyMap(mp reflect.Value, fn func(key string, val any)) error { + return EachMap(mp, func(key, val reflect.Value) { fn(String(key), val.Interface()) }) } diff --git a/vendor/github.com/gookit/goutil/reflects/reflects.go b/vendor/github.com/gookit/goutil/reflects/reflects.go index 0a6cdde8a..9c42e24ff 100644 --- a/vendor/github.com/gookit/goutil/reflects/reflects.go +++ b/vendor/github.com/gookit/goutil/reflects/reflects.go @@ -3,6 +3,7 @@ package reflects import ( "reflect" + "time" ) var emptyValue = reflect.Value{} @@ -11,6 +12,14 @@ var ( anyType = reflect.TypeOf((*any)(nil)).Elem() errorType = reflect.TypeOf((*error)(nil)).Elem() + // time.Time type + timeType = reflect.TypeOf(time.Time{}) + // time.Duration type + durationType = reflect.TypeOf(time.Duration(0)) + // fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() reflectValueType = reflect.TypeOf((*reflect.Value)(nil)).Elem() ) + +// ConvFunc custom func convert input value to kind reflect.Value +type ConvFunc func(val any, kind reflect.Kind) (reflect.Value, error) diff --git a/vendor/github.com/gookit/goutil/reflects/util.go b/vendor/github.com/gookit/goutil/reflects/util.go index 54e7f92af..a6c40366e 100644 --- a/vendor/github.com/gookit/goutil/reflects/util.go +++ b/vendor/github.com/gookit/goutil/reflects/util.go @@ -98,10 +98,9 @@ func Len(v reflect.Value) int { return len(strconv.FormatInt(v.Int(), 10)) case reflect.Float32, reflect.Float64: return len(fmt.Sprint(v.Interface())) + default: + return -1 // cannot get length } - - // cannot get length - return -1 } // SliceSubKind get sub-elem kind of the array, slice, variadic-var. alias SliceElemKind() @@ -152,7 +151,7 @@ func SetUnexportedValue(rv reflect.Value, value any) { // SetValue to a `reflect.Value`. will auto convert type if needed. func SetValue(rv reflect.Value, val any) error { - // get real type of the ptr value + // get a real type of the ptr value if rv.Kind() == reflect.Ptr { if rv.IsNil() { elemTyp := rv.Type().Elem() @@ -170,7 +169,7 @@ func SetValue(rv reflect.Value, val any) error { return err } -// SetRValue to a `reflect.Value`. will direct set value without convert type. +// SetRValue to a `reflect.Value`. will direct set value without a type convert. func SetRValue(rv, val reflect.Value) { if rv.Kind() == reflect.Ptr { if rv.IsNil() { diff --git a/vendor/github.com/gookit/goutil/stdio/ioutil.go b/vendor/github.com/gookit/goutil/stdio/ioutil.go deleted file mode 100644 index 1bf65c0f4..000000000 --- a/vendor/github.com/gookit/goutil/stdio/ioutil.go +++ /dev/null @@ -1,27 +0,0 @@ -package stdio - -import ( - "fmt" - "io" - "strings" -) - -// QuietFprint to writer, will ignore error -func QuietFprint(w io.Writer, a ...any) { - _, _ = fmt.Fprint(w, a...) -} - -// QuietFprintf to writer, will ignore error -func QuietFprintf(w io.Writer, tpl string, vs ...any) { - _, _ = fmt.Fprintf(w, tpl, vs...) -} - -// QuietFprintln to writer, will ignore error -func QuietFprintln(w io.Writer, a ...any) { - _, _ = fmt.Fprintln(w, a...) -} - -// QuietWriteString to writer, will ignore error -func QuietWriteString(w io.Writer, ss ...string) { - _, _ = io.WriteString(w, strings.Join(ss, "")) -} diff --git a/vendor/github.com/gookit/goutil/structs/README.md b/vendor/github.com/gookit/goutil/structs/README.md index d75da8bf6..da0fd88fe 100644 --- a/vendor/github.com/gookit/goutil/structs/README.md +++ b/vendor/github.com/gookit/goutil/structs/README.md @@ -1,6 +1,6 @@ # Structs -Provide some extends util functions for struct. eg: convert, tag parse, struct data init +`structs` Provide some extends util functions for struct. eg: convert, tag parse, struct data init - `structs.Aliases` - implemented a simple string alias map. - Convert a struct to `map[string]any` data @@ -67,7 +67,7 @@ type Extra struct { type User struct { Name string `default:"inhere"` Age int `default:"30"` - Extra Extra + Extra Extra `default:""` // NEED mark tag } u := &User{} diff --git a/vendor/github.com/gookit/goutil/structs/README.zh-CN.md b/vendor/github.com/gookit/goutil/structs/README.zh-CN.md new file mode 100644 index 000000000..e888d4cbf --- /dev/null +++ b/vendor/github.com/gookit/goutil/structs/README.zh-CN.md @@ -0,0 +1,61 @@ +# Structs + +`structs` 包为 go struct 提供一些有用的工具函数库。例如:convert、tag parse、struct data init + +- 快速将结构体转换为 `map[string]any` 数据 +- `structs.Aliases` - 实现了一个简单的字符串别名映射。 +- 通过字段 `"default"` 标签快速初始化结构体默认值 +- 通过map数据快速设置 struct 字段值 +- 解析 struct 并收集 tag,解析 tag 值 +- 以及更多 util 函数... + +## Install + +```shell +go get github.com/gookit/goutil/structs +``` + +## Go docs + +- [Go docs](https://pkg.go.dev/github.com/gookit/goutil/structs) + +## 初始化结构体 + +- 支持初始化使用环境变量 +- 支持初始化 slice 字段,嵌套结构体 + +```go +type ExtraDefault struct { + City string `default:"some where"` + Github string `default:"${ GITHUB_ADDR }"` +} + +type User struct { + Name string `default:"inhere"` + Age int `default:"300"` + Extra *ExtraDefault `default:""` // 标记需要初始化 +} + +optFn := func(opt *structs.InitOptions) { + opt.ParseEnv = true +} + +obj := &User{} +err := structs.InitDefaults(obj, optFn) +goutil.PanicErr(err) + +dump.P(obj) +``` + +**初始化结果**: + +```go +&structs_test.User { + Name: string("inhere"), #len=6 + Age: int(300), + Extra: &structs_test.ExtraDefault { + City: string("some where"), #len=10 + Github: string("https://some .... url"), #len=21 + }, +}, +``` \ No newline at end of file diff --git a/vendor/github.com/gookit/goutil/structs/convert.go b/vendor/github.com/gookit/goutil/structs/convert.go index 3158a922a..f8029865d 100644 --- a/vendor/github.com/gookit/goutil/structs/convert.go +++ b/vendor/github.com/gookit/goutil/structs/convert.go @@ -4,12 +4,13 @@ import ( "errors" "fmt" "reflect" + "time" "github.com/gookit/goutil/maputil" "github.com/gookit/goutil/reflects" ) -// ToMap quickly convert structs to map by reflect +// ToMap quickly convert structs to map by reflection func ToMap(st any, optFns ...MapOptFunc) map[string]any { mp, _ := StructToMap(st, optFns...) return mp @@ -29,13 +30,13 @@ func TryToMap(st any, optFns ...MapOptFunc) (map[string]any, error) { return StructToMap(st, optFns...) } -// ToSMap quickly and safe convert structs to map[string]string by reflect +// ToSMap quickly and safe convert structs to map[string]string by reflection func ToSMap(st any, optFns ...MapOptFunc) map[string]string { mp, _ := StructToMap(st, optFns...) return maputil.ToStringMap(mp) } -// TryToSMap quickly convert structs to map[string]string by reflect +// TryToSMap quickly convert structs to map[string]string by reflection func TryToSMap(st any, optFns ...MapOptFunc) (map[string]string, error) { mp, err := StructToMap(st, optFns...) if err != nil { @@ -64,16 +65,28 @@ func ToString(st any, optFns ...MapOptFunc) string { const defaultFieldTag = "json" +// CustomUserFunc for map convert +// - fName: raw field name in struct +// +// Returns: +// - ok: return true to collect field, otherwise excluded. +// - newVal: `newVal != nil` return new value to collect, otherwise collect original value. +type CustomUserFunc func(fName string, fv reflect.Value) (ok bool, newVal any) + // MapOptions for convert struct to map type MapOptions struct { // TagName for map filed. default is "json" TagName string - // ParseDepth for parse. TODO support depth + // ParseDepth for parse and collect. TODO support depth ParseDepth int // MergeAnonymous struct fields to parent map. default is true MergeAnonymous bool // ExportPrivate export private fields. default is false ExportPrivate bool + // IgnoreEmpty ignore empty value item. default: false + IgnoreEmpty bool + // UserFunc custom interceptor for filter or handle field value. + UserFunc CustomUserFunc } // MapOptFunc define @@ -81,23 +94,26 @@ type MapOptFunc func(opt *MapOptions) // WithMapTagName set tag name for map field func WithMapTagName(tagName string) MapOptFunc { - return func(opt *MapOptions) { - opt.TagName = tagName - } + return func(opt *MapOptions) { opt.TagName = tagName } +} + +// WithUserFunc custom user func +func WithUserFunc(fn CustomUserFunc) MapOptFunc { + return func(opt *MapOptions) { opt.UserFunc = fn } } // MergeAnonymous merge anonymous struct fields to parent map -func MergeAnonymous(opt *MapOptions) { - opt.MergeAnonymous = true -} +func MergeAnonymous(opt *MapOptions) { opt.MergeAnonymous = true } // ExportPrivate merge anonymous struct fields to parent map -func ExportPrivate(opt *MapOptions) { - opt.ExportPrivate = true -} +func ExportPrivate(opt *MapOptions) { opt.ExportPrivate = true } -// StructToMap quickly convert structs to map[string]any by reflect. -// Can custom export field name by tag `json` or custom tag +// WithIgnoreEmpty ignore on field value is empty +func WithIgnoreEmpty(opt *MapOptions) { opt.IgnoreEmpty = true } + +// StructToMap quickly convert structs to map[string]any by reflection. +// +// Can custom export field name by tag `json` or custom tag. see MapOptions func StructToMap(st any, optFns ...MapOptFunc) (map[string]any, error) { mp := make(map[string]any) if st == nil { @@ -106,7 +122,7 @@ func StructToMap(st any, optFns ...MapOptFunc) (map[string]any, error) { obj := reflect.Indirect(reflect.ValueOf(st)) if obj.Kind() != reflect.Struct { - return mp, errors.New("must be an struct value") + return mp, errors.New("StructToMap: must be an struct value") } opt := &MapOptions{TagName: defaultFieldTag} @@ -125,14 +141,14 @@ func structToMap(obj reflect.Value, opt *MapOptions, mp map[string]any) (map[str refType := obj.Type() for i := 0; i < obj.NumField(); i++ { - ft := refType.Field(i) - name := ft.Name + sf := refType.Field(i) + name := sf.Name // skip un-exported field if !opt.ExportPrivate && IsUnexported(name) { continue } - tagVal, ok := ft.Tag.Lookup(opt.TagName) + tagVal, ok := sf.Tag.Lookup(opt.TagName) if ok && tagVal != "" { sMap, err := ParseTagValueDefault(name, tagVal) if err != nil { @@ -140,25 +156,36 @@ func structToMap(obj reflect.Value, opt *MapOptions, mp map[string]any) (map[str } name = sMap.Default("name", name) - if name == "" { // un-exported field + if name == "" || name == "-" { // un-exported field continue } } - field := reflect.Indirect(obj.Field(i)) - if !field.IsValid() { + fv := reflect.Indirect(obj.Field(i)) + if !fv.IsValid() { continue } - if field.Kind() == reflect.Struct { + // opt: ignore empty field + if opt.IgnoreEmpty && reflects.IsEmpty(fv) { + continue + } + + if fv.Kind() == reflect.Struct { + // up: special handle time.Time field value + if reflects.IsTimeType(fv.Type()) { + mp[name] = fv.Interface().(time.Time).Format(time.RFC3339) + continue + } + // collect anonymous struct values to parent. - if ft.Anonymous && opt.MergeAnonymous { - _, err := structToMap(field, opt, mp) + if sf.Anonymous && opt.MergeAnonymous { + _, err := structToMap(fv, opt, mp) if err != nil { return nil, err } } else { // collect struct values to submap - sub, err := structToMap(field, opt, nil) + sub, err := structToMap(fv, opt, nil) if err != nil { return nil, err } @@ -167,10 +194,26 @@ func structToMap(obj reflect.Value, opt *MapOptions, mp map[string]any) (map[str continue } - if field.CanInterface() { - mp[name] = field.Interface() - } else if field.CanAddr() { // for unexported field - mp[name] = reflects.UnexportedValue(field) + // TODO support struct slice field. + + // up: support custom user func + if opt.UserFunc != nil { + ok1, newVal := opt.UserFunc(sf.Name, fv) + if !ok1 { + continue + } + + // ok1=true, newVal != nil + if newVal != nil { + mp[name] = newVal + continue + } + } + + if fv.CanInterface() { + mp[name] = fv.Interface() + } else if fv.CanAddr() { // for unexported field + mp[name] = reflects.UnexportedValue(fv) } } diff --git a/vendor/github.com/gookit/goutil/structs/init.go b/vendor/github.com/gookit/goutil/structs/init.go index 9c42b159c..7d01c2e51 100644 --- a/vendor/github.com/gookit/goutil/structs/init.go +++ b/vendor/github.com/gookit/goutil/structs/init.go @@ -20,16 +20,35 @@ type InitOptFunc func(opt *InitOptions) // InitOptions struct type InitOptions struct { // TagName default value tag name. tag: default - TagName string + TagName string + // EnvPrefix default ENV prefix name. + EnvPrefix string + // EnvPrefixTagName default value tag name. tag: defaultenvprefix EnvPrefixTagName string // ParseEnv var name on default value. eg: `default:"${APP_ENV}"` // // default: false ParseEnv bool + // ParseTime parse string to `time.Duration`, `time.Time`. default: false + // + // eg: default:"10s", default:"2025-04-23 15:04:05" + ParseTime bool // ValueHook before set value hook TODO ValueHook func(val string) any } +// WithParseTime set parse time string on default value. +func (opt *InitOptions) WithParseTime(val bool) *InitOptions { + opt.ParseTime = val + return opt +} + +// WithParseEnv set parse env var on default value. +func (opt *InitOptions) WithParseEnv(val bool) *InitOptions { + opt.ParseEnv = val + return opt +} + // Init struct default value by field "default" tag. func Init(ptr any, optFns ...InitOptFunc) error { return InitDefaults(ptr, optFns...) @@ -70,12 +89,17 @@ func InitDefaults(ptr any, optFns ...InitOptFunc) error { return initDefaults(rv, opt, "") } +// type InitBuilder struct { +// opt InitOptions +// } + func initDefaults(rv reflect.Value, opt *InitOptions, envPrefix string) error { rt := rv.Type() + opt.EnvPrefix = envPrefix for i := 0; i < rt.NumField(); i++ { sf := rt.Field(i) - // skip don't exported field + // skip doesn't exported field if IsUnexported(sf.Name) { continue } @@ -84,12 +108,22 @@ func initDefaults(rv reflect.Value, opt *InitOptions, envPrefix string) error { if !hasTag || val == "-" { continue } + + var childPrefixVar string prefixVar, _ := sf.Tag.Lookup(opt.EnvPrefixTagName) - childPrefixVar := fmt.Sprintf("%s%s", envPrefix, prefixVar) + if prefixVar != "" { + childPrefixVar = fmt.Sprintf("%s%s", envPrefix, prefixVar) + // opt.EnvPrefix = childPrefixVar + } fv := rv.Field(i) if fv.Kind() == reflect.Struct { - if err := initDefaults(fv, opt, childPrefixVar); err != nil { + // special: struct is time.Time type + if reflects.IsTimeType(fv.Type()) { + if err := initDefaultValue(fv, val, opt); err != nil { + return err + } + } else if err := initDefaults(fv, opt, childPrefixVar); err != nil { return err } continue @@ -170,7 +204,7 @@ func initDefaults(rv reflect.Value, opt *InitOptions, envPrefix string) error { } } - if err := initDefaultValue(fv, val, opt.ParseEnv, envPrefix); err != nil { + if err := initDefaultValue(fv, val, opt); err != nil { return err } } @@ -179,9 +213,13 @@ func initDefaults(rv reflect.Value, opt *InitOptions, envPrefix string) error { } func enhanceDefaultVar(val string, envPrefix string) string { - cleaned_var := strings.TrimSpace(val) - if strings.HasPrefix(cleaned_var, "${") && strings.HasSuffix(cleaned_var, "}") { - parts := strings.SplitN(cleaned_var[2:len(cleaned_var)-1], "|", 2) + if len(envPrefix) == 0 { + return val + } + cleanedVar := strings.TrimSpace(val) + + if strings.HasPrefix(cleanedVar, "${") && strings.HasSuffix(cleanedVar, "}") { + parts := strings.SplitN(cleanedVar[2:len(cleanedVar)-1], "|", 2) if len(parts) > 0 { env := strings.TrimSpace(parts[0]) if env != "" { @@ -196,19 +234,27 @@ func enhanceDefaultVar(val string, envPrefix string) string { return val } -func initDefaultValue(fv reflect.Value, val string, parseEnv bool, envPrefix string) error { +func initDefaultValue(fv reflect.Value, val string, opt *InitOptions) error { if val == "" || !fv.CanSet() { return nil } // parse env var - if parseEnv { - val = enhanceDefaultVar(val, envPrefix) - val = varexpr.SafeParse(val) + if opt.ParseEnv { + val = varexpr.SafeParse(enhanceDefaultVar(val, opt.EnvPrefix)) } var anyVal any = val + // enhance: parse special value type. eg: time.Duration, time.Time + if opt.ParseTime { + newVal, err := reflects.ToTimeOrDuration(val, fv.Type()) + if err != nil { + return err + } + anyVal = newVal + } + // simple slice: convert simple kind(string,intX,uintX,...) to slice. eg: "1,2,3" => []int{1,2,3} if reflects.IsArrayOrSlice(fv.Kind()) && reflects.IsSimpleKind(reflects.SliceElemKind(fv.Type())) { ss := strutil.SplitTrimmed(val, ",") diff --git a/vendor/github.com/gookit/goutil/structs/wrapper.go b/vendor/github.com/gookit/goutil/structs/wrapper.go index 530a848a2..348eaeccd 100644 --- a/vendor/github.com/gookit/goutil/structs/wrapper.go +++ b/vendor/github.com/gookit/goutil/structs/wrapper.go @@ -9,7 +9,7 @@ import ( type Wrapper struct { // src any // source struct - // reflect.Value of source struct + // raw reflect.Value of source struct rv reflect.Value // FieldTagName field name for read/write value. default tag: json @@ -20,10 +20,8 @@ type Wrapper struct { fvCacheMap map[string]reflect.Value //lint:ignore U1000 for unused } -// Wrap create a struct wrapper -func Wrap(src any) *Wrapper { - return NewWrapper(src) -} +// Wrap quick create a struct wrapper +func Wrap(src any) *Wrapper { return NewWrapper(src) } // NewWrapper create a struct wrapper func NewWrapper(src any) *Wrapper { @@ -39,7 +37,7 @@ func WrapValue(rv reflect.Value) *Wrapper { return &Wrapper{rv: rv} } -// Get field value by name, name allow use dot syntax. +// Get field value by name, name allows to use dot syntax. func (r *Wrapper) Get(name string) any { val, ok := r.Lookup(name) if !ok { @@ -48,7 +46,7 @@ func (r *Wrapper) Get(name string) any { return val } -// Lookup field value by name, name allow use dot syntax. +// Lookup field value by name, name allows to use dot syntax. func (r *Wrapper) Lookup(name string) (val any, ok bool) { fv := r.rv.FieldByName(name) if !fv.IsValid() { @@ -61,15 +59,15 @@ func (r *Wrapper) Lookup(name string) (val any, ok bool) { return } -// Set field value by name, name allow use dot syntax. +// Set field value by name. name allows using dot syntax. func (r *Wrapper) Set(name string, val any) error { fv := r.rv.FieldByName(name) if !fv.IsValid() { - return errors.New("field not found") + return errors.New("field " + name + " not found") } if !fv.CanSet() { - return errors.New("field can not set value") + return errors.New("can not set value for field: " + name) } fv.Set(reflect.ValueOf(val)) diff --git a/vendor/github.com/gookit/goutil/structs/writer.go b/vendor/github.com/gookit/goutil/structs/writer.go index 5231ebbdf..13a2c0ef0 100644 --- a/vendor/github.com/gookit/goutil/structs/writer.go +++ b/vendor/github.com/gookit/goutil/structs/writer.go @@ -4,21 +4,21 @@ import ( "errors" "fmt" "reflect" - "time" "github.com/gookit/goutil/comdef" - "github.com/gookit/goutil/maputil" "github.com/gookit/goutil/reflects" "github.com/gookit/goutil/strutil" ) // NewWriter create a struct writer +// +// TIP: must be pointer for set field value func NewWriter(ptr any) *Wrapper { rv := reflect.ValueOf(ptr) + if rv.Kind() != reflect.Pointer { panic("must be provider an pointer value") } - return WrapValue(rv) } @@ -29,27 +29,32 @@ func NewWriter(ptr any) *Wrapper { // SetOptFunc define type SetOptFunc func(opt *SetOptions) +// BeforeSetFunc hook type. +type BeforeSetFunc func(fieldName string, value any, fv reflect.Value) any + // SetOptions for set values to struct type SetOptions struct { // FieldTagName get field name for read value. default tag: json FieldTagName string - // ValueHook before set value hook TODO - ValueHook func(val any) any + // BeforeSetFn hook func. will fire on before set value. + // - you can modify value here. + // - returns nil will skip set value. + // - returns value will be set to field value. + BeforeSetFn BeforeSetFunc - // ParseDefault init default value by DefaultValTag tag value. - // default: false + // ParseTime parse string to `time.Duration`, `time.Time`. default: false + // + // eg: default:"10s", default:"2025-04-23 15:04:05" + ParseTime bool + // ParseDefault init default value by DefaultValTag tag value. default: false // // see InitDefaults() ParseDefault bool - // DefaultValTag name. tag: default DefaultValTag string - // ParseDefaultEnv parse env var on default tag. eg: `default:"${APP_ENV}"` - // - // default: false + // ParseDefaultEnv parse env var on default tag. eg: `default:"${APP_ENV}"` default: false ParseDefaultEnv bool - // DefaultEnvPrefixTag name. tag: defaultenvprefix DefaultEnvPrefixTag string @@ -58,8 +63,26 @@ type SetOptions struct { } // WithParseDefault value by tag "default" -func WithParseDefault(opt *SetOptions) { - opt.ParseDefault = true +func WithParseDefault(opt *SetOptions) { opt.ParseDefault = true } + +// WithBeforeSetFn value by tag "default" +func WithBeforeSetFn(fn BeforeSetFunc) SetOptFunc { + return func(opt *SetOptions) { opt.BeforeSetFn = fn } +} + +// TODO refactoring SetValues to the struct +type ValuesSetter struct { + src any // source struct + // raw reflect.Value of source struct + rv reflect.Value + + option *SetOptions + initOpt *InitOptions +} + +// BindData set values to struct ptr from map data. +func BindData(ptr any, data map[string]any, optFns ...SetOptFunc) error { + return SetValues(ptr, data, optFns...) } // SetValues set values to struct ptr from map data. @@ -96,6 +119,11 @@ func setValues(rv reflect.Value, data map[string]any, opt *SetOptions, envPrefix } var es comdef.Errors + initOpt := &InitOptions{ + EnvPrefix: envPrefix, + ParseEnv: opt.ParseDefaultEnv, + ParseTime: opt.ParseTime, + } rt := rv.Type() for i := 0; i < rt.NumField(); i++ { @@ -107,8 +135,8 @@ func setValues(rv reflect.Value, data map[string]any, opt *SetOptions, envPrefix } // get field name - tagVal, ok := ft.Tag.Lookup(opt.FieldTagName) - if ok { + tagVal, ok0 := ft.Tag.Lookup(opt.FieldTagName) + if ok0 { info, err := ParseTagValueDefault(name, tagVal) if err != nil { es = append(es, err) @@ -123,7 +151,7 @@ func setValues(rv reflect.Value, data map[string]any, opt *SetOptions, envPrefix // set field value by default tag. if !ok && opt.ParseDefault && fv.IsZero() { defVal := ft.Tag.Get(opt.DefaultValTag) - if err := initDefaultValue(fv, defVal, opt.ParseDefaultEnv, envPrefix); err != nil { + if err := initDefaultValue(fv, defVal, initOpt); err != nil { es = append(es, err) } continue @@ -137,22 +165,41 @@ func setValues(rv reflect.Value, data map[string]any, opt *SetOptions, envPrefix fv = fv.Elem() } + // hook: call BeforeSet func + if opt.BeforeSetFn != nil { + val = opt.BeforeSetFn(ft.Name, val, fv) + if val == nil { + continue + } + ok = true // on value is not nil + } + // field is struct if fv.Kind() == reflect.Struct { - // up: special handle time.Time struct - if _, ok := fv.Interface().(time.Time); ok { - tm, er := strutil.ToTime(strutil.StringOr(val, "")) - if er != nil { - es = append(es, er) + valRf := reflects.Indirect(reflect.ValueOf(val)) + + // up: special handle time.Time field + if reflects.IsTimeType(fv.Type()) { + // maybe val is time.Time + if reflects.IsTimeType(valRf.Type()) { + if err := reflects.SetValue(fv, valRf); err != nil { + es = append(es, err) + } continue } - if er = reflects.SetValue(fv, tm); er != nil { - es = append(es, er) + + // val is datetime string + tm, err := strutil.ToTime(strutil.StringOr(val, "")) + if err != nil { + es = append(es, err) + } else if err = reflects.SetValue(fv, tm); err != nil { + es = append(es, err) } continue } - asMp, err := maputil.TryAnyMap(val) + // val as map + asMp, err := reflects.TryAnyMap(valRf) if err != nil { err = fmt.Errorf("must provide map for set struct field %q, err=%v", ft.Name, err) es = append(es, err) @@ -170,9 +217,10 @@ func setValues(rv reflect.Value, data map[string]any, opt *SetOptions, envPrefix } // set field value - if err := reflects.SetValue(fv, val); err != nil { - es = append(es, err) - continue + if ok && val != nil { + if err := reflects.SetValue(fv, val); err != nil { + es = append(es, err) + } } } diff --git a/vendor/github.com/gookit/goutil/strutil/bytes.go b/vendor/github.com/gookit/goutil/strutil/bytes.go index 1b39b1832..6493842f2 100644 --- a/vendor/github.com/gookit/goutil/strutil/bytes.go +++ b/vendor/github.com/gookit/goutil/strutil/bytes.go @@ -7,8 +7,13 @@ import ( // Buffer wrap and extends the bytes.Buffer type Buffer = byteutil.Buffer -// NewBuffer instance -func NewBuffer() *Buffer { +// NewBuffer instance, can set init size +func NewBuffer(initSize ...int) *Buffer { + if len(initSize) > 0 && initSize[0] > 0 { + buf := &Buffer{} + buf.Grow(initSize[0]) + return buf + } return &Buffer{} } diff --git a/vendor/github.com/gookit/goutil/strutil/check.go b/vendor/github.com/gookit/goutil/strutil/check.go index bf87f839b..04660c307 100644 --- a/vendor/github.com/gookit/goutil/strutil/check.go +++ b/vendor/github.com/gookit/goutil/strutil/check.go @@ -6,18 +6,28 @@ import ( "strings" "unicode" "unicode/utf8" + + "github.com/gookit/goutil/internal/checkfn" ) // Equal check, alias of strings.EqualFold var Equal = strings.EqualFold +var IsHttpURL = checkfn.IsHttpURL // IsNumChar returns true if the given character is a numeric, otherwise false. func IsNumChar(c byte) bool { return c >= '0' && c <= '9' } -var numReg = regexp.MustCompile(`^\d+$`) +var intReg = regexp.MustCompile(`^\d+$`) +var floatReg = regexp.MustCompile(`^[-+]?\d*\.?\d+$`) -// IsNumeric returns true if the given string is a numeric, otherwise false. -func IsNumeric(s string) bool { return numReg.MatchString(s) } +// IsInt check the string is an integer number +func IsInt(s string) bool { return intReg.MatchString(s) } + +// IsFloat check the string is a float number +func IsFloat(s string) bool { return floatReg.MatchString(s) } + +// IsNumeric returns true if the given string is a numeric(int/float), otherwise false. +func IsNumeric(s string) bool { return checkfn.IsNumeric(s) } // IsAlphabet char func IsAlphabet(char uint8) bool { @@ -44,7 +54,7 @@ func StrPos(s, sub string) int { return strings.Index(s, sub) } // BytePos alias of the strings.IndexByte func BytePos(s string, bt byte) int { return strings.IndexByte(s, bt) } -// IEqual ignore case check given two string is equals. +// IEqual ignore case check given two strings are equals. func IEqual(s1, s2 string) bool { return strings.EqualFold(s1, s2) } // NoCaseEq check two strings is equals and case-insensitivity @@ -56,9 +66,7 @@ func IContains(s, sub string) bool { } // ContainsByte in given string. -func ContainsByte(s string, c byte) bool { - return strings.IndexByte(s, c) >= 0 -} +func ContainsByte(s string, c byte) bool { return strings.IndexByte(s, c) >= 0 } // InArray alias of HasOneSub() var InArray = HasOneSub @@ -76,6 +84,17 @@ func HasOneSub(s string, subs []string) bool { return false } +// IContainsOne ignore case check has one substr(s) in the given string. +func IContainsOne(s string, subs []string) bool { + s = strings.ToLower(s) + for _, sub := range subs { + if strings.Contains(s, strings.ToLower(sub)) { + return true + } + } + return false +} + // ContainsAll substr(s) in the given string. alias of HasAllSubs() func ContainsAll(s string, subs []string) bool { return HasAllSubs(s, subs) } @@ -89,6 +108,17 @@ func HasAllSubs(s string, subs []string) bool { return true } +// IContainsAll like ContainsAll(), but ignore case +func IContainsAll(s string, subs []string) bool { + s = strings.ToLower(s) + for _, sub := range subs { + if !strings.Contains(s, strings.ToLower(sub)) { + return false + } + } + return true +} + // StartsWithAny alias of the HasOnePrefix var StartsWithAny = HasOneSuffix @@ -97,10 +127,10 @@ func IsStartsOf(s string, prefixes []string) bool { return HasOnePrefix(s, prefixes) } -// HasOnePrefix the string start withs one of the subs +// HasOnePrefix the string starts with one of the subs func HasOnePrefix(s string, prefixes []string) bool { for _, prefix := range prefixes { - if strings.HasPrefix(s, prefix) { + if len(s) >= len(prefix) && s[0:len(prefix)] == prefix { return true } } @@ -122,7 +152,7 @@ func HasSuffix(s string, suffix string) bool { return strings.HasSuffix(s, suffi // IsEndOf alias of the strings.HasSuffix func IsEndOf(s, suffix string) bool { return strings.HasSuffix(s, suffix) } -// HasOneSuffix the string end withs one of the subs +// HasOneSuffix the string end with one of the subs func HasOneSuffix(s string, suffixes []string) bool { for _, suffix := range suffixes { if strings.HasSuffix(s, suffix) { @@ -138,7 +168,14 @@ func IsValidUtf8(s string) bool { return utf8.ValidString(s) } // ----- refer from github.com/yuin/goldmark/util // refer from github.com/yuin/goldmark/util -var spaceTable = [256]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +var spaceTable = [256]int8{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +} // IsSpace returns true if the given character is a space, otherwise false. func IsSpace(c byte) bool { return spaceTable[c] == 1 } @@ -185,15 +222,25 @@ func IsAllEmpty(ss ...string) bool { return true } -var verRegex = regexp.MustCompile(`^[0-9][\d.]+(-\w+)?$`) +var ( + // regex for check version number + verRegex = regexp.MustCompile(`^[0-9][\d.]+(-\w+)?$`) + // regex for check variable name + varRegex = regexp.MustCompile(`^[a-zA-Z][\w-]*$`) + // IsVariableName alias for IsVarName + IsVariableName = IsVarName +) // IsVersion number. eg: 1.2.0 func IsVersion(s string) bool { return verRegex.MatchString(s) } -// Compare for two string. +// IsVarName is valid variable name. +func IsVarName(s string) bool { return varRegex.MatchString(s) } + +// Compare for two strings. func Compare(s1, s2, op string) bool { return VersionCompare(s1, s2, op) } -// VersionCompare for two version string. +// VersionCompare for two version strings. func VersionCompare(v1, v2, op string) bool { switch op { case ">", "gt": @@ -211,12 +258,12 @@ func VersionCompare(v1, v2, op string) bool { } } -// SimpleMatch all sub-string in the give text string. +// SimpleMatch all substring in the give text string. // // Difference the ContainsAll: // // - start with ^ for exclude contains check. -// - end with $ for check end with keyword. +// - end with $ for the check end with keyword. func SimpleMatch(s string, keywords []string) bool { for _, keyword := range keywords { kln := len(keyword) @@ -245,7 +292,7 @@ func SimpleMatch(s string, keywords []string) bool { return true } -// QuickMatch check for a string. pattern can be a sub string. +// QuickMatch check for a string. pattern can be a substring. func QuickMatch(pattern, s string) bool { if strings.ContainsRune(pattern, '*') { return GlobMatch(pattern, s) @@ -303,7 +350,7 @@ func LikeMatch(pattern, s string) bool { // MatchNodePath check for a string match the pattern. // -// Use on pattern: +// Use on a pattern: // - `*` match any to sep // - `**` match any to end. only allow at start or end on pattern. // diff --git a/vendor/github.com/gookit/goutil/strutil/convbase.go b/vendor/github.com/gookit/goutil/strutil/convbase.go index 0d0aac89a..a405778a6 100644 --- a/vendor/github.com/gookit/goutil/strutil/convbase.go +++ b/vendor/github.com/gookit/goutil/strutil/convbase.go @@ -4,7 +4,7 @@ import ( "strconv" "strings" - "github.com/gookit/goutil/basefn" + "github.com/gookit/goutil/x/basefn" ) // @@ -20,10 +20,8 @@ const ( Base64Chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/" ) -// Base10Conv convert base 10 string to new base string. -func Base10Conv(src string, to int) string { - return BaseConv(src, 10, to) -} +// Base10Conv convert base10 string to new base string. +func Base10Conv(src string, to int) string { return BaseConv(src, 10, to) } // BaseConv convert base string by from and to base. // diff --git a/vendor/github.com/gookit/goutil/strutil/convert.go b/vendor/github.com/gookit/goutil/strutil/convert.go index c77615d17..d258621f8 100644 --- a/vendor/github.com/gookit/goutil/strutil/convert.go +++ b/vendor/github.com/gookit/goutil/strutil/convert.go @@ -71,6 +71,9 @@ func Join(sep string, ss ...string) string { return strings.Join(ss, sep) } // JoinList alias of strings.Join func JoinList(sep string, ss []string) string { return strings.Join(ss, sep) } +// JoinComma quick join strings by comma +func JoinComma(ss []string) string { return strings.Join(ss, ",") } + // JoinAny type to string func JoinAny(sep string, parts ...any) string { ss := make([]string, 0, len(parts)) @@ -100,7 +103,7 @@ func StringOrErr(val any) (string, error) { return ToStringWith(val) } // QuietString convert value to string, will ignore error. same as SafeString() func QuietString(val any) string { return SafeString(val) } -// SafeString convert value to string, will ignore error +// SafeString convert value to string. Will ignore error func SafeString(in any) string { s, _ := AnyToString(in, false) return s @@ -109,7 +112,7 @@ func SafeString(in any) string { // StringOrPanic convert value to string, will panic on error func StringOrPanic(val any) string { return MustString(val) } -// MustString convert value to string, will panic on error +// MustString convert value to string. will panic on error func MustString(val any) string { s, err := ToStringWith(val) if err != nil { @@ -161,13 +164,13 @@ func ToBool(s string) (bool, error) { // QuietBool convert to bool, will ignore error func QuietBool(s string) bool { return SafeBool(s) } -// SafeBool convert to bool, will ignore error +// SafeBool convert to bool and will ignore error func SafeBool(s string) bool { val, _ := comfunc.StrToBool(strings.TrimSpace(s)) return val } -// MustBool convert to bool, will panic on error +// MustBool convert to bool and will panic on error func MustBool(s string) bool { val, err := ToBool(s) if err != nil { diff --git a/vendor/github.com/gookit/goutil/strutil/ext.go b/vendor/github.com/gookit/goutil/strutil/ext.go index 5d2da7d27..2066810e8 100644 --- a/vendor/github.com/gookit/goutil/strutil/ext.go +++ b/vendor/github.com/gookit/goutil/strutil/ext.go @@ -3,6 +3,8 @@ package strutil import ( "fmt" "strings" + + "github.com/gookit/goutil/mathutil" ) // SimilarComparator definition @@ -36,7 +38,7 @@ func Similarity(s, t string, rate float32) (float32, bool) { // rate, ok :c.Similar(0.3) func (c *SimilarComparator) Similar(minDifferRate float32) (float32, bool) { dist := c.editDistance([]byte(c.src), []byte(c.dst)) - differRate := dist / float32(max(len(c.src), len(c.dst))+4) + differRate := dist / float32(mathutil.Max(len(c.src), len(c.dst))+4) return differRate, differRate >= minDifferRate } @@ -60,7 +62,7 @@ func (c *SimilarComparator) editDistance(s, t []byte) float32 { if s[i-1] == t[j-1] { d[i][j] = d[i-1][j-1] } else { - d[i][j] = min(d[i-1][j]+1, min(d[i][j-1]+1, d[i-1][j-1]+1)) + d[i][j] = mathutil.Min(d[i-1][j]+1, mathutil.Min(d[i][j-1]+1, d[i-1][j-1]+1)) } } } @@ -68,20 +70,6 @@ func (c *SimilarComparator) editDistance(s, t []byte) float32 { return d[m][n] } -func min(x, y float32) float32 { - if x < y { - return x - } - return y -} - -func max(x, y int) int { - if x > y { - return x - } - return y -} - // Builder struct type Builder struct { strings.Builder diff --git a/vendor/github.com/gookit/goutil/strutil/gensn.go b/vendor/github.com/gookit/goutil/strutil/gensn.go index 052ada265..31cf6cbf7 100644 --- a/vendor/github.com/gookit/goutil/strutil/gensn.go +++ b/vendor/github.com/gookit/goutil/strutil/gensn.go @@ -7,8 +7,8 @@ import ( "strconv" "time" - "github.com/gookit/goutil/basefn" "github.com/gookit/goutil/mathutil" + "github.com/gookit/goutil/x/basefn" ) // global id: @@ -30,23 +30,22 @@ var ( // b32id := Base10Conv(mtId, 32) // eg: em1jia8akl78n len: 13 // b36id := Base10Conv(mtId, 36) // eg: 3ko088phiuoev len: 13 // b62id := Base10Conv(mtId, 62) // eg: kb24SKgsQ9V len: 11 -func MicroTimeID() string { - return MTimeBaseID(10) -} +func MicroTimeID() string { return MTimeBaseID(10) } -// MicroTimeHexID micro time hex id generate. +// MicroTimeHexID micro time HEX ID generate. // // return like: 5b5f0588af1761ad3(len: 16-17) func MicroTimeHexID() string { return MTimeHexID() } -// MTimeHexID micro time hex id generate. +// MTimeHexID micro time HEX ID generate. // // return like: 5b5f0588af1761ad3(len: 16-17) -func MTimeHexID() string { - return MTimeBaseID(16) -} +func MTimeHexID() string { return MTimeBaseID(16) } -// MTimeBaseID micro time BASE id generate. toBase: 2 - 36 +// MTimeBase36 micro time BASE36 id generate. +func MTimeBase36() string { return MTimeBaseID(36) } + +// MTimeBaseID micro time BASE id generate. toBase: 2-36 // // Examples: // - MTimeBaseID(16): 5b5f0588af1761ad3(len: 16-17) @@ -61,9 +60,7 @@ func MTimeBaseID(toBase int) string { // // - No prefix, return like: 2023041410484904074285478388(len: 28) // - With prefix, return like: prefix2023041410484904074285478388(len: 28 + len(prefix)) -func DatetimeNo(prefix string) string { - return DateSN(prefix) -} +func DatetimeNo(prefix string) string { return DateSN(prefix) } // DateSN generate date serial number. PREFIX + yyyyMMddHHmmss + ext(微秒+随机数) func DateSN(prefix string) string { diff --git a/vendor/github.com/gookit/goutil/strutil/hash.go b/vendor/github.com/gookit/goutil/strutil/hash.go index 9cfa239fc..a9435220a 100644 --- a/vendor/github.com/gookit/goutil/strutil/hash.go +++ b/vendor/github.com/gookit/goutil/strutil/hash.go @@ -4,14 +4,14 @@ import ( "crypto/hmac" "crypto/sha256" "encoding/hex" + "math/big" + "strings" "github.com/gookit/goutil/byteutil" ) // Md5 Generate a 32-bit md5 string -func Md5(src any) string { - return string(Md5Bytes(src)) -} +func Md5(src any) string { return string(Md5Bytes(src)) } // MD5 Generate a 32-bit md5 string func MD5(src any) string { return Md5(src) } @@ -19,16 +19,55 @@ func MD5(src any) string { return Md5(src) } // GenMd5 Generate a 32-bit md5 string func GenMd5(src any) string { return Md5(src) } -// Md5Bytes Generate a 32-bit md5 bytes -func Md5Bytes(src any) []byte { - return byteutil.Md5(src) +// Md5Simple md5 加密原始二进制的每个byte转为 base62,缩短长度(16) +func Md5Simple(src any) string { + md5Bytes := byteutil.Md5Sum(src) + b62str := new(strings.Builder) + b62str.Grow(16) + + // 直接将每个 byte 转为 base62 数字然后拼接 + for _, b := range md5Bytes { + b62str.WriteByte(Base62Chars[b%0x3E]) + } + return b62str.String() } -// ShortMd5 Generate a 16-bit md5 string. remove first 8 and last 8 bytes from 32-bit md5 string. -func ShortMd5(src any) string { - return string(byteutil.ShortMd5(src)) +// Md5Base62 md5 加密原始二进制的转为 base62 字符串,缩短长度(21~22) +func Md5Base62(src any) string { + md5Bytes := byteutil.Md5Sum(src) + + // Step 2: 将字节数组转为 big.Int + number := new(big.Int) + number.SetBytes(md5Bytes) + + // Step 3: 转换为 Base62 字符串 + base62 := new(strings.Builder) + // base62.Grow(22) + for number.Sign() > 0 { + remainder := new(big.Int) + number.DivMod(number, big.NewInt(62), remainder) + base62.WriteByte(Base62Chars[remainder.Int64()]) + } + + // 反转字符串以得到正确的顺序 + runes := []rune(base62.String()) + for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { + runes[i], runes[j] = runes[j], runes[i] + } + + return string(runes) } +// Md5Bytes Generate a 32-bit md5 bytes +func Md5Bytes(src any) []byte { return byteutil.Md5(src) } + +// ShortMd5 Generate a 16-bit md5 string. remove the first 8 and last 8 bytes from 32-bit md5 string. +func ShortMd5(src any) string { return string(byteutil.ShortMd5(src)) } + +// +// ----------------------- hash password ----------------------------- +// + // HashPasswd for quick hash an input password string func HashPasswd(pwd, key string) string { hm := hmac.New(sha256.New, []byte(key)) diff --git a/vendor/github.com/gookit/goutil/strutil/parse.go b/vendor/github.com/gookit/goutil/strutil/parse.go index 8a9713367..c03cb125d 100644 --- a/vendor/github.com/gookit/goutil/strutil/parse.go +++ b/vendor/github.com/gookit/goutil/strutil/parse.go @@ -19,7 +19,7 @@ func MustToTime(s string, layouts ...string) time.Time { return t } -// auto match use some commonly layouts. +// auto match uses some common layouts. // key is layout length. var layoutMap = map[int][]string{ 6: {"200601", "060102", time.Kitchen}, diff --git a/vendor/github.com/gookit/goutil/strutil/random.go b/vendor/github.com/gookit/goutil/strutil/random.go index c0d20ac2a..5960cf8dd 100644 --- a/vendor/github.com/gookit/goutil/strutil/random.go +++ b/vendor/github.com/gookit/goutil/strutil/random.go @@ -2,10 +2,10 @@ package strutil import ( "github.com/gookit/goutil/byteutil" - "github.com/gookit/goutil/encodes" + "github.com/gookit/goutil/x/encodes" ) -// some constants string chars +// some constant string chars const ( Numbers = "0123456789" HexChars = "0123456789abcdef" // base16 diff --git a/vendor/github.com/gookit/goutil/strutil/runes.go b/vendor/github.com/gookit/goutil/strutil/runes.go index 246ad64ad..6a3debba0 100644 --- a/vendor/github.com/gookit/goutil/strutil/runes.go +++ b/vendor/github.com/gookit/goutil/strutil/runes.go @@ -115,6 +115,10 @@ func Utf8Truncate(s string, w int, tail string) string { return string(r[0:i]) + tail } +// Chunk split string to chunks by size. +// func Chunk[T ~string](s T, size int) []T { +// } + // TextSplit alias of the Utf8Split() func TextSplit(s string, w int) []string { return Utf8Split(s, w) } diff --git a/vendor/github.com/gookit/goutil/strutil/split.go b/vendor/github.com/gookit/goutil/strutil/split.go index 124665142..bb1abb07e 100644 --- a/vendor/github.com/gookit/goutil/strutil/split.go +++ b/vendor/github.com/gookit/goutil/strutil/split.go @@ -41,7 +41,7 @@ func AfterLast(s, sep string) string { * String split operation *************************************************************/ -// Cut same of the strings.Cut +// Cut alias of the strings.Cut func Cut(s, sep string) (before string, after string, found bool) { if i := strings.Index(s, sep); i >= 0 { return s[:i], s[i+len(sep):], true @@ -169,27 +169,25 @@ func Substr(s string, pos, length int) string { return string(runes[pos:stopIdx]) } -// SplitInlineComment for an inline text string. +// SplitInlineComment for an inline text string. default is strict mode. func SplitInlineComment(val string, strict ...bool) (string, string) { - // strict check: must with space - if len(strict) > 0 && strict[0] { + // strict check: must with a space + if len(strict) == 0 || strict[0] { if pos := strings.Index(val, " #"); pos > -1 { return strings.TrimRight(val[0:pos], " "), val[pos+1:] } - if pos := strings.Index(val, " //"); pos > -1 { return strings.TrimRight(val[0:pos], " "), val[pos+1:] } - } else { - if pos := strings.IndexByte(val, '#'); pos > -1 { - return strings.TrimRight(val[0:pos], " "), val[pos:] - } - - if pos := strings.Index(val, "//"); pos > -1 { - return strings.TrimRight(val[0:pos], " "), val[pos:] - } + return val, "" } + if pos := strings.IndexByte(val, '#'); pos > -1 { + return strings.TrimRight(val[0:pos], " "), val[pos:] + } + if pos := strings.Index(val, "//"); pos > -1 { + return strings.TrimRight(val[0:pos], " "), val[pos:] + } return val, "" } diff --git a/vendor/github.com/gookit/goutil/strutil/textutil/litetpl.go b/vendor/github.com/gookit/goutil/strutil/textutil/litetpl.go index e9faeccf4..be5cc99c9 100644 --- a/vendor/github.com/gookit/goutil/strutil/textutil/litetpl.go +++ b/vendor/github.com/gookit/goutil/strutil/textutil/litetpl.go @@ -8,11 +8,11 @@ import ( "text/template" "github.com/gookit/goutil/arrutil" - "github.com/gookit/goutil/basefn" "github.com/gookit/goutil/fsutil" "github.com/gookit/goutil/reflects" "github.com/gookit/goutil/structs" "github.com/gookit/goutil/strutil" + "github.com/gookit/goutil/x/basefn" ) // LTemplateOptFn lite template option func diff --git a/vendor/github.com/gookit/goutil/strutil/textutil/strvar_expr.go b/vendor/github.com/gookit/goutil/strutil/textutil/strvar_expr.go new file mode 100644 index 000000000..bcfff8d24 --- /dev/null +++ b/vendor/github.com/gookit/goutil/strutil/textutil/strvar_expr.go @@ -0,0 +1,181 @@ +package textutil + +import ( + "fmt" + "regexp" + "strings" + + "github.com/gookit/goutil/maputil" + "github.com/gookit/goutil/reflects" + "github.com/gookit/goutil/strutil" +) + +// type SimpleAnyFunc func(args ...any) any + +// StrVarRenderer implements like shell vars renderer +// 简单的实现类似 php, kotlin, shell 插值变量渲染,表达式解析处理。 +// +// - var format: $var_name, ${some_var}, ${top.sub_var} +// - func call: ${func($var_name, 'const string')} +type StrVarRenderer struct { + // global variables + vars map[string]any + // fallback func for var not exists + getter FallbackFn + // funcMap map[string]any TODO use any, add reflect value to rfs + funcMap map[string]func(...any) any + // var func map. refer the text/template TODO + // + // Func allow return 1 or 2 values, if return 2 values, the second value is error. + rfs map[string]*reflects.FuncX +} + +// NewStrVarRenderer create a new StrVarRenderer +func NewStrVarRenderer() *StrVarRenderer { + return &StrVarRenderer{ + vars: make(map[string]any), + // funcMap: make(map[string]any), + funcMap: make(map[string]func(...any) any), + } +} + +// SetVars set variables +func (r *StrVarRenderer) SetVars(vars map[string]any) *StrVarRenderer { + for k, v := range vars { + r.vars[k] = v + } + return r +} + +// SetVar set a variable +func (r *StrVarRenderer) SetVar(name string, value any) *StrVarRenderer { + r.vars[name] = value + return r +} + +// SetFuncMap set function map +func (r *StrVarRenderer) SetFuncMap(funcMap map[string]func(...any) any) *StrVarRenderer { + for k, v := range funcMap { + r.funcMap[k] = v + } + return r +} + +// SetFunc set a function +func (r *StrVarRenderer) SetFunc(name string, fn func(...any) any) *StrVarRenderer { + r.funcMap[name] = fn + return r +} + +// SetGetter set variable getter +func (r *StrVarRenderer) SetGetter(getter FallbackFn) *StrVarRenderer { + r.getter = getter + return r +} + +var ( + // 处理 $var_name 格式 + // - 允许:$1..$N 这样的变量 + // - 也支持 $@, $* 变量 + reS = regexp.MustCompile(`\$(\w[a-zA-Z0-9_]*|[@|*])`) + // 处理 ${var_name} ${top.sub} 格式 + reQ = regexp.MustCompile(`\$\{([a-zA-Z][a-zA-Z0-9_.]*)\}`) + // 处理 ${func(...)} 格式 + reFn = regexp.MustCompile(`\$\{([a-zA-Z][a-zA-Z0-9_]*)\(([^}]*)\)\}`) +) + +// Render rendering input string with variables +func (r *StrVarRenderer) Render(input string, vars map[string]any) string { + vars = maputil.Merge1level(r.vars, vars) + data := maputil.Map(vars) + + // 处理 $var_name 格式 + input = r.replaceVars(input, reS, data) + + if strings.Contains(input, "${") { + // 处理 ${var.name} 格式 + input = r.replaceVars(input, reQ, data) + // 处理 ${func(...)} 格式 + input = r.handleFuncCalls(input, reFn, data) + } + + return input +} + +func (r *StrVarRenderer) handleFuncCalls(input string, re *regexp.Regexp, vars maputil.Map) string { + return re.ReplaceAllStringFunc(input, func(matched string) string { + submatch := re.FindStringSubmatch(matched) + funcName := submatch[1] + + // 解析参数 并 调用函数 + if fn, ok := r.funcMap[funcName]; ok { + args := r.parseArgs(submatch[2], vars) + result := fn(args...) + return fmt.Sprint(result) + } + + return matched + }) +} + +func (r *StrVarRenderer) parseArgs(argsStr string, data maputil.Map) []any { + if argsStr == "" { + return []any{} + } + + // 简单参数解析,按逗号分割 + args := strings.Split(argsStr, ",") + results := make([]any, len(args)) + + for i, arg := range args { + arg = strings.TrimSpace(arg) + // is var name + if arg[0] == '$' { + results[i] = data.Get(arg[1:]) + continue + } + + // 常量值:去掉引号 + last := len(arg) - 1 + // strconv.Unquote( arg) + if (arg[0] == '"' && arg[last] == '"') || (arg[0] == '\'' && arg[last] == '\'') { + results[i] = arg[1 : len(arg)-1] + } else if arg == "true" || arg == "false" { + results[i] = strutil.SafeBool(arg) + } else if strutil.IsInt(arg) { + results[i] = strutil.SafeInt(arg) + } else { + results[i] = arg + } + } + + return results +} + +func (r *StrVarRenderer) replaceVars(input string, re *regexp.Regexp, data maputil.Map) string { + return re.ReplaceAllStringFunc(input, func(matched string) string { + var varName string + + // format: ${var_name} 提取变量名 + if strings.HasPrefix(matched, "${") { + varName = matched[2 : len(matched)-1] + } else { + // format: $var_name + varName = matched[1:] + } + + // 从 vars map 获取值,支持嵌套变量名 + if val, ok := data.GetByPath(varName); ok { + return fmt.Sprint(val) + } + + // fallback: 使用 getter fn 获取值 + if r.getter != nil { + if val, ok := r.getter(varName); ok { + return fmt.Sprint(val) + } + } + + return matched + }) +} diff --git a/vendor/github.com/gookit/goutil/strutil/textutil/texttpl.go b/vendor/github.com/gookit/goutil/strutil/textutil/texttpl.go index ba4d50971..d0054fd3b 100644 --- a/vendor/github.com/gookit/goutil/strutil/textutil/texttpl.go +++ b/vendor/github.com/gookit/goutil/strutil/textutil/texttpl.go @@ -7,9 +7,9 @@ import ( "text/template" "github.com/gookit/goutil" - "github.com/gookit/goutil/basefn" "github.com/gookit/goutil/fsutil" "github.com/gookit/goutil/strutil" + "github.com/gookit/goutil/x/basefn" ) var builtInFuncs = template.FuncMap{ diff --git a/vendor/github.com/gookit/goutil/strutil/textutil/textutil.go b/vendor/github.com/gookit/goutil/strutil/textutil/textutil.go index a269b1523..7ade12c09 100644 --- a/vendor/github.com/gookit/goutil/strutil/textutil/textutil.go +++ b/vendor/github.com/gookit/goutil/strutil/textutil/textutil.go @@ -6,20 +6,21 @@ import ( "strings" "github.com/gookit/goutil/arrutil" + "github.com/gookit/goutil/internal/comfunc" "github.com/gookit/goutil/maputil" "github.com/gookit/goutil/strutil" ) // ReplaceVars by regex replace given tpl vars. // -// If format is empty, will use {const defaultVarFormat} +// If a format is empty, will use {const defaultVarFormat} func ReplaceVars(text string, vars map[string]any, format string) string { return NewVarReplacer(format).Replace(text, vars) } -// RenderSMap by regex replace given tpl vars. +// RenderSMap by regex replacement given tpl vars. // -// If format is empty, will use {const defaultVarFormat} +// If a format is empty, will use {const defaultVarFormat} func RenderSMap(text string, vars map[string]string, format string) string { return NewVarReplacer(format).RenderSimple(text, vars) } @@ -62,3 +63,16 @@ func ParseInlineINI(tagVal string, keys ...string) (mp maputil.SMap, err error) } return } + +// ParseSimpleINI parse simple multiline config string to a string-map. +// Can use to parse simple INI or dotenv file contents. +// +// NOTE: +// +// - it's like INI format contents. +// - support comments line with: "#", ";", "//" +// - support inline comments with: " #" eg: name=tom # a comments +// - DON'T support submap parse. +func ParseSimpleINI(text string) (mp maputil.SMap, err error) { + return comfunc.ParseEnvLines(text, comfunc.ParseEnvLineOption{}) +} diff --git a/vendor/github.com/gookit/goutil/strutil/textutil/var_replacer.go b/vendor/github.com/gookit/goutil/strutil/textutil/var_replacer.go index 748245943..40044f300 100644 --- a/vendor/github.com/gookit/goutil/strutil/textutil/var_replacer.go +++ b/vendor/github.com/gookit/goutil/strutil/textutil/var_replacer.go @@ -24,12 +24,19 @@ type VarReplacer struct { lLen, rLen int varReg *regexp.Regexp - // flatten sub map in vars + // flatten sub map in vars. default: true + // + // eg: {name: {a: 1, b: 2}} => {name.a: 1, name.b: 2} flatSubs bool + // do parse env value. default: false parseEnv bool - // support parse default value. eg: {{ name | inhere }} + // do parse default value. default: false + // + // eg: {{ name | inhere }} parseDef bool - // keepMissVars list. default False: will clear on each replace + // keepMissVars list. + // + // default: False - will clear on each replacement keepMissVars bool // missing vars list missVars []string @@ -43,7 +50,9 @@ type VarReplacer struct { // // Usage: // -// rpl := NewVarReplacer("{{,}}") +// rpl := NewVarReplacer("{{,}}") // access var: {{ var }}, {{ top.sub }} +// // or +// rpl := NewVarReplacer("$") // access var: $var, $top.sub func NewVarReplacer(format string, opFns ...func(vp *VarReplacer)) *VarReplacer { vp := &VarReplacer{flatSubs: true} for _, fn := range opFns { @@ -75,7 +84,7 @@ func (r *VarReplacer) KeepMissingVars() *VarReplacer { return r } -// WithParseDefault value on the input template contents +// WithParseDefault value on the input template contents. eg: {{ name | inhere }} func (r *VarReplacer) WithParseDefault() *VarReplacer { r.parseDef = true return r @@ -87,7 +96,7 @@ func (r *VarReplacer) WithParseEnv() *VarReplacer { return r } -// OnNotFound var handle +// OnNotFound var handle func func (r *VarReplacer) OnNotFound(fn FallbackFn) *VarReplacer { r.NotFound = fn return r @@ -115,7 +124,7 @@ func (r *VarReplacer) Init() { } } -// ParseVars the text contents and collect vars +// ParseVars parse the text contents and collect vars func (r *VarReplacer) ParseVars(s string) []string { ss := arrutil.StringsMap(r.varReg.FindAllString(s, -1), func(val string) string { return strings.TrimSpace(val[r.lLen : len(val)-r.rLen]) @@ -195,7 +204,7 @@ func (r *VarReplacer) ResetMissVars() { // Replace string-map vars in the text contents func (r *VarReplacer) doReplace(s string, varMap map[string]string) string { if !r.keepMissVars { - r.missVars = make([]string, 0) // clear on each replace + r.missVars = make([]string, 0) // clear on each replacement } // use custom render func @@ -215,6 +224,7 @@ func (r *VarReplacer) doReplace(s string, varMap map[string]string) string { return val } + // has custom not found handle func if r.NotFound != nil { if val, ok := r.NotFound(name); ok { return val diff --git a/vendor/github.com/gookit/goutil/syncs/chan.go b/vendor/github.com/gookit/goutil/syncs/chan.go index ac89e8cd7..458d1aedf 100644 --- a/vendor/github.com/gookit/goutil/syncs/chan.go +++ b/vendor/github.com/gookit/goutil/syncs/chan.go @@ -1,28 +1,5 @@ package syncs -import ( - "os" - "os/signal" - "syscall" -) - -// WaitCloseSignals for some huang program. -// -// Usage: -// -// // do something. eg: start a http server -// -// syncs.WaitCloseSignals(func(sig os.Signal) { -// // do something -// }) -func WaitCloseSignals(onClose func(sig os.Signal)) { - signals := make(chan os.Signal, 1) - signal.Notify(signals, os.Interrupt, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) - - // block until a signal is received. - onClose(<-signals) -} - // Go is a basic promise implementation: it wraps calls a function in a goroutine // and returns a channel which will later return the function's return value. func Go(f func() error) error { @@ -30,6 +7,5 @@ func Go(f func() error) error { go func() { ch <- f() }() - return <-ch } diff --git a/vendor/github.com/gookit/goutil/syncs/signal.go b/vendor/github.com/gookit/goutil/syncs/signal.go index 41f032ce0..1da1c3310 100644 --- a/vendor/github.com/gookit/goutil/syncs/signal.go +++ b/vendor/github.com/gookit/goutil/syncs/signal.go @@ -5,8 +5,33 @@ import ( "fmt" "os" "os/signal" + "syscall" ) +// WaitCloseSignals for some huang program. +// +// Usage: +// +// // do something. eg: start a http server +// +// syncs.WaitCloseSignals(func(sig os.Signal) { +// // do something on shutdown. eg: close db, flush logs +// }) +func WaitCloseSignals(onClose func(sig os.Signal), sigCh ...chan os.Signal) { + var signals chan os.Signal + if len(sigCh) > 0 && sigCh[0] != nil { + signals = sigCh[0] + } else { + signals = make(chan os.Signal, 1) + } + + signal.Notify(signals, os.Interrupt, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) + + // block until a signal is received. + onClose(<-signals) + close(signals) +} + // SignalHandler returns an actor, i.e. an execute and interrupt func, that // terminates with SignalError when the process receives one of the provided // signals, or the parent context is canceled. @@ -18,6 +43,7 @@ func SignalHandler(ctx context.Context, signals ...os.Signal) (execute func() er c := make(chan os.Signal, 1) signal.Notify(c, signals...) defer signal.Stop(c) + select { case sig := <-c: return SignalError{Signal: sig} diff --git a/vendor/github.com/gookit/goutil/syncs/syncs.go b/vendor/github.com/gookit/goutil/syncs/syncs.go index 45164de73..e8933afdc 100644 --- a/vendor/github.com/gookit/goutil/syncs/syncs.go +++ b/vendor/github.com/gookit/goutil/syncs/syncs.go @@ -1,2 +1,18 @@ // Package syncs provides synchronization primitives util functions. package syncs + +import "sync" + +// WaitGroup is a wrapper of sync.WaitGroup. +type WaitGroup struct { + sync.WaitGroup +} + +// Go runs the given function in a new goroutine. will auto call Add and Done. +func (wg *WaitGroup) Go(fn func()) { + wg.Add(1) + go func() { + defer wg.Done() + fn() + }() +} diff --git a/vendor/github.com/gookit/goutil/sysutil/README.md b/vendor/github.com/gookit/goutil/sysutil/README.md index 79c2b49e5..54e5102df 100644 --- a/vendor/github.com/gookit/goutil/sysutil/README.md +++ b/vendor/github.com/gookit/goutil/sysutil/README.md @@ -88,6 +88,8 @@ err = rr.Run() ### Functions API +> generate by: `go doc ./sysutil` + ```go func BinDir() string func BinFile() string diff --git a/vendor/github.com/gookit/goutil/sysutil/cmdr/cmd.go b/vendor/github.com/gookit/goutil/sysutil/cmdr/cmd.go index f66e9f619..686d73088 100644 --- a/vendor/github.com/gookit/goutil/sysutil/cmdr/cmd.go +++ b/vendor/github.com/gookit/goutil/sysutil/cmdr/cmd.go @@ -19,10 +19,12 @@ type Cmd struct { *exec.Cmd // Name of the command Name string - // DryRun if True, not real execute command + // DryRun setting. if True, not really execute command DryRun bool - // Vars mapping + // Vars mapping TODO Vars map[string]string + // PrintLine placeholder for print command cline. + PrintLine string // BeforeRun hook BeforeRun func(c *Cmd) @@ -87,6 +89,12 @@ func (c *Cmd) PrintCmdline() *Cmd { return c } +// PrintCmdline2 on exec command +func (c *Cmd) PrintCmdline2() *Cmd { + c.BeforeRun = PrintCmdline2 + return c +} + // OnBefore exec add hook func (c *Cmd) OnBefore(fn func(c *Cmd)) *Cmd { c.BeforeRun = fn @@ -135,7 +143,7 @@ func (c *Cmd) WithWorkDir(dir string) *Cmd { // WorkDirOnNE set workdir on input is not empty func (c *Cmd) WorkDirOnNE(dir string) *Cmd { - if dir == "" { + if dir != "" { c.Dir = dir } return c @@ -169,9 +177,7 @@ func (c *Cmd) AppendEnv(mp map[string]string) *Cmd { } // OutputToOS output to OS stdout and error -func (c *Cmd) OutputToOS() *Cmd { - return c.ToOSStdoutStderr() -} +func (c *Cmd) OutputToOS() *Cmd { return c.ToOSStdoutStderr() } // ToOSStdoutStderr output to OS stdout and error func (c *Cmd) ToOSStdoutStderr() *Cmd { @@ -208,27 +214,25 @@ func (c *Cmd) WithAnyArgs(args ...any) *Cmd { return c } -// AddArg add args and returns the current object +// AddArg add args and return the current object func (c *Cmd) AddArg(args ...string) *Cmd { return c.WithArg(args...) } -// WithArg add args and returns the current object. alias of the WithArg() +// WithArg add args and return the current object. alias of the WithArg() func (c *Cmd) WithArg(args ...string) *Cmd { c.Args = append(c.Args, args...) return c } -// AddArgf add args and returns the current object. alias of the WithArgf() -func (c *Cmd) AddArgf(format string, args ...any) *Cmd { - return c.WithArgf(format, args...) -} +// AddArgf add args and return the current object. alias of the WithArgf() +func (c *Cmd) AddArgf(format string, args ...any) *Cmd { return c.WithArgf(format, args...) } -// WithArgf add arg and returns the current object +// WithArgf add arg and return the current object func (c *Cmd) WithArgf(format string, args ...any) *Cmd { c.Args = append(c.Args, fmt.Sprintf(format, args...)) return c } -// ArgIf add arg and returns the current object +// ArgIf add arg and return the current object func (c *Cmd) ArgIf(arg string, exprOk bool) *Cmd { if exprOk { c.Args = append(c.Args, arg) @@ -236,7 +240,7 @@ func (c *Cmd) ArgIf(arg string, exprOk bool) *Cmd { return c } -// WithArgIf add arg and returns the current object +// WithArgIf add arg and return the current object func (c *Cmd) WithArgIf(arg string, exprOk bool) *Cmd { return c.ArgIf(arg, exprOk) } @@ -252,7 +256,7 @@ func (c *Cmd) WithArgs(args []string) *Cmd { return c } -// WithArgsIf add arg and returns the current object +// WithArgsIf add arg and return the current object func (c *Cmd) WithArgsIf(args []string, exprOk bool) *Cmd { if exprOk && len(args) > 0 { c.Args = append(c.Args, args...) @@ -260,7 +264,7 @@ func (c *Cmd) WithArgsIf(args []string, exprOk bool) *Cmd { return c } -// WithVars add vars and returns the current object +// WithVars add vars and return the current object func (c *Cmd) WithVars(vs map[string]string) *Cmd { if len(vs) > 0 { c.Vars = vs @@ -268,7 +272,7 @@ func (c *Cmd) WithVars(vs map[string]string) *Cmd { return c } -// SetVar add var and returns the current object +// SetVar add var and return the current object func (c *Cmd) SetVar(name, val string) *Cmd { c.Vars[name] = val return c @@ -320,12 +324,16 @@ func (c *Cmd) ResetArgs() { } // Workdir of the command -func (c *Cmd) Workdir() string { - return c.Dir -} +func (c *Cmd) Workdir() string { return c.Dir } // Cmdline to command line -func (c *Cmd) Cmdline() string { +func (c *Cmd) Cmdline() string { return comfunc.Cmdline(c.Args) } + +// RawLine raw command line for print show. +func (c *Cmd) RawLine() string { + if c.PrintLine != "" { + return c.PrintLine + } return comfunc.Cmdline(c.Args) } diff --git a/vendor/github.com/gookit/goutil/sysutil/cmdr/cmdr.go b/vendor/github.com/gookit/goutil/sysutil/cmdr/cmdr.go index 09161c474..37ee20e68 100644 --- a/vendor/github.com/gookit/goutil/sysutil/cmdr/cmdr.go +++ b/vendor/github.com/gookit/goutil/sysutil/cmdr/cmdr.go @@ -4,15 +4,27 @@ package cmdr import ( "strings" - "github.com/gookit/color" + "github.com/gookit/goutil/x/ccolor" ) // PrintCmdline on before exec func PrintCmdline(c *Cmd) { if c.DryRun { - color.Yellowln("DRY-RUN>", c.Cmdline()) + ccolor.Yellowln("DRY-RUN>", c.RawLine()) } else { - color.Yellowln(">", c.Cmdline()) + ccolor.Yellowln(">", c.RawLine()) + } +} + +// PrintCmdline2 on before exec +func PrintCmdline2(c *Cmd) { + if c.Dir != "" { + ccolor.Magentaln("> Workdir:", c.Dir) + } + if c.DryRun { + ccolor.Yellowln("DRY-RUN>", c.RawLine()) + } else { + ccolor.Yellowln(">", c.RawLine()) } } diff --git a/vendor/github.com/gookit/goutil/sysutil/cmdr/runner.go b/vendor/github.com/gookit/goutil/sysutil/cmdr/runner.go index 35f81dd79..9d90aa4db 100644 --- a/vendor/github.com/gookit/goutil/sysutil/cmdr/runner.go +++ b/vendor/github.com/gookit/goutil/sysutil/cmdr/runner.go @@ -4,13 +4,13 @@ import ( "fmt" "strings" - "github.com/gookit/color" "github.com/gookit/goutil/arrutil" "github.com/gookit/goutil/cliutil/cmdline" "github.com/gookit/goutil/errorx" "github.com/gookit/goutil/maputil" "github.com/gookit/goutil/mathutil" "github.com/gookit/goutil/strutil/textutil" + "github.com/gookit/goutil/x/ccolor" ) // Task struct @@ -226,7 +226,7 @@ func (r *Runner) Run() error { } if r.DryRun { - color.Infof("DRY-RUN: task#%d execute completed\n\n", i+1) + ccolor.Infof("DRY-RUN: task#%d execute completed\n\n", i+1) continue } @@ -265,7 +265,7 @@ func (r *Runner) RunTask(task *Task) (goon bool) { // do running if err := task.RunWith(r.Params); err != nil { r.Errs[task.ID] = err - color.Errorf("Task#%d run error: %s\n", task.Index()+1, err) + ccolor.Errorf("Task#%d run error: %s\n", task.Index()+1, err) // not ignore error, stop. if !r.IgnoreErr { diff --git a/vendor/github.com/gookit/goutil/sysutil/os_version_windows.go b/vendor/github.com/gookit/goutil/sysutil/os_version_windows.go index b3c01e1a7..a9bb0c983 100644 --- a/vendor/github.com/gookit/goutil/sysutil/os_version_windows.go +++ b/vendor/github.com/gookit/goutil/sysutil/os_version_windows.go @@ -6,6 +6,7 @@ import ( "github.com/gookit/goutil/errorx" "github.com/gookit/goutil/strutil" + "golang.org/x/sys/windows" ) // OSVersionInfo 结构体用于存储操作系统版本信息 @@ -22,18 +23,42 @@ type OSVersionInfo struct { RevisionNumber uint32 } -// FetchOsVersion Get Windows system version information +// cache: 全局变量 +var stdOv = VersionInfoBySys() + +// OsVersion Get operating system version information +func OsVersion() *OSVersionInfo { return stdOv } + +// VersionInfoBySys Get Windows system version information by sys/windows +func VersionInfoBySys() *OSVersionInfo { + // Get the Windows Version and Build Number + var majorVersion, minorVersion, buildNumber = windows.RtlGetNtVersionNumbers() + + return &OSVersionInfo{ + MajorVersion: uint16(majorVersion), + MinorVersion: uint16(minorVersion), + BuildNumber: buildNumber, + } +} + +// OsVersionByParse Get Windows system version information by parse string +// +// cmdOut eg: "Microsoft Windows [Version 10.0.22631.4391]" +func OsVersionByParse(cmdOut string) (*OSVersionInfo, error) { + return parseOsVersionString(cmdOut) +} + +// OsVersionByVerCmd Get Windows system version information // // 还可用使用dll获取: // // 通过 GetVersion, GetVersionEx 函数获取的信息不准确. win11获取到 6.2.9200, 实际是 10.0.22631 -func FetchOsVersion() (*OSVersionInfo, error) { +func OsVersionByVerCmd() (*OSVersionInfo, error) { // Windows cmd 执行 ver 命令 out, err := ShellExec("ver", "cmd") if err != nil { return nil, err } - return parseOsVersionString(out) } @@ -47,9 +72,9 @@ func (ov *OSVersionInfo) IsWindows7() bool { return ov.MajorVersion == 6 && ov.MinorVersion == 1 } -// IsWindows8 判断是否为 Windows 8 +// IsWindows8 判断是否为 Windows 8/8.1 func (ov *OSVersionInfo) IsWindows8() bool { - return ov.MajorVersion == 6 && ov.MinorVersion == 2 + return ov.MajorVersion == 6 && (ov.MinorVersion == 2 || ov.MinorVersion == 3) } // IsWindows10 判断是否为 Windows 10 @@ -129,15 +154,3 @@ func parseOsVersionString(out string) (*OSVersionInfo, error) { return &ovi, nil } -// 全局变量 -var stdOv, stdErr = FetchOsVersion() - -// OsVersion Get operating system version information -func OsVersion() *OSVersionInfo { - return stdOv -} - -// OvParseError error on parse os version info -func OvParseError() error { - return stdErr -} diff --git a/vendor/github.com/gookit/goutil/sysutil/stack.go b/vendor/github.com/gookit/goutil/sysutil/stack.go deleted file mode 100644 index 7d4f8224d..000000000 --- a/vendor/github.com/gookit/goutil/sysutil/stack.go +++ /dev/null @@ -1,64 +0,0 @@ -package sysutil - -import ( - "runtime" - "strconv" -) - -// CallerInfo struct -type CallerInfo struct { - PC uintptr - Fc *runtime.Func - File string - Line int -} - -// String convert -func (ci *CallerInfo) String() string { - return ci.File + ":" + strconv.Itoa(ci.Line) -} - -// CallersInfos returns an array of the CallerInfo. -// -// Usage: -// -// cs := sysutil.CallersInfos(3, 2) -// for _, ci := range cs { -// fc := runtime.FuncForPC(pc) -// // maybe need check fc = nil -// fnName = fc.Name() -// } -func CallersInfos(skip, num int, filters ...func(file string, fc *runtime.Func) bool) []*CallerInfo { - filterLn := len(filters) - callers := make([]*CallerInfo, 0, num) - for i := skip; i < skip+num; i++ { - pc, file, line, ok := runtime.Caller(i) - if !ok { - // The breaks below failed to terminate the loop, and we ran off the - // end of the call stack. - break - } - - fc := runtime.FuncForPC(pc) - if fc == nil { - continue - } - - if filterLn > 0 && filters[0] != nil { - // filter - return false for skip - if !filters[0](file, fc) { - continue - } - } - - // collecting - callers = append(callers, &CallerInfo{ - PC: pc, - Fc: fc, - File: file, - Line: line, - }) - } - - return callers -} diff --git a/vendor/github.com/gookit/goutil/sysutil/sysenv.go b/vendor/github.com/gookit/goutil/sysutil/sysenv.go index 7eee8add1..0aac4d61c 100644 --- a/vendor/github.com/gookit/goutil/sysutil/sysenv.go +++ b/vendor/github.com/gookit/goutil/sysutil/sysenv.go @@ -13,12 +13,23 @@ import ( "golang.org/x/term" ) +// os names +const ( + Windows = "windows" + Linux = "linux" + Darwin = "darwin" + FreeBSD = "freebsd" +) + // IsMSys msys(MINGW64) env,不一定支持颜色 func IsMSys() bool { // "MSYSTEM=MINGW64" return len(os.Getenv("MSYSTEM")) > 0 } +// IsWSL system env +func IsWSL() bool { return checkfn.IsWSL() } + // IsConsole check out is in stderr/stdout/stdin // // Usage: @@ -61,8 +72,8 @@ func Hostname() string { // // eg "/bin/zsh" "/bin/bash". // if onlyName=true, will return "zsh", "bash" -func CurrentShell(onlyName bool) (path string) { - return comfunc.CurrentShell(onlyName) +func CurrentShell(onlyName bool, fallbackShell ...string) string { + return comfunc.CurrentShell(onlyName, fallbackShell...) } // HasShellEnv has shell env check. @@ -145,9 +156,14 @@ func EnvPaths() []string { return filepath.SplitList(os.Getenv("PATH")) } +// ToEnvPATH convert []string to $PATH string. +func ToEnvPATH(paths []string) string { + return strings.Join(paths, string(filepath.ListSeparator)) +} + // SearchPathOption settings for SearchPath type SearchPathOption struct { - // 限制的扩展名 + // 限制查找的扩展名(Windows) such as ".exe", ".bat", ".cmd" LimitExt []string } @@ -156,14 +172,18 @@ type SearchPathOption struct { // Usage: // // sysutil.SearchPath("go") -func SearchPath(keywords string, limit int) []string { +func SearchPath(keywords string, limit int, opts ...SearchPathOption) []string { path := os.Getenv("PATH") ptn := "*" + keywords + "*" list := make([]string, 0) + opt := SearchPathOption{LimitExt: []string{".exe", ".bat", ".cmd"}} + if len(opts) > 0 { + opt = opts[0] + } + // if windows, will limit with .exe, .bat, .cmd isWindows := IsWindows() - winExts := []string{".exe", ".bat", ".cmd"} checked := make(map[string]bool) for _, dir := range filepath.SplitList(path) { // Unix shell semantics: path element "" means "." @@ -183,7 +203,7 @@ func SearchPath(keywords string, limit int) []string { // if windows, will limit with .exe, .bat, .cmd for _, fPath := range matches { fExt := filepath.Ext(fPath) - if checkfn.StringsContains(winExts, fExt) { + if checkfn.StringsContains(opt.LimitExt, fExt) { continue } list = append(list, fPath) diff --git a/vendor/github.com/gookit/goutil/sysutil/sysgo.go b/vendor/github.com/gookit/goutil/sysutil/sysgo.go index a4c4dd758..42dde9990 100644 --- a/vendor/github.com/gookit/goutil/sysutil/sysgo.go +++ b/vendor/github.com/gookit/goutil/sysutil/sysgo.go @@ -3,7 +3,7 @@ package sysutil import ( "runtime" - "github.com/gookit/goutil/goinfo" + "github.com/gookit/goutil/x/goinfo" ) // GoVersion get go runtime version. eg: "1.18.2" @@ -14,6 +14,9 @@ func GoVersion() string { // GoInfo define. alias of goinfo.GoInfo type GoInfo = goinfo.GoInfo +// CallerInfo define. alias of goinfo.CallerInfo +type CallerInfo = goinfo.CallerInfo + // ParseGoVersion get info by parse `go version` results. alias of goinfo.ParseGoVersion() // // Examples: @@ -33,3 +36,8 @@ func ParseGoVersion(line string) (*GoInfo, error) { func OsGoInfo() (*GoInfo, error) { return goinfo.OsGoInfo() } + +// CallersInfos returns an array of the CallerInfo. can with filters +func CallersInfos(skip, num int, filters ...goinfo.CallerFilterFunc) []*CallerInfo { + return goinfo.CallersInfos(skip+1, num, filters...) +} diff --git a/vendor/github.com/gookit/goutil/sysutil/sysutil.go b/vendor/github.com/gookit/goutil/sysutil/sysutil.go index 60fe67b60..00cb17721 100644 --- a/vendor/github.com/gookit/goutil/sysutil/sysutil.go +++ b/vendor/github.com/gookit/goutil/sysutil/sysutil.go @@ -28,16 +28,12 @@ func BinFile() string { } // Open file or url address -func Open(fileOrURL string) error { - return OpenURL(fileOrURL) -} +func Open(fileOrURL string) error { return OpenURL(fileOrURL) } // OpenBrowser file or url address -func OpenBrowser(fileOrURL string) error { - return OpenURL(fileOrURL) -} +func OpenBrowser(fileOrURL string) error { return OpenURL(fileOrURL) } -// OpenFile opens new browser window for the file path. +// OpenFile open files browser window for the file path. func OpenFile(path string) error { fpath, err := filepath.Abs(path) if err != nil { diff --git a/vendor/github.com/gookit/goutil/sysutil/sysutil_darwin.go b/vendor/github.com/gookit/goutil/sysutil/sysutil_darwin.go index 3fe2da509..f0962d8a2 100644 --- a/vendor/github.com/gookit/goutil/sysutil/sysutil_darwin.go +++ b/vendor/github.com/gookit/goutil/sysutil/sysutil_darwin.go @@ -1,7 +1,11 @@ +//go:build darwin package sysutil import "os/exec" +// OsName system name. like runtime.GOOS. allow: linux, windows, darwin +const OsName = Darwin + // IsWin system. linux windows darwin func IsWin() bool { return false } diff --git a/vendor/github.com/gookit/goutil/sysutil/sysutil_unix.go b/vendor/github.com/gookit/goutil/sysutil/sysutil_linux.go similarity index 89% rename from vendor/github.com/gookit/goutil/sysutil/sysutil_unix.go rename to vendor/github.com/gookit/goutil/sysutil/sysutil_linux.go index 8059b09ab..eef2bb129 100644 --- a/vendor/github.com/gookit/goutil/sysutil/sysutil_unix.go +++ b/vendor/github.com/gookit/goutil/sysutil/sysutil_linux.go @@ -1,5 +1,3 @@ -//go:build !windows && !darwin - package sysutil import ( @@ -7,6 +5,9 @@ import ( "strings" ) +// OsName system name. like runtime.GOOS. allow: linux, windows, darwin +const OsName = Linux + // IsWin system. linux windows darwin func IsWin() bool { return false } @@ -20,9 +21,7 @@ func IsMac() bool { return false } func IsDarwin() bool { return false } // IsLinux system -func IsLinux() bool { - return true -} +func IsLinux() bool { return true } // There are multiple possible providers to open a browser on linux // One of them is xdg-open, another is x-www-browser, then there's www-browser, etc. diff --git a/vendor/github.com/gookit/goutil/sysutil/sysutil_windows.go b/vendor/github.com/gookit/goutil/sysutil/sysutil_windows.go index ba458b174..19e98abcb 100644 --- a/vendor/github.com/gookit/goutil/sysutil/sysutil_windows.go +++ b/vendor/github.com/gookit/goutil/sysutil/sysutil_windows.go @@ -10,6 +10,9 @@ import ( "golang.org/x/sys/windows" ) +// OsName system name. like runtime.GOOS. only allow: linux, windows, darwin +const OsName = Windows + // IsWin system. linux windows darwin func IsWin() bool { return true } @@ -27,7 +30,7 @@ func IsLinux() bool { return false } // Kill a process by pid func Kill(pid int, signal syscall.Signal) error { - return errors.New("not support") + return errors.New("not support on Windows") } // ProcessExists check process exists by pid @@ -35,7 +38,7 @@ func ProcessExists(pid int) bool { panic("TIP: please use sysutil/process.Exists()") } -// OpenURL Open file or browser URL +// OpenURL Open file or browser URL // // - refers https://github.com/pkg/browser // diff --git a/vendor/github.com/gookit/goutil/sysutil/user.go b/vendor/github.com/gookit/goutil/sysutil/user.go index 6fb64d0dc..7d97889ca 100644 --- a/vendor/github.com/gookit/goutil/sysutil/user.go +++ b/vendor/github.com/gookit/goutil/sysutil/user.go @@ -17,9 +17,7 @@ func MustFindUser(uname string) *user.User { } // LoginUser must get current user, will panic if error -func LoginUser() *user.User { - return CurrentUser() -} +func LoginUser() *user.User { return CurrentUser() } // CurrentUser must get current user, will panic if error func CurrentUser() *user.User { diff --git a/vendor/github.com/gookit/goutil/sysutil/user_nonwin.go b/vendor/github.com/gookit/goutil/sysutil/user_nonwin.go index 2f2d5e61b..dbcd9344c 100644 --- a/vendor/github.com/gookit/goutil/sysutil/user_nonwin.go +++ b/vendor/github.com/gookit/goutil/sysutil/user_nonwin.go @@ -18,7 +18,7 @@ func IsAdmin() bool { func ChangeUserByName(newUname string) error { u := MustFindUser(newUname) // syscall.Setlogin(newUname) - return ChangeUserUIDGid(strutil.IntOrPanic(u.Uid), strutil.IntOrPanic(u.Gid)) + return ChangeUserUIDGid(strutil.MustInt(u.Uid), strutil.MustInt(u.Gid)) } // ChangeUserUidGid change work user by new username uid,gid diff --git a/vendor/github.com/gookit/goutil/sysutil/user_windows.go b/vendor/github.com/gookit/goutil/sysutil/user_windows.go index fba825e96..ff3756493 100644 --- a/vendor/github.com/gookit/goutil/sysutil/user_windows.go +++ b/vendor/github.com/gookit/goutil/sysutil/user_windows.go @@ -1,17 +1,16 @@ //go:build windows -// +build windows package sysutil // ChangeUserByName change work user by new username. -func ChangeUserByName(newUname string) (err error) { +func ChangeUserByName(newUname string) error { return ChangeUserUIDGid(0, 0) } // ChangeUserUidGid change work user by new username uid,gid // // Deprecated: use ChangeUserUIDGid instead -func ChangeUserUidGid(newUid int, newGid int) (err error) { +func ChangeUserUidGid(newUid int, newGid int) error { return ChangeUserUIDGid(newUid, newGid) } diff --git a/vendor/github.com/gookit/goutil/basefn/basefn.go b/vendor/github.com/gookit/goutil/x/basefn/basefn.go similarity index 93% rename from vendor/github.com/gookit/goutil/basefn/basefn.go rename to vendor/github.com/gookit/goutil/x/basefn/basefn.go index 3694a3f48..ec9e8956e 100644 --- a/vendor/github.com/gookit/goutil/basefn/basefn.go +++ b/vendor/github.com/gookit/goutil/x/basefn/basefn.go @@ -11,7 +11,7 @@ func Panicf(format string, v ...any) { panic(fmt.Sprintf(format, v...)) } -// PanicIf if cond = true, panics with error message +// PanicIf if cond = true, panics with an error message func PanicIf(cond bool, fmtAndArgs ...any) { if cond { panic(errors.New(formatWithArgs(fmtAndArgs))) @@ -110,7 +110,7 @@ func FirstOr[T any](sl []T, elseVal T) T { return elseVal } -// OrValue get +// OrValue get. like: if cond { okVal } else { elVal } func OrValue[T any](cond bool, okVal, elVal T) T { if cond { return okVal @@ -119,6 +119,8 @@ func OrValue[T any](cond bool, okVal, elVal T) T { } // OrReturn call okFunc() on condition is true, else call elseFn() +// +// like expr: if cond { okFunc() } else { elseFn() } func OrReturn[T any](cond bool, okFn, elseFn func() T) T { if cond { return okFn() diff --git a/vendor/github.com/gookit/goutil/x/ccolor/README.md b/vendor/github.com/gookit/goutil/x/ccolor/README.md new file mode 100644 index 000000000..d1a0768f9 --- /dev/null +++ b/vendor/github.com/gookit/goutil/x/ccolor/README.md @@ -0,0 +1,13 @@ +# CColor + +`CColor` is a simple command-line color output library that uses ANSI color codes to output text with colors. +Its main code is a partial code extracted and simplified from `gookit/color`, which only supports simple 16-color ANSI color output. + +> Tip: If you want to render with richer colors, use the [github.com/gookit/color](https://github.com/gookit/color) package. + +## Install + +```bash +go get github.com/gookit/goutil/x/ccolor +``` + diff --git a/vendor/github.com/gookit/goutil/x/ccolor/README.zh-CN.md b/vendor/github.com/gookit/goutil/x/ccolor/README.zh-CN.md new file mode 100644 index 000000000..62ae6b07c --- /dev/null +++ b/vendor/github.com/gookit/goutil/x/ccolor/README.zh-CN.md @@ -0,0 +1,12 @@ +# CColor + +`CColor` 是一个简单的命令行颜色输出库,它使用 ANSI 颜色代码来输出带有颜色的文本。 +它的主要代码是抽取和简化自 `gookit/color` 的部分代码,仅支持简单的 16 色 ANSI 颜色输出。 + +> **提示**:如果想要使用更丰富的颜色渲染,请使用 [github.com/gookit/color](https://github.com/gookit/color) 包。 + +## 安装 + +```bash +go get github.com/gookit/goutil/x/ccolor +``` diff --git a/vendor/github.com/gookit/goutil/x/ccolor/ccolor.go b/vendor/github.com/gookit/goutil/x/ccolor/ccolor.go new file mode 100644 index 000000000..fe6c90206 --- /dev/null +++ b/vendor/github.com/gookit/goutil/x/ccolor/ccolor.go @@ -0,0 +1,139 @@ +// Package ccolor is a simple color render library for terminal. +// Its main code is the code that is extracted and simplified from gookit/color, +// +// TIP: +// +// If you want to render with richer colors, use the https://github.com/gookit/color package. +package ccolor + +import ( + "fmt" + "io" + "log" + "os" + "regexp" + + "github.com/gookit/goutil/x/termenv" +) + +// color render templates +// +// ESC 操作的表示: +// +// "\033"(Octal 8进制) = "\x1b"(Hexadecimal 16进制) = 27 (10进制) +const ( + // StartSet chars + StartSet = "\x1b[" + // ResetSet close all properties. + ResetSet = "\x1b[0m" + // SettingTpl string. + SettingTpl = "\x1b[%sm" + // FullColorTpl for build color code + FullColorTpl = "\x1b[%sm%s\x1b[0m" + // CodeSuffix string for color code. + CodeSuffix = "[0m" +) + +// CodeExpr regex to clear color codes eg "\033[1;36mText\x1b[0m" +const CodeExpr = `\033\[[\d;?]+m` + +var ( + // last error + lastErr error + // output the default io.Writer message print + output io.Writer = os.Stdout + // match color codes + codeRegex = regexp.MustCompile(CodeExpr) +) + +// SetOutput set output writer +func SetOutput(w io.Writer) { output = w } + +// LastErr info +func LastErr() error { + defer func() { + lastErr = nil + }() + return lastErr +} + +// +// ---------------- support detect from termenv ---------------- +// + +// Disable color of current terminal. +func Disable() { termenv.DisableColor() } + +// Level value of current terminal. +func Level() termenv.ColorLevel { return termenv.TermColorLevel() } + +// IsSupportColor returns true if the terminal supports color. +func IsSupportColor() bool { return termenv.IsSupportColor() } + +// IsSupport256Color returns true if the terminal supports 256 colors. +func IsSupport256Color() bool { return termenv.IsSupport256Color() } + +// IsSupportTrueColor returns true if the terminal supports true color. +func IsSupportTrueColor() bool { return termenv.IsSupportTrueColor() } + +// +// ---------------- for testing ---------------- +// + +// ForceEnableColor setting value. TIP: use for unit testing. +// +// Usage: +// +// ccolor.ForceEnableColor() +// defer ccolor.RevertColorSupport() +func ForceEnableColor() { + termenv.ForceEnableColor() +} + +// RevertColorSupport value +func RevertColorSupport() { + termenv.RevertColorSupport() +} + +// +// ---------------- print with color tag style ---------------- +// + +// Print parse color tag and print messages +func Print(v ...any) { Fprint(output, v...) } + +// Printf format and print messages +func Printf(format string, v ...any) { Fprintf(output, format, v...) } + +// Println messages with new line +func Println(v ...any) { Fprintln(output, v...) } + +// Sprint parse color tags, return rendered string +func Sprint(v ...any) string { + return ReplaceTag(fmt.Sprint(v...)) +} + +// Sprintf format and return rendered string +func Sprintf(format string, a ...any) string { + return ReplaceTag(fmt.Sprintf(format, a...)) +} + +// Fprint auto parse color-tag, print rendered messages to the writer +func Fprint(w io.Writer, v ...any) { + _, lastErr = fmt.Fprint(w, ReplaceTag(fmt.Sprint(v...))) +} + +// Fprintf auto parse color-tag, print rendered messages to the writer. +func Fprintf(w io.Writer, format string, v ...any) { + _, lastErr = fmt.Fprint(w, ReplaceTag(fmt.Sprintf(format, v...))) +} + +// Fprintln auto parse color-tag, print rendered messages to the writer +func Fprintln(w io.Writer, v ...any) { + _, lastErr = fmt.Fprintln(w, ReplaceTag(formatLikePrintln(v))) +} + +// Lprint passes colored messages to a log.Logger for printing. +func Lprint(l *log.Logger, v ...any) { + l.Print(ReplaceTag(fmt.Sprint(v...))) +} diff --git a/vendor/github.com/gookit/color/color_16.go b/vendor/github.com/gookit/goutil/x/ccolor/color16.go similarity index 66% rename from vendor/github.com/gookit/color/color_16.go rename to vendor/github.com/gookit/goutil/x/ccolor/color16.go index eda226a15..fa74a937d 100644 --- a/vendor/github.com/gookit/color/color_16.go +++ b/vendor/github.com/gookit/goutil/x/ccolor/color16.go @@ -1,4 +1,4 @@ -package color +package ccolor import ( "fmt" @@ -8,60 +8,28 @@ import ( // Color Color16, 16 color value type // 3(2^3=8) OR 4(2^4=16) bite color. type Color uint8 -type Basic = Color // alias of Color - -// Opts basic color options. code: 0 - 9 -type Opts []Color - -// Add option value -func (o *Opts) Add(ops ...Color) { - for _, op := range ops { - if uint8(op) < 10 { - *o = append(*o, op) - } - } -} - -// IsValid options -func (o Opts) IsValid() bool { - return len(o) > 0 -} - -// IsEmpty options -func (o Opts) IsEmpty() bool { - return len(o) == 0 -} - -// String options to string. eg: "1;3" -func (o Opts) String() string { - return Colors2code(o...) -} /************************************************************* * Basic 16 color definition *************************************************************/ -const ( - // OptMax max option value. range: 0 - 9 - OptMax = 10 - // DiffFgBg diff foreground and background color - DiffFgBg = 10 -) - // Boundary value for foreground/background color 16 // // - base: fg 30~37, bg 40~47 // - light: fg 90~97, bg 100~107 const ( - FgBase uint8 = 30 - FgMax uint8 = 37 - BgBase uint8 = 40 - BgMax uint8 = 47 + fgBase uint8 = 30 + fgMax uint8 = 37 + bgBase uint8 = 40 + bgMax uint8 = 47 - HiFgBase uint8 = 90 - HiFgMax uint8 = 97 - HiBgBase uint8 = 100 - HiBgMax uint8 = 107 + hiFgBase uint8 = 90 + hiFgMax uint8 = 97 + hiBgBase uint8 = 100 + hiBgMax uint8 = 107 + + // optMax max option value. range: 0 - 9 + optMax = 10 ) // Foreground colors. basic foreground colors 30 - 37 @@ -178,22 +146,10 @@ const ( BgHiMagenta = BgLightMagenta ) -// Bit4 a method for create Color -func Bit4(code uint8) Color { return Color(code) } - /************************************************************* * Color render methods *************************************************************/ -// Name get color code name. -func (c Color) Name() string { - name, ok := basic2nameMap[uint8(c)] - if ok { - return name - } - return "unknown" -} - // Text render a text message func (c Color) Text(message string) string { return RenderString(c.String(), message) } @@ -201,16 +157,16 @@ func (c Color) Text(message string) string { return RenderString(c.String(), mes // // Usage: // -// green := color.FgGreen.Render +// green := ccolor.FgGreen.Render // fmt.Println(green("message")) func (c Color) Render(a ...any) string { return RenderCode(c.String(), a...) } // Renderln messages by color setting. -// like Println, will add spaces for each argument +// like fmt.Println, will add spaces for each argument // // Usage: // -// green := color.FgGreen.Renderln +// green := ccolor.FgGreen.Renderln // fmt.Println(green("message")) func (c Color) Renderln(a ...any) string { return RenderWithSpaces(c.String(), a...) } @@ -221,8 +177,8 @@ func (c Color) Sprint(a ...any) string { return RenderCode(c.String(), a...) } // // Usage: // -// green := color.Green.Sprintf -// colored := green("message") +// green := ccolor.Green.Sprintf +// colored := green("message") func (c Color) Sprintf(format string, args ...any) string { return RenderString(c.String(), fmt.Sprintf(format, args...)) } @@ -231,27 +187,27 @@ func (c Color) Sprintf(format string, args ...any) string { // // Usage: // -// color.Green.Print("message") +// ccolor.Green.Print("message") // // OR: // -// green := color.FgGreen.Print +// green := ccolor.FgGreen.Print // green("message") func (c Color) Print(args ...any) { - doPrintV2(c.Code(), fmt.Sprint(args...)) + doPrint(c.Code(), fmt.Sprint(args...)) } // Printf format and print messages. // // Usage: // -// color.Cyan.Printf("string %s", "arg0") +// ccolor.Cyan.Printf("string %s", "arg0") func (c Color) Printf(format string, a ...any) { - doPrintV2(c.Code(), fmt.Sprintf(format, a...)) + doPrint(c.Code(), fmt.Sprintf(format, a...)) } // Println messages with new line -func (c Color) Println(a ...any) { doPrintlnV2(c.String(), a) } +func (c Color) Println(a ...any) { doPrintln(c.String(), a) } // Light current color. eg: 36(FgCyan) -> 96(FgLightCyan). // @@ -285,30 +241,6 @@ func (c Color) Darken() Color { return c } -// C256 convert 16 color to 256-color code. -func (c Color) C256() Color256 { - val := uint8(c) - if val < 10 { // is option code - return emptyC256 // empty - } - - var isBg uint8 - if val >= BgBase && val <= 47 { // is bg - isBg = AsBg - val = val - 10 // to fg code - } else if val >= HiBgBase && val <= 107 { // is hi bg - isBg = AsBg - val = val - 10 // to fg code - } - - if c256, ok := basicTo256Map[val]; ok { - return Color256{c256, isBg} - } - - // use raw value direct convert - return Color256{val} -} - // ToFg always convert fg func (c Color) ToFg() Color { val := uint8(c) @@ -329,16 +261,6 @@ func (c Color) ToBg() Color { return Color(Fg2Bg(val)) } -// RGB convert 16 color to 256-color code. -func (c Color) RGB() RGBColor { - val := uint8(c) - if val < 10 { // is option code - return emptyRGBColor - } - - return HEX(Basic2hex(val), c.IsBg()) -} - // Code convert to code string. eg "35" func (c Color) Code() string { return strconv.FormatInt(int64(c), 10) @@ -352,20 +274,20 @@ func (c Color) String() string { // IsBg check is background color func (c Color) IsBg() bool { val := uint8(c) - return val >= BgBase && val <= BgMax || val >= HiBgBase && val <= HiBgMax + return val >= bgBase && val <= bgMax || val >= hiBgBase && val <= hiBgMax } // IsFg check is foreground color func (c Color) IsFg() bool { val := uint8(c) - return val >= FgBase && val <= FgMax || val >= HiFgBase && val <= HiFgMax + return val >= fgBase && val <= fgMax || val >= hiFgBase && val <= hiFgMax } // IsOption check is option code: 0-9 -func (c Color) IsOption() bool { return uint8(c) < OptMax } +func (c Color) IsOption() bool { return uint8(c) < optMax } // IsValid color value -func (c Color) IsValid() bool { return uint8(c) < HiBgMax } +func (c Color) IsValid() bool { return uint8(c) < hiBgMax } /************************************************************* * basic color maps @@ -421,11 +343,6 @@ var ExBgColors = map[string]Color{ "lightWhite": BgLightWhite, } -// Options color options map -// -// Deprecated: please use AllOptions instead. -var Options = AllOptions - // AllOptions color options map var AllOptions = map[string]Color{ "reset": OpReset, @@ -438,49 +355,11 @@ var AllOptions = map[string]Color{ "concealed": OpConcealed, } -var ( - // TODO basic name alias - // basicNameAlias = map[string]string{} - // optionWithAlias = buildOpWithAlias() - // basic color name to code - // name2basicMap = initName2basicMap() - - // basic2nameMap basic color code to name - basic2nameMap = map[uint8]string{ - 30: "black", - 31: "red", - 32: "green", - 33: "yellow", - 34: "blue", - 35: "magenta", - 36: "cyan", - 37: "white", - // hi color code - 90: "lightBlack", - 91: "lightRed", - 92: "lightGreen", - 93: "lightYellow", - 94: "lightBlue", - 95: "lightMagenta", - 96: "lightCyan", - 97: "lightWhite", - // options - 0: "reset", - 1: "bold", - 2: "fuzzy", - 3: "italic", - 4: "underscore", - 5: "blink", - 7: "reverse", - 8: "concealed", - } -) - // Bg2Fg bg color value to fg value func Bg2Fg(val uint8) uint8 { - if val >= BgBase && val <= 47 { // is bg + if val >= bgBase && val <= 47 { // is bg val = val - 10 - } else if val >= HiBgBase && val <= 107 { // is hi bg + } else if val >= hiBgBase && val <= 107 { // is hi bg val = val - 10 } return val @@ -488,24 +367,10 @@ func Bg2Fg(val uint8) uint8 { // Fg2Bg fg color value to bg value func Fg2Bg(val uint8) uint8 { - if val >= FgBase && val <= 37 { // is fg + if val >= fgBase && val <= 37 { // is fg val = val + 10 - } else if val >= HiFgBase && val <= 97 { // is hi fg + } else if val >= hiFgBase && val <= 97 { // is hi fg val = val + 10 } return val } - -// Basic2nameMap data -func Basic2nameMap() map[uint8]string { return basic2nameMap } - -// func initName2basicMap() map[string]uint8 { -// n2b := make(map[string]uint8, len(basic2nameMap)) -// for u, s := range basic2nameMap { -// n2b[s] = u -// } -// return n2b -// } - -// func buildOpWithAlias() map[string]uint8 { -// } diff --git a/vendor/github.com/gookit/goutil/x/ccolor/color_tag.go b/vendor/github.com/gookit/goutil/x/ccolor/color_tag.go new file mode 100644 index 000000000..69e554342 --- /dev/null +++ b/vendor/github.com/gookit/goutil/x/ccolor/color_tag.go @@ -0,0 +1,255 @@ +package ccolor + +import ( + "fmt" + "regexp" + "strings" + + "github.com/gookit/goutil/x/termenv" +) + +// output colored text like uses custom tag. +const ( + // MatchExpr regex to match color tags + // + // Notice: golang 不支持反向引用. 即不支持使用 \1 引用第一个匹配 ([a-z=;]+) + // MatchExpr = `<([a-z=;]+)>(.*?)<\/\1>` + // 所以调整一下 统一使用 `` 来结束标签,例如 "some text" + // + // - NOTE: 不支持自定义属性。如有需要请使用 gookit/color 包 + // + // (?s:...) s - 让 "." 匹配换行 + MatchExpr = `<([0-9a-zA-Z_]+)>(?s:(.*?))<\/>` + + // StripExpr regex used for removing color tags + // StripExpr = `<[\/]?[a-zA-Z=;]+>` + // 随着上面的做一些调整 + StripExpr = `<[\/]?[0-9a-zA-Z_=,;]*>` +) + +var ( + matchRegex = regexp.MustCompile(MatchExpr) + stripRegex = regexp.MustCompile(StripExpr) +) + +/************************************************************* + * internal defined color tags + *************************************************************/ + +// There are internal defined fg color tags +// +// Usage: +// +// content text +// +// @notice 加 0 在前面是为了防止之前的影响到现在的设置 +var colorTags = map[string]string{ + // basic tags + "red": "0;31", + "red1": "1;31", // with bold + "redB": "1;31", + "red_b": "1;31", + "blue": "0;34", + "blue1": "1;34", // with bold + "blueB": "1;34", + "blue_b": "1;34", + "cyan": "0;36", + "cyan1": "1;36", // with bold + "cyanB": "1;36", + "cyan_b": "1;36", + "green": "0;32", + "green1": "1;32", // with bold + "greenB": "1;32", + "green_b": "1;32", + "black": "0;30", + "white": "1;37", + "default": "0;39", // no color + "normal": "0;39", // no color + "brown": "0;33", // #A52A2A + "yellow": "0;33", + "ylw0": "0;33", + "yellowB": "1;33", // with bold + "ylw1": "1;33", + "ylwB": "1;33", + "magenta": "0;35", + "mga": "0;35", // short name + "magentaB": "1;35", // with bold + "magenta1": "1;35", + "mgb": "1;35", + "mga1": "1;35", + "mgaB": "1;35", + + // light/hi tags + + "gray": "0;90", + "darkGray": "0;90", + "dark_gray": "0;90", + "lightYellow": "0;93", + "light_yellow": "0;93", + "hiYellow": "0;93", + "hi_yellow": "0;93", + "hiYellowB": "1;93", // with bold + "hi_yellow_b": "1;93", + "lightMagenta": "0;95", + "light_magenta": "0;95", + "hiMagenta": "0;95", + "hi_magenta": "0;95", + "lightMagenta1": "1;95", // with bold + "hiMagentaB": "1;95", // with bold + "hi_magenta_b": "1;95", + "lightRed": "0;91", + "light_red": "0;91", + "hiRed": "0;91", + "hi_red": "0;91", + "lightRedB": "1;91", // with bold + "light_red_b": "1;91", + "hi_red_b": "1;91", + "lightGreen": "0;92", + "light_green": "0;92", + "hiGreen": "0;92", + "hi_green": "0;92", + "lightGreenB": "1;92", + "light_green_b": "1;92", + "hi_green_b": "1;92", + "lightBlue": "0;94", + "light_blue": "0;94", + "hiBlue": "0;94", + "hi_blue": "0;94", + "lightBlueB": "1;94", + "light_blue_b": "1;94", + "hi_blue_b": "1;94", + "lightCyan": "0;96", + "light_cyan": "0;96", + "hiCyan": "0;96", + "hi_cyan": "0;96", + "lightCyanB": "1;96", + "light_cyan_b": "1;96", + "hi_cyan_b": "1;96", + "lightWhite": "0;97;40", + "light_white": "0;97;40", + + // option + "bold": "1", + "b": "1", + "italic": "3", + "i": "3", // italic + "underscore": "4", + "us": "4", // short name for 'underscore' + "blink": "5", + "fb": "6", // fast blink + "reverse": "7", + "st": "9", // strikethrough + + // alert tags, like bootstrap's alert + "suc": "1;32", // same "green" and "bold" + "success": "1;32", + "info": "0;32", // same "green", + "comment": "0;33", // same "brown" + "note": "36;1", + "notice": "36;4", + "warn": "0;1;33", + "warning": "0;30;43", + "primary": "0;34", + "danger": "1;31", // same "red" but add bold + "err": "97;41", + "error": "97;41", // fg light white; bg red +} + +/************************************************************* + * parse color tags + *************************************************************/ + +// Render parse color tags, return rendered string. +// +// Usage: +// +// text := Render("hello world!") +// fmt.Println(text) +func Render(a ...any) string { + if len(a) == 0 { + return "" + } + return ReplaceTag(fmt.Sprint(a...)) +} + +// ReplaceTag parse string, replace color tag and return rendered string +func ReplaceTag(str string) string { return ParseTagByEnv(str) } + +// ParseTagByEnv parse given string. will check package setting. +func ParseTagByEnv(str string) string { + // disable OR not support color + if termenv.NoColor() || !termenv.IsSupportColor() { + return ClearTag(str) + } + return ParseTag(str) +} + +// ParseTag parse given string, replace color tag and return rendered string +// +// Use built in tags: +// +// CONTENT +// // e.g: `message` +// +// TIP: code is from gookit/color package +// +// - Not support custom attributes +// - Not support c256 or rgb color +func ParseTag(str string) string { + // not contains color tag + if !strings.Contains(str, "") { + return str + } + + // find color tags by regex. str eg: "content" + matched := matchRegex.FindAllStringSubmatch(str, -1) + + // item: 0 full text 1 tag name 2 tag content + for _, item := range matched { + full, tag, body := item[0], item[1], item[2] + + // use defined color tag name: "content" -> tag: "info" + if code := colorTags[tag]; len(code) > 0 { + str = strings.Replace(str, full, RenderString(code, body), 1) + } + } + + return str +} + +// ClearTag clear-all tag for a string +func ClearTag(s string) string { + if !strings.Contains(s, "") { + return s + } + return stripRegex.ReplaceAllString(s, "") +} + +/************************************************************* + * helper methods + *************************************************************/ + +// GetTagCode get color code by tag name +func GetTagCode(name string) string { return colorTags[name] } + +// ApplyTag for messages +func ApplyTag(tag string, a ...any) string { + return RenderCode(GetTagCode(tag), a...) +} + +// WrapTag wrap a tag for a string "content" +func WrapTag(s string, tag string) string { + if s == "" || tag == "" { + return s + } + return fmt.Sprintf("<%s>%s", tag, s) +} + +// ColorTags get all internal color tags +func ColorTags() map[string]string { return colorTags } + +// IsDefinedTag is defined tag name +func IsDefinedTag(name string) bool { + _, ok := colorTags[name] + return ok +} diff --git a/vendor/github.com/gookit/color/quickstart.go b/vendor/github.com/gookit/goutil/x/ccolor/quickuse.go similarity index 74% rename from vendor/github.com/gookit/color/quickstart.go rename to vendor/github.com/gookit/goutil/x/ccolor/quickuse.go index b368b8a14..16d424ebb 100644 --- a/vendor/github.com/gookit/color/quickstart.go +++ b/vendor/github.com/gookit/goutil/x/ccolor/quickuse.go @@ -1,4 +1,4 @@ -package color +package ccolor /************************************************************* * quick use color print message @@ -31,53 +31,53 @@ func Cyanf(format string, a ...any) { Cyan.Printf(format, a...) } // Cyanln print message line with Cyan color func Cyanln(a ...any) { Cyan.Println(a...) } -// Grayp print message with Gray color +// Grayp print message with gray color func Grayp(a ...any) { Gray.Print(a...) } -// Grayf print message with Gray color +// Grayf print message with gray color func Grayf(format string, a ...any) { Gray.Printf(format, a...) } -// Grayln print message line with Gray color +// Grayln print message line with gray color func Grayln(a ...any) { Gray.Println(a...) } -// Greenp print message with Green color +// Greenp print message with green color func Greenp(a ...any) { Green.Print(a...) } -// Greenf print message with Green color +// Greenf print message with green color func Greenf(format string, a ...any) { Green.Printf(format, a...) } -// Greenln print message line with Green color +// Greenln print message line with green color func Greenln(a ...any) { Green.Println(a...) } -// Yellowp print message with Yellow color +// Yellowp print message with yellow color func Yellowp(a ...any) { Yellow.Print(a...) } -// Yellowf print message with Yellow color +// Yellowf print message with yellow color func Yellowf(format string, a ...any) { Yellow.Printf(format, a...) } -// Yellowln print message line with Yellow color +// Yellowln print message line with yellow color func Yellowln(a ...any) { Yellow.Println(a...) } -// Magentap print message with Magenta color +// Magentap print message with magenta color func Magentap(a ...any) { Magenta.Print(a...) } -// Magentaf print message with Magenta color +// Magentaf print message with magenta color func Magentaf(format string, a ...any) { Magenta.Printf(format, a...) } -// Magentaln print message line with Magenta color +// Magentaln print message line with magenta color func Magentaln(a ...any) { Magenta.Println(a...) } /************************************************************* * quick use style print message *************************************************************/ -// Infop print message with Info color +// Infop print message with info color func Infop(a ...any) { Info.Print(a...) } -// Infof print message with Info style +// Infof print message with info style func Infof(format string, a ...any) { Info.Printf(format, a...) } -// Infoln print message with Info style +// Infoln print message with info style func Infoln(a ...any) { Info.Println(a...) } // Successp print message with success color @@ -89,20 +89,20 @@ func Successf(format string, a ...any) { Success.Printf(format, a...) } // Successln print message with success style func Successln(a ...any) { Success.Println(a...) } -// Errorp print message with Error color +// Errorp print message with error color func Errorp(a ...any) { Error.Print(a...) } -// Errorf print message with Error style +// Errorf print message with error style func Errorf(format string, a ...any) { Error.Printf(format, a...) } -// Errorln print message with Error style +// Errorln print message with error style func Errorln(a ...any) { Error.Println(a...) } -// Warnp print message with Warn color +// Warnp print message with warn color func Warnp(a ...any) { Warn.Print(a...) } -// Warnf print message with Warn style +// Warnf print message with warn style func Warnf(format string, a ...any) { Warn.Printf(format, a...) } -// Warnln print message with Warn style +// Warnln print message with warn style func Warnln(a ...any) { Warn.Println(a...) } diff --git a/vendor/github.com/gookit/goutil/x/ccolor/style.go b/vendor/github.com/gookit/goutil/x/ccolor/style.go new file mode 100644 index 000000000..5a8089120 --- /dev/null +++ b/vendor/github.com/gookit/goutil/x/ccolor/style.go @@ -0,0 +1,97 @@ +package ccolor + +import ( + "fmt" + "io" + "strings" +) + +// String color style string. TODO +// eg: +// s := String("red,bold") +// s.Println("some text message") +type String string + +// Style for color render. +type Style struct { + Fg Color + Bg Color + Opts []Color +} + +// NewStyle fg, bg and options +func NewStyle(fg Color, bg Color, opts ...Color) *Style { + return &Style{ + Fg: fg, + Bg: bg, + Opts: opts, + } +} + +// Print like fmt.Print, but with color +func (s *Style) Print(v ...any) { + doPrint(s.String(), fmt.Sprint(v...)) +} + +// Println like fmt.Println, but with color +func (s *Style) Println(v ...any) { + doPrintln(s.String(), v) +} + +// Printf render and print text +func (s *Style) Printf(format string, v ...any) { + doPrint(s.String(), fmt.Sprintf(format, v...)) +} + +// Sprint like fmt.Sprint, but with color +func (s *Style) Sprint(v ...any) string { + return RenderString(s.String(), fmt.Sprint(v...)) +} + +// Sprintln like fmt.Sprintln, but with color +func (s *Style) Sprintln(v ...any) string { + return RenderWithSpaces(s.String(), v...) +} + +// Sprintf format and render message. +func (s *Style) Sprintf(format string, v ...any) string { + return RenderString(s.String(), fmt.Sprintf(format, v...)) +} + +// Fprint like fmt.Fprint, but with color +func (s *Style) Fprint(w io.Writer, v ...any) { + doPrintTo(w, s.String(), fmt.Sprint(v...)) +} + +// String convert style setting to color code string. +func (s *Style) String() string { + var codes []string + if s.Fg.IsFg() { + codes = append(codes, s.Fg.String()) + } + if s.Bg.IsBg() { + codes = append(codes, s.Bg.String()) + } + + if len(s.Opts) > 0 { + codes = append(codes, ColorsToCode(s.Opts...)) + } + + if len(codes) == 0 { + return "" + } + return strings.Join(codes, ";") +} + +var ( + // Info color style + Info = &Style{Fg: FgGreen} + // Warn color style + Warn = &Style{Fg: FgYellow} + // Error color style + Error = NewStyle(FgLightWhite, BgRed) + // Debug color style + Debug = &Style{Fg: FgCyan} + // Success color style + Success = &Style{Fg: FgGreen, Opts: []Color{OpBold}} +) diff --git a/vendor/github.com/gookit/goutil/x/ccolor/util.go b/vendor/github.com/gookit/goutil/x/ccolor/util.go new file mode 100644 index 000000000..46ed2b4bb --- /dev/null +++ b/vendor/github.com/gookit/goutil/x/ccolor/util.go @@ -0,0 +1,131 @@ +package ccolor + +import ( + "fmt" + "io" + "strings" + + "github.com/gookit/goutil/x/termenv" +) + +// ColorsToCode convert colors to code. return like "32;45;3" +func ColorsToCode(colors ...Color) string { + if len(colors) == 0 { + return "" + } + + var codes []string + for _, color := range colors { + codes = append(codes, color.String()) + } + + return strings.Join(codes, ";") +} + +/************************************************************* + * render color code + *************************************************************/ + +// RenderCode render message by color code. +// +// Usage: +// +// msg := RenderCode("3;32;45", "some", "message") +func RenderCode(code string, args ...any) string { + var message string + if ln := len(args); ln == 0 { + return "" + } + + message = fmt.Sprint(args...) + if len(code) == 0 { + return message + } + + // disabled OR not support color + if !termenv.IsSupportColor() { + return ClearCode(message) + } + + // return fmt.Sprintf(FullColorTpl, code, message) + return StartSet + code + "m" + message + ResetSet +} + +// RenderString render a string with color code. +// +// Usage: +// +// msg := RenderString("3;32;45", "a message") +func RenderString(code string, str string) string { + if len(code) == 0 || str == "" { + return str + } + + // disabled OR not support color + if !termenv.IsSupportColor() { + return ClearCode(str) + } + + // return fmt.Sprintf(FullColorTpl, code, str) + return StartSet + code + "m" + str + ResetSet +} + +// RenderWithSpaces Render code with spaces. +// If the number of args is > 1, a space will be added between the args +func RenderWithSpaces(code string, args ...any) string { + msg := formatLikePrintln(args) + if len(code) == 0 { + return msg + } + + // disabled OR not support color + if !termenv.IsSupportColor() { + return ClearCode(msg) + } + return StartSet + code + "m" + msg + ResetSet +} + +// ClearCode clear color codes. +// +// eg: +// +// "\033[36;1mText\x1b[0m" -> "Text" +func ClearCode(str string) string { + if !strings.Contains(str, CodeSuffix) { + return str + } + return codeRegex.ReplaceAllString(str, "") +} + +/************************************************************* + * helper methods for print + *************************************************************/ + +// new implementation, support render full color code on pwsh.exe, cmd.exe +func doPrint(code, str string) { + _, lastErr = fmt.Fprint(output, RenderString(code, str)) +} + +func doPrintTo(w io.Writer, code, str string) { + _, lastErr = fmt.Fprint(w, RenderString(code, str)) +} + +// new implementation, support render full color code on pwsh.exe, cmd.exe +func doPrintln(code string, args []any) { + _, lastErr = fmt.Fprintln(output, RenderString(code, formatLikePrintln(args))) +} + +// use Println, will add spaces for each arg +func formatLikePrintln(args []any) (message string) { + if ln := len(args); ln == 0 { + message = "" + } else if ln == 1 { + message = fmt.Sprint(args[0]) + } else { + message = fmt.Sprintln(args...) + // clear last "\n" + message = message[:len(message)-1] + } + return +} + diff --git a/vendor/github.com/gookit/goutil/encodes/encodes.go b/vendor/github.com/gookit/goutil/x/encodes/encodes.go similarity index 100% rename from vendor/github.com/gookit/goutil/encodes/encodes.go rename to vendor/github.com/gookit/goutil/x/encodes/encodes.go diff --git a/vendor/github.com/gookit/goutil/goinfo/README.md b/vendor/github.com/gookit/goutil/x/goinfo/README.md similarity index 78% rename from vendor/github.com/gookit/goutil/goinfo/README.md rename to vendor/github.com/gookit/goutil/x/goinfo/README.md index 2df63a6e0..22f6cd436 100644 --- a/vendor/github.com/gookit/goutil/goinfo/README.md +++ b/vendor/github.com/gookit/goutil/x/goinfo/README.md @@ -1,13 +1,13 @@ # GoInfo - `goutil/goinfo` provide some useful info for golang. + `goutil/x/goinfo` provide some useful info for golang. > Github: https://github.com/gookit/goutil ## Install ```bash -go get github.com/gookit/goutil/goinfo +go get github.com/gookit/goutil/x/goinfo ``` ## Go docs diff --git a/vendor/github.com/gookit/goutil/goinfo/gofunc.go b/vendor/github.com/gookit/goutil/x/goinfo/gofunc.go similarity index 86% rename from vendor/github.com/gookit/goutil/goinfo/gofunc.go rename to vendor/github.com/gookit/goutil/x/goinfo/gofunc.go index a0d5d2a38..a070b5193 100644 --- a/vendor/github.com/gookit/goutil/goinfo/gofunc.go +++ b/vendor/github.com/gookit/goutil/x/goinfo/gofunc.go @@ -11,9 +11,9 @@ import ( // FullFcName struct. type FullFcName struct { - // FullName eg: "github.com/gookit/goutil/goinfo.PanicIf" + // FullName eg: "github.com/gookit/goutil/x/goinfo.PanicIf" FullName string - pkgPath string // "github.com/gookit/goutil/goinfo" + pkgPath string // "github.com/gookit/goutil/x/goinfo" pkgName string // "goinfo" funcName string // "PanicIf" } @@ -32,7 +32,7 @@ func (ffn *FullFcName) Parse() { ffn.pkgPath += ffn.pkgName } -// PkgPath string get. eg: github.com/gookit/goutil/goinfo +// PkgPath string get. eg: github.com/gookit/goutil/x/goinfo func (ffn *FullFcName) PkgPath() string { ffn.Parse() return ffn.pkgPath @@ -59,7 +59,7 @@ func (ffn *FullFcName) String() string { // // eg: // -// // OUTPUT: github.com/gookit/goutil/goinfo.PkgName +// // OUTPUT: github.com/gookit/goutil/x/goinfo.PkgName // goinfo.FuncName(goinfo.PkgName) func FuncName(fn any) string { return runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name() @@ -68,7 +68,7 @@ func FuncName(fn any) string { // CutFuncName get pkg path and short func name // eg: // -// "github.com/gookit/goutil/goinfo.FuncName" => [github.com/gookit/goutil/goinfo, FuncName] +// "github.com/gookit/goutil/x/goinfo.FuncName" => [github.com/gookit/goutil/x/goinfo, FuncName] func CutFuncName(fullFcName string) (pkgPath, shortFnName string) { ffn := FullFcName{FullName: fullFcName} return ffn.PkgPath(), ffn.FuncName() diff --git a/vendor/github.com/gookit/goutil/goinfo/goinfo.go b/vendor/github.com/gookit/goutil/x/goinfo/goinfo.go similarity index 100% rename from vendor/github.com/gookit/goutil/goinfo/goinfo.go rename to vendor/github.com/gookit/goutil/x/goinfo/goinfo.go diff --git a/vendor/github.com/gookit/goutil/goinfo/stack.go b/vendor/github.com/gookit/goutil/x/goinfo/stack.go similarity index 60% rename from vendor/github.com/gookit/goutil/goinfo/stack.go rename to vendor/github.com/gookit/goutil/x/goinfo/stack.go index 07033caa8..3912b7aa9 100644 --- a/vendor/github.com/gookit/goutil/goinfo/stack.go +++ b/vendor/github.com/gookit/goutil/x/goinfo/stack.go @@ -6,7 +6,7 @@ import ( "strconv" "strings" - "github.com/gookit/goutil/basefn" + "github.com/gookit/goutil/x/basefn" ) // some commonly consts @@ -44,7 +44,7 @@ func GetCallStacks(all bool) []byte { // // returns: // -// github.com/gookit/goutil/goinfo_test.someFunc2(),stack_test.go:26 +// github.com/gookit/goutil/x/goinfo_test.someFunc2(),stack_test.go:26 func GetCallerInfo(skip int) string { skip++ // ignore current func cs := GetCallersInfo(skip, skip+1) @@ -92,7 +92,7 @@ func GetCallersInfo(skip, max int) []string { if strings.ContainsRune(file, '/') { name = fc.Name() file = filepath.Base(file) - // eg: github.com/gookit/goutil/goinfo_test.someFunc2(),stack_test.go:26 + // eg: github.com/gookit/goutil/x/goinfo_test.someFunc2(),stack_test.go:26 callers = append(callers, name+"(),"+file+":"+strconv.Itoa(line)) } @@ -103,3 +103,64 @@ func GetCallersInfo(skip, max int) []string { return callers } + +// CallerInfo struct +type CallerInfo struct { + PC uintptr + Fc *runtime.Func + File string + Line int +} + +// String convert +func (ci *CallerInfo) String() string { + return ci.File + ":" + strconv.Itoa(ci.Line) +} + +// CallerFilterFunc type +type CallerFilterFunc func(file string, fc *runtime.Func) bool + +// CallersInfos returns an array of the CallerInfo, can with filters +// +// Usage: +// +// cs := sysutil.CallersInfos(3, 2) +// for _, ci := range cs { +// fc := runtime.FuncForPC(pc) +// // maybe need check fc = nil +// fnName = fc.Name() +// } +func CallersInfos(skip, num int, filters ...CallerFilterFunc) []*CallerInfo { + filterLn := len(filters) + callers := make([]*CallerInfo, 0, num) + for i := skip; i < skip+num; i++ { + pc, file, line, ok := runtime.Caller(i) + if !ok { + // The breaks below failed to terminate the loop, and we ran off the + // end of the call stack. + break + } + + fc := runtime.FuncForPC(pc) + if fc == nil { + continue + } + + if filterLn > 0 && filters[0] != nil { + // filter - return false for skip + if !filters[0](file, fc) { + continue + } + } + + // collecting + callers = append(callers, &CallerInfo{ + PC: pc, + Fc: fc, + File: file, + Line: line, + }) + } + + return callers +} diff --git a/vendor/github.com/gookit/goutil/stdio/README.md b/vendor/github.com/gookit/goutil/x/stdio/README.md similarity index 70% rename from vendor/github.com/gookit/goutil/stdio/README.md rename to vendor/github.com/gookit/goutil/x/stdio/README.md index 5a8370c68..f8d957e16 100644 --- a/vendor/github.com/gookit/goutil/stdio/README.md +++ b/vendor/github.com/gookit/goutil/x/stdio/README.md @@ -5,12 +5,12 @@ ## Install ```bash -go get github.com/gookit/goutil/stdio +go get github.com/gookit/goutil/x/stdio ``` ## Go docs -- [Go docs](https://pkg.go.dev/github.com/gookit/goutil/stdio) +- [Go docs](https://pkg.go.dev/github.com/gookit/goutil/x/stdio) ## Usage diff --git a/vendor/github.com/gookit/goutil/stdio/iface.go b/vendor/github.com/gookit/goutil/x/stdio/iface.go similarity index 100% rename from vendor/github.com/gookit/goutil/stdio/iface.go rename to vendor/github.com/gookit/goutil/x/stdio/iface.go diff --git a/vendor/github.com/gookit/goutil/x/stdio/ioutil.go b/vendor/github.com/gookit/goutil/x/stdio/ioutil.go new file mode 100644 index 000000000..8f25ea449 --- /dev/null +++ b/vendor/github.com/gookit/goutil/x/stdio/ioutil.go @@ -0,0 +1,31 @@ +package stdio + +import ( + "fmt" + "io" + "strings" +) + +// Fprint to writer, will ignore error +func Fprint(w io.Writer, a ...any) { + _, _ = fmt.Fprint(w, a...) +} + +// Fprintf to writer, will ignore error +func Fprintf(w io.Writer, tpl string, vs ...any) { + _, _ = fmt.Fprintf(w, tpl, vs...) +} + +// Fprintln to writer, will ignore error +func Fprintln(w io.Writer, a ...any) { + _, _ = fmt.Fprintln(w, a...) +} + +// WriteStringTo a writer, will ignore error +func WriteStringTo(w io.Writer, ss ...string) { + if len(ss) == 1 { + _, _ = io.WriteString(w, ss[0]) + } else if len(ss) > 1 { + _, _ = io.WriteString(w, strings.Join(ss, "")) + } +} diff --git a/vendor/github.com/gookit/goutil/stdio/stdio.go b/vendor/github.com/gookit/goutil/x/stdio/stdio.go similarity index 95% rename from vendor/github.com/gookit/goutil/stdio/stdio.go rename to vendor/github.com/gookit/goutil/x/stdio/stdio.go index a98b3a002..c15253e68 100644 --- a/vendor/github.com/gookit/goutil/stdio/stdio.go +++ b/vendor/github.com/gookit/goutil/x/stdio/stdio.go @@ -77,12 +77,12 @@ func WritelnBytes(bs []byte) { _, _ = os.Stdout.Write([]byte("\n")) } -// WriteString to stdout, will ignore error +// WriteString to stdout. will ignore error func WriteString(s string) { _, _ = os.Stdout.WriteString(s) } -// Writeln string to stdout, will ignore error +// Writeln string to stdout. will ignore error func Writeln(s string) { _, _ = os.Stdout.WriteString(s) _, _ = os.Stdout.Write([]byte("\n")) diff --git a/vendor/github.com/gookit/goutil/stdio/writer.go b/vendor/github.com/gookit/goutil/x/stdio/writer.go similarity index 100% rename from vendor/github.com/gookit/goutil/stdio/writer.go rename to vendor/github.com/gookit/goutil/x/stdio/writer.go diff --git a/vendor/github.com/gookit/goutil/x/termenv/detect_color.go b/vendor/github.com/gookit/goutil/x/termenv/detect_color.go new file mode 100644 index 000000000..ddd6825b4 --- /dev/null +++ b/vendor/github.com/gookit/goutil/x/termenv/detect_color.go @@ -0,0 +1,197 @@ +package termenv + +import ( + "errors" + "os" + "runtime" + "strconv" + "strings" +) + +// ColorLevel is the color level supported by a terminal. +type ColorLevel uint8 + +const ( + TermColorNone ColorLevel = iota // not support color + TermColor16 // 16(4bit) ANSI color supported + TermColor256 // 256(8bit) color supported + TermColorTrue // support TRUE(RGB) color +) + +// String returns the string name of the color level. +func (l ColorLevel) String() string { + switch l { + case TermColor16: + return "ansiColor" + case TermColor256: + return "256color" + case TermColorTrue: + return "trueColor" + default: + return "none" + } +} + +// NoColor returns true if the NO_COLOR environment variable is set. +func NoColor() bool { return noColor } + +// TermColorLevel returns the color support level for the current terminal. +func TermColorLevel() ColorLevel { return colorLevel } + +// IsSupportColor returns true if the terminal supports color. +func IsSupportColor() bool { return colorLevel > TermColorNone } + +// IsSupport256Color returns true if the terminal supports 256 colors. +func IsSupport256Color() bool { return colorLevel >= TermColor256 } + +// IsSupportTrueColor returns true if the terminal supports true color. +func IsSupportTrueColor() bool { return colorLevel == TermColorTrue } + +// +// ---------------- Force set color support ---------------- +// + +var backLevel ColorLevel + +// SetColorLevel value force. +func SetColorLevel(level ColorLevel) { + // backup old value + backLevel = colorLevel + + // force set color level + colorLevel = level + supportColor = level > TermColorNone + noColor = supportColor == false +} + +// DisableColor in the current terminal +func DisableColor() { + // backup old value + backLevel = colorLevel + + // force disable color + noColor = true + supportColor = false + colorLevel = TermColorNone +} + +// ForceEnableColor flags value. TIP: use for unit testing. +// +// Usage: +// +// ccolor.ForceEnableColor() +// defer ccolor.RevertColorSupport() +func ForceEnableColor() { + // backup old value + backLevel = colorLevel + + // force enables color + noColor = false + supportColor = true + colorLevel = TermColor256 + // return colorLevel +} + +// RevertColorSupport flags to init value. +func RevertColorSupport() { + // revert color flags var + colorLevel = backLevel + supportColor = backLevel > TermColorNone + noColor = os.Getenv("NO_COLOR") == "" +} + +/************************************************************* + * helper methods for detect color supports + *************************************************************/ + +// DetectColorLevel for current env +// +// NOTICE: The method will detect terminal info each time. +// +// if only want to get current color level, please direct call IsSupportColor() or TermColorLevel() +func DetectColorLevel() ColorLevel { + level, _ := detectTermColorLevel() + return level +} + +// on TERM=screen: not support true-color +const noTrueColorTerm = "screen" + +// detect terminal color support level +// +// refer https://github.com/Delta456/box-cli-maker +func detectTermColorLevel() (level ColorLevel, needVTP bool) { + isWin := runtime.GOOS == "windows" + termVal := os.Getenv("TERM") + + if termVal != noTrueColorTerm { + // On JetBrains Terminal + // - TERM value not set, but support true-color + // env: + // TERMINAL_EMULATOR=JetBrains-JediTerm + val := os.Getenv("TERMINAL_EMULATOR") + if val == "JetBrains-JediTerm" { + debugf("True Color support on JetBrains-JediTerm, is win: %v", isWin) + return TermColorTrue, false + } + } + + level = detectColorLevelFromEnv(termVal, isWin) + + // fallback: simple detect by TERM value string. + if level == TermColorNone { + debugf("level=none - fallback check special term color support") + level, needVTP = detectSpecialTermColor(termVal) + debugf("color level by detectSpecialTermColor: %s", level.String()) + } else { + debugf("color level by detectColorLevelFromEnv: %s", level.String()) + } + return +} + +// detectColorFromEnv returns the color level COLORTERM, FORCE_COLOR, +// TERM_PROGRAM, or determined from the TERM environment variable. +// +// refer the github.com/xo/terminfo.ColorLevelFromEnv() +// https://en.wikipedia.org/wiki/Terminfo +func detectColorLevelFromEnv(termVal string, isWin bool) ColorLevel { + if termVal == noTrueColorTerm { // on TERM=screen: not support true-color + return TermColor256 + } + + // check for overriding environment variables + colorTerm, termProg, forceColor := os.Getenv("COLORTERM"), os.Getenv("TERM_PROGRAM"), os.Getenv("FORCE_COLOR") + switch { + case strings.Contains(colorTerm, "truecolor") || strings.Contains(colorTerm, "24bit"): + return TermColorTrue + case colorTerm != "" || forceColor != "": + return TermColor16 + case termProg == "Apple_Terminal": + return TermColor256 + case termProg == "Terminus" || termProg == "Hyper": + return TermColorTrue + case termProg == "iTerm.app": + // check iTerm version + termVer := os.Getenv("TERM_PROGRAM_VERSION") + if termVer != "" { + i, err := strconv.Atoi(strings.Split(termVer, ".")[0]) + if err != nil { + setLastErr(errors.New("invalid TERM_PROGRAM_VERSION=" + termVer)) + return TermColor256 // return TermColorNone + } + if i == 3 { + return TermColorTrue + } + } + return TermColor256 + } + + // otherwise determine from TERM's max_colors capability + // if !isWin && termVal != "" { + // debugf("TERM=%s - TODO check color level by load terminfo file", termVal) + // return TermColor16 + // } + + // no TERM env value. default return none level + return TermColorNone +} diff --git a/vendor/github.com/gookit/goutil/x/termenv/detect_nonwin.go b/vendor/github.com/gookit/goutil/x/termenv/detect_nonwin.go new file mode 100644 index 000000000..39f8e3678 --- /dev/null +++ b/vendor/github.com/gookit/goutil/x/termenv/detect_nonwin.go @@ -0,0 +1,46 @@ +//go:build !windows + +package termenv + +import ( + "strings" + "syscall" + + "github.com/gookit/goutil/internal/checkfn" +) + +// detect special term color support on macOS, linux, unix +func detectSpecialTermColor(termVal string) (ColorLevel, bool) { + if termVal == "" { + // detect WSL as it has True Color support + // on Windows WSL: + // - runtime.GOOS == "Linux" + // - support true-color + if checkfn.IsWSL() { + debugf("True Color support on WSL environment") + return TermColorTrue, false + } + return TermColorNone, false + } + + debugf("terminfo check - fallback detect color by check TERM value") + + // on TERM=screen: + // - support 256, not support true-color. test on macOS + if termVal == noTrueColorTerm { + return TermColor256, false + } + + if strings.Contains(termVal, "256color") { + return TermColor256, false + } + + if strings.Contains(termVal, "xterm") { + return TermColor256, false + } + return TermColor16, false +} + +func syscallStdinFd() int { + return syscall.Stdin +} \ No newline at end of file diff --git a/vendor/github.com/gookit/goutil/x/termenv/detect_windows.go b/vendor/github.com/gookit/goutil/x/termenv/detect_windows.go new file mode 100644 index 000000000..720662594 --- /dev/null +++ b/vendor/github.com/gookit/goutil/x/termenv/detect_windows.go @@ -0,0 +1,171 @@ +//go:build windows + +package termenv + +import ( + "os" + "syscall" + + "golang.org/x/sys/windows" +) + +// Get the Windows Version and Build Number +var majorVersion, _, buildNumber = windows.RtlGetNtVersionNumbers() + +// refer +// +// https://github.com/Delta456/box-cli-maker/blob/7b5a1ad8a016ce181e7d8b05e24b54ff60b4b38a/detect_windows.go#L30-L57 +// https://github.com/gookit/color/issues/25#issuecomment-738727917 +// +// detects the color level supported on Windows: CMD, PowerShell +func detectSpecialTermColor(_ string) (tl ColorLevel, needVTP bool) { + if os.Getenv("ConEmuANSI") == "ON" { + debugf("True Color support by ConEmuANSI=ON") + // ConEmuANSI is "ON" for generic ANSI support + // but True Color option is enabled by default + // I am just assuming that people wouldn't have disabled it + // Even if it is not enabled then ConEmu will auto round off accordingly + return TermColorTrue, false + } + + // Before Windows 10 Build Number 10586, console never supported ANSI Colors + if buildNumber < 10586 || majorVersion < 10 { + // Detect if using ANSICON on older systems + if os.Getenv("ANSICON") != "" { + conVersion := os.Getenv("ANSICON_VER") + // 8-bit Colors were only supported after v1.81 release + if conVersion >= "181" { + return TermColor256, false + } + return TermColor16, false + } + + return TermColorNone, false + } + + // True Color is not available before build 14931 so fallback to 8-bit color. + if buildNumber < 14931 { + return TermColor256, true + } + + // Windows 10 build 14931 is the first release that supports 16m/TrueColor + debugf("support True Color on windows version is >= build 14931") + return TermColorTrue, true +} + +// TryEnableVTP try force enables colors on Windows terminal +func TryEnableVTP(enable bool) bool { + if !enable { + return false + } + + // enable colors on Windows terminal + if tryEnableOnCONOUT() { + debugf("True-Color by enable VirtualTerminalProcessing on windows") + return true + } + + // initKernel32Proc() + suc := tryEnableOnStdout() + debugf("True-Color by enable VirtualTerminalProcessing on windows") + return suc +} + +func tryEnableOnCONOUT() bool { + outHandle, err := syscall.Open("CONOUT$", syscall.O_RDWR, 0) + if err != nil { + setLastErr(err) + return false + } + + err = EnableVTProcessing(outHandle, true) + if err != nil { + setLastErr(err) + return false + } + + return true +} + +func tryEnableOnStdout() bool { + // try direct open syscall.Stdout + err := EnableVTProcessing(syscall.Stdout, true) + if err != nil { + setLastErr(err) + return false + } + + return true +} + +// related docs +// https://docs.microsoft.com/zh-cn/windows/console/console-virtual-terminal-sequences +// https://docs.microsoft.com/zh-cn/windows/console/console-virtual-terminal-sequences#samples +var ( + // isMSys bool + kernel32 *syscall.LazyDLL + + procGetConsoleMode *syscall.LazyProc + procSetConsoleMode *syscall.LazyProc +) + +func initKernel32Proc() { + if kernel32 != nil { + return + } + + // load related Windows dll + // https://docs.microsoft.com/en-us/windows/console/setconsolemode + kernel32 = syscall.NewLazyDLL("kernel32.dll") + + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") + procSetConsoleMode = kernel32.NewProc("SetConsoleMode") +} + +/************************************************************* + * render full color code on Windows(8,16,24bit color) + *************************************************************/ + +// EnableVTProcessing Enable virtual terminal processing on Windows +// +// ref from github.com/konsorten/go-windows-terminal-sequences +// doc https://docs.microsoft.com/zh-cn/windows/console/console-virtual-terminal-sequences#samples +// +// Usage: +// +// err := EnableVTProcessing(syscall.Stdout, true) +// // support print color text +// err = EnableVTProcessing(syscall.Stdout, false) +func EnableVTProcessing(stream syscall.Handle, enable bool) error { + var mode uint32 + // Check if it is currently in the terminal + // err := syscall.GetConsoleMode(syscall.Stdout, &mode) + err := syscall.GetConsoleMode(stream, &mode) + if err != nil { + debugf("enable Windows VirtualTerminalProcessing error: %v", err) + return err + } + + // docs https://docs.microsoft.com/zh-cn/windows/console/getconsolemode#parameters + if enable { + mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING + } else { + mode &^= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING + } + + err = windows.SetConsoleMode(windows.Handle(stream), mode) + if err != nil { + return err + } + + // ret, _, err := procSetConsoleMode.Call(uintptr(stream), uintptr(mode)) + // if ret == 0 { + // return err + // } + return nil +} + +// on Windows, must convert 'syscall.Stdin' to int +func syscallStdinFd() int { + return int(syscall.Stdin) +} diff --git a/vendor/github.com/gookit/goutil/x/termenv/init_windows.go b/vendor/github.com/gookit/goutil/x/termenv/init_windows.go new file mode 100644 index 000000000..25827b892 --- /dev/null +++ b/vendor/github.com/gookit/goutil/x/termenv/init_windows.go @@ -0,0 +1,11 @@ +package termenv + +func init() { + // terminal supports color OR noColor=true: Don't need to enable virtual process + if colorLevel != TermColorNone || noColor { + return + } + + // try force enable colors on Windows terminal + TryEnableVTP(needVTP) +} diff --git a/vendor/github.com/gookit/goutil/x/termenv/termenv.go b/vendor/github.com/gookit/goutil/x/termenv/termenv.go new file mode 100644 index 000000000..dbc82ca5c --- /dev/null +++ b/vendor/github.com/gookit/goutil/x/termenv/termenv.go @@ -0,0 +1,95 @@ +// Package termenv provides detect color support of the current terminal. +// And with some utils for terminal env. +package termenv + +import ( + "fmt" + "os" + + "golang.org/x/term" +) + +var ( + lastErr error + // debug mode + debugMode bool + // support color of current terminal + supportColor bool + + // value of os color render and display + // + // NOTICE: + // if ENV: NO_COLOR is not empty, will disable color render. + noColor = os.Getenv("NO_COLOR") != "" + + // the color support level for current terminal + // needVTP - need enable VTP, only for Windows OS + colorLevel, needVTP = detectTermColorLevel() +) + +// SetDebugMode sets debug mode. +func SetDebugMode(enable bool) { debugMode = enable } + +// LastErr returns the last error. +func LastErr() error { + defer func() { + lastErr = nil // reset on get + }() + return lastErr +} + +func debugf(tpl string, v ...any) { + if debugMode { + fmt.Printf("TERMENV: "+tpl+"\n", v...) + } +} + +func setLastErr(err error) { + if err != nil { + debugf("TERMENV: last error: %v", err) + lastErr = err + } +} + +// exec: `stty -a 2>&1` +// const ( +// mac: speed 9600 baud; 97 rows; 362 columns; +// macSttyMsgPattern = `(\d+)\s+rows;\s*(\d+)\s+columns;` +// linux: speed 38400 baud; rows 97; columns 362; line = 0; +// linuxSttyMsgPattern = `rows\s+(\d+);\s*columns\s+(\d+);` +// ) +var terminalWidth, terminalHeight int + +// GetTermSize for current console terminal. +func GetTermSize(refresh ...bool) (w int, h int) { + if terminalWidth > 0 && len(refresh) > 0 && !refresh[0] { + return terminalWidth, terminalHeight + } + + var err error + w, h, err = term.GetSize(syscallStdinFd()) + if err != nil { + return + } + + // cache result + terminalWidth, terminalHeight = w, h + return +} + +// ReadPassword from console terminal +func ReadPassword(question ...string) string { + if len(question) > 0 { + print(question[0]) + } else { + print("Enter Password: ") + } + + bs, err := term.ReadPassword(syscallStdinFd()) + if err != nil { + return "" + } + + println() // new line + return string(bs) +} diff --git a/vendor/github.com/gookit/goutil/x/termenv/util.go b/vendor/github.com/gookit/goutil/x/termenv/util.go new file mode 100644 index 000000000..97b8fd8de --- /dev/null +++ b/vendor/github.com/gookit/goutil/x/termenv/util.go @@ -0,0 +1,79 @@ +package termenv + +import ( + "os" + "os/exec" + "runtime" + "strings" + + "github.com/gookit/goutil/internal/checkfn" + "github.com/gookit/goutil/internal/comfunc" + "golang.org/x/term" +) + +var ( + cmdList = []string{"cmd", "cmd.exe"} + pwshList = []string{"powershell", "powershell.exe", "pwsh", "pwsh.exe"} +) + +// IsTerminal 检查是否为终端设备中 +func IsTerminal() bool { + fd := int(os.Stdout.Fd()) + return term.IsTerminal(fd) +} + +// CurrentShell get current used shell env file. +// +// eg "/bin/zsh" "/bin/bash". +// if onlyName=true, will return "zsh", "bash" +func CurrentShell(onlyName bool, fallbackShell ...string) string { + return comfunc.CurrentShell(onlyName, fallbackShell...) +} + +// HasShellEnv has shell env check. +// +// Usage: +// +// HasShellEnv("sh") +// HasShellEnv("bash") +func HasShellEnv(shell string) bool { + // can also use: "echo $0" + out, err := shellExec("echo OK", shell) + if err != nil { + return false + } + return strings.TrimSpace(out) == "OK" +} + +// IsShellSpecialVar reports whether the character identifies a special +// shell variable such as $*. +func IsShellSpecialVar(c uint8) bool { + switch c { + case '*', '#', '$', '@', '!', '?', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + return true + } + return false +} + +func shellExec(expr, shell string) (string, error) { + // "-c" for bash,sh,zsh shell + mark := "-c" + if shell == "" { + shell = CurrentShell(true, "sh") + } + + // special for Windows shell + if runtime.GOOS == "windows" { + // use cmd.exe, mark is "/c" + if checkfn.StringsContains(cmdList, shell) { + mark = "/c" + } else if checkfn.StringsContains(pwshList, shell) { + // "-Command" for powershell + mark = "-Command" + } + } + + cmd := exec.Command(shell, mark, expr) + bs, err := cmd.CombinedOutput() + return string(bs), err +} diff --git a/vendor/github.com/xo/terminfo/.gitignore b/vendor/github.com/xo/terminfo/.gitignore deleted file mode 100644 index 368e0c06c..000000000 --- a/vendor/github.com/xo/terminfo/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -/.cache/ - -/cmd/infocmp/infocmp -/cmd/infocmp/.out/ - -/infocmp -/.out/ - -*.txt diff --git a/vendor/github.com/xo/terminfo/README.md b/vendor/github.com/xo/terminfo/README.md deleted file mode 100644 index e5002d239..000000000 --- a/vendor/github.com/xo/terminfo/README.md +++ /dev/null @@ -1,139 +0,0 @@ -# About terminfo [![GoDoc][1]][2] - -Package `terminfo` provides a pure-Go implementation of reading information -from the terminfo database. - -`terminfo` is meant as a replacement for `ncurses` in simple Go programs. - -## Installing - -Install in the usual Go way: - -```sh -$ go get -u github.com/xo/terminfo -``` - -## Using - -Please see the [GoDoc API listing][2] for more information on using `terminfo`. - -```go -// _examples/simple/main.go -package main - -import ( - "bytes" - "fmt" - "log" - "os" - "os/signal" - "strings" - "sync" - "syscall" - - "github.com/xo/terminfo" -) - -func main() { - //r := rand.New(nil) - - // load terminfo - ti, err := terminfo.LoadFromEnv() - if err != nil { - log.Fatal(err) - } - - // cleanup - defer func() { - err := recover() - termreset(ti) - if err != nil { - log.Fatal(err) - } - }() - - terminit(ti) - termtitle(ti, "simple example!") - termputs(ti, 3, 3, "Ctrl-C to exit") - maxColors := termcolors(ti) - if maxColors > 256 { - maxColors = 256 - } - for i := 0; i < maxColors; i++ { - termputs(ti, 5+i/16, 5+i%16, ti.Colorf(i, 0, "█")) - } - - // wait for signal - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) - <-sigs -} - -// terminit initializes the special CA mode on the terminal, and makes the -// cursor invisible. -func terminit(ti *terminfo.Terminfo) { - buf := new(bytes.Buffer) - // set the cursor invisible - ti.Fprintf(buf, terminfo.CursorInvisible) - // enter special mode - ti.Fprintf(buf, terminfo.EnterCaMode) - // clear the screen - ti.Fprintf(buf, terminfo.ClearScreen) - os.Stdout.Write(buf.Bytes()) -} - -// termreset is the inverse of terminit. -func termreset(ti *terminfo.Terminfo) { - buf := new(bytes.Buffer) - ti.Fprintf(buf, terminfo.ExitCaMode) - ti.Fprintf(buf, terminfo.CursorNormal) - os.Stdout.Write(buf.Bytes()) -} - -// termputs puts a string at row, col, interpolating v. -func termputs(ti *terminfo.Terminfo, row, col int, s string, v ...interface{}) { - buf := new(bytes.Buffer) - ti.Fprintf(buf, terminfo.CursorAddress, row, col) - fmt.Fprintf(buf, s, v...) - os.Stdout.Write(buf.Bytes()) -} - -// sl is the status line terminfo. -var sl *terminfo.Terminfo - -// termtitle sets the window title. -func termtitle(ti *terminfo.Terminfo, s string) { - var once sync.Once - once.Do(func() { - if ti.Has(terminfo.HasStatusLine) { - return - } - // load the sl xterm if terminal is an xterm or has COLORTERM - if strings.Contains(strings.ToLower(os.Getenv("TERM")), "xterm") || os.Getenv("COLORTERM") == "truecolor" { - sl, _ = terminfo.Load("xterm+sl") - } - }) - if sl != nil { - ti = sl - } - if !ti.Has(terminfo.HasStatusLine) { - return - } - buf := new(bytes.Buffer) - ti.Fprintf(buf, terminfo.ToStatusLine) - fmt.Fprint(buf, s) - ti.Fprintf(buf, terminfo.FromStatusLine) - os.Stdout.Write(buf.Bytes()) -} - -// termcolors returns the maximum colors available for the terminal. -func termcolors(ti *terminfo.Terminfo) int { - if colors := ti.Num(terminfo.MaxColors); colors > 0 { - return colors - } - return int(terminfo.ColorLevelBasic) -} -``` - -[1]: https://godoc.org/github.com/xo/terminfo?status.svg -[2]: https://godoc.org/github.com/xo/terminfo diff --git a/vendor/github.com/xo/terminfo/caps.go b/vendor/github.com/xo/terminfo/caps.go deleted file mode 100644 index e5e1d41f1..000000000 --- a/vendor/github.com/xo/terminfo/caps.go +++ /dev/null @@ -1,31 +0,0 @@ -package terminfo - -// BoolCapName returns the bool capability name. -func BoolCapName(i int) string { - return boolCapNames[2*i] -} - -// BoolCapNameShort returns the short bool capability name. -func BoolCapNameShort(i int) string { - return boolCapNames[2*i+1] -} - -// NumCapName returns the num capability name. -func NumCapName(i int) string { - return numCapNames[2*i] -} - -// NumCapNameShort returns the short num capability name. -func NumCapNameShort(i int) string { - return numCapNames[2*i+1] -} - -// StringCapName returns the string capability name. -func StringCapName(i int) string { - return stringCapNames[2*i] -} - -// StringCapNameShort returns the short string capability name. -func StringCapNameShort(i int) string { - return stringCapNames[2*i+1] -} diff --git a/vendor/github.com/xo/terminfo/capvals.go b/vendor/github.com/xo/terminfo/capvals.go deleted file mode 100644 index 0c2274e3a..000000000 --- a/vendor/github.com/xo/terminfo/capvals.go +++ /dev/null @@ -1,1525 +0,0 @@ -package terminfo - -// Code generated by gen.go. DO NOT EDIT. -// Bool capabilities. -const ( - // The AutoLeftMargin [auto_left_margin, bw] bool capability indicates cub1 wraps from column 0 to last column. - AutoLeftMargin = iota - // The AutoRightMargin [auto_right_margin, am] bool capability indicates terminal has automatic margins. - AutoRightMargin - // The NoEscCtlc [no_esc_ctlc, xsb] bool capability indicates beehive (f1=escape, f2=ctrl C). - NoEscCtlc - // The CeolStandoutGlitch [ceol_standout_glitch, xhp] bool capability indicates standout not erased by overwriting (hp). - CeolStandoutGlitch - // The EatNewlineGlitch [eat_newline_glitch, xenl] bool capability indicates newline ignored after 80 cols (concept). - EatNewlineGlitch - // The EraseOverstrike [erase_overstrike, eo] bool capability indicates can erase overstrikes with a blank. - EraseOverstrike - // The GenericType [generic_type, gn] bool capability indicates generic line type. - GenericType - // The HardCopy [hard_copy, hc] bool capability indicates hardcopy terminal. - HardCopy - // The HasMetaKey [has_meta_key, km] bool capability indicates Has a meta key (i.e., sets 8th-bit). - HasMetaKey - // The HasStatusLine [has_status_line, hs] bool capability indicates has extra status line. - HasStatusLine - // The InsertNullGlitch [insert_null_glitch, in] bool capability indicates insert mode distinguishes nulls. - InsertNullGlitch - // The MemoryAbove [memory_above, da] bool capability indicates display may be retained above the screen. - MemoryAbove - // The MemoryBelow [memory_below, db] bool capability indicates display may be retained below the screen. - MemoryBelow - // The MoveInsertMode [move_insert_mode, mir] bool capability indicates safe to move while in insert mode. - MoveInsertMode - // The MoveStandoutMode [move_standout_mode, msgr] bool capability indicates safe to move while in standout mode. - MoveStandoutMode - // The OverStrike [over_strike, os] bool capability indicates terminal can overstrike. - OverStrike - // The StatusLineEscOk [status_line_esc_ok, eslok] bool capability indicates escape can be used on the status line. - StatusLineEscOk - // The DestTabsMagicSmso [dest_tabs_magic_smso, xt] bool capability indicates tabs destructive, magic so char (t1061). - DestTabsMagicSmso - // The TildeGlitch [tilde_glitch, hz] bool capability indicates cannot print ~'s (Hazeltine). - TildeGlitch - // The TransparentUnderline [transparent_underline, ul] bool capability indicates underline character overstrikes. - TransparentUnderline - // The XonXoff [xon_xoff, xon] bool capability indicates terminal uses xon/xoff handshaking. - XonXoff - // The NeedsXonXoff [needs_xon_xoff, nxon] bool capability indicates padding will not work, xon/xoff required. - NeedsXonXoff - // The PrtrSilent [prtr_silent, mc5i] bool capability indicates printer will not echo on screen. - PrtrSilent - // The HardCursor [hard_cursor, chts] bool capability indicates cursor is hard to see. - HardCursor - // The NonRevRmcup [non_rev_rmcup, nrrmc] bool capability indicates smcup does not reverse rmcup. - NonRevRmcup - // The NoPadChar [no_pad_char, npc] bool capability indicates pad character does not exist. - NoPadChar - // The NonDestScrollRegion [non_dest_scroll_region, ndscr] bool capability indicates scrolling region is non-destructive. - NonDestScrollRegion - // The CanChange [can_change, ccc] bool capability indicates terminal can re-define existing colors. - CanChange - // The BackColorErase [back_color_erase, bce] bool capability indicates screen erased with background color. - BackColorErase - // The HueLightnessSaturation [hue_lightness_saturation, hls] bool capability indicates terminal uses only HLS color notation (Tektronix). - HueLightnessSaturation - // The ColAddrGlitch [col_addr_glitch, xhpa] bool capability indicates only positive motion for hpa/mhpa caps. - ColAddrGlitch - // The CrCancelsMicroMode [cr_cancels_micro_mode, crxm] bool capability indicates using cr turns off micro mode. - CrCancelsMicroMode - // The HasPrintWheel [has_print_wheel, daisy] bool capability indicates printer needs operator to change character set. - HasPrintWheel - // The RowAddrGlitch [row_addr_glitch, xvpa] bool capability indicates only positive motion for vpa/mvpa caps. - RowAddrGlitch - // The SemiAutoRightMargin [semi_auto_right_margin, sam] bool capability indicates printing in last column causes cr. - SemiAutoRightMargin - // The CpiChangesRes [cpi_changes_res, cpix] bool capability indicates changing character pitch changes resolution. - CpiChangesRes - // The LpiChangesRes [lpi_changes_res, lpix] bool capability indicates changing line pitch changes resolution. - LpiChangesRes - // The BackspacesWithBs [backspaces_with_bs, OTbs] bool capability indicates uses ^H to move left. - BackspacesWithBs - // The CrtNoScrolling [crt_no_scrolling, OTns] bool capability indicates crt cannot scroll. - CrtNoScrolling - // The NoCorrectlyWorkingCr [no_correctly_working_cr, OTnc] bool capability indicates no way to go to start of line. - NoCorrectlyWorkingCr - // The GnuHasMetaKey [gnu_has_meta_key, OTMT] bool capability indicates has meta key. - GnuHasMetaKey - // The LinefeedIsNewline [linefeed_is_newline, OTNL] bool capability indicates move down with \n. - LinefeedIsNewline - // The HasHardwareTabs [has_hardware_tabs, OTpt] bool capability indicates has 8-char tabs invoked with ^I. - HasHardwareTabs - // The ReturnDoesClrEol [return_does_clr_eol, OTxr] bool capability indicates return clears the line. - ReturnDoesClrEol -) - -// Num capabilities. -const ( - // The Columns [columns, cols] num capability is number of columns in a line. - Columns = iota - // The InitTabs [init_tabs, it] num capability is tabs initially every # spaces. - InitTabs - // The Lines [lines, lines] num capability is number of lines on screen or page. - Lines - // The LinesOfMemory [lines_of_memory, lm] num capability is lines of memory if > line. 0 means varies. - LinesOfMemory - // The MagicCookieGlitch [magic_cookie_glitch, xmc] num capability is number of blank characters left by smso or rmso. - MagicCookieGlitch - // The PaddingBaudRate [padding_baud_rate, pb] num capability is lowest baud rate where padding needed. - PaddingBaudRate - // The VirtualTerminal [virtual_terminal, vt] num capability is virtual terminal number (CB/unix). - VirtualTerminal - // The WidthStatusLine [width_status_line, wsl] num capability is number of columns in status line. - WidthStatusLine - // The NumLabels [num_labels, nlab] num capability is number of labels on screen. - NumLabels - // The LabelHeight [label_height, lh] num capability is rows in each label. - LabelHeight - // The LabelWidth [label_width, lw] num capability is columns in each label. - LabelWidth - // The MaxAttributes [max_attributes, ma] num capability is maximum combined attributes terminal can handle. - MaxAttributes - // The MaximumWindows [maximum_windows, wnum] num capability is maximum number of definable windows. - MaximumWindows - // The MaxColors [max_colors, colors] num capability is maximum number of colors on screen. - MaxColors - // The MaxPairs [max_pairs, pairs] num capability is maximum number of color-pairs on the screen. - MaxPairs - // The NoColorVideo [no_color_video, ncv] num capability is video attributes that cannot be used with colors. - NoColorVideo - // The BufferCapacity [buffer_capacity, bufsz] num capability is numbers of bytes buffered before printing. - BufferCapacity - // The DotVertSpacing [dot_vert_spacing, spinv] num capability is spacing of pins vertically in pins per inch. - DotVertSpacing - // The DotHorzSpacing [dot_horz_spacing, spinh] num capability is spacing of dots horizontally in dots per inch. - DotHorzSpacing - // The MaxMicroAddress [max_micro_address, maddr] num capability is maximum value in micro_..._address. - MaxMicroAddress - // The MaxMicroJump [max_micro_jump, mjump] num capability is maximum value in parm_..._micro. - MaxMicroJump - // The MicroColSize [micro_col_size, mcs] num capability is character step size when in micro mode. - MicroColSize - // The MicroLineSize [micro_line_size, mls] num capability is line step size when in micro mode. - MicroLineSize - // The NumberOfPins [number_of_pins, npins] num capability is numbers of pins in print-head. - NumberOfPins - // The OutputResChar [output_res_char, orc] num capability is horizontal resolution in units per line. - OutputResChar - // The OutputResLine [output_res_line, orl] num capability is vertical resolution in units per line. - OutputResLine - // The OutputResHorzInch [output_res_horz_inch, orhi] num capability is horizontal resolution in units per inch. - OutputResHorzInch - // The OutputResVertInch [output_res_vert_inch, orvi] num capability is vertical resolution in units per inch. - OutputResVertInch - // The PrintRate [print_rate, cps] num capability is print rate in characters per second. - PrintRate - // The WideCharSize [wide_char_size, widcs] num capability is character step size when in double wide mode. - WideCharSize - // The Buttons [buttons, btns] num capability is number of buttons on mouse. - Buttons - // The BitImageEntwining [bit_image_entwining, bitwin] num capability is number of passes for each bit-image row. - BitImageEntwining - // The BitImageType [bit_image_type, bitype] num capability is type of bit-image device. - BitImageType - // The MagicCookieGlitchUl [magic_cookie_glitch_ul, OTug] num capability is number of blanks left by ul. - MagicCookieGlitchUl - // The CarriageReturnDelay [carriage_return_delay, OTdC] num capability is pad needed for CR. - CarriageReturnDelay - // The NewLineDelay [new_line_delay, OTdN] num capability is pad needed for LF. - NewLineDelay - // The BackspaceDelay [backspace_delay, OTdB] num capability is padding required for ^H. - BackspaceDelay - // The HorizontalTabDelay [horizontal_tab_delay, OTdT] num capability is padding required for ^I. - HorizontalTabDelay - // The NumberOfFunctionKeys [number_of_function_keys, OTkn] num capability is count of function keys. - NumberOfFunctionKeys -) - -// String capabilities. -const ( - // The BackTab [back_tab, cbt] string capability is the back tab (P). - BackTab = iota - // The Bell [bell, bel] string capability is the audible signal (bell) (P). - Bell - // The CarriageReturn [carriage_return, cr] string capability is the carriage return (P*) (P*). - CarriageReturn - // The ChangeScrollRegion [change_scroll_region, csr] string capability is the change region to line #1 to line #2 (P). - ChangeScrollRegion - // The ClearAllTabs [clear_all_tabs, tbc] string capability is the clear all tab stops (P). - ClearAllTabs - // The ClearScreen [clear_screen, clear] string capability is the clear screen and home cursor (P*). - ClearScreen - // The ClrEol [clr_eol, el] string capability is the clear to end of line (P). - ClrEol - // The ClrEos [clr_eos, ed] string capability is the clear to end of screen (P*). - ClrEos - // The ColumnAddress [column_address, hpa] string capability is the horizontal position #1, absolute (P). - ColumnAddress - // The CommandCharacter [command_character, cmdch] string capability is the terminal settable cmd character in prototype !?. - CommandCharacter - // The CursorAddress [cursor_address, cup] string capability is the move to row #1 columns #2. - CursorAddress - // The CursorDown [cursor_down, cud1] string capability is the down one line. - CursorDown - // The CursorHome [cursor_home, home] string capability is the home cursor (if no cup). - CursorHome - // The CursorInvisible [cursor_invisible, civis] string capability is the make cursor invisible. - CursorInvisible - // The CursorLeft [cursor_left, cub1] string capability is the move left one space. - CursorLeft - // The CursorMemAddress [cursor_mem_address, mrcup] string capability is the memory relative cursor addressing, move to row #1 columns #2. - CursorMemAddress - // The CursorNormal [cursor_normal, cnorm] string capability is the make cursor appear normal (undo civis/cvvis). - CursorNormal - // The CursorRight [cursor_right, cuf1] string capability is the non-destructive space (move right one space). - CursorRight - // The CursorToLl [cursor_to_ll, ll] string capability is the last line, first column (if no cup). - CursorToLl - // The CursorUp [cursor_up, cuu1] string capability is the up one line. - CursorUp - // The CursorVisible [cursor_visible, cvvis] string capability is the make cursor very visible. - CursorVisible - // The DeleteCharacter [delete_character, dch1] string capability is the delete character (P*). - DeleteCharacter - // The DeleteLine [delete_line, dl1] string capability is the delete line (P*). - DeleteLine - // The DisStatusLine [dis_status_line, dsl] string capability is the disable status line. - DisStatusLine - // The DownHalfLine [down_half_line, hd] string capability is the half a line down. - DownHalfLine - // The EnterAltCharsetMode [enter_alt_charset_mode, smacs] string capability is the start alternate character set (P). - EnterAltCharsetMode - // The EnterBlinkMode [enter_blink_mode, blink] string capability is the turn on blinking. - EnterBlinkMode - // The EnterBoldMode [enter_bold_mode, bold] string capability is the turn on bold (extra bright) mode. - EnterBoldMode - // The EnterCaMode [enter_ca_mode, smcup] string capability is the string to start programs using cup. - EnterCaMode - // The EnterDeleteMode [enter_delete_mode, smdc] string capability is the enter delete mode. - EnterDeleteMode - // The EnterDimMode [enter_dim_mode, dim] string capability is the turn on half-bright mode. - EnterDimMode - // The EnterInsertMode [enter_insert_mode, smir] string capability is the enter insert mode. - EnterInsertMode - // The EnterSecureMode [enter_secure_mode, invis] string capability is the turn on blank mode (characters invisible). - EnterSecureMode - // The EnterProtectedMode [enter_protected_mode, prot] string capability is the turn on protected mode. - EnterProtectedMode - // The EnterReverseMode [enter_reverse_mode, rev] string capability is the turn on reverse video mode. - EnterReverseMode - // The EnterStandoutMode [enter_standout_mode, smso] string capability is the begin standout mode. - EnterStandoutMode - // The EnterUnderlineMode [enter_underline_mode, smul] string capability is the begin underline mode. - EnterUnderlineMode - // The EraseChars [erase_chars, ech] string capability is the erase #1 characters (P). - EraseChars - // The ExitAltCharsetMode [exit_alt_charset_mode, rmacs] string capability is the end alternate character set (P). - ExitAltCharsetMode - // The ExitAttributeMode [exit_attribute_mode, sgr0] string capability is the turn off all attributes. - ExitAttributeMode - // The ExitCaMode [exit_ca_mode, rmcup] string capability is the strings to end programs using cup. - ExitCaMode - // The ExitDeleteMode [exit_delete_mode, rmdc] string capability is the end delete mode. - ExitDeleteMode - // The ExitInsertMode [exit_insert_mode, rmir] string capability is the exit insert mode. - ExitInsertMode - // The ExitStandoutMode [exit_standout_mode, rmso] string capability is the exit standout mode. - ExitStandoutMode - // The ExitUnderlineMode [exit_underline_mode, rmul] string capability is the exit underline mode. - ExitUnderlineMode - // The FlashScreen [flash_screen, flash] string capability is the visible bell (may not move cursor). - FlashScreen - // The FormFeed [form_feed, ff] string capability is the hardcopy terminal page eject (P*). - FormFeed - // The FromStatusLine [from_status_line, fsl] string capability is the return from status line. - FromStatusLine - // The Init1string [init_1string, is1] string capability is the initialization string. - Init1string - // The Init2string [init_2string, is2] string capability is the initialization string. - Init2string - // The Init3string [init_3string, is3] string capability is the initialization string. - Init3string - // The InitFile [init_file, if] string capability is the name of initialization file. - InitFile - // The InsertCharacter [insert_character, ich1] string capability is the insert character (P). - InsertCharacter - // The InsertLine [insert_line, il1] string capability is the insert line (P*). - InsertLine - // The InsertPadding [insert_padding, ip] string capability is the insert padding after inserted character. - InsertPadding - // The KeyBackspace [key_backspace, kbs] string capability is the backspace key. - KeyBackspace - // The KeyCatab [key_catab, ktbc] string capability is the clear-all-tabs key. - KeyCatab - // The KeyClear [key_clear, kclr] string capability is the clear-screen or erase key. - KeyClear - // The KeyCtab [key_ctab, kctab] string capability is the clear-tab key. - KeyCtab - // The KeyDc [key_dc, kdch1] string capability is the delete-character key. - KeyDc - // The KeyDl [key_dl, kdl1] string capability is the delete-line key. - KeyDl - // The KeyDown [key_down, kcud1] string capability is the down-arrow key. - KeyDown - // The KeyEic [key_eic, krmir] string capability is the sent by rmir or smir in insert mode. - KeyEic - // The KeyEol [key_eol, kel] string capability is the clear-to-end-of-line key. - KeyEol - // The KeyEos [key_eos, ked] string capability is the clear-to-end-of-screen key. - KeyEos - // The KeyF0 [key_f0, kf0] string capability is the F0 function key. - KeyF0 - // The KeyF1 [key_f1, kf1] string capability is the F1 function key. - KeyF1 - // The KeyF10 [key_f10, kf10] string capability is the F10 function key. - KeyF10 - // The KeyF2 [key_f2, kf2] string capability is the F2 function key. - KeyF2 - // The KeyF3 [key_f3, kf3] string capability is the F3 function key. - KeyF3 - // The KeyF4 [key_f4, kf4] string capability is the F4 function key. - KeyF4 - // The KeyF5 [key_f5, kf5] string capability is the F5 function key. - KeyF5 - // The KeyF6 [key_f6, kf6] string capability is the F6 function key. - KeyF6 - // The KeyF7 [key_f7, kf7] string capability is the F7 function key. - KeyF7 - // The KeyF8 [key_f8, kf8] string capability is the F8 function key. - KeyF8 - // The KeyF9 [key_f9, kf9] string capability is the F9 function key. - KeyF9 - // The KeyHome [key_home, khome] string capability is the home key. - KeyHome - // The KeyIc [key_ic, kich1] string capability is the insert-character key. - KeyIc - // The KeyIl [key_il, kil1] string capability is the insert-line key. - KeyIl - // The KeyLeft [key_left, kcub1] string capability is the left-arrow key. - KeyLeft - // The KeyLl [key_ll, kll] string capability is the lower-left key (home down). - KeyLl - // The KeyNpage [key_npage, knp] string capability is the next-page key. - KeyNpage - // The KeyPpage [key_ppage, kpp] string capability is the previous-page key. - KeyPpage - // The KeyRight [key_right, kcuf1] string capability is the right-arrow key. - KeyRight - // The KeySf [key_sf, kind] string capability is the scroll-forward key. - KeySf - // The KeySr [key_sr, kri] string capability is the scroll-backward key. - KeySr - // The KeyStab [key_stab, khts] string capability is the set-tab key. - KeyStab - // The KeyUp [key_up, kcuu1] string capability is the up-arrow key. - KeyUp - // The KeypadLocal [keypad_local, rmkx] string capability is the leave 'keyboard_transmit' mode. - KeypadLocal - // The KeypadXmit [keypad_xmit, smkx] string capability is the enter 'keyboard_transmit' mode. - KeypadXmit - // The LabF0 [lab_f0, lf0] string capability is the label on function key f0 if not f0. - LabF0 - // The LabF1 [lab_f1, lf1] string capability is the label on function key f1 if not f1. - LabF1 - // The LabF10 [lab_f10, lf10] string capability is the label on function key f10 if not f10. - LabF10 - // The LabF2 [lab_f2, lf2] string capability is the label on function key f2 if not f2. - LabF2 - // The LabF3 [lab_f3, lf3] string capability is the label on function key f3 if not f3. - LabF3 - // The LabF4 [lab_f4, lf4] string capability is the label on function key f4 if not f4. - LabF4 - // The LabF5 [lab_f5, lf5] string capability is the label on function key f5 if not f5. - LabF5 - // The LabF6 [lab_f6, lf6] string capability is the label on function key f6 if not f6. - LabF6 - // The LabF7 [lab_f7, lf7] string capability is the label on function key f7 if not f7. - LabF7 - // The LabF8 [lab_f8, lf8] string capability is the label on function key f8 if not f8. - LabF8 - // The LabF9 [lab_f9, lf9] string capability is the label on function key f9 if not f9. - LabF9 - // The MetaOff [meta_off, rmm] string capability is the turn off meta mode. - MetaOff - // The MetaOn [meta_on, smm] string capability is the turn on meta mode (8th-bit on). - MetaOn - // The Newline [newline, nel] string capability is the newline (behave like cr followed by lf). - Newline - // The PadChar [pad_char, pad] string capability is the padding char (instead of null). - PadChar - // The ParmDch [parm_dch, dch] string capability is the delete #1 characters (P*). - ParmDch - // The ParmDeleteLine [parm_delete_line, dl] string capability is the delete #1 lines (P*). - ParmDeleteLine - // The ParmDownCursor [parm_down_cursor, cud] string capability is the down #1 lines (P*). - ParmDownCursor - // The ParmIch [parm_ich, ich] string capability is the insert #1 characters (P*). - ParmIch - // The ParmIndex [parm_index, indn] string capability is the scroll forward #1 lines (P). - ParmIndex - // The ParmInsertLine [parm_insert_line, il] string capability is the insert #1 lines (P*). - ParmInsertLine - // The ParmLeftCursor [parm_left_cursor, cub] string capability is the move #1 characters to the left (P). - ParmLeftCursor - // The ParmRightCursor [parm_right_cursor, cuf] string capability is the move #1 characters to the right (P*). - ParmRightCursor - // The ParmRindex [parm_rindex, rin] string capability is the scroll back #1 lines (P). - ParmRindex - // The ParmUpCursor [parm_up_cursor, cuu] string capability is the up #1 lines (P*). - ParmUpCursor - // The PkeyKey [pkey_key, pfkey] string capability is the program function key #1 to type string #2. - PkeyKey - // The PkeyLocal [pkey_local, pfloc] string capability is the program function key #1 to execute string #2. - PkeyLocal - // The PkeyXmit [pkey_xmit, pfx] string capability is the program function key #1 to transmit string #2. - PkeyXmit - // The PrintScreen [print_screen, mc0] string capability is the print contents of screen. - PrintScreen - // The PrtrOff [prtr_off, mc4] string capability is the turn off printer. - PrtrOff - // The PrtrOn [prtr_on, mc5] string capability is the turn on printer. - PrtrOn - // The RepeatChar [repeat_char, rep] string capability is the repeat char #1 #2 times (P*). - RepeatChar - // The Reset1string [reset_1string, rs1] string capability is the reset string. - Reset1string - // The Reset2string [reset_2string, rs2] string capability is the reset string. - Reset2string - // The Reset3string [reset_3string, rs3] string capability is the reset string. - Reset3string - // The ResetFile [reset_file, rf] string capability is the name of reset file. - ResetFile - // The RestoreCursor [restore_cursor, rc] string capability is the restore cursor to position of last save_cursor. - RestoreCursor - // The RowAddress [row_address, vpa] string capability is the vertical position #1 absolute (P). - RowAddress - // The SaveCursor [save_cursor, sc] string capability is the save current cursor position (P). - SaveCursor - // The ScrollForward [scroll_forward, ind] string capability is the scroll text up (P). - ScrollForward - // The ScrollReverse [scroll_reverse, ri] string capability is the scroll text down (P). - ScrollReverse - // The SetAttributes [set_attributes, sgr] string capability is the define video attributes #1-#9 (PG9). - SetAttributes - // The SetTab [set_tab, hts] string capability is the set a tab in every row, current columns. - SetTab - // The SetWindow [set_window, wind] string capability is the current window is lines #1-#2 cols #3-#4. - SetWindow - // The Tab [tab, ht] string capability is the tab to next 8-space hardware tab stop. - Tab - // The ToStatusLine [to_status_line, tsl] string capability is the move to status line, column #1. - ToStatusLine - // The UnderlineChar [underline_char, uc] string capability is the underline char and move past it. - UnderlineChar - // The UpHalfLine [up_half_line, hu] string capability is the half a line up. - UpHalfLine - // The InitProg [init_prog, iprog] string capability is the path name of program for initialization. - InitProg - // The KeyA1 [key_a1, ka1] string capability is the upper left of keypad. - KeyA1 - // The KeyA3 [key_a3, ka3] string capability is the upper right of keypad. - KeyA3 - // The KeyB2 [key_b2, kb2] string capability is the center of keypad. - KeyB2 - // The KeyC1 [key_c1, kc1] string capability is the lower left of keypad. - KeyC1 - // The KeyC3 [key_c3, kc3] string capability is the lower right of keypad. - KeyC3 - // The PrtrNon [prtr_non, mc5p] string capability is the turn on printer for #1 bytes. - PrtrNon - // The CharPadding [char_padding, rmp] string capability is the like ip but when in insert mode. - CharPadding - // The AcsChars [acs_chars, acsc] string capability is the graphics charset pairs, based on vt100. - AcsChars - // The PlabNorm [plab_norm, pln] string capability is the program label #1 to show string #2. - PlabNorm - // The KeyBtab [key_btab, kcbt] string capability is the back-tab key. - KeyBtab - // The EnterXonMode [enter_xon_mode, smxon] string capability is the turn on xon/xoff handshaking. - EnterXonMode - // The ExitXonMode [exit_xon_mode, rmxon] string capability is the turn off xon/xoff handshaking. - ExitXonMode - // The EnterAmMode [enter_am_mode, smam] string capability is the turn on automatic margins. - EnterAmMode - // The ExitAmMode [exit_am_mode, rmam] string capability is the turn off automatic margins. - ExitAmMode - // The XonCharacter [xon_character, xonc] string capability is the XON character. - XonCharacter - // The XoffCharacter [xoff_character, xoffc] string capability is the XOFF character. - XoffCharacter - // The EnaAcs [ena_acs, enacs] string capability is the enable alternate char set. - EnaAcs - // The LabelOn [label_on, smln] string capability is the turn on soft labels. - LabelOn - // The LabelOff [label_off, rmln] string capability is the turn off soft labels. - LabelOff - // The KeyBeg [key_beg, kbeg] string capability is the begin key. - KeyBeg - // The KeyCancel [key_cancel, kcan] string capability is the cancel key. - KeyCancel - // The KeyClose [key_close, kclo] string capability is the close key. - KeyClose - // The KeyCommand [key_command, kcmd] string capability is the command key. - KeyCommand - // The KeyCopy [key_copy, kcpy] string capability is the copy key. - KeyCopy - // The KeyCreate [key_create, kcrt] string capability is the create key. - KeyCreate - // The KeyEnd [key_end, kend] string capability is the end key. - KeyEnd - // The KeyEnter [key_enter, kent] string capability is the enter/send key. - KeyEnter - // The KeyExit [key_exit, kext] string capability is the exit key. - KeyExit - // The KeyFind [key_find, kfnd] string capability is the find key. - KeyFind - // The KeyHelp [key_help, khlp] string capability is the help key. - KeyHelp - // The KeyMark [key_mark, kmrk] string capability is the mark key. - KeyMark - // The KeyMessage [key_message, kmsg] string capability is the message key. - KeyMessage - // The KeyMove [key_move, kmov] string capability is the move key. - KeyMove - // The KeyNext [key_next, knxt] string capability is the next key. - KeyNext - // The KeyOpen [key_open, kopn] string capability is the open key. - KeyOpen - // The KeyOptions [key_options, kopt] string capability is the options key. - KeyOptions - // The KeyPrevious [key_previous, kprv] string capability is the previous key. - KeyPrevious - // The KeyPrint [key_print, kprt] string capability is the print key. - KeyPrint - // The KeyRedo [key_redo, krdo] string capability is the redo key. - KeyRedo - // The KeyReference [key_reference, kref] string capability is the reference key. - KeyReference - // The KeyRefresh [key_refresh, krfr] string capability is the refresh key. - KeyRefresh - // The KeyReplace [key_replace, krpl] string capability is the replace key. - KeyReplace - // The KeyRestart [key_restart, krst] string capability is the restart key. - KeyRestart - // The KeyResume [key_resume, kres] string capability is the resume key. - KeyResume - // The KeySave [key_save, ksav] string capability is the save key. - KeySave - // The KeySuspend [key_suspend, kspd] string capability is the suspend key. - KeySuspend - // The KeyUndo [key_undo, kund] string capability is the undo key. - KeyUndo - // The KeySbeg [key_sbeg, kBEG] string capability is the shifted begin key. - KeySbeg - // The KeyScancel [key_scancel, kCAN] string capability is the shifted cancel key. - KeyScancel - // The KeyScommand [key_scommand, kCMD] string capability is the shifted command key. - KeyScommand - // The KeyScopy [key_scopy, kCPY] string capability is the shifted copy key. - KeyScopy - // The KeyScreate [key_screate, kCRT] string capability is the shifted create key. - KeyScreate - // The KeySdc [key_sdc, kDC] string capability is the shifted delete-character key. - KeySdc - // The KeySdl [key_sdl, kDL] string capability is the shifted delete-line key. - KeySdl - // The KeySelect [key_select, kslt] string capability is the select key. - KeySelect - // The KeySend [key_send, kEND] string capability is the shifted end key. - KeySend - // The KeySeol [key_seol, kEOL] string capability is the shifted clear-to-end-of-line key. - KeySeol - // The KeySexit [key_sexit, kEXT] string capability is the shifted exit key. - KeySexit - // The KeySfind [key_sfind, kFND] string capability is the shifted find key. - KeySfind - // The KeyShelp [key_shelp, kHLP] string capability is the shifted help key. - KeyShelp - // The KeyShome [key_shome, kHOM] string capability is the shifted home key. - KeyShome - // The KeySic [key_sic, kIC] string capability is the shifted insert-character key. - KeySic - // The KeySleft [key_sleft, kLFT] string capability is the shifted left-arrow key. - KeySleft - // The KeySmessage [key_smessage, kMSG] string capability is the shifted message key. - KeySmessage - // The KeySmove [key_smove, kMOV] string capability is the shifted move key. - KeySmove - // The KeySnext [key_snext, kNXT] string capability is the shifted next key. - KeySnext - // The KeySoptions [key_soptions, kOPT] string capability is the shifted options key. - KeySoptions - // The KeySprevious [key_sprevious, kPRV] string capability is the shifted previous key. - KeySprevious - // The KeySprint [key_sprint, kPRT] string capability is the shifted print key. - KeySprint - // The KeySredo [key_sredo, kRDO] string capability is the shifted redo key. - KeySredo - // The KeySreplace [key_sreplace, kRPL] string capability is the shifted replace key. - KeySreplace - // The KeySright [key_sright, kRIT] string capability is the shifted right-arrow key. - KeySright - // The KeySrsume [key_srsume, kRES] string capability is the shifted resume key. - KeySrsume - // The KeySsave [key_ssave, kSAV] string capability is the shifted save key. - KeySsave - // The KeySsuspend [key_ssuspend, kSPD] string capability is the shifted suspend key. - KeySsuspend - // The KeySundo [key_sundo, kUND] string capability is the shifted undo key. - KeySundo - // The ReqForInput [req_for_input, rfi] string capability is the send next input char (for ptys). - ReqForInput - // The KeyF11 [key_f11, kf11] string capability is the F11 function key. - KeyF11 - // The KeyF12 [key_f12, kf12] string capability is the F12 function key. - KeyF12 - // The KeyF13 [key_f13, kf13] string capability is the F13 function key. - KeyF13 - // The KeyF14 [key_f14, kf14] string capability is the F14 function key. - KeyF14 - // The KeyF15 [key_f15, kf15] string capability is the F15 function key. - KeyF15 - // The KeyF16 [key_f16, kf16] string capability is the F16 function key. - KeyF16 - // The KeyF17 [key_f17, kf17] string capability is the F17 function key. - KeyF17 - // The KeyF18 [key_f18, kf18] string capability is the F18 function key. - KeyF18 - // The KeyF19 [key_f19, kf19] string capability is the F19 function key. - KeyF19 - // The KeyF20 [key_f20, kf20] string capability is the F20 function key. - KeyF20 - // The KeyF21 [key_f21, kf21] string capability is the F21 function key. - KeyF21 - // The KeyF22 [key_f22, kf22] string capability is the F22 function key. - KeyF22 - // The KeyF23 [key_f23, kf23] string capability is the F23 function key. - KeyF23 - // The KeyF24 [key_f24, kf24] string capability is the F24 function key. - KeyF24 - // The KeyF25 [key_f25, kf25] string capability is the F25 function key. - KeyF25 - // The KeyF26 [key_f26, kf26] string capability is the F26 function key. - KeyF26 - // The KeyF27 [key_f27, kf27] string capability is the F27 function key. - KeyF27 - // The KeyF28 [key_f28, kf28] string capability is the F28 function key. - KeyF28 - // The KeyF29 [key_f29, kf29] string capability is the F29 function key. - KeyF29 - // The KeyF30 [key_f30, kf30] string capability is the F30 function key. - KeyF30 - // The KeyF31 [key_f31, kf31] string capability is the F31 function key. - KeyF31 - // The KeyF32 [key_f32, kf32] string capability is the F32 function key. - KeyF32 - // The KeyF33 [key_f33, kf33] string capability is the F33 function key. - KeyF33 - // The KeyF34 [key_f34, kf34] string capability is the F34 function key. - KeyF34 - // The KeyF35 [key_f35, kf35] string capability is the F35 function key. - KeyF35 - // The KeyF36 [key_f36, kf36] string capability is the F36 function key. - KeyF36 - // The KeyF37 [key_f37, kf37] string capability is the F37 function key. - KeyF37 - // The KeyF38 [key_f38, kf38] string capability is the F38 function key. - KeyF38 - // The KeyF39 [key_f39, kf39] string capability is the F39 function key. - KeyF39 - // The KeyF40 [key_f40, kf40] string capability is the F40 function key. - KeyF40 - // The KeyF41 [key_f41, kf41] string capability is the F41 function key. - KeyF41 - // The KeyF42 [key_f42, kf42] string capability is the F42 function key. - KeyF42 - // The KeyF43 [key_f43, kf43] string capability is the F43 function key. - KeyF43 - // The KeyF44 [key_f44, kf44] string capability is the F44 function key. - KeyF44 - // The KeyF45 [key_f45, kf45] string capability is the F45 function key. - KeyF45 - // The KeyF46 [key_f46, kf46] string capability is the F46 function key. - KeyF46 - // The KeyF47 [key_f47, kf47] string capability is the F47 function key. - KeyF47 - // The KeyF48 [key_f48, kf48] string capability is the F48 function key. - KeyF48 - // The KeyF49 [key_f49, kf49] string capability is the F49 function key. - KeyF49 - // The KeyF50 [key_f50, kf50] string capability is the F50 function key. - KeyF50 - // The KeyF51 [key_f51, kf51] string capability is the F51 function key. - KeyF51 - // The KeyF52 [key_f52, kf52] string capability is the F52 function key. - KeyF52 - // The KeyF53 [key_f53, kf53] string capability is the F53 function key. - KeyF53 - // The KeyF54 [key_f54, kf54] string capability is the F54 function key. - KeyF54 - // The KeyF55 [key_f55, kf55] string capability is the F55 function key. - KeyF55 - // The KeyF56 [key_f56, kf56] string capability is the F56 function key. - KeyF56 - // The KeyF57 [key_f57, kf57] string capability is the F57 function key. - KeyF57 - // The KeyF58 [key_f58, kf58] string capability is the F58 function key. - KeyF58 - // The KeyF59 [key_f59, kf59] string capability is the F59 function key. - KeyF59 - // The KeyF60 [key_f60, kf60] string capability is the F60 function key. - KeyF60 - // The KeyF61 [key_f61, kf61] string capability is the F61 function key. - KeyF61 - // The KeyF62 [key_f62, kf62] string capability is the F62 function key. - KeyF62 - // The KeyF63 [key_f63, kf63] string capability is the F63 function key. - KeyF63 - // The ClrBol [clr_bol, el1] string capability is the Clear to beginning of line. - ClrBol - // The ClearMargins [clear_margins, mgc] string capability is the clear right and left soft margins. - ClearMargins - // The SetLeftMargin [set_left_margin, smgl] string capability is the set left soft margin at current column. (ML is not in BSD termcap). - SetLeftMargin - // The SetRightMargin [set_right_margin, smgr] string capability is the set right soft margin at current column. - SetRightMargin - // The LabelFormat [label_format, fln] string capability is the label format. - LabelFormat - // The SetClock [set_clock, sclk] string capability is the set clock, #1 hrs #2 mins #3 secs. - SetClock - // The DisplayClock [display_clock, dclk] string capability is the display clock. - DisplayClock - // The RemoveClock [remove_clock, rmclk] string capability is the remove clock. - RemoveClock - // The CreateWindow [create_window, cwin] string capability is the define a window #1 from #2,#3 to #4,#5. - CreateWindow - // The GotoWindow [goto_window, wingo] string capability is the go to window #1. - GotoWindow - // The Hangup [hangup, hup] string capability is the hang-up phone. - Hangup - // The DialPhone [dial_phone, dial] string capability is the dial number #1. - DialPhone - // The QuickDial [quick_dial, qdial] string capability is the dial number #1 without checking. - QuickDial - // The Tone [tone, tone] string capability is the select touch tone dialing. - Tone - // The Pulse [pulse, pulse] string capability is the select pulse dialing. - Pulse - // The FlashHook [flash_hook, hook] string capability is the flash switch hook. - FlashHook - // The FixedPause [fixed_pause, pause] string capability is the pause for 2-3 seconds. - FixedPause - // The WaitTone [wait_tone, wait] string capability is the wait for dial-tone. - WaitTone - // The User0 [user0, u0] string capability is the User string #0. - User0 - // The User1 [user1, u1] string capability is the User string #1. - User1 - // The User2 [user2, u2] string capability is the User string #2. - User2 - // The User3 [user3, u3] string capability is the User string #3. - User3 - // The User4 [user4, u4] string capability is the User string #4. - User4 - // The User5 [user5, u5] string capability is the User string #5. - User5 - // The User6 [user6, u6] string capability is the User string #6. - User6 - // The User7 [user7, u7] string capability is the User string #7. - User7 - // The User8 [user8, u8] string capability is the User string #8. - User8 - // The User9 [user9, u9] string capability is the User string #9. - User9 - // The OrigPair [orig_pair, op] string capability is the Set default pair to its original value. - OrigPair - // The OrigColors [orig_colors, oc] string capability is the Set all color pairs to the original ones. - OrigColors - // The InitializeColor [initialize_color, initc] string capability is the initialize color #1 to (#2,#3,#4). - InitializeColor - // The InitializePair [initialize_pair, initp] string capability is the Initialize color pair #1 to fg=(#2,#3,#4), bg=(#5,#6,#7). - InitializePair - // The SetColorPair [set_color_pair, scp] string capability is the Set current color pair to #1. - SetColorPair - // The SetForeground [set_foreground, setf] string capability is the Set foreground color #1. - SetForeground - // The SetBackground [set_background, setb] string capability is the Set background color #1. - SetBackground - // The ChangeCharPitch [change_char_pitch, cpi] string capability is the Change number of characters per inch to #1. - ChangeCharPitch - // The ChangeLinePitch [change_line_pitch, lpi] string capability is the Change number of lines per inch to #1. - ChangeLinePitch - // The ChangeResHorz [change_res_horz, chr] string capability is the Change horizontal resolution to #1. - ChangeResHorz - // The ChangeResVert [change_res_vert, cvr] string capability is the Change vertical resolution to #1. - ChangeResVert - // The DefineChar [define_char, defc] string capability is the Define a character #1, #2 dots wide, descender #3. - DefineChar - // The EnterDoublewideMode [enter_doublewide_mode, swidm] string capability is the Enter double-wide mode. - EnterDoublewideMode - // The EnterDraftQuality [enter_draft_quality, sdrfq] string capability is the Enter draft-quality mode. - EnterDraftQuality - // The EnterItalicsMode [enter_italics_mode, sitm] string capability is the Enter italic mode. - EnterItalicsMode - // The EnterLeftwardMode [enter_leftward_mode, slm] string capability is the Start leftward carriage motion. - EnterLeftwardMode - // The EnterMicroMode [enter_micro_mode, smicm] string capability is the Start micro-motion mode. - EnterMicroMode - // The EnterNearLetterQuality [enter_near_letter_quality, snlq] string capability is the Enter NLQ mode. - EnterNearLetterQuality - // The EnterNormalQuality [enter_normal_quality, snrmq] string capability is the Enter normal-quality mode. - EnterNormalQuality - // The EnterShadowMode [enter_shadow_mode, sshm] string capability is the Enter shadow-print mode. - EnterShadowMode - // The EnterSubscriptMode [enter_subscript_mode, ssubm] string capability is the Enter subscript mode. - EnterSubscriptMode - // The EnterSuperscriptMode [enter_superscript_mode, ssupm] string capability is the Enter superscript mode. - EnterSuperscriptMode - // The EnterUpwardMode [enter_upward_mode, sum] string capability is the Start upward carriage motion. - EnterUpwardMode - // The ExitDoublewideMode [exit_doublewide_mode, rwidm] string capability is the End double-wide mode. - ExitDoublewideMode - // The ExitItalicsMode [exit_italics_mode, ritm] string capability is the End italic mode. - ExitItalicsMode - // The ExitLeftwardMode [exit_leftward_mode, rlm] string capability is the End left-motion mode. - ExitLeftwardMode - // The ExitMicroMode [exit_micro_mode, rmicm] string capability is the End micro-motion mode. - ExitMicroMode - // The ExitShadowMode [exit_shadow_mode, rshm] string capability is the End shadow-print mode. - ExitShadowMode - // The ExitSubscriptMode [exit_subscript_mode, rsubm] string capability is the End subscript mode. - ExitSubscriptMode - // The ExitSuperscriptMode [exit_superscript_mode, rsupm] string capability is the End superscript mode. - ExitSuperscriptMode - // The ExitUpwardMode [exit_upward_mode, rum] string capability is the End reverse character motion. - ExitUpwardMode - // The MicroColumnAddress [micro_column_address, mhpa] string capability is the Like column_address in micro mode. - MicroColumnAddress - // The MicroDown [micro_down, mcud1] string capability is the Like cursor_down in micro mode. - MicroDown - // The MicroLeft [micro_left, mcub1] string capability is the Like cursor_left in micro mode. - MicroLeft - // The MicroRight [micro_right, mcuf1] string capability is the Like cursor_right in micro mode. - MicroRight - // The MicroRowAddress [micro_row_address, mvpa] string capability is the Like row_address #1 in micro mode. - MicroRowAddress - // The MicroUp [micro_up, mcuu1] string capability is the Like cursor_up in micro mode. - MicroUp - // The OrderOfPins [order_of_pins, porder] string capability is the Match software bits to print-head pins. - OrderOfPins - // The ParmDownMicro [parm_down_micro, mcud] string capability is the Like parm_down_cursor in micro mode. - ParmDownMicro - // The ParmLeftMicro [parm_left_micro, mcub] string capability is the Like parm_left_cursor in micro mode. - ParmLeftMicro - // The ParmRightMicro [parm_right_micro, mcuf] string capability is the Like parm_right_cursor in micro mode. - ParmRightMicro - // The ParmUpMicro [parm_up_micro, mcuu] string capability is the Like parm_up_cursor in micro mode. - ParmUpMicro - // The SelectCharSet [select_char_set, scs] string capability is the Select character set, #1. - SelectCharSet - // The SetBottomMargin [set_bottom_margin, smgb] string capability is the Set bottom margin at current line. - SetBottomMargin - // The SetBottomMarginParm [set_bottom_margin_parm, smgbp] string capability is the Set bottom margin at line #1 or (if smgtp is not given) #2 lines from bottom. - SetBottomMarginParm - // The SetLeftMarginParm [set_left_margin_parm, smglp] string capability is the Set left (right) margin at column #1. - SetLeftMarginParm - // The SetRightMarginParm [set_right_margin_parm, smgrp] string capability is the Set right margin at column #1. - SetRightMarginParm - // The SetTopMargin [set_top_margin, smgt] string capability is the Set top margin at current line. - SetTopMargin - // The SetTopMarginParm [set_top_margin_parm, smgtp] string capability is the Set top (bottom) margin at row #1. - SetTopMarginParm - // The StartBitImage [start_bit_image, sbim] string capability is the Start printing bit image graphics. - StartBitImage - // The StartCharSetDef [start_char_set_def, scsd] string capability is the Start character set definition #1, with #2 characters in the set. - StartCharSetDef - // The StopBitImage [stop_bit_image, rbim] string capability is the Stop printing bit image graphics. - StopBitImage - // The StopCharSetDef [stop_char_set_def, rcsd] string capability is the End definition of character set #1. - StopCharSetDef - // The SubscriptCharacters [subscript_characters, subcs] string capability is the List of subscriptable characters. - SubscriptCharacters - // The SuperscriptCharacters [superscript_characters, supcs] string capability is the List of superscriptable characters. - SuperscriptCharacters - // The TheseCauseCr [these_cause_cr, docr] string capability is the Printing any of these characters causes CR. - TheseCauseCr - // The ZeroMotion [zero_motion, zerom] string capability is the No motion for subsequent character. - ZeroMotion - // The CharSetNames [char_set_names, csnm] string capability is the Produce #1'th item from list of character set names. - CharSetNames - // The KeyMouse [key_mouse, kmous] string capability is the Mouse event has occurred. - KeyMouse - // The MouseInfo [mouse_info, minfo] string capability is the Mouse status information. - MouseInfo - // The ReqMousePos [req_mouse_pos, reqmp] string capability is the Request mouse position. - ReqMousePos - // The GetMouse [get_mouse, getm] string capability is the Curses should get button events, parameter #1 not documented. - GetMouse - // The SetAForeground [set_a_foreground, setaf] string capability is the Set foreground color to #1, using ANSI escape. - SetAForeground - // The SetABackground [set_a_background, setab] string capability is the Set background color to #1, using ANSI escape. - SetABackground - // The PkeyPlab [pkey_plab, pfxl] string capability is the Program function key #1 to type string #2 and show string #3. - PkeyPlab - // The DeviceType [device_type, devt] string capability is the Indicate language/codeset support. - DeviceType - // The CodeSetInit [code_set_init, csin] string capability is the Init sequence for multiple codesets. - CodeSetInit - // The Set0DesSeq [set0_des_seq, s0ds] string capability is the Shift to codeset 0 (EUC set 0, ASCII). - Set0DesSeq - // The Set1DesSeq [set1_des_seq, s1ds] string capability is the Shift to codeset 1. - Set1DesSeq - // The Set2DesSeq [set2_des_seq, s2ds] string capability is the Shift to codeset 2. - Set2DesSeq - // The Set3DesSeq [set3_des_seq, s3ds] string capability is the Shift to codeset 3. - Set3DesSeq - // The SetLrMargin [set_lr_margin, smglr] string capability is the Set both left and right margins to #1, #2. (ML is not in BSD termcap). - SetLrMargin - // The SetTbMargin [set_tb_margin, smgtb] string capability is the Sets both top and bottom margins to #1, #2. - SetTbMargin - // The BitImageRepeat [bit_image_repeat, birep] string capability is the Repeat bit image cell #1 #2 times. - BitImageRepeat - // The BitImageNewline [bit_image_newline, binel] string capability is the Move to next row of the bit image. - BitImageNewline - // The BitImageCarriageReturn [bit_image_carriage_return, bicr] string capability is the Move to beginning of same row. - BitImageCarriageReturn - // The ColorNames [color_names, colornm] string capability is the Give name for color #1. - ColorNames - // The DefineBitImageRegion [define_bit_image_region, defbi] string capability is the Define rectangular bit image region. - DefineBitImageRegion - // The EndBitImageRegion [end_bit_image_region, endbi] string capability is the End a bit-image region. - EndBitImageRegion - // The SetColorBand [set_color_band, setcolor] string capability is the Change to ribbon color #1. - SetColorBand - // The SetPageLength [set_page_length, slines] string capability is the Set page length to #1 lines. - SetPageLength - // The DisplayPcChar [display_pc_char, dispc] string capability is the Display PC character #1. - DisplayPcChar - // The EnterPcCharsetMode [enter_pc_charset_mode, smpch] string capability is the Enter PC character display mode. - EnterPcCharsetMode - // The ExitPcCharsetMode [exit_pc_charset_mode, rmpch] string capability is the Exit PC character display mode. - ExitPcCharsetMode - // The EnterScancodeMode [enter_scancode_mode, smsc] string capability is the Enter PC scancode mode. - EnterScancodeMode - // The ExitScancodeMode [exit_scancode_mode, rmsc] string capability is the Exit PC scancode mode. - ExitScancodeMode - // The PcTermOptions [pc_term_options, pctrm] string capability is the PC terminal options. - PcTermOptions - // The ScancodeEscape [scancode_escape, scesc] string capability is the Escape for scancode emulation. - ScancodeEscape - // The AltScancodeEsc [alt_scancode_esc, scesa] string capability is the Alternate escape for scancode emulation. - AltScancodeEsc - // The EnterHorizontalHlMode [enter_horizontal_hl_mode, ehhlm] string capability is the Enter horizontal highlight mode. - EnterHorizontalHlMode - // The EnterLeftHlMode [enter_left_hl_mode, elhlm] string capability is the Enter left highlight mode. - EnterLeftHlMode - // The EnterLowHlMode [enter_low_hl_mode, elohlm] string capability is the Enter low highlight mode. - EnterLowHlMode - // The EnterRightHlMode [enter_right_hl_mode, erhlm] string capability is the Enter right highlight mode. - EnterRightHlMode - // The EnterTopHlMode [enter_top_hl_mode, ethlm] string capability is the Enter top highlight mode. - EnterTopHlMode - // The EnterVerticalHlMode [enter_vertical_hl_mode, evhlm] string capability is the Enter vertical highlight mode. - EnterVerticalHlMode - // The SetAAttributes [set_a_attributes, sgr1] string capability is the Define second set of video attributes #1-#6. - SetAAttributes - // The SetPglenInch [set_pglen_inch, slength] string capability is the Set page length to #1 hundredth of an inch (some implementations use sL for termcap). - SetPglenInch - // The TermcapInit2 [termcap_init2, OTi2] string capability is the secondary initialization string. - TermcapInit2 - // The TermcapReset [termcap_reset, OTrs] string capability is the terminal reset string. - TermcapReset - // The LinefeedIfNotLf [linefeed_if_not_lf, OTnl] string capability is the use to move down. - LinefeedIfNotLf - // The BackspaceIfNotBs [backspace_if_not_bs, OTbc] string capability is the move left, if not ^H. - BackspaceIfNotBs - // The OtherNonFunctionKeys [other_non_function_keys, OTko] string capability is the list of self-mapped keycaps. - OtherNonFunctionKeys - // The ArrowKeyMap [arrow_key_map, OTma] string capability is the map motion-keys for vi version 2. - ArrowKeyMap - // The AcsUlcorner [acs_ulcorner, OTG2] string capability is the single upper left. - AcsUlcorner - // The AcsLlcorner [acs_llcorner, OTG3] string capability is the single lower left. - AcsLlcorner - // The AcsUrcorner [acs_urcorner, OTG1] string capability is the single upper right. - AcsUrcorner - // The AcsLrcorner [acs_lrcorner, OTG4] string capability is the single lower right. - AcsLrcorner - // The AcsLtee [acs_ltee, OTGR] string capability is the tee pointing right. - AcsLtee - // The AcsRtee [acs_rtee, OTGL] string capability is the tee pointing left. - AcsRtee - // The AcsBtee [acs_btee, OTGU] string capability is the tee pointing up. - AcsBtee - // The AcsTtee [acs_ttee, OTGD] string capability is the tee pointing down. - AcsTtee - // The AcsHline [acs_hline, OTGH] string capability is the single horizontal line. - AcsHline - // The AcsVline [acs_vline, OTGV] string capability is the single vertical line. - AcsVline - // The AcsPlus [acs_plus, OTGC] string capability is the single intersection. - AcsPlus - // The MemoryLock [memory_lock, meml] string capability is the lock memory above cursor. - MemoryLock - // The MemoryUnlock [memory_unlock, memu] string capability is the unlock memory. - MemoryUnlock - // The BoxChars1 [box_chars_1, box1] string capability is the box characters primary set. - BoxChars1 -) -const ( - // CapCountBool is the count of bool capabilities. - CapCountBool = ReturnDoesClrEol + 1 - // CapCountNum is the count of num capabilities. - CapCountNum = NumberOfFunctionKeys + 1 - // CapCountString is the count of string capabilities. - CapCountString = BoxChars1 + 1 -) - -// boolCapNames are the bool term cap names. -var boolCapNames = [...]string{ - "auto_left_margin", "bw", - "auto_right_margin", "am", - "no_esc_ctlc", "xsb", - "ceol_standout_glitch", "xhp", - "eat_newline_glitch", "xenl", - "erase_overstrike", "eo", - "generic_type", "gn", - "hard_copy", "hc", - "has_meta_key", "km", - "has_status_line", "hs", - "insert_null_glitch", "in", - "memory_above", "da", - "memory_below", "db", - "move_insert_mode", "mir", - "move_standout_mode", "msgr", - "over_strike", "os", - "status_line_esc_ok", "eslok", - "dest_tabs_magic_smso", "xt", - "tilde_glitch", "hz", - "transparent_underline", "ul", - "xon_xoff", "xon", - "needs_xon_xoff", "nxon", - "prtr_silent", "mc5i", - "hard_cursor", "chts", - "non_rev_rmcup", "nrrmc", - "no_pad_char", "npc", - "non_dest_scroll_region", "ndscr", - "can_change", "ccc", - "back_color_erase", "bce", - "hue_lightness_saturation", "hls", - "col_addr_glitch", "xhpa", - "cr_cancels_micro_mode", "crxm", - "has_print_wheel", "daisy", - "row_addr_glitch", "xvpa", - "semi_auto_right_margin", "sam", - "cpi_changes_res", "cpix", - "lpi_changes_res", "lpix", - "backspaces_with_bs", "OTbs", - "crt_no_scrolling", "OTns", - "no_correctly_working_cr", "OTnc", - "gnu_has_meta_key", "OTMT", - "linefeed_is_newline", "OTNL", - "has_hardware_tabs", "OTpt", - "return_does_clr_eol", "OTxr", -} - -// numCapNames are the num term cap names. -var numCapNames = [...]string{ - "columns", "cols", - "init_tabs", "it", - "lines", "lines", - "lines_of_memory", "lm", - "magic_cookie_glitch", "xmc", - "padding_baud_rate", "pb", - "virtual_terminal", "vt", - "width_status_line", "wsl", - "num_labels", "nlab", - "label_height", "lh", - "label_width", "lw", - "max_attributes", "ma", - "maximum_windows", "wnum", - "max_colors", "colors", - "max_pairs", "pairs", - "no_color_video", "ncv", - "buffer_capacity", "bufsz", - "dot_vert_spacing", "spinv", - "dot_horz_spacing", "spinh", - "max_micro_address", "maddr", - "max_micro_jump", "mjump", - "micro_col_size", "mcs", - "micro_line_size", "mls", - "number_of_pins", "npins", - "output_res_char", "orc", - "output_res_line", "orl", - "output_res_horz_inch", "orhi", - "output_res_vert_inch", "orvi", - "print_rate", "cps", - "wide_char_size", "widcs", - "buttons", "btns", - "bit_image_entwining", "bitwin", - "bit_image_type", "bitype", - "magic_cookie_glitch_ul", "OTug", - "carriage_return_delay", "OTdC", - "new_line_delay", "OTdN", - "backspace_delay", "OTdB", - "horizontal_tab_delay", "OTdT", - "number_of_function_keys", "OTkn", -} - -// stringCapNames are the string term cap names. -var stringCapNames = [...]string{ - "back_tab", "cbt", - "bell", "bel", - "carriage_return", "cr", - "change_scroll_region", "csr", - "clear_all_tabs", "tbc", - "clear_screen", "clear", - "clr_eol", "el", - "clr_eos", "ed", - "column_address", "hpa", - "command_character", "cmdch", - "cursor_address", "cup", - "cursor_down", "cud1", - "cursor_home", "home", - "cursor_invisible", "civis", - "cursor_left", "cub1", - "cursor_mem_address", "mrcup", - "cursor_normal", "cnorm", - "cursor_right", "cuf1", - "cursor_to_ll", "ll", - "cursor_up", "cuu1", - "cursor_visible", "cvvis", - "delete_character", "dch1", - "delete_line", "dl1", - "dis_status_line", "dsl", - "down_half_line", "hd", - "enter_alt_charset_mode", "smacs", - "enter_blink_mode", "blink", - "enter_bold_mode", "bold", - "enter_ca_mode", "smcup", - "enter_delete_mode", "smdc", - "enter_dim_mode", "dim", - "enter_insert_mode", "smir", - "enter_secure_mode", "invis", - "enter_protected_mode", "prot", - "enter_reverse_mode", "rev", - "enter_standout_mode", "smso", - "enter_underline_mode", "smul", - "erase_chars", "ech", - "exit_alt_charset_mode", "rmacs", - "exit_attribute_mode", "sgr0", - "exit_ca_mode", "rmcup", - "exit_delete_mode", "rmdc", - "exit_insert_mode", "rmir", - "exit_standout_mode", "rmso", - "exit_underline_mode", "rmul", - "flash_screen", "flash", - "form_feed", "ff", - "from_status_line", "fsl", - "init_1string", "is1", - "init_2string", "is2", - "init_3string", "is3", - "init_file", "if", - "insert_character", "ich1", - "insert_line", "il1", - "insert_padding", "ip", - "key_backspace", "kbs", - "key_catab", "ktbc", - "key_clear", "kclr", - "key_ctab", "kctab", - "key_dc", "kdch1", - "key_dl", "kdl1", - "key_down", "kcud1", - "key_eic", "krmir", - "key_eol", "kel", - "key_eos", "ked", - "key_f0", "kf0", - "key_f1", "kf1", - "key_f10", "kf10", - "key_f2", "kf2", - "key_f3", "kf3", - "key_f4", "kf4", - "key_f5", "kf5", - "key_f6", "kf6", - "key_f7", "kf7", - "key_f8", "kf8", - "key_f9", "kf9", - "key_home", "khome", - "key_ic", "kich1", - "key_il", "kil1", - "key_left", "kcub1", - "key_ll", "kll", - "key_npage", "knp", - "key_ppage", "kpp", - "key_right", "kcuf1", - "key_sf", "kind", - "key_sr", "kri", - "key_stab", "khts", - "key_up", "kcuu1", - "keypad_local", "rmkx", - "keypad_xmit", "smkx", - "lab_f0", "lf0", - "lab_f1", "lf1", - "lab_f10", "lf10", - "lab_f2", "lf2", - "lab_f3", "lf3", - "lab_f4", "lf4", - "lab_f5", "lf5", - "lab_f6", "lf6", - "lab_f7", "lf7", - "lab_f8", "lf8", - "lab_f9", "lf9", - "meta_off", "rmm", - "meta_on", "smm", - "newline", "nel", - "pad_char", "pad", - "parm_dch", "dch", - "parm_delete_line", "dl", - "parm_down_cursor", "cud", - "parm_ich", "ich", - "parm_index", "indn", - "parm_insert_line", "il", - "parm_left_cursor", "cub", - "parm_right_cursor", "cuf", - "parm_rindex", "rin", - "parm_up_cursor", "cuu", - "pkey_key", "pfkey", - "pkey_local", "pfloc", - "pkey_xmit", "pfx", - "print_screen", "mc0", - "prtr_off", "mc4", - "prtr_on", "mc5", - "repeat_char", "rep", - "reset_1string", "rs1", - "reset_2string", "rs2", - "reset_3string", "rs3", - "reset_file", "rf", - "restore_cursor", "rc", - "row_address", "vpa", - "save_cursor", "sc", - "scroll_forward", "ind", - "scroll_reverse", "ri", - "set_attributes", "sgr", - "set_tab", "hts", - "set_window", "wind", - "tab", "ht", - "to_status_line", "tsl", - "underline_char", "uc", - "up_half_line", "hu", - "init_prog", "iprog", - "key_a1", "ka1", - "key_a3", "ka3", - "key_b2", "kb2", - "key_c1", "kc1", - "key_c3", "kc3", - "prtr_non", "mc5p", - "char_padding", "rmp", - "acs_chars", "acsc", - "plab_norm", "pln", - "key_btab", "kcbt", - "enter_xon_mode", "smxon", - "exit_xon_mode", "rmxon", - "enter_am_mode", "smam", - "exit_am_mode", "rmam", - "xon_character", "xonc", - "xoff_character", "xoffc", - "ena_acs", "enacs", - "label_on", "smln", - "label_off", "rmln", - "key_beg", "kbeg", - "key_cancel", "kcan", - "key_close", "kclo", - "key_command", "kcmd", - "key_copy", "kcpy", - "key_create", "kcrt", - "key_end", "kend", - "key_enter", "kent", - "key_exit", "kext", - "key_find", "kfnd", - "key_help", "khlp", - "key_mark", "kmrk", - "key_message", "kmsg", - "key_move", "kmov", - "key_next", "knxt", - "key_open", "kopn", - "key_options", "kopt", - "key_previous", "kprv", - "key_print", "kprt", - "key_redo", "krdo", - "key_reference", "kref", - "key_refresh", "krfr", - "key_replace", "krpl", - "key_restart", "krst", - "key_resume", "kres", - "key_save", "ksav", - "key_suspend", "kspd", - "key_undo", "kund", - "key_sbeg", "kBEG", - "key_scancel", "kCAN", - "key_scommand", "kCMD", - "key_scopy", "kCPY", - "key_screate", "kCRT", - "key_sdc", "kDC", - "key_sdl", "kDL", - "key_select", "kslt", - "key_send", "kEND", - "key_seol", "kEOL", - "key_sexit", "kEXT", - "key_sfind", "kFND", - "key_shelp", "kHLP", - "key_shome", "kHOM", - "key_sic", "kIC", - "key_sleft", "kLFT", - "key_smessage", "kMSG", - "key_smove", "kMOV", - "key_snext", "kNXT", - "key_soptions", "kOPT", - "key_sprevious", "kPRV", - "key_sprint", "kPRT", - "key_sredo", "kRDO", - "key_sreplace", "kRPL", - "key_sright", "kRIT", - "key_srsume", "kRES", - "key_ssave", "kSAV", - "key_ssuspend", "kSPD", - "key_sundo", "kUND", - "req_for_input", "rfi", - "key_f11", "kf11", - "key_f12", "kf12", - "key_f13", "kf13", - "key_f14", "kf14", - "key_f15", "kf15", - "key_f16", "kf16", - "key_f17", "kf17", - "key_f18", "kf18", - "key_f19", "kf19", - "key_f20", "kf20", - "key_f21", "kf21", - "key_f22", "kf22", - "key_f23", "kf23", - "key_f24", "kf24", - "key_f25", "kf25", - "key_f26", "kf26", - "key_f27", "kf27", - "key_f28", "kf28", - "key_f29", "kf29", - "key_f30", "kf30", - "key_f31", "kf31", - "key_f32", "kf32", - "key_f33", "kf33", - "key_f34", "kf34", - "key_f35", "kf35", - "key_f36", "kf36", - "key_f37", "kf37", - "key_f38", "kf38", - "key_f39", "kf39", - "key_f40", "kf40", - "key_f41", "kf41", - "key_f42", "kf42", - "key_f43", "kf43", - "key_f44", "kf44", - "key_f45", "kf45", - "key_f46", "kf46", - "key_f47", "kf47", - "key_f48", "kf48", - "key_f49", "kf49", - "key_f50", "kf50", - "key_f51", "kf51", - "key_f52", "kf52", - "key_f53", "kf53", - "key_f54", "kf54", - "key_f55", "kf55", - "key_f56", "kf56", - "key_f57", "kf57", - "key_f58", "kf58", - "key_f59", "kf59", - "key_f60", "kf60", - "key_f61", "kf61", - "key_f62", "kf62", - "key_f63", "kf63", - "clr_bol", "el1", - "clear_margins", "mgc", - "set_left_margin", "smgl", - "set_right_margin", "smgr", - "label_format", "fln", - "set_clock", "sclk", - "display_clock", "dclk", - "remove_clock", "rmclk", - "create_window", "cwin", - "goto_window", "wingo", - "hangup", "hup", - "dial_phone", "dial", - "quick_dial", "qdial", - "tone", "tone", - "pulse", "pulse", - "flash_hook", "hook", - "fixed_pause", "pause", - "wait_tone", "wait", - "user0", "u0", - "user1", "u1", - "user2", "u2", - "user3", "u3", - "user4", "u4", - "user5", "u5", - "user6", "u6", - "user7", "u7", - "user8", "u8", - "user9", "u9", - "orig_pair", "op", - "orig_colors", "oc", - "initialize_color", "initc", - "initialize_pair", "initp", - "set_color_pair", "scp", - "set_foreground", "setf", - "set_background", "setb", - "change_char_pitch", "cpi", - "change_line_pitch", "lpi", - "change_res_horz", "chr", - "change_res_vert", "cvr", - "define_char", "defc", - "enter_doublewide_mode", "swidm", - "enter_draft_quality", "sdrfq", - "enter_italics_mode", "sitm", - "enter_leftward_mode", "slm", - "enter_micro_mode", "smicm", - "enter_near_letter_quality", "snlq", - "enter_normal_quality", "snrmq", - "enter_shadow_mode", "sshm", - "enter_subscript_mode", "ssubm", - "enter_superscript_mode", "ssupm", - "enter_upward_mode", "sum", - "exit_doublewide_mode", "rwidm", - "exit_italics_mode", "ritm", - "exit_leftward_mode", "rlm", - "exit_micro_mode", "rmicm", - "exit_shadow_mode", "rshm", - "exit_subscript_mode", "rsubm", - "exit_superscript_mode", "rsupm", - "exit_upward_mode", "rum", - "micro_column_address", "mhpa", - "micro_down", "mcud1", - "micro_left", "mcub1", - "micro_right", "mcuf1", - "micro_row_address", "mvpa", - "micro_up", "mcuu1", - "order_of_pins", "porder", - "parm_down_micro", "mcud", - "parm_left_micro", "mcub", - "parm_right_micro", "mcuf", - "parm_up_micro", "mcuu", - "select_char_set", "scs", - "set_bottom_margin", "smgb", - "set_bottom_margin_parm", "smgbp", - "set_left_margin_parm", "smglp", - "set_right_margin_parm", "smgrp", - "set_top_margin", "smgt", - "set_top_margin_parm", "smgtp", - "start_bit_image", "sbim", - "start_char_set_def", "scsd", - "stop_bit_image", "rbim", - "stop_char_set_def", "rcsd", - "subscript_characters", "subcs", - "superscript_characters", "supcs", - "these_cause_cr", "docr", - "zero_motion", "zerom", - "char_set_names", "csnm", - "key_mouse", "kmous", - "mouse_info", "minfo", - "req_mouse_pos", "reqmp", - "get_mouse", "getm", - "set_a_foreground", "setaf", - "set_a_background", "setab", - "pkey_plab", "pfxl", - "device_type", "devt", - "code_set_init", "csin", - "set0_des_seq", "s0ds", - "set1_des_seq", "s1ds", - "set2_des_seq", "s2ds", - "set3_des_seq", "s3ds", - "set_lr_margin", "smglr", - "set_tb_margin", "smgtb", - "bit_image_repeat", "birep", - "bit_image_newline", "binel", - "bit_image_carriage_return", "bicr", - "color_names", "colornm", - "define_bit_image_region", "defbi", - "end_bit_image_region", "endbi", - "set_color_band", "setcolor", - "set_page_length", "slines", - "display_pc_char", "dispc", - "enter_pc_charset_mode", "smpch", - "exit_pc_charset_mode", "rmpch", - "enter_scancode_mode", "smsc", - "exit_scancode_mode", "rmsc", - "pc_term_options", "pctrm", - "scancode_escape", "scesc", - "alt_scancode_esc", "scesa", - "enter_horizontal_hl_mode", "ehhlm", - "enter_left_hl_mode", "elhlm", - "enter_low_hl_mode", "elohlm", - "enter_right_hl_mode", "erhlm", - "enter_top_hl_mode", "ethlm", - "enter_vertical_hl_mode", "evhlm", - "set_a_attributes", "sgr1", - "set_pglen_inch", "slength", - "termcap_init2", "OTi2", - "termcap_reset", "OTrs", - "linefeed_if_not_lf", "OTnl", - "backspace_if_not_bs", "OTbc", - "other_non_function_keys", "OTko", - "arrow_key_map", "OTma", - "acs_ulcorner", "OTG2", - "acs_llcorner", "OTG3", - "acs_urcorner", "OTG1", - "acs_lrcorner", "OTG4", - "acs_ltee", "OTGR", - "acs_rtee", "OTGL", - "acs_btee", "OTGU", - "acs_ttee", "OTGD", - "acs_hline", "OTGH", - "acs_vline", "OTGV", - "acs_plus", "OTGC", - "memory_lock", "meml", - "memory_unlock", "memu", - "box_chars_1", "box1", -} diff --git a/vendor/github.com/xo/terminfo/color.go b/vendor/github.com/xo/terminfo/color.go deleted file mode 100644 index 76c439fc9..000000000 --- a/vendor/github.com/xo/terminfo/color.go +++ /dev/null @@ -1,88 +0,0 @@ -package terminfo - -import ( - "os" - "strconv" - "strings" -) - -// ColorLevel is the color level supported by a terminal. -type ColorLevel uint - -// ColorLevel values. -const ( - ColorLevelNone ColorLevel = iota - ColorLevelBasic - ColorLevelHundreds - ColorLevelMillions -) - -// String satisfies the Stringer interface. -func (c ColorLevel) String() string { - switch c { - case ColorLevelBasic: - return "basic" - case ColorLevelHundreds: - return "hundreds" - case ColorLevelMillions: - return "millions" - } - return "none" -} - -// ChromaFormatterName returns the github.com/alecthomas/chroma compatible -// formatter name for the color level. -func (c ColorLevel) ChromaFormatterName() string { - switch c { - case ColorLevelBasic: - return "terminal" - case ColorLevelHundreds: - return "terminal256" - case ColorLevelMillions: - return "terminal16m" - } - return "noop" -} - -// ColorLevelFromEnv returns the color level COLORTERM, FORCE_COLOR, -// TERM_PROGRAM, or determined from the TERM environment variable. -func ColorLevelFromEnv() (ColorLevel, error) { - // check for overriding environment variables - colorTerm, termProg, forceColor := os.Getenv("COLORTERM"), os.Getenv("TERM_PROGRAM"), os.Getenv("FORCE_COLOR") - switch { - case strings.Contains(colorTerm, "truecolor") || strings.Contains(colorTerm, "24bit") || termProg == "Hyper": - return ColorLevelMillions, nil - case colorTerm != "" || forceColor != "": - return ColorLevelBasic, nil - case termProg == "Apple_Terminal": - return ColorLevelHundreds, nil - case termProg == "iTerm.app": - ver := os.Getenv("TERM_PROGRAM_VERSION") - if ver == "" { - return ColorLevelHundreds, nil - } - i, err := strconv.Atoi(strings.Split(ver, ".")[0]) - if err != nil { - return ColorLevelNone, ErrInvalidTermProgramVersion - } - if i == 3 { - return ColorLevelMillions, nil - } - return ColorLevelHundreds, nil - } - // otherwise determine from TERM's max_colors capability - if term := os.Getenv("TERM"); term != "" { - ti, err := Load(term) - if err != nil { - return ColorLevelNone, err - } - v, ok := ti.Nums[MaxColors] - switch { - case !ok || v <= 16: - return ColorLevelNone, nil - case ok && v >= 256: - return ColorLevelHundreds, nil - } - } - return ColorLevelBasic, nil -} diff --git a/vendor/github.com/xo/terminfo/dec.go b/vendor/github.com/xo/terminfo/dec.go deleted file mode 100644 index dacc88e38..000000000 --- a/vendor/github.com/xo/terminfo/dec.go +++ /dev/null @@ -1,245 +0,0 @@ -package terminfo - -import ( - "sort" -) - -const ( - // maxFileLength is the max file length. - maxFileLength = 4096 - // magic is the file magic for terminfo files. - magic = 0o432 - // magicExtended is the file magic for terminfo files with the extended - // number format. - magicExtended = 0o1036 -) - -// header fields. -const ( - fieldMagic = iota - fieldNameSize - fieldBoolCount - fieldNumCount - fieldStringCount - fieldTableSize -) - -// header extended fields. -const ( - fieldExtBoolCount = iota - fieldExtNumCount - fieldExtStringCount - fieldExtOffsetCount - fieldExtTableSize -) - -// hasInvalidCaps determines if the capabilities in h are invalid. -func hasInvalidCaps(h []int) bool { - return h[fieldBoolCount] > CapCountBool || - h[fieldNumCount] > CapCountNum || - h[fieldStringCount] > CapCountString -} - -// capLength returns the total length of the capabilities in bytes. -func capLength(h []int) int { - return h[fieldNameSize] + - h[fieldBoolCount] + - (h[fieldNameSize]+h[fieldBoolCount])%2 + // account for word align - h[fieldNumCount]*2 + - h[fieldStringCount]*2 + - h[fieldTableSize] -} - -// hasInvalidExtOffset determines if the extended offset field is valid. -func hasInvalidExtOffset(h []int) bool { - return h[fieldExtBoolCount]+ - h[fieldExtNumCount]+ - h[fieldExtStringCount]*2 != h[fieldExtOffsetCount] -} - -// extCapLength returns the total length of extended capabilities in bytes. -func extCapLength(h []int, numWidth int) int { - return h[fieldExtBoolCount] + - h[fieldExtBoolCount]%2 + // account for word align - h[fieldExtNumCount]*(numWidth/8) + - h[fieldExtOffsetCount]*2 + - h[fieldExtTableSize] -} - -// findNull finds the position of null in buf. -func findNull(buf []byte, i int) int { - for ; i < len(buf); i++ { - if buf[i] == 0 { - return i - } - } - return -1 -} - -// readStrings decodes n strings from string data table buf using the indexes in idx. -func readStrings(idx []int, buf []byte, n int) (map[int][]byte, int, error) { - var last int - m := make(map[int][]byte) - for i := 0; i < n; i++ { - start := idx[i] - if start < 0 { - continue - } - if end := findNull(buf, start); end != -1 { - m[i], last = buf[start:end], end+1 - } else { - return nil, 0, ErrInvalidStringTable - } - } - return m, last, nil -} - -// decoder holds state info while decoding a terminfo file. -type decoder struct { - buf []byte - pos int - n int -} - -// readBytes reads the next n bytes of buf, incrementing pos by n. -func (d *decoder) readBytes(n int) ([]byte, error) { - if d.n < d.pos+n { - return nil, ErrUnexpectedFileEnd - } - n, d.pos = d.pos, d.pos+n - return d.buf[n:d.pos], nil -} - -// readInts reads n number of ints with width w. -func (d *decoder) readInts(n, w int) ([]int, error) { - w /= 8 - l := n * w - buf, err := d.readBytes(l) - if err != nil { - return nil, err - } - // align - d.pos += d.pos % 2 - z := make([]int, n) - for i, j := 0, 0; i < l; i, j = i+w, j+1 { - switch w { - case 1: - z[i] = int(buf[i]) - case 2: - z[j] = int(int16(buf[i+1])<<8 | int16(buf[i])) - case 4: - z[j] = int(buf[i+3])<<24 | int(buf[i+2])<<16 | int(buf[i+1])<<8 | int(buf[i]) - } - } - return z, nil -} - -// readBools reads the next n bools. -func (d *decoder) readBools(n int) (map[int]bool, map[int]bool, error) { - buf, err := d.readInts(n, 8) - if err != nil { - return nil, nil, err - } - // process - bools, boolsM := make(map[int]bool), make(map[int]bool) - for i, b := range buf { - bools[i] = b == 1 - if int8(b) == -2 { - boolsM[i] = true - } - } - return bools, boolsM, nil -} - -// readNums reads the next n nums. -func (d *decoder) readNums(n, w int) (map[int]int, map[int]bool, error) { - buf, err := d.readInts(n, w) - if err != nil { - return nil, nil, err - } - // process - nums, numsM := make(map[int]int), make(map[int]bool) - for i := 0; i < n; i++ { - nums[i] = buf[i] - if buf[i] == -2 { - numsM[i] = true - } - } - return nums, numsM, nil -} - -// readStringTable reads the string data for n strings and the accompanying data -// table of length sz. -func (d *decoder) readStringTable(n, sz int) ([][]byte, []int, error) { - buf, err := d.readInts(n, 16) - if err != nil { - return nil, nil, err - } - // read string data table - data, err := d.readBytes(sz) - if err != nil { - return nil, nil, err - } - // align - d.pos += d.pos % 2 - // process - s := make([][]byte, n) - var m []int - for i := 0; i < n; i++ { - start := buf[i] - if start == -2 { - m = append(m, i) - } else if start >= 0 { - if end := findNull(data, start); end != -1 { - s[i] = data[start:end] - } else { - return nil, nil, ErrInvalidStringTable - } - } - } - return s, m, nil -} - -// readStrings reads the next n strings and processes the string data table of -// length sz. -func (d *decoder) readStrings(n, sz int) (map[int][]byte, map[int]bool, error) { - s, m, err := d.readStringTable(n, sz) - if err != nil { - return nil, nil, err - } - strs := make(map[int][]byte) - for k, v := range s { - if k == AcsChars { - v = canonicalizeAscChars(v) - } - strs[k] = v - } - strsM := make(map[int]bool, len(m)) - for _, k := range m { - strsM[k] = true - } - return strs, strsM, nil -} - -// canonicalizeAscChars reorders chars to be unique, in order. -// -// see repair_ascc in ncurses-6.3/progs/dump_entry.c -func canonicalizeAscChars(z []byte) []byte { - var c []byte - enc := make(map[byte]byte, len(z)/2) - for i := 0; i < len(z); i += 2 { - if _, ok := enc[z[i]]; !ok { - a, b := z[i], z[i+1] - // log.Printf(">>> a: %d %c, b: %d %c", a, a, b, b) - c, enc[a] = append(c, b), b - } - } - sort.Slice(c, func(i, j int) bool { - return c[i] < c[j] - }) - r := make([]byte, 2*len(c)) - for i := 0; i < len(c); i++ { - r[i*2], r[i*2+1] = c[i], enc[c[i]] - } - return r -} diff --git a/vendor/github.com/xo/terminfo/load.go b/vendor/github.com/xo/terminfo/load.go deleted file mode 100644 index d7cd266cb..000000000 --- a/vendor/github.com/xo/terminfo/load.go +++ /dev/null @@ -1,64 +0,0 @@ -package terminfo - -import ( - "os" - "os/user" - "path" - "strings" - "sync" -) - -// termCache is the terminfo cache. -var termCache = struct { - db map[string]*Terminfo - sync.RWMutex -}{ - db: make(map[string]*Terminfo), -} - -// Load follows the behavior described in terminfo(5) to find correct the -// terminfo file using the name, reads the file and then returns a Terminfo -// struct that describes the file. -func Load(name string) (*Terminfo, error) { - if name == "" { - return nil, ErrEmptyTermName - } - termCache.RLock() - ti, ok := termCache.db[name] - termCache.RUnlock() - if ok { - return ti, nil - } - var checkDirs []string - // check $TERMINFO - if dir := os.Getenv("TERMINFO"); dir != "" { - checkDirs = append(checkDirs, dir) - } - // check $HOME/.terminfo - u, err := user.Current() - if err != nil { - return nil, err - } - checkDirs = append(checkDirs, path.Join(u.HomeDir, ".terminfo")) - // check $TERMINFO_DIRS - if dirs := os.Getenv("TERMINFO_DIRS"); dirs != "" { - checkDirs = append(checkDirs, strings.Split(dirs, ":")...) - } - // check fallback directories - checkDirs = append(checkDirs, "/etc/terminfo", "/lib/terminfo", "/usr/share/terminfo") - for _, dir := range checkDirs { - ti, err = Open(dir, name) - if err != nil && err != ErrFileNotFound && !os.IsNotExist(err) { - return nil, err - } else if err == nil { - return ti, nil - } - } - return nil, ErrDatabaseDirectoryNotFound -} - -// LoadFromEnv loads the terminal info based on the name contained in -// environment variable TERM. -func LoadFromEnv() (*Terminfo, error) { - return Load(os.Getenv("TERM")) -} diff --git a/vendor/github.com/xo/terminfo/param.go b/vendor/github.com/xo/terminfo/param.go deleted file mode 100644 index ed4cb86b6..000000000 --- a/vendor/github.com/xo/terminfo/param.go +++ /dev/null @@ -1,405 +0,0 @@ -package terminfo - -import ( - "bytes" - "fmt" - "io" - "strconv" - "strings" - "sync" -) - -// parametizer represents the a scan state for a parameterized string. -type parametizer struct { - // z is the string to parameterize - z []byte - // pos is the current position in s. - pos int - // nest is the current nest level. - nest int - // s is the variable stack. - s stack - // skipElse keeps the state of skipping else. - skipElse bool - // buf is the result buffer. - buf *bytes.Buffer - // params are the parameters to interpolate. - params [9]interface{} - // vars are dynamic variables. - vars [26]interface{} -} - -// staticVars are the static, global variables. -var staticVars = struct { - vars [26]interface{} - sync.Mutex -}{} - -var parametizerPool = sync.Pool{ - New: func() interface{} { - p := new(parametizer) - p.buf = bytes.NewBuffer(make([]byte, 0, 45)) - return p - }, -} - -// newParametizer returns a new initialized parametizer from the pool. -func newParametizer(z []byte) *parametizer { - p := parametizerPool.Get().(*parametizer) - p.z = z - return p -} - -// reset resets the parametizer. -func (p *parametizer) reset() { - p.pos, p.nest = 0, 0 - p.s.reset() - p.buf.Reset() - p.params, p.vars = [9]interface{}{}, [26]interface{}{} - parametizerPool.Put(p) -} - -// stateFn represents the state of the scanner as a function that returns the -// next state. -type stateFn func() stateFn - -// exec executes the parameterizer, interpolating the supplied parameters. -func (p *parametizer) exec() string { - for state := p.scanTextFn; state != nil; { - state = state() - } - return p.buf.String() -} - -// peek returns the next byte. -func (p *parametizer) peek() (byte, error) { - if p.pos >= len(p.z) { - return 0, io.EOF - } - return p.z[p.pos], nil -} - -// writeFrom writes the characters from ppos to pos to the buffer. -func (p *parametizer) writeFrom(ppos int) { - if p.pos > ppos { - // append remaining characters. - p.buf.Write(p.z[ppos:p.pos]) - } -} - -func (p *parametizer) scanTextFn() stateFn { - ppos := p.pos - for { - ch, err := p.peek() - if err != nil { - p.writeFrom(ppos) - return nil - } - if ch == '%' { - p.writeFrom(ppos) - p.pos++ - return p.scanCodeFn - } - p.pos++ - } -} - -func (p *parametizer) scanCodeFn() stateFn { - ch, err := p.peek() - if err != nil { - return nil - } - switch ch { - case '%': - p.buf.WriteByte('%') - case ':': - // this character is used to avoid interpreting "%-" and "%+" as operators. - // the next character is where the format really begins. - p.pos++ - _, err = p.peek() - if err != nil { - return nil - } - return p.scanFormatFn - case '#', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.': - return p.scanFormatFn - case 'o': - p.buf.WriteString(strconv.FormatInt(int64(p.s.popInt()), 8)) - case 'd': - p.buf.WriteString(strconv.Itoa(p.s.popInt())) - case 'x': - p.buf.WriteString(strconv.FormatInt(int64(p.s.popInt()), 16)) - case 'X': - p.buf.WriteString(strings.ToUpper(strconv.FormatInt(int64(p.s.popInt()), 16))) - case 's': - p.buf.WriteString(p.s.popString()) - case 'c': - p.buf.WriteByte(p.s.popByte()) - case 'p': - p.pos++ - return p.pushParamFn - case 'P': - p.pos++ - return p.setDsVarFn - case 'g': - p.pos++ - return p.getDsVarFn - case '\'': - p.pos++ - ch, err = p.peek() - if err != nil { - return nil - } - p.s.push(ch) - // skip the '\'' - p.pos++ - case '{': - p.pos++ - return p.pushIntfn - case 'l': - p.s.push(len(p.s.popString())) - case '+': - bi, ai := p.s.popInt(), p.s.popInt() - p.s.push(ai + bi) - case '-': - bi, ai := p.s.popInt(), p.s.popInt() - p.s.push(ai - bi) - case '*': - bi, ai := p.s.popInt(), p.s.popInt() - p.s.push(ai * bi) - case '/': - bi, ai := p.s.popInt(), p.s.popInt() - if bi != 0 { - p.s.push(ai / bi) - } else { - p.s.push(0) - } - case 'm': - bi, ai := p.s.popInt(), p.s.popInt() - if bi != 0 { - p.s.push(ai % bi) - } else { - p.s.push(0) - } - case '&': - bi, ai := p.s.popInt(), p.s.popInt() - p.s.push(ai & bi) - case '|': - bi, ai := p.s.popInt(), p.s.popInt() - p.s.push(ai | bi) - case '^': - bi, ai := p.s.popInt(), p.s.popInt() - p.s.push(ai ^ bi) - case '=': - bi, ai := p.s.popInt(), p.s.popInt() - p.s.push(ai == bi) - case '>': - bi, ai := p.s.popInt(), p.s.popInt() - p.s.push(ai > bi) - case '<': - bi, ai := p.s.popInt(), p.s.popInt() - p.s.push(ai < bi) - case 'A': - bi, ai := p.s.popBool(), p.s.popBool() - p.s.push(ai && bi) - case 'O': - bi, ai := p.s.popBool(), p.s.popBool() - p.s.push(ai || bi) - case '!': - p.s.push(!p.s.popBool()) - case '~': - p.s.push(^p.s.popInt()) - case 'i': - for i := range p.params[:2] { - if n, ok := p.params[i].(int); ok { - p.params[i] = n + 1 - } - } - case '?', ';': - case 't': - return p.scanThenFn - case 'e': - p.skipElse = true - return p.skipTextFn - } - p.pos++ - return p.scanTextFn -} - -func (p *parametizer) scanFormatFn() stateFn { - // the character was already read, so no need to check the error. - ch, _ := p.peek() - // 6 should be the maximum length of a format string, for example "%:-9.9d". - f := []byte{'%', ch, 0, 0, 0, 0} - var err error - for { - p.pos++ - ch, err = p.peek() - if err != nil { - return nil - } - f = append(f, ch) - switch ch { - case 'o', 'd', 'x', 'X': - fmt.Fprintf(p.buf, string(f), p.s.popInt()) - break - case 's': - fmt.Fprintf(p.buf, string(f), p.s.popString()) - break - case 'c': - fmt.Fprintf(p.buf, string(f), p.s.popByte()) - break - } - } - p.pos++ - return p.scanTextFn -} - -func (p *parametizer) pushParamFn() stateFn { - ch, err := p.peek() - if err != nil { - return nil - } - if ai := int(ch - '1'); ai >= 0 && ai < len(p.params) { - p.s.push(p.params[ai]) - } else { - p.s.push(0) - } - // skip the '}' - p.pos++ - return p.scanTextFn -} - -func (p *parametizer) setDsVarFn() stateFn { - ch, err := p.peek() - if err != nil { - return nil - } - if ch >= 'A' && ch <= 'Z' { - staticVars.Lock() - staticVars.vars[int(ch-'A')] = p.s.pop() - staticVars.Unlock() - } else if ch >= 'a' && ch <= 'z' { - p.vars[int(ch-'a')] = p.s.pop() - } - p.pos++ - return p.scanTextFn -} - -func (p *parametizer) getDsVarFn() stateFn { - ch, err := p.peek() - if err != nil { - return nil - } - var a byte - if ch >= 'A' && ch <= 'Z' { - a = 'A' - } else if ch >= 'a' && ch <= 'z' { - a = 'a' - } - staticVars.Lock() - p.s.push(staticVars.vars[int(ch-a)]) - staticVars.Unlock() - p.pos++ - return p.scanTextFn -} - -func (p *parametizer) pushIntfn() stateFn { - var ai int - for { - ch, err := p.peek() - if err != nil { - return nil - } - p.pos++ - if ch < '0' || ch > '9' { - p.s.push(ai) - return p.scanTextFn - } - ai = (ai * 10) + int(ch-'0') - } -} - -func (p *parametizer) scanThenFn() stateFn { - p.pos++ - if p.s.popBool() { - return p.scanTextFn - } - p.skipElse = false - return p.skipTextFn -} - -func (p *parametizer) skipTextFn() stateFn { - for { - ch, err := p.peek() - if err != nil { - return nil - } - p.pos++ - if ch == '%' { - break - } - } - if p.skipElse { - return p.skipElseFn - } - return p.skipThenFn -} - -func (p *parametizer) skipThenFn() stateFn { - ch, err := p.peek() - if err != nil { - return nil - } - p.pos++ - switch ch { - case ';': - if p.nest == 0 { - return p.scanTextFn - } - p.nest-- - case '?': - p.nest++ - case 'e': - if p.nest == 0 { - return p.scanTextFn - } - } - return p.skipTextFn -} - -func (p *parametizer) skipElseFn() stateFn { - ch, err := p.peek() - if err != nil { - return nil - } - p.pos++ - switch ch { - case ';': - if p.nest == 0 { - return p.scanTextFn - } - p.nest-- - case '?': - p.nest++ - } - return p.skipTextFn -} - -// Printf evaluates a parameterized terminfo value z, interpolating params. -func Printf(z []byte, params ...interface{}) string { - p := newParametizer(z) - defer p.reset() - // make sure we always have 9 parameters -- makes it easier - // later to skip checks and its faster - for i := 0; i < len(p.params) && i < len(params); i++ { - p.params[i] = params[i] - } - return p.exec() -} - -// Fprintf evaluates a parameterized terminfo value z, interpolating params and -// writing to w. -func Fprintf(w io.Writer, z []byte, params ...interface{}) { - w.Write([]byte(Printf(z, params...))) -} diff --git a/vendor/github.com/xo/terminfo/stack.go b/vendor/github.com/xo/terminfo/stack.go deleted file mode 100644 index a6de39503..000000000 --- a/vendor/github.com/xo/terminfo/stack.go +++ /dev/null @@ -1,48 +0,0 @@ -package terminfo - -type stack []interface{} - -func (s *stack) push(v interface{}) { - *s = append(*s, v) -} - -func (s *stack) pop() interface{} { - if len(*s) == 0 { - return nil - } - v := (*s)[len(*s)-1] - *s = (*s)[:len(*s)-1] - return v -} - -func (s *stack) popInt() int { - if i, ok := s.pop().(int); ok { - return i - } - return 0 -} - -func (s *stack) popBool() bool { - if b, ok := s.pop().(bool); ok { - return b - } - return false -} - -func (s *stack) popByte() byte { - if b, ok := s.pop().(byte); ok { - return b - } - return 0 -} - -func (s *stack) popString() string { - if a, ok := s.pop().(string); ok { - return a - } - return "" -} - -func (s *stack) reset() { - *s = (*s)[:0] -} diff --git a/vendor/github.com/xo/terminfo/terminfo.go b/vendor/github.com/xo/terminfo/terminfo.go deleted file mode 100644 index 69e3b6064..000000000 --- a/vendor/github.com/xo/terminfo/terminfo.go +++ /dev/null @@ -1,479 +0,0 @@ -// Package terminfo implements reading terminfo files in pure go. -package terminfo - -//go:generate go run gen.go - -import ( - "io" - "io/ioutil" - "path" - "strconv" - "strings" -) - -// Error is a terminfo error. -type Error string - -// Error satisfies the error interface. -func (err Error) Error() string { - return string(err) -} - -const ( - // ErrInvalidFileSize is the invalid file size error. - ErrInvalidFileSize Error = "invalid file size" - // ErrUnexpectedFileEnd is the unexpected file end error. - ErrUnexpectedFileEnd Error = "unexpected file end" - // ErrInvalidStringTable is the invalid string table error. - ErrInvalidStringTable Error = "invalid string table" - // ErrInvalidMagic is the invalid magic error. - ErrInvalidMagic Error = "invalid magic" - // ErrInvalidHeader is the invalid header error. - ErrInvalidHeader Error = "invalid header" - // ErrInvalidNames is the invalid names error. - ErrInvalidNames Error = "invalid names" - // ErrInvalidExtendedHeader is the invalid extended header error. - ErrInvalidExtendedHeader Error = "invalid extended header" - // ErrEmptyTermName is the empty term name error. - ErrEmptyTermName Error = "empty term name" - // ErrDatabaseDirectoryNotFound is the database directory not found error. - ErrDatabaseDirectoryNotFound Error = "database directory not found" - // ErrFileNotFound is the file not found error. - ErrFileNotFound Error = "file not found" - // ErrInvalidTermProgramVersion is the invalid TERM_PROGRAM_VERSION error. - ErrInvalidTermProgramVersion Error = "invalid TERM_PROGRAM_VERSION" -) - -// Terminfo describes a terminal's capabilities. -type Terminfo struct { - // File is the original source file. - File string - // Names are the provided cap names. - Names []string - // Bools are the bool capabilities. - Bools map[int]bool - // BoolsM are the missing bool capabilities. - BoolsM map[int]bool - // Nums are the num capabilities. - Nums map[int]int - // NumsM are the missing num capabilities. - NumsM map[int]bool - // Strings are the string capabilities. - Strings map[int][]byte - // StringsM are the missing string capabilities. - StringsM map[int]bool - // ExtBools are the extended bool capabilities. - ExtBools map[int]bool - // ExtBoolsNames is the map of extended bool capabilities to their index. - ExtBoolNames map[int][]byte - // ExtNums are the extended num capabilities. - ExtNums map[int]int - // ExtNumsNames is the map of extended num capabilities to their index. - ExtNumNames map[int][]byte - // ExtStrings are the extended string capabilities. - ExtStrings map[int][]byte - // ExtStringsNames is the map of extended string capabilities to their index. - ExtStringNames map[int][]byte -} - -// Decode decodes the terminfo data contained in buf. -func Decode(buf []byte) (*Terminfo, error) { - var err error - // check max file length - if len(buf) >= maxFileLength { - return nil, ErrInvalidFileSize - } - d := &decoder{ - buf: buf, - n: len(buf), - } - // read header - h, err := d.readInts(6, 16) - if err != nil { - return nil, err - } - var numWidth int - // check magic - switch { - case h[fieldMagic] == magic: - numWidth = 16 - case h[fieldMagic] == magicExtended: - numWidth = 32 - default: - return nil, ErrInvalidMagic - } - // check header - if hasInvalidCaps(h) { - return nil, ErrInvalidHeader - } - // check remaining length - if d.n-d.pos < capLength(h) { - return nil, ErrUnexpectedFileEnd - } - // read names - names, err := d.readBytes(h[fieldNameSize]) - if err != nil { - return nil, err - } - // check name is terminated properly - i := findNull(names, 0) - if i == -1 { - return nil, ErrInvalidNames - } - names = names[:i] - // read bool caps - bools, boolsM, err := d.readBools(h[fieldBoolCount]) - if err != nil { - return nil, err - } - // read num caps - nums, numsM, err := d.readNums(h[fieldNumCount], numWidth) - if err != nil { - return nil, err - } - // read string caps - strs, strsM, err := d.readStrings(h[fieldStringCount], h[fieldTableSize]) - if err != nil { - return nil, err - } - ti := &Terminfo{ - Names: strings.Split(string(names), "|"), - Bools: bools, - BoolsM: boolsM, - Nums: nums, - NumsM: numsM, - Strings: strs, - StringsM: strsM, - } - // at the end of file, so no extended caps - if d.pos >= d.n { - return ti, nil - } - // decode extended header - eh, err := d.readInts(5, 16) - if err != nil { - return nil, err - } - // check extended offset field - if hasInvalidExtOffset(eh) { - return nil, ErrInvalidExtendedHeader - } - // check extended cap lengths - if d.n-d.pos != extCapLength(eh, numWidth) { - return nil, ErrInvalidExtendedHeader - } - // read extended bool caps - ti.ExtBools, _, err = d.readBools(eh[fieldExtBoolCount]) - if err != nil { - return nil, err - } - // read extended num caps - ti.ExtNums, _, err = d.readNums(eh[fieldExtNumCount], numWidth) - if err != nil { - return nil, err - } - // read extended string data table indexes - extIndexes, err := d.readInts(eh[fieldExtOffsetCount], 16) - if err != nil { - return nil, err - } - // read string data table - extData, err := d.readBytes(eh[fieldExtTableSize]) - if err != nil { - return nil, err - } - // precautionary check that exactly at end of file - if d.pos != d.n { - return nil, ErrUnexpectedFileEnd - } - var last int - // read extended string caps - ti.ExtStrings, last, err = readStrings(extIndexes, extData, eh[fieldExtStringCount]) - if err != nil { - return nil, err - } - extIndexes, extData = extIndexes[eh[fieldExtStringCount]:], extData[last:] - // read extended bool names - ti.ExtBoolNames, _, err = readStrings(extIndexes, extData, eh[fieldExtBoolCount]) - if err != nil { - return nil, err - } - extIndexes = extIndexes[eh[fieldExtBoolCount]:] - // read extended num names - ti.ExtNumNames, _, err = readStrings(extIndexes, extData, eh[fieldExtNumCount]) - if err != nil { - return nil, err - } - extIndexes = extIndexes[eh[fieldExtNumCount]:] - // read extended string names - ti.ExtStringNames, _, err = readStrings(extIndexes, extData, eh[fieldExtStringCount]) - if err != nil { - return nil, err - } - // extIndexes = extIndexes[eh[fieldExtStringCount]:] - return ti, nil -} - -// Open reads the terminfo file name from the specified directory dir. -func Open(dir, name string) (*Terminfo, error) { - var err error - var buf []byte - var filename string - for _, f := range []string{ - path.Join(dir, name[0:1], name), - path.Join(dir, strconv.FormatUint(uint64(name[0]), 16), name), - } { - buf, err = ioutil.ReadFile(f) - if err == nil { - filename = f - break - } - } - if buf == nil { - return nil, ErrFileNotFound - } - // decode - ti, err := Decode(buf) - if err != nil { - return nil, err - } - // save original file name - ti.File = filename - // add to cache - termCache.Lock() - for _, n := range ti.Names { - termCache.db[n] = ti - } - termCache.Unlock() - return ti, nil -} - -// boolCaps returns all bool and extended capabilities using f to format the -// index key. -func (ti *Terminfo) boolCaps(f func(int) string, extended bool) map[string]bool { - m := make(map[string]bool, len(ti.Bools)+len(ti.ExtBools)) - if !extended { - for k, v := range ti.Bools { - m[f(k)] = v - } - } else { - for k, v := range ti.ExtBools { - m[string(ti.ExtBoolNames[k])] = v - } - } - return m -} - -// BoolCaps returns all bool capabilities. -func (ti *Terminfo) BoolCaps() map[string]bool { - return ti.boolCaps(BoolCapName, false) -} - -// BoolCapsShort returns all bool capabilities, using the short name as the -// index. -func (ti *Terminfo) BoolCapsShort() map[string]bool { - return ti.boolCaps(BoolCapNameShort, false) -} - -// ExtBoolCaps returns all extended bool capabilities. -func (ti *Terminfo) ExtBoolCaps() map[string]bool { - return ti.boolCaps(BoolCapName, true) -} - -// ExtBoolCapsShort returns all extended bool capabilities, using the short -// name as the index. -func (ti *Terminfo) ExtBoolCapsShort() map[string]bool { - return ti.boolCaps(BoolCapNameShort, true) -} - -// numCaps returns all num and extended capabilities using f to format the -// index key. -func (ti *Terminfo) numCaps(f func(int) string, extended bool) map[string]int { - m := make(map[string]int, len(ti.Nums)+len(ti.ExtNums)) - if !extended { - for k, v := range ti.Nums { - m[f(k)] = v - } - } else { - for k, v := range ti.ExtNums { - m[string(ti.ExtNumNames[k])] = v - } - } - return m -} - -// NumCaps returns all num capabilities. -func (ti *Terminfo) NumCaps() map[string]int { - return ti.numCaps(NumCapName, false) -} - -// NumCapsShort returns all num capabilities, using the short name as the -// index. -func (ti *Terminfo) NumCapsShort() map[string]int { - return ti.numCaps(NumCapNameShort, false) -} - -// ExtNumCaps returns all extended num capabilities. -func (ti *Terminfo) ExtNumCaps() map[string]int { - return ti.numCaps(NumCapName, true) -} - -// ExtNumCapsShort returns all extended num capabilities, using the short -// name as the index. -func (ti *Terminfo) ExtNumCapsShort() map[string]int { - return ti.numCaps(NumCapNameShort, true) -} - -// stringCaps returns all string and extended capabilities using f to format the -// index key. -func (ti *Terminfo) stringCaps(f func(int) string, extended bool) map[string][]byte { - m := make(map[string][]byte, len(ti.Strings)+len(ti.ExtStrings)) - if !extended { - for k, v := range ti.Strings { - m[f(k)] = v - } - } else { - for k, v := range ti.ExtStrings { - m[string(ti.ExtStringNames[k])] = v - } - } - return m -} - -// StringCaps returns all string capabilities. -func (ti *Terminfo) StringCaps() map[string][]byte { - return ti.stringCaps(StringCapName, false) -} - -// StringCapsShort returns all string capabilities, using the short name as the -// index. -func (ti *Terminfo) StringCapsShort() map[string][]byte { - return ti.stringCaps(StringCapNameShort, false) -} - -// ExtStringCaps returns all extended string capabilities. -func (ti *Terminfo) ExtStringCaps() map[string][]byte { - return ti.stringCaps(StringCapName, true) -} - -// ExtStringCapsShort returns all extended string capabilities, using the short -// name as the index. -func (ti *Terminfo) ExtStringCapsShort() map[string][]byte { - return ti.stringCaps(StringCapNameShort, true) -} - -// Has determines if the bool cap i is present. -func (ti *Terminfo) Has(i int) bool { - return ti.Bools[i] -} - -// Num returns the num cap i, or -1 if not present. -func (ti *Terminfo) Num(i int) int { - n, ok := ti.Nums[i] - if !ok { - return -1 - } - return n -} - -// Printf formats the string cap i, interpolating parameters v. -func (ti *Terminfo) Printf(i int, v ...interface{}) string { - return Printf(ti.Strings[i], v...) -} - -// Fprintf prints the string cap i to writer w, interpolating parameters v. -func (ti *Terminfo) Fprintf(w io.Writer, i int, v ...interface{}) { - Fprintf(w, ti.Strings[i], v...) -} - -// Color takes a foreground and background color and returns string that sets -// them for this terminal. -func (ti *Terminfo) Colorf(fg, bg int, str string) string { - maxColors := int(ti.Nums[MaxColors]) - // map bright colors to lower versions if the color table only holds 8. - if maxColors == 8 { - if fg > 7 && fg < 16 { - fg -= 8 - } - if bg > 7 && bg < 16 { - bg -= 8 - } - } - var s string - if maxColors > fg && fg >= 0 { - s += ti.Printf(SetAForeground, fg) - } - if maxColors > bg && bg >= 0 { - s += ti.Printf(SetABackground, bg) - } - return s + str + ti.Printf(ExitAttributeMode) -} - -// Goto returns a string suitable for addressing the cursor at the given -// row and column. The origin 0, 0 is in the upper left corner of the screen. -func (ti *Terminfo) Goto(row, col int) string { - return Printf(ti.Strings[CursorAddress], row, col) -} - -// Puts emits the string to the writer, but expands inline padding indications -// (of the form $<[delay]> where [delay] is msec) to a suitable number of -// padding characters (usually null bytes) based upon the supplied baud. At -// high baud rates, more padding characters will be inserted. -/*func (ti *Terminfo) Puts(w io.Writer, s string, lines, baud int) (int, error) { - var err error - for { - start := strings.Index(s, "$<") - if start == -1 { - // most strings don't need padding, which is good news! - return io.WriteString(w, s) - } - end := strings.Index(s, ">") - if end == -1 { - // unterminated... just emit bytes unadulterated. - return io.WriteString(w, "$<"+s) - } - var c int - c, err = io.WriteString(w, s[:start]) - if err != nil { - return n + c, err - } - n += c - s = s[start+2:] - val := s[:end] - s = s[end+1:] - var ms int - var dot, mandatory, asterisk bool - unit := 1000 - for _, ch := range val { - switch { - case ch >= '0' && ch <= '9': - ms = (ms * 10) + int(ch-'0') - if dot { - unit *= 10 - } - case ch == '.' && !dot: - dot = true - case ch == '*' && !asterisk: - ms *= lines - asterisk = true - case ch == '/': - mandatory = true - default: - break - } - } - z, pad := ((baud/8)/unit)*ms, ti.Strings[PadChar] - b := make([]byte, len(pad)*z) - for bp := copy(b, pad); bp < len(b); bp *= 2 { - copy(b[bp:], b[:bp]) - } - if (!ti.Bools[XonXoff] && baud > int(ti.Nums[PaddingBaudRate])) || mandatory { - c, err = w.Write(b) - if err != nil { - return n + c, err - } - n += c - } - } - return n, nil -}*/ diff --git a/vendor/modules.txt b/vendor/modules.txt index ac1e753d6..21a97dab2 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -614,6 +614,10 @@ github.com/go-task/slim-sprig github.com/go-task/slim-sprig/v3 # github.com/go-test/deep v1.1.0 ## explicit; go 1.16 +# github.com/go-viper/mapstructure/v2 v2.4.0 +## explicit; go 1.18 +github.com/go-viper/mapstructure/v2 +github.com/go-viper/mapstructure/v2/internal/errors # github.com/gobwas/glob v0.2.3 ## explicit github.com/gobwas/glob @@ -717,26 +721,20 @@ github.com/google/renameio/v2 # github.com/google/uuid v1.6.0 ## explicit github.com/google/uuid -# github.com/gookit/color v1.5.4 -## explicit; go 1.18 -github.com/gookit/color -# github.com/gookit/config/v2 v2.2.6 +# github.com/gookit/config/v2 v2.2.7 ## explicit; go 1.19 github.com/gookit/config/v2 github.com/gookit/config/v2/yaml -# github.com/gookit/goutil v0.6.18 +# github.com/gookit/goutil v0.7.1 ## explicit; go 1.19 github.com/gookit/goutil github.com/gookit/goutil/arrutil -github.com/gookit/goutil/basefn github.com/gookit/goutil/byteutil github.com/gookit/goutil/cliutil/cmdline github.com/gookit/goutil/comdef -github.com/gookit/goutil/encodes github.com/gookit/goutil/envutil github.com/gookit/goutil/errorx github.com/gookit/goutil/fsutil -github.com/gookit/goutil/goinfo github.com/gookit/goutil/internal/checkfn github.com/gookit/goutil/internal/comfunc github.com/gookit/goutil/internal/varexpr @@ -744,13 +742,18 @@ github.com/gookit/goutil/jsonutil github.com/gookit/goutil/maputil github.com/gookit/goutil/mathutil github.com/gookit/goutil/reflects -github.com/gookit/goutil/stdio github.com/gookit/goutil/structs github.com/gookit/goutil/strutil github.com/gookit/goutil/strutil/textutil github.com/gookit/goutil/syncs github.com/gookit/goutil/sysutil github.com/gookit/goutil/sysutil/cmdr +github.com/gookit/goutil/x/basefn +github.com/gookit/goutil/x/ccolor +github.com/gookit/goutil/x/encodes +github.com/gookit/goutil/x/goinfo +github.com/gookit/goutil/x/stdio +github.com/gookit/goutil/x/termenv # github.com/gorilla/handlers v1.5.1 ## explicit; go 1.14 github.com/gorilla/handlers @@ -1927,9 +1930,6 @@ github.com/xeipuuv/gojsonreference # github.com/xhit/go-simple-mail/v2 v2.16.0 ## explicit; go 1.13 github.com/xhit/go-simple-mail/v2 -# github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e -## explicit; go 1.19 -github.com/xo/terminfo # github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 ## explicit; go 1.15 github.com/xrash/smetrics