github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/gtf/gtf.go (about) 1 package gtf 2 3 import ( 4 "fmt" 5 htmlTemplate "html/template" 6 "log" 7 "math" 8 "math/rand" 9 "net/url" 10 "reflect" 11 "regexp" 12 "strings" 13 textTemplate "text/template" 14 "time" 15 ) 16 17 var striptagsRegexp = regexp.MustCompile("<[^>]*?>") 18 19 // FindInSlice finds an element in the slice. 20 func FindInSlice(slice interface{}, f func(value interface{}) bool) int { 21 var s reflect.Value 22 23 if rv, ok := slice.(reflect.Value); ok { 24 s = rv 25 } else { 26 s = reflect.ValueOf(slice) 27 } 28 29 if s.Kind() != reflect.Slice && s.Kind() != reflect.Array { 30 return -1 31 } 32 33 for i := 0; i < s.Len(); i++ { 34 if f(s.Index(i).Interface()) { 35 return i 36 } 37 } 38 39 return -1 40 } 41 42 var TextFuncMap = WrapRecover(textTemplate.FuncMap{ 43 "safeEq": SafeEq, 44 "contains": Contains, 45 "replace": func(s1, s2 string) string { 46 return strings.Replace(s2, s1, "", -1) 47 }, 48 "findreplace": func(s1, s2, s3 string) string { 49 return strings.Replace(s3, s1, s2, -1) 50 }, 51 "title": func(s string) string { 52 return strings.Title(s) 53 }, 54 "default": func(arg, value interface{}) interface{} { 55 v := reflect.ValueOf(value) 56 switch v.Kind() { 57 case reflect.String, reflect.Slice, reflect.Array, reflect.Map: 58 if v.Len() == 0 { 59 return arg 60 } 61 case reflect.Bool: 62 if !v.Bool() { 63 return arg 64 } 65 default: 66 return value 67 } 68 69 return value 70 }, 71 "length": func(value interface{}) int { 72 v := reflect.ValueOf(value) 73 switch v.Kind() { 74 case reflect.Slice, reflect.Array, reflect.Map: 75 return v.Len() 76 case reflect.String: 77 return len([]rune(v.String())) 78 } 79 80 return 0 81 }, 82 "lower": func(s string) string { 83 return strings.ToLower(s) 84 }, 85 "upper": func(s string) string { 86 return strings.ToUpper(s) 87 }, 88 "truncatechars": func(n int, s string) string { 89 if n < 0 { 90 return s 91 } 92 93 r := []rune(s) 94 rLength := len(r) 95 96 if n >= rLength { 97 return s 98 } 99 100 if n > 3 && rLength > 3 { 101 return string(r[:n-3]) + "..." 102 } 103 104 return string(r[:n]) 105 }, 106 "urlencode": func(s string) string { 107 return url.QueryEscape(s) 108 }, 109 "wordcount": func(s string) int { 110 return len(strings.Fields(s)) 111 }, 112 "divisibleby": func(arg interface{}, value interface{}) bool { 113 var v float64 114 switch value.(type) { 115 case int, int8, int16, int32, int64: 116 v = float64(reflect.ValueOf(value).Int()) 117 case uint, uint8, uint16, uint32, uint64: 118 v = float64(reflect.ValueOf(value).Uint()) 119 case float32, float64: 120 v = reflect.ValueOf(value).Float() 121 default: 122 return false 123 } 124 125 var a float64 126 switch arg.(type) { 127 case int, int8, int16, int32, int64: 128 a = float64(reflect.ValueOf(arg).Int()) 129 case uint, uint8, uint16, uint32, uint64: 130 a = float64(reflect.ValueOf(arg).Uint()) 131 case float32, float64: 132 a = reflect.ValueOf(arg).Float() 133 default: 134 return false 135 } 136 137 return math.Mod(v, a) == 0 138 }, 139 "lengthis": func(arg int, value interface{}) bool { 140 v := reflect.ValueOf(value) 141 switch v.Kind() { 142 case reflect.Slice, reflect.Array, reflect.Map: 143 return v.Len() == arg 144 case reflect.String: 145 return len([]rune(v.String())) == arg 146 } 147 148 return false 149 }, 150 "trim": func(s string) string { 151 return strings.TrimSpace(s) 152 }, 153 "capfirst": func(s string) string { 154 return strings.ToUpper(string(s[0])) + s[1:] 155 }, 156 "pluralize": func(arg string, value interface{}) string { 157 flag := false 158 v := reflect.ValueOf(value) 159 switch v.Kind() { 160 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 161 flag = v.Int() == 1 162 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 163 flag = v.Uint() == 1 164 default: 165 return "" 166 } 167 168 if !strings.Contains(arg, ",") { 169 arg = "," + arg 170 } 171 172 bits := strings.Split(arg, ",") 173 174 if len(bits) > 2 { 175 return "" 176 } 177 178 if flag { 179 return bits[0] 180 } 181 182 return bits[1] 183 }, 184 "yesno": func(yes, no string, value bool) string { 185 if value { 186 return yes 187 } 188 189 return no 190 }, 191 "rjust": func(arg int, value string) string { 192 n := arg - len([]rune(value)) 193 if n > 0 { 194 value = strings.Repeat(" ", n) + value 195 } 196 197 return value 198 }, 199 "ljust": func(arg int, value string) string { 200 n := arg - len([]rune(value)) 201 202 if n > 0 { 203 value = value + strings.Repeat(" ", n) 204 } 205 206 return value 207 }, 208 "center": func(arg int, value string) string { 209 n := arg - len([]rune(value)) 210 211 if n > 0 { 212 left := n / 2 213 right := n - left 214 value = strings.Repeat(" ", left) + value + strings.Repeat(" ", right) 215 } 216 217 return value 218 }, 219 "filesizeformat": func(value interface{}) string { 220 var size float64 221 222 v := reflect.ValueOf(value) 223 switch v.Kind() { 224 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 225 size = float64(v.Int()) 226 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 227 size = float64(v.Uint()) 228 case reflect.Float32, reflect.Float64: 229 size = v.Float() 230 default: 231 return "" 232 } 233 234 var KB float64 = 1 << 10 235 var MB float64 = 1 << 20 236 var GB float64 = 1 << 30 237 var TB float64 = 1 << 40 238 var PB float64 = 1 << 50 239 240 filesizeFormat := func(filesize float64, suffix string) string { 241 return strings.Replace(fmt.Sprintf("%.1f %s", filesize, suffix), ".0", "", -1) 242 } 243 244 var result string 245 if size < KB { 246 result = filesizeFormat(size, "bytes") 247 } else if size < MB { 248 result = filesizeFormat(size/KB, "KB") 249 } else if size < GB { 250 result = filesizeFormat(size/MB, "MB") 251 } else if size < TB { 252 result = filesizeFormat(size/GB, "GB") 253 } else if size < PB { 254 result = filesizeFormat(size/TB, "TB") 255 } else { 256 result = filesizeFormat(size/PB, "PB") 257 } 258 259 return result 260 }, 261 "apnumber": func(value interface{}) interface{} { 262 name := [10]string{ 263 "one", "two", "three", "four", "five", 264 "six", "seven", "eight", "nine", 265 } 266 267 v := reflect.ValueOf(value) 268 switch v.Kind() { 269 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 270 if v.Int() < 10 { 271 return name[v.Int()-1] 272 } 273 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 274 if v.Uint() < 10 { 275 return name[v.Uint()-1] 276 } 277 } 278 279 return value 280 }, 281 "intcomma": func(value interface{}) string { 282 v := reflect.ValueOf(value) 283 284 var x uint 285 minus := false 286 switch v.Kind() { 287 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 288 if v.Int() < 0 { 289 minus = true 290 x = uint(-v.Int()) 291 } else { 292 x = uint(v.Int()) 293 } 294 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 295 x = uint(v.Uint()) 296 default: 297 return "" 298 } 299 300 var result string 301 for x >= 1000 { 302 result = fmt.Sprintf(",%03d%s", x%1000, result) 303 x /= 1000 304 } 305 result = fmt.Sprintf("%d%s", x, result) 306 307 if minus { 308 result = "-" + result 309 } 310 311 return result 312 }, 313 "ordinal": func(value interface{}) string { 314 v := reflect.ValueOf(value) 315 316 var x uint 317 switch v.Kind() { 318 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 319 if v.Int() < 0 { 320 return "" 321 } 322 x = uint(v.Int()) 323 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 324 x = uint(v.Uint()) 325 default: 326 return "" 327 } 328 329 suffixes := [10]string{"th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"} 330 331 switch x % 100 { 332 case 11, 12, 13: 333 return fmt.Sprintf("%d%s", x, suffixes[0]) 334 } 335 336 return fmt.Sprintf("%d%s", x, suffixes[x%10]) 337 }, 338 "first": func(value interface{}) interface{} { 339 v := reflect.ValueOf(value) 340 341 switch v.Kind() { 342 case reflect.String: 343 return string([]rune(v.String())[0]) 344 case reflect.Slice, reflect.Array: 345 return v.Index(0).Interface() 346 } 347 348 return "" 349 }, 350 "last": func(value interface{}) interface{} { 351 switch v := reflect.ValueOf(value); v.Kind() { 352 case reflect.String: 353 str := []rune(v.String()) 354 return string(str[len(str)-1]) 355 case reflect.Slice, reflect.Array: 356 return v.Index(v.Len() - 1).Interface() 357 } 358 359 return "" 360 }, 361 "join": func(arg string, value []string) string { 362 return strings.Join(value, arg) 363 }, 364 "slice": func(start int, end int, value interface{}) interface{} { 365 v := reflect.ValueOf(value) 366 if start < 0 { 367 start = 0 368 } 369 370 switch v.Kind() { 371 case reflect.String: 372 str := []rune(v.String()) 373 374 if end > len(str) { 375 end = len(str) 376 } 377 378 return string(str[start:end]) 379 case reflect.Slice: 380 return v.Slice(start, end).Interface() 381 } 382 return "" 383 }, 384 "random": func(value interface{}) interface{} { 385 rand.Seed(time.Now().UTC().UnixNano()) 386 v := reflect.ValueOf(value) 387 388 switch v.Kind() { 389 case reflect.String: 390 str := []rune(v.String()) 391 return string(str[rand.Intn(len(str))]) 392 case reflect.Slice, reflect.Array: 393 return v.Index(rand.Intn(v.Len())).Interface() 394 } 395 396 return "" 397 }, 398 "randomintrange": func(min, max int, value interface{}) int { 399 rand.Seed(time.Now().UTC().UnixNano()) 400 return rand.Intn(max-min) + min 401 }, 402 "striptags": func(s string) string { 403 return strings.TrimSpace(striptagsRegexp.ReplaceAllString(s, "")) 404 }, 405 406 "panic": func(s interface{}) interface{} { 407 panic(s) 408 }, 409 }) 410 411 func WrapRecover(funcMap textTemplate.FuncMap) textTemplate.FuncMap { 412 m := textTemplate.FuncMap{} 413 for k, v := range funcMap { 414 m[k] = recoverWrapFunc(v) 415 } 416 417 return m 418 } 419 420 func recoverWrapFunc(fn interface{}) interface{} { 421 fnv := reflect.ValueOf(fn) 422 return reflect.MakeFunc(fnv.Type(), func(args []reflect.Value) []reflect.Value { 423 defer func() { // recovery will silently swallow all unexpected panics. 424 if r := recover(); r != nil { 425 log.Printf("E! recover from %v", r) 426 } 427 }() 428 429 return fnv.Call(args) 430 }).Interface() 431 } 432 433 // HtmlFuncMap defines the html template functions map. 434 var HtmlFuncMap = htmlTemplate.FuncMap(TextFuncMap) 435 436 // NewHtmlTemplate is a wrapper function of template.New(https://golang.org/pkg/html/template/#New). 437 // It automatically adds the gtf functions to the template's function map 438 // and returns template.Template(http://golang.org/pkg/html/template/#Template). 439 func NewHtmlTemplate(name string) *htmlTemplate.Template { 440 return htmlTemplate.New(name).Funcs(HtmlFuncMap) 441 } 442 443 // NewTextTemplate is a wrapper function of template.New(https://golang.org/pkg/text/template/#New). 444 // It automatically adds the gtf functions to the template's function map 445 // and returns template.Template(http://golang.org/pkg/text/template/#Template). 446 func NewTextTemplate(name string) *textTemplate.Template { 447 return textTemplate.New(name).Funcs(TextFuncMap) 448 } 449 450 // Inject injects gtf functions into the passed FuncMap. 451 // It does not overwrite the original function which have same name as a gtf function. 452 func Inject(funcs map[string]interface{}, force bool, prefix string) { 453 for k, v := range TextFuncMap { 454 if force { 455 funcs[prefix+k] = v 456 } else if _, ok := funcs[k]; !ok { 457 funcs[prefix+k] = v 458 } 459 } 460 } 461 462 func SafeEq(v interface{}, name string, defaultValue, compareValue interface{}) bool { 463 rv := reflect.ValueOf(v) 464 if rv.Kind() == reflect.Ptr { 465 rv = rv.Elem() 466 } 467 468 switch rv.Kind() { 469 case reflect.Map: 470 if v := rv.MapIndex(reflect.ValueOf(name)); v.IsValid() { 471 return v.Interface() == compareValue 472 } 473 case reflect.Struct: 474 if v := rv.FieldByName(name); v.IsValid() { 475 return v.Interface() == compareValue 476 } 477 } 478 479 return defaultValue == compareValue 480 } 481 482 func Contains(v interface{}, name string) bool { 483 rv := reflect.ValueOf(v) 484 if rv.Kind() == reflect.Ptr { 485 rv = rv.Elem() 486 } 487 488 switch rv.Kind() { 489 case reflect.Map: 490 return rv.MapIndex(reflect.ValueOf(name)).IsValid() 491 case reflect.Struct: 492 return rv.FieldByName(name).IsValid() 493 case reflect.Slice, reflect.Array: 494 f := func(v interface{}) bool { return v == name } 495 return FindInSlice(rv, f) >= 0 496 } 497 498 return false 499 }