mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-30 15:42:46 +00:00
383 lines
11 KiB
Go
383 lines
11 KiB
Go
|
package sprig
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"html/template"
|
||
|
"math/rand"
|
||
|
"os"
|
||
|
"path"
|
||
|
"path/filepath"
|
||
|
"reflect"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
ttemplate "text/template"
|
||
|
"time"
|
||
|
|
||
|
util "github.com/Masterminds/goutils"
|
||
|
"github.com/huandu/xstrings"
|
||
|
"github.com/shopspring/decimal"
|
||
|
)
|
||
|
|
||
|
// FuncMap produces the function map.
|
||
|
//
|
||
|
// Use this to pass the functions into the template engine:
|
||
|
//
|
||
|
// tpl := template.New("foo").Funcs(sprig.FuncMap()))
|
||
|
//
|
||
|
func FuncMap() template.FuncMap {
|
||
|
return HtmlFuncMap()
|
||
|
}
|
||
|
|
||
|
// HermeticTxtFuncMap returns a 'text/template'.FuncMap with only repeatable functions.
|
||
|
func HermeticTxtFuncMap() ttemplate.FuncMap {
|
||
|
r := TxtFuncMap()
|
||
|
for _, name := range nonhermeticFunctions {
|
||
|
delete(r, name)
|
||
|
}
|
||
|
return r
|
||
|
}
|
||
|
|
||
|
// HermeticHtmlFuncMap returns an 'html/template'.Funcmap with only repeatable functions.
|
||
|
func HermeticHtmlFuncMap() template.FuncMap {
|
||
|
r := HtmlFuncMap()
|
||
|
for _, name := range nonhermeticFunctions {
|
||
|
delete(r, name)
|
||
|
}
|
||
|
return r
|
||
|
}
|
||
|
|
||
|
// TxtFuncMap returns a 'text/template'.FuncMap
|
||
|
func TxtFuncMap() ttemplate.FuncMap {
|
||
|
return ttemplate.FuncMap(GenericFuncMap())
|
||
|
}
|
||
|
|
||
|
// HtmlFuncMap returns an 'html/template'.Funcmap
|
||
|
func HtmlFuncMap() template.FuncMap {
|
||
|
return template.FuncMap(GenericFuncMap())
|
||
|
}
|
||
|
|
||
|
// GenericFuncMap returns a copy of the basic function map as a map[string]interface{}.
|
||
|
func GenericFuncMap() map[string]interface{} {
|
||
|
gfm := make(map[string]interface{}, len(genericMap))
|
||
|
for k, v := range genericMap {
|
||
|
gfm[k] = v
|
||
|
}
|
||
|
return gfm
|
||
|
}
|
||
|
|
||
|
// These functions are not guaranteed to evaluate to the same result for given input, because they
|
||
|
// refer to the environment or global state.
|
||
|
var nonhermeticFunctions = []string{
|
||
|
// Date functions
|
||
|
"date",
|
||
|
"date_in_zone",
|
||
|
"date_modify",
|
||
|
"now",
|
||
|
"htmlDate",
|
||
|
"htmlDateInZone",
|
||
|
"dateInZone",
|
||
|
"dateModify",
|
||
|
|
||
|
// Strings
|
||
|
"randAlphaNum",
|
||
|
"randAlpha",
|
||
|
"randAscii",
|
||
|
"randNumeric",
|
||
|
"randBytes",
|
||
|
"uuidv4",
|
||
|
|
||
|
// OS
|
||
|
"env",
|
||
|
"expandenv",
|
||
|
|
||
|
// Network
|
||
|
"getHostByName",
|
||
|
}
|
||
|
|
||
|
var genericMap = map[string]interface{}{
|
||
|
"hello": func() string { return "Hello!" },
|
||
|
|
||
|
// Date functions
|
||
|
"ago": dateAgo,
|
||
|
"date": date,
|
||
|
"date_in_zone": dateInZone,
|
||
|
"date_modify": dateModify,
|
||
|
"dateInZone": dateInZone,
|
||
|
"dateModify": dateModify,
|
||
|
"duration": duration,
|
||
|
"durationRound": durationRound,
|
||
|
"htmlDate": htmlDate,
|
||
|
"htmlDateInZone": htmlDateInZone,
|
||
|
"must_date_modify": mustDateModify,
|
||
|
"mustDateModify": mustDateModify,
|
||
|
"mustToDate": mustToDate,
|
||
|
"now": time.Now,
|
||
|
"toDate": toDate,
|
||
|
"unixEpoch": unixEpoch,
|
||
|
|
||
|
// Strings
|
||
|
"abbrev": abbrev,
|
||
|
"abbrevboth": abbrevboth,
|
||
|
"trunc": trunc,
|
||
|
"trim": strings.TrimSpace,
|
||
|
"upper": strings.ToUpper,
|
||
|
"lower": strings.ToLower,
|
||
|
"title": strings.Title,
|
||
|
"untitle": untitle,
|
||
|
"substr": substring,
|
||
|
// Switch order so that "foo" | repeat 5
|
||
|
"repeat": func(count int, str string) string { return strings.Repeat(str, count) },
|
||
|
// Deprecated: Use trimAll.
|
||
|
"trimall": func(a, b string) string { return strings.Trim(b, a) },
|
||
|
// Switch order so that "$foo" | trimall "$"
|
||
|
"trimAll": func(a, b string) string { return strings.Trim(b, a) },
|
||
|
"trimSuffix": func(a, b string) string { return strings.TrimSuffix(b, a) },
|
||
|
"trimPrefix": func(a, b string) string { return strings.TrimPrefix(b, a) },
|
||
|
"nospace": util.DeleteWhiteSpace,
|
||
|
"initials": initials,
|
||
|
"randAlphaNum": randAlphaNumeric,
|
||
|
"randAlpha": randAlpha,
|
||
|
"randAscii": randAscii,
|
||
|
"randNumeric": randNumeric,
|
||
|
"swapcase": util.SwapCase,
|
||
|
"shuffle": xstrings.Shuffle,
|
||
|
"snakecase": xstrings.ToSnakeCase,
|
||
|
"camelcase": xstrings.ToCamelCase,
|
||
|
"kebabcase": xstrings.ToKebabCase,
|
||
|
"wrap": func(l int, s string) string { return util.Wrap(s, l) },
|
||
|
"wrapWith": func(l int, sep, str string) string { return util.WrapCustom(str, l, sep, true) },
|
||
|
// Switch order so that "foobar" | contains "foo"
|
||
|
"contains": func(substr string, str string) bool { return strings.Contains(str, substr) },
|
||
|
"hasPrefix": func(substr string, str string) bool { return strings.HasPrefix(str, substr) },
|
||
|
"hasSuffix": func(substr string, str string) bool { return strings.HasSuffix(str, substr) },
|
||
|
"quote": quote,
|
||
|
"squote": squote,
|
||
|
"cat": cat,
|
||
|
"indent": indent,
|
||
|
"nindent": nindent,
|
||
|
"replace": replace,
|
||
|
"plural": plural,
|
||
|
"sha1sum": sha1sum,
|
||
|
"sha256sum": sha256sum,
|
||
|
"adler32sum": adler32sum,
|
||
|
"toString": strval,
|
||
|
|
||
|
// Wrap Atoi to stop errors.
|
||
|
"atoi": func(a string) int { i, _ := strconv.Atoi(a); return i },
|
||
|
"int64": toInt64,
|
||
|
"int": toInt,
|
||
|
"float64": toFloat64,
|
||
|
"seq": seq,
|
||
|
"toDecimal": toDecimal,
|
||
|
|
||
|
//"gt": func(a, b int) bool {return a > b},
|
||
|
//"gte": func(a, b int) bool {return a >= b},
|
||
|
//"lt": func(a, b int) bool {return a < b},
|
||
|
//"lte": func(a, b int) bool {return a <= b},
|
||
|
|
||
|
// split "/" foo/bar returns map[int]string{0: foo, 1: bar}
|
||
|
"split": split,
|
||
|
"splitList": func(sep, orig string) []string { return strings.Split(orig, sep) },
|
||
|
// splitn "/" foo/bar/fuu returns map[int]string{0: foo, 1: bar/fuu}
|
||
|
"splitn": splitn,
|
||
|
"toStrings": strslice,
|
||
|
|
||
|
"until": until,
|
||
|
"untilStep": untilStep,
|
||
|
|
||
|
// VERY basic arithmetic.
|
||
|
"add1": func(i interface{}) int64 { return toInt64(i) + 1 },
|
||
|
"add": func(i ...interface{}) int64 {
|
||
|
var a int64 = 0
|
||
|
for _, b := range i {
|
||
|
a += toInt64(b)
|
||
|
}
|
||
|
return a
|
||
|
},
|
||
|
"sub": func(a, b interface{}) int64 { return toInt64(a) - toInt64(b) },
|
||
|
"div": func(a, b interface{}) int64 { return toInt64(a) / toInt64(b) },
|
||
|
"mod": func(a, b interface{}) int64 { return toInt64(a) % toInt64(b) },
|
||
|
"mul": func(a interface{}, v ...interface{}) int64 {
|
||
|
val := toInt64(a)
|
||
|
for _, b := range v {
|
||
|
val = val * toInt64(b)
|
||
|
}
|
||
|
return val
|
||
|
},
|
||
|
"randInt": func(min, max int) int { return rand.Intn(max-min) + min },
|
||
|
"add1f": func(i interface{}) float64 {
|
||
|
return execDecimalOp(i, []interface{}{1}, func(d1, d2 decimal.Decimal) decimal.Decimal { return d1.Add(d2) })
|
||
|
},
|
||
|
"addf": func(i ...interface{}) float64 {
|
||
|
a := interface{}(float64(0))
|
||
|
return execDecimalOp(a, i, func(d1, d2 decimal.Decimal) decimal.Decimal { return d1.Add(d2) })
|
||
|
},
|
||
|
"subf": func(a interface{}, v ...interface{}) float64 {
|
||
|
return execDecimalOp(a, v, func(d1, d2 decimal.Decimal) decimal.Decimal { return d1.Sub(d2) })
|
||
|
},
|
||
|
"divf": func(a interface{}, v ...interface{}) float64 {
|
||
|
return execDecimalOp(a, v, func(d1, d2 decimal.Decimal) decimal.Decimal { return d1.Div(d2) })
|
||
|
},
|
||
|
"mulf": func(a interface{}, v ...interface{}) float64 {
|
||
|
return execDecimalOp(a, v, func(d1, d2 decimal.Decimal) decimal.Decimal { return d1.Mul(d2) })
|
||
|
},
|
||
|
"biggest": max,
|
||
|
"max": max,
|
||
|
"min": min,
|
||
|
"maxf": maxf,
|
||
|
"minf": minf,
|
||
|
"ceil": ceil,
|
||
|
"floor": floor,
|
||
|
"round": round,
|
||
|
|
||
|
// string slices. Note that we reverse the order b/c that's better
|
||
|
// for template processing.
|
||
|
"join": join,
|
||
|
"sortAlpha": sortAlpha,
|
||
|
|
||
|
// Defaults
|
||
|
"default": dfault,
|
||
|
"empty": empty,
|
||
|
"coalesce": coalesce,
|
||
|
"all": all,
|
||
|
"any": any,
|
||
|
"compact": compact,
|
||
|
"mustCompact": mustCompact,
|
||
|
"fromJson": fromJson,
|
||
|
"toJson": toJson,
|
||
|
"toPrettyJson": toPrettyJson,
|
||
|
"toRawJson": toRawJson,
|
||
|
"mustFromJson": mustFromJson,
|
||
|
"mustToJson": mustToJson,
|
||
|
"mustToPrettyJson": mustToPrettyJson,
|
||
|
"mustToRawJson": mustToRawJson,
|
||
|
"ternary": ternary,
|
||
|
"deepCopy": deepCopy,
|
||
|
"mustDeepCopy": mustDeepCopy,
|
||
|
|
||
|
// Reflection
|
||
|
"typeOf": typeOf,
|
||
|
"typeIs": typeIs,
|
||
|
"typeIsLike": typeIsLike,
|
||
|
"kindOf": kindOf,
|
||
|
"kindIs": kindIs,
|
||
|
"deepEqual": reflect.DeepEqual,
|
||
|
|
||
|
// OS:
|
||
|
"env": os.Getenv,
|
||
|
"expandenv": os.ExpandEnv,
|
||
|
|
||
|
// Network:
|
||
|
"getHostByName": getHostByName,
|
||
|
|
||
|
// Paths:
|
||
|
"base": path.Base,
|
||
|
"dir": path.Dir,
|
||
|
"clean": path.Clean,
|
||
|
"ext": path.Ext,
|
||
|
"isAbs": path.IsAbs,
|
||
|
|
||
|
// Filepaths:
|
||
|
"osBase": filepath.Base,
|
||
|
"osClean": filepath.Clean,
|
||
|
"osDir": filepath.Dir,
|
||
|
"osExt": filepath.Ext,
|
||
|
"osIsAbs": filepath.IsAbs,
|
||
|
|
||
|
// Encoding:
|
||
|
"b64enc": base64encode,
|
||
|
"b64dec": base64decode,
|
||
|
"b32enc": base32encode,
|
||
|
"b32dec": base32decode,
|
||
|
|
||
|
// Data Structures:
|
||
|
"tuple": list, // FIXME: with the addition of append/prepend these are no longer immutable.
|
||
|
"list": list,
|
||
|
"dict": dict,
|
||
|
"get": get,
|
||
|
"set": set,
|
||
|
"unset": unset,
|
||
|
"hasKey": hasKey,
|
||
|
"pluck": pluck,
|
||
|
"keys": keys,
|
||
|
"pick": pick,
|
||
|
"omit": omit,
|
||
|
"merge": merge,
|
||
|
"mergeOverwrite": mergeOverwrite,
|
||
|
"mustMerge": mustMerge,
|
||
|
"mustMergeOverwrite": mustMergeOverwrite,
|
||
|
"values": values,
|
||
|
|
||
|
"append": push, "push": push,
|
||
|
"mustAppend": mustPush, "mustPush": mustPush,
|
||
|
"prepend": prepend,
|
||
|
"mustPrepend": mustPrepend,
|
||
|
"first": first,
|
||
|
"mustFirst": mustFirst,
|
||
|
"rest": rest,
|
||
|
"mustRest": mustRest,
|
||
|
"last": last,
|
||
|
"mustLast": mustLast,
|
||
|
"initial": initial,
|
||
|
"mustInitial": mustInitial,
|
||
|
"reverse": reverse,
|
||
|
"mustReverse": mustReverse,
|
||
|
"uniq": uniq,
|
||
|
"mustUniq": mustUniq,
|
||
|
"without": without,
|
||
|
"mustWithout": mustWithout,
|
||
|
"has": has,
|
||
|
"mustHas": mustHas,
|
||
|
"slice": slice,
|
||
|
"mustSlice": mustSlice,
|
||
|
"concat": concat,
|
||
|
"dig": dig,
|
||
|
"chunk": chunk,
|
||
|
"mustChunk": mustChunk,
|
||
|
|
||
|
// Crypto:
|
||
|
"bcrypt": bcrypt,
|
||
|
"htpasswd": htpasswd,
|
||
|
"genPrivateKey": generatePrivateKey,
|
||
|
"derivePassword": derivePassword,
|
||
|
"buildCustomCert": buildCustomCertificate,
|
||
|
"genCA": generateCertificateAuthority,
|
||
|
"genCAWithKey": generateCertificateAuthorityWithPEMKey,
|
||
|
"genSelfSignedCert": generateSelfSignedCertificate,
|
||
|
"genSelfSignedCertWithKey": generateSelfSignedCertificateWithPEMKey,
|
||
|
"genSignedCert": generateSignedCertificate,
|
||
|
"genSignedCertWithKey": generateSignedCertificateWithPEMKey,
|
||
|
"encryptAES": encryptAES,
|
||
|
"decryptAES": decryptAES,
|
||
|
"randBytes": randBytes,
|
||
|
|
||
|
// UUIDs:
|
||
|
"uuidv4": uuidv4,
|
||
|
|
||
|
// SemVer:
|
||
|
"semver": semver,
|
||
|
"semverCompare": semverCompare,
|
||
|
|
||
|
// Flow Control:
|
||
|
"fail": func(msg string) (string, error) { return "", errors.New(msg) },
|
||
|
|
||
|
// Regex
|
||
|
"regexMatch": regexMatch,
|
||
|
"mustRegexMatch": mustRegexMatch,
|
||
|
"regexFindAll": regexFindAll,
|
||
|
"mustRegexFindAll": mustRegexFindAll,
|
||
|
"regexFind": regexFind,
|
||
|
"mustRegexFind": mustRegexFind,
|
||
|
"regexReplaceAll": regexReplaceAll,
|
||
|
"mustRegexReplaceAll": mustRegexReplaceAll,
|
||
|
"regexReplaceAllLiteral": regexReplaceAllLiteral,
|
||
|
"mustRegexReplaceAllLiteral": mustRegexReplaceAllLiteral,
|
||
|
"regexSplit": regexSplit,
|
||
|
"mustRegexSplit": mustRegexSplit,
|
||
|
"regexQuoteMeta": regexQuoteMeta,
|
||
|
|
||
|
// URLs:
|
||
|
"urlParse": urlParse,
|
||
|
"urlJoin": urlJoin,
|
||
|
}
|