From: bruce Date: Fri, 22 Aug 2025 01:57:27 +0000 (+0000) Subject: feat(main): 1. add strutil/unicode.go. X-Git-Tag: v0.3.3 X-Git-Url: https://git.uzoombox.com/git/?a=commitdiff_plain;h=refs%2Fheads%2Fmaster;p=uzsdk.git feat(main): 1. add strutil/unicode.go. 2. add convutil.MergeStructs. 3. add fsutil.Sha1string. 4. add osutil.[RunCommand, IsPathExecutable]. --- diff --git a/utils/chrome/chromedp.go b/utils/chrome/chromedp.go index 829bff3..0d8845f 100644 --- a/utils/chrome/chromedp.go +++ b/utils/chrome/chromedp.go @@ -14,12 +14,12 @@ import ( ) // ClickandWait click an element with selector 'sel' if it's exist, and wait for done if 'wait' is true. -func ClickandWait(ctx context.Context, sel string, options ...interface{}) error { +func ClickandWait(ctx context.Context, sel string, options ...any) error { wait := true for _, option := range options { - switch option.(type) { + switch option := option.(type) { case bool: - wait = option.(bool) + wait = option } } if err := TryWaiting(ctx, sel, time.Millisecond*10, 1); err == nil { @@ -59,15 +59,15 @@ func FillInput(ctx context.Context, sel, value string) error { } // TryWaiting try to wait an element with selector 'sel' for exist. -func TryWaiting(ctx context.Context, sel string, options ...interface{}) error { +func TryWaiting(ctx context.Context, sel string, options ...any) error { interval := time.Millisecond * 20 count := 10 for _, option := range options { - switch option.(type) { + switch option := option.(type) { case time.Duration: - interval = option.(time.Duration) + interval = option case int: - count = option.(int) + count = option } } diff --git a/utils/convutil/reflect.go b/utils/convutil/reflect.go index c8bebbb..e0210f1 100644 --- a/utils/convutil/reflect.go +++ b/utils/convutil/reflect.go @@ -1,4 +1,4 @@ -// Copyright 2010-2022 nanzoom.com. All Rights Reserved. +// Copyright 2010-2025 nanzoom.com. All Rights Reserved. package convutil @@ -13,7 +13,7 @@ import ( ) // PrintStruct print information of struct, v can be ptr or struct. -func PrintStruct(v interface{}) error { +func PrintStruct(v any) error { vv := reflect.ValueOf(v) if vv.Kind() == reflect.Ptr && !vv.IsNil() { vv = vv.Elem() @@ -33,10 +33,10 @@ func PrintStruct(v interface{}) error { return nil } -// AssignStruct set struct dst's value with map[string]interface{} src, +// AssignStruct set struct dst's value with map[string]any src, // if force=true: assign value anytime, // else: assign value when field does not exist or field value is zero. -func AssignStruct(dst interface{}, src interface{}, force bool) error { +func AssignStruct(dst any, src any, force bool) error { vd := reflect.ValueOf(dst) vs := reflect.ValueOf(src) if vd.Kind() != reflect.Ptr || vd.IsNil() { @@ -88,23 +88,23 @@ func AssignStruct(dst interface{}, src interface{}, force bool) error { // // excludes []string: MUST NOT copy fields. // includes []string: MUST copy fields that even it's value is nil. -func CopyStruct(v interface{}, notnil bool, options ...interface{}) interface{} { +func CopyStruct(v any, notnil bool, options ...any) any { // parse options. var excludes []string var includes []string for _, option := range options { - switch option.(type) { + switch option := option.(type) { case []string: - excludes = option.([]string) - case map[string]interface{}: - for k, v := range option.(map[string]interface{}) { - switch v.(type) { + excludes = option + case map[string]any: + for k, v := range option { + switch v := v.(type) { case []string: switch k { case "excludes": - excludes = v.([]string) + excludes = v case "includes": - includes = v.([]string) + includes = v } } } @@ -164,9 +164,87 @@ func CopyStruct(v interface{}, notnil bool, options ...interface{}) interface{} return vd.Interface() } +// MergeStructs merge struct src's value to struct dst +func MergeStructs(dst, src any) error { + vd := reflect.ValueOf(dst) + vs := reflect.ValueOf(src) + if vd.Kind() != reflect.Ptr { + return fmt.Errorf("dst is not a valid pointer.") + } + if vs.Kind() != reflect.Ptr { + return fmt.Errorf("src is not a valid pointer.") + } + + vd = vd.Elem() + vs = vs.Elem() + if vd.Kind() != reflect.Struct { + return fmt.Errorf("dst must be a struct.") + } + if vs.Kind() != reflect.Struct { + return fmt.Errorf("src must be a struct.") + } + if vd.Type() != vs.Type() { + return fmt.Errorf("dst and src must be the same type.") + } + + for i := range vs.NumField() { + srcField, srcFieldType := vs.Field(i), vs.Type().Field(i) + dstField := vd.FieldByName(srcFieldType.Name) + if !dstField.IsValid() { + fmt.Printf("Skipping invalid field: %s\n", srcFieldType.Name) + continue + } + if !dstField.CanSet() { + fmt.Printf("Skipping unexported or unsettable field: %s\n", srcFieldType.Name) + continue + } + + isDstFieldZero := dstField.IsZero() + if srcField.Kind() == reflect.Struct { + // 如果字段是结构体 + if isDstFieldZero { + // 目标结构体是零值,并且源结构体有值,则直接用源结构体覆盖目标结构体 + if !srcField.IsZero() { + dstField.Set(srcField) + } + } else { + // 目标结构体非零,递归合并其字段 + dstFieldAddr := dstField.Addr().Interface() + srcFieldAddr := srcField.Addr().Interface() + if err := MergeStructs(dstFieldAddr, srcFieldAddr); err != nil { + return fmt.Errorf("error merging nested struct %s: %w", srcFieldType.Name, err) + } + } + } else if srcField.Kind() == reflect.Ptr && srcField.Elem().Kind() == reflect.Struct { + // 如果字段是指向结构体的指针 + if dstField.IsNil() { + // 目标指针是 nil, 并且源指针不是 nil,则复制整个指向的结构体 + if !srcField.IsNil() { + dstField.Set(srcField) + } + } else if !srcField.IsNil() { + // 目标和源指针都不是 nil,递归合并它们指向的结构体 + if err := MergeStructs(dstField.Interface(), srcField.Interface()); err != nil { + return fmt.Errorf("error merging nested ptr struct %s: %w", srcFieldType.Name, err) + } + } + } else { + // 对于基本类型或非结构体指针类型 + if isDstFieldZero { + // 如果目标字段是零值,并且源字段不是零值 + if srcField.IsValid() && srcField.Type().AssignableTo(dstField.Type()) { + dstField.Set(srcField) + } + } + } + } + + return nil +} + // BindString make a string from value, // and returns the point of it for binding. -func BindString(value interface{}) *string { +func BindString(value any) *string { val, _ := conv.String(value) return &val } diff --git a/utils/convutil/sort.go b/utils/convutil/sort.go index 7110d26..cb686f3 100644 --- a/utils/convutil/sort.go +++ b/utils/convutil/sort.go @@ -8,7 +8,7 @@ import ( ) // SortMap returns an sorted array of map keys. -func SortMap(v interface{}) []string { +func SortMap(v any) []string { vv := reflect.ValueOf(v) if vv.Kind() != reflect.Map { return nil @@ -22,7 +22,7 @@ func SortMap(v interface{}) []string { } // SortMapInt returns an sorted int array of map keys. -func SortMapInt(v interface{}) []int { +func SortMapInt(v any) []int { vv := reflect.ValueOf(v) if vv.Kind() != reflect.Map { return nil @@ -49,7 +49,7 @@ func SortMapInt(v interface{}) []int { } // SortMapFloat64 returns an sorted float array of map keys. -func SortMapFloat64(v interface{}) []float64 { +func SortMapFloat64(v any) []float64 { vv := reflect.ValueOf(v) if vv.Kind() != reflect.Map { return nil @@ -72,7 +72,7 @@ func SortMapFloat64(v interface{}) []float64 { } // SortMapString returns an sorted string array of map keys. -func SortMapString(v interface{}) []string { +func SortMapString(v any) []string { vv := reflect.ValueOf(v) if vv.Kind() != reflect.Map { return nil diff --git a/utils/dbutil/format.go b/utils/dbutil/format.go index 0285ec2..9fc363e 100644 --- a/utils/dbutil/format.go +++ b/utils/dbutil/format.go @@ -29,55 +29,55 @@ type Query struct { } // Parse returns request list to Query. -func (query *Query) Parse(params interface{}) { +func (query *Query) Parse(params any) { switch params.(type) { - case map[string]interface{}: + case map[string]any: default: return } - for key, value := range params.(map[string]interface{}) { + for key, value := range params.(map[string]any) { switch strings.ToLower(key) { case "fields": query.Fields = make([]string, 0) - switch value.(type) { + switch value := value.(type) { case string: - query.Fields = append(query.Fields, value.(string)) - case []interface{}: - for _, v := range value.([]interface{}) { - switch v.(type) { + query.Fields = append(query.Fields, value) + case []any: + for _, v := range value { + switch v := v.(type) { case string: - query.Fields = append(query.Fields, v.(string)) + query.Fields = append(query.Fields, v) } } } case "wheres": query.Wheres = make(map[string]string) switch value.(type) { - case map[string]interface{}: - for k, v := range value.(map[string]interface{}) { - switch v.(type) { + case map[string]any: + for k, v := range value.(map[string]any) { + switch v := v.(type) { case string: - query.Wheres[k] = fmt.Sprintf("%s", v.(string)) - case []interface{}: + query.Wheres[k] = fmt.Sprintf("%s", v) + case []any: vt := make([]string, 0) - for _, t := range v.([]interface{}) { - switch t.(type) { + for _, t := range v { + switch t := t.(type) { case string: - vt = append(vt, fmt.Sprintf("'%s'", t.(string))) + vt = append(vt, fmt.Sprintf("'%s'", t)) } } if len(vt) >= 2 { query.Wheres[k] = fmt.Sprintf("BETWEEN %s AND %s", vt[0], vt[1]) } - case map[string]interface{}: - for _, t := range v.(map[string]interface{}) { - switch t.(type) { - case []interface{}: + case map[string]any: + for _, t := range v { + switch t := t.(type) { + case []any: vt := make([]string, 0) - for _, p := range t.([]interface{}) { - switch p.(type) { + for _, p := range t { + switch p := p.(type) { case string: - vt = append(vt, fmt.Sprintf("'%s'", p.(string))) + vt = append(vt, fmt.Sprintf("'%s'", p)) } } if len(vt) > 0 { @@ -90,20 +90,20 @@ func (query *Query) Parse(params interface{}) { } } case "groupby": - switch value.(type) { + switch value := value.(type) { case string: - query.Groupby = value.(string) + query.Groupby = value } case "orderby": - switch value.(type) { + switch value := value.(type) { case string: - query.OrderBy = value.(string) + query.OrderBy = value } case "limits": query.Limits = make(map[string]int64) - switch value.(type) { - case map[string]interface{}: - for k, v := range value.(map[string]interface{}) { + switch value := value.(type) { + case map[string]any: + for k, v := range value { switch strings.ToLower(k) { case "offset": query.Limits["offset"], _ = strconv.ParseInt(fmt.Sprint(v), 0, 64) @@ -114,9 +114,9 @@ func (query *Query) Parse(params interface{}) { } case "params": query.Params = make(map[string]string) - switch value.(type) { - case map[string]interface{}: - for k, v := range value.(map[string]interface{}) { + switch value := value.(type) { + case map[string]any: + for k, v := range value { query.Params[k] = fmt.Sprint(v) } } @@ -179,25 +179,25 @@ type Stats struct { // StatsRes defines response stats object. type StatsRes struct { - Minute *interface{} `db:"minute"` - Hour *interface{} `db:"hour"` - Day *interface{} `db:"day"` - Week *interface{} `db:"week"` - Month *interface{} `db:"month"` - Quarter *interface{} `db:"quarter"` - Year *interface{} `db:"year"` - All *interface{} `db:"all"` + Minute *any `db:"minute"` + Hour *any `db:"hour"` + Day *any `db:"day"` + Week *any `db:"week"` + Month *any `db:"month"` + Quarter *any `db:"quarter"` + Year *any `db:"year"` + All *any `db:"all"` } // Parse returns request list to Stats. -func (stats *Stats) Parse(params interface{}) { +func (stats *Stats) Parse(params any) { switch params.(type) { - case map[string]interface{}: + case map[string]any: default: return } - for key, value := range params.(map[string]interface{}) { + for key, value := range params.(map[string]any) { switch strings.ToLower(key) { case "name": stats.Name = fmt.Sprint(value) @@ -206,45 +206,45 @@ func (stats *Stats) Parse(params interface{}) { case "timezone": stats.Timezone = fmt.Sprint(value) case "series": - switch value.(type) { + switch value := value.(type) { case string: - stats.Series = append(stats.Series, value.(string)) - case []interface{}: - for _, v := range value.([]interface{}) { - switch v.(type) { + stats.Series = append(stats.Series, value) + case []any: + for _, v := range value { + switch v := v.(type) { case string: - stats.Series = append(stats.Series, v.(string)) + stats.Series = append(stats.Series, v) } } } case "wheres": stats.Wheres = make(map[string]string) switch value.(type) { - case map[string]interface{}: - for k, v := range value.(map[string]interface{}) { - switch v.(type) { + case map[string]any: + for k, v := range value.(map[string]any) { + switch v := v.(type) { case string: - stats.Wheres[k] = fmt.Sprintf("%s", v.(string)) - case []interface{}: + stats.Wheres[k] = fmt.Sprintf("%s", v) + case []any: vt := make([]string, 0) - for _, t := range v.([]interface{}) { - switch t.(type) { + for _, t := range v { + switch t := t.(type) { case string: - vt = append(vt, fmt.Sprintf("'%s'", t.(string))) + vt = append(vt, fmt.Sprintf("'%s'", t)) } } if len(vt) >= 2 { stats.Wheres[k] = fmt.Sprintf("BETWEEN %s AND %s", vt[0], vt[1]) } - case map[string]interface{}: - for _, t := range v.(map[string]interface{}) { + case map[string]any: + for _, t := range v { switch t.(type) { - case []interface{}: + case []any: vt := make([]string, 0) - for _, p := range t.([]interface{}) { - switch p.(type) { + for _, p := range t.([]any) { + switch p := p.(type) { case string: - vt = append(vt, fmt.Sprintf("'%s'", p.(string))) + vt = append(vt, fmt.Sprintf("'%s'", p)) } } if len(vt) > 0 { @@ -322,19 +322,19 @@ type Serie struct { // SerieRes defines response serie object. type SerieRes struct { - Field []interface{} - Value []interface{} + Field []any + Value []any } // Parse returns request list to Stats. -func (serie *Serie) Parse(params interface{}) { +func (serie *Serie) Parse(params any) { switch params.(type) { - case map[string]interface{}: + case map[string]any: default: return } - for key, value := range params.(map[string]interface{}) { + for key, value := range params.(map[string]any) { switch strings.ToLower(key) { case "name": serie.Name = fmt.Sprint(value) @@ -351,31 +351,31 @@ func (serie *Serie) Parse(params interface{}) { case "wheres": serie.Wheres = make(map[string]string) switch value.(type) { - case map[string]interface{}: - for k, v := range value.(map[string]interface{}) { - switch v.(type) { + case map[string]any: + for k, v := range value.(map[string]any) { + switch v := v.(type) { case string: - serie.Wheres[k] = fmt.Sprintf("%s", v.(string)) - case []interface{}: + serie.Wheres[k] = fmt.Sprintf("%s", v) + case []any: vt := make([]string, 0) - for _, t := range v.([]interface{}) { - switch t.(type) { + for _, t := range v { + switch t := t.(type) { case string: - vt = append(vt, fmt.Sprintf("'%s'", t.(string))) + vt = append(vt, fmt.Sprintf("'%s'", t)) } } if len(vt) >= 2 { serie.Wheres[k] = fmt.Sprintf("BETWEEN %s AND %s", vt[0], vt[1]) } - case map[string]interface{}: - for _, t := range v.(map[string]interface{}) { + case map[string]any: + for _, t := range v { switch t.(type) { - case []interface{}: + case []any: vt := make([]string, 0) - for _, p := range t.([]interface{}) { - switch p.(type) { + for _, p := range t.([]any) { + switch p := p.(type) { case string: - vt = append(vt, fmt.Sprintf("'%s'", p.(string))) + vt = append(vt, fmt.Sprintf("'%s'", p)) } } if len(vt) > 0 { @@ -478,12 +478,12 @@ LEFT JOIN ( // Wheres returns an formatted sql where string without 'WHERE' from obj's value, // exp: {ad_client_id: '1', name: 'test'} => "ad_client_id='1' AND name='test'" -func Wheres(obj interface{}) string { +func Wheres(obj any) string { var wheres string - m := make(map[string]interface{}) - switch obj.(type) { - case map[string]interface{}: - m = obj.(map[string]interface{}) + m := make(map[string]any) + switch obj := obj.(type) { + case map[string]any: + m = obj default: m = structs.Map(obj) } diff --git a/utils/dsutil/array.go b/utils/dsutil/array.go index 807fe9c..effd222 100644 --- a/utils/dsutil/array.go +++ b/utils/dsutil/array.go @@ -7,7 +7,7 @@ import ( ) // Equal returns true if array equals to val. -func Equal(array interface{}, val interface{}) bool { +func Equal(array any, val any) bool { va, vv := reflect.ValueOf(array), reflect.ValueOf(val) if (va.Kind() != reflect.Array && va.Kind() != reflect.Slice) || (vv.Kind() != reflect.Array && vv.Kind() != reflect.Slice) { @@ -29,7 +29,7 @@ func Equal(array interface{}, val interface{}) bool { } // Includes returns true if an array includes a certain value. -func Includes(array interface{}, val interface{}) bool { +func Includes(array any, val any) bool { va := reflect.ValueOf(array) if va.Kind() != reflect.Array && va.Kind() != reflect.Slice { return false @@ -47,7 +47,7 @@ func Includes(array interface{}, val interface{}) bool { } // IncludesAt returns index if array includes val. -func IncludesAt(array interface{}, val interface{}) int { +func IncludesAt(array any, val any) int { va, vv := reflect.ValueOf(array), reflect.ValueOf(val) if (va.Kind() != reflect.Array && va.Kind() != reflect.Slice) || 0 >= va.Len() { return -1 @@ -86,13 +86,13 @@ func IncludesAt(array interface{}, val interface{}) int { } // ContainsAny returns true if array contains any elements in val. -func ContainsAny(array interface{}, val interface{}) bool { +func ContainsAny(array any, val any) bool { i, _ := ContainsAnyAt(array, val) return i != -1 } // ContainsAnyAt returns index if array contains any elements in val. -func ContainsAnyAt(array interface{}, val interface{}) (int, int) { +func ContainsAnyAt(array any, val any) (int, int) { va, vv := reflect.ValueOf(array), reflect.ValueOf(val) if (va.Kind() != reflect.Array && va.Kind() != reflect.Slice) || 0 >= va.Len() { return -1, -1 @@ -126,13 +126,13 @@ func ContainsAnyAt(array interface{}, val interface{}) (int, int) { } // ContainsAll returns true if array contains all elements in val. -func ContainsAll(array interface{}, val interface{}) bool { +func ContainsAll(array any, val any) bool { res := ContainsAllAt(array, val) return res != nil } // ContainsAllAt returns index if array contains all elements in val. -func ContainsAllAt(array interface{}, val interface{}) []int { +func ContainsAllAt(array any, val any) []int { va, vv := reflect.ValueOf(array), reflect.ValueOf(val) if (va.Kind() != reflect.Array && va.Kind() != reflect.Slice) || 0 >= va.Len() { return nil diff --git a/utils/fmtutil/format.go b/utils/fmtutil/format.go index efb2170..9dc0fc0 100644 --- a/utils/fmtutil/format.go +++ b/utils/fmtutil/format.go @@ -10,13 +10,13 @@ import ( ) // FormatDate returns a string formatted value with type: date.storage. -func FormatDate(value interface{}, timezone, format string) string { +func FormatDate(value any, timezone, format string) string { var s time.Time - switch value.(type) { + switch value := value.(type) { case string: - s, _ = time.Parse(time.RFC3339, value.(string)) + s, _ = time.Parse(time.RFC3339, value) case *time.Time: - s = *value.(*time.Time) + s = *value } var options string @@ -37,13 +37,13 @@ func FormatDate(value interface{}, timezone, format string) string { } // FormatStorage returns a string formatted value with type: uom.storage. -func FormatStorage(value interface{}) string { +func FormatStorage(value any) string { var bytes int64 - switch value.(type) { + switch value := value.(type) { case int64: - bytes = value.(int64) + bytes = value case *string: - bytes, _ = strconv.ParseInt(*value.(*string), 0, 64) + bytes, _ = strconv.ParseInt(*value, 0, 64) } if bytes == 0 { diff --git a/utils/fsutil/archive.go b/utils/fsutil/archive.go index a3a6aa6..1ce9788 100644 --- a/utils/fsutil/archive.go +++ b/utils/fsutil/archive.go @@ -48,15 +48,15 @@ func ZipNamedTree(tree map[string]ZipNode, target string) error { // // recursive bool: zip directory recursively, default: true. // files []string: only zip these files, default zip all files. -func ZipFilepath(paths []string, target string, options ...interface{}) error { +func ZipFilepath(paths []string, target string, options ...any) error { var recursive = true var files []string for _, option := range options { - switch option.(type) { + switch option := option.(type) { case bool: - recursive = option.(bool) + recursive = option case []string: - files = option.([]string) + files = option } } diff --git a/utils/fsutil/crypto.go b/utils/fsutil/crypto.go index aac6e83..b0c625d 100644 --- a/utils/fsutil/crypto.go +++ b/utils/fsutil/crypto.go @@ -1,4 +1,4 @@ -// Copyright 2010-2022 nanzoom.com. All Rights Reserved. +// Copyright 2010-2025 nanzoom.com. All Rights Reserved. package fsutil @@ -8,6 +8,7 @@ import ( "crypto/aes" "crypto/cipher" "crypto/md5" + "crypto/sha1" "crypto/sha256" "fmt" "hash" @@ -98,6 +99,11 @@ func Md5string(ss ...string) string { return hashString(crypto.MD5, ss...) } +// Sha1string returns the SHA1 checksum of string(s). +func Sha1string(ss ...string) string { + return hashString(crypto.SHA1, ss...) +} + // Sha256string returns the SHA256 checksum of string(s). func Sha256string(ss ...string) string { return hashString(crypto.SHA256, ss...) @@ -225,6 +231,8 @@ func hashString(t crypto.Hash, ss ...string) string { switch t { case crypto.MD5: h = md5.New() + case crypto.SHA1: + h = sha1.New() case crypto.SHA256: h = sha256.New() default: diff --git a/utils/fsutil/encoding.go b/utils/fsutil/encoding.go index 2b2d9c8..b8caa24 100644 --- a/utils/fsutil/encoding.go +++ b/utils/fsutil/encoding.go @@ -1,4 +1,4 @@ -// Copyright 2010-2022 nanzoom.com. All Rights Reserved. +// Copyright 2010-2025 nanzoom.com. All Rights Reserved. package fsutil @@ -21,12 +21,12 @@ func Base64file(path string) string { // options will be [noPadding]: // // noPadding bool: not padding with '=' if set to true, default: false. -func Base64Encode(text string, options ...interface{}) string { +func Base64Encode(text string, options ...any) string { var noPadding bool for _, option := range options { - switch option.(type) { + switch option := option.(type) { case bool: - noPadding = option.(bool) + noPadding = option } } @@ -41,12 +41,12 @@ func Base64Encode(text string, options ...interface{}) string { // options will be [noPadding]: // // noPadding bool: not padding with '=' if set to true, default: false. -func Base64Decode(text string, options ...interface{}) string { +func Base64Decode(text string, options ...any) string { var noPadding bool for _, option := range options { - switch option.(type) { + switch option := option.(type) { case bool: - noPadding = option.(bool) + noPadding = option } } diff --git a/utils/netutil/mime.go b/utils/netutil/mime.go index 979847a..d5283cc 100644 --- a/utils/netutil/mime.go +++ b/utils/netutil/mime.go @@ -19,17 +19,17 @@ import ( // file string: path of file. // fi fs.FileInfo: fileinfo. // re *regexp.Regexp: regexp. -func Mimetype(options ...interface{}) string { +func Mimetype(options ...any) string { var fi fs.FileInfo var re *regexp.Regexp for _, option := range options { - switch option.(type) { + switch option := option.(type) { case string: - fi, _ = os.Stat(option.(string)) + fi, _ = os.Stat(option) case fs.FileInfo: - fi = option.(fs.FileInfo) + fi = option case *regexp.Regexp: - re = option.(*regexp.Regexp) + re = option } } @@ -49,14 +49,14 @@ func Mimetype(options ...interface{}) string { // options will be [content]: // // content [*http.Request, *string]: header["Content-Type"] -func Multipart(options ...interface{}) string { +func Multipart(options ...any) string { var content string // "Content-Type: multipart/form-data;boundary=---yb1zYhTI38xpQxBK" for _, option := range options { - switch option.(type) { + switch option := option.(type) { case *http.Request: - content = option.(*http.Request).Header.Get("Content-Type") + content = option.Header.Get("Content-Type") case *string: - content = *option.(*string) + content = *option } } diff --git a/utils/osutil/exec.go b/utils/osutil/exec.go index e84acc6..9c672b2 100644 --- a/utils/osutil/exec.go +++ b/utils/osutil/exec.go @@ -1,4 +1,4 @@ -// Copyright 2010-2022 nanzoom.com. All Rights Reserved. +// Copyright 2010-2025 nanzoom.com. All Rights Reserved. package osutil @@ -7,6 +7,13 @@ import ( "os/exec" ) +// RunCommand construct a command struct and then call cmd.Start and return immediately, +// returns the command struct and exec status at last. +func RunCommand(name string, arg ...string) (*exec.Cmd, error) { + cmd := exec.Command(name, arg...) + return cmd, cmd.Start() +} + // ExecCommand construct a command struct and then call cmd.Start and cmd.Wait, // returns the command struct and exec status at last. func ExecCommand(name string, arg ...string) (*exec.Cmd, error) { diff --git a/utils/osutil/path.go b/utils/osutil/path.go index 6a8913b..60dd5ba 100644 --- a/utils/osutil/path.go +++ b/utils/osutil/path.go @@ -1,4 +1,4 @@ -// Copyright 2010-2022 nanzoom.com. All Rights Reserved. +// Copyright 2010-2025 nanzoom.com. All Rights Reserved. package osutil @@ -35,6 +35,20 @@ func IsPathExist(path string) bool { return true } +// IsPathExecutable returns true if the pathfile have permmode:x. +func IsPathExecutable(path string) bool { + info, err := os.Stat(path) + if err != nil { + return false + } + + if info.IsDir() { + return false + } + + return info.Mode()&0111 != 0 +} + // IsDir returns true if the path is an exist directory. func IsDir(path string) bool { fi, err := os.Stat(path) diff --git a/utils/osutil/syscall.go b/utils/osutil/syscall.go index 3764463..70a0852 100644 --- a/utils/osutil/syscall.go +++ b/utils/osutil/syscall.go @@ -10,8 +10,8 @@ import ( ) // DiskUsage estimate file space usage, returns the command struct and exec status at last. -func DiskUsage(path string) (map[string]interface{}, error) { - result := make(map[string]interface{}) +func DiskUsage(path string) (map[string]any, error) { + result := make(map[string]any) var buf unix.Statfs_t if err := unix.Statfs(path, &buf); err != nil { return result, err diff --git a/utils/strutil/encoding.go b/utils/strutil/encoding.go index 1358f96..aeefc1d 100644 --- a/utils/strutil/encoding.go +++ b/utils/strutil/encoding.go @@ -25,7 +25,7 @@ func Base64URLDecode(v string) string { } // Base64URLHash returns a base64 encoded string hashed with murmur of v. -func Base64URLHash(v interface{}) string { +func Base64URLHash(v any) string { str := fmt.Sprint(v) sum := murmur3.Sum64([]byte(str)) buf := fmt.Sprintf("%.16x", sum) diff --git a/utils/strutil/unicode.go b/utils/strutil/unicode.go new file mode 100644 index 0000000..1558f5a --- /dev/null +++ b/utils/strutil/unicode.go @@ -0,0 +1,26 @@ +// Copyright 2010-2025 nanzoom.com. All Rights Reserved. + +package strutil + +import "unicode" + +// ContainsNonBasicLatinLetter checks if s contains any Unicode letter +// that is not a basic Latin letter (part of the "Latin" script and not ASCII a-zA-Z). +// This is a more robust way to define "English letter" vs "non-English letter". +func ContainsNonBasicLatinLetter(s string) bool { + for _, r := range s { + if unicode.IsLetter(r) && !unicode.Is(unicode.Latin, r) { + // It's a letter, but not from the Latin script (e.g., Cyrillic, Han, Greek) + return true + } + // If it's a Latin script letter, check if it's an extended Latin letter + // (like é, ü, ñ) which are Latin but not basic a-zA-Z. + // Basic a-zA-Z are ASCII. + if unicode.IsLetter(r) && unicode.Is(unicode.Latin, r) && r > unicode.MaxASCII { + // It's a Latin letter but outside the ASCII range (0-127), + // so it must be an extended Latin letter. + return true + } + } + return false +} diff --git a/uzsdk/logger/logger.go b/uzsdk/logger/logger.go index 84a1fd7..cd9422a 100644 --- a/uzsdk/logger/logger.go +++ b/uzsdk/logger/logger.go @@ -31,7 +31,7 @@ type Logger struct { // New creates a new Logger, the path is the filepath of logfile, // logv is an string in ['fatal','error','warn','info','debug'], // prefix is the content write after timestamp and before message. -func New(path, logv, prefix string, options ...interface{}) *Logger { +func New(path, logv, prefix string, options ...any) *Logger { var writer *os.File if path != "" { writer, _ = os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) @@ -65,7 +65,7 @@ func New(path, logv, prefix string, options ...interface{}) *Logger { // NewWriter creates a new Logger, the writer is the *os.File of logfile, // logv is an string in ['fatal','error','warn','info','debug'], // prefix is the content write after timestamp and before message. -func NewWriter(writer *os.File, logv, prefix string, options ...interface{}) *Logger { +func NewWriter(writer *os.File, logv, prefix string, options ...any) *Logger { if writer == nil { writer = os.Stdout } @@ -101,52 +101,52 @@ func (l *Logger) Writer() *os.File { } // Fatal calls output(Fatal, Sprint(v)) to print the log. -func (l *Logger) Fatal(v ...interface{}) { +func (l *Logger) Fatal(v ...any) { l.Output(Fatal, "FATAL", fmt.Sprint(v...)) } // Fatalf calls output(Fatal, Sprintf(format, v)) to print the log. -func (l *Logger) Fatalf(format string, v ...interface{}) { +func (l *Logger) Fatalf(format string, v ...any) { l.Output(Fatal, "FATAL", fmt.Sprintf(format, v...)) } // Error calls output(Error) to print the log. -func (l *Logger) Error(v ...interface{}) { +func (l *Logger) Error(v ...any) { l.Output(Error, "ERROR", fmt.Sprint(v...)) } // Errorf calls output(Error, Sprintf(format, v)) to print the log. -func (l *Logger) Errorf(format string, v ...interface{}) { +func (l *Logger) Errorf(format string, v ...any) { l.Output(Error, "ERROR", fmt.Sprintf(format, v...)) } // Warn calls output(Warn) to print the log. -func (l *Logger) Warn(v ...interface{}) { +func (l *Logger) Warn(v ...any) { l.Output(Warn, "WARN", fmt.Sprint(v...)) } // Warnf calls output(Warn, Sprintf(format, v)) to print the log. -func (l *Logger) Warnf(format string, v ...interface{}) { +func (l *Logger) Warnf(format string, v ...any) { l.Output(Warn, "WARN", fmt.Sprintf(format, v...)) } // Info calls output(Info) to print the log. -func (l *Logger) Info(v ...interface{}) { +func (l *Logger) Info(v ...any) { l.Output(Info, "INFO", fmt.Sprint(v...)) } // Infof calls output(Info, Sprintf(format, v)) to print the log. -func (l *Logger) Infof(format string, v ...interface{}) { +func (l *Logger) Infof(format string, v ...any) { l.Output(Info, "INFO", fmt.Sprintf(format, v...)) } // Debug calls output(Debug) to print the log. -func (l *Logger) Debug(v ...interface{}) { +func (l *Logger) Debug(v ...any) { l.Output(Debug, "DEBUG", fmt.Sprint(v...)) } // Debugf calls output(Debug, Sprintf(format, v)) to print the log. -func (l *Logger) Debugf(format string, v ...interface{}) { +func (l *Logger) Debugf(format string, v ...any) { l.Output(Debug, "DEBUG", fmt.Sprintf(format, v...)) } diff --git a/uzsdk/service.go b/uzsdk/service.go index 42dfd05..883c445 100644 --- a/uzsdk/service.go +++ b/uzsdk/service.go @@ -62,24 +62,24 @@ var RespCode = map[string]int64{ // Error defines error object, reference to https://www.jsonrpc.org/specification#error_object. type Error struct { - Code int64 `json:"code"` - Message string `json:"message"` - Data interface{} `json:"data"` + Code int64 `json:"code"` + Message string `json:"message"` + Data any `json:"data"` } // Request defines Request object, reference to https://www.jsonrpc.org/specification#request_object. type Request struct { // Jsonrpc string `json:"jsonrpc"` - Method string `json:"method"` - Params interface{} `json:"params"` - ID interface{} `json:"id"` // string, number or null - Token string `json:"token"` + Method string `json:"method"` + Params any `json:"params"` + ID any `json:"id"` // string, number or null + Token string `json:"token"` } // ReqParamList defines Request.Param of method:list. type ReqParamList struct { - Fields []string `json:"fields"` - Wheres interface{} `json:"wheres"` // use gin.H, exp: gin.H{"isactive": "='Y'"} + Fields []string `json:"fields"` + Wheres any `json:"wheres"` // use gin.H, exp: gin.H{"isactive": "='Y'"} Limits struct { Offset int64 `json:"offset"` Limit int64 `json:"limit"` @@ -91,21 +91,21 @@ type ReqParamList struct { // Response defines response object, reference to https://www.jsonrpc.org/specification#response_object. type Response struct { // Jsonrpc string `json:"jsonrpc"` - Result interface{} `json:"result"` - Error Error `json:"error"` - ID interface{} `json:"id"` // string, number or null + Result any `json:"result"` + Error Error `json:"error"` + ID any `json:"id"` // string, number or null } // ResResultExec defines Response.Result of method:[add,del,set]. type ResResultExec struct { - Affected int64 `json:"affected"` - Returnids []interface{} `json:"returnids"` + Affected int64 `json:"affected"` + Returnids []any `json:"returnids"` } // ResResultList defines Response.Result of method:list. type ResResultList struct { - Count int64 `json:"count"` - Rows []interface{} `json:"rows"` + Count int64 `json:"count"` + Rows []any `json:"rows"` } // Respcode returns a structure of Respnode that message has been translated using language if options.language is set, @@ -113,12 +113,12 @@ type ResResultList struct { // options will be [language]: // // language *string: name of language. -func Respcode(code string, options ...interface{}) Respnode { +func Respcode(code string, options ...any) Respnode { var language *string = nil for _, option := range options { - switch option.(type) { + switch option := option.(type) { case *string: - language = option.(*string) + language = option } } @@ -136,7 +136,7 @@ type RequireResource struct { } // TokenCheckFunc defines function type of check client privilege by token. -type TokenCheckFunc func(token string, options ...interface{}) bool +type TokenCheckFunc func(token string, options ...any) bool // Instance is the framework's instance, used for server & plugin both. type Instance struct { diff --git a/version.go b/version.go index d34c29d..f453987 100644 --- a/version.go +++ b/version.go @@ -1,5 +1,5 @@ -// Copyright 2010-2024 nanzoom.com. All Rights Reserved. +// Copyright 2010-2025 nanzoom.com. All Rights Reserved. package uzsdk // Version is the current opensdk's version. -const Version = "0.3.2" +const Version = "0.3.3"