github.com/jxskiss/gopkg/v2@v2.14.9-0.20240514120614-899f3e7952b4/easy/utils.go (about)

     1  package easy
     2  
     3  import (
     4  	"reflect"
     5  	"runtime"
     6  	"strings"
     7  
     8  	"github.com/jxskiss/gopkg/v2/unsafe/reflectx"
     9  )
    10  
    11  // SetDefault checks whether dst points to a zero value, if yes, it sets
    12  // the first non-zero value to dst.
    13  // dst must be a pointer to same type as value, else it panics.
    14  func SetDefault(dst any, value ...any) {
    15  	dstVal := reflect.ValueOf(dst)
    16  	if dstVal.Kind() != reflect.Ptr || !reflect.Indirect(dstVal).IsValid() {
    17  		panic("SetDefault: dst must be a non-nil pointer")
    18  	}
    19  	if reflect.Indirect(dstVal).IsZero() {
    20  		kind := dstVal.Elem().Kind()
    21  		for _, x := range value {
    22  			xval := reflect.ValueOf(x)
    23  			if !xval.IsZero() {
    24  				switch kind {
    25  				case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    26  					dstVal.Elem().SetInt(reflectx.ReflectInt(xval))
    27  				case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
    28  					dstVal.Elem().SetUint(uint64(reflectx.ReflectInt(xval)))
    29  				case reflect.Float32, reflect.Float64:
    30  					dstVal.Elem().SetFloat(xval.Float())
    31  				default:
    32  					dstVal.Elem().Set(xval)
    33  				}
    34  				break
    35  			}
    36  		}
    37  	}
    38  }
    39  
    40  // Caller returns function name, filename, and the line number of the caller.
    41  // The argument skip is the number of stack frames to ascend, with 0
    42  // identifying the caller of Caller.
    43  func Caller(skip int) (name, file string, line int) {
    44  	pc, file, line, _ := runtime.Caller(skip + 1)
    45  	name = runtime.FuncForPC(pc).Name()
    46  	for i := len(name) - 1; i >= 0; i-- {
    47  		if name[i] == '/' {
    48  			name = name[i+1:]
    49  			break
    50  		}
    51  	}
    52  	pathSepCnt := 0
    53  	for i := len(file) - 1; i >= 0; i-- {
    54  		if file[i] == '/' {
    55  			pathSepCnt++
    56  			if pathSepCnt == 2 {
    57  				file = file[i+1:]
    58  				break
    59  			}
    60  		}
    61  	}
    62  	return
    63  }
    64  
    65  // CallerName returns the function name of the direct caller.
    66  // This is a convenient wrapper around Caller.
    67  func CallerName() string {
    68  	name, _, _ := Caller(1)
    69  	return name
    70  }
    71  
    72  // SingleJoin joins the given text segments using sep.
    73  // No matter whether a segment begins or ends with sep or not, it
    74  // guarantees that only one sep appears between two segments.
    75  func SingleJoin(sep string, text ...string) string {
    76  	if len(text) == 0 {
    77  		return ""
    78  	}
    79  	result := text[0]
    80  	for _, next := range text[1:] {
    81  		asep := strings.HasSuffix(result, sep)
    82  		bsep := strings.HasPrefix(next, sep)
    83  		switch {
    84  		case asep && bsep:
    85  			result += next[len(sep):]
    86  		case !asep && !bsep:
    87  			result += sep + next
    88  		default:
    89  			result += next
    90  		}
    91  	}
    92  	return result
    93  }
    94  
    95  // SlashJoin joins the given path segments using "/".
    96  // No matter whether a segment begins or ends with "/" or not, it guarantees
    97  // that only one "/" appears between two segments.
    98  func SlashJoin(path ...string) string {
    99  	return SingleJoin("/", path...)
   100  }