github.com/Aoi-hosizora/ahlib@v1.5.1-0.20230404072829-241b93cf91c7/xstring/xstring_mass.go (about)

     1  package xstring
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sort"
     7  	"strconv"
     8  	"strings"
     9  )
    10  
    11  // ==============
    12  // mass functions
    13  // ==============
    14  
    15  // Bool returns given t if given value is true, otherwise returns given f.
    16  func Bool(b bool, t, f string) string {
    17  	if b {
    18  		return t
    19  	}
    20  	return f
    21  }
    22  
    23  // MaskToken masks a token string and returns the result, using given mask rune and indices for mask characters, this function also supports minus index.
    24  func MaskToken(s string, mask rune, indices ...int) string {
    25  	return coreMaskToken(s, string(mask), true, indices...)
    26  }
    27  
    28  // MaskTokenR masks a token string and returns the result, using given mask rune and indices for non-mask characters, this function also supports minus index,
    29  func MaskTokenR(s string, mask rune, indices ...int) string {
    30  	return coreMaskToken(s, string(mask), false, indices...)
    31  }
    32  
    33  // StringMaskToken masks a token string and returns the result, using given mask string and indices for mask characters, this function also supports minus index.
    34  func StringMaskToken(s string, mask string, indices ...int) string {
    35  	return coreMaskToken(s, mask, true, indices...)
    36  }
    37  
    38  // StringMaskTokenR masks a token string and returns the result, using given mask string and indices for non-mask characters, this function also supports minus index,
    39  func StringMaskTokenR(s string, mask string, indices ...int) string {
    40  	return coreMaskToken(s, mask, false, indices...)
    41  }
    42  
    43  // coreMaskToken is the core implementation of MaskToken and MaskTokenR.
    44  func coreMaskToken(s string, mask string, toMask bool, indices ...int) string {
    45  	switch {
    46  	case len(s) == 0: // empty
    47  		return ""
    48  	case len(indices) == 0: // no change or full change
    49  		if toMask {
    50  			return s
    51  		}
    52  		return strings.Repeat(mask, len(s))
    53  	}
    54  
    55  	length := 0
    56  	for range s {
    57  		length++
    58  	}
    59  	newIndices := make(map[int]struct{}) // idx map
    60  	idxs := make([]int, 0, len(indices))
    61  	for _, i := range indices {
    62  		if 0 <= i && i < length {
    63  			idxs = append(idxs, i)
    64  		} else if -length <= i && i < 0 {
    65  			idxs = append(idxs, length+i)
    66  		}
    67  	}
    68  	sort.Ints(idxs)
    69  	for _, index := range idxs {
    70  		newIndices[index] = struct{}{}
    71  	}
    72  
    73  	sb := strings.Builder{}
    74  	sb.Grow(len(s))
    75  	// use index to write mask or character
    76  	for i, ch := range s {
    77  		_, contains := newIndices[i]
    78  		if (toMask && contains) || (!toMask && !contains) {
    79  			sb.WriteString(mask)
    80  		} else {
    81  			sb.WriteRune(ch)
    82  		}
    83  	}
    84  	return sb.String()
    85  }
    86  
    87  // EncodeUrlValues encodes the values (see url.Values) into url encoded form ("bar=baz&foo=quux") sorted by key with escape.
    88  // The escapeFunc can be url.QueryEscape, url.PathEscape or the functions you defined, use nil for no escape.
    89  func EncodeUrlValues(values map[string][]string, escapeFunc func(string) string) string {
    90  	keys := make([]string, 0, len(values))
    91  	for k := range values {
    92  		keys = append(keys, k)
    93  	}
    94  	sort.Strings(keys)
    95  
    96  	sb := strings.Builder{}
    97  	sb.Grow(len(values) * 4)
    98  	for _, k := range keys {
    99  		key := k
   100  		if escapeFunc != nil {
   101  			key = escapeFunc(key)
   102  		}
   103  		for _, v := range values[k] {
   104  			val := v
   105  			if escapeFunc != nil {
   106  				val = escapeFunc(val)
   107  			}
   108  			if sb.Len() > 0 {
   109  				sb.WriteString("&")
   110  			}
   111  			sb.WriteString(key) // escaped
   112  			sb.WriteString("=")
   113  			sb.WriteString(val) // escaped
   114  		}
   115  	}
   116  
   117  	return sb.String()
   118  }
   119  
   120  const (
   121  	panicIndexOutOfRange = "xstring: index out of range"
   122  )
   123  
   124  // SplitAndGet returns the string item from the split result slices, this also supports minus index.
   125  func SplitAndGet(s string, sep string, index int) string {
   126  	sp := strings.Split(s, sep)
   127  	l := len(sp)
   128  
   129  	if index >= 0 && index < l {
   130  		return sp[index]
   131  	}
   132  	if newIndex := l + index; newIndex >= 0 && newIndex < l {
   133  		return sp[newIndex]
   134  	}
   135  
   136  	panic(panicIndexOutOfRange)
   137  }
   138  
   139  // StringSliceToMap returns a string-string map from given string slice. Note that extra argument will be skipped.
   140  func StringSliceToMap(args []string) map[string]string {
   141  	l := len(args)
   142  	out := make(map[string]string, l/2)
   143  	for i := 0; i < l; i += 2 {
   144  		if i+1 >= l {
   145  			break // ignore the extra arg
   146  		}
   147  		key, value := args[i], args[i+1]
   148  		out[key] = value
   149  	}
   150  	return out
   151  }
   152  
   153  // SliceToStringMap returns a string-interface{} map from given interface{} slice. Note that nil arguments and extra argument will be skipped.
   154  func SliceToStringMap(args []interface{}) map[string]interface{} {
   155  	l := len(args)
   156  	out := make(map[string]interface{}, l/2)
   157  
   158  	for i := 0; i < l; i += 2 {
   159  		if i+1 >= l {
   160  			break // ignore the extra arg
   161  		}
   162  		key := ""
   163  		keyItf, value := args[i], args[i+1]
   164  		if keyItf == nil {
   165  			i--
   166  			continue
   167  		}
   168  		if s, ok := keyItf.(string); ok {
   169  			key = s
   170  		} else if bs, ok := keyItf.([]byte); ok {
   171  			key = FastBtos(bs)
   172  		} else {
   173  			key = fmt.Sprintf("%v", keyItf) // %v
   174  		}
   175  		out[key] = value
   176  	}
   177  
   178  	return out
   179  }
   180  
   181  var (
   182  	errUnsupportedVersionString = errors.New("xstring: unsupported version string")
   183  )
   184  
   185  // SemanticVersion parses given semantic version string to (major, minor, patch) version number. Note that this function supports
   186  // "1", "1.1", "1.1.1", "v1", "v1.1", "v1.1.1" format.
   187  func SemanticVersion(semver string) (uint64, uint64, uint64, error) {
   188  	if len(semver) == 0 || (len(semver) == 1 && !IsArabicNumber(rune(semver[0]))) {
   189  		return 0, 0, 0, errUnsupportedVersionString
   190  	}
   191  	if semver[0] == 'v' {
   192  		semver = semver[1:]
   193  	}
   194  	first := strings.IndexByte(semver, '.')
   195  	last := strings.LastIndexByte(semver, '.')
   196  	if first == -1 {
   197  		// 1 / v1
   198  		p1, err1 := strconv.ParseUint(semver, 10, 64)
   199  		if err1 != nil {
   200  			return 0, 0, 0, errUnsupportedVersionString
   201  		}
   202  		return p1, 0, 0, nil
   203  	}
   204  	if first == last {
   205  		// 1.1 / v1.1
   206  		p1, err1 := strconv.ParseUint(semver[:first], 10, 64)
   207  		p2, err2 := strconv.ParseUint(semver[first+1:], 10, 64)
   208  		if err1 != nil || err2 != nil {
   209  			return 0, 0, 0, errUnsupportedVersionString
   210  		}
   211  		return p1, p2, 0, nil
   212  	}
   213  	// 1.1.1 / v1.1.1
   214  	p1, err1 := strconv.ParseUint(semver[:first], 10, 64)
   215  	p2, err2 := strconv.ParseUint(semver[first+1:last], 10, 64)
   216  	p3, err3 := strconv.ParseUint(semver[last+1:], 10, 64)
   217  	if err1 != nil || err2 != nil || err3 != nil {
   218  		return 0, 0, 0, errUnsupportedVersionString
   219  	}
   220  	return p1, p2, p3, nil
   221  }