Files
opencloud/vendor/github.com/olekukonko/ll/concat.go
dependabot[bot] 70fc6eb40b build(deps): bump github.com/olekukonko/tablewriter from 0.0.5 to 1.0.6
Bumps [github.com/olekukonko/tablewriter](https://github.com/olekukonko/tablewriter) from 0.0.5 to 1.0.6.
- [Commits](https://github.com/olekukonko/tablewriter/compare/v0.0.5...v1.0.6)

---
updated-dependencies:
- dependency-name: github.com/olekukonko/tablewriter
  dependency-version: 1.0.6
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-19 15:04:23 +00:00

422 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package ll
import (
"fmt"
"github.com/olekukonko/ll/lx"
"reflect"
"strconv"
"strings"
"unsafe"
)
const (
maxRecursionDepth = 20 // Maximum depth for recursive type handling to prevent stack overflow
nilString = "<nil>" // String representation for nil values
unexportedString = "<?>" // String representation for unexported fields
)
// Concat efficiently concatenates values without a separator using the default logger.
// It converts each argument to a string and joins them directly, optimizing for performance
// in logging scenarios. Thread-safe as it does not modify shared state.
// Example:
//
// msg := ll.Concat("Hello", 42, true) // Returns "Hello42true"
func Concat(args ...any) string {
return concat(args...)
}
// ConcatSpaced concatenates values with a space separator using the default logger.
// It converts each argument to a string and joins them with spaces, suitable for log message
// formatting. Thread-safe as it does not modify shared state.
// Example:
//
// msg := ll.ConcatSpaced("Hello", 42, true) // Returns "Hello 42 true"
func ConcatSpaced(args ...any) string {
return concatSpaced(args...)
}
// ConcatAll concatenates elements with a separator, prefix, and suffix using the default logger.
// It combines before, main, and after arguments with the specified separator, optimizing memory
// allocation for logging. Thread-safe as it does not modify shared state.
// Example:
//
// msg := ll.ConcatAll(",", []any{"prefix"}, []any{"suffix"}, "main")
// // Returns "prefix,main,suffix"
func ConcatAll(sep string, before, after []any, args ...any) string {
return concatenate(sep, before, after, args...)
}
// concat efficiently concatenates values without a separator.
// It converts each argument to a string and joins them directly, optimizing for performance
// in logging scenarios. Used internally by Concat and other logging functions.
// Example:
//
// msg := concat("Hello", 42, true) // Returns "Hello42true"
func concat(args ...any) string {
return concatWith("", args...)
}
// concatSpaced concatenates values with a space separator.
// It converts each argument to a string and joins them with spaces, suitable for formatting
// log messages. Used internally by ConcatSpaced.
// Example:
//
// msg := concatSpaced("Hello", 42, true) // Returns "Hello 42 true"
func concatSpaced(args ...any) string {
return concatWith(lx.Space, args...)
}
// concatWith concatenates values with a specified separator using optimized type handling.
// It builds a string from arguments, handling various types efficiently (strings, numbers,
// structs, etc.), and is used by concat and concatSpaced for log message construction.
// Thread-safe as it does not modify shared state.
// Example:
//
// msg := concatWith(",", "Hello", 42, true) // Returns "Hello,42,true"
func concatWith(sep string, args ...any) string {
switch len(args) {
case 0:
return ""
case 1:
return concatToString(args[0])
}
var b strings.Builder
b.Grow(concatEstimateArgs(sep, args))
for i, arg := range args {
if i > 0 {
b.WriteString(sep)
}
concatWriteValue(&b, arg, 0)
}
return b.String()
}
// concatenate concatenates elements with separators, prefixes, and suffixes efficiently.
// It combines before, main, and after arguments with the specified separator, optimizing
// memory allocation for complex log message formatting. Used internally by ConcatAll.
// Example:
//
// msg := concatenate(",", []any{"prefix"}, []any{"suffix"}, "main")
// // Returns "prefix,main,suffix"
func concatenate(sep string, before []any, after []any, args ...any) string {
totalLen := len(before) + len(after) + len(args)
switch totalLen {
case 0:
return ""
case 1:
switch {
case len(before) > 0:
return concatToString(before[0])
case len(args) > 0:
return concatToString(args[0])
default:
return concatToString(after[0])
}
}
var b strings.Builder
b.Grow(concatEstimateTotal(sep, before, after, args))
// Write before elements
concatWriteGroup(&b, sep, before)
// Write main arguments
if len(before) > 0 && len(args) > 0 {
b.WriteString(sep)
}
concatWriteGroup(&b, sep, args)
// Write after elements
if len(after) > 0 && (len(before) > 0 || len(args) > 0) {
b.WriteString(sep)
}
concatWriteGroup(&b, sep, after)
return b.String()
}
// concatWriteGroup writes a group of arguments to a strings.Builder with a separator.
// It handles each argument by converting it to a string, used internally by concatenate
// to process before, main, or after groups in log message construction.
// Example:
//
// var b strings.Builder
// concatWriteGroup(&b, ",", []any{"a", 42}) // Writes "a,42" to b
func concatWriteGroup(b *strings.Builder, sep string, group []any) {
for i, arg := range group {
if i > 0 {
b.WriteString(sep)
}
concatWriteValue(b, arg, 0)
}
}
// concatToString converts a single argument to a string efficiently.
// It handles common types (string, []byte, fmt.Stringer) with minimal overhead and falls
// back to fmt.Sprint for other types. Used internally by concat and concatenate.
// Example:
//
// s := concatToString("Hello") // Returns "Hello"
// s := concatToString([]byte{65, 66}) // Returns "AB"
func concatToString(arg any) string {
switch v := arg.(type) {
case string:
return v
case []byte:
return *(*string)(unsafe.Pointer(&v))
case fmt.Stringer:
return v.String()
case error:
return v.Error()
default:
return fmt.Sprint(v)
}
}
// concatEstimateTotal estimates the total string length for concatenate.
// It calculates the expected size of the concatenated string, including before, main, and
// after arguments with separators, to preallocate the strings.Builder capacity.
// Example:
//
// size := concatEstimateTotal(",", []any{"prefix"}, []any{"suffix"}, "main")
// // Returns estimated length for "prefix,main,suffix"
func concatEstimateTotal(sep string, before, after, args []any) int {
size := 0
if len(before) > 0 {
size += concatEstimateArgs(sep, before)
}
if len(args) > 0 {
if size > 0 {
size += len(sep)
}
size += concatEstimateArgs(sep, args)
}
if len(after) > 0 {
if size > 0 {
size += len(sep)
}
size += concatEstimateArgs(sep, after)
}
return size
}
// concatEstimateArgs estimates the string length for a group of arguments.
// It sums the estimated sizes of each argument plus separators, used by concatEstimateTotal
// and concatWith to optimize memory allocation for log message construction.
// Example:
//
// size := concatEstimateArgs(",", []any{"hello", 42}) // Returns estimated length for "hello,42"
func concatEstimateArgs(sep string, args []any) int {
if len(args) == 0 {
return 0
}
size := len(sep) * (len(args) - 1)
for _, arg := range args {
size += concatEstimateSize(arg)
}
return size
}
// concatEstimateSize estimates the string length for a single argument.
// It provides size estimates for various types (strings, numbers, booleans, etc.) to
// optimize strings.Builder capacity allocation in logging functions.
// Example:
//
// size := concatEstimateSize("hello") // Returns 5
// size := concatEstimateSize(42) // Returns ~2
func concatEstimateSize(arg any) int {
switch v := arg.(type) {
case string:
return len(v)
case []byte:
return len(v)
case int:
return concatNumLen(int64(v))
case int64:
return concatNumLen(v)
case int32:
return concatNumLen(int64(v))
case int16:
return concatNumLen(int64(v))
case int8:
return concatNumLen(int64(v))
case uint:
return concatNumLen(uint64(v))
case uint64:
return concatNumLen(v)
case uint32:
return concatNumLen(uint64(v))
case uint16:
return concatNumLen(uint64(v))
case uint8:
return concatNumLen(uint64(v))
case float64:
return 24 // Max digits for float64
case float32:
return 16 // Max digits for float32
case bool:
if v {
return 4 // "true"
}
return 5 // "false"
case fmt.Stringer:
return 16 // Conservative estimate
default:
return 16 // Default estimate
}
}
// concatNumLen estimates the string length for a signed or unsigned integer.
// It returns a conservative estimate (20 digits) for int64 or uint64 values, including
// a sign for negative numbers, used by concatEstimateSize for memory allocation.
// Example:
//
// size := concatNumLen(int64(-123)) // Returns 20
// size := concatNumLen(uint64(123)) // Returns 20
func concatNumLen[T int64 | uint64](v T) int {
if v < 0 {
return 20 // Max digits for int64 + sign
}
return 20 // Max digits for uint64
}
// concatWriteValue writes a formatted value to a strings.Builder with recursion depth tracking.
// It handles various types (strings, numbers, structs, slices, etc.) and prevents infinite
// recursion by limiting depth. Used internally by concatWith and concatWriteGroup for log
// message formatting.
// Example:
//
// var b strings.Builder
// concatWriteValue(&b, "hello", 0) // Writes "hello" to b
// concatWriteValue(&b, []int{1, 2}, 0) // Writes "[1,2]" to b
func concatWriteValue(b *strings.Builder, arg any, depth int) {
if depth > maxRecursionDepth {
b.WriteString("...")
return
}
if arg == nil {
b.WriteString(nilString)
return
}
if s, ok := arg.(fmt.Stringer); ok {
b.WriteString(s.String())
return
}
switch v := arg.(type) {
case string:
b.WriteString(v)
case []byte:
b.Write(v)
case int:
b.WriteString(strconv.FormatInt(int64(v), 10))
case int64:
b.WriteString(strconv.FormatInt(v, 10))
case int32:
b.WriteString(strconv.FormatInt(int64(v), 10))
case int16:
b.WriteString(strconv.FormatInt(int64(v), 10))
case int8:
b.WriteString(strconv.FormatInt(int64(v), 10))
case uint:
b.WriteString(strconv.FormatUint(uint64(v), 10))
case uint64:
b.WriteString(strconv.FormatUint(v, 10))
case uint32:
b.WriteString(strconv.FormatUint(uint64(v), 10))
case uint16:
b.WriteString(strconv.FormatUint(uint64(v), 10))
case uint8:
b.WriteString(strconv.FormatUint(uint64(v), 10))
case float64:
b.WriteString(strconv.FormatFloat(v, 'f', -1, 64))
case float32:
b.WriteString(strconv.FormatFloat(float64(v), 'f', -1, 32))
case bool:
if v {
b.WriteString("true")
} else {
b.WriteString("false")
}
default:
val := reflect.ValueOf(arg)
if val.Kind() == reflect.Ptr {
if val.IsNil() {
b.WriteString(nilString)
return
}
val = val.Elem()
}
switch val.Kind() {
case reflect.Slice, reflect.Array:
concatFormatSlice(b, val, depth)
case reflect.Struct:
concatFormatStruct(b, val, depth)
default:
fmt.Fprint(b, v)
}
}
}
// concatFormatSlice formats a slice or array for logging.
// It writes the elements in a bracketed, comma-separated format, handling nested types
// recursively with depth tracking. Used internally by concatWriteValue for log message formatting.
// Example:
//
// var b strings.Builder
// val := reflect.ValueOf([]int{1, 2})
// concatFormatSlice(&b, val, 0) // Writes "[1,2]" to b
func concatFormatSlice(b *strings.Builder, val reflect.Value, depth int) {
b.WriteByte('[')
for i := 0; i < val.Len(); i++ {
if i > 0 {
b.WriteByte(',')
}
concatWriteValue(b, val.Index(i).Interface(), depth+1)
}
b.WriteByte(']')
}
// concatFormatStruct formats a struct for logging.
// It writes the structs exported fields in a bracketed, name:value format, handling nested
// types recursively with depth tracking. Unexported fields are represented as "<?>".
// Used internally by concatWriteValue for log message formatting.
// Example:
//
// var b strings.Builder
// val := reflect.ValueOf(struct{ Name string }{Name: "test"})
// concatFormatStruct(&b, val, 0) // Writes "[Name:test]" to b
func concatFormatStruct(b *strings.Builder, val reflect.Value, depth int) {
typ := val.Type()
b.WriteByte('[')
first := true
for i := 0; i < val.NumField(); i++ {
field := typ.Field(i)
fieldValue := val.Field(i)
if !first {
b.WriteString("; ")
}
first = false
b.WriteString(field.Name)
b.WriteByte(':')
if !fieldValue.CanInterface() {
b.WriteString(unexportedString)
continue
}
concatWriteValue(b, fieldValue.Interface(), depth+1)
}
b.WriteByte(']')
}