2. add convutil.MergeStructs.
3. add fsutil.Sha1string.
4. add osutil.[RunCommand, IsPathExecutable].
)
// 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 {
}
// 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
}
}
-// Copyright 2010-2022 nanzoom.com. All Rights Reserved.
+// Copyright 2010-2025 nanzoom.com. All Rights Reserved.
package convutil
)
// 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()
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() {
//
// 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
}
}
}
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
}
)
// 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
}
// 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
}
// 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
}
// 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
}
// 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 {
}
}
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)
}
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)
}
}
// 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)
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 {
// 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)
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 {
// 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)
}
)
// 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) {
}
// 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
}
// 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
}
// 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
}
// 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
)
// 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
}
// 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 {
//
// 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
}
}
-// Copyright 2010-2022 nanzoom.com. All Rights Reserved.
+// Copyright 2010-2025 nanzoom.com. All Rights Reserved.
package fsutil
"crypto/aes"
"crypto/cipher"
"crypto/md5"
+ "crypto/sha1"
"crypto/sha256"
"fmt"
"hash"
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...)
switch t {
case crypto.MD5:
h = md5.New()
+ case crypto.SHA1:
+ h = sha1.New()
case crypto.SHA256:
h = sha256.New()
default:
-// Copyright 2010-2022 nanzoom.com. All Rights Reserved.
+// Copyright 2010-2025 nanzoom.com. All Rights Reserved.
package fsutil
// 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
}
}
// 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
}
}
// 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
}
}
// 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
}
}
-// Copyright 2010-2022 nanzoom.com. All Rights Reserved.
+// Copyright 2010-2025 nanzoom.com. All Rights Reserved.
package osutil
"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) {
-// Copyright 2010-2022 nanzoom.com. All Rights Reserved.
+// Copyright 2010-2025 nanzoom.com. All Rights Reserved.
package osutil
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)
)
// 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
}
// 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)
--- /dev/null
+// 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
+}
// 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)
// 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
}
}
// 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...))
}
// 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"`
// 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,
// 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
}
}
}
// 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 {
-// 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"