functions.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. package sprig
  2. import (
  3. "errors"
  4. "html/template"
  5. "os"
  6. "path"
  7. "reflect"
  8. "strconv"
  9. "strings"
  10. ttemplate "text/template"
  11. "time"
  12. util "github.com/Masterminds/goutils"
  13. "github.com/huandu/xstrings"
  14. )
  15. // Produce the function map.
  16. //
  17. // Use this to pass the functions into the template engine:
  18. //
  19. // tpl := template.New("foo").Funcs(sprig.FuncMap()))
  20. //
  21. func FuncMap() template.FuncMap {
  22. return HtmlFuncMap()
  23. }
  24. // HermeticTxtFuncMap returns a 'text/template'.FuncMap with only repeatable functions.
  25. func HermeticTxtFuncMap() ttemplate.FuncMap {
  26. r := TxtFuncMap()
  27. for _, name := range nonhermeticFunctions {
  28. delete(r, name)
  29. }
  30. return r
  31. }
  32. // HermeticHtmlFuncMap returns an 'html/template'.Funcmap with only repeatable functions.
  33. func HermeticHtmlFuncMap() template.FuncMap {
  34. r := HtmlFuncMap()
  35. for _, name := range nonhermeticFunctions {
  36. delete(r, name)
  37. }
  38. return r
  39. }
  40. // TxtFuncMap returns a 'text/template'.FuncMap
  41. func TxtFuncMap() ttemplate.FuncMap {
  42. return ttemplate.FuncMap(GenericFuncMap())
  43. }
  44. // HtmlFuncMap returns an 'html/template'.Funcmap
  45. func HtmlFuncMap() template.FuncMap {
  46. return template.FuncMap(GenericFuncMap())
  47. }
  48. // GenericFuncMap returns a copy of the basic function map as a map[string]interface{}.
  49. func GenericFuncMap() map[string]interface{} {
  50. gfm := make(map[string]interface{}, len(genericMap))
  51. for k, v := range genericMap {
  52. gfm[k] = v
  53. }
  54. return gfm
  55. }
  56. // These functions are not guaranteed to evaluate to the same result for given input, because they
  57. // refer to the environemnt or global state.
  58. var nonhermeticFunctions = []string{
  59. // Date functions
  60. "date",
  61. "date_in_zone",
  62. "date_modify",
  63. "now",
  64. "htmlDate",
  65. "htmlDateInZone",
  66. "dateInZone",
  67. "dateModify",
  68. // Strings
  69. "randAlphaNum",
  70. "randAlpha",
  71. "randAscii",
  72. "randNumeric",
  73. "uuidv4",
  74. // OS
  75. "env",
  76. "expandenv",
  77. // Network
  78. "getHostByName",
  79. }
  80. var genericMap = map[string]interface{}{
  81. "hello": func() string { return "Hello!" },
  82. // Date functions
  83. "date": date,
  84. "date_in_zone": dateInZone,
  85. "date_modify": dateModify,
  86. "now": func() time.Time { return time.Now() },
  87. "htmlDate": htmlDate,
  88. "htmlDateInZone": htmlDateInZone,
  89. "dateInZone": dateInZone,
  90. "dateModify": dateModify,
  91. "ago": dateAgo,
  92. "toDate": toDate,
  93. "unixEpoch": unixEpoch,
  94. // Strings
  95. "abbrev": abbrev,
  96. "abbrevboth": abbrevboth,
  97. "trunc": trunc,
  98. "trim": strings.TrimSpace,
  99. "upper": strings.ToUpper,
  100. "lower": strings.ToLower,
  101. "title": strings.Title,
  102. "untitle": untitle,
  103. "substr": substring,
  104. // Switch order so that "foo" | repeat 5
  105. "repeat": func(count int, str string) string { return strings.Repeat(str, count) },
  106. // Deprecated: Use trimAll.
  107. "trimall": func(a, b string) string { return strings.Trim(b, a) },
  108. // Switch order so that "$foo" | trimall "$"
  109. "trimAll": func(a, b string) string { return strings.Trim(b, a) },
  110. "trimSuffix": func(a, b string) string { return strings.TrimSuffix(b, a) },
  111. "trimPrefix": func(a, b string) string { return strings.TrimPrefix(b, a) },
  112. "nospace": util.DeleteWhiteSpace,
  113. "initials": initials,
  114. "randAlphaNum": randAlphaNumeric,
  115. "randAlpha": randAlpha,
  116. "randAscii": randAscii,
  117. "randNumeric": randNumeric,
  118. "swapcase": util.SwapCase,
  119. "shuffle": xstrings.Shuffle,
  120. "snakecase": xstrings.ToSnakeCase,
  121. "camelcase": xstrings.ToCamelCase,
  122. "kebabcase": xstrings.ToKebabCase,
  123. "wrap": func(l int, s string) string { return util.Wrap(s, l) },
  124. "wrapWith": func(l int, sep, str string) string { return util.WrapCustom(str, l, sep, true) },
  125. // Switch order so that "foobar" | contains "foo"
  126. "contains": func(substr string, str string) bool { return strings.Contains(str, substr) },
  127. "hasPrefix": func(substr string, str string) bool { return strings.HasPrefix(str, substr) },
  128. "hasSuffix": func(substr string, str string) bool { return strings.HasSuffix(str, substr) },
  129. "quote": quote,
  130. "squote": squote,
  131. "cat": cat,
  132. "indent": indent,
  133. "nindent": nindent,
  134. "replace": replace,
  135. "plural": plural,
  136. "sha1sum": sha1sum,
  137. "sha256sum": sha256sum,
  138. "adler32sum": adler32sum,
  139. "toString": strval,
  140. // Wrap Atoi to stop errors.
  141. "atoi": func(a string) int { i, _ := strconv.Atoi(a); return i },
  142. "int64": toInt64,
  143. "int": toInt,
  144. "float64": toFloat64,
  145. "toDecimal": toDecimal,
  146. //"gt": func(a, b int) bool {return a > b},
  147. //"gte": func(a, b int) bool {return a >= b},
  148. //"lt": func(a, b int) bool {return a < b},
  149. //"lte": func(a, b int) bool {return a <= b},
  150. // split "/" foo/bar returns map[int]string{0: foo, 1: bar}
  151. "split": split,
  152. "splitList": func(sep, orig string) []string { return strings.Split(orig, sep) },
  153. // splitn "/" foo/bar/fuu returns map[int]string{0: foo, 1: bar/fuu}
  154. "splitn": splitn,
  155. "toStrings": strslice,
  156. "until": until,
  157. "untilStep": untilStep,
  158. // VERY basic arithmetic.
  159. "add1": func(i interface{}) int64 { return toInt64(i) + 1 },
  160. "add": func(i ...interface{}) int64 {
  161. var a int64 = 0
  162. for _, b := range i {
  163. a += toInt64(b)
  164. }
  165. return a
  166. },
  167. "sub": func(a, b interface{}) int64 { return toInt64(a) - toInt64(b) },
  168. "div": func(a, b interface{}) int64 { return toInt64(a) / toInt64(b) },
  169. "mod": func(a, b interface{}) int64 { return toInt64(a) % toInt64(b) },
  170. "mul": func(a interface{}, v ...interface{}) int64 {
  171. val := toInt64(a)
  172. for _, b := range v {
  173. val = val * toInt64(b)
  174. }
  175. return val
  176. },
  177. "biggest": max,
  178. "max": max,
  179. "min": min,
  180. "ceil": ceil,
  181. "floor": floor,
  182. "round": round,
  183. // string slices. Note that we reverse the order b/c that's better
  184. // for template processing.
  185. "join": join,
  186. "sortAlpha": sortAlpha,
  187. // Defaults
  188. "default": dfault,
  189. "empty": empty,
  190. "coalesce": coalesce,
  191. "compact": compact,
  192. "deepCopy": deepCopy,
  193. "toJson": toJson,
  194. "toPrettyJson": toPrettyJson,
  195. "ternary": ternary,
  196. // Reflection
  197. "typeOf": typeOf,
  198. "typeIs": typeIs,
  199. "typeIsLike": typeIsLike,
  200. "kindOf": kindOf,
  201. "kindIs": kindIs,
  202. "deepEqual": reflect.DeepEqual,
  203. // OS:
  204. "env": func(s string) string { return os.Getenv(s) },
  205. "expandenv": func(s string) string { return os.ExpandEnv(s) },
  206. // Network:
  207. "getHostByName": getHostByName,
  208. // File Paths:
  209. "base": path.Base,
  210. "dir": path.Dir,
  211. "clean": path.Clean,
  212. "ext": path.Ext,
  213. "isAbs": path.IsAbs,
  214. // Encoding:
  215. "b64enc": base64encode,
  216. "b64dec": base64decode,
  217. "b32enc": base32encode,
  218. "b32dec": base32decode,
  219. // Data Structures:
  220. "tuple": list, // FIXME: with the addition of append/prepend these are no longer immutable.
  221. "list": list,
  222. "dict": dict,
  223. "set": set,
  224. "unset": unset,
  225. "hasKey": hasKey,
  226. "pluck": pluck,
  227. "keys": keys,
  228. "pick": pick,
  229. "omit": omit,
  230. "merge": merge,
  231. "mergeOverwrite": mergeOverwrite,
  232. "values": values,
  233. "append": push, "push": push,
  234. "prepend": prepend,
  235. "first": first,
  236. "rest": rest,
  237. "last": last,
  238. "initial": initial,
  239. "reverse": reverse,
  240. "uniq": uniq,
  241. "without": without,
  242. "has": has,
  243. "slice": slice,
  244. "concat": concat,
  245. // Crypto:
  246. "genPrivateKey": generatePrivateKey,
  247. "derivePassword": derivePassword,
  248. "buildCustomCert": buildCustomCertificate,
  249. "genCA": generateCertificateAuthority,
  250. "genSelfSignedCert": generateSelfSignedCertificate,
  251. "genSignedCert": generateSignedCertificate,
  252. "encryptAES": encryptAES,
  253. "decryptAES": decryptAES,
  254. // UUIDs:
  255. "uuidv4": uuidv4,
  256. // SemVer:
  257. "semver": semver,
  258. "semverCompare": semverCompare,
  259. // Flow Control:
  260. "fail": func(msg string) (string, error) { return "", errors.New(msg) },
  261. // Regex
  262. "regexMatch": regexMatch,
  263. "regexFindAll": regexFindAll,
  264. "regexFind": regexFind,
  265. "regexReplaceAll": regexReplaceAll,
  266. "regexReplaceAllLiteral": regexReplaceAllLiteral,
  267. "regexSplit": regexSplit,
  268. // URLs:
  269. "urlParse": urlParse,
  270. "urlJoin": urlJoin,
  271. }