build(deps): bump github.com/gookit/config/v2 from 2.2.4 to 2.2.5

Bumps [github.com/gookit/config/v2](https://github.com/gookit/config) from 2.2.4 to 2.2.5.
- [Release notes](https://github.com/gookit/config/releases)
- [Commits](https://github.com/gookit/config/compare/v2.2.4...v2.2.5)

---
updated-dependencies:
- dependency-name: github.com/gookit/config/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot]
2024-01-16 06:31:49 +00:00
committed by Ralf Haferkamp
parent 2253096609
commit 81e000b987
33 changed files with 1028 additions and 626 deletions
+2 -2
View File
@@ -47,7 +47,7 @@ require (
github.com/google/go-cmp v0.6.0
github.com/google/go-tika v0.3.0
github.com/google/uuid v1.5.0
github.com/gookit/config/v2 v2.2.4
github.com/gookit/config/v2 v2.2.5
github.com/gorilla/mux v1.8.1
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0
github.com/jellydator/ttlcache/v2 v2.11.1
@@ -223,7 +223,7 @@ require (
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
github.com/google/renameio/v2 v2.0.0 // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/gookit/goutil v0.6.14 // indirect
github.com/gookit/goutil v0.6.15 // indirect
github.com/gorilla/handlers v1.5.1 // indirect
github.com/gorilla/schema v1.2.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
+6 -6
View File
@@ -1423,12 +1423,12 @@ github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
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.4 h1:uLHNzFzREe5gDBP4Gb1+WOC9LB6vauPvq4eolp32Dcg=
github.com/gookit/config/v2 v2.2.4/go.mod h1:k1ofSAuJnW6n1kTriFMSzFDC8ZT20tAPQ+1iGI3QOrU=
github.com/gookit/goutil v0.6.14 h1:96elyOG4BvVoDaiT7vx1vHPrVyEtFfYlPPBODR0/FGQ=
github.com/gookit/goutil v0.6.14/go.mod h1:YyDBddefmjS+mU2PDPgCcjVzTDM5WgExiDv5ZA/b8I8=
github.com/gookit/ini/v2 v2.2.2 h1:3B8abZJrVH1vi/7TU4STuTBxdhiAq1ORSt6NJZCahaI=
github.com/gookit/ini/v2 v2.2.2/go.mod h1:wGEfnBxv+7nVXytWM44tiqczv5hLKJ+m9MaA2uJg3iM=
github.com/gookit/config/v2 v2.2.5 h1:RECbYYbtherywmzn3LNeu9NA5ZqhD7MSKEMsJ7l+MpU=
github.com/gookit/config/v2 v2.2.5/go.mod h1:NeX+yiNYn6Ei10eJvCQFXuHEPIE/IPS8bqaFIsszzaM=
github.com/gookit/goutil v0.6.15 h1:mMQ0ElojNZoyPD0eVROk5QXJPh2uKR4g06slgPDF5Jo=
github.com/gookit/goutil v0.6.15/go.mod h1:qdKdYEHQdEtyH+4fNdQNZfJHhI0jUZzHxQVAV3DaMDY=
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/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=
+11 -5
View File
@@ -89,6 +89,7 @@ import (
// go run ./examples/yaml.go
func main() {
// config.ParseEnv: will parse env var in string value. eg: shell: ${SHELL}
config.WithOptions(config.ParseEnv)
// add driver for support yaml content
@@ -350,7 +351,7 @@ ioutil.WriteFile("my-config.yaml", buf.Bytes(), 0755)
```go
// Options config options
type Options struct {
// parse env value. like: "${EnvName}" "${EnvName|default}"
// parse env in string value. like: "${EnvName}" "${EnvName|default}"
ParseEnv bool
// ParseTime parses a duration string to time.Duration
// eg: 10s, 2m
@@ -361,10 +362,6 @@ type Options struct {
EnableCache bool
// parse key, allow find value by key path. default is True eg: 'key.sub' will find `map[key]sub`
ParseKey bool
// tag name for binding data to struct
// Deprecated
// please set tag name by DecoderConfig
TagName string
// the delimiter char for split key path, if `FindByPath=true`. default is '.'
Delimiter byte
// default write format
@@ -380,6 +377,15 @@ type Options struct {
}
```
Examples for set options:
```go
config.WithOptions(config.WithTagName("mytag"))
config.WithOptions(func(opt *Options) {
opt.SetTagNames("config")
})
```
### Options: Parse default
Support parse default value by struct tag `default`
+21 -8
View File
@@ -21,14 +21,20 @@
- 支持从 OS ENV 变量数据加载配置
- 支持从远程 URL 加载配置数据
- 支持从命令行参数(`flags`)设置配置数据
- 支持在配置数据更改时触发事件
- 可用事件: `set.value`, `set.data`, `load.data`, `clean.data`, `reload.data`
- 支持数据覆盖合并,加载多份数据时将按key自动合并
- 数据自动覆盖合并,加载多份数据时将按`key`自动合并
- 支持丰富的自定义选项设置
- `Readonly` 支持设置配置数据只读
- `EnableCache` 支持设置配置数据缓存
- `ParseEnv` 支持获取时自动解析string值里的ENV变量(`shell: ${SHELL}` -> `shell: /bin/zsh`)
- `ParseDefault` 支持在绑定数据到结构体时解析默认值 (tag: `default:"def_value"`, 配合ParseEnv也支持ENV变量)
- `ParseTime` 支持绑定数据到struct时自动转换 `10s`,`2m``time.Duration`
- 完整选项设置请查看 `config.Options`
- 支持将全部或部分配置数据绑定到结构体 `config.BindStruct("key", &s)`
- 支持通过结构体标签 `default` 解析并设置默认值. eg: `default:"def_value"`
- 支持从 ENV 初始化设置字段值 `default:"${APP_ENV | dev}"`
- 支持通过 `.` 分隔符来按路径获取子级值,也支持自定义分隔符。 e.g `map.key` `arr.2`
- 支持解析ENV变量名称。 like `shell: ${SHELL}` -> `shell: /bin/zsh`
- 支持在配置数据更改时触发事件
- 可用事件: `set.value`, `set.data`, `load.data`, `clean.data`, `reload.data`
- 简洁的使用API `Get` `Int` `Uint` `Int64` `String` `Bool` `Ints` `IntMap` `Strings` `StringMap` ...
- 完善的单元测试(code coverage > 95%)
@@ -88,7 +94,7 @@ import (
// go run ./examples/yaml.go
func main() {
// 设置选项支持 ENV 解析
// 设置选项支持ENV变量解析:当获取的值为string类型时,会尝试解析其中的ENV变量
config.WithOptions(config.ParseEnv)
// 添加驱动程序以支持yaml内容解析(除了JSON是默认支持,其他的则是按需使用)
@@ -334,7 +340,7 @@ ioutil.WriteFile("my-config.yaml", buf.Bytes(), 0755)
```go
// Options config options
type Options struct {
// parse env value. like: "${EnvName}" "${EnvName|default}"
// parse env in string value. like: "${EnvName}" "${EnvName|default}"
ParseEnv bool
// ParseTime parses a duration string to time.Duration
// eg: 10s, 2m
@@ -345,8 +351,6 @@ type Options struct {
EnableCache bool
// parse key, allow find value by key path. default is True eg: 'key.sub' will find `map[key]sub`
ParseKey bool
// tag name for binding data to struct
TagName string
// the delimiter char for split key, when `FindByPath=true`. default is '.'
Delimiter byte
// default write format. default is JSON
@@ -362,6 +366,15 @@ type Options struct {
}
```
Examples for set options:
```go
config.WithOptions(config.WithTagName("mytag"))
config.WithOptions(func(opt *Options) {
opt.SetTagNames("config")
})
```
### 选项: 解析默认值
NEW: 支持通过结构标签 `default` 解析并设置默认值
+7 -6
View File
@@ -111,23 +111,24 @@ type Config struct {
sMapCache map[string]strMap
}
// New config instance, default add JSON driver
// New config instance with custom options, default with JSON driver
func New(name string, opts ...OptionFn) *Config {
return NewEmpty(name).WithDriver(JSONDriver).WithOptions(opts...)
return NewEmpty(name, opts...).WithDriver(JSONDriver)
}
// NewEmpty config instance
func NewEmpty(name string) *Config {
return &Config{
// NewEmpty create config instance with custom options
func NewEmpty(name string, opts ...OptionFn) *Config {
c := &Config{
name: name,
opts: newDefaultOption(),
data: make(map[string]any),
// don't add any drivers
encoders: map[string]Encoder{},
decoders: map[string]Decoder{},
aliasMap: make(map[string]string),
}
return c.WithOptions(opts...)
}
// NewWith create config instance, and you can call some init func
+1 -3
View File
@@ -121,7 +121,7 @@ func (c *Config) Structure(key string, dst any) (err error) {
return err
}
// ToJSON string
// ToJSON string, will ignore error
func (c *Config) ToJSON() string {
buf := &bytes.Buffer{}
@@ -129,7 +129,6 @@ func (c *Config) ToJSON() string {
if err != nil {
return ""
}
return buf.String()
}
@@ -168,7 +167,6 @@ func (c *Config) DumpTo(out io.Writer, format string) (n int64, err error) {
// write content to out
num, _ := fmt.Fprintln(out, string(encoded))
return int64(num), nil
}
+63 -23
View File
@@ -12,9 +12,9 @@ import (
"strings"
"time"
"dario.cat/mergo"
"github.com/gookit/goutil/errorx"
"github.com/gookit/goutil/fsutil"
"github.com/imdario/mergo"
)
// LoadFiles load one or multi files, will fire OnLoadData event
@@ -103,10 +103,8 @@ func (c *Config) LoadOSEnv(keys []string, keyToLower bool) {
if keyToLower {
key = strings.ToLower(key)
}
_ = c.Set(key, val)
}
c.fireHook(OnLoadData)
}
@@ -195,7 +193,7 @@ func LoadData(dataSource ...any) error { return dc.LoadData(dataSource...) }
// LoadData load data from map OR struct
//
// The dataSources can be:
// The dataSources type allow:
// - map[string]any
// - map[string]string
func (c *Config) LoadData(dataSources ...any) (err error) {
@@ -203,19 +201,24 @@ func (c *Config) LoadData(dataSources ...any) (err error) {
c.opts.Delimiter = defaultDelimiter
}
var loaded bool
for _, ds := range dataSources {
if smp, ok := ds.(map[string]string); ok {
loaded = true
c.LoadSMap(smp)
continue
}
err = mergo.Merge(&c.data, ds, mergo.WithOverride)
err = mergo.Merge(&c.data, ds, c.opts.MergeOptions...)
if err != nil {
return errorx.WithStack(err)
}
loaded = true
}
c.fireHook(OnLoadData)
if loaded {
c.fireHook(OnLoadData)
}
return
}
@@ -237,8 +240,8 @@ func LoadSources(format string, src []byte, more ...[]byte) error {
// Usage:
//
// config.LoadSources(config.Yaml, []byte(`
// name: blog
// arr:
// name: blog
// arr:
// key: val
//
// `))
@@ -308,6 +311,24 @@ func (c *Config) LoadExistsByFormat(format string, configFiles ...string) (err e
return
}
// LoadOptions for load config from dir.
type LoadOptions struct {
// DataKey use for load config from dir.
// see https://github.com/gookit/config/issues/173
DataKey string
}
// LoadOptFn type func
type LoadOptFn func(lo *LoadOptions)
func newLoadOptions(loFns []LoadOptFn) *LoadOptions {
lo := &LoadOptions{}
for _, fn := range loFns {
fn(lo)
}
return lo
}
// LoadFromDir Load custom format files from the given directory, the file name will be used as the key.
//
// Example:
@@ -317,24 +338,30 @@ func (c *Config) LoadExistsByFormat(format string, configFiles ...string) (err e
//
// // after load
// Config.data = map[string]any{"task": file data}
func LoadFromDir(dirPath, format string) error {
return dc.LoadFromDir(dirPath, format)
func LoadFromDir(dirPath, format string, loFns ...LoadOptFn) error {
return dc.LoadFromDir(dirPath, format, loFns...)
}
// LoadFromDir Load custom format files from the given directory, the file name will be used as the key.
//
// NOTE: will not be reloaded on call ReloadFiles(), if data loaded by the method.
//
// Example:
//
// // file: /somedir/task.json
// // file: /somedir/task.json , will use filename 'task' as key
// Config.LoadFromDir("/somedir", "json")
//
// // after load
// Config.data = map[string]any{"task": file data}
func (c *Config) LoadFromDir(dirPath, format string) (err error) {
// // after load, the data will be:
// Config.data = map[string]any{"task": {file data}}
func (c *Config) LoadFromDir(dirPath, format string, loFns ...LoadOptFn) (err error) {
extName := "." + format
extLen := len(extName)
return fsutil.FindInDir(dirPath, func(fPath string, ent fs.DirEntry) error {
lo := newLoadOptions(loFns)
dirData := make(map[string]any)
dataList := make([]map[string]any, 0, 8)
err = fsutil.FindInDir(dirPath, func(fPath string, ent fs.DirEntry) error {
baseName := ent.Name()
if strings.HasSuffix(baseName, extName) {
data, err := c.parseSourceToMap(format, fsutil.MustReadFile(fPath))
@@ -342,18 +369,31 @@ func (c *Config) LoadFromDir(dirPath, format string) (err error) {
return err
}
// filename without ext.
onlyName := baseName[:len(baseName)-extLen]
err = c.loadDataMap(map[string]any{onlyName: data})
if lo.DataKey != "" {
dataList = append(dataList, data)
} else {
dirData[onlyName] = data
}
// use file name as key, it cannot be reloaded. SO, cannot append to loadedFiles
// if err == nil {
// c.loadedFiles = append(c.loadedFiles, fPath)
// }
return err
// TODO use file name as key, it cannot be reloaded. So, cannot append to loadedFiles
// c.loadedFiles = append(c.loadedFiles, fPath)
}
return nil
})
if err != nil {
return err
}
if lo.DataKey != "" {
dirData[lo.DataKey] = dataList
}
if len(dirData) == 0 {
return nil
}
return c.loadDataMap(dirData)
}
// ReloadFiles reload config data use loaded files
@@ -440,7 +480,7 @@ func (c *Config) loadDataMap(data map[string]any) (err error) {
c.data = data
} else {
// again ... will merge data
err = mergo.Merge(&c.data, data, mergo.WithOverride, mergo.WithTypeCheck)
err = mergo.Merge(&c.data, data, c.opts.MergeOptions...)
}
if !c.reloading && err == nil {
+21 -11
View File
@@ -3,6 +3,8 @@ package config
import (
"strings"
"dario.cat/mergo"
"github.com/gookit/goutil"
"github.com/mitchellh/mapstructure"
)
@@ -20,20 +22,22 @@ type HookFunc func(event string, c *Config)
// Options config options
type Options struct {
// ParseEnv parse env value. like: "${EnvName}" "${EnvName|default}"
// ParseEnv parse env in string value and default value. like: "${EnvName}" "${EnvName|default}"
ParseEnv bool
// ParseTime parses a duration string to time.Duration
// eg: 10s, 2m
ParseTime bool
// Readonly config is readonly
Readonly bool
// ParseDefault tag on binding data to struct. tag: default
ParseDefault bool
// EnableCache enable config data cache
EnableCache bool
// ParseKey parse key path, allow find value by key path. eg: 'key.sub' will find `map[key]sub`
ParseKey bool
// TagName tag name for binding data to struct
//
// Tips: please set tag name by DecoderConfig
// Deprecated: please set tag name by DecoderConfig, or use SetTagName()
TagName string
// Delimiter the delimiter char for split key path, if `FindByPath=true`. default is '.'
Delimiter byte
@@ -45,8 +49,8 @@ type Options struct {
DecoderConfig *mapstructure.DecoderConfig
// HookFunc on data changed. you can do something...
HookFunc HookFunc
// ParseDefault tag on binding data to struct. tag: default
ParseDefault bool
// MergeOptions settings for merge two data
MergeOptions []func(*mergo.Config)
// WatchChange bool
}
@@ -63,6 +67,10 @@ func newDefaultOption() *Options {
ReadFormat: JSON,
// struct decoder config
DecoderConfig: newDefaultDecoderConfig(""),
MergeOptions: []func(*mergo.Config){
mergo.WithOverride,
mergo.WithTypeCheck,
},
}
}
@@ -79,6 +87,12 @@ func newDefaultDecoderConfig(tagName string) *mapstructure.DecoderConfig {
}
}
// SetTagName for mapping data to struct
func (o *Options) SetTagName(tagName string) {
o.TagName = tagName
o.DecoderConfig.TagName = tagName
}
func (o *Options) shouldAddHookFunc() bool {
return o.ParseTime || o.ParseEnv
}
@@ -113,8 +127,7 @@ func (o *Options) makeDecoderConfig() *mapstructure.DecoderConfig {
// WithTagName set tag name for export to struct
func WithTagName(tagName string) func(*Options) {
return func(opts *Options) {
opts.TagName = tagName
opts.DecoderConfig.TagName = tagName
opts.SetTagName(tagName)
}
}
@@ -137,15 +150,12 @@ func Delimiter(sep byte) func(*Options) {
}
}
// SaveFileOnSet set hook func
// SaveFileOnSet set hook func, will panic on save error
func SaveFileOnSet(fileName string, format string) func(options *Options) {
return func(opts *Options) {
opts.HookFunc = func(event string, c *Config) {
if strings.HasPrefix(event, "set.") {
err := c.DumpToFile(fileName, format)
if err != nil {
panic(err)
}
goutil.PanicErr(c.DumpToFile(fileName, format))
}
}
}
+44 -27
View File
@@ -664,7 +664,6 @@ func SizeToString(size uint64) string
func StringToByte(sizeStr string) uint64
func ParseByte(sizeStr string) uint64
func PrettyJSON(v any) (string, error)
func StringsToInts(ss []string) (ints []int, err error)
func ArgsWithSpaces(vs []any) (message string)
// source at fmtutil/time.go
func HowLongAgo(sec int64) string
@@ -909,6 +908,8 @@ func SimpleMerge(src, dst 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 MergeStringMap(src, dst map[string]string, ignoreCase bool) map[string]string
func MergeMultiSMap(mps ...map[string]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)
// source at maputil/setval.go
@@ -942,36 +943,49 @@ func MaxI64(x, y int64) int64
func SwapMaxI64(x, y int64) (int64, int64)
func MaxFloat(x, y float64) float64
// source at mathutil/convert.go
func NewConvOption[T any](optFns ...ConvOptionFn[T]) *ConvOption[T]
func WithNilAsFail[T any](opt *ConvOption[T])
func WithHandlePtr[T any](opt *ConvOption[T])
func WithUserConvFn[T any](fn ToTypeFunc[T]) ConvOptionFn[T]
func Int(in any) (int, error)
func SafeInt(in any) int
func QuietInt(in any) int
func MustInt(in any) int
func IntOrPanic(in any) int
func MustInt(in any) int
func IntOrDefault(in any, defVal int) int
func IntOr(in any, defVal int) int
func IntOrErr(in any) (iVal int, err error)
func ToInt(in any) (iVal int, err error)
func ToIntWithFunc(in any, usrFn ToIntFunc) (iVal int, err error)
func IntOrErr(in any) (int, error)
func ToInt(in any) (int, error)
func ToIntWith(in any, optFns ...ConvOptionFn[int]) (iVal int, err error)
func StrInt(s string) int
func StrIntOr(s string, defVal int) int
func Uint(in any) (uint64, error)
func SafeUint(in any) uint64
func QuietUint(in any) uint64
func MustUint(in any) uint64
func UintOrDefault(in any, defVal uint64) uint64
func UintOr(in any, defVal uint64) uint64
func UintOrErr(in any) (uint64, error)
func ToUint(in any) (u64 uint64, err error)
func ToUintWithFunc(in any, usrFn ToUintFunc) (u64 uint64, err error)
func Int64(in any) (int64, error)
func SafeInt64(in any) int64
func QuietInt64(in any) int64
func MustInt64(in any) int64
func Int64OrDefault(in any, defVal int64) int64
func Int64Or(in any, defVal int64) int64
func ToInt64(in any) (int64, error)
func Int64OrErr(in any) (int64, error)
func ToInt64(in any) (i64 int64, err error)
func ToInt64WithFunc(in any, usrFn ToInt64Func) (i64 int64, err error)
func ToInt64With(in any, optFns ...ConvOptionFn[int64]) (i64 int64, err error)
func Uint(in any) (uint, error)
func SafeUint(in any) uint
func QuietUint(in any) uint
func MustUint(in any) uint
func UintOrDefault(in any, defVal uint) uint
func UintOr(in any, defVal uint) uint
func UintOrErr(in any) (uint, error)
func ToUint(in any) (u64 uint, err error)
func ToUintWith(in any, optFns ...ConvOptionFn[uint]) (uVal uint, err error)
func Uint64(in any) (uint64, error)
func QuietUint64(in any) uint64
func SafeUint64(in any) uint64
func MustUint64(in any) uint64
func Uint64OrDefault(in any, defVal uint64) uint64
func Uint64Or(in any, defVal uint64) uint64
func Uint64OrErr(in any) (uint64, error)
func ToUint64(in any) (uint64, error)
func ToUint64With(in any, optFns ...ConvOptionFn[uint64]) (u64 uint64, err error)
func QuietFloat(in any) float64
func SafeFloat(in any) float64
func FloatOrPanic(in any) float64
@@ -980,8 +994,8 @@ func FloatOrDefault(in any, defVal float64) float64
func FloatOr(in any, defVal float64) float64
func Float(in any) (float64, error)
func FloatOrErr(in any) (float64, error)
func ToFloat(in any) (f64 float64, err error)
func ToFloatWithFunc(in any, usrFn ToFloatFunc) (f64 float64, err error)
func ToFloat(in any) (float64, error)
func ToFloatWith(in any, optFns ...ConvOptionFn[float64]) (f64 float64, err error)
func MustString(val any) string
func StringOrPanic(val any) string
func StringOrDefault(val any, defVal string) string
@@ -991,10 +1005,8 @@ func StringOrErr(val any) (string, error)
func QuietString(val any) string
func String(val any) string
func SafeString(val any) string
func TryToString(val any, defaultAsErr bool) (str string, err error)
func ToStringWithFunc(val any, usrFn comdef.ToStringFunc) (str string, err error)
func Percent(val, total int) float64
func ElapsedTime(startTime time.Time) string
func TryToString(val any, defaultAsErr bool) (string, error)
func ToStringWith(in any, optFns ...comfunc.ConvOptionFn) (string, error)
// source at mathutil/format.go
func DataSize(size uint64) string
func HowLongAgo(sec int64) string
@@ -1005,11 +1017,19 @@ 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 MulF2i(a, b float64) int
func Div[T1, T2 comdef.XintOrFloat](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
// source at mathutil/random.go
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
@@ -1223,7 +1243,7 @@ func MustString(val any) string
func StringOrDefault(val any, defVal string) string
func StringOr(val any, defVal string) string
func AnyToString(val any, defaultAsErr bool) (s string, err error)
func ToStringWithFunc(val any, fbFn comdef.ToStringFunc) (str string, err error)
func ToStringWith(in any, optFns ...comfunc.ConvOptionFn) (string, error)
func ToBool(s string) (bool, error)
func QuietBool(s string) bool
func SafeBool(s string) bool
@@ -1238,8 +1258,8 @@ func QuietInt(s string) int
func MustInt(s string) int
func IntOrPanic(s string) int
func Int64(s string) int64
func SafeInt64(s string) int64
func QuietInt64(s string) int64
func SafeInt64(s string) int64
func ToInt64(s string) (int64, error)
func Int64OrDefault(s string, defVal int64) int64
func Int64Or(s string, defVal int64) int64
@@ -1401,9 +1421,6 @@ func OrHandle(s string, fn comdef.StringHandleFunc) string
func Valid(ss ...string) string
func Replaces(str string, pairs map[string]string) string
func NewReplacer(pairs map[string]string) *strings.Replacer
func PrettyJSON(v any) (string, error)
func RenderTemplate(input string, data any, fns template.FuncMap, isFile ...bool) string
func RenderText(input string, data any, fns template.FuncMap, isFile ...bool) string
func WrapTag(s, tag string) string
func SubstrCount(s, substr string, params ...uint64) (int, error)
```
+44 -27
View File
@@ -665,7 +665,6 @@ func SizeToString(size uint64) string
func StringToByte(sizeStr string) uint64
func ParseByte(sizeStr string) uint64
func PrettyJSON(v any) (string, error)
func StringsToInts(ss []string) (ints []int, err error)
func ArgsWithSpaces(vs []any) (message string)
// source at fmtutil/time.go
func HowLongAgo(sec int64) string
@@ -910,6 +909,8 @@ func SimpleMerge(src, dst 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 MergeStringMap(src, dst map[string]string, ignoreCase bool) map[string]string
func MergeMultiSMap(mps ...map[string]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)
// source at maputil/setval.go
@@ -943,36 +944,49 @@ func MaxI64(x, y int64) int64
func SwapMaxI64(x, y int64) (int64, int64)
func MaxFloat(x, y float64) float64
// source at mathutil/convert.go
func NewConvOption[T any](optFns ...ConvOptionFn[T]) *ConvOption[T]
func WithNilAsFail[T any](opt *ConvOption[T])
func WithHandlePtr[T any](opt *ConvOption[T])
func WithUserConvFn[T any](fn ToTypeFunc[T]) ConvOptionFn[T]
func Int(in any) (int, error)
func SafeInt(in any) int
func QuietInt(in any) int
func MustInt(in any) int
func IntOrPanic(in any) int
func MustInt(in any) int
func IntOrDefault(in any, defVal int) int
func IntOr(in any, defVal int) int
func IntOrErr(in any) (iVal int, err error)
func ToInt(in any) (iVal int, err error)
func ToIntWithFunc(in any, usrFn ToIntFunc) (iVal int, err error)
func IntOrErr(in any) (int, error)
func ToInt(in any) (int, error)
func ToIntWith(in any, optFns ...ConvOptionFn[int]) (iVal int, err error)
func StrInt(s string) int
func StrIntOr(s string, defVal int) int
func Uint(in any) (uint64, error)
func SafeUint(in any) uint64
func QuietUint(in any) uint64
func MustUint(in any) uint64
func UintOrDefault(in any, defVal uint64) uint64
func UintOr(in any, defVal uint64) uint64
func UintOrErr(in any) (uint64, error)
func ToUint(in any) (u64 uint64, err error)
func ToUintWithFunc(in any, usrFn ToUintFunc) (u64 uint64, err error)
func Int64(in any) (int64, error)
func SafeInt64(in any) int64
func QuietInt64(in any) int64
func MustInt64(in any) int64
func Int64OrDefault(in any, defVal int64) int64
func Int64Or(in any, defVal int64) int64
func ToInt64(in any) (int64, error)
func Int64OrErr(in any) (int64, error)
func ToInt64(in any) (i64 int64, err error)
func ToInt64WithFunc(in any, usrFn ToInt64Func) (i64 int64, err error)
func ToInt64With(in any, optFns ...ConvOptionFn[int64]) (i64 int64, err error)
func Uint(in any) (uint, error)
func SafeUint(in any) uint
func QuietUint(in any) uint
func MustUint(in any) uint
func UintOrDefault(in any, defVal uint) uint
func UintOr(in any, defVal uint) uint
func UintOrErr(in any) (uint, error)
func ToUint(in any) (u64 uint, err error)
func ToUintWith(in any, optFns ...ConvOptionFn[uint]) (uVal uint, err error)
func Uint64(in any) (uint64, error)
func QuietUint64(in any) uint64
func SafeUint64(in any) uint64
func MustUint64(in any) uint64
func Uint64OrDefault(in any, defVal uint64) uint64
func Uint64Or(in any, defVal uint64) uint64
func Uint64OrErr(in any) (uint64, error)
func ToUint64(in any) (uint64, error)
func ToUint64With(in any, optFns ...ConvOptionFn[uint64]) (u64 uint64, err error)
func QuietFloat(in any) float64
func SafeFloat(in any) float64
func FloatOrPanic(in any) float64
@@ -981,8 +995,8 @@ func FloatOrDefault(in any, defVal float64) float64
func FloatOr(in any, defVal float64) float64
func Float(in any) (float64, error)
func FloatOrErr(in any) (float64, error)
func ToFloat(in any) (f64 float64, err error)
func ToFloatWithFunc(in any, usrFn ToFloatFunc) (f64 float64, err error)
func ToFloat(in any) (float64, error)
func ToFloatWith(in any, optFns ...ConvOptionFn[float64]) (f64 float64, err error)
func MustString(val any) string
func StringOrPanic(val any) string
func StringOrDefault(val any, defVal string) string
@@ -992,10 +1006,8 @@ func StringOrErr(val any) (string, error)
func QuietString(val any) string
func String(val any) string
func SafeString(val any) string
func TryToString(val any, defaultAsErr bool) (str string, err error)
func ToStringWithFunc(val any, usrFn comdef.ToStringFunc) (str string, err error)
func Percent(val, total int) float64
func ElapsedTime(startTime time.Time) string
func TryToString(val any, defaultAsErr bool) (string, error)
func ToStringWith(in any, optFns ...comfunc.ConvOptionFn) (string, error)
// source at mathutil/format.go
func DataSize(size uint64) string
func HowLongAgo(sec int64) string
@@ -1006,11 +1018,19 @@ 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 MulF2i(a, b float64) int
func Div[T1, T2 comdef.XintOrFloat](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
// source at mathutil/random.go
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
@@ -1224,7 +1244,7 @@ func MustString(val any) string
func StringOrDefault(val any, defVal string) string
func StringOr(val any, defVal string) string
func AnyToString(val any, defaultAsErr bool) (s string, err error)
func ToStringWithFunc(val any, fbFn comdef.ToStringFunc) (str string, err error)
func ToStringWith(in any, optFns ...comfunc.ConvOptionFn) (string, error)
func ToBool(s string) (bool, error)
func QuietBool(s string) bool
func SafeBool(s string) bool
@@ -1239,8 +1259,8 @@ func QuietInt(s string) int
func MustInt(s string) int
func IntOrPanic(s string) int
func Int64(s string) int64
func SafeInt64(s string) int64
func QuietInt64(s string) int64
func SafeInt64(s string) int64
func ToInt64(s string) (int64, error)
func Int64OrDefault(s string, defVal int64) int64
func Int64Or(s string, defVal int64) int64
@@ -1402,9 +1422,6 @@ func OrHandle(s string, fn comdef.StringHandleFunc) string
func Valid(ss ...string) string
func Replaces(str string, pairs map[string]string) string
func NewReplacer(pairs map[string]string) *strings.Replacer
func PrettyJSON(v any) (string, error)
func RenderTemplate(input string, data any, fns template.FuncMap, isFile ...bool) string
func RenderText(input string, data any, fns template.FuncMap, isFile ...bool) string
func WrapTag(s, tag string) string
func SubstrCount(s, substr string, params ...uint64) (int, error)
```
+3
View File
@@ -9,6 +9,9 @@ type (
UnmarshalFunc func(bts []byte, ptr any) error
)
// ToTypeFunc convert value to defined type
type ToTypeFunc[T any] func(any) (T, error)
// IntCheckFunc check func
type IntCheckFunc func(val int) error
+33 -1
View File
@@ -1,6 +1,38 @@
package comdef
import "errors"
import (
"errors"
"strings"
)
// ErrConvType error
var ErrConvType = errors.New("convert value type error")
// Errors multi error list
type Errors []error
// Error string
func (es Errors) Error() string {
var sb strings.Builder
for _, err := range es {
sb.WriteString(err.Error())
sb.WriteByte('\n')
}
return sb.String()
}
// ErrOrNil error
func (es Errors) ErrOrNil() error {
if len(es) == 0 {
return nil
}
return es
}
// First error
func (es Errors) First() error {
if len(es) > 0 {
return es[0]
}
return nil
}
+5
View File
@@ -24,6 +24,11 @@ type Int64able interface {
Int64() (int64, error)
}
// Float64able interface
type Float64able interface {
Float64() (float64, error)
}
//
//
// Matcher type
+21 -10
View File
@@ -57,17 +57,28 @@ func ToInt64(v any) (int64, error) {
return mathutil.ToInt64(v)
}
// Uint convert value to uint64
func Uint(v any) uint64 {
// Uint convert value to uint
func Uint(v any) uint {
iv, _ := mathutil.ToUint(v)
return iv
}
// ToUint try to convert value to uint64
func ToUint(v any) (uint64, error) {
// ToUint try to convert value to uint
func ToUint(v any) (uint, error) {
return mathutil.ToUint(v)
}
// Uint64 convert value to uint64
func Uint64(v any) uint64 {
iv, _ := mathutil.ToUint64(v)
return iv
}
// ToUint64 try to convert value to uint64
func ToUint64(v any) (uint64, error) {
return mathutil.ToUint64(v)
}
// BoolString convert bool to string
func BoolString(bl bool) string {
return strconv.FormatBool(bl)
@@ -166,12 +177,12 @@ func ToKind(val any, kind reflect.Kind, fbFunc func(val any) (any, error)) (newV
newVal = dstV
}
case reflect.Uint:
var dstV uint64
var dstV uint
if dstV, err = mathutil.ToUint(val); err == nil {
newVal = uint(dstV)
newVal = dstV
}
case reflect.Uint8:
var dstV uint64
var dstV uint
if dstV, err = mathutil.ToUint(val); err == nil {
if dstV > math.MaxUint8 {
return nil, fmt.Errorf("value overflow uint8. val: %v", val)
@@ -179,7 +190,7 @@ func ToKind(val any, kind reflect.Kind, fbFunc func(val any) (any, error)) (newV
newVal = uint8(dstV)
}
case reflect.Uint16:
var dstV uint64
var dstV uint
if dstV, err = mathutil.ToUint(val); err == nil {
if dstV > math.MaxUint16 {
return nil, fmt.Errorf("value overflow uint16. val: %v", val)
@@ -187,7 +198,7 @@ func ToKind(val any, kind reflect.Kind, fbFunc func(val any) (any, error)) (newV
newVal = uint16(dstV)
}
case reflect.Uint32:
var dstV uint64
var dstV uint
if dstV, err = mathutil.ToUint(val); err == nil {
if dstV > math.MaxUint32 {
return nil, fmt.Errorf("value overflow uint32. val: %v", val)
@@ -196,7 +207,7 @@ func ToKind(val any, kind reflect.Kind, fbFunc func(val any) (any, error)) (newV
}
case reflect.Uint64:
var dstV uint64
if dstV, err = mathutil.ToUint(val); err == nil {
if dstV, err = mathutil.ToUint64(val); err == nil {
newVal = dstV
}
case reflect.Float32:
+1
View File
@@ -1,3 +1,4 @@
// Package encodes provide some util for encode/decode data
package encodes
import (
+4 -1
View File
@@ -57,7 +57,8 @@ func TempDir(dir, pattern string) (string, error) {
// MustSave create file and write contents to file, panic on error.
//
// data type allow: string, []byte, io.Reader
// - data type allow: string, []byte, io.Reader
//
// default option see NewOpenOption()
func MustSave(filePath string, data any, optFns ...OpenOptionFunc) {
basefn.MustOK(SaveFile(filePath, data, optFns...))
@@ -65,6 +66,8 @@ func MustSave(filePath string, data any, optFns ...OpenOptionFunc) {
// SaveFile create file and write contents to file. will auto create dir.
//
// - data type allow: string, []byte, io.Reader
//
// default option see NewOpenOption()
func SaveFile(filePath string, data any, optFns ...OpenOptionFunc) error {
opt := NewOpenOption(optFns...)
+1 -1
View File
@@ -46,7 +46,7 @@ func SafeRun(fn func()) (err error) {
return nil
}
// SafeRun sync run a func with error.
// SafeRunWithError sync run a func with error.
// If the func panics, the panic value is returned as an error.
func SafeRunWithError(fn func() error) (err error) {
defer func() {
+39 -9
View File
@@ -1,11 +1,10 @@
package checkfn
import (
"bytes"
"fmt"
"reflect"
"strings"
"github.com/gookit/goutil/reflects"
)
// IsNil value check
@@ -13,15 +12,46 @@ func IsNil(v any) bool {
if v == nil {
return true
}
return reflects.IsNil(reflect.ValueOf(v))
rv := reflect.ValueOf(v)
switch rv.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
return rv.IsNil()
default:
return false
}
}
// IsEmpty value check
func IsEmpty(v any) bool {
if v == nil {
// IsSimpleKind kind in: string, bool, intX, uintX, floatX
func IsSimpleKind(k reflect.Kind) bool {
if reflect.String == k {
return true
}
return reflects.IsEmpty(reflect.ValueOf(v))
return k > reflect.Invalid && k <= reflect.Float64
}
// 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
}
bs1, ok := src.([]byte)
if !ok {
return reflect.DeepEqual(src, dst)
}
bs2, ok := dst.([]byte)
if !ok {
return false
}
if bs1 == nil || bs2 == nil {
return bs1 == nil && bs2 == nil
}
return bytes.Equal(bs1, bs2)
}
// Contains try loop over the data check if the data includes the element.
@@ -57,7 +87,7 @@ func Contains(data, elem any) (valid, found bool) {
if dataKind == reflect.Map {
mapKeys := dataRv.MapKeys()
for i := 0; i < len(mapKeys); i++ {
if reflects.IsEqual(mapKeys[i].Interface(), elem) {
if IsEqual(mapKeys[i].Interface(), elem) {
return true, true
}
}
@@ -70,7 +100,7 @@ func Contains(data, elem any) (valid, found bool) {
}
for i := 0; i < dataRv.Len(); i++ {
if reflects.IsEqual(dataRv.Index(i).Interface(), elem) {
if IsEqual(dataRv.Index(i).Interface(), elem) {
return true, true
}
}
-1
View File
@@ -16,7 +16,6 @@ func Environ() map[string]string {
for _, str := range envList {
nodes := strings.SplitN(str, "=", 2)
if len(nodes) < 2 {
envMap[nodes[0]] = ""
} else {
+118
View File
@@ -2,9 +2,13 @@ package comfunc
import (
"fmt"
"reflect"
"strconv"
"strings"
"time"
"github.com/gookit/goutil/comdef"
"github.com/gookit/goutil/internal/checkfn"
)
// Bool try to convert type to bool
@@ -60,3 +64,117 @@ func FormatWithArgs(fmtAndArgs []any) string {
}
return fmt.Sprint(fmtAndArgs...)
}
// ConvOption convert options
type ConvOption struct {
// if ture: value is nil, will return convert error;
// if false(default): value is nil, will convert to zero value
NilAsFail bool
// HandlePtr auto convert ptr type(int,float,string) value. eg: *int to int
// - if true: will use real type try convert. default is false
// - NOTE: current T type's ptr is default support.
HandlePtr bool
// set custom fallback convert func for not supported type.
UserConvFn comdef.ToStringFunc
}
// ConvOptionFn convert option func
type ConvOptionFn func(opt *ConvOption)
// StrBySprintFn convert any value to string by fmt.Sprint
var StrBySprintFn = func(v any) (string, error) {
return fmt.Sprint(v), nil
}
// WithHandlePtr set ConvOption.HandlePtr option
func WithHandlePtr(opt *ConvOption) {
opt.HandlePtr = true
}
// WithUserConvFn set ConvOption.UserConvFn option
func WithUserConvFn(fn comdef.ToStringFunc) ConvOptionFn {
return func(opt *ConvOption) {
opt.UserConvFn = fn
}
}
// NewConvOption create a new ConvOption
func NewConvOption(optFns ...ConvOptionFn) *ConvOption {
opt := &ConvOption{}
opt.WithOption(optFns...)
return opt
}
// WithOption set convert option
func (opt *ConvOption) WithOption(optFns ...ConvOptionFn) {
for _, fn := range optFns {
if fn != nil {
fn(opt)
}
}
}
// ToStringWith try to convert value to string. can with some option func, more see ConvOption.
func ToStringWith(in any, optFns ...ConvOptionFn) (str string, err error) {
opt := NewConvOption(optFns...)
if !opt.NilAsFail && in == nil {
return "", nil
}
switch value := in.(type) {
case int:
str = strconv.Itoa(value)
case int8:
str = strconv.Itoa(int(value))
case int16:
str = strconv.Itoa(int(value))
case int32: // same as `rune`
str = strconv.Itoa(int(value))
case int64:
str = strconv.FormatInt(value, 10)
case uint:
str = strconv.FormatUint(uint64(value), 10)
case uint8:
str = strconv.FormatUint(uint64(value), 10)
case uint16:
str = strconv.FormatUint(uint64(value), 10)
case uint32:
str = strconv.FormatUint(uint64(value), 10)
case uint64:
str = strconv.FormatUint(value, 10)
case float32:
str = strconv.FormatFloat(float64(value), 'f', -1, 32)
case float64:
str = strconv.FormatFloat(value, 'f', -1, 64)
case bool:
str = strconv.FormatBool(value)
case string:
str = value
case *string:
str = *value
case []byte:
str = string(value)
case time.Duration:
str = strconv.FormatInt(int64(value), 10)
case fmt.Stringer:
str = value.String()
case error:
str = value.Error()
default:
if opt.HandlePtr {
if rv := reflect.ValueOf(in); rv.Kind() == reflect.Pointer {
rv = rv.Elem()
if checkfn.IsSimpleKind(rv.Kind()) {
return ToStringWith(rv.Interface(), optFns...)
}
}
}
if opt.UserConvFn != nil {
str, err = opt.UserConvFn(in)
} else {
err = comdef.ErrConvType
}
}
return
}
+31 -15
View File
@@ -20,8 +20,14 @@ import (
"strings"
)
// SepChar separator char
const SepChar = "|"
const (
// SepChar separator char split var name and default value
SepChar = "|"
VarLeft = "${" // default var left format chars
VarRight = "}" // default var right format chars
mustPrefix = '?' // must prefix char
)
// ParseOptFn option func
type ParseOptFn func(o *ParseOpts)
@@ -34,8 +40,15 @@ type ParseOpts struct {
ParseFn func(string) (string, error)
// Regexp custom expression regex.
Regexp *regexp.Regexp
// Keyword check chars for expression. default is "${"
Keyword string
// var format chars for expression.
// default left="${", right="}"
VarLeft, VarRight string
}
func (opt *ParseOpts) useDefaultRegex() {
opt.Regexp = envRegex
opt.VarLeft = VarLeft
opt.VarRight = VarRight
}
// must add "?" - To ensure that there is no greedy match
@@ -71,15 +84,12 @@ type Parser struct {
// New create a new Parser
func New(optFns ...ParseOptFn) *Parser {
opts := &ParseOpts{
Getter: os.Getenv,
Regexp: envRegex,
Keyword: "${",
}
opts := &ParseOpts{Getter: os.Getenv}
opts.useDefaultRegex()
for _, fn := range optFns {
fn(opts)
}
return &Parser{ParseOpts: *opts}
}
@@ -92,13 +102,19 @@ func New(optFns ...ParseOptFn) *Parser {
// ${var_name | ?error} With error on value is empty.
func (p *Parser) Parse(val string) (newVal string, err error) {
if p.Regexp == nil {
p.Regexp = envRegex
p.useDefaultRegex()
}
if p.Keyword != "" && !strings.Contains(val, p.Keyword) {
times := strings.Count(val, p.VarLeft)
if times == 0 {
return val, nil
}
// enhance: see https://github.com/gookit/goutil/issues/135
if times == 1 && strings.HasPrefix(val, p.VarLeft) && strings.HasSuffix(val, p.VarRight) {
return p.parseOne(val)
}
// parse expression
newVal = p.Regexp.ReplaceAllStringFunc(val, func(s string) string {
if err != nil {
@@ -116,11 +132,11 @@ func (p *Parser) parseOne(eVar string) (val string, err error) {
return p.ParseFn(eVar)
}
// eVar like "${NotExist|defValue}", first remove "${" and "}", then split it
// like "${NotExist | defValue}". first remove "${" and "}", then split it
ss := strings.SplitN(eVar[2:len(eVar)-1], SepChar, 2)
var name, def string
// with default value. ${NotExist|defValue}
// with default value.
if len(ss) == 2 {
name, def = strings.TrimSpace(ss[0]), strings.TrimSpace(ss[1])
} else {
@@ -131,7 +147,7 @@ func (p *Parser) parseOne(eVar string) (val string, err error) {
val = p.Getter(name)
if val == "" && def != "" {
// check def is "?error"
if def[0] == '?' {
if def[0] == mustPrefix {
msg := "value is required for var: " + name
if len(def) > 1 {
msg = def[1:]
+21 -3
View File
@@ -20,8 +20,8 @@ func (d Data) Has(key string) bool {
return ok
}
// IsEmtpy if the data map
func (d Data) IsEmtpy() bool {
// IsEmpty if the data map
func (d Data) IsEmpty() bool {
return len(d) == 0
}
@@ -127,13 +127,21 @@ func (d Data) Int64(key string) int64 {
}
// Uint value get
func (d Data) Uint(key string) uint64 {
func (d Data) Uint(key string) uint {
if val, ok := d.GetByPath(key); ok {
return mathutil.QuietUint(val)
}
return 0
}
// Uint64 value get
func (d Data) Uint64(key string) uint64 {
if val, ok := d.GetByPath(key); ok {
return mathutil.QuietUint64(val)
}
return 0
}
// Str value get by key
func (d Data) Str(key string) string {
if val, ok := d.GetByPath(key); ok {
@@ -223,6 +231,16 @@ func (d Data) Sub(key string) Data {
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)
}
// Keys of the data map
func (d Data) Keys() []string {
keys := make([]string, 0, len(d))
+22 -2
View File
@@ -16,8 +16,7 @@ const (
KeySepChar = '.'
)
// SimpleMerge simple merge two data map by string key.
// will merge the src to dst map
// SimpleMerge simple merge two data map by string key. will merge the src to dst map
func SimpleMerge(src, dst map[string]any) map[string]any {
if len(src) == 0 {
return dst
@@ -66,6 +65,27 @@ func MergeStringMap(src, dst map[string]string, ignoreCase bool) map[string]stri
return dst
}
// MergeMultiSMap quick merge multi string-map data.
func MergeMultiSMap(mps ...map[string]string) map[string]string {
newMp := make(map[string]string)
for _, mp := range mps {
for k, v := range mp {
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 {
if val == "" {
delete(sm, key)
}
}
return sm
}
// MakeByPath build new value by key names
//
// Example:
+14
View File
@@ -105,6 +105,20 @@ func (m SMap) Strings(key string) (ss []string) {
return
}
// IfExist key, then call the fn with value.
func (m SMap) 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)) {
if val, ok := m[key]; ok && val != "" {
fn(val)
}
}
// Keys of the string-map
func (m SMap) Keys() []string {
keys := make([]string, 0, len(m))
+382 -259
View File
@@ -3,11 +3,14 @@ package mathutil
import (
"fmt"
"math"
"reflect"
"strconv"
"strings"
"time"
"github.com/gookit/goutil/comdef"
"github.com/gookit/goutil/internal/checkfn"
"github.com/gookit/goutil/internal/comfunc"
)
// ToIntFunc convert value to int
@@ -17,19 +20,76 @@ type ToIntFunc func(any) (int, error)
type ToInt64Func func(any) (int64, error)
// ToUintFunc convert value to uint
type ToUintFunc func(any) (uint64, error)
type ToUintFunc func(any) (uint, error)
// ToUint64Func convert value to uint
type ToUint64Func func(any) (uint64, error)
// ToFloatFunc convert value to float
type ToFloatFunc func(any) (float64, error)
// ToTypeFunc convert value to defined type
type ToTypeFunc[T any] func(any) (T, error)
// ConvOption convert options
type ConvOption[T any] struct {
// if ture: value is nil, will return convert error;
// if false(default): value is nil, will convert to zero value
NilAsFail bool
// HandlePtr auto convert ptr type(int,float,string) value. eg: *int to int
// - if true: will use real type try convert. default is false
// - NOTE: current T type's ptr is default support.
HandlePtr bool
// set custom fallback convert func for not supported type.
UserConvFn ToTypeFunc[T]
}
// NewConvOption create a new ConvOption
func NewConvOption[T any](optFns ...ConvOptionFn[T]) *ConvOption[T] {
opt := &ConvOption[T]{}
opt.WithOption(optFns...)
return opt
}
// WithOption set convert option
func (opt *ConvOption[T]) WithOption(optFns ...ConvOptionFn[T]) {
for _, fn := range optFns {
if fn != nil {
fn(opt)
}
}
}
// ConvOptionFn convert option func
type ConvOptionFn[T any] func(opt *ConvOption[T])
// WithNilAsFail set ConvOption.NilAsFail option
//
// Example:
//
// ToIntWithFunc(val, mathutil.WithNilAsFail[int])
func WithNilAsFail[T any](opt *ConvOption[T]) {
opt.NilAsFail = true
}
// WithHandlePtr set ConvOption.HandlePtr option
func WithHandlePtr[T any](opt *ConvOption[T]) {
opt.HandlePtr = true
}
// WithUserConvFn set ConvOption.UserConvFn option
func WithUserConvFn[T any](fn ToTypeFunc[T]) ConvOptionFn[T] {
return func(opt *ConvOption[T]) {
opt.UserConvFn = fn
}
}
/*************************************************************
* convert value to int
*************************************************************/
// Int convert value to int
func Int(in any) (int, error) {
return ToInt(in)
}
func Int(in any) (int, error) { return ToInt(in) }
// SafeInt convert value to int, will ignore error
func SafeInt(in any) int {
@@ -38,12 +98,10 @@ func SafeInt(in any) int {
}
// QuietInt convert value to int, will ignore error
func QuietInt(in any) int {
return SafeInt(in)
}
func QuietInt(in any) int { return SafeInt(in) }
// MustInt convert value to int, will panic on error
func MustInt(in any) int {
// IntOrPanic convert value to int, will panic on error
func IntOrPanic(in any) int {
val, err := ToInt(in)
if err != nil {
panic(err)
@@ -51,19 +109,15 @@ func MustInt(in any) int {
return val
}
// IntOrPanic convert value to int, will panic on error
func IntOrPanic(in any) int {
return MustInt(in)
}
// MustInt convert value to int, will panic on error
func MustInt(in any) int { return IntOrPanic(in) }
// IntOrDefault convert value to int, return defaultVal on failed
func IntOrDefault(in any, defVal int) int {
return IntOr(in, defVal)
}
func IntOrDefault(in any, defVal int) int { return IntOr(in, defVal) }
// IntOr convert value to int, return defaultVal on failed
func IntOr(in any, defVal int) int {
val, err := ToIntWithFunc(in, nil)
val, err := ToIntWith(in)
if err != nil {
return defVal
}
@@ -71,20 +125,28 @@ func IntOr(in any, defVal int) int {
}
// IntOrErr convert value to int, return error on failed
func IntOrErr(in any) (iVal int, err error) {
return ToIntWithFunc(in, nil)
}
func IntOrErr(in any) (int, error) { return ToIntWith(in) }
// ToInt convert value to int, return error on failed
func ToInt(in any) (iVal int, err error) {
return ToIntWithFunc(in, nil)
}
func ToInt(in any) (int, error) { return ToIntWith(in) }
// ToIntWith convert value to int, can with some option func.
//
// Example:
//
// ToIntWithFunc(val, mathutil.WithNilAsFail, mathutil.WithUserConvFn(func(in any) (int, error) {
// })
func ToIntWith(in any, optFns ...ConvOptionFn[int]) (iVal int, err error) {
opt := NewConvOption[int](optFns...)
if !opt.NilAsFail && in == nil {
return 0, nil
}
// ToIntWithFunc convert value to int, will call usrFn on value type not supported.
func ToIntWithFunc(in any, usrFn ToIntFunc) (iVal int, err error) {
switch tVal := in.(type) {
case int:
iVal = tVal
case *int: // default support int ptr type
iVal = *tVal
case int8:
iVal = int(tVal)
case int16:
@@ -131,7 +193,7 @@ func ToIntWithFunc(in any, usrFn ToIntFunc) (iVal int, err error) {
}
case string:
iVal, err = strconv.Atoi(strings.TrimSpace(tVal))
case interface{ Int64() (int64, error) }: // eg: json.Number
case comdef.Int64able: // eg: json.Number
var i64 int64
if i64, err = tVal.Int64(); err == nil {
if i64 > math.MaxInt32 {
@@ -141,11 +203,19 @@ func ToIntWithFunc(in any, usrFn ToIntFunc) (iVal int, err error) {
}
}
default:
if usrFn != nil {
return usrFn(in)
} else {
err = comdef.ErrConvType
if opt.HandlePtr {
if rv := reflect.ValueOf(in); rv.Kind() == reflect.Pointer {
rv = rv.Elem()
if checkfn.IsSimpleKind(rv.Kind()) {
return ToIntWith(rv.Interface(), optFns...)
}
}
}
if opt.UserConvFn != nil {
return opt.UserConvFn(in)
}
err = comdef.ErrConvType
}
return
}
@@ -165,29 +235,127 @@ func StrIntOr(s string, defVal int) int {
return iVal
}
/*************************************************************
* convert value to int64
*************************************************************/
// Int64 convert value to int64, return error on failed
func Int64(in any) (int64, error) { return ToInt64(in) }
// SafeInt64 convert value to int64, will ignore error
func SafeInt64(in any) int64 {
i64, _ := ToInt64With(in)
return i64
}
// QuietInt64 convert value to int64, will ignore error
func QuietInt64(in any) int64 { return SafeInt64(in) }
// MustInt64 convert value to int64, will panic on error
func MustInt64(in any) int64 {
i64, err := ToInt64With(in)
if err != nil {
panic(err)
}
return i64
}
// Int64OrDefault convert value to int64, return default val on failed
func Int64OrDefault(in any, defVal int64) int64 { return Int64Or(in, defVal) }
// Int64Or convert value to int64, return default val on failed
func Int64Or(in any, defVal int64) int64 {
i64, err := ToInt64With(in)
if err != nil {
return defVal
}
return i64
}
// ToInt64 convert value to int64, return error on failed
func ToInt64(in any) (int64, error) { return ToInt64With(in) }
// Int64OrErr convert value to int64, return error on failed
func Int64OrErr(in any) (int64, error) { return ToInt64With(in) }
// ToInt64With try to convert value to int64. can with some option func, more see ConvOption.
func ToInt64With(in any, optFns ...ConvOptionFn[int64]) (i64 int64, err error) {
opt := NewConvOption(optFns...)
if !opt.NilAsFail && in == nil {
return 0, nil
}
switch tVal := in.(type) {
case string:
i64, err = strconv.ParseInt(strings.TrimSpace(tVal), 10, 0)
case int:
i64 = int64(tVal)
case int8:
i64 = int64(tVal)
case int16:
i64 = int64(tVal)
case int32:
i64 = int64(tVal)
case int64:
i64 = tVal
case *int64: // default support int64 ptr type
i64 = *tVal
case uint:
i64 = int64(tVal)
case uint8:
i64 = int64(tVal)
case uint16:
i64 = int64(tVal)
case uint32:
i64 = int64(tVal)
case uint64:
i64 = int64(tVal)
case float32:
i64 = int64(tVal)
case float64:
i64 = int64(tVal)
case time.Duration:
i64 = int64(tVal)
case comdef.Int64able: // eg: json.Number
i64, err = tVal.Int64()
default:
if opt.HandlePtr {
if rv := reflect.ValueOf(in); rv.Kind() == reflect.Pointer {
rv = rv.Elem()
if checkfn.IsSimpleKind(rv.Kind()) {
return ToInt64With(rv.Interface(), optFns...)
}
}
}
if opt.UserConvFn != nil {
i64, err = opt.UserConvFn(in)
} else {
err = comdef.ErrConvType
}
}
return
}
/*************************************************************
* convert value to uint
*************************************************************/
// Uint convert any to uint, return error on failed
func Uint(in any) (uint64, error) {
return ToUint(in)
}
func Uint(in any) (uint, error) { return ToUint(in) }
// SafeUint convert any to uint, will ignore error
func SafeUint(in any) uint64 {
func SafeUint(in any) uint {
val, _ := ToUint(in)
return val
}
// QuietUint convert any to uint, will ignore error
func QuietUint(in any) uint64 {
return SafeUint(in)
}
func QuietUint(in any) uint { return SafeUint(in) }
// MustUint convert any to uint, will panic on error
func MustUint(in any) uint64 {
val, err := ToUintWithFunc(in, nil)
func MustUint(in any) uint {
val, err := ToUintWith(in)
if err != nil {
panic(err)
}
@@ -195,13 +363,11 @@ func MustUint(in any) uint64 {
}
// UintOrDefault convert any to uint, return default val on failed
func UintOrDefault(in any, defVal uint64) uint64 {
return UintOr(in, defVal)
}
func UintOrDefault(in any, defVal uint) uint { return UintOr(in, defVal) }
// UintOr convert any to uint, return default val on failed
func UintOr(in any, defVal uint64) uint64 {
val, err := ToUintWithFunc(in, nil)
func UintOr(in any, defVal uint) uint {
val, err := ToUintWith(in)
if err != nil {
return defVal
}
@@ -209,17 +375,124 @@ func UintOr(in any, defVal uint64) uint64 {
}
// UintOrErr convert value to uint, return error on failed
func UintOrErr(in any) (uint64, error) {
return ToUintWithFunc(in, nil)
}
func UintOrErr(in any) (uint, error) { return ToUintWith(in) }
// ToUint convert value to uint, return error on failed
func ToUint(in any) (u64 uint64, err error) {
return ToUintWithFunc(in, nil)
func ToUint(in any) (u64 uint, err error) { return ToUintWith(in) }
// ToUintWith try to convert value to uint. can with some option func, more see ConvOption.
func ToUintWith(in any, optFns ...ConvOptionFn[uint]) (uVal uint, err error) {
opt := NewConvOption(optFns...)
if !opt.NilAsFail && in == nil {
return 0, nil
}
switch tVal := in.(type) {
case int:
uVal = uint(tVal)
case int8:
uVal = uint(tVal)
case int16:
uVal = uint(tVal)
case int32:
uVal = uint(tVal)
case int64:
uVal = uint(tVal)
case uint:
uVal = tVal
case *uint: // default support uint ptr type
uVal = *tVal
case uint8:
uVal = uint(tVal)
case uint16:
uVal = uint(tVal)
case uint32:
uVal = uint(tVal)
case uint64:
uVal = uint(tVal)
case float32:
uVal = uint(tVal)
case float64:
uVal = uint(tVal)
case time.Duration:
uVal = uint(tVal)
case comdef.Int64able: // eg: json.Number
var i64 int64
i64, err = tVal.Int64()
uVal = uint(i64)
case string:
var u64 uint64
u64, err = strconv.ParseUint(strings.TrimSpace(tVal), 10, 0)
uVal = uint(u64)
default:
if opt.HandlePtr {
if rv := reflect.ValueOf(in); rv.Kind() == reflect.Pointer {
rv = rv.Elem()
if checkfn.IsSimpleKind(rv.Kind()) {
return ToUintWith(rv.Interface(), optFns...)
}
}
}
if opt.UserConvFn != nil {
uVal, err = opt.UserConvFn(in)
} else {
err = comdef.ErrConvType
}
}
return
}
// ToUintWithFunc convert value to uint, will call usrFn on value type not supported.
func ToUintWithFunc(in any, usrFn ToUintFunc) (u64 uint64, err error) {
/*************************************************************
* convert value to uint64
*************************************************************/
// Uint64 convert any to uint64, return error on failed
func Uint64(in any) (uint64, error) { return ToUint64(in) }
// QuietUint64 convert any to uint64, will ignore error
func QuietUint64(in any) uint64 { return SafeUint64(in) }
// SafeUint64 convert any to uint64, will ignore error
func SafeUint64(in any) uint64 {
val, _ := ToUint64(in)
return val
}
// MustUint64 convert any to uint64, will panic on error
func MustUint64(in any) uint64 {
val, err := ToUint64With(in)
if err != nil {
panic(err)
}
return val
}
// Uint64OrDefault convert any to uint64, return default val on failed
func Uint64OrDefault(in any, defVal uint64) uint64 { return Uint64Or(in, defVal) }
// Uint64Or convert any to uint64, return default val on failed
func Uint64Or(in any, defVal uint64) uint64 {
val, err := ToUint64With(in)
if err != nil {
return defVal
}
return val
}
// Uint64OrErr convert value to uint64, return error on failed
func Uint64OrErr(in any) (uint64, error) { return ToUint64With(in) }
// ToUint64 convert value to uint64, return error on failed
func ToUint64(in any) (uint64, error) { return ToUint64With(in) }
// ToUint64With try to convert value to uint64. can with some option func, more see ConvOption.
func ToUint64With(in any, optFns ...ConvOptionFn[uint64]) (u64 uint64, err error) {
opt := NewConvOption(optFns...)
if !opt.NilAsFail && in == nil {
return 0, nil
}
switch tVal := in.(type) {
case int:
u64 = uint64(tVal)
@@ -241,21 +514,32 @@ func ToUintWithFunc(in any, usrFn ToUintFunc) (u64 uint64, err error) {
u64 = uint64(tVal)
case uint64:
u64 = tVal
case *uint64: // default support uint64 ptr type
u64 = *tVal
case float32:
u64 = uint64(tVal)
case float64:
u64 = uint64(tVal)
case time.Duration:
u64 = uint64(tVal)
case interface{ Int64() (int64, error) }: // eg: json.Number
case comdef.Int64able: // eg: json.Number
var i64 int64
i64, err = tVal.Int64()
u64 = uint64(i64)
case string:
u64, err = strconv.ParseUint(strings.TrimSpace(tVal), 10, 0)
default:
if usrFn != nil {
u64, err = usrFn(in)
if opt.HandlePtr {
if rv := reflect.ValueOf(in); rv.Kind() == reflect.Pointer {
rv = rv.Elem()
if checkfn.IsSimpleKind(rv.Kind()) {
return ToUint64With(rv.Interface(), optFns...)
}
}
}
if opt.UserConvFn != nil {
u64, err = opt.UserConvFn(in)
} else {
err = comdef.ErrConvType
}
@@ -264,124 +548,24 @@ func ToUintWithFunc(in any, usrFn ToUintFunc) (u64 uint64, err error) {
}
/*************************************************************
* convert value to int64
*************************************************************/
// Int64 convert value to int64, return error on failed
func Int64(in any) (int64, error) {
return ToInt64(in)
}
// SafeInt64 convert value to int64, will ignore error
func SafeInt64(in any) int64 {
i64, _ := ToInt64WithFunc(in, nil)
return i64
}
// QuietInt64 convert value to int64, will ignore error
func QuietInt64(in any) int64 {
return SafeInt64(in)
}
// MustInt64 convert value to int64, will panic on error
func MustInt64(in any) int64 {
i64, err := ToInt64WithFunc(in, nil)
if err != nil {
panic(err)
}
return i64
}
// Int64OrDefault convert value to int64, return default val on failed
func Int64OrDefault(in any, defVal int64) int64 {
return Int64Or(in, defVal)
}
// Int64Or convert value to int64, return default val on failed
func Int64Or(in any, defVal int64) int64 {
i64, err := ToInt64WithFunc(in, nil)
if err != nil {
return defVal
}
return i64
}
// Int64OrErr convert value to int64, return error on failed
func Int64OrErr(in any) (int64, error) {
return ToInt64(in)
}
// ToInt64 convert value to int64, return error on failed
func ToInt64(in any) (i64 int64, err error) {
return ToInt64WithFunc(in, nil)
}
// ToInt64WithFunc convert value to int64, will call usrFn on value type not supported.
func ToInt64WithFunc(in any, usrFn ToInt64Func) (i64 int64, err error) {
switch tVal := in.(type) {
case string:
i64, err = strconv.ParseInt(strings.TrimSpace(tVal), 10, 0)
case int:
i64 = int64(tVal)
case int8:
i64 = int64(tVal)
case int16:
i64 = int64(tVal)
case int32:
i64 = int64(tVal)
case int64:
i64 = tVal
case uint:
i64 = int64(tVal)
case uint8:
i64 = int64(tVal)
case uint16:
i64 = int64(tVal)
case uint32:
i64 = int64(tVal)
case uint64:
i64 = int64(tVal)
case float32:
i64 = int64(tVal)
case float64:
i64 = int64(tVal)
case time.Duration:
i64 = int64(tVal)
case interface{ Int64() (int64, error) }: // eg: json.Number
i64, err = tVal.Int64()
default:
if usrFn != nil {
i64, err = usrFn(in)
} else {
err = comdef.ErrConvType
}
}
return
}
/*************************************************************
* convert value to float
* convert value to float64
*************************************************************/
// QuietFloat convert value to float64, will ignore error. alias of SafeFloat
func QuietFloat(in any) float64 {
return SafeFloat(in)
}
func QuietFloat(in any) float64 { return SafeFloat(in) }
// SafeFloat convert value to float64, will ignore error
func SafeFloat(in any) float64 {
val, _ := ToFloatWithFunc(in, nil)
val, _ := ToFloatWith(in)
return val
}
// FloatOrPanic convert value to float64, will panic on error
func FloatOrPanic(in any) float64 {
return MustFloat(in)
}
func FloatOrPanic(in any) float64 { return MustFloat(in) }
// MustFloat convert value to float64, will panic on error
func MustFloat(in any) float64 {
val, err := ToFloatWithFunc(in, nil)
val, err := ToFloatWith(in)
if err != nil {
panic(err)
}
@@ -389,13 +573,11 @@ func MustFloat(in any) float64 {
}
// FloatOrDefault convert value to float64, will return default value on error
func FloatOrDefault(in any, defVal float64) float64 {
return FloatOr(in, defVal)
}
func FloatOrDefault(in any, defVal float64) float64 { return FloatOr(in, defVal) }
// FloatOr convert value to float64, will return default value on error
func FloatOr(in any, defVal float64) float64 {
val, err := ToFloatWithFunc(in, nil)
val, err := ToFloatWith(in)
if err != nil {
return defVal
}
@@ -403,22 +585,21 @@ func FloatOr(in any, defVal float64) float64 {
}
// Float convert value to float64, return error on failed
func Float(in any) (float64, error) {
return ToFloatWithFunc(in, nil)
}
func Float(in any) (float64, error) { return ToFloatWith(in) }
// FloatOrErr convert value to float64, return error on failed
func FloatOrErr(in any) (float64, error) {
return ToFloatWithFunc(in, nil)
}
func FloatOrErr(in any) (float64, error) { return ToFloatWith(in) }
// ToFloat convert value to float64, return error on failed
func ToFloat(in any) (f64 float64, err error) {
return ToFloatWithFunc(in, nil)
}
func ToFloat(in any) (float64, error) { return ToFloatWith(in) }
// ToFloatWith try to convert value to float64. can with some option func, more see ConvOption.
func ToFloatWith(in any, optFns ...ConvOptionFn[float64]) (f64 float64, err error) {
opt := NewConvOption(optFns...)
if !opt.NilAsFail && in == nil {
return 0, nil
}
// ToFloatWithFunc convert value to float64, will call usrFn if value type not supported.
func ToFloatWithFunc(in any, usrFn ToFloatFunc) (f64 float64, err error) {
switch tVal := in.(type) {
case string:
f64, err = strconv.ParseFloat(strings.TrimSpace(tVal), 64)
@@ -446,13 +627,24 @@ func ToFloatWithFunc(in any, usrFn ToFloatFunc) (f64 float64, err error) {
f64 = float64(tVal)
case float64:
f64 = tVal
case *float64: // default support float64 ptr type
f64 = *tVal
case time.Duration:
f64 = float64(tVal)
case interface{ Float64() (float64, error) }: // eg: json.Number
case comdef.Float64able: // eg: json.Number
f64, err = tVal.Float64()
default:
if usrFn != nil {
f64, err = usrFn(in)
if opt.HandlePtr {
if rv := reflect.ValueOf(in); rv.Kind() == reflect.Pointer {
rv = rv.Elem()
if checkfn.IsSimpleKind(rv.Kind()) {
return ToFloatWith(rv.Interface(), optFns...)
}
}
}
if opt.UserConvFn != nil {
f64, err = opt.UserConvFn(in)
} else {
err = comdef.ErrConvType
}
@@ -466,7 +658,7 @@ func ToFloatWithFunc(in any, usrFn ToFloatFunc) (f64 float64, err error) {
// MustString convert intX/floatX value to string, will panic on error
func MustString(val any) string {
str, err := ToStringWithFunc(val, nil)
str, err := ToStringWith(val)
if err != nil {
panic(err)
}
@@ -477,13 +669,11 @@ func MustString(val any) string {
func StringOrPanic(val any) string { return MustString(val) }
// StringOrDefault convert intX/floatX value to string, will return default value on error
func StringOrDefault(val any, defVal string) string {
return StringOr(val, defVal)
}
func StringOrDefault(val any, defVal string) string { return StringOr(val, defVal) }
// StringOr convert intX/floatX value to string, will return default value on error
func StringOr(val any, defVal string) string {
str, err := ToStringWithFunc(val, nil)
str, err := ToStringWith(val)
if err != nil {
return defVal
}
@@ -491,19 +681,13 @@ func StringOr(val any, defVal string) string {
}
// ToString convert intX/floatX value to string, return error on failed
func ToString(val any) (string, error) {
return ToStringWithFunc(val, nil)
}
func ToString(val any) (string, error) { return ToStringWith(val) }
// StringOrErr convert intX/floatX value to string, return error on failed
func StringOrErr(val any) (string, error) {
return ToStringWithFunc(val, nil)
}
func StringOrErr(val any) (string, error) { return ToStringWith(val) }
// QuietString convert intX/floatX value to string, other type convert by fmt.Sprint
func QuietString(val any) string {
return SafeString(val)
}
func QuietString(val any) string { return SafeString(val) }
// String convert intX/floatX value to string, other type convert by fmt.Sprint
func String(val any) string {
@@ -520,76 +704,15 @@ func SafeString(val any) string {
// TryToString try convert intX/floatX value to string
//
// if defaultAsErr is False, will use fmt.Sprint convert other type
func TryToString(val any, defaultAsErr bool) (str string, err error) {
var usrFn comdef.ToStringFunc
func TryToString(val any, defaultAsErr bool) (string, error) {
var optFn comfunc.ConvOptionFn
if !defaultAsErr {
usrFn = func(v any) (string, error) {
if val == nil {
return "", nil
}
return fmt.Sprint(v), nil
}
optFn = comfunc.WithUserConvFn(comfunc.StrBySprintFn)
}
return ToStringWithFunc(val, usrFn)
return ToStringWith(val, optFn)
}
// ToStringWithFunc try convert intX/floatX value to string, will call usrFn if value type not supported.
//
// if defaultAsErr is False, will use fmt.Sprint convert other type
func ToStringWithFunc(val any, usrFn comdef.ToStringFunc) (str string, err error) {
switch value := val.(type) {
case int:
str = strconv.Itoa(value)
case int8:
str = strconv.Itoa(int(value))
case int16:
str = strconv.Itoa(int(value))
case int32: // same as `rune`
str = strconv.Itoa(int(value))
case int64:
str = strconv.FormatInt(value, 10)
case uint:
str = strconv.FormatUint(uint64(value), 10)
case uint8:
str = strconv.FormatUint(uint64(value), 10)
case uint16:
str = strconv.FormatUint(uint64(value), 10)
case uint32:
str = strconv.FormatUint(uint64(value), 10)
case uint64:
str = strconv.FormatUint(value, 10)
case float32:
str = strconv.FormatFloat(float64(value), 'f', -1, 32)
case float64:
str = strconv.FormatFloat(value, 'f', -1, 64)
case time.Duration:
str = strconv.FormatInt(int64(value), 10)
case string:
str = value
case fmt.Stringer:
str = value.String()
default:
if usrFn != nil {
str, err = usrFn(val)
} else {
err = comdef.ErrConvType
}
}
return
}
// Percent returns a values percent of the total
func Percent(val, total int) float64 {
if total == 0 {
return float64(0)
}
return (float64(val) / float64(total)) * 100
}
// ElapsedTime calc elapsed time 计算运行时间消耗 单位 ms(毫秒)
//
// Deprecated: use timex.ElapsedTime()
func ElapsedTime(startTime time.Time) string {
return fmt.Sprintf("%.3f", time.Since(startTime).Seconds()*1000)
// ToStringWith try to convert value to string. can with some option func, more see comfunc.ConvOption.
func ToStringWith(in any, optFns ...comfunc.ConvOptionFn) (string, error) {
return comfunc.ToStringWith(in, optFns...)
}
+36
View File
@@ -2,6 +2,8 @@
package mathutil
import (
"math"
"github.com/gookit/goutil/comdef"
)
@@ -71,3 +73,37 @@ func GteOr[T comdef.XintOrFloat](val, min, defVal T) T {
}
return defVal
}
// Mul computes the a*b value, rounding the result.
func Mul[T1, T2 comdef.XintOrFloat](a T1, b T2) float64 {
return math.Round(SafeFloat(a) * SafeFloat(b))
}
// MulF2i computes the float64 type a * b value, rounding the result to an integer.
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 {
return math.Round(SafeFloat(a) / SafeFloat(b))
}
// DivInt computes the int type a / b value, rounding the result to an integer.
func DivInt[T comdef.Integer](a, b T) int {
fv := math.Round(float64(a) / float64(b))
return int(fv)
}
// DivF2i computes the float64 type a / b value, rounding the result to an integer.
func DivF2i(a, b float64) int {
return int(math.Round(a / b))
}
// Percent returns a values percent of the total
func Percent(val, total int) float64 {
if total == 0 {
return float64(0)
}
return (float64(val) / float64(total)) * 100
}
+6 -5
View File
@@ -10,8 +10,9 @@ func HasChild(v reflect.Value) bool {
switch v.Kind() {
case reflect.Array, reflect.Slice, reflect.Map, reflect.Struct:
return true
default:
return false
}
return false
}
// IsArrayOrSlice check. eg: array, slice
@@ -122,9 +123,9 @@ func IsEmpty(v reflect.Value) bool {
return v.Float() == 0
case reflect.Interface, reflect.Ptr, reflect.Func:
return v.IsNil()
default:
return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
}
return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
}
// IsEmptyValue reflect value check, alias of the IsEmptyReal()
@@ -158,7 +159,7 @@ func IsEmptyReal(v reflect.Value) bool {
return v.IsNil()
case reflect.Invalid:
return true
default:
return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
}
return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
}
+6 -6
View File
@@ -126,12 +126,12 @@ func ConvToKind(val any, kind reflect.Kind) (rv reflect.Value, err error) {
rv = reflect.ValueOf(dstV)
}
case reflect.Uint:
var dstV uint64
var dstV uint
if dstV, err = mathutil.ToUint(val); err == nil {
rv = reflect.ValueOf(uint(dstV))
rv = reflect.ValueOf(dstV)
}
case reflect.Uint8:
var dstV uint64
var dstV uint
if dstV, err = mathutil.ToUint(val); err == nil {
if dstV > math.MaxUint8 {
return rv, fmt.Errorf("value overflow uint8. val: %v", val)
@@ -139,7 +139,7 @@ func ConvToKind(val any, kind reflect.Kind) (rv reflect.Value, err error) {
rv = reflect.ValueOf(uint8(dstV))
}
case reflect.Uint16:
var dstV uint64
var dstV uint
if dstV, err = mathutil.ToUint(val); err == nil {
if dstV > math.MaxUint16 {
return rv, fmt.Errorf("value overflow uint16. val: %v", val)
@@ -147,7 +147,7 @@ func ConvToKind(val any, kind reflect.Kind) (rv reflect.Value, err error) {
rv = reflect.ValueOf(uint16(dstV))
}
case reflect.Uint32:
var dstV uint64
var dstV uint
if dstV, err = mathutil.ToUint(val); err == nil {
if dstV > math.MaxUint32 {
return rv, fmt.Errorf("value overflow uint32. val: %v", val)
@@ -156,7 +156,7 @@ func ConvToKind(val any, kind reflect.Kind) (rv reflect.Value, err error) {
}
case reflect.Uint64:
var dstV uint64
if dstV, err = mathutil.ToUint(val); err == nil {
if dstV, err = mathutil.ToUint64(val); err == nil {
rv = reflect.ValueOf(dstV)
}
case reflect.Float32:
+1 -2
View File
@@ -2,7 +2,6 @@
package reflects
import (
"fmt"
"reflect"
)
@@ -12,6 +11,6 @@ var (
anyType = reflect.TypeOf((*any)(nil)).Elem()
errorType = reflect.TypeOf((*error)(nil)).Elem()
fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
// fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
reflectValueType = reflect.TypeOf((*reflect.Value)(nil)).Elem()
)
+34 -9
View File
@@ -4,9 +4,12 @@ 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
@@ -46,6 +49,9 @@ type SetOptions struct {
//
// default: false
ParseDefaultEnv bool
// StopOnError if true, will stop set value on error happened. default: false
// StopOnError bool
}
// WithParseDefault value by tag "default"
@@ -85,6 +91,7 @@ func setValues(rv reflect.Value, data map[string]any, opt *SetOptions) error {
return nil
}
var es comdef.Errors
rt := rv.Type()
for i := 0; i < rt.NumField(); i++ {
@@ -100,7 +107,8 @@ func setValues(rv reflect.Value, data map[string]any, opt *SetOptions) error {
if ok {
info, err := ParseTagValueDefault(name, tagVal)
if err != nil {
return err
es = append(es, err)
continue
}
name = info.Get("name")
}
@@ -112,7 +120,7 @@ func setValues(rv reflect.Value, data map[string]any, opt *SetOptions) error {
if !ok && opt.ParseDefault && fv.IsZero() {
defVal := ft.Tag.Get(opt.DefaultValTag)
if err := initDefaultValue(fv, defVal, opt.ParseDefaultEnv); err != nil {
return err
es = append(es, err)
}
continue
}
@@ -127,22 +135,39 @@ func setValues(rv reflect.Value, data map[string]any, opt *SetOptions) error {
// field is struct
if fv.Kind() == reflect.Struct {
asMp, err := maputil.TryAnyMap(val)
if err != nil {
return fmt.Errorf("must provide map data for field %q, err=%v", ft.Name, err)
// 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)
continue
}
if er = reflects.SetValue(fv, tm); er != nil {
es = append(es, er)
}
continue
}
if err := setValues(fv, asMp, opt); err != nil {
return err
asMp, err := maputil.TryAnyMap(val)
if err != nil {
err = fmt.Errorf("must provide map for set struct field %q, err=%v", ft.Name, err)
es = append(es, err)
continue
}
// recursive processing sub-struct
if err = setValues(fv, asMp, opt); err != nil {
es = append(es, err)
}
continue
}
// set field value
if err := reflects.SetValue(fv, val); err != nil {
return err
es = append(es, err)
continue
}
}
return nil
return es.ErrOrNil()
}
+28 -117
View File
@@ -2,7 +2,6 @@ package strutil
import (
"errors"
"fmt"
"reflect"
"regexp"
"strconv"
@@ -10,7 +9,6 @@ import (
"time"
"unsafe"
"github.com/gookit/goutil/comdef"
"github.com/gookit/goutil/internal/comfunc"
"github.com/gookit/goutil/mathutil"
)
@@ -91,39 +89,29 @@ func Implode(sep string, ss ...string) string { return strings.Join(ss, sep) }
*************************************************************/
// String convert value to string, return error on failed
func String(val any) (string, error) {
return ToStringWithFunc(val, nil)
}
func String(val any) (string, error) { return ToStringWith(val) }
// ToString convert value to string, return error on failed
func ToString(val any) (string, error) {
return ToStringWithFunc(val, nil)
}
func ToString(val any) (string, error) { return ToStringWith(val) }
// StringOrErr convert value to string, return error on failed
func StringOrErr(val any) (string, error) {
return ToStringWithFunc(val, nil)
}
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)
}
func QuietString(val any) string { return SafeString(val) }
// SafeString convert value to string, will ignore error
func SafeString(in any) string {
val, _ := ToStringWithFunc(in, SprintToStrFunc)
return val
s, _ := AnyToString(in, false)
return s
}
// StringOrPanic convert value to string, will panic on error
func StringOrPanic(val any) string {
return MustString(val)
}
func StringOrPanic(val any) string { return MustString(val) }
// MustString convert value to string, will panic on error
func MustString(val any) string {
s, err := ToStringWithFunc(val, nil)
s, err := ToStringWith(val)
if err != nil {
panic(err)
}
@@ -131,93 +119,34 @@ func MustString(val any) string {
}
// StringOrDefault convert any value to string, return default value on failed
func StringOrDefault(val any, defVal string) string {
return StringOr(val, defVal)
}
func StringOrDefault(val any, defVal string) string { return StringOr(val, defVal) }
// StringOr convert any value to string, return default value on failed
func StringOr(val any, defVal string) string {
s, err := ToStringWithFunc(val, nil)
s, err := ToStringWith(val)
if err != nil {
return defVal
}
return s
}
// SprintToStrFunc convert any value to string by fmt.Sprint
var SprintToStrFunc = func(v any) (string, error) {
if v == nil {
return "", nil
}
return fmt.Sprint(v), nil
}
// AnyToString convert any value to string.
//
// For defaultAsErr:
//
// - False will use fmt.Sprint convert complex type
// - True will return error on fail.
// - False will use fmt.Sprint convert unsupported type
// - True will return error on convert fail.
func AnyToString(val any, defaultAsErr bool) (s string, err error) {
var fbFunc comdef.ToStringFunc
var optFn comfunc.ConvOptionFn
if !defaultAsErr {
fbFunc = SprintToStrFunc
optFn = comfunc.WithUserConvFn(comfunc.StrBySprintFn)
}
return ToStringWithFunc(val, fbFunc)
return ToStringWith(val, optFn)
}
// ToStringWithFunc convert value to string, with a func to fallback handle.
//
// On not convert:
// - If fbFn is nil, will return comdef.ErrConvType.
// - If fbFn is not nil, will call it to convert.
func ToStringWithFunc(val any, fbFn comdef.ToStringFunc) (str string, err error) {
switch value := val.(type) {
case int:
str = strconv.Itoa(value)
case int8:
str = strconv.Itoa(int(value))
case int16:
str = strconv.Itoa(int(value))
case int32: // same as `rune`
str = strconv.Itoa(int(value))
case int64:
str = strconv.FormatInt(value, 10)
case uint:
str = strconv.FormatUint(uint64(value), 10)
case uint8:
str = strconv.FormatUint(uint64(value), 10)
case uint16:
str = strconv.FormatUint(uint64(value), 10)
case uint32:
str = strconv.FormatUint(uint64(value), 10)
case uint64:
str = strconv.FormatUint(value, 10)
case float32:
str = strconv.FormatFloat(float64(value), 'f', -1, 32)
case float64:
str = strconv.FormatFloat(value, 'f', -1, 64)
case bool:
str = strconv.FormatBool(value)
case string:
str = value
case []byte:
str = string(value)
case time.Duration:
str = strconv.FormatInt(int64(value), 10)
case fmt.Stringer:
str = value.String()
case error:
str = value.Error()
default:
if fbFn == nil {
err = comdef.ErrConvType
} else {
str, err = fbFn(value)
}
}
return
// ToStringWith try to convert value to string. can with some option func, more see comfunc.ConvOption.
func ToStringWith(in any, optFns ...comfunc.ConvOptionFn) (string, error) {
return comfunc.ToStringWith(in, optFns...)
}
/*************************************************************
@@ -230,9 +159,7 @@ func ToBool(s string) (bool, error) {
}
// QuietBool convert to bool, will ignore error
func QuietBool(s string) bool {
return SafeBool(s)
}
func QuietBool(s string) bool { return SafeBool(s) }
// SafeBool convert to bool, will ignore error
func SafeBool(s string) bool {
@@ -289,14 +216,10 @@ func SafeInt(s string) int {
}
// QuietInt convert string to int, will ignore error
func QuietInt(s string) int {
return SafeInt(s)
}
func QuietInt(s string) int { return SafeInt(s) }
// MustInt convert string to int, will panic on error
func MustInt(s string) int {
return IntOrPanic(s)
}
func MustInt(s string) int { return IntOrPanic(s) }
// IntOrPanic convert value to int, will panic on error
func IntOrPanic(s string) int {
@@ -312,10 +235,10 @@ func IntOrPanic(s string) int {
*************************************************************/
// Int64 convert string to int, will ignore error
func Int64(s string) int64 {
val, _ := Int64OrErr(s)
return val
}
func Int64(s string) int64 { return SafeInt64(s) }
// QuietInt64 convert string to int, will ignore error
func QuietInt64(s string) int64 { return SafeInt64(s) }
// SafeInt64 convert string to int, will ignore error
func SafeInt64(s string) int64 {
@@ -323,11 +246,6 @@ func SafeInt64(s string) int64 {
return val
}
// QuietInt64 convert string to int, will ignore error
func QuietInt64(s string) int64 {
return SafeInt64(s)
}
// ToInt64 convert string to int, return error on fail
func ToInt64(s string) (int64, error) {
return strconv.ParseInt(s, 10, 0)
@@ -353,9 +271,7 @@ func Int64OrErr(s string) (int64, error) {
}
// MustInt64 convert value to int, will panic on error
func MustInt64(s string) int64 {
return Int64OrPanic(s)
}
func MustInt64(s string) int64 { return Int64OrPanic(s) }
// Int64OrPanic convert value to int, will panic on error
func Int64OrPanic(s string) int64 {
@@ -371,10 +287,7 @@ func Int64OrPanic(s string) int64 {
*************************************************************/
// Uint convert string to uint, will ignore error
func Uint(s string) uint64 {
val, _ := UintOrErr(s)
return val
}
func Uint(s string) uint64 { return SafeUint(s) }
// SafeUint convert string to uint, will ignore error
func SafeUint(s string) uint64 {
@@ -393,9 +306,7 @@ func UintOrErr(s string) (uint64, error) {
}
// MustUint convert value to uint, will panic on error. alias of UintOrPanic()
func MustUint(s string) uint64 {
return UintOrPanic(s)
}
func MustUint(s string) uint64 { return UintOrPanic(s) }
// UintOrPanic convert value to uint, will panic on error
func UintOrPanic(s string) uint64 {
-65
View File
@@ -2,12 +2,9 @@
package strutil
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"strings"
"text/template"
"github.com/gookit/goutil/comdef"
)
@@ -92,68 +89,6 @@ func NewReplacer(pairs map[string]string) *strings.Replacer {
return strings.NewReplacer(ss...)
}
// PrettyJSON get pretty Json string
// Deprecated: please use fmtutil.PrettyJSON() or jsonutil.Pretty() instead it
func PrettyJSON(v any) (string, error) {
out, err := json.MarshalIndent(v, "", " ")
return string(out), err
}
var builtInFuncs = template.FuncMap{
// don't escape content
"raw": func(s string) string {
return s
},
"trim": func(s string) string {
return strings.TrimSpace(s)
},
// join strings
"join": func(ss []string, sep string) string {
return strings.Join(ss, sep)
},
// lower first char
"lcFirst": func(s string) string {
return LowerFirst(s)
},
// upper first char
"upFirst": func(s string) string {
return UpperFirst(s)
},
}
// RenderTemplate quickly render text template.
//
// Deprecated: please use textutil.RenderTpl() instead it
func RenderTemplate(input string, data any, fns template.FuncMap, isFile ...bool) string {
return RenderText(input, data, fns, isFile...)
}
// RenderText quickly render text template
//
// Deprecated: please use textutil.RenderTpl() instead it
func RenderText(input string, data any, fns template.FuncMap, isFile ...bool) string {
t := template.New("simple-text")
t.Funcs(builtInFuncs)
// add custom template functions
if len(fns) > 0 {
t.Funcs(fns)
}
if len(isFile) > 0 && isFile[0] {
template.Must(t.ParseFiles(input))
} else {
template.Must(t.Parse(input))
}
// use buffer receive rendered content
buf := new(bytes.Buffer)
if err := t.Execute(buf, data); err != nil {
panic(err)
}
return buf.String()
}
// WrapTag for given string.
func WrapTag(s, tag string) string {
if s == "" {
+2 -2
View File
@@ -1107,11 +1107,11 @@ 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.4
# github.com/gookit/config/v2 v2.2.5
## explicit; go 1.19
github.com/gookit/config/v2
github.com/gookit/config/v2/yaml
# github.com/gookit/goutil v0.6.14
# github.com/gookit/goutil v0.6.15
## explicit; go 1.19
github.com/gookit/goutil
github.com/gookit/goutil/arrutil