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  }