OwnCloud Infinite Scale with Go 1.25.x
Quick and dirty manual how to build ocis with go 1.25.4
git clone https://github.com/owncloud/ocis.git ocis
push ocis
go get -u github.com/gookit/goutil/sysutil
go mod tidy
go mod vendor
gmake generate
gmake -C ocis clean
gmake -C ocis build
strip ocis/bin/ocis
Now you have shiny new build of OCIS inside file ocis/bin/ocis .
For me it looks like following patch file I have, and the result is working site https://owncloud.ostreff.info :
diff --git a/go.mod b/go.mod
index 7ccb9a06a99..3d83a1d83f5 100644
--- a/go.mod
+++ b/go.mod
@@ -221,7 +221,7 @@ require (
github.com/google/go-tpm v0.9.6 // indirect
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
github.com/google/renameio/v2 v2.0.0 // indirect
- github.com/gookit/goutil v0.7.1 // indirect
+ github.com/gookit/goutil v0.7.2 // 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
diff --git a/go.sum b/go.sum
index 18d7a3f4831..dbe99ef2f36 100644
--- a/go.sum
+++ b/go.sum
@@ -486,8 +486,8 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gookit/config/v2 v2.2.7 h1:P58/uENzkDp7r7Hp8YSZxOhZ/F5a5Y/AzyhDUkQYa9A=
github.com/gookit/config/v2 v2.2.7/go.mod h1:QST99HmkZXXD/HkZmOm1OXpgdAnc6Rl9syGl+u62Pi8=
-github.com/gookit/goutil v0.7.1 h1:AaFJPN9mrdeYBv8HOybri26EHGCC34WJVT7jUStGJsI=
-github.com/gookit/goutil v0.7.1/go.mod h1:vJS9HXctYTCLtCsZot5L5xF+O1oR17cDYO9R0HxBmnU=
+github.com/gookit/goutil v0.7.2 h1:NSiqWWY+BT0MwIlKDeSVPfQmr9xTkkAqwDjhplobdgo=
+github.com/gookit/goutil v0.7.2/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/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
diff --git a/protogen/gen/ocis/messages/settings/v0/settings.pb.go b/protogen/gen/ocis/messages/settings/v0/settings.pb.go
index cf104c870ba..563436da5a5 100644
--- a/protogen/gen/ocis/messages/settings/v0/settings.pb.go
+++ b/protogen/gen/ocis/messages/settings/v0/settings.pb.go
@@ -627,13 +627,13 @@ type Bundle struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
- Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" yaml:"id"` // @gotags: yaml:"id"
- Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty" yaml:"name"` // @gotags: yaml:"name"
- Type Bundle_Type `protobuf:"varint,3,opt,name=type,proto3,enum=ocis.messages.settings.v0.Bundle_Type" json:"type,omitempty" yaml:"type"` // @gotags: yaml:"type"
- Extension string `protobuf:"bytes,4,opt,name=extension,proto3" json:"extension,omitempty" yaml:"extension"` // @gotags: yaml:"extension"
- DisplayName string `protobuf:"bytes,5,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty" yaml:"display_name"` // @gotags: yaml:"display_name"
- Settings []*Setting `protobuf:"bytes,6,rep,name=settings,proto3" json:"settings,omitempty" yaml:"settings"` // @gotags: yaml:"settings"
- Resource *Resource `protobuf:"bytes,7,opt,name=resource,proto3" json:"resource,omitempty" yaml:"resource"` // @gotags: yaml:"resource"
+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // @gotags: yaml:"id"
+ Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // @gotags: yaml:"name"
+ Type Bundle_Type `protobuf:"varint,3,opt,name=type,proto3,enum=ocis.messages.settings.v0.Bundle_Type" json:"type,omitempty"` // @gotags: yaml:"type"
+ Extension string `protobuf:"bytes,4,opt,name=extension,proto3" json:"extension,omitempty"` // @gotags: yaml:"extension"
+ DisplayName string `protobuf:"bytes,5,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` // @gotags: yaml:"display_name"
+ Settings []*Setting `protobuf:"bytes,6,rep,name=settings,proto3" json:"settings,omitempty"` // @gotags: yaml:"settings"
+ Resource *Resource `protobuf:"bytes,7,opt,name=resource,proto3" json:"resource,omitempty"` // @gotags: yaml:"resource"
}
func (x *Bundle) Reset() {
@@ -722,10 +722,10 @@ type Setting struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
- Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" yaml:"id"` // @gotags: yaml:"id"
- Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty" yaml:"name"` // @gotags: yaml:"name"
- DisplayName string `protobuf:"bytes,3,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty" yaml:"display_name"` // @gotags: yaml:"display_name"
- Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty" yaml:"description"` // @gotags: yaml:"description"
+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // @gotags: yaml:"id"
+ Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // @gotags: yaml:"name"
+ DisplayName string `protobuf:"bytes,3,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` // @gotags: yaml:"display_name"
+ Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` // @gotags: yaml:"description"
// Types that are assignable to Value:
//
// *Setting_IntValue
@@ -736,7 +736,7 @@ type Setting struct {
// *Setting_PermissionValue
// *Setting_MultiChoiceCollectionValue
Value isSetting_Value `protobuf_oneof:"value"`
- Resource *Resource `protobuf:"bytes,11,opt,name=resource,proto3" json:"resource,omitempty" yaml:"resource"` // @gotags: yaml:"resource"
+ Resource *Resource `protobuf:"bytes,11,opt,name=resource,proto3" json:"resource,omitempty"` // @gotags: yaml:"resource"
}
func (x *Setting) Reset() {
@@ -867,31 +867,31 @@ type isSetting_Value interface {
}
type Setting_IntValue struct {
- IntValue *Int `protobuf:"bytes,5,opt,name=int_value,json=intValue,proto3,oneof" yaml:"int_value"` // @gotags: yaml:"int_value"
+ IntValue *Int `protobuf:"bytes,5,opt,name=int_value,json=intValue,proto3,oneof"` // @gotags: yaml:"int_value"
}
type Setting_StringValue struct {
- StringValue *String `protobuf:"bytes,6,opt,name=string_value,json=stringValue,proto3,oneof" yaml:"string_value"` // @gotags: yaml:"string_value"
+ StringValue *String `protobuf:"bytes,6,opt,name=string_value,json=stringValue,proto3,oneof"` // @gotags: yaml:"string_value"
}
type Setting_BoolValue struct {
- BoolValue *Bool `protobuf:"bytes,7,opt,name=bool_value,json=boolValue,proto3,oneof" yaml:"bool_value"` // @gotags: yaml:"bool_value"
+ BoolValue *Bool `protobuf:"bytes,7,opt,name=bool_value,json=boolValue,proto3,oneof"` // @gotags: yaml:"bool_value"
}
type Setting_SingleChoiceValue struct {
- SingleChoiceValue *SingleChoiceList `protobuf:"bytes,8,opt,name=single_choice_value,json=singleChoiceValue,proto3,oneof" yaml:"single_choice_value"` // @gotags: yaml:"single_choice_value"
+ SingleChoiceValue *SingleChoiceList `protobuf:"bytes,8,opt,name=single_choice_value,json=singleChoiceValue,proto3,oneof"` // @gotags: yaml:"single_choice_value"
}
type Setting_MultiChoiceValue struct {
- MultiChoiceValue *MultiChoiceList `protobuf:"bytes,9,opt,name=multi_choice_value,json=multiChoiceValue,proto3,oneof" yaml:"multi_choice_value"` // @gotags: yaml:"multi_choice_value"
+ MultiChoiceValue *MultiChoiceList `protobuf:"bytes,9,opt,name=multi_choice_value,json=multiChoiceValue,proto3,oneof"` // @gotags: yaml:"multi_choice_value"
}
type Setting_PermissionValue struct {
- PermissionValue *Permission `protobuf:"bytes,10,opt,name=permission_value,json=permissionValue,proto3,oneof" yaml:"permission_value"` // @gotags: yaml:"permission_value"
+ PermissionValue *Permission `protobuf:"bytes,10,opt,name=permission_value,json=permissionValue,proto3,oneof"` // @gotags: yaml:"permission_value"
}
type Setting_MultiChoiceCollectionValue struct {
- MultiChoiceCollectionValue *MultiChoiceCollection `protobuf:"bytes,12,opt,name=multi_choice_collection_value,json=multiChoiceCollectionValue,proto3,oneof" yaml:"multi_choice_collection_value"` // @gotags: yaml:"multi_choice_collection_value"
+ MultiChoiceCollectionValue *MultiChoiceCollection `protobuf:"bytes,12,opt,name=multi_choice_collection_value,json=multiChoiceCollectionValue,proto3,oneof"` // @gotags: yaml:"multi_choice_collection_value"
}
func (*Setting_IntValue) isSetting_Value() {}
@@ -913,11 +913,11 @@ type Int struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
- Default int64 `protobuf:"varint,1,opt,name=default,proto3" json:"default,omitempty" yaml:"default"` // @gotags: yaml:"default"
- Min int64 `protobuf:"varint,2,opt,name=min,proto3" json:"min,omitempty" yaml:"min"` // @gotags: yaml:"min"
- Max int64 `protobuf:"varint,3,opt,name=max,proto3" json:"max,omitempty" yaml:"max"` // @gotags: yaml:"max"
- Step int64 `protobuf:"varint,4,opt,name=step,proto3" json:"step,omitempty" yaml:"step"` // @gotags: yaml:"step"
- Placeholder string `protobuf:"bytes,5,opt,name=placeholder,proto3" json:"placeholder,omitempty" yaml:"placeholder"` // @gotags: yaml:"placeholder"
+ Default int64 `protobuf:"varint,1,opt,name=default,proto3" json:"default,omitempty"` // @gotags: yaml:"default"
+ Min int64 `protobuf:"varint,2,opt,name=min,proto3" json:"min,omitempty"` // @gotags: yaml:"min"
+ Max int64 `protobuf:"varint,3,opt,name=max,proto3" json:"max,omitempty"` // @gotags: yaml:"max"
+ Step int64 `protobuf:"varint,4,opt,name=step,proto3" json:"step,omitempty"` // @gotags: yaml:"step"
+ Placeholder string `protobuf:"bytes,5,opt,name=placeholder,proto3" json:"placeholder,omitempty"` // @gotags: yaml:"placeholder"
}
func (x *Int) Reset() {
@@ -992,11 +992,11 @@ type String struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
- Default string `protobuf:"bytes,1,opt,name=default,proto3" json:"default,omitempty" yaml:"default"` // @gotags: yaml:"default"
- Required bool `protobuf:"varint,2,opt,name=required,proto3" json:"required,omitempty" yaml:"required"` // @gotags: yaml:"required"
- MinLength int32 `protobuf:"varint,3,opt,name=min_length,json=minLength,proto3" json:"min_length,omitempty" yaml:"min_length"` // @gotags: yaml:"min_length"
- MaxLength int32 `protobuf:"varint,4,opt,name=max_length,json=maxLength,proto3" json:"max_length,omitempty" yaml:"max_length"` // @gotags: yaml:"max_length"
- Placeholder string `protobuf:"bytes,5,opt,name=placeholder,proto3" json:"placeholder,omitempty" yaml:"placeholder"` // @gotags: yaml:"placeholder"
+ Default string `protobuf:"bytes,1,opt,name=default,proto3" json:"default,omitempty"` // @gotags: yaml:"default"
+ Required bool `protobuf:"varint,2,opt,name=required,proto3" json:"required,omitempty"` // @gotags: yaml:"required"
+ MinLength int32 `protobuf:"varint,3,opt,name=min_length,json=minLength,proto3" json:"min_length,omitempty"` // @gotags: yaml:"min_length"
+ MaxLength int32 `protobuf:"varint,4,opt,name=max_length,json=maxLength,proto3" json:"max_length,omitempty"` // @gotags: yaml:"max_length"
+ Placeholder string `protobuf:"bytes,5,opt,name=placeholder,proto3" json:"placeholder,omitempty"` // @gotags: yaml:"placeholder"
}
func (x *String) Reset() {
@@ -1071,8 +1071,8 @@ type Bool struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
- Default bool `protobuf:"varint,1,opt,name=default,proto3" json:"default,omitempty" yaml:"default"` // @gotags: yaml:"default"
- Label string `protobuf:"bytes,2,opt,name=label,proto3" json:"label,omitempty" yaml:"label"` // @gotags: yaml:"label"
+ Default bool `protobuf:"varint,1,opt,name=default,proto3" json:"default,omitempty"` // @gotags: yaml:"default"
+ Label string `protobuf:"bytes,2,opt,name=label,proto3" json:"label,omitempty"` // @gotags: yaml:"label"
}
func (x *Bool) Reset() {
@@ -1126,7 +1126,7 @@ type SingleChoiceList struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
- Options []*ListOption `protobuf:"bytes,1,rep,name=options,proto3" json:"options,omitempty" yaml:"options"` // @gotags: yaml:"options"
+ Options []*ListOption `protobuf:"bytes,1,rep,name=options,proto3" json:"options,omitempty"` // @gotags: yaml:"options"
}
func (x *SingleChoiceList) Reset() {
@@ -1173,7 +1173,7 @@ type MultiChoiceList struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
- Options []*ListOption `protobuf:"bytes,1,rep,name=options,proto3" json:"options,omitempty" yaml:"options"` // @gotags: yaml:"options"
+ Options []*ListOption `protobuf:"bytes,1,rep,name=options,proto3" json:"options,omitempty"` // @gotags: yaml:"options"
}
func (x *MultiChoiceList) Reset() {
@@ -1220,9 +1220,9 @@ type ListOption struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
- Value *ListOptionValue `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty" yaml:"value"` // @gotags: yaml:"value"
- Default bool `protobuf:"varint,2,opt,name=default,proto3" json:"default,omitempty" yaml:"default"` // @gotags: yaml:"default"
- DisplayValue string `protobuf:"bytes,3,opt,name=display_value,json=displayValue,proto3" json:"display_value,omitempty" yaml:"display_value"` // @gotags: yaml:"display_value"
+ Value *ListOptionValue `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` // @gotags: yaml:"value"
+ Default bool `protobuf:"varint,2,opt,name=default,proto3" json:"default,omitempty"` // @gotags: yaml:"default"
+ DisplayValue string `protobuf:"bytes,3,opt,name=display_value,json=displayValue,proto3" json:"display_value,omitempty"` // @gotags: yaml:"display_value"
}
func (x *ListOption) Reset() {
@@ -1283,7 +1283,7 @@ type MultiChoiceCollection struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
- Options []*MultiChoiceCollectionOption `protobuf:"bytes,1,rep,name=options,proto3" json:"options,omitempty" yaml:"options"` // @gotags: yaml:"options"
+ Options []*MultiChoiceCollectionOption `protobuf:"bytes,1,rep,name=options,proto3" json:"options,omitempty"` // @gotags: yaml:"options"
}
func (x *MultiChoiceCollection) Reset() {
@@ -1330,10 +1330,10 @@ type MultiChoiceCollectionOption struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
- Value *MultiChoiceCollectionOptionValue `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty" yaml:"value"` // @gotags: yaml:"value"
- Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty" yaml:"key"` // @gotags: yaml:"key"
- Attribute string `protobuf:"bytes,3,opt,name=attribute,proto3" json:"attribute,omitempty" yaml:"attribute"` // @gotags: yaml:"attribute"
- DisplayValue string `protobuf:"bytes,4,opt,name=display_value,json=displayValue,proto3" json:"display_value,omitempty" yaml:"display_value"` // @gotags: yaml:"display_value"
+ Value *MultiChoiceCollectionOptionValue `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` // @gotags: yaml:"value"
+ Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` // @gotags: yaml:"key"
+ Attribute string `protobuf:"bytes,3,opt,name=attribute,proto3" json:"attribute,omitempty"` // @gotags: yaml:"attribute"
+ DisplayValue string `protobuf:"bytes,4,opt,name=display_value,json=displayValue,proto3" json:"display_value,omitempty"` // @gotags: yaml:"display_value"
}
func (x *MultiChoiceCollectionOption) Reset() {
@@ -1474,15 +1474,15 @@ type isMultiChoiceCollectionOptionValue_Option interface {
}
type MultiChoiceCollectionOptionValue_IntValue struct {
- IntValue *Int `protobuf:"bytes,1,opt,name=int_value,json=intValue,proto3,oneof" yaml:"int_value"` // @gotags: yaml:"int_value"
+ IntValue *Int `protobuf:"bytes,1,opt,name=int_value,json=intValue,proto3,oneof"` // @gotags: yaml:"int_value"
}
type MultiChoiceCollectionOptionValue_StringValue struct {
- StringValue *String `protobuf:"bytes,2,opt,name=string_value,json=stringValue,proto3,oneof" yaml:"string_value"` // @gotags: yaml:"string_value"
+ StringValue *String `protobuf:"bytes,2,opt,name=string_value,json=stringValue,proto3,oneof"` // @gotags: yaml:"string_value"
}
type MultiChoiceCollectionOptionValue_BoolValue struct {
- BoolValue *Bool `protobuf:"bytes,3,opt,name=bool_value,json=boolValue,proto3,oneof" yaml:"bool_value"` // @gotags: yaml:"bool_value"
+ BoolValue *Bool `protobuf:"bytes,3,opt,name=bool_value,json=boolValue,proto3,oneof"` // @gotags: yaml:"bool_value"
}
func (*MultiChoiceCollectionOptionValue_IntValue) isMultiChoiceCollectionOptionValue_Option() {}
@@ -1496,8 +1496,8 @@ type Permission struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
- Operation Permission_Operation `protobuf:"varint,1,opt,name=operation,proto3,enum=ocis.messages.settings.v0.Permission_Operation" json:"operation,omitempty" yaml:"operation"` // @gotags: yaml:"operation"
- Constraint Permission_Constraint `protobuf:"varint,2,opt,name=constraint,proto3,enum=ocis.messages.settings.v0.Permission_Constraint" json:"constraint,omitempty" yaml:"constraint"` // @gotags: yaml:"constraint"
+ Operation Permission_Operation `protobuf:"varint,1,opt,name=operation,proto3,enum=ocis.messages.settings.v0.Permission_Operation" json:"operation,omitempty"` // @gotags: yaml:"operation"
+ Constraint Permission_Constraint `protobuf:"varint,2,opt,name=constraint,proto3,enum=ocis.messages.settings.v0.Permission_Constraint" json:"constraint,omitempty"` // @gotags: yaml:"constraint"
}
func (x *Permission) Reset() {
@@ -1552,12 +1552,12 @@ type Value struct {
unknownFields protoimpl.UnknownFields
// id is the id of the Value. It is generated on saving it.
- Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" yaml:"id"` // @gotags: yaml:"id"
- BundleId string `protobuf:"bytes,2,opt,name=bundle_id,json=bundleId,proto3" json:"bundle_id,omitempty" yaml:"bundle_id"` // @gotags: yaml:"bundle_id"
+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // @gotags: yaml:"id"
+ BundleId string `protobuf:"bytes,2,opt,name=bundle_id,json=bundleId,proto3" json:"bundle_id,omitempty"` // @gotags: yaml:"bundle_id"
// setting_id is the id of the setting from within its bundle.
- SettingId string `protobuf:"bytes,3,opt,name=setting_id,json=settingId,proto3" json:"setting_id,omitempty" yaml:"setting_id"` // @gotags: yaml:"setting_id"
- AccountUuid string `protobuf:"bytes,4,opt,name=account_uuid,json=accountUuid,proto3" json:"account_uuid,omitempty" yaml:"account_uuid"` // @gotags: yaml:"account_uuid"
- Resource *Resource `protobuf:"bytes,5,opt,name=resource,proto3" json:"resource,omitempty" yaml:"resource"` // @gotags: yaml:"resource"
+ SettingId string `protobuf:"bytes,3,opt,name=setting_id,json=settingId,proto3" json:"setting_id,omitempty"` // @gotags: yaml:"setting_id"
+ AccountUuid string `protobuf:"bytes,4,opt,name=account_uuid,json=accountUuid,proto3" json:"account_uuid,omitempty"` // @gotags: yaml:"account_uuid"
+ Resource *Resource `protobuf:"bytes,5,opt,name=resource,proto3" json:"resource,omitempty"` // @gotags: yaml:"resource"
// Types that are assignable to Value:
//
// *Value_BoolValue
@@ -1682,23 +1682,23 @@ type isValue_Value interface {
}
type Value_BoolValue struct {
- BoolValue bool `protobuf:"varint,6,opt,name=bool_value,json=boolValue,proto3,oneof" yaml:"bool_value"` // @gotags: yaml:"bool_value"
+ BoolValue bool `protobuf:"varint,6,opt,name=bool_value,json=boolValue,proto3,oneof"` // @gotags: yaml:"bool_value"
}
type Value_IntValue struct {
- IntValue int64 `protobuf:"varint,7,opt,name=int_value,json=intValue,proto3,oneof" yaml:"int_value"` // @gotags: yaml:"int_value"
+ IntValue int64 `protobuf:"varint,7,opt,name=int_value,json=intValue,proto3,oneof"` // @gotags: yaml:"int_value"
}
type Value_StringValue struct {
- StringValue string `protobuf:"bytes,8,opt,name=string_value,json=stringValue,proto3,oneof" yaml:"string_value"` // @gotags: yaml:"string_value"
+ StringValue string `protobuf:"bytes,8,opt,name=string_value,json=stringValue,proto3,oneof"` // @gotags: yaml:"string_value"
}
type Value_ListValue struct {
- ListValue *ListValue `protobuf:"bytes,9,opt,name=list_value,json=listValue,proto3,oneof" yaml:"list_value"` // @gotags: yaml:"list_value"
+ ListValue *ListValue `protobuf:"bytes,9,opt,name=list_value,json=listValue,proto3,oneof"` // @gotags: yaml:"list_value"
}
type Value_CollectionValue struct {
- CollectionValue *CollectionValue `protobuf:"bytes,10,opt,name=collection_value,json=collectionValue,proto3,oneof" yaml:"collection_value"` // @gotags: yaml:"collection_value"
+ CollectionValue *CollectionValue `protobuf:"bytes,10,opt,name=collection_value,json=collectionValue,proto3,oneof"` // @gotags: yaml:"collection_value"
}
func (*Value_BoolValue) isValue_Value() {}
@@ -1716,7 +1716,7 @@ type ListValue struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
- Values []*ListOptionValue `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty" yaml:"values"` // @gotags: yaml:"values"
+ Values []*ListOptionValue `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` // @gotags: yaml:"values"
}
func (x *ListValue) Reset() {
@@ -1836,15 +1836,15 @@ type isListOptionValue_Option interface {
}
type ListOptionValue_StringValue struct {
- StringValue string `protobuf:"bytes,1,opt,name=string_value,json=stringValue,proto3,oneof" yaml:"string_value"` // @gotags: yaml:"string_value"
+ StringValue string `protobuf:"bytes,1,opt,name=string_value,json=stringValue,proto3,oneof"` // @gotags: yaml:"string_value"
}
type ListOptionValue_IntValue struct {
- IntValue int64 `protobuf:"varint,2,opt,name=int_value,json=intValue,proto3,oneof" yaml:"int_value"` // @gotags: yaml:"int_value"
+ IntValue int64 `protobuf:"varint,2,opt,name=int_value,json=intValue,proto3,oneof"` // @gotags: yaml:"int_value"
}
type ListOptionValue_BoolValue struct {
- BoolValue bool `protobuf:"varint,3,opt,name=bool_value,json=boolValue,proto3,oneof" yaml:"bool_value"` // @gotags: yaml:"bool_value"
+ BoolValue bool `protobuf:"varint,3,opt,name=bool_value,json=boolValue,proto3,oneof"` // @gotags: yaml:"bool_value"
}
func (*ListOptionValue_StringValue) isListOptionValue_Option() {}
@@ -1858,7 +1858,7 @@ type CollectionValue struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
- Values []*CollectionOption `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty" yaml:"values"` // @gotags: yaml:"values"
+ Values []*CollectionOption `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` // @gotags: yaml:"values"
}
func (x *CollectionValue) Reset() {
@@ -1906,7 +1906,7 @@ type CollectionOption struct {
unknownFields protoimpl.UnknownFields
// required
- Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty" yaml:"key"` // @gotags: yaml:"key"
+ Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` // @gotags: yaml:"key"
// Types that are assignable to Option:
//
// *CollectionOption_IntValue
@@ -1987,15 +1987,15 @@ type isCollectionOption_Option interface {
}
type CollectionOption_IntValue struct {
- IntValue int64 `protobuf:"varint,2,opt,name=int_value,json=intValue,proto3,oneof" yaml:"int_value"` // @gotags: yaml:"int_value"
+ IntValue int64 `protobuf:"varint,2,opt,name=int_value,json=intValue,proto3,oneof"` // @gotags: yaml:"int_value"
}
type CollectionOption_StringValue struct {
- StringValue string `protobuf:"bytes,3,opt,name=string_value,json=stringValue,proto3,oneof" yaml:"string_value"` // @gotags: yaml:"string_value"
+ StringValue string `protobuf:"bytes,3,opt,name=string_value,json=stringValue,proto3,oneof"` // @gotags: yaml:"string_value"
}
type CollectionOption_BoolValue struct {
- BoolValue bool `protobuf:"varint,4,opt,name=bool_value,json=boolValue,proto3,oneof" yaml:"bool_value"` // @gotags: yaml:"bool_value"
+ BoolValue bool `protobuf:"varint,4,opt,name=bool_value,json=boolValue,proto3,oneof"` // @gotags: yaml:"bool_value"
}
func (*CollectionOption_IntValue) isCollectionOption_Option() {}
diff --git a/vendor/github.com/gookit/goutil/.gitignore b/vendor/github.com/gookit/goutil/.gitignore
index 6eec81adfef..ec0f864bcf5 100644
--- a/vendor/github.com/gookit/goutil/.gitignore
+++ b/vendor/github.com/gookit/goutil/.gitignore
@@ -18,6 +18,7 @@
*.out
*.cov
.DS_Store
+.xenv.toml
*~
testdata/
diff --git a/vendor/github.com/gookit/goutil/README.md b/vendor/github.com/gookit/goutil/README.md
index 60ae33df5a2..0d03f6e20ca 100644
--- a/vendor/github.com/gookit/goutil/README.md
+++ b/vendor/github.com/gookit/goutil/README.md
@@ -1,4 +1,4 @@
-# Go Util
+# GoUtil

[](https://github.com/gookit/goutil)
@@ -7,7 +7,7 @@
[](https://coveralls.io/github/gookit/goutil?branch=master)
[](https://pkg.go.dev/github.com/gookit/goutil)
-💪 Useful utils(**800+**) package for the Go: int, string, array/slice, map, error, time, format, CLI, ENV, filesystem, system, testing and more.
+💪 Useful utils(**900+**) package for the Go: int, string, array/slice, map, struct, reflect, error, time, format, CLI, ENV, filesystem, system, testing and more.
> **[中文说明](README.zh-CN.md)**
@@ -52,8 +52,8 @@
- [`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
+- [`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.
@@ -63,7 +63,7 @@
## Go Doc
Please see [Go doc](https://pkg.go.dev/github.com/gookit/goutil).
-Wiki docs on [DeepWiki - gookit/goutil](https://deepwiki.com/gookit/goutil)
+Wiki docs on [ZRead.ai - gookit/goutil](https://zread.ai/gookit/goutil)
## Install
@@ -109,6 +109,8 @@ dump.Print(somevar, somevar2, ...)
> Package `github.com/gookit/goutil/arrutil`
+<details><summary>Click to see functions 👈</summary>
+
```go
// source at arrutil/arrutil.go
func GetRandomOne[T any](arr []T) T
@@ -152,6 +154,7 @@ func IntsToString[T comdef.Integer](ints []T) string
func ToInt64s(arr any) (ret []int64, err error)
func MustToInt64s(arr any) []int64
func SliceToInt64s(arr []any) []int64
+func ToMap[T any, K comdef.ScalarType, V any](list []T, mapFn func(T) (K, V)) map[K]V
func AnyToSlice(sl any) (ls []any, err error)
func AnyToStrings(arr any) []string
func MustToStrings(arr any) []string
@@ -171,7 +174,8 @@ func FormatIndent(arr any, indent string) string
func Reverse[T any](ls []T)
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 Map[T, V any](list []T, mapFn MapFn[T, V]) []V
+func Map1[T, R any](list []T, fn func(t T) R) []R
func Column[T any, V any](list []T, mapFn func(obj T) (val V, find bool)) []V
func Unique[T comdef.NumberOrString](list []T) []T
func IndexOf[T comdef.NumberOrString](val T, list []T) int
@@ -189,6 +193,8 @@ func StringsFilter(ss []string, filter ...comdef.StringMatchFunc) []string
func StringsMap(ss []string, mapFn func(s string) string) []string
func TrimStrings(ss []string, cutSet ...string) []string
+
+
ArrUtil Usage
check value:
@@ -211,10 +217,13 @@ ss, err := arrutil.ToStrings([]int{1, 2}) // ss: []string{"1", "2"}
+
### Bytes Utils
> Package `github.com/gookit/goutil/byteutil`
+<details><summary>Click to see functions 👈</summary>
+
```go
// source at byteutil/buffer.go
func NewBuffer() *Buffer
@@ -244,18 +253,22 @@ func NewStdEncoder(encFn BytesEncodeFunc, decFn BytesDecodeFunc) *StdEncoder
// source at byteutil/pool.go
func NewChanPool(chSize int, width int, capWidth int) *ChanPool
+
+
Cflag
Package
github.com/gookit/goutil/cflag
+cflag - Wraps and extends go flag.FlagSet to build simple command line applications
+
+
+
Click to see functions 👈
+
-// source at cflag/app.go
-func NewApp(fns ...func(app *App)) *App
-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
+func NewWith(name, version, desc string, fns ...func(c *CFlags)) *CFlags
func NewEmpty(fns ...func(c *CFlags)) *CFlags
func WithDesc(desc string) func(c *CFlags)
func WithVersion(version string) func(c *CFlags)
@@ -269,6 +282,8 @@ func Value
// source at cflag/optarg.go
func NewArg(name, desc string, required bool) *FlagArg
// source at cflag/util.go
+func SetDebug(open bool)
+func DebugMsg(format string, args ...any)
func IsGoodName(name string) bool
func IsZeroValue(opt *flag.Flag, value string) (bool, bool)
func AddPrefix(name string) string
@@ -280,15 +295,20 @@ func IsFlagHelpErr(err error) bool
func WrapColorForCode(s string) string
func ReplaceShorts(args []string, shortsMap map[string]string) []string
+
+
cflag Usage
cflag usage please see cflag/README.md
CLI Utils
Package
github.com/gookit/goutil/cliutil
+
Click to see functions 👈
+
// source at cliutil/cliutil.go
func SplitMulti(ss []string, sep string) []string
@@ -325,6 +345,8 @@ func InputIsYes(ans string) bool
func ByteIsYes(ans byte) bool
func ReadPassword(question ...string) string
+
+
CLI Util Usage
@@ -381,10 +403,13 @@ Build line: ./myapp -a val0 -m "this is message" arg0
More, please see ./cliutil/README
Var Dumper
Package
github.com/gookit/goutil/dump
+
Click to see functions 👈
+
// source at dump/dump.go
func Std() *Dumper
@@ -411,7 +436,10 @@ func WithoutPosition() OptionFunc
func WithoutOutput(out io.Writer) OptionFunc
func WithoutColor() OptionFunc
func WithoutType() OptionFunc
+func WithoutLen() OptionFunc
+
+
Examples
example code:
@@ -456,10 +484,13 @@ Preview:

ENV/Environment
Package
github.com/gookit/goutil/envutil
+
Click to see functions 👈
+
// source at envutil/envutil.go
func VarReplace(s string) string
@@ -503,6 +534,8 @@ func UnsetEnvs(keys ...string)
func LoadText(text string)
func LoadString(line string) bool
+
+
ENV Util Usage
helper functions:
@@ -522,13 +555,18 @@ envutil.ParseValue("${ENV_NAME | defValue}")
+
### Errorx
> Package `github.com/gookit/goutil/errorx`
-Package errorx provide a enhanced error implements, allow with call stack and wrap another error.
+`errorx` provides an enhanced error reporting implementation that contains call stack information and can wrap the previous level of error.
+
+> Additional call stack information is included when printing errors, making it easy to log and find problems.
+<details><summary>Click to see functions 👈</summary>
+
```go
// source at errorx/assert.go
func IsTrue(result bool, fmtAndArgs ...any) error
@@ -578,6 +616,8 @@ func Is(err, target error) bool
func To(err error, target any) bool
func As(err error, target any) bool
+
+
Errorx Usage
@@ -655,16 +695,23 @@ runtime.goexit()
+
### File System
> Package `github.com/gookit/goutil/fsutil`
+Package `fsutil` Filesystem util functions: quick check, create, read and write file. eg: file and dir check, operate
+
+
+<details><summary>Click to see functions 👈</summary>
+
```go
// source at fsutil/check.go
func PathExists(path string) bool
func IsDir(path string) bool
func FileExists(path string) bool
func IsFile(path string) bool
+func IsSymlink(path string) bool
func IsAbsPath(aPath string) bool
func IsEmptyDir(dirPath string) bool
func IsImageFile(path string) bool
@@ -680,6 +727,10 @@ func FirstExistsDir(paths ...string) string
func FirstExistsFile(paths ...string) string
func MatchPaths(paths []string, matcher PathMatchFunc) []string
func MatchFirst(paths []string, matcher PathMatchFunc, defaultPath string) string
+func FindAllInParentDirs(dirPath, name string, optFns ...FindParentOptFn) []string
+func FindOneInParentDirs(dirPath, name string, optFns ...FindParentOptFn) string
+func FindNameInParentDirs(dirPath, name string, collectFn func(fullPath string), optFns ...FindParentOptFn)
+func FindInParentDirs(dirPath string, matchFunc func(dir string) bool, maxLevel int)
func SearchNameUp(dirPath, name string) string
func SearchNameUpx(dirPath, name string) (string, bool)
func WalkDir(dir string, fn fs.WalkDirFunc) error
@@ -713,9 +764,12 @@ func FileExt(fPath string) string
func Extname(fPath string) string
func Suffix(fPath string) string
func Expand(pathStr string) string
+func ExpandHome(pathStr string) string
func ExpandPath(pathStr string) string
func ResolvePath(pathStr string) string
func SplitPath(pathStr string) (dir, name string)
+func UserHomeDir() string
+func HomeDir() string
// source at fsutil/info_nonwin.go
func Realpath(pathStr string) string
// source at fsutil/mime.go
@@ -723,9 +777,11 @@ func DetectMime(path string) string
func MimeType(path string) (mime string)
func ReaderMimeType(r io.Reader) (mime string)
// source at fsutil/operate.go
-func Mkdir(dirPath string, perm os.FileMode) error
-func MkDirs(perm os.FileMode, dirPaths ...string) error
-func MkSubDirs(perm os.FileMode, parentDir string, subDirs ...string) error
+func Mkdir(dirPath string, perm fs.FileMode) error
+func MkdirQuick(dirPath string) error
+func EnsureDir(path string) error
+func MkDirs(perm fs.FileMode, dirPaths ...string) error
+func MkSubDirs(perm fs.FileMode, parentDir string, subDirs ...string) error
func MkParentDir(fpath string) error
func NewOpenOption(optFns ...OpenOptionFunc) *OpenOption
func OpenOptOrNew(opt *OpenOption) *OpenOption
@@ -779,7 +835,10 @@ func WriteOSFile(f *os.File, data any) (n int, err error)
func CopyFile(srcPath, dstPath string) error
func MustCopyFile(srcPath, dstPath string)
func UpdateContents(filePath string, handleFn func(bs []byte) []byte) error
+func CreateSymlink(target, linkPath string) error
+
+
FsUtil Usage
@@ -811,6 +870,7 @@ func main() {
+
### JSON Utils
> Package `github.com/gookit/goutil/jsonutil`
@@ -841,10 +901,13 @@ func IsObject(s string) bool
func StripComments(src string) string
-### Map
+
+### Maputil
Package
github.com/gookit/goutil/maputil
+
Click to see functions 👈
+
// source at maputil/check.go
func HasKey(mp, key any) (ok bool)
@@ -890,6 +953,7 @@ 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 AppendSMap(dst, src 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
@@ -900,16 +964,23 @@ func MakeByKeys(keys []string, val any) (mp map[string]any)
func SetByPath(mp *map[string]any, path string, val any) error
func SetByKeys(mp *map[string]any, keys []string, val any) (err error)
+
+
Math/Number
Package
github.com/gookit/goutil/mathutil
+Package mathutil provide math(int, number) util functions. eg: convert, math calc, random
+
+
Click to see functions 👈
+
// source at mathutil/calc.go
func Abs[T comdef.Int](val T) T
// source at mathutil/check.go
func IsNumeric(c byte) bool
+func IsInteger(val any) 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
@@ -928,11 +999,7 @@ func SwapMaxInt(x, y int) (int, int)
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]
+// source at mathutil/conv2int.go
func Int(in any) (int, error)
func SafeInt(in any) int
func QuietInt(in any) int
@@ -943,8 +1010,6 @@ func IntOr(in any, defVal int) int
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 Int64(in any) (int64, error)
func SafeInt64(in any) int64
func QuietInt64(in any) int64
@@ -972,6 +1037,19 @@ 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 StrInt(s string) int
+func StrIntOr(s string, defVal int) int
+func TryStrInt(s string) (int, error)
+func TryStrInt64(s string) (int64, error)
+func TryStrUint64(s string) (uint64, error)
+// 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 WithStrictMode[T any](opt *ConvOption[T])
+func WithUserConvFn[T any](fn ToTypeFunc[T]) ConvOptionFn[T]
+func StrictInt(val any) (int64, bool)
+func StrictUint(val any) (uint64, bool)
func QuietFloat(in any) float64
func SafeFloat(in any) float64
func FloatOrPanic(in any) float64
@@ -995,6 +1073,7 @@ 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 FormatBytes(bytes int) string
func HowLongAgo(sec int64) string
// source at mathutil/mathutil.go
func OrElse[T comdef.Number](val, defVal T) T
@@ -1015,11 +1094,17 @@ func RandInt(min, max int) int
func RandIntWithSeed(min, max int, seed int64) int
func RandomIntWithSeed(min, max int, seed int64) int
+
+
Reflects
Package
github.com/gookit/goutil/reflects
+Package reflects Provide extends reflection util functions. eg: check, convert, value set, etc.
+
+
Click to see functions 👈
+
// source at reflects/check.go
func IsTimeType(t reflect.Type) bool
@@ -1085,11 +1170,17 @@ func SetRValue(rv, val reflect.Value)
func Wrap(rv reflect.Value) Value
func ValueOf(v any) Value
+
-### Structs
+
+### Struct Utils
Package
github.com/gookit/goutil/structs
+Package structs Provide some extends util functions for struct. eg: tag parse, struct init, value set/get
+
+
Click to see functions 👈
+
// source at structs/alias.go
func NewAliases(checker func(alias string)) *Aliases
@@ -1140,11 +1231,15 @@ 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
+
-### Strings
+
+### String Utils
Package
github.com/gookit/goutil/strutil
+
Click to see functions 👈
+
// source at strutil/bytes.go
func NewBuffer(initSize ...int) *Buffer
@@ -1152,16 +1247,21 @@ func NewByteChanPool(maxSize, width, capWidth int) *ByteChanPool
// source at strutil/check.go
func IsNumChar(c byte) bool
func IsInt(s string) bool
+func IsUint(s string) bool
func IsFloat(s string) bool
func IsNumeric(s string) bool
+func IsPositiveNum(s string) bool
func IsAlphabet(char uint8) bool
func IsAlphaNum(c uint8) bool
+func IsUpper(s string) bool
+func IsLower(s string) bool
func StrPos(s, sub string) int
func BytePos(s string, bt byte) int
func IEqual(s1, s2 string) bool
func NoCaseEq(s, t string) bool
func IContains(s, sub string) bool
func ContainsByte(s string, c byte) bool
+func ContainsByteOne(s string, bs []byte) bool
func ContainsOne(s string, subs []string) bool
func HasOneSub(s string, subs []string) bool
func IContainsOne(s string, subs []string) bool
@@ -1186,6 +1286,7 @@ func HasEmpty(ss ...string) bool
func IsAllEmpty(ss ...string) bool
func IsVersion(s string) bool
func IsVarName(s string) bool
+func IsEnvName(s string) bool
func Compare(s1, s2, op string) bool
func VersionCompare(v1, v2, op string) bool
func SimpleMatch(s string, keywords []string) bool
@@ -1197,7 +1298,9 @@ func MatchNodePath(pattern, s string, sep string) bool
// source at strutil/convbase.go
func Base10Conv(src string, to int) string
func BaseConv(src string, from, to int) string
+func BaseConvInt(src uint64, to int) string
func BaseConvByTpl(src string, fromBase, toBase string) string
+func BaseConvIntByTpl(dec uint64, toBase string) string
// source at strutil/convert.go
func Quote(s string) string
func Unquote(s string) string
@@ -1257,7 +1360,6 @@ func ToArray(s string, sep ...string) []string
func Strings(s string, sep ...string) []string
func ToStrings(s string, sep ...string) []string
func ToSlice(s string, sep ...string) []string
-func ToOSArgs(s string) []string
func ToDuration(s string) (time.Duration, error)
// source at strutil/encode.go
func EscapeJS(s string) string
@@ -1299,6 +1401,10 @@ func Camel(s string, sep ...string) string
func CamelCase(s string, sep ...string) string
func Indent(s, prefix string) string
func IndentBytes(b, prefix []byte) []byte
+func Replaces(str string, pairs map[string]string) string
+func ReplaceVars(s string, vars map[string]string) string
+func NewReplacer(pairs map[string]string) *strings.Replacer
+func WrapTag(s, tag string) string
// source at strutil/gensn.go
func MicroTimeID() string
func MicroTimeHexID() string
@@ -1307,7 +1413,8 @@ func MTimeBase36() string
func MTimeBaseID(toBase int) string
func DatetimeNo(prefix string) string
func DateSN(prefix string) string
-func DateSNV2(prefix string, extBase ...int) string
+func DateSNv2(prefix string, extBase ...int) string
+func DateSNv3(prefix string, dateLen int, extBase ...int) string
// source at strutil/hash.go
func Md5(src any) string
func MD5(src any) string
@@ -1335,6 +1442,7 @@ func RepeatRune(char rune, times int) []rune
func RepeatBytes(char byte, times int) []byte
func RepeatChars[T byte | rune](char T, times int) []T
// source at strutil/parse.go
+func NumVersion(s string) string
func MustToTime(s string, layouts ...string) time.Time
func ToTime(s string, layouts ...string) (t time.Time, err error)
func ParseSizeRange(expr string, opt *ParseSizeOpt) (min, max uint64, err error)
@@ -1398,31 +1506,17 @@ func OrElse(s, orVal string) string
func OrElseNilSafe(s *string, orVal string) string
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 WrapTag(s, tag string) string
func SubstrCount(s, substr string, params ...uint64) (int, error)
+
-### Syncs
-> Package github.com/gookit/goutil/syncs
-go -// source at syncs/chan.go -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)) -
System Utils
Package
github.com/gookit/goutil/sysutil
+
Click to see functions 👈
+
// source at sysutil/exec.go
func NewCmd(bin string, args ...string) *cmdr.Cmd
@@ -1473,6 +1567,13 @@ 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/sysutil_unix.go
+func IsWin() bool
+func IsWindows() bool
+func IsMac() bool
+func IsDarwin() bool
+func IsLinux() bool
+func OpenURL(URL string) error
// source at sysutil/user.go
func MustFindUser(uname string) *user.User
func LoginUser() *user.User
@@ -1491,24 +1592,29 @@ func ChangeUserByName(newUname string) error
func ChangeUserUidGid(newUID int, newGid int) error
func ChangeUserUIDGid(newUID int, newGid int) (err error)
+
+
Testing Utils
Package
github.com/gookit/goutil/testutil
+
Click to see functions 👈
+
// source at testutil/buffer.go
func NewBuffer() *byteutil.Buffer
+func NewSafeBuffer() *SafeBuffer
// source at testutil/envmock.go
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 MockOsEnv(mp map[string]string, fn func())
func MockCleanOsEnv(mp map[string]string, fn func())
// source at testutil/httpmock.go
func NewHTTPRequest(method, path string, data *MD) *http.Request
@@ -1533,12 +1639,17 @@ func RestoreTimeLocal()
func NewTestWriter() *TestWriter
func NewDirEnt(fPath string, isDir ...bool) *fakeobj.DirEntry
+
+
Timex
Package
github.com/gookit/goutil/timex
-Provides an enhanced time.Time implementation, and add more commonly used functional methods.
+Provides an enhanced time.Time implementation, and add more commonly used functional methods.
+
+
Click to see functions 👈
+
// source at timex/check.go
func IsDuration(s string) bool
@@ -1546,12 +1657,14 @@ func InRange(dst, start, end time.Time) bool
// source at timex/conv.go
func Elapsed(start, end time.Time) string
func ElapsedNow(start time.Time) string
+func FormatDuration(d time.Duration) string
func FromNow(t time.Time) string
func FromNowWith(u time.Time, tms []TimeMessage) string
func HowLongAgo(diffSec int64) string
func HowLongAgo2(diffSec int64, tms []TimeMessage) string
func ToTime(s string, layouts ...string) (time.Time, error)
func ToDur(s string) (time.Duration, error)
+func ParseDuration(s string) (time.Duration, error)
func ToDuration(s string) (time.Duration, error)
func TryToTime(s string, bt time.Time) (time.Time, error)
func ParseRange(expr string, opt *ParseRangeOpt) (start, end time.Time, err error)
@@ -1600,6 +1713,8 @@ func FormatUnix(sec int64, layout ...string) string
func FormatUnixBy(sec int64, layout string) string
func FormatUnixByTpl(sec int64, template ...string) string
+
+
Timex Usage
Create timex instance
@@ -1719,6 +1834,7 @@ date := FormatUnixByTpl(ts, "Y-m-d H:I:S") // Get: 2022-04-20 19:40:34
+
## Code Check & Testing
```bash
diff --git a/vendor/github.com/gookit/goutil/README.zh-CN.md b/vendor/github.com/gookit/goutil/README.zh-CN.md
index ee6d10db0e0..17d654cd612 100644
--- a/vendor/github.com/gookit/goutil/README.zh-CN.md
+++ b/vendor/github.com/gookit/goutil/README.zh-CN.md
@@ -1,4 +1,4 @@
-# Go Util
+# GoUtil

[](https://github.com/gookit/goutil)
@@ -7,7 +7,7 @@
[](https://coveralls.io/github/gookit/goutil?branch=master)
[](https://pkg.go.dev/github.com/gookit/goutil)
-`goutil` Go 常用功能的扩展工具库(**800+**)。包含:数字,byte, 字符串,slice/数组,Map,结构体,反射,文本,文件,错误,时间日期,测试,特殊处理,格式化,常用信息获取等等。
+`goutil` Go 常用功能的扩展工具库(**900+**)。包含:数字,byte, 字符串,slice/数组,Map,结构体,反射,文本,文件,错误,时间日期,测试,特殊处理,格式化,常用信息获取等等。
> **[EN README](README.md)**
@@ -53,17 +53,17 @@
- [`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) 提供一些进程操作相关的实用功能。
+- [`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)
+- Wiki docs on [ZRead.ai - gookit/goutil](https://zread.ai/gookit/goutil)
## 获取
@@ -109,6 +109,8 @@ dump.Print(somevar, somevar2, ...)
> Package `github.com/gookit/goutil/arrutil`
+<details><summary>Click to see functions 👈</summary>
+
```go
// source at arrutil/arrutil.go
func GetRandomOne[T any](arr []T) T
@@ -152,6 +154,7 @@ func IntsToString[T comdef.Integer](ints []T) string
func ToInt64s(arr any) (ret []int64, err error)
func MustToInt64s(arr any) []int64
func SliceToInt64s(arr []any) []int64
+func ToMap[T any, K comdef.ScalarType, V any](list []T, mapFn func(T) (K, V)) map[K]V
func AnyToSlice(sl any) (ls []any, err error)
func AnyToStrings(arr any) []string
func MustToStrings(arr any) []string
@@ -171,7 +174,8 @@ func FormatIndent(arr any, indent string) string
func Reverse[T any](ls []T)
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 Map[T, V any](list []T, mapFn MapFn[T, V]) []V
+func Map1[T, R any](list []T, fn func(t T) R) []R
func Column[T any, V any](list []T, mapFn func(obj T) (val V, find bool)) []V
func Unique[T comdef.NumberOrString](list []T) []T
func IndexOf[T comdef.NumberOrString](val T, list []T) int
@@ -189,6 +193,8 @@ func StringsFilter(ss []string, filter ...comdef.StringMatchFunc) []string
func StringsMap(ss []string, mapFn func(s string) string) []string
func TrimStrings(ss []string, cutSet ...string) []string
+
+
ArrUtil Usage
check value:
@@ -211,10 +217,13 @@ ss, err := arrutil.ToStrings([]int{1, 2}) // ss: []string{"1", "2"}
+
### Bytes Utils
> Package `github.com/gookit/goutil/byteutil`
+<details><summary>Click to see functions 👈</summary>
+
```go
// source at byteutil/buffer.go
func NewBuffer() *Buffer
@@ -244,18 +253,22 @@ func NewStdEncoder(encFn BytesEncodeFunc, decFn BytesDecodeFunc) *StdEncoder
// source at byteutil/pool.go
func NewChanPool(chSize int, width int, capWidth int) *ChanPool
+
+
Cflag
Package
github.com/gookit/goutil/cflag
+cflag - Wraps and extends go flag.FlagSet to build simple command line applications
+
+
+
Click to see functions 👈
+
-// source at cflag/app.go
-func NewApp(fns ...func(app *App)) *App
-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
+func NewWith(name, version, desc string, fns ...func(c *CFlags)) *CFlags
func NewEmpty(fns ...func(c *CFlags)) *CFlags
func WithDesc(desc string) func(c *CFlags)
func WithVersion(version string) func(c *CFlags)
@@ -269,6 +282,8 @@ func Value
// source at cflag/optarg.go
func NewArg(name, desc string, required bool) *FlagArg
// source at cflag/util.go
+func SetDebug(open bool)
+func DebugMsg(format string, args ...any)
func IsGoodName(name string) bool
func IsZeroValue(opt *flag.Flag, value string) (bool, bool)
func AddPrefix(name string) string
@@ -280,15 +295,20 @@ func IsFlagHelpErr(err error) bool
func WrapColorForCode(s string) string
func ReplaceShorts(args []string, shortsMap map[string]string) []string
+
+
cflag Usage
-cflag 使用说明请看 cflag/README.zh-CN.md
+cflag usage please see cflag/README.md
+
CLI Utils
Package
github.com/gookit/goutil/cliutil
+
Click to see functions 👈
+
// source at cliutil/cliutil.go
func SplitMulti(ss []string, sep string) []string
@@ -325,6 +345,8 @@ func InputIsYes(ans string) bool
func ByteIsYes(ans byte) bool
func ReadPassword(question ...string) string
+
+
CLI Util Usage
@@ -381,10 +403,13 @@ Build line: ./myapp -a val0 -m "this is message" arg0
More, please see ./cliutil/README
Var Dumper
Package
github.com/gookit/goutil/dump
+
Click to see functions 👈
+
// source at dump/dump.go
func Std() *Dumper
@@ -411,7 +436,10 @@ func WithoutPosition() OptionFunc
func WithoutOutput(out io.Writer) OptionFunc
func WithoutColor() OptionFunc
func WithoutType() OptionFunc
+func WithoutLen() OptionFunc
+
+
Examples
example code:
@@ -456,10 +484,13 @@ Preview:

ENV/Environment
Package
github.com/gookit/goutil/envutil
+
Click to see functions 👈
+
// source at envutil/envutil.go
func VarReplace(s string) string
@@ -503,6 +534,8 @@ func UnsetEnvs(keys ...string)
func LoadText(text string)
func LoadString(line string) bool
+
+
ENV Util Usage
helper functions:
@@ -522,15 +555,18 @@ envutil.ParseValue("${ENV_NAME | defValue}")
+
### Errorx
> Package `github.com/gookit/goutil/errorx`
-`errorx` 提供了增强的错误报告实现,包含调用堆栈信息并且可以包装上一级错误。
+`errorx` provides an enhanced error reporting implementation that contains call stack information and can wrap the previous level of error.
-> 在打印 error 时会额外附带调用栈信息, 方便记录日志和查找问题。
+> Additional call stack information is included when printing errors, making it easy to log and find problems.
+<details><summary>Click to see functions 👈</summary>
+
```go
// source at errorx/assert.go
func IsTrue(result bool, fmtAndArgs ...any) error
@@ -580,12 +616,14 @@ func Is(err, target error) bool
func To(err error, target any) bool
func As(err error, target any) bool
+
+
-#### Errorx 使用示例
+#### Errorx Usage
-创建错误带有调用栈信息
+Create error with call stack info
-- 使用 errorx.New 替代 errors.New
+- use the errorx.New instead errors.New
func doSomething() error {
@@ -596,7 +634,7 @@ func doSomething() error {
}
-- 使用 errorx.Newf 或者 errorx.Errorf 替代 fmt.Errorf
+- use the errorx.Newf or errorx.Errorf instead fmt.Errorf
func doSomething() error {
@@ -607,9 +645,9 @@ func doSomething() error {
}
-包装上一级错误
+Wrap the previous error
-之前这样使用:
+used like this before:
if err := SomeFunc(); err != nil {
@@ -617,7 +655,7 @@ func doSomething() error {
}
-可以替换成:
+can be replaced with:
if err := SomeFunc(); err != nil {
@@ -625,9 +663,9 @@ func doSomething() error {
}
-使用效果示例
+Print the errorx.New() error
-更多关于 errorx 的使用请看 ./errorx/README
+Examples for use errorx package, more please see ./errorx/README
err := errorx.New("the error message")
@@ -657,16 +695,23 @@ runtime.goexit()
File System
Package
github.com/gookit/goutil/fsutil
+Package fsutil Filesystem util functions: quick check, create, read and write file. eg: file and dir check, operate
+
+
+
Click to see functions 👈
+
// source at fsutil/check.go
func PathExists(path string) bool
func IsDir(path string) bool
func FileExists(path string) bool
func IsFile(path string) bool
+func IsSymlink(path string) bool
func IsAbsPath(aPath string) bool
func IsEmptyDir(dirPath string) bool
func IsImageFile(path string) bool
@@ -682,6 +727,10 @@ func FirstExistsDir(paths ...string) string
func FirstExistsFile(paths ...string) string
func MatchPaths(paths []string, matcher PathMatchFunc) []string
func MatchFirst(paths []string, matcher PathMatchFunc, defaultPath string) string
+func FindAllInParentDirs(dirPath, name string, optFns ...FindParentOptFn) []string
+func FindOneInParentDirs(dirPath, name string, optFns ...FindParentOptFn) string
+func FindNameInParentDirs(dirPath, name string, collectFn func(fullPath string), optFns ...FindParentOptFn)
+func FindInParentDirs(dirPath string, matchFunc func(dir string) bool, maxLevel int)
func SearchNameUp(dirPath, name string) string
func SearchNameUpx(dirPath, name string) (string, bool)
func WalkDir(dir string, fn fs.WalkDirFunc) error
@@ -715,9 +764,12 @@ func FileExt(fPath string) string
func Extname(fPath string) string
func Suffix(fPath string) string
func Expand(pathStr string) string
+func ExpandHome(pathStr string) string
func ExpandPath(pathStr string) string
func ResolvePath(pathStr string) string
func SplitPath(pathStr string) (dir, name string)
+func UserHomeDir() string
+func HomeDir() string
// source at fsutil/info_nonwin.go
func Realpath(pathStr string) string
// source at fsutil/mime.go
@@ -725,9 +777,11 @@ func DetectMime(path string) string
func MimeType(path string) (mime string)
func ReaderMimeType(r io.Reader) (mime string)
// source at fsutil/operate.go
-func Mkdir(dirPath string, perm os.FileMode) error
-func MkDirs(perm os.FileMode, dirPaths ...string) error
-func MkSubDirs(perm os.FileMode, parentDir string, subDirs ...string) error
+func Mkdir(dirPath string, perm fs.FileMode) error
+func MkdirQuick(dirPath string) error
+func EnsureDir(path string) error
+func MkDirs(perm fs.FileMode, dirPaths ...string) error
+func MkSubDirs(perm fs.FileMode, parentDir string, subDirs ...string) error
func MkParentDir(fpath string) error
func NewOpenOption(optFns ...OpenOptionFunc) *OpenOption
func OpenOptOrNew(opt *OpenOption) *OpenOption
@@ -781,7 +835,10 @@ func WriteOSFile(f *os.File, data any) (n int, err error)
func CopyFile(srcPath, dstPath string) error
func MustCopyFile(srcPath, dstPath string)
func UpdateContents(filePath string, handleFn func(bs []byte) []byte) error
+func CreateSymlink(target, linkPath string) error
+
+
FsUtil Usage
@@ -813,6 +870,7 @@ func main() {
+
### JSON Utils
> Package `github.com/gookit/goutil/jsonutil`
@@ -843,10 +901,13 @@ func IsObject(s string) bool
func StripComments(src string) string
-### Map
+
+### Maputil
Package
github.com/gookit/goutil/maputil
+
Click to see functions 👈
+
// source at maputil/check.go
func HasKey(mp, key any) (ok bool)
@@ -892,6 +953,7 @@ 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 AppendSMap(dst, src 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
@@ -902,16 +964,23 @@ func MakeByKeys(keys []string, val any) (mp map[string]any)
func SetByPath(mp *map[string]any, path string, val any) error
func SetByKeys(mp *map[string]any, keys []string, val any) (err error)
+
+
Math/Number
Package
github.com/gookit/goutil/mathutil
+Package mathutil provide math(int, number) util functions. eg: convert, math calc, random
+
+
Click to see functions 👈
+
// source at mathutil/calc.go
func Abs[T comdef.Int](val T) T
// source at mathutil/check.go
func IsNumeric(c byte) bool
+func IsInteger(val any) 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
@@ -930,11 +999,7 @@ func SwapMaxInt(x, y int) (int, int)
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]
+// source at mathutil/conv2int.go
func Int(in any) (int, error)
func SafeInt(in any) int
func QuietInt(in any) int
@@ -945,8 +1010,6 @@ func IntOr(in any, defVal int) int
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 Int64(in any) (int64, error)
func SafeInt64(in any) int64
func QuietInt64(in any) int64
@@ -974,6 +1037,19 @@ 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 StrInt(s string) int
+func StrIntOr(s string, defVal int) int
+func TryStrInt(s string) (int, error)
+func TryStrInt64(s string) (int64, error)
+func TryStrUint64(s string) (uint64, error)
+// 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 WithStrictMode[T any](opt *ConvOption[T])
+func WithUserConvFn[T any](fn ToTypeFunc[T]) ConvOptionFn[T]
+func StrictInt(val any) (int64, bool)
+func StrictUint(val any) (uint64, bool)
func QuietFloat(in any) float64
func SafeFloat(in any) float64
func FloatOrPanic(in any) float64
@@ -997,6 +1073,7 @@ 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 FormatBytes(bytes int) string
func HowLongAgo(sec int64) string
// source at mathutil/mathutil.go
func OrElse[T comdef.Number](val, defVal T) T
@@ -1017,11 +1094,17 @@ func RandInt(min, max int) int
func RandIntWithSeed(min, max int, seed int64) int
func RandomIntWithSeed(min, max int, seed int64) int
+
+
Reflects
Package
github.com/gookit/goutil/reflects
+Package reflects Provide extends reflection util functions. eg: check, convert, value set, etc.
+
+
Click to see functions 👈
+
// source at reflects/check.go
func IsTimeType(t reflect.Type) bool
@@ -1087,11 +1170,17 @@ func SetRValue(rv, val reflect.Value)
func Wrap(rv reflect.Value) Value
func ValueOf(v any) Value
+
-### Structs
+
+### Struct Utils
Package
github.com/gookit/goutil/structs
+Package structs Provide some extends util functions for struct. eg: tag parse, struct init, value set/get
+
+
Click to see functions 👈
+
// source at structs/alias.go
func NewAliases(checker func(alias string)) *Aliases
@@ -1142,11 +1231,15 @@ 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
+
-### Strings
+
+### String Utils
Package
github.com/gookit/goutil/strutil
+
Click to see functions 👈
+
// source at strutil/bytes.go
func NewBuffer(initSize ...int) *Buffer
@@ -1154,16 +1247,21 @@ func NewByteChanPool(maxSize, width, capWidth int) *ByteChanPool
// source at strutil/check.go
func IsNumChar(c byte) bool
func IsInt(s string) bool
+func IsUint(s string) bool
func IsFloat(s string) bool
func IsNumeric(s string) bool
+func IsPositiveNum(s string) bool
func IsAlphabet(char uint8) bool
func IsAlphaNum(c uint8) bool
+func IsUpper(s string) bool
+func IsLower(s string) bool
func StrPos(s, sub string) int
func BytePos(s string, bt byte) int
func IEqual(s1, s2 string) bool
func NoCaseEq(s, t string) bool
func IContains(s, sub string) bool
func ContainsByte(s string, c byte) bool
+func ContainsByteOne(s string, bs []byte) bool
func ContainsOne(s string, subs []string) bool
func HasOneSub(s string, subs []string) bool
func IContainsOne(s string, subs []string) bool
@@ -1188,6 +1286,7 @@ func HasEmpty(ss ...string) bool
func IsAllEmpty(ss ...string) bool
func IsVersion(s string) bool
func IsVarName(s string) bool
+func IsEnvName(s string) bool
func Compare(s1, s2, op string) bool
func VersionCompare(v1, v2, op string) bool
func SimpleMatch(s string, keywords []string) bool
@@ -1199,7 +1298,9 @@ func MatchNodePath(pattern, s string, sep string) bool
// source at strutil/convbase.go
func Base10Conv(src string, to int) string
func BaseConv(src string, from, to int) string
+func BaseConvInt(src uint64, to int) string
func BaseConvByTpl(src string, fromBase, toBase string) string
+func BaseConvIntByTpl(dec uint64, toBase string) string
// source at strutil/convert.go
func Quote(s string) string
func Unquote(s string) string
@@ -1259,7 +1360,6 @@ func ToArray(s string, sep ...string) []string
func Strings(s string, sep ...string) []string
func ToStrings(s string, sep ...string) []string
func ToSlice(s string, sep ...string) []string
-func ToOSArgs(s string) []string
func ToDuration(s string) (time.Duration, error)
// source at strutil/encode.go
func EscapeJS(s string) string
@@ -1301,6 +1401,10 @@ func Camel(s string, sep ...string) string
func CamelCase(s string, sep ...string) string
func Indent(s, prefix string) string
func IndentBytes(b, prefix []byte) []byte
+func Replaces(str string, pairs map[string]string) string
+func ReplaceVars(s string, vars map[string]string) string
+func NewReplacer(pairs map[string]string) *strings.Replacer
+func WrapTag(s, tag string) string
// source at strutil/gensn.go
func MicroTimeID() string
func MicroTimeHexID() string
@@ -1309,7 +1413,8 @@ func MTimeBase36() string
func MTimeBaseID(toBase int) string
func DatetimeNo(prefix string) string
func DateSN(prefix string) string
-func DateSNV2(prefix string, extBase ...int) string
+func DateSNv2(prefix string, extBase ...int) string
+func DateSNv3(prefix string, dateLen int, extBase ...int) string
// source at strutil/hash.go
func Md5(src any) string
func MD5(src any) string
@@ -1337,6 +1442,7 @@ func RepeatRune(char rune, times int) []rune
func RepeatBytes(char byte, times int) []byte
func RepeatChars[T byte | rune](char T, times int) []T
// source at strutil/parse.go
+func NumVersion(s string) string
func MustToTime(s string, layouts ...string) time.Time
func ToTime(s string, layouts ...string) (t time.Time, err error)
func ParseSizeRange(expr string, opt *ParseSizeOpt) (min, max uint64, err error)
@@ -1400,31 +1506,17 @@ func OrElse(s, orVal string) string
func OrElseNilSafe(s *string, orVal string) string
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 WrapTag(s, tag string) string
func SubstrCount(s, substr string, params ...uint64) (int, error)
+
-### Syncs
-> Package github.com/gookit/goutil/syncs
-go -// source at syncs/chan.go -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)) -
System Utils
Package
github.com/gookit/goutil/sysutil
+
Click to see functions 👈
+
// source at sysutil/exec.go
func NewCmd(bin string, args ...string) *cmdr.Cmd
@@ -1475,6 +1567,13 @@ 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/sysutil_unix.go
+func IsWin() bool
+func IsWindows() bool
+func IsMac() bool
+func IsDarwin() bool
+func IsLinux() bool
+func OpenURL(URL string) error
// source at sysutil/user.go
func MustFindUser(uname string) *user.User
func LoginUser() *user.User
@@ -1493,24 +1592,29 @@ func ChangeUserByName(newUname string) error
func ChangeUserUidGid(newUID int, newGid int) error
func ChangeUserUIDGid(newUID int, newGid int) (err error)
+
+
Testing Utils
Package
github.com/gookit/goutil/testutil
+
Click to see functions 👈
+
// source at testutil/buffer.go
func NewBuffer() *byteutil.Buffer
+func NewSafeBuffer() *SafeBuffer
// source at testutil/envmock.go
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 MockOsEnv(mp map[string]string, fn func())
func MockCleanOsEnv(mp map[string]string, fn func())
// source at testutil/httpmock.go
func NewHTTPRequest(method, path string, data *MD) *http.Request
@@ -1535,12 +1639,17 @@ func RestoreTimeLocal()
func NewTestWriter() *TestWriter
func NewDirEnt(fPath string, isDir ...bool) *fakeobj.DirEntry
+
+
Timex
Package
github.com/gookit/goutil/timex
-Provides an enhanced time.Time implementation, and add more commonly used functional methods.
+Provides an enhanced time.Time implementation, and add more commonly used functional methods.
+
+
Click to see functions 👈
+
// source at timex/check.go
func IsDuration(s string) bool
@@ -1548,12 +1657,14 @@ func InRange(dst, start, end time.Time) bool
// source at timex/conv.go
func Elapsed(start, end time.Time) string
func ElapsedNow(start time.Time) string
+func FormatDuration(d time.Duration) string
func FromNow(t time.Time) string
func FromNowWith(u time.Time, tms []TimeMessage) string
func HowLongAgo(diffSec int64) string
func HowLongAgo2(diffSec int64, tms []TimeMessage) string
func ToTime(s string, layouts ...string) (time.Time, error)
func ToDur(s string) (time.Duration, error)
+func ParseDuration(s string) (time.Duration, error)
func ToDuration(s string) (time.Duration, error)
func TryToTime(s string, bt time.Time) (time.Time, error)
func ParseRange(expr string, opt *ParseRangeOpt) (start, end time.Time, err error)
@@ -1602,6 +1713,8 @@ func FormatUnix(sec int64, layout ...string) string
func FormatUnixBy(sec int64, layout string) string
func FormatUnixByTpl(sec int64, template ...string) string
+
+
Timex Usage
Create timex instance
@@ -1721,6 +1834,7 @@ date := FormatUnixByTpl(ts, "Y-m-d H:I:S") // Get: 2022-04-20 19:40:34
+
## Code Check & Testing
```bash
diff --git a/vendor/github.com/gookit/goutil/arrutil/convert.go b/vendor/github.com/gookit/goutil/arrutil/convert.go
index 9b7fe30f09b..a505e14fe19 100644
--- a/vendor/github.com/gookit/goutil/arrutil/convert.go
+++ b/vendor/github.com/gookit/goutil/arrutil/convert.go
@@ -125,6 +125,27 @@ func SliceToInt64s(arr []any) []int64 {
return i64s
}
+// ToMap convert a list to new map.
+//
+// Example:
+// type User struct {
+// Name string
+// Age int
+// }
+// users := []User{{"Tom", 18}, {"Jack", 20}}
+// mp := arrutil.ToMap(users, func(u User) (string, int) {
+// return u.Name, u.Age
+// })
+// // mp = map[string]int{"Tom":18, "Jack":20}
+func ToMap[T any, K comdef.ScalarType, V any](list []T, mapFn func(T) (K, V)) map[K]V {
+ mp := make(map[K]V, len(list))
+ for _, item := range list {
+ k, v := mapFn(item)
+ mp[k] = v
+ }
+ return mp
+}
+
/*************************************************************
* convert func for any-slice
*************************************************************/
diff --git a/vendor/github.com/gookit/goutil/arrutil/process.go b/vendor/github.com/gookit/goutil/arrutil/process.go
index dc72c9df65e..0bbb8532fa8 100644
--- a/vendor/github.com/gookit/goutil/arrutil/process.go
+++ b/vendor/github.com/gookit/goutil/arrutil/process.go
@@ -38,6 +38,9 @@ func Filter[T any](ls []T, filter ...comdef.MatchFunc[T]) []T {
fn = filter[0]
} else {
fn = func(el T) bool {
+ // if el == nil { // Filter nil value
+ // return false
+ // }
return !reflect.ValueOf(el).IsZero()
}
}
@@ -57,7 +60,7 @@ type MapFn[T any, V any] func(input T) (target V, find bool)
// Map a list to new list
//
// eg: mapping [object0{},object1{},...] to flatten list [object0.someKey, object1.someKey, ...]
-func Map[T any, V any](list []T, mapFn MapFn[T, V]) []V {
+func Map[T, V any](list []T, mapFn MapFn[T, V]) []V {
flatArr := make([]V, 0, len(list))
for _, obj := range list {
@@ -68,6 +71,16 @@ func Map[T any, V any](list []T, mapFn MapFn[T, V]) []V {
return flatArr
}
+// Map1 a list to new list, alias of Map func
+func Map1[T, R any](list []T, fn func(t T) R) []R {
+ ret := make([]R, len(list))
+
+ for i := range list {
+ ret[i] = fn(list[i])
+ }
+ return ret
+}
+
// Column alias of Map func
func Column[T any, V any](list []T, mapFn func(obj T) (val V, find bool)) []V {
return Map(list, mapFn)
diff --git a/vendor/github.com/gookit/goutil/byteutil/buffer.go b/vendor/github.com/gookit/goutil/byteutil/buffer.go
index 6a833627cd3..f8690cede57 100644
--- a/vendor/github.com/gookit/goutil/byteutil/buffer.go
+++ b/vendor/github.com/gookit/goutil/byteutil/buffer.go
@@ -16,9 +16,7 @@ type Buffer struct {
}
// NewBuffer instance
-func NewBuffer() *Buffer {
- return &Buffer{}
-}
+func NewBuffer() *Buffer { return &Buffer{} }
// PrintByte to buffer, ignore error. alias of WriteByte()
func (b *Buffer) PrintByte(c byte) {
@@ -26,14 +24,10 @@ func (b *Buffer) PrintByte(c byte) {
}
// WriteStr1 quiet write one string to buffer
-func (b *Buffer) WriteStr1(s string) {
- b.writeStringNl(s, false)
-}
+func (b *Buffer) WriteStr1(s string) { b.writeStringNl(s, false) }
// WriteStr1Nl quiet write one string and end with newline
-func (b *Buffer) WriteStr1Nl(s string) {
- b.writeStringNl(s, true)
-}
+func (b *Buffer) WriteStr1Nl(s string) { b.writeStringNl(s, true) }
// writeStringNl quiet write one string and end with newline
func (b *Buffer) writeStringNl(s string, nl bool) {
@@ -93,6 +87,9 @@ func (b *Buffer) writeAnysWithNl(vs []any, nl bool) {
}
}
+// Writef write message to buffer, ignore error. alias of Printf()
+func (b *Buffer) Writef(tpl string, vs ...any) { _, _ = fmt.Fprintf(b, tpl, vs...) }
+
// Printf quick write message to buffer, ignore error.
func (b *Buffer) Printf(tpl string, vs ...any) { _, _ = fmt.Fprintf(b, tpl, vs...) }
@@ -112,16 +109,10 @@ func (b *Buffer) ResetAndGet() string {
}
// Close buffer
-func (b *Buffer) Close() error {
- return b.CloseErr
-}
+func (b *Buffer) Close() error { return b.CloseErr }
// Flush buffer
-func (b *Buffer) Flush() error {
- return b.FlushErr
-}
+func (b *Buffer) Flush() error { return b.FlushErr }
// Sync anf flush buffer
-func (b *Buffer) Sync() error {
- return b.SyncErr
-}
+func (b *Buffer) Sync() error { return b.SyncErr }
diff --git a/vendor/github.com/gookit/goutil/byteutil/check.go b/vendor/github.com/gookit/goutil/byteutil/check.go
index f4d2e5ba607..cab9c7a8693 100644
--- a/vendor/github.com/gookit/goutil/byteutil/check.go
+++ b/vendor/github.com/gookit/goutil/byteutil/check.go
@@ -7,4 +7,3 @@ func IsNumChar(c byte) bool { return c >= '0' && c <= '9' }
func IsAlphaChar(c byte) bool {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
}
-
diff --git a/vendor/github.com/gookit/goutil/conv.go b/vendor/github.com/gookit/goutil/conv.go
index f9e8047a0b2..20eff91cba1 100644
--- a/vendor/github.com/gookit/goutil/conv.go
+++ b/vendor/github.com/gookit/goutil/conv.go
@@ -122,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/info.go b/vendor/github.com/gookit/goutil/envutil/info.go
index b18cb448553..9ff250b648c 100644
--- a/vendor/github.com/gookit/goutil/envutil/info.go
+++ b/vendor/github.com/gookit/goutil/envutil/info.go
@@ -36,7 +36,6 @@ func IsMSys() bool {
return sysutil.IsMSys()
}
-
// IsTerminal isatty check
//
// Usage:
diff --git a/vendor/github.com/gookit/goutil/envutil/set.go b/vendor/github.com/gookit/goutil/envutil/set.go
index b317155f542..8c7fa6b570a 100644
--- a/vendor/github.com/gookit/goutil/envutil/set.go
+++ b/vendor/github.com/gookit/goutil/envutil/set.go
@@ -34,7 +34,8 @@ func UnsetEnvs(keys ...string) {
// LoadText parse multiline text to ENV. Can use to load .env file contents.
//
// Usage:
-// envutil.LoadText(fsutil.ReadFile(".env"))
+//
+// envutil.LoadText(fsutil.ReadFile(".env"))
func LoadText(text string) {
envMp := SplitText2map(text)
for key, value := range envMp {
diff --git a/vendor/github.com/gookit/goutil/fsutil/check.go b/vendor/github.com/gookit/goutil/fsutil/check.go
index a015d40b02b..337a10e8085 100644
--- a/vendor/github.com/gookit/goutil/fsutil/check.go
+++ b/vendor/github.com/gookit/goutil/fsutil/check.go
@@ -61,6 +61,8 @@ func FileExists(path string) bool {
}
// IsFile reports whether the named file or directory exists.
+//
+// - NOTE: not support symlink file
func IsFile(path string) bool {
if path == "" || len(path) > 468 {
return false
@@ -72,6 +74,15 @@ func IsFile(path string) bool {
return false
}
+// IsSymlink reports whether the named file is a symlink.
+func IsSymlink(path string) bool {
+ fi, err := os.Lstat(path)
+ if err != nil {
+ return false
+ }
+ return fi.Mode()&os.ModeSymlink != 0
+}
+
// IsAbsPath is abs path.
func IsAbsPath(aPath string) bool {
if len(aPath) > 0 {
diff --git a/vendor/github.com/gookit/goutil/fsutil/find.go b/vendor/github.com/gookit/goutil/fsutil/find.go
index 4dadc430722..5eea9f78083 100644
--- a/vendor/github.com/gookit/goutil/fsutil/find.go
+++ b/vendor/github.com/gookit/goutil/fsutil/find.go
@@ -23,7 +23,7 @@ func FilePathInDirs(fPath string, dirs ...string) string {
}
for _, dirPath := range dirs {
- fPath := JoinSubPaths(dirPath, fPath)
+ fPath = JoinSubPaths(dirPath, fPath)
if FileExists(fPath) {
return fPath
}
@@ -67,6 +67,103 @@ func MatchFirst(paths []string, matcher PathMatchFunc, defaultPath string) strin
return defaultPath
}
+// FindParentOption options
+type FindParentOption struct {
+ MaxLevel int // default: 10
+ // NeedDir true: find dirs; false(default): find files
+ NeedDir bool
+ OnlyOne bool // only find one, default: true
+ // Collector func
+ Collector func(fullPath string)
+ // MatchFunc custom matcher func. return false to stop find.
+ MatchFunc func(currentDir string) bool
+}
+
+// FindParentOptFn find parent option func
+type FindParentOptFn func(opt *FindParentOption)
+
+// FindAllInParentDirs looks for all match file(default)/dir in the current directory and parent directories
+func FindAllInParentDirs(dirPath, name string, optFns ...FindParentOptFn) []string {
+ var foundPaths []string
+ optFns = append(optFns, func(opt *FindParentOption) {
+ opt.OnlyOne = false
+ })
+
+ FindNameInParentDirs(dirPath, name, func(fullPath string) {
+ foundPaths = append(foundPaths, fullPath)
+ }, optFns...)
+ return foundPaths
+}
+
+// FindOneInParentDirs looks for a file(default)/dir in the current directory and parent directories
+func FindOneInParentDirs(dirPath, name string, optFns ...FindParentOptFn) string {
+ var foundPath string
+ FindNameInParentDirs(dirPath, name, func(fullPath string) {
+ foundPath = fullPath
+ }, optFns...)
+ return foundPath
+}
+
+// FindNameInParentDirs looks for file(default)/dir in the current directory and parent directories
+func FindNameInParentDirs(dirPath, name string, collectFn func(fullPath string), optFns ...FindParentOptFn) {
+ opts := &FindParentOption{
+ MaxLevel: 10,
+ OnlyOne: true,
+ Collector: collectFn,
+ }
+ for _, fn := range optFns {
+ fn(opts)
+ }
+
+ FindInParentDirs(dirPath, func(currentDir string) bool {
+ filePath := filepath.Join(currentDir, name)
+ if fi, err := os.Stat(filePath); err == nil {
+ found := false
+ if fi.IsDir() {
+ found = opts.NeedDir
+ } else {
+ found = !opts.NeedDir
+ }
+
+ if found {
+ opts.Collector(filePath)
+ return !opts.OnlyOne
+ }
+ }
+ return true
+ }, opts.MaxLevel)
+}
+
+// FindInParentDirs looks for file/dir in the current directory and parent directories
+// - MatchFunc custom matcher func. return false to stop find.
+func FindInParentDirs(dirPath string, matchFunc func(dir string) bool, maxLevel int) {
+ currentLv := 1
+ currentDir := ToAbsPath(dirPath)
+
+ for {
+ // Check if the file exists in the current directory
+ if !matchFunc(currentDir) {
+ return
+ }
+
+ // check find level
+ if maxLevel > 0 && currentLv > maxLevel {
+ break
+ }
+
+ // Get parent directory
+ parentDir := filepath.Dir(currentDir)
+ if parentDir == currentDir {
+ // Reached the root, file not found
+ return
+ }
+
+ // Move to parent directory
+ currentLv++
+ currentDir = parentDir
+ }
+}
+
// SearchNameUp find file/dir name in dirPath or parent dirs,
// return the name of directory path
//
@@ -158,14 +255,10 @@ type (
)
// OnlyFindDir on find
-func OnlyFindDir(_ string, ent fs.DirEntry) bool {
- return ent.IsDir()
-}
+func OnlyFindDir(_ string, ent fs.DirEntry) bool { return ent.IsDir() }
// OnlyFindFile on find
-func OnlyFindFile(_ string, ent fs.DirEntry) bool {
- return !ent.IsDir()
-}
+func OnlyFindFile(_ string, ent fs.DirEntry) bool { return !ent.IsDir() }
// ExcludeNames on find
func ExcludeNames(names ...string) FilterFunc {
@@ -182,9 +275,7 @@ func IncludeSuffix(ss ...string) FilterFunc {
}
// ExcludeDotFile on find
-func ExcludeDotFile(_ string, ent fs.DirEntry) bool {
- return ent.Name()[0] != '.'
-}
+func ExcludeDotFile(_ string, ent fs.DirEntry) bool { return ent.Name()[0] != '.' }
// ExcludeSuffix on find
func ExcludeSuffix(ss ...string) FilterFunc {
@@ -205,7 +296,7 @@ func ApplyFilters(fPath string, ent fs.DirEntry, filters []FilterFunc) bool {
// FindInDir code refer the go pkg: path/filepath.glob()
//
-// - TIP: will be not found in sub-dir.
+// - TIP: default will be not found in sub-dir.
//
// filters: return false will skip the file.
func FindInDir(dir string, handleFn HandleFunc, filters ...FilterFunc) (e error) {
diff --git a/vendor/github.com/gookit/goutil/fsutil/info.go b/vendor/github.com/gookit/goutil/fsutil/info.go
index c843bc792ab..94b45cc7981 100644
--- a/vendor/github.com/gookit/goutil/fsutil/info.go
+++ b/vendor/github.com/gookit/goutil/fsutil/info.go
@@ -9,12 +9,15 @@ import (
)
// DirPath get dir path from filepath, without a last name.
+// eg: "/foo/bar/baz.js" => "/foo/bar"
func DirPath(fPath string) string { return filepath.Dir(fPath) }
// Dir get dir path from filepath, without a last name.
+// eg: "/foo/bar/baz.js" => "/foo/bar"
func Dir(fPath string) string { return filepath.Dir(fPath) }
-// PathName get file/dir name from a full path
+// PathName get file/dir name from a full path.
+// eg: "/foo/bar/baz.js" => "baz.js"
func PathName(fPath string) string { return filepath.Base(fPath) }
// PathNoExt get path from full path, without ext.
@@ -30,7 +33,9 @@ func PathNoExt(fPath string) string {
// Name get file/dir name from full path.
//
-// eg: path/to/main.go => "main.go"
+// eg:
+// "path/to/main.go" => "main.go"
+// "/foo/bar/baz" => "baz"
func Name(fPath string) string {
if fPath == "" {
return ""
@@ -74,16 +79,15 @@ func Extname(fPath string) string {
func Suffix(fPath string) string { return filepath.Ext(fPath) }
// Expand will parse first `~` to user home dir path.
-func Expand(pathStr string) string {
- return comfunc.ExpandHome(pathStr)
-}
+func Expand(pathStr string) string { return comfunc.ExpandHome(pathStr) }
+
+// ExpandHome will parse first `~` to user home dir path.
+func ExpandHome(pathStr string) string { return comfunc.ExpandHome(pathStr) }
// ExpandPath will parse `~` to user home dir path.
-func ExpandPath(pathStr string) string {
- return comfunc.ExpandHome(pathStr)
-}
+func ExpandPath(pathStr string) string { return comfunc.ExpandHome(pathStr) }
-// ResolvePath will parse `~` and env var in path
+// ResolvePath will parse `~` and ENV var in path
func ResolvePath(pathStr string) string {
pathStr = comfunc.ExpandHome(pathStr)
// return comfunc.ParseEnvVar()
@@ -91,6 +95,18 @@ func ResolvePath(pathStr string) string {
}
// SplitPath splits path immediately following the final Separator, separating it into a directory and file name component
-func SplitPath(pathStr string) (dir, name string) {
- return filepath.Split(pathStr)
+func SplitPath(pathStr string) (dir, name string) { return filepath.Split(pathStr) }
+
+// homeDir cache
+var _homeDir string
+
+// UserHomeDir is alias of os.UserHomeDir, but ignore error.(by os.UserHomeDir)
+func UserHomeDir() string {
+ if _homeDir == "" {
+ _homeDir, _ = os.UserHomeDir()
+ }
+ return _homeDir
}
+
+// HomeDir get user home dir path.
+func HomeDir() string { return UserHomeDir() }
diff --git a/vendor/github.com/gookit/goutil/fsutil/operate.go b/vendor/github.com/gookit/goutil/fsutil/operate.go
index 1d68e4c5377..59bd4d98589 100644
--- a/vendor/github.com/gookit/goutil/fsutil/operate.go
+++ b/vendor/github.com/gookit/goutil/fsutil/operate.go
@@ -13,12 +13,21 @@ import (
)
// Mkdir alias of os.MkdirAll()
-func Mkdir(dirPath string, perm os.FileMode) error {
- return os.MkdirAll(dirPath, perm)
+func Mkdir(dirPath string, perm fs.FileMode) error { return os.MkdirAll(dirPath, perm) }
+
+// MkdirQuick with default permission 0755.
+func MkdirQuick(dirPath string) error { return EnsureDir(dirPath) }
+
+// EnsureDir creates a directory if it doesn't exist
+func EnsureDir(path string) error {
+ if !DirExist(path) {
+ return os.MkdirAll(path, 0755)
+ }
+ return nil
}
// MkDirs batch makes multi dirs at once
-func MkDirs(perm os.FileMode, dirPaths ...string) error {
+func MkDirs(perm fs.FileMode, dirPaths ...string) error {
for _, dirPath := range dirPaths {
if err := os.MkdirAll(dirPath, perm); err != nil {
return err
@@ -28,7 +37,7 @@ func MkDirs(perm os.FileMode, dirPaths ...string) error {
}
// MkSubDirs batch makes multi sub-dirs at once
-func MkSubDirs(perm os.FileMode, parentDir string, subDirs ...string) error {
+func MkSubDirs(perm fs.FileMode, parentDir string, subDirs ...string) error {
for _, dirName := range subDirs {
dirPath := parentDir + "/" + dirName
if err := os.MkdirAll(dirPath, perm); err != nil {
diff --git a/vendor/github.com/gookit/goutil/fsutil/opwrite.go b/vendor/github.com/gookit/goutil/fsutil/opwrite.go
index 305d8ce8b13..0431ca9718c 100644
--- a/vendor/github.com/gookit/goutil/fsutil/opwrite.go
+++ b/vendor/github.com/gookit/goutil/fsutil/opwrite.go
@@ -1,6 +1,7 @@
package fsutil
import (
+ "fmt"
"io"
"os"
@@ -185,3 +186,16 @@ func UpdateContents(filePath string, handleFn func(bs []byte) []byte) error {
}
return err
}
+
+// CreateSymlink creates a symbolic link
+func CreateSymlink(target, linkPath string) error {
+ // Check if the link already exists
+ if IsFile(linkPath) {
+ // Remove existing link/file
+ if err := os.Remove(linkPath); err != nil {
+ return fmt.Errorf("failed to remove existing symlink: %w", err)
+ }
+ }
+
+ return os.Symlink(target, linkPath)
+}
diff --git a/vendor/github.com/gookit/goutil/goutil.go b/vendor/github.com/gookit/goutil/goutil.go
index c0e92e6a574..33713b85b61 100644
--- a/vendor/github.com/gookit/goutil/goutil.go
+++ b/vendor/github.com/gookit/goutil/goutil.go
@@ -206,4 +206,3 @@ func FuncName(f any) string {
func PkgName(funcName string) string {
return goinfo.PkgName(funcName)
}
-
diff --git a/vendor/github.com/gookit/goutil/internal/checkfn/check.go b/vendor/github.com/gookit/goutil/internal/checkfn/check.go
index e85f2315431..81eaee37ce3 100644
--- a/vendor/github.com/gookit/goutil/internal/checkfn/check.go
+++ b/vendor/github.com/gookit/goutil/internal/checkfn/check.go
@@ -118,11 +118,31 @@ func StringsContains(ss []string, s string) bool {
return false
}
-// check is number: int or float
-var numReg = regexp.MustCompile(`^[-+]?\d*\.?\d+$`)
+var (
+ // check is number: int or float
+ numReg = regexp.MustCompile(`^[-+]?\d*\.?\d+$`)
+ // is positive number: int or float
+ pNumReg = 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) }
+func IsNumeric(s string) bool {
+ if s == "" {
+ return false
+ }
+ return numReg.MatchString(s)
+}
+
+// IsPositiveNum check input string is positive number
+func IsPositiveNum(s string) bool {
+ if s == "" {
+ return false
+ }
+ if s[0] == '-' {
+ return false
+ }
+ return pNumReg.MatchString(s)
+}
// IsHttpURL check input is http/https url
func IsHttpURL(s string) bool {
diff --git a/vendor/github.com/gookit/goutil/internal/comfunc/convert.go b/vendor/github.com/gookit/goutil/internal/comfunc/convert.go
index 3d4f3b278ef..9e12be8377c 100644
--- a/vendor/github.com/gookit/goutil/internal/comfunc/convert.go
+++ b/vendor/github.com/gookit/goutil/internal/comfunc/convert.go
@@ -43,6 +43,10 @@ func StrToBool(s string) (bool, error) {
}
// FormatWithArgs format message with args
+//
+// - only one element, format to string
+// - first is format: fmt.Sprintf(firstElem, fmtAndArgs[1:]...)
+// - all is args: return fmt.Sprint(fmtAndArgs...)
func FormatWithArgs(fmtAndArgs []any) string {
ln := len(fmtAndArgs)
if ln == 0 {
@@ -50,16 +54,15 @@ func FormatWithArgs(fmtAndArgs []any) string {
}
first := fmtAndArgs[0]
-
if ln == 1 {
- if msgAsStr, ok := first.(string); ok {
- return msgAsStr
+ if str, ok := first.(string); ok {
+ return str
}
return fmt.Sprintf("%+v", first)
}
// is template string.
- if tplStr, ok := first.(string); ok {
+ if tplStr, ok := first.(string); ok && strings.IndexByte(tplStr, '%') >= 0 {
return fmt.Sprintf(tplStr, fmtAndArgs[1:]...)
}
return fmt.Sprint(fmtAndArgs...)
@@ -86,11 +89,6 @@ 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) {
@@ -116,11 +114,6 @@ func (opt *ConvOption) WithOption(optFns ...ConvOptionFn) {
// 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)
@@ -161,6 +154,18 @@ func ToStringWith(in any, optFns ...ConvOptionFn) (str string, err error) {
case error:
str = value.Error()
default:
+ if len(optFns) == 0 && in == nil {
+ return "", nil
+ }
+
+ opt := NewConvOption(optFns...)
+ if in == nil {
+ if opt.NilAsFail {
+ err = comdef.ErrConvType
+ }
+ return
+ }
+
if opt.HandlePtr {
if rv := reflect.ValueOf(in); rv.Kind() == reflect.Pointer {
rv = rv.Elem()
diff --git a/vendor/github.com/gookit/goutil/internal/comfunc/sysfunc.go b/vendor/github.com/gookit/goutil/internal/comfunc/sysfunc.go
index 4f43d3ad132..d3f6224be25 100644
--- a/vendor/github.com/gookit/goutil/internal/comfunc/sysfunc.go
+++ b/vendor/github.com/gookit/goutil/internal/comfunc/sysfunc.go
@@ -87,19 +87,24 @@ func CurrentShell(onlyName bool, fallbackShell ...string) (binPath string) {
// 检查父进程名称
parentProcess := os.Getenv("GOPROCESS")
if parentProcess != "" {
- return parentProcess
+ binPath = parentProcess
+ } else {
+ binPath = os.Getenv("SHELL") // 适用于 Unix-like 系统
+ if len(binPath) == 0 {
+ // TODO check on Windows git bash
+ binPath, err = ShellExec("echo $SHELL")
+ if err != nil {
+ binPath = fbShell
+ }
+ }
+ binPath = strings.TrimSpace(binPath)
}
- binPath = os.Getenv("SHELL") // 适用于 Unix-like 系统
- if len(binPath) == 0 {
- // TODO check on Windows
- binPath, err = ShellExec("echo $SHELL")
- if err != nil {
- return fbShell
- }
+ // fix: 去除 .exe 后缀
+ if pos := strings.IndexByte(binPath, '.'); pos > 0 {
+ binPath = binPath[:pos]
}
- binPath = strings.TrimSpace(binPath)
// cache result
curShellCache = binPath
} else {
diff --git a/vendor/github.com/gookit/goutil/jsonutil/encoding.go b/vendor/github.com/gookit/goutil/jsonutil/encoding.go
index b921a421523..791033f7535 100644
--- a/vendor/github.com/gookit/goutil/jsonutil/encoding.go
+++ b/vendor/github.com/gookit/goutil/jsonutil/encoding.go
@@ -72,4 +72,4 @@ func DecodeFile(file string, ptr any) error {
}
return json.Unmarshal(bs, ptr)
-}
\ No newline at end of file
+}
diff --git a/vendor/github.com/gookit/goutil/maputil/alias.go b/vendor/github.com/gookit/goutil/maputil/alias.go
index b8109591177..9f739d22a7e 100644
--- a/vendor/github.com/gookit/goutil/maputil/alias.go
+++ b/vendor/github.com/gookit/goutil/maputil/alias.go
@@ -1,11 +1,15 @@
package maputil
-import "fmt"
+import (
+ "fmt"
+ "sort"
+)
// Aliases implemented a simple string alias map.
+// - key: alias, value: real name
type Aliases map[string]string
-// AddAlias to the Aliases
+// AddAlias to the Aliases map
func (as Aliases) AddAlias(alias, real string) {
if rn, ok := as[alias]; ok {
panic(fmt.Sprintf("The alias '%s' is already used by '%s'", alias, rn))
@@ -13,21 +17,21 @@ func (as Aliases) AddAlias(alias, real string) {
as[alias] = real
}
-// AddAliases to the Aliases
+// AddAliases to the Aliases map
func (as Aliases) AddAliases(real string, aliases []string) {
for _, a := range aliases {
as.AddAlias(a, real)
}
}
-// AddAliasMap to the Aliases
+// AddAliasMap to the Aliases map
func (as Aliases) AddAliasMap(alias2real map[string]string) {
for a, r := range alias2real {
as.AddAlias(a, r)
}
}
-// HasAlias in the Aliases
+// HasAlias in the Aliases map
func (as Aliases) HasAlias(alias string) bool {
if _, ok := as[alias]; ok {
return true
@@ -42,3 +46,24 @@ func (as Aliases) ResolveAlias(alias string) string {
}
return alias
}
+
+// AliasesNames returns all sorted alias names.
+func (as Aliases) AliasesNames() []string {
+ ns := make([]string, 0, len(as))
+ for alias := range as {
+ ns = append(ns, alias)
+ }
+ sort.Strings(ns)
+ return ns
+}
+
+// GroupAliases groups aliases by real name.
+//
+// returns: {real name -> []aliases, ...}
+func (as Aliases) GroupAliases() map[string][]string {
+ gaMap := make(map[string][]string)
+ for alias, name := range as {
+ gaMap[name] = append(gaMap[name], alias)
+ }
+ return gaMap
+}
diff --git a/vendor/github.com/gookit/goutil/maputil/convert.go b/vendor/github.com/gookit/goutil/maputil/convert.go
index fd0a17d4ac5..215ba9d4b8a 100644
--- a/vendor/github.com/gookit/goutil/maputil/convert.go
+++ b/vendor/github.com/gookit/goutil/maputil/convert.go
@@ -86,7 +86,7 @@ func CombineToMap[K comdef.SortedType, V any](keys []K, values []V) map[K]V {
}
// SliceToSMap convert string k-v pairs slice to map[string]string
-// - eg: []string{k1,v1,k2,v2} -> map[string]string{k1:v1, k2:v2}
+// - 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
diff --git a/vendor/github.com/gookit/goutil/maputil/data.go b/vendor/github.com/gookit/goutil/maputil/data.go
index 71e227ad997..5eb89cfbc78 100644
--- a/vendor/github.com/gookit/goutil/maputil/data.go
+++ b/vendor/github.com/gookit/goutil/maputil/data.go
@@ -9,12 +9,12 @@ import (
"github.com/gookit/goutil/strutil"
)
-// Data an map data type
-type Data map[string]any
-
// Map alias of Data
type Map = Data
+// Data alias of map[string]any
+type Data map[string]any
+
// Has value on the data map
func (d Data) Has(key string) bool {
_, ok := d.GetByPath(key)
diff --git a/vendor/github.com/gookit/goutil/maputil/maputil.go b/vendor/github.com/gookit/goutil/maputil/maputil.go
index 8acd64d409f..6aaf2584da5 100644
--- a/vendor/github.com/gookit/goutil/maputil/maputil.go
+++ b/vendor/github.com/gookit/goutil/maputil/maputil.go
@@ -63,6 +63,11 @@ func MergeStrMap(src, dst map[string]string) map[string]string {
return MergeStringMap(src, dst, false)
}
+// AppendSMap append string map data to dst map.
+func AppendSMap(dst, src 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 {
diff --git a/vendor/github.com/gookit/goutil/maputil/smap.go b/vendor/github.com/gookit/goutil/maputil/smap.go
index 6fc767a64ce..63dc2d0fea0 100644
--- a/vendor/github.com/gookit/goutil/maputil/smap.go
+++ b/vendor/github.com/gookit/goutil/maputil/smap.go
@@ -5,6 +5,8 @@ import (
"github.com/gookit/goutil/strutil"
)
+// SM is alias of map[string]string
+type SM = StrMap
// SMap and StrMap is alias of map[string]string
type SMap = StrMap
type StrMap map[string]string
diff --git a/vendor/github.com/gookit/goutil/mathutil/check.go b/vendor/github.com/gookit/goutil/mathutil/check.go
index ed1d11957c9..2eda9ebe6dd 100644
--- a/vendor/github.com/gookit/goutil/mathutil/check.go
+++ b/vendor/github.com/gookit/goutil/mathutil/check.go
@@ -3,8 +3,16 @@ package mathutil
import "github.com/gookit/goutil/comdef"
// IsNumeric returns true if the given character is a numeric, otherwise false.
-func IsNumeric(c byte) bool {
- return c >= '0' && c <= '9'
+func IsNumeric(c byte) bool { return c >= '0' && c <= '9' }
+
+// IsInteger strict check the given value is an integer(intX,uintX), otherwise false.
+func IsInteger(val any) bool {
+ switch val.(type) {
+ case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr:
+ return true
+ default:
+ return false
+ }
}
// Compare any intX,floatX value by given op. returns `first op(=,!=,<,<=,>,>=) second`
diff --git a/vendor/github.com/gookit/goutil/mathutil/convert.go b/vendor/github.com/gookit/goutil/mathutil/convert.go
index cbedf90f6bd..edcb49fcc2e 100644
--- a/vendor/github.com/gookit/goutil/mathutil/convert.go
+++ b/vendor/github.com/gookit/goutil/mathutil/convert.go
@@ -1,8 +1,6 @@
package mathutil
import (
- "fmt"
- "math"
"reflect"
"strconv"
"strings"
@@ -13,24 +11,6 @@ import (
"github.com/gookit/goutil/internal/comfunc"
)
-// ToIntFunc convert value to int
-type ToIntFunc func(any) (int, error)
-
-// ToInt64Func convert value to int64
-type ToInt64Func func(any) (int64, error)
-
-// ToUintFunc convert value to uint
-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;
@@ -40,6 +20,11 @@ type ConvOption[T any] struct {
// - if true: will use real type try convert. default is false
// - NOTE: current T type's ptr is default support.
HandlePtr bool
+ // StrictMode for convert value. default is false
+ //
+ // TRUE:
+ // - to int: string, float will return error
+ StrictMode bool
// set custom fallback convert func for not supported type.
UserConvFn ToTypeFunc[T]
}
@@ -68,14 +53,13 @@ type ConvOptionFn[T any] func(opt *ConvOption[T])
// Example:
//
// ToIntWithFunc(val, mathutil.WithNilAsFail[int])
-func WithNilAsFail[T any](opt *ConvOption[T]) {
- opt.NilAsFail = true
-}
+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
-}
+func WithHandlePtr[T any](opt *ConvOption[T]) { opt.HandlePtr = true }
+
+// WithStrictMode set ConvOption.StrictMode option
+func WithStrictMode[T any](opt *ConvOption[T]) { opt.StrictMode = true }
// WithUserConvFn set ConvOption.UserConvFn option
func WithUserConvFn[T any](fn ToTypeFunc[T]) ConvOptionFn[T] {
@@ -85,488 +69,71 @@ func WithUserConvFn[T any](fn ToTypeFunc[T]) ConvOptionFn[T] {
}
/*************************************************************
- * convert value to int
+ * region Strict to int/uint
*************************************************************/
-// Int convert value to int
-func Int(in any) (int, error) { return ToInt(in) }
-
-// SafeInt convert value to int, will ignore error
-func SafeInt(in any) int {
- val, _ := ToInt(in)
- return val
-}
-
-// QuietInt convert value to int, will ignore error
-func QuietInt(in any) int { return SafeInt(in) }
-
-// IntOrPanic convert value to int, will panic on error
-func IntOrPanic(in any) int {
- val, err := ToInt(in)
- if err != nil {
- panic(err)
- }
- return val
-}
-
-// 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) }
-
-// IntOr convert value to int, return defaultVal on failed
-func IntOr(in any, defVal int) int {
- val, err := ToIntWith(in)
- if err != nil {
- return defVal
- }
- return val
-}
-
-// IntOrErr convert value to int, return error on failed
-func IntOrErr(in any) (int, error) { return ToIntWith(in) }
-
-// ToInt convert value to int, return error on failed
-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
- }
-
- switch tVal := in.(type) {
+// StrictInt check the given value is an integer(intX,uintX), return the int64 value and true if success
+func StrictInt(val any) (int64, bool) {
+ switch tVal := val.(type) {
case int:
- iVal = tVal
- case *int: // default support int ptr type
- iVal = *tVal
+ return int64(tVal), true
case int8:
- iVal = int(tVal)
+ return int64(tVal), true
case int16:
- iVal = int(tVal)
+ return int64(tVal), true
case int32:
- iVal = int(tVal)
+ return int64(tVal), true
case int64:
- if tVal > math.MaxInt32 {
- err = fmt.Errorf("value overflow int32. input: %v", tVal)
- } else {
- iVal = int(tVal)
- }
+ return tVal, true
case uint:
- if tVal > math.MaxInt32 {
- err = fmt.Errorf("value overflow int32. input: %v", tVal)
- } else {
- iVal = int(tVal)
- }
+ return int64(tVal), true
case uint8:
- iVal = int(tVal)
+ return int64(tVal), true
case uint16:
- iVal = int(tVal)
+ return int64(tVal), true
case uint32:
- if tVal > math.MaxInt32 {
- err = fmt.Errorf("value overflow int32. input: %v", tVal)
- } else {
- iVal = int(tVal)
- }
+ return int64(tVal), true
case uint64:
- if tVal > math.MaxInt32 {
- err = fmt.Errorf("value overflow int32. input: %v", tVal)
- } else {
- iVal = int(tVal)
- }
- case float32:
- iVal = int(tVal)
- case float64:
- iVal = int(tVal)
- case time.Duration:
- if tVal > math.MaxInt32 {
- err = fmt.Errorf("value overflow int32. input: %v", tVal)
- } else {
- iVal = int(tVal)
- }
- case string:
- sVal := strings.TrimSpace(tVal)
- iVal, err = strconv.Atoi(sVal)
- // handle the case where the string might be a float
- if err != nil && checkfn.IsNumeric(sVal) {
- var floatVal float64
- if floatVal, err = strconv.ParseFloat(sVal, 64); err == nil {
- iVal = int(math.Round(floatVal))
- err = nil
- }
- }
- case comdef.Int64able: // eg: json.Number
- var i64 int64
- if i64, err = tVal.Int64(); err == nil {
- if i64 > math.MaxInt32 {
- err = fmt.Errorf("value overflow int32. input: %v", tVal)
- } else {
- iVal = int(i64)
- }
- }
+ return int64(tVal), true
+ case uintptr:
+ return int64(tVal), true
default:
- 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
-}
-
-// StrInt convert.
-func StrInt(s string) int {
- iVal, _ := strconv.Atoi(strings.TrimSpace(s))
- return iVal
-}
-
-// StrIntOr convert string to int, return default val on failed
-func StrIntOr(s string, defVal int) int {
- iVal, err := strconv.Atoi(strings.TrimSpace(s))
- if err != nil {
- return defVal
- }
- 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 0, false
}
- 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:
- sVal := strings.TrimSpace(tVal)
- i64, err = strconv.ParseInt(sVal, 10, 0)
- // handle the case where the string might be a float
- if err != nil && checkfn.IsNumeric(sVal) {
- var floatVal float64
- if floatVal, err = strconv.ParseFloat(sVal, 64); err == nil {
- i64 = int64(math.Round(floatVal))
- err = nil
- }
- }
+// StrictUint strict check value is integer(intX,uintX) and convert to uint64.
+func StrictUint(val any) (uint64, bool) {
+ switch tVal := val.(type) {
case int:
- i64 = int64(tVal)
+ return uint64(tVal), true
case int8:
- i64 = int64(tVal)
+ return uint64(tVal), true
case int16:
- i64 = int64(tVal)
+ return uint64(tVal), true
case int32:
- i64 = int64(tVal)
+ return uint64(tVal), true
case int64:
- i64 = tVal
- case *int64: // default support int64 ptr type
- i64 = *tVal
+ return uint64(tVal), true
case uint:
- i64 = int64(tVal)
+ return uint64(tVal), true
case uint8:
- i64 = int64(tVal)
+ return uint64(tVal), true
case uint16:
- i64 = int64(tVal)
+ return uint64(tVal), true
case uint32:
- i64 = int64(tVal)
+ return uint64(tVal), true
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()
+ return tVal, true
+ case uintptr:
+ return uint64(tVal), true
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 0, false
}
- return
-}
-
-/*************************************************************
- * convert value to uint
- *************************************************************/
-
-// Uint convert any to uint, return error on failed
-func Uint(in any) (uint, error) { return ToUint(in) }
-
-// SafeUint convert any to uint, will ignore error
-func SafeUint(in any) uint {
- val, _ := ToUint(in)
- return val
-}
-
-// QuietUint convert any to uint, will ignore error
-func QuietUint(in any) uint { return SafeUint(in) }
-
-// MustUint convert any to uint, will panic on error
-func MustUint(in any) uint {
- val, err := ToUintWith(in)
- if err != nil {
- panic(err)
- }
- return val
-}
-
-// UintOrDefault convert any to uint, return default val on failed
-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 uint) uint {
- val, err := ToUintWith(in)
- if err != nil {
- return defVal
- }
- return val
-}
-
-// UintOrErr convert value to uint, return error on failed
-func UintOrErr(in any) (uint, error) { return ToUintWith(in) }
-
-// ToUint convert value to uint, return error on failed
-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
-}
-
-/*************************************************************
- * 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)
- case int8:
- u64 = uint64(tVal)
- case int16:
- u64 = uint64(tVal)
- case int32:
- u64 = uint64(tVal)
- case int64:
- u64 = uint64(tVal)
- case uint:
- u64 = uint64(tVal)
- case uint8:
- u64 = uint64(tVal)
- case uint16:
- u64 = uint64(tVal)
- case uint32:
- 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 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 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
- }
- }
- return
}
/*************************************************************
- * convert value to float64
+ * region convert to float64
*************************************************************/
// QuietFloat convert value to float64, will ignore error. alias of SafeFloat
@@ -671,7 +238,7 @@ func ToFloatWith(in any, optFns ...ConvOptionFn[float64]) (f64 float64, err erro
}
/*************************************************************
- * convert intX/floatX to string
+ * region intX/floatX to string
*************************************************************/
// MustString convert intX/floatX value to string, will panic on error
diff --git a/vendor/github.com/gookit/goutil/mathutil/format.go b/vendor/github.com/gookit/goutil/mathutil/format.go
index deb29fd2a50..e310eb09116 100644
--- a/vendor/github.com/gookit/goutil/mathutil/format.go
+++ b/vendor/github.com/gookit/goutil/mathutil/format.go
@@ -22,6 +22,21 @@ func DataSize(size uint64) string {
}
}
+// FormatBytes Format the byte size to be a readable string. eg: 1024 => 1 KB
+func FormatBytes(bytes int) string {
+ const unit = 1024
+ if bytes < unit {
+ return fmt.Sprintf("%d B", bytes)
+ }
+
+ div, exp := int64(unit), 0
+ for n := bytes / unit; n >= unit; n /= unit {
+ div *= unit
+ exp++
+ }
+ return fmt.Sprintf("%.2f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
+}
+
var timeFormats = [][]int{
{0},
{1},
diff --git a/vendor/github.com/gookit/goutil/structs/convert.go b/vendor/github.com/gookit/goutil/structs/convert.go
index f8029865dad..8c55e943164 100644
--- a/vendor/github.com/gookit/goutil/structs/convert.go
+++ b/vendor/github.com/gookit/goutil/structs/convert.go
@@ -66,11 +66,11 @@ func ToString(st any, optFns ...MapOptFunc) string {
const defaultFieldTag = "json"
// CustomUserFunc for map convert
-// - fName: raw field name in struct
+// - 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.
+// - 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
diff --git a/vendor/github.com/gookit/goutil/strutil/check.go b/vendor/github.com/gookit/goutil/strutil/check.go
index 04660c307d9..a990f6f0575 100644
--- a/vendor/github.com/gookit/goutil/strutil/check.go
+++ b/vendor/github.com/gookit/goutil/strutil/check.go
@@ -3,6 +3,7 @@ package strutil
import (
"path"
"regexp"
+ "strconv"
"strings"
"unicode"
"unicode/utf8"
@@ -17,18 +18,43 @@ 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 intReg = regexp.MustCompile(`^\d+$`)
-var floatReg = regexp.MustCompile(`^[-+]?\d*\.?\d+$`)
+var (
+ uintReg = regexp.MustCompile(`^\d+$`)
+ intReg = regexp.MustCompile(`^[-+]?\d+$`)
+
+ floatReg = regexp.MustCompile(`^[-+]?\d*\.?\d+$`)
+)
// IsInt check the string is an integer number
-func IsInt(s string) bool { return intReg.MatchString(s) }
+func IsInt(s string) bool {
+ if s == "" {
+ return false
+ }
+ return intReg.MatchString(s)
+}
+
+// IsUint check the string is an unsigned integer number
+func IsUint(s string) bool {
+ if s == "" {
+ return false
+ }
+ return uintReg.MatchString(s)
+}
// IsFloat check the string is a float number
-func IsFloat(s string) bool { return floatReg.MatchString(s) }
+func IsFloat(s string) bool {
+ if s == "" {
+ return false
+ }
+ 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) }
+// IsPositiveNum check the string is a positive number
+func IsPositiveNum(s string) bool { return checkfn.IsPositiveNum(s) }
+
// IsAlphabet char
func IsAlphabet(char uint8) bool {
// A 65 -> Z 90
@@ -48,6 +74,28 @@ func IsAlphaNum(c uint8) bool {
return c == '_' || '0' <= c && c <= '9' || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z'
}
+// IsUpper returns true if the given string is an uppercase, otherwise false.
+func IsUpper(s string) bool {
+ for i := 0; i < len(s); i++ {
+ if s[i] >= 'A' && s[i] <= 'Z' {
+ continue
+ }
+ return false
+ }
+ return true
+}
+
+// IsLower returns true if the given string is a lowercase, otherwise false.
+func IsLower(s string) bool {
+ for i := 0; i < len(s); i++ {
+ if s[i] >= 'a' && s[i] <= 'z' {
+ continue
+ }
+ return false
+ }
+ return true
+}
+
// StrPos alias of the strings.Index
func StrPos(s, sub string) int { return strings.Index(s, sub) }
@@ -68,6 +116,16 @@ func IContains(s, sub string) bool {
// ContainsByte in given string.
func ContainsByte(s string, c byte) bool { return strings.IndexByte(s, c) >= 0 }
+// ContainsByteOne in given string.
+func ContainsByteOne(s string, bs []byte) bool {
+ for _, b := range bs {
+ if strings.IndexByte(s, b) >= 0 {
+ return true
+ }
+ }
+ return false
+}
+
// InArray alias of HasOneSub()
var InArray = HasOneSub
@@ -95,10 +153,10 @@ func IContainsOne(s string, subs []string) bool {
return false
}
-// ContainsAll substr(s) in the given string. alias of HasAllSubs()
+// ContainsAll given string should contain all substrings. alias of HasAllSubs()
func ContainsAll(s string, subs []string) bool { return HasAllSubs(s, subs) }
-// HasAllSubs all substr in the given string.
+// HasAllSubs given string should contain all substrings
func HasAllSubs(s string, subs []string) bool {
for _, sub := range subs {
if !strings.Contains(s, sub) {
@@ -227,6 +285,8 @@ var (
verRegex = regexp.MustCompile(`^[0-9][\d.]+(-\w+)?$`)
// regex for check variable name
varRegex = regexp.MustCompile(`^[a-zA-Z][\w-]*$`)
+ // regex for check env var name
+ envRegex = regexp.MustCompile(`^[A-Z][A-Z0-9_]*$`)
// IsVariableName alias for IsVarName
IsVariableName = IsVarName
)
@@ -237,25 +297,90 @@ func IsVersion(s string) bool { return verRegex.MatchString(s) }
// 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) }
+// IsEnvName is valid ENV var name. eg: APP_NAME
+func IsEnvName(s string) bool { return envRegex.MatchString(s) }
-// VersionCompare for two version strings.
-func VersionCompare(v1, v2, op string) bool {
+// Compare for two strings.
+func Compare(s1, s2, op string) bool {
switch op {
case ">", "gt":
- return v1 > v2
+ return s1 > s2
case "<", "lt":
- return v1 < v2
+ return s1 < s2
case ">=", "gte":
- return v1 >= v2
+ return s1 >= s2
case "<=", "lte":
- return v1 <= v2
+ return s1 <= s2
case "!=", "ne", "neq":
- return v1 != v2
+ return s1 != s2
default: // eq
- return v1 == v2
+ return s1 == s2
+ }
+}
+
+// VersionCompare for two version strings. eg: 1.2.0 > 1.1.0
+func VersionCompare(v1, v2, op string) bool {
+ parts1 := parseVersion(v1)
+ parts2 := parseVersion(v2)
+
+ result := compareVersions(parts1, parts2)
+ switch op {
+ case ">", "gt":
+ return result > 0
+ case "<", "lt":
+ return result < 0
+ case "=", "==", "eq":
+ return result == 0
+ case "!=", "ne", "neq":
+ return result != 0
+ case ">=", "gte":
+ return result >= 0
+ case "<=", "lte":
+ return result <= 0
+ default:
+ return false
+ }
+}
+
+// parseVersion 将版本号字符串解析为整数数组
+func parseVersion(version string) []int {
+ parts := strings.Split(version, ".")
+ result := make([]int, len(parts))
+
+ for i, part := range parts {
+ num, _ := strconv.Atoi(part)
+ result[i] = num
+ }
+ return result
+}
+
+// compareVersions 比较两个版本号数组
+// 返回: -1 表示 v1 < v2, 0 表示 v1 = v2, 1 表示 v1 > v2
+func compareVersions(v1, v2 []int) int {
+ maxLen := len(v1)
+ if len(v2) > maxLen {
+ maxLen = len(v2)
}
+
+ for i := 0; i < maxLen; i++ {
+ num1 := 0
+ if i < len(v1) {
+ num1 = v1[i]
+ }
+
+ num2 := 0
+ if i < len(v2) {
+ num2 = v2[i]
+ }
+
+ if num1 > num2 {
+ return 1
+ } else if num1 < num2 {
+ return -1
+ }
+ }
+
+ return 0
}
// SimpleMatch all substring in the give text string.
diff --git a/vendor/github.com/gookit/goutil/strutil/convbase.go b/vendor/github.com/gookit/goutil/strutil/convbase.go
index a405778a647..c9f57edd425 100644
--- a/vendor/github.com/gookit/goutil/strutil/convbase.go
+++ b/vendor/github.com/gookit/goutil/strutil/convbase.go
@@ -16,6 +16,7 @@ const (
Base16Chars = "0123456789abcdef"
Base32Chars = "0123456789abcdefghjkmnpqrstvwxyz"
Base36Chars = "0123456789abcdefghijklmnopqrstuvwxyz"
+ Base48Chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL"
Base62Chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
Base64Chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/"
)
@@ -33,14 +34,26 @@ func Base10Conv(src string, to int) string { return BaseConv(src, 10, to) }
// BaseConv("7b", 16, 10) // Output: "123"
func BaseConv(src string, from, to int) string {
if from > 64 || from < 2 {
- return ""
+ from = 10
}
if to > 64 || to < 2 {
- return ""
+ to = 16
}
return BaseConvByTpl(src, Base64Chars[:from], Base64Chars[:to])
}
+// BaseConvInt convert base int to new base string.
+//
+// Usage:
+//
+// BaseConv(123, 16) // Output: "7b"
+func BaseConvInt(src uint64, to int) string {
+ if to > 64 || to < 2 {
+ to = 16
+ }
+ return BaseConvIntByTpl(src, Base64Chars[:to])
+}
+
// BaseConvByTpl convert base string by template.
//
// Usage:
@@ -58,7 +71,7 @@ func BaseConvByTpl(src string, fromBase, toBase string) string {
var err error
dec, err = strconv.ParseUint(src, 10, 0)
if err != nil {
- basefn.Panicf("input is not a valid decimal number: %s", src)
+ basefn.Panicf("input is not a valid decimal number: %s(%v)", src, err)
}
} else {
fLen := uint64(len(fromBase))
@@ -67,6 +80,12 @@ func BaseConvByTpl(src string, fromBase, toBase string) string {
}
}
+ // convert to new base
+ return BaseConvIntByTpl(dec, toBase)
+}
+
+// BaseConvIntByTpl convert base int to new base string.
+func BaseConvIntByTpl(dec uint64, toBase string) string {
// convert to new base
var res string
toLen := uint64(len(toBase))
diff --git a/vendor/github.com/gookit/goutil/strutil/convert.go b/vendor/github.com/gookit/goutil/strutil/convert.go
index d258621f8d3..f7bbac4b0d9 100644
--- a/vendor/github.com/gookit/goutil/strutil/convert.go
+++ b/vendor/github.com/gookit/goutil/strutil/convert.go
@@ -88,7 +88,7 @@ func JoinAny(sep string, parts ...any) string {
func Implode(sep string, ss ...string) string { return strings.Join(ss, sep) }
/*************************************************************
- * convert value to string
+ * region value to string
*************************************************************/
// String convert value to string, return error on failed
@@ -144,7 +144,7 @@ func AnyToString(val any, defaultAsErr bool) (s string, err error) {
if !defaultAsErr {
optFn = comfunc.WithUserConvFn(comfunc.StrBySprintFn)
}
- return ToStringWith(val, optFn)
+ return comfunc.ToStringWith(val, optFn)
}
// ToStringWith try to convert value to string. can with some option func, more see comfunc.ConvOption.
@@ -153,7 +153,7 @@ func ToStringWith(in any, optFns ...comfunc.ConvOptionFn) (string, error) {
}
/*************************************************************
- * convert string value to bool
+ * region string value to bool
*************************************************************/
// ToBool convert string to bool
@@ -185,7 +185,7 @@ func Bool(s string) (bool, error) {
}
/*************************************************************
- * convert string value to int
+ * region string value to int
*************************************************************/
// Int convert string to int, alias of ToInt()
@@ -234,7 +234,7 @@ func IntOrPanic(s string) int {
}
/*************************************************************
- * convert string value to int64
+ * region convert string to int64
*************************************************************/
// Int64 convert string to int, will ignore error
@@ -286,7 +286,7 @@ func Int64OrPanic(s string) int64 {
}
/*************************************************************
- * convert string value to uint
+ * region string value to uint
*************************************************************/
// Uint convert string to uint, will ignore error
@@ -335,7 +335,7 @@ func UintOr(s string, defVal uint64) uint64 {
}
/*************************************************************
- * convert string value to byte
+ * region string value to byte
* refer from https://github.com/valyala/fastjson/blob/master/util.go
*************************************************************/
@@ -361,7 +361,7 @@ func ToBytes(s string) (b []byte) {
}
/*************************************************************
- * convert string value to int/string slice, time.Time
+ * region string to int/string slice, time.Time
*************************************************************/
// Ints alias of the ToIntSlice(). default sep is comma(,)
@@ -406,11 +406,6 @@ func ToSlice(s string, sep ...string) []string {
return Split(s, ",")
}
-// ToOSArgs split string to string[](such as os.Args)
-// func ToOSArgs(s string) []string {
-// return cliutil.StringToOSArgs(s) // error: import cycle not allowed
-// }
-
// ToDuration parses a duration string. such as "300ms", "-1.5h" or "2h45m".
// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
func ToDuration(s string) (time.Duration, error) {
diff --git a/vendor/github.com/gookit/goutil/strutil/format.go b/vendor/github.com/gookit/goutil/strutil/format.go
index 05150f5a4ad..c63b5d8b848 100644
--- a/vendor/github.com/gookit/goutil/strutil/format.go
+++ b/vendor/github.com/gookit/goutil/strutil/format.go
@@ -1,6 +1,7 @@
package strutil
import (
+ "fmt"
"regexp"
"strings"
"unicode"
@@ -184,3 +185,46 @@ func IndentBytes(b, prefix []byte) []byte {
}
return res
}
+
+// Replaces replace multi strings
+//
+// pairs: {old1: new1, old2: new2, ...}
+//
+// Can also use:
+//
+// strings.NewReplacer("old1", "new1", "old2", "new2").Replace(str)
+func Replaces(str string, pairs map[string]string) string {
+ return NewReplacer(pairs).Replace(str)
+}
+
+// ReplaceVars replaces simple variables in a string. format: {varName}
+func ReplaceVars(s string, vars map[string]string) string {
+ if !ContainsByte(s, '{') {
+ return s
+ }
+
+ // format var name to {name}
+ pairs := make(map[string]string)
+ for k, v := range vars {
+ vName := "{" + k + "}"
+ pairs[vName] = v
+ }
+ return NewReplacer(pairs).Replace(s)
+}
+
+// NewReplacer instance
+func NewReplacer(pairs map[string]string) *strings.Replacer {
+ ss := make([]string, len(pairs)*2)
+ for old, newVal := range pairs {
+ ss = append(ss, old, newVal)
+ }
+ return strings.NewReplacer(ss...)
+}
+
+// WrapTag for given string.
+func WrapTag(s, tag string) string {
+ if s == "" {
+ return s
+ }
+ return fmt.Sprintf("<%s>%s</%s>", tag, s, tag)
+}
diff --git a/vendor/github.com/gookit/goutil/strutil/gensn.go b/vendor/github.com/gookit/goutil/strutil/gensn.go
index 31cf6cbf74d..2c2981741dc 100644
--- a/vendor/github.com/gookit/goutil/strutil/gensn.go
+++ b/vendor/github.com/gookit/goutil/strutil/gensn.go
@@ -7,7 +7,6 @@ import (
"strconv"
"time"
- "github.com/gookit/goutil/mathutil"
"github.com/gookit/goutil/x/basefn"
)
@@ -34,12 +33,12 @@ func MicroTimeID() string { return MTimeBaseID(10) }
// MicroTimeHexID micro time HEX ID generate.
//
-// return like: 5b5f0588af1761ad3(len: 16-17)
+// return like: 643d4cec7db9e(len: 13)
func MicroTimeHexID() string { return MTimeHexID() }
// MTimeHexID micro time HEX ID generate.
//
-// return like: 5b5f0588af1761ad3(len: 16-17)
+// return like: 643d4cec7db9e(len: 13)
func MTimeHexID() string { return MTimeBaseID(16) }
// MTimeBase36 micro time BASE36 id generate.
@@ -48,12 +47,19 @@ func MTimeBase36() string { return MTimeBaseID(36) }
// MTimeBaseID micro time BASE id generate. toBase: 2-36
//
// Examples:
-// - MTimeBaseID(16): 5b5f0588af1761ad3(len: 16-17)
-// - MTimeBaseID(36): gorntzvsa73mo(len: 13)
+// - toBase=16: 643d4cec7db9e(len: 13)
+// - toBase=36: hd312z9ka2(len: 10)
func MTimeBaseID(toBase int) string {
+ // eg: 1763431181849557
ms := time.Now().UnixMicro()
- ri := mathutil.RandomInt(DefMinInt, DefMaxInt)
- return strconv.FormatInt(ms, toBase) + strconv.FormatInt(int64(ri), toBase)
+ // rand 1000 - 9999
+ // ri := mathutil.RandomInt(DefMinInt, DefMaxInt)
+ ri := 1000 + rand.Int63n(8999)
+
+ if toBase > 36 {
+ return BaseConvInt(uint64(ms)+uint64(ri), toBase)
+ }
+ return strconv.FormatInt(ms+ri, toBase)
}
// DatetimeNo generate. can use for order-no.
@@ -84,20 +90,20 @@ func DateSN(prefix string) string {
bs = strconv.AppendUint(bs, uint64(c32%99), 10)
// rand 1000 - 9999
- rs := rand.New(rand.NewSource(nt.UnixNano()))
- bs = strconv.AppendInt(bs, 1000+rs.Int63n(8999), 10)
+ // rs := rand.New(rand.NewSource(nt.UnixNano()))
+ bs = strconv.AppendInt(bs, 1000+rand.Int63n(8999), 10)
return string(bs)
}
-// DateSNV2 generate date serial number.
+// DateSNv2 generate date serial number.
// - 2 < extBase <= 36
// - return: PREFIX + yyyyMMddHHmmss + extBase(6bit micro + 5bit random number)
//
// Example:
// - prefix=P, extBase=16, return: P2023091414361354b4490(len=22)
// - prefix=P, extBase=36, return: P202309141436131gw3jg(len=21)
-func DateSNV2(prefix string, extBase ...int) string {
+func DateSNv2(prefix string, extBase ...int) string {
pl := len(prefix)
bs := make([]byte, 0, 22+pl)
if pl > 0 {
@@ -109,9 +115,9 @@ func DateSNV2(prefix string, extBase ...int) string {
bs = nt.AppendFormat(bs, "20060102150405.000000")
// rand 10000 - 99999
- rs := rand.New(rand.NewSource(nt.UnixNano()))
+ // rs := rand.New(rand.NewSource(nt.UnixNano()))
// 6bit micro + 5bit rand
- ext := strconv.AppendInt(bs[16+pl:], 10000+rs.Int63n(89999), 10)
+ ext := strconv.AppendInt(bs[16+pl:], 10000+rand.Int63n(89999), 10)
base := basefn.FirstOr(extBase, 16)
// prefix + yyyyMMddHHmmss + ext(convert to base)
@@ -119,3 +125,43 @@ func DateSNV2(prefix string, extBase ...int) string {
return string(bs)
}
+
+// DateSNv3 generate date serial number.
+// - 2 < extBase <= 64
+// - return: PREFIX + DATETIME(yyyyMMddHHmmss).dateLen + extBase(DATETIME.after+6bit micro + 5bit random number)
+// - dateLen: 为 DATETIME(yyyyMMddHHmmss) 保留的长度,默认为 8(yyyyMMdd) 后面的给 extBase 使用
+//
+// Example:
+// - prefix=P, dateLen=8, extBase=16, return: P202511139vs99gbifnj len: 20
+// - prefix=P, dateLen=6, extBase=36, return: P2025119yn52qhefati len: 19
+// - prefix=P, dateLen=6, extBase=48, return: P202511k9ksgD1fe6x len: 18
+// - prefix=P, dateLen=4, extBase=62, return: P2025aZl8N0y58M7 len: 16
+func DateSNv3(prefix string, dateLen int, extBase ...int) string {
+ pl := len(prefix)
+ bs := make([]byte, 0, 22+pl)
+ if pl > 0 {
+ bs = append(bs, prefix...)
+ }
+
+ // micro datetime
+ nt := time.Now()
+ bs = nt.AppendFormat(bs, "20060102150405.000000")
+ // 去掉 bs 中间的 .
+ bs = append(bs[:14+pl], bs[15+pl:]...)
+
+ // rand 10000 - 99999
+ // rs := rand.New(rand.NewSource(nt.UnixNano())) // ERR: 这样使用,同时调用产生随机数相同,导致重复
+ // 6bit micro + 5bit rand
+ // ext := strconv.AppendInt(bs[dateLen+pl:], 10000+rs.Int63n(89999), 10)
+ ext := strconv.AppendInt(bs[dateLen+pl:], 10000+rand.Int63n(89999), 10)
+ extI64, _ := strconv.ParseInt(string(ext), 10, 0)
+
+ base := basefn.FirstOr(extBase, 32)
+ // PREFIX + DATETIME(yyyyMMddHHmmss).dateLen + extBase(convert to base)
+ if base > 36 {
+ bs = append(bs[:dateLen+pl], BaseConvInt(uint64(extI64), base)...)
+ } else {
+ bs = append(bs[:dateLen+pl], strconv.FormatInt(extI64, base)...)
+ }
+ return string(bs)
+}
diff --git a/vendor/github.com/gookit/goutil/strutil/parse.go b/vendor/github.com/gookit/goutil/strutil/parse.go
index c03cb125d20..580b66baae7 100644
--- a/vendor/github.com/gookit/goutil/strutil/parse.go
+++ b/vendor/github.com/gookit/goutil/strutil/parse.go
@@ -2,6 +2,7 @@ package strutil
import (
"errors"
+ "regexp"
"strconv"
"strings"
"time"
@@ -10,6 +11,13 @@ import (
"github.com/gookit/goutil/byteutil"
)
+var regNumVersion = regexp.MustCompile(`[0-9][\d.]+([_-]\d+)?`)
+
+// NumVersion parse input string, get valid number version. eg: go-1.22.3 -> 1.22.3
+func NumVersion(s string) string {
+ return regNumVersion.FindString(s)
+}
+
// MustToTime convert date string to time.Time
func MustToTime(s string, layouts ...string) time.Time {
t, err := ToTime(s, layouts...)
diff --git a/vendor/github.com/gookit/goutil/strutil/runes.go b/vendor/github.com/gookit/goutil/strutil/runes.go
index 6a3debba025..1bb678aad35 100644
--- a/vendor/github.com/gookit/goutil/strutil/runes.go
+++ b/vendor/github.com/gookit/goutil/strutil/runes.go
@@ -75,8 +75,8 @@ func Utf8Width(s string) int { return RunesWidth([]rune(s)) }
// str := "hi,你好"
//
// len(str) // 9
-// strutil.Utf8Width(str) // 7
-// len([]rune(str)) = utf8.RuneCountInString(s) // 5
+// strutil.RunesWidth(str) // 7 一个中文字占两个字符
+// len([]rune(str)) = utf8.RuneCountInString(s) // 5 按字算
func RunesWidth(rs []rune) (w int) {
if len(rs) == 0 {
return
diff --git a/vendor/github.com/gookit/goutil/strutil/strutil.go b/vendor/github.com/gookit/goutil/strutil/strutil.go
index 9e46773b942..2e15436f903 100644
--- a/vendor/github.com/gookit/goutil/strutil/strutil.go
+++ b/vendor/github.com/gookit/goutil/strutil/strutil.go
@@ -3,7 +3,6 @@ package strutil
import (
"errors"
- "fmt"
"strings"
"github.com/gookit/goutil/comdef"
@@ -77,34 +76,6 @@ func Valid(ss ...string) string {
return ""
}
-// Replaces replace multi strings
-//
-// pairs: {old1: new1, old2: new2, ...}
-//
-// Can also use:
-//
-// strings.NewReplacer("old1", "new1", "old2", "new2").Replace(str)
-func Replaces(str string, pairs map[string]string) string {
- return NewReplacer(pairs).Replace(str)
-}
-
-// NewReplacer instance
-func NewReplacer(pairs map[string]string) *strings.Replacer {
- ss := make([]string, len(pairs)*2)
- for old, newVal := range pairs {
- ss = append(ss, old, newVal)
- }
- return strings.NewReplacer(ss...)
-}
-
-// WrapTag for given string.
-func WrapTag(s, tag string) string {
- if s == "" {
- return s
- }
- return fmt.Sprintf("<%s>%s</%s>", tag, s, tag)
-}
-
// SubstrCount returns the number of times the substr substring occurs in the s string.
// Actually, it comes from strings.Count().
//
diff --git a/vendor/github.com/gookit/goutil/strutil/textutil/strvar_expr.go b/vendor/github.com/gookit/goutil/strutil/textutil/strvar_expr.go
index bcfff8d2485..ea91b5b4f99 100644
--- a/vendor/github.com/gookit/goutil/strutil/textutil/strvar_expr.go
+++ b/vendor/github.com/gookit/goutil/strutil/textutil/strvar_expr.go
@@ -15,8 +15,8 @@ import (
// 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')}
+// - 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
diff --git a/vendor/github.com/gookit/goutil/strutil/textutil/textutil.go b/vendor/github.com/gookit/goutil/strutil/textutil/textutil.go
index 7ade12c091f..931e9221daa 100644
--- a/vendor/github.com/gookit/goutil/strutil/textutil/textutil.go
+++ b/vendor/github.com/gookit/goutil/strutil/textutil/textutil.go
@@ -13,14 +13,14 @@ import (
// ReplaceVars by regex replace given tpl vars.
//
-// If a 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 replacement given tpl vars.
//
-// If a 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)
}
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 40044f300f6..798e0b25b6b 100644
--- a/vendor/github.com/gookit/goutil/strutil/textutil/var_replacer.go
+++ b/vendor/github.com/gookit/goutil/strutil/textutil/var_replacer.go
@@ -1,6 +1,7 @@
package textutil
import (
+ "os"
"reflect"
"regexp"
"strings"
@@ -11,7 +12,8 @@ import (
"github.com/gookit/goutil/strutil"
)
-const defaultVarFormat = "{{,}}"
+// DefaultVarFormat var template
+const DefaultVarFormat = "{{,}}"
// FallbackFn type
type FallbackFn = func(name string) (val string, ok bool)
@@ -28,7 +30,7 @@ type VarReplacer struct {
//
// eg: {name: {a: 1, b: 2}} => {name.a: 1, name.b: 2}
flatSubs bool
- // do parse env value. default: false
+ // do parse env value in var-value and tpl var-name. default: false
parseEnv bool
// do parse default value. default: false
//
@@ -46,7 +48,7 @@ type VarReplacer struct {
RenderFn func(s string, vs map[string]string) string
}
-// NewVarReplacer instance.
+// NewVarReplacer instance. default format is: DefaultVarFormat
//
// Usage:
//
@@ -104,7 +106,7 @@ func (r *VarReplacer) OnNotFound(fn FallbackFn) *VarReplacer {
// WithFormat custom var template
func (r *VarReplacer) WithFormat(format string) *VarReplacer {
- r.Left, r.Right = strutil.QuietCut(strutil.OrElse(format, defaultVarFormat), ",")
+ r.Left, r.Right = strutil.QuietCut(strutil.OrElse(format, DefaultVarFormat), ",")
r.Init()
return r
}
@@ -183,7 +185,11 @@ func (r *VarReplacer) RenderSimple(s string, varMap map[string]string) string {
if r.parseEnv {
for name, val := range varMap {
- varMap[name] = varexpr.SafeParse(val)
+ if strings.Contains(val, "${") {
+ varMap[name] = varexpr.SafeParse(val)
+ } else {
+ varMap[name] = val
+ }
}
}
@@ -223,6 +229,11 @@ func (r *VarReplacer) doReplace(s string, varMap map[string]string) string {
if val, ok := varMap[name]; ok {
return val
}
+ if r.parseEnv && strutil.IsEnvName(name) {
+ if val := os.Getenv(name); val != "" {
+ return val
+ }
+ }
// has custom not found handle func
if r.NotFound != nil {
diff --git a/vendor/github.com/gookit/goutil/syncs/syncs.go b/vendor/github.com/gookit/goutil/syncs/syncs.go
index e8933afdc75..64f7aab3fec 100644
--- a/vendor/github.com/gookit/goutil/syncs/syncs.go
+++ b/vendor/github.com/gookit/goutil/syncs/syncs.go
@@ -1,7 +1,10 @@
// Package syncs provides synchronization primitives util functions.
package syncs
-import "sync"
+import (
+ "context"
+ "sync"
+)
// WaitGroup is a wrapper of sync.WaitGroup.
type WaitGroup struct {
@@ -16,3 +19,8 @@ func (wg *WaitGroup) Go(fn func()) {
fn()
}()
}
+
+// ContextValue create a new context with given value
+func ContextValue(key, value any) context.Context {
+ return context.WithValue(context.Background(), key, value)
+}
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 a9bb0c98347..279d6027c41 100644
--- a/vendor/github.com/gookit/goutil/sysutil/os_version_windows.go
+++ b/vendor/github.com/gookit/goutil/sysutil/os_version_windows.go
@@ -43,7 +43,7 @@ func VersionInfoBySys() *OSVersionInfo {
// OsVersionByParse Get Windows system version information by parse string
//
-// cmdOut eg: "Microsoft Windows [Version 10.0.22631.4391]"
+// cmdOut eg: "Microsoft Windows [Version 10.0.22631.4391]"
func OsVersionByParse(cmdOut string) (*OSVersionInfo, error) {
return parseOsVersionString(cmdOut)
}
@@ -153,4 +153,3 @@ func parseOsVersionString(out string) (*OSVersionInfo, error) {
}
return &ovi, nil
}
-
diff --git a/vendor/github.com/gookit/goutil/sysutil/sysutil_darwin.go b/vendor/github.com/gookit/goutil/sysutil/sysutil_darwin.go
index f0962d8a2da..f8bb1503c70 100644
--- a/vendor/github.com/gookit/goutil/sysutil/sysutil_darwin.go
+++ b/vendor/github.com/gookit/goutil/sysutil/sysutil_darwin.go
@@ -1,4 +1,5 @@
//go:build darwin
+
package sysutil
import "os/exec"
diff --git a/vendor/github.com/gookit/goutil/x/basefn/basefn.go b/vendor/github.com/gookit/goutil/x/basefn/basefn.go
index ec9e8956e0c..3ab144eee7a 100644
--- a/vendor/github.com/gookit/goutil/x/basefn/basefn.go
+++ b/vendor/github.com/gookit/goutil/x/basefn/basefn.go
@@ -4,40 +4,18 @@ package basefn
import (
"errors"
"fmt"
+
+ "github.com/gookit/goutil/internal/comfunc"
)
// Panicf format panic message use fmt.Sprintf
-func Panicf(format string, v ...any) {
- panic(fmt.Sprintf(format, v...))
-}
+func Panicf(format string, v ...any) { panic(fmt.Sprintf(format, v...)) }
// PanicIf if cond = true, panics with an error message
func PanicIf(cond bool, fmtAndArgs ...any) {
if cond {
- panic(errors.New(formatWithArgs(fmtAndArgs)))
- }
-}
-
-func formatWithArgs(fmtAndArgs []any) string {
- ln := len(fmtAndArgs)
- if ln == 0 {
- return ""
- }
-
- first := fmtAndArgs[0]
-
- if ln == 1 {
- if msgAsStr, ok := first.(string); ok {
- return msgAsStr
- }
- return fmt.Sprintf("%+v", first)
- }
-
- // is template string.
- if tplStr, ok := first.(string); ok {
- return fmt.Sprintf(tplStr, fmtAndArgs[1:]...)
+ panic(errors.New(comfunc.FormatWithArgs(fmtAndArgs)))
}
- return fmt.Sprint(fmtAndArgs...)
}
// PanicErr panics if error is not empty
diff --git a/vendor/github.com/gookit/goutil/x/ccolor/color_tag.go b/vendor/github.com/gookit/goutil/x/ccolor/color_tag.go
index 69e554342fe..9162dce8447 100644
--- a/vendor/github.com/gookit/goutil/x/ccolor/color_tag.go
+++ b/vendor/github.com/gookit/goutil/x/ccolor/color_tag.go
@@ -4,8 +4,6 @@ import (
"fmt"
"regexp"
"strings"
-
- "github.com/gookit/goutil/x/termenv"
)
// output colored text like uses custom tag.
@@ -67,6 +65,7 @@ var colorTags = map[string]string{
"normal": "0;39", // no color
"brown": "0;33", // #A52A2A
"yellow": "0;33",
+ "ylw": "0;33",
"ylw0": "0;33",
"yellowB": "1;33", // with bold
"ylw1": "1;33",
@@ -178,7 +177,7 @@ 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() {
+ if shouldCleanColor() {
return ClearTag(str)
}
return ParseTag(str)
diff --git a/vendor/github.com/gookit/goutil/x/ccolor/style.go b/vendor/github.com/gookit/goutil/x/ccolor/style.go
index 5a808912024..2fd9f5791f2 100644
--- a/vendor/github.com/gookit/goutil/x/ccolor/style.go
+++ b/vendor/github.com/gookit/goutil/x/ccolor/style.go
@@ -8,8 +8,9 @@ import (
// String color style string. TODO
// eg:
-// s := String("red,bold")
-// s.Println("some text message")
+//
+// s := String("red,bold")
+// s.Println("some text message")
type String string
// Style for color render.
diff --git a/vendor/github.com/gookit/goutil/x/ccolor/util.go b/vendor/github.com/gookit/goutil/x/ccolor/util.go
index 46ed2b4bbc4..a00110dc29e 100644
--- a/vendor/github.com/gookit/goutil/x/ccolor/util.go
+++ b/vendor/github.com/gookit/goutil/x/ccolor/util.go
@@ -22,6 +22,10 @@ func ColorsToCode(colors ...Color) string {
return strings.Join(codes, ";")
}
+func shouldCleanColor() bool {
+ return termenv.NoColor() || !termenv.IsSupportColor()
+}
+
/*************************************************************
* render color code
*************************************************************/
@@ -43,7 +47,7 @@ func RenderCode(code string, args ...any) string {
}
// disabled OR not support color
- if !termenv.IsSupportColor() {
+ if shouldCleanColor() {
return ClearCode(message)
}
@@ -62,7 +66,7 @@ func RenderString(code string, str string) string {
}
// disabled OR not support color
- if !termenv.IsSupportColor() {
+ if shouldCleanColor() {
return ClearCode(str)
}
@@ -79,7 +83,7 @@ func RenderWithSpaces(code string, args ...any) string {
}
// disabled OR not support color
- if !termenv.IsSupportColor() {
+ if shouldCleanColor() {
return ClearCode(msg)
}
return StartSet + code + "m" + msg + ResetSet
@@ -128,4 +132,3 @@ func formatLikePrintln(args []any) (message string) {
}
return
}
-
diff --git a/vendor/github.com/gookit/goutil/x/goinfo/stack.go b/vendor/github.com/gookit/goutil/x/goinfo/stack.go
index 3912b7aa9b3..e6183569d77 100644
--- a/vendor/github.com/gookit/goutil/x/goinfo/stack.go
+++ b/vendor/github.com/gookit/goutil/x/goinfo/stack.go
@@ -124,12 +124,12 @@ type CallerFilterFunc func(file string, fc *runtime.Func) bool
//
// Usage:
//
-// cs := sysutil.CallersInfos(3, 2)
-// for _, ci := range cs {
-// fc := runtime.FuncForPC(pc)
-// // maybe need check fc = nil
-// fnName = fc.Name()
-// }
+// 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)
diff --git a/vendor/github.com/gookit/goutil/x/termenv/detect_nonwin.go b/vendor/github.com/gookit/goutil/x/termenv/detect_nonwin.go
index 39f8e367873..94ca89cda3f 100644
--- a/vendor/github.com/gookit/goutil/x/termenv/detect_nonwin.go
+++ b/vendor/github.com/gookit/goutil/x/termenv/detect_nonwin.go
@@ -43,4 +43,4 @@ func detectSpecialTermColor(termVal string) (ColorLevel, bool) {
func syscallStdinFd() int {
return syscall.Stdin
-}
\ No newline at end of file
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 515ece4f837..12695f2da16 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -738,7 +738,7 @@ github.com/google/uuid
## explicit; go 1.19
github.com/gookit/config/v2
github.com/gookit/config/v2/yaml
-# github.com/gookit/goutil v0.7.1
+# github.com/gookit/goutil v0.7.2
## explicit; go 1.19
github.com/gookit/goutil
github.com/gookit/goutil/arrutil