mirror of
https://github.com/Luzifer/clean-github-branches.git
synced 2024-11-10 07:00:08 +00:00
471 lines
9.9 KiB
Go
471 lines
9.9 KiB
Go
|
package gtf
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
htmlTemplate "html/template"
|
||
|
"math"
|
||
|
"math/rand"
|
||
|
"net/url"
|
||
|
"reflect"
|
||
|
"regexp"
|
||
|
"strings"
|
||
|
textTemplate "text/template"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
var striptagsRegexp = regexp.MustCompile("<[^>]*?>")
|
||
|
|
||
|
// recovery will silently swallow all unexpected panics.
|
||
|
func recovery() {
|
||
|
recover()
|
||
|
}
|
||
|
|
||
|
var GtfTextFuncMap = textTemplate.FuncMap{
|
||
|
"replace": func(s1 string, s2 string) string {
|
||
|
defer recovery()
|
||
|
|
||
|
return strings.Replace(s2, s1, "", -1)
|
||
|
},
|
||
|
"title": func(s string) string {
|
||
|
defer recovery()
|
||
|
return strings.Title(s)
|
||
|
},
|
||
|
"default": func(arg interface{}, value interface{}) interface{} {
|
||
|
defer recovery()
|
||
|
|
||
|
v := reflect.ValueOf(value)
|
||
|
switch v.Kind() {
|
||
|
case reflect.String, reflect.Slice, reflect.Array, reflect.Map:
|
||
|
if v.Len() == 0 {
|
||
|
return arg
|
||
|
}
|
||
|
case reflect.Bool:
|
||
|
if !v.Bool() {
|
||
|
return arg
|
||
|
}
|
||
|
default:
|
||
|
return value
|
||
|
}
|
||
|
|
||
|
return value
|
||
|
},
|
||
|
"length": func(value interface{}) int {
|
||
|
defer recovery()
|
||
|
|
||
|
v := reflect.ValueOf(value)
|
||
|
switch v.Kind() {
|
||
|
case reflect.Slice, reflect.Array, reflect.Map:
|
||
|
return v.Len()
|
||
|
case reflect.String:
|
||
|
return len([]rune(v.String()))
|
||
|
}
|
||
|
|
||
|
return 0
|
||
|
},
|
||
|
"lower": func(s string) string {
|
||
|
defer recovery()
|
||
|
|
||
|
return strings.ToLower(s)
|
||
|
},
|
||
|
"upper": func(s string) string {
|
||
|
defer recovery()
|
||
|
|
||
|
return strings.ToUpper(s)
|
||
|
},
|
||
|
"truncatechars": func(n int, s string) string {
|
||
|
defer recovery()
|
||
|
|
||
|
if n < 0 {
|
||
|
return s
|
||
|
}
|
||
|
|
||
|
r := []rune(s)
|
||
|
rLength := len(r)
|
||
|
|
||
|
if n >= rLength {
|
||
|
return s
|
||
|
}
|
||
|
|
||
|
if n > 3 && rLength > 3 {
|
||
|
return string(r[:n-3]) + "..."
|
||
|
}
|
||
|
|
||
|
return string(r[:n])
|
||
|
},
|
||
|
"urlencode": func(s string) string {
|
||
|
defer recovery()
|
||
|
|
||
|
return url.QueryEscape(s)
|
||
|
},
|
||
|
"wordcount": func(s string) int {
|
||
|
defer recovery()
|
||
|
|
||
|
return len(strings.Fields(s))
|
||
|
},
|
||
|
"divisibleby": func(arg interface{}, value interface{}) bool {
|
||
|
defer recovery()
|
||
|
|
||
|
var v float64
|
||
|
switch value.(type) {
|
||
|
case int, int8, int16, int32, int64:
|
||
|
v = float64(reflect.ValueOf(value).Int())
|
||
|
case uint, uint8, uint16, uint32, uint64:
|
||
|
v = float64(reflect.ValueOf(value).Uint())
|
||
|
case float32, float64:
|
||
|
v = reflect.ValueOf(value).Float()
|
||
|
default:
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
var a float64
|
||
|
switch arg.(type) {
|
||
|
case int, int8, int16, int32, int64:
|
||
|
a = float64(reflect.ValueOf(arg).Int())
|
||
|
case uint, uint8, uint16, uint32, uint64:
|
||
|
a = float64(reflect.ValueOf(arg).Uint())
|
||
|
case float32, float64:
|
||
|
a = reflect.ValueOf(arg).Float()
|
||
|
default:
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
return math.Mod(v, a) == 0
|
||
|
},
|
||
|
"lengthis": func(arg int, value interface{}) bool {
|
||
|
defer recovery()
|
||
|
|
||
|
v := reflect.ValueOf(value)
|
||
|
switch v.Kind() {
|
||
|
case reflect.Slice, reflect.Array, reflect.Map:
|
||
|
return v.Len() == arg
|
||
|
case reflect.String:
|
||
|
return len([]rune(v.String())) == arg
|
||
|
}
|
||
|
|
||
|
return false
|
||
|
},
|
||
|
"trim": func(s string) string {
|
||
|
defer recovery()
|
||
|
|
||
|
return strings.TrimSpace(s)
|
||
|
},
|
||
|
"capfirst": func(s string) string {
|
||
|
defer recovery()
|
||
|
|
||
|
return strings.ToUpper(string(s[0])) + s[1:]
|
||
|
},
|
||
|
"pluralize": func(arg string, value interface{}) string {
|
||
|
defer recovery()
|
||
|
|
||
|
flag := false
|
||
|
v := reflect.ValueOf(value)
|
||
|
switch v.Kind() {
|
||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
|
flag = v.Int() == 1
|
||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||
|
flag = v.Uint() == 1
|
||
|
default:
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
if !strings.Contains(arg, ",") {
|
||
|
arg = "," + arg
|
||
|
}
|
||
|
|
||
|
bits := strings.Split(arg, ",")
|
||
|
|
||
|
if len(bits) > 2 {
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
if flag {
|
||
|
return bits[0]
|
||
|
}
|
||
|
|
||
|
return bits[1]
|
||
|
},
|
||
|
"yesno": func(yes string, no string, value bool) string {
|
||
|
defer recovery()
|
||
|
|
||
|
if value {
|
||
|
return yes
|
||
|
}
|
||
|
|
||
|
return no
|
||
|
},
|
||
|
"rjust": func(arg int, value string) string {
|
||
|
defer recovery()
|
||
|
|
||
|
n := arg - len([]rune(value))
|
||
|
|
||
|
if n > 0 {
|
||
|
value = strings.Repeat(" ", n) + value
|
||
|
}
|
||
|
|
||
|
return value
|
||
|
},
|
||
|
"ljust": func(arg int, value string) string {
|
||
|
defer recovery()
|
||
|
|
||
|
n := arg - len([]rune(value))
|
||
|
|
||
|
if n > 0 {
|
||
|
value = value + strings.Repeat(" ", n)
|
||
|
}
|
||
|
|
||
|
return value
|
||
|
},
|
||
|
"center": func(arg int, value string) string {
|
||
|
defer recovery()
|
||
|
|
||
|
n := arg - len([]rune(value))
|
||
|
|
||
|
if n > 0 {
|
||
|
left := n / 2
|
||
|
right := n - left
|
||
|
value = strings.Repeat(" ", left) + value + strings.Repeat(" ", right)
|
||
|
}
|
||
|
|
||
|
return value
|
||
|
},
|
||
|
"filesizeformat": func(value interface{}) string {
|
||
|
defer recovery()
|
||
|
|
||
|
var size float64
|
||
|
|
||
|
v := reflect.ValueOf(value)
|
||
|
switch v.Kind() {
|
||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
|
size = float64(v.Int())
|
||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||
|
size = float64(v.Uint())
|
||
|
case reflect.Float32, reflect.Float64:
|
||
|
size = v.Float()
|
||
|
default:
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
var KB float64 = 1 << 10
|
||
|
var MB float64 = 1 << 20
|
||
|
var GB float64 = 1 << 30
|
||
|
var TB float64 = 1 << 40
|
||
|
var PB float64 = 1 << 50
|
||
|
|
||
|
filesizeFormat := func(filesize float64, suffix string) string {
|
||
|
return strings.Replace(fmt.Sprintf("%.1f %s", filesize, suffix), ".0", "", -1)
|
||
|
}
|
||
|
|
||
|
var result string
|
||
|
if size < KB {
|
||
|
result = filesizeFormat(size, "bytes")
|
||
|
} else if size < MB {
|
||
|
result = filesizeFormat(size/KB, "KB")
|
||
|
} else if size < GB {
|
||
|
result = filesizeFormat(size/MB, "MB")
|
||
|
} else if size < TB {
|
||
|
result = filesizeFormat(size/GB, "GB")
|
||
|
} else if size < PB {
|
||
|
result = filesizeFormat(size/TB, "TB")
|
||
|
} else {
|
||
|
result = filesizeFormat(size/PB, "PB")
|
||
|
}
|
||
|
|
||
|
return result
|
||
|
},
|
||
|
"apnumber": func(value interface{}) interface{} {
|
||
|
defer recovery()
|
||
|
|
||
|
name := [10]string{"one", "two", "three", "four", "five",
|
||
|
"six", "seven", "eight", "nine"}
|
||
|
|
||
|
v := reflect.ValueOf(value)
|
||
|
switch v.Kind() {
|
||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
|
if v.Int() < 10 {
|
||
|
return name[v.Int()-1]
|
||
|
}
|
||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||
|
if v.Uint() < 10 {
|
||
|
return name[v.Uint()-1]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return value
|
||
|
},
|
||
|
"intcomma": func(value interface{}) string {
|
||
|
defer recovery()
|
||
|
|
||
|
v := reflect.ValueOf(value)
|
||
|
|
||
|
var x uint
|
||
|
minus := false
|
||
|
switch v.Kind() {
|
||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
|
if v.Int() < 0 {
|
||
|
minus = true
|
||
|
x = uint(-v.Int())
|
||
|
} else {
|
||
|
x = uint(v.Int())
|
||
|
}
|
||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||
|
x = uint(v.Uint())
|
||
|
default:
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
var result string
|
||
|
for x >= 1000 {
|
||
|
result = fmt.Sprintf(",%03d%s", x%1000, result)
|
||
|
x /= 1000
|
||
|
}
|
||
|
result = fmt.Sprintf("%d%s", x, result)
|
||
|
|
||
|
if minus {
|
||
|
result = "-" + result
|
||
|
}
|
||
|
|
||
|
return result
|
||
|
},
|
||
|
"ordinal": func(value interface{}) string {
|
||
|
defer recovery()
|
||
|
|
||
|
v := reflect.ValueOf(value)
|
||
|
|
||
|
var x uint
|
||
|
switch v.Kind() {
|
||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
|
if v.Int() < 0 {
|
||
|
return ""
|
||
|
}
|
||
|
x = uint(v.Int())
|
||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||
|
x = uint(v.Uint())
|
||
|
default:
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
suffixes := [10]string{"th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"}
|
||
|
|
||
|
switch x % 100 {
|
||
|
case 11, 12, 13:
|
||
|
return fmt.Sprintf("%d%s", x, suffixes[0])
|
||
|
}
|
||
|
|
||
|
return fmt.Sprintf("%d%s", x, suffixes[x%10])
|
||
|
},
|
||
|
"first": func(value interface{}) interface{} {
|
||
|
defer recovery()
|
||
|
|
||
|
v := reflect.ValueOf(value)
|
||
|
|
||
|
switch v.Kind() {
|
||
|
case reflect.String:
|
||
|
return string([]rune(v.String())[0])
|
||
|
case reflect.Slice, reflect.Array:
|
||
|
return v.Index(0).Interface()
|
||
|
}
|
||
|
|
||
|
return ""
|
||
|
},
|
||
|
"last": func(value interface{}) interface{} {
|
||
|
defer recovery()
|
||
|
|
||
|
v := reflect.ValueOf(value)
|
||
|
|
||
|
switch v.Kind() {
|
||
|
case reflect.String:
|
||
|
str := []rune(v.String())
|
||
|
return string(str[len(str)-1])
|
||
|
case reflect.Slice, reflect.Array:
|
||
|
return v.Index(v.Len() - 1).Interface()
|
||
|
}
|
||
|
|
||
|
return ""
|
||
|
},
|
||
|
"join": func(arg string, value []string) string {
|
||
|
defer recovery()
|
||
|
|
||
|
return strings.Join(value, arg)
|
||
|
},
|
||
|
"slice": func(start int, end int, value interface{}) interface{} {
|
||
|
defer recovery()
|
||
|
|
||
|
v := reflect.ValueOf(value)
|
||
|
|
||
|
if start < 0 {
|
||
|
start = 0
|
||
|
}
|
||
|
|
||
|
switch v.Kind() {
|
||
|
case reflect.String:
|
||
|
str := []rune(v.String())
|
||
|
|
||
|
if end > len(str) {
|
||
|
end = len(str)
|
||
|
}
|
||
|
|
||
|
return string(str[start:end])
|
||
|
case reflect.Slice:
|
||
|
return v.Slice(start, end).Interface()
|
||
|
}
|
||
|
return ""
|
||
|
},
|
||
|
"random": func(value interface{}) interface{} {
|
||
|
defer recovery()
|
||
|
|
||
|
rand.Seed(time.Now().UTC().UnixNano())
|
||
|
|
||
|
v := reflect.ValueOf(value)
|
||
|
|
||
|
switch v.Kind() {
|
||
|
case reflect.String:
|
||
|
str := []rune(v.String())
|
||
|
return string(str[rand.Intn(len(str))])
|
||
|
case reflect.Slice, reflect.Array:
|
||
|
return v.Index(rand.Intn(v.Len())).Interface()
|
||
|
}
|
||
|
|
||
|
return ""
|
||
|
},
|
||
|
"striptags": func(s string) string {
|
||
|
return strings.TrimSpace(striptagsRegexp.ReplaceAllString(s, ""))
|
||
|
},
|
||
|
}
|
||
|
|
||
|
var GtfFuncMap = htmlTemplate.FuncMap(GtfTextFuncMap)
|
||
|
|
||
|
// gtf.New is a wrapper function of template.New(https://golang.org/pkg/html/template/#New).
|
||
|
// It automatically adds the gtf functions to the template's function map
|
||
|
// and returns template.Template(http://golang.org/pkg/html/template/#Template).
|
||
|
func New(name string) *htmlTemplate.Template {
|
||
|
return htmlTemplate.New(name).Funcs(GtfFuncMap)
|
||
|
}
|
||
|
|
||
|
// gtf.Inject injects gtf functions into the passed FuncMap.
|
||
|
// It does not overwrite the original function which have same name as a gtf function.
|
||
|
func Inject(funcs map[string]interface{}) {
|
||
|
for k, v := range GtfFuncMap {
|
||
|
if _, ok := funcs[k]; !ok {
|
||
|
funcs[k] = v
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// gtf.ForceInject injects gtf functions into the passed FuncMap.
|
||
|
// It overwrites the original function which have same name as a gtf function.
|
||
|
func ForceInject(funcs map[string]interface{}) {
|
||
|
for k, v := range GtfFuncMap {
|
||
|
funcs[k] = v
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// gtf.Inject injects gtf functions into the passed FuncMap.
|
||
|
// It prefixes the gtf functions with the specified prefix.
|
||
|
// If there are many function which have same names as the gtf functions,
|
||
|
// you can use this function to prefix the gtf functions.
|
||
|
func InjectWithPrefix(funcs map[string]interface{}, prefix string) {
|
||
|
for k, v := range GtfFuncMap {
|
||
|
funcs[prefix+k] = v
|
||
|
}
|
||
|
}
|