chore: import upstream snapshot with attribution
CI / license-header (push) Has been skipped
CI / e2e-dry-run (push) Has been skipped
CI / fast-gate (push) Failing after 0s
Test PR Label Logic / test-pr-labels (push) Failing after 1s
Skill Format Check / check-format (push) Failing after 2s
CI / security (push) Failing after 5s
CI / unit-test (push) Has been skipped
CI / lint (push) Has been skipped
CI / script-test (push) Has been skipped
CI / deterministic-gate (push) Has been skipped
CI / coverage (push) Has been skipped
CI / results (push) Has been cancelled
CI / deadcode (push) Has been cancelled
CI / e2e-live (push) Has been cancelled
CI / license-header (push) Has been skipped
CI / e2e-dry-run (push) Has been skipped
CI / fast-gate (push) Failing after 0s
Test PR Label Logic / test-pr-labels (push) Failing after 1s
Skill Format Check / check-format (push) Failing after 2s
CI / security (push) Failing after 5s
CI / unit-test (push) Has been skipped
CI / lint (push) Has been skipped
CI / script-test (push) Has been skipped
CI / deterministic-gate (push) Has been skipped
CI / coverage (push) Has been skipped
CI / results (push) Has been cancelled
CI / deadcode (push) Has been cancelled
CI / e2e-live (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// ToFloat64 extracts a float64 from a value that may be float64 or json.Number.
|
||||
// Returns (0, false) if the value is neither.
|
||||
func ToFloat64(v interface{}) (float64, bool) {
|
||||
switch n := v.(type) {
|
||||
case float64:
|
||||
return n, true
|
||||
case json.Number:
|
||||
f, err := n.Float64()
|
||||
return f, err == nil
|
||||
case int:
|
||||
return float64(n), true
|
||||
case int64:
|
||||
return float64(n), true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package util
|
||||
|
||||
import "reflect"
|
||||
|
||||
// IsNil reports whether v is nil, covering both untyped nil (interface itself)
|
||||
// and typed nil (e.g. (*T)(nil) wrapped in interface{}).
|
||||
// Avoids direct interface{} == nil comparison .
|
||||
func IsNil(v interface{}) bool {
|
||||
rv := reflect.ValueOf(v)
|
||||
if !rv.IsValid() {
|
||||
return true
|
||||
}
|
||||
switch rv.Kind() {
|
||||
case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Func, reflect.Interface, reflect.Chan:
|
||||
return rv.IsNil()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// IsEmptyValue checks whether v is considered empty using reflect.
|
||||
// Returns true for nil interface, and zero values of the underlying type
|
||||
// (e.g. "", 0, false, empty slice/map).
|
||||
func IsEmptyValue(v interface{}) bool {
|
||||
rv := reflect.ValueOf(v)
|
||||
if !rv.IsValid() {
|
||||
return true
|
||||
}
|
||||
return rv.IsZero()
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package util
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestIsNil(t *testing.T) {
|
||||
var nilPtr *int
|
||||
var nilSlice []int
|
||||
var nilMap map[string]int
|
||||
var nilChan chan int
|
||||
var nilFunc func()
|
||||
nonNilPtr := new(int)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
v interface{}
|
||||
want bool
|
||||
}{
|
||||
{"nil", nil, true},
|
||||
{"empty string", "", false},
|
||||
{"zero int", 0, false},
|
||||
{"false", false, false},
|
||||
{"non-nil map", map[string]interface{}{}, false},
|
||||
{"non-nil slice", []interface{}{}, false},
|
||||
{"string value", "hello", false},
|
||||
{"typed-nil pointer", nilPtr, true},
|
||||
{"typed-nil slice", nilSlice, true},
|
||||
{"typed-nil map", nilMap, true},
|
||||
{"typed-nil chan", nilChan, true},
|
||||
{"typed-nil func", nilFunc, true},
|
||||
{"non-nil pointer", nonNilPtr, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := IsNil(tt.v); got != tt.want {
|
||||
t.Errorf("IsNil(%v) = %v, want %v", tt.v, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsEmptyValue(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
v interface{}
|
||||
want bool
|
||||
}{
|
||||
{"nil", nil, true},
|
||||
{"empty string", "", true},
|
||||
{"non-empty string", "hello", false},
|
||||
{"zero int", 0, true},
|
||||
{"non-zero int", 42, false},
|
||||
{"zero float64", float64(0), true},
|
||||
{"non-zero float64", float64(3.14), false},
|
||||
{"false", false, true},
|
||||
{"true", true, false},
|
||||
{"nil slice", []interface{}(nil), true},
|
||||
{"empty slice", []interface{}{}, false},
|
||||
{"non-empty slice", []interface{}{1}, false},
|
||||
{"nil map", map[string]interface{}(nil), true},
|
||||
{"empty map", map[string]interface{}{}, false},
|
||||
{"non-empty map", map[string]interface{}{"a": 1}, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := IsEmptyValue(tt.v); got != tt.want {
|
||||
t.Errorf("IsEmptyValue(%v) = %v, want %v", tt.v, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package util
|
||||
|
||||
// TruncateStr truncates s to at most n runes, safe for multi-byte (e.g. CJK) characters.
|
||||
func TruncateStr(s string, n int) string {
|
||||
if n <= 0 {
|
||||
return ""
|
||||
}
|
||||
r := []rune(s)
|
||||
if len(r) <= n {
|
||||
return s
|
||||
}
|
||||
return string(r[:n])
|
||||
}
|
||||
|
||||
// TruncateStrWithEllipsis truncates s to at most n runes (including "..." suffix).
|
||||
func TruncateStrWithEllipsis(s string, n int) string {
|
||||
if n <= 0 {
|
||||
return ""
|
||||
}
|
||||
r := []rune(s)
|
||||
if len(r) <= n {
|
||||
return s
|
||||
}
|
||||
if n < 3 {
|
||||
return string(r[:n])
|
||||
}
|
||||
return string(r[:n-3]) + "..."
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package util
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestTruncateStr(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
s string
|
||||
n int
|
||||
want string
|
||||
}{
|
||||
{"short string", "hello", 10, "hello"},
|
||||
{"exact length", "hello", 5, "hello"},
|
||||
{"truncate", "hello world", 5, "hello"},
|
||||
{"empty", "", 5, ""},
|
||||
{"zero limit", "hello", 0, ""},
|
||||
{"negative limit", "hello", -1, ""},
|
||||
{"CJK characters", "你好世界测试", 4, "你好世界"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := TruncateStr(tt.s, tt.n); got != tt.want {
|
||||
t.Errorf("TruncateStr(%q, %d) = %q, want %q", tt.s, tt.n, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTruncateStrWithEllipsis(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
s string
|
||||
n int
|
||||
want string
|
||||
}{
|
||||
{"short string", "hello", 10, "hello"},
|
||||
{"exact length", "hello", 5, "hello"},
|
||||
{"truncate with ellipsis", "hello world", 8, "hello..."},
|
||||
{"limit less than 3", "hello", 2, "he"},
|
||||
{"limit equals 3", "hello world", 3, "..."},
|
||||
{"empty", "", 5, ""},
|
||||
{"zero limit", "hello", 0, ""},
|
||||
{"negative limit", "hello", -1, ""},
|
||||
{"CJK with ellipsis", "你好世界测试", 5, "你好..."},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := TruncateStrWithEllipsis(tt.s, tt.n); got != tt.want {
|
||||
t.Errorf("TruncateStrWithEllipsis(%q, %d) = %q, want %q", tt.s, tt.n, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user