1package rediscmd 2 3import ( 4 "encoding/hex" 5 "fmt" 6 "strconv" 7 "strings" 8 "time" 9 10 "github.com/go-redis/redis/v8" 11) 12 13func CmdString(cmd redis.Cmder) string { 14 b := make([]byte, 0, 32) 15 b = AppendCmd(b, cmd) 16 return String(b) 17} 18 19func CmdsString(cmds []redis.Cmder) (string, string) { 20 const numCmdLimit = 100 21 const numNameLimit = 10 22 23 seen := make(map[string]struct{}, numNameLimit) 24 unqNames := make([]string, 0, numNameLimit) 25 26 b := make([]byte, 0, 32*len(cmds)) 27 28 for i, cmd := range cmds { 29 if i > numCmdLimit { 30 break 31 } 32 33 if i > 0 { 34 b = append(b, '\n') 35 } 36 b = AppendCmd(b, cmd) 37 38 if len(unqNames) >= numNameLimit { 39 continue 40 } 41 42 name := cmd.FullName() 43 if _, ok := seen[name]; !ok { 44 seen[name] = struct{}{} 45 unqNames = append(unqNames, name) 46 } 47 } 48 49 summary := strings.Join(unqNames, " ") 50 return summary, String(b) 51} 52 53func AppendCmd(b []byte, cmd redis.Cmder) []byte { 54 const numArgLimit = 32 55 56 for i, arg := range cmd.Args() { 57 if i > numArgLimit { 58 break 59 } 60 if i > 0 { 61 b = append(b, ' ') 62 } 63 b = appendArg(b, arg) 64 } 65 66 if err := cmd.Err(); err != nil { 67 b = append(b, ": "...) 68 b = append(b, err.Error()...) 69 } 70 71 return b 72} 73 74func appendArg(b []byte, v interface{}) []byte { 75 const argLenLimit = 64 76 77 switch v := v.(type) { 78 case nil: 79 return append(b, "<nil>"...) 80 case string: 81 if len(v) > argLenLimit { 82 v = v[:argLenLimit] 83 } 84 return appendUTF8String(b, Bytes(v)) 85 case []byte: 86 if len(v) > argLenLimit { 87 v = v[:argLenLimit] 88 } 89 return appendUTF8String(b, v) 90 case int: 91 return strconv.AppendInt(b, int64(v), 10) 92 case int8: 93 return strconv.AppendInt(b, int64(v), 10) 94 case int16: 95 return strconv.AppendInt(b, int64(v), 10) 96 case int32: 97 return strconv.AppendInt(b, int64(v), 10) 98 case int64: 99 return strconv.AppendInt(b, v, 10) 100 case uint: 101 return strconv.AppendUint(b, uint64(v), 10) 102 case uint8: 103 return strconv.AppendUint(b, uint64(v), 10) 104 case uint16: 105 return strconv.AppendUint(b, uint64(v), 10) 106 case uint32: 107 return strconv.AppendUint(b, uint64(v), 10) 108 case uint64: 109 return strconv.AppendUint(b, v, 10) 110 case float32: 111 return strconv.AppendFloat(b, float64(v), 'f', -1, 64) 112 case float64: 113 return strconv.AppendFloat(b, v, 'f', -1, 64) 114 case bool: 115 if v { 116 return append(b, "true"...) 117 } 118 return append(b, "false"...) 119 case time.Time: 120 return v.AppendFormat(b, time.RFC3339Nano) 121 default: 122 return append(b, fmt.Sprint(v)...) 123 } 124} 125 126func appendUTF8String(dst []byte, src []byte) []byte { 127 if isSimple(src) { 128 dst = append(dst, src...) 129 return dst 130 } 131 132 s := len(dst) 133 dst = append(dst, make([]byte, hex.EncodedLen(len(src)))...) 134 hex.Encode(dst[s:], src) 135 return dst 136} 137 138func isSimple(b []byte) bool { 139 for _, c := range b { 140 if !isSimpleByte(c) { 141 return false 142 } 143 } 144 return true 145} 146 147func isSimpleByte(c byte) bool { 148 return c >= 0x21 && c <= 0x7e 149} 150