forked from go-goodies/go_utils
-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.go
311 lines (289 loc) · 8.07 KB
/
utils.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
package go_utils
import (
"crypto/rand"
"encoding/binary"
"errors"
. "fmt"
go_deepcopy "github.com/margnus1/go-deepcopy"
gouuid "github.com/nu7hatch/gouuid"
"net/http"
"reflect"
"strings"
"time"
)
const (
ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
UPPER_A_ORD = 65
UPPER_Z_ORD = 90
LOWER_A_ORD = 97
LOWER_Z_ORD = 122
NOT_FOUND_INDEX = -1
)
// Iif_string is and immediate if helper that takes a boolean expression
// and returns a string, true_val if the expression is true else false_val
func Iif_string(expr bool, true_val string, false_val string) string {
return map[bool]string{true: true_val, false: false_val}[expr]
}
// Concat joins the strings in a slice, delimiting them with a comma, but it
// allows you to pass the delimiter string to create a single string
// Ex: data: []string{"A", "B", "C"}; Join(data) ==> "A,B,C" ; Join(data, "|") ==> "A|B|C"
func Join(slice []string, args ...interface{}) string {
delimiter := ","
for _, arg := range args {
switch t := arg.(type) {
case string:
delimiter = t
default:
panic(Sprintf("ERROR - Invalid argument (%v). Must be a string.", arg))
}
}
ret := ""
for i, s := range slice {
// append delimiter except at the very end
ret += s + Iif_string((i < len(slice)-1), delimiter, "")
}
return ret
}
// Substr returns a portion (length characters) of string (s), beginning at a specified position (pos)
func Substr(s string, pos, length int) string {
runes := []rune(s)
l := pos + length
if l > len(runes) {
l = len(runes)
}
return string(runes[pos:l])
}
// PadRight pads a string (s) with with a specified string (optional parameter) for padLen characters
// If no string argument is passed, then s will be padded, to the right, with a single space character
func PadRight(s string, padLen int, args ...interface{}) string {
padStr := " "
for _, arg := range args {
switch t := arg.(type) {
case string:
padStr = t
default:
panic("Unknown argument")
}
}
return s + strings.Repeat(padStr, padLen-len(s))
}
// PadLeft pads a string (s) with with a specified string (optional parameter) for padLen characters
// If no string argument is passed, then s will be padded, to the left, with a single space character
func PadLeft(s string, padLen int, args ...interface{}) string {
padStr := " "
for _, arg := range args {
switch t := arg.(type) {
case string:
padStr = t
default:
panic("Unknown argument")
}
}
return strings.Repeat(padStr, padLen-len(s)) + s
}
// reflect doesn't consider 0 or "" to be zero, so we double check those here
// Can handle a struct field (only one level)
func IsEmpty(args ...interface{}) bool {
val := reflect.ValueOf(args[0])
valType := val.Kind()
switch valType {
case reflect.String:
return val.String() == ""
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return val.Int() == 0
case reflect.Float32, reflect.Float64:
return val.Float() == 0
case reflect.Interface, reflect.Slice, reflect.Ptr, reflect.Map, reflect.Chan, reflect.Func:
if val.IsNil() {
return true
} else if valType == reflect.Slice || valType == reflect.Map {
return val.Len() == 0
}
case reflect.Struct:
return IsEmptyStruct(args[0])
default:
return false
}
return false
}
// Assumes the argument is a struct
func IsEmptyStruct(args ...interface{}) bool {
val := reflect.ValueOf(args[0])
valType := val.Kind()
switch valType {
case reflect.Struct:
// verify that all of the struct's properties are empty
fieldCount := val.NumField()
for i := 0; i < fieldCount; i++ {
field := val.Field(i)
if field.IsValid() && !IsEmptyNonStruct(field) {
return false
}
}
return true
default:
return false
}
}
// Assumes the argument is not a struct
func IsEmptyNonStruct(args ...interface{}) bool {
val := reflect.ValueOf(args[0])
valType := val.Kind()
switch valType {
case reflect.String:
return val.String() == ""
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return val.Int() == 0
case reflect.Float32, reflect.Float64:
return val.Float() == 0
case reflect.Interface, reflect.Slice, reflect.Ptr, reflect.Map, reflect.Chan, reflect.Func:
if val.IsNil() {
return true
} else if valType == reflect.Slice || valType == reflect.Map {
return val.Len() == 0
}
default:
return false
}
return false
}
// Repeat a character (typically used for simple formatting of output)
func Dashes(repeatCount int, args ...interface{}) string {
dashChar := "-"
for _, arg := range args {
switch t := arg.(type) {
case string:
dashChar = t
default:
panic("Unknown argument")
}
}
return strings.Repeat(dashChar, repeatCount)
}
// A: 65
// B: 66
// . . .
func PrintAlphabet() {
var thisChar string
for _, c := range ALPHABET {
thisChar = Sprintf("%c", c)
Printf("%s: %d\n", thisChar, c)
}
}
// Find index of search string in target string, starting at startPos
// Ex: domain := email[IndexOf("@", email, 0)+1:]
func IndexOf(search string, target string, startPos int) int {
if startPos < 0 {
startPos = 0
}
if len(target) < startPos {
return NOT_FOUND_INDEX
}
if IsEmpty(target) || IsEmpty(search) {
return NOT_FOUND_INDEX
}
foundPos := strings.Index(target[startPos:len(target)], search)
if foundPos == -1 {
return NOT_FOUND_INDEX
}
return foundPos + startPos
}
// IndexOfGeneric returns the index of an element in any type of slice
// ints := []int{1, 2, 3}
// strings := []string{"A", "B", "C"}
// IndexOfGeneric(len(ints), func(i int) bool { return ints[i] == 2 })
// IndexOfGeneric(len(strings), func(i int) bool { return strings[i] == "B" })
func IndexOfGeneric(maxLen int, findExpr func(i int) bool) int {
for i := 0; i < maxLen; i++ {
if findExpr(i) {
return i
}
}
return -1
}
// Printf("isLower(\"a\"): %v\n", isLower("a")) // isLower("a"): true
// Printf("isLower(\"A\"): %v\n", isLower("A")) // isLower("A"): false
func IsLower(letter string) bool {
// var thisChar string
var ret bool
for _, c := range letter {
// thisChar = Sprintf("%c", c)
// Printf("%s: %d\n", thisChar, c)
ret = (c >= LOWER_A_ORD && c <= LOWER_Z_ORD)
}
return ret
}
// Copy makes a recursive deep copy of obj and returns the result.
// Wrap go_deepcopy.Copy (can later swap out implementation w/o breaking clients).
func DeepCopy(obj interface{}) (r interface{}) {
return go_deepcopy.Copy(obj)
}
func NewUuid() (uuid string, err error) {
uuidPtr, err := gouuid.NewV4()
if err != nil {
err = errors.New("Could not generate UUID")
} else {
uuid = uuidPtr.String()
}
return
}
// ToCurrency converts the value to a dollar and cents string
func ToCurrencyString(v interface{}) string {
return Sprintf("%.2f", v)
}
// ToTS converts the value to a timestamp string (accepts both time.Time and *time.Time as argument)
func ToTS(v interface{}) string {
var t time.Time
ret := ""
if TypeOf(v) == "time.Time" {
t = v.(time.Time)
ret = t.Format("2006-01-02 15:04 MST")
} else if TypeOf(v) == "*time.Time" {
ret = Sprintf("%s", time.Time(t).Format("2006-01-02 15:04 MST"))
}
return ret
}
// Prevent special CSV characters ("," and ";") from splitting a column
func CsvScrub(a interface{}) string {
s := Sprint(a)
commaPos := strings.Index(s, ",")
semicolonPos := strings.Index(s, ";")
if commaPos > -1 || semicolonPos > -1 {
// comma or semicolon found
s = Sprintf("\"%s\"", s) // surround with quotes per IETF RFC 2.6 guideline
}
return s
}
func Rand32() int32 {
var n int32
binary.Read(rand.Reader, binary.LittleEndian, &n)
return n
}
// QueryString takes an http request object and returns its query string
func QueryString(req *http.Request) string {
queryParamMap := req.URL.Query()
i := 0
queryParamString := ""
for key, value := range queryParamMap {
if len(value) > 1 {
for _, arrayValue := range value {
if i == 0 {
queryParamString += "?"
} else {
queryParamString += "&"
}
i += 1
queryParamString += key + "=" + arrayValue
}
} else {
if i == 0 {
queryParamString += "?"
} else {
queryParamString += "&"
}
i += 1
queryParamString += key + "=" + queryParamMap.Get(key)
}
}
return queryParamString
}