functions.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. package sprig
  2. import (
  3. "errors"
  4. "html/template"
  5. "math/rand"
  6. "os"
  7. "path"
  8. "path/filepath"
  9. "reflect"
  10. "strconv"
  11. "strings"
  12. ttemplate "text/template"
  13. "time"
  14. )
  15. // FuncMap produces 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 environment 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. "randBytes",
  74. "uuidv4",
  75. // OS
  76. "env",
  77. "expandenv",
  78. // Network
  79. "getHostByName",
  80. }
  81. var genericMap = map[string]interface{}{
  82. "hello": func() string { return "Hello!" },
  83. // Date functions
  84. "ago": dateAgo,
  85. "date": date,
  86. "date_in_zone": dateInZone,
  87. "date_modify": dateModify,
  88. "dateInZone": dateInZone,
  89. "dateModify": dateModify,
  90. "duration": duration,
  91. "durationRound": durationRound,
  92. "htmlDate": htmlDate,
  93. "htmlDateInZone": htmlDateInZone,
  94. "must_date_modify": mustDateModify,
  95. "mustDateModify": mustDateModify,
  96. "mustToDate": mustToDate,
  97. "now": time.Now,
  98. "toDate": toDate,
  99. "unixEpoch": unixEpoch,
  100. // Strings
  101. "trunc": trunc,
  102. "trim": strings.TrimSpace,
  103. "upper": strings.ToUpper,
  104. "lower": strings.ToLower,
  105. "title": strings.Title,
  106. "substr": substring,
  107. // Switch order so that "foo" | repeat 5
  108. "repeat": func(count int, str string) string { return strings.Repeat(str, count) },
  109. // Deprecated: Use trimAll.
  110. "trimall": func(a, b string) string { return strings.Trim(b, a) },
  111. // Switch order so that "$foo" | trimall "$"
  112. "trimAll": func(a, b string) string { return strings.Trim(b, a) },
  113. "trimSuffix": func(a, b string) string { return strings.TrimSuffix(b, a) },
  114. "trimPrefix": func(a, b string) string { return strings.TrimPrefix(b, a) },
  115. // Switch order so that "foobar" | contains "foo"
  116. "contains": func(substr string, str string) bool { return strings.Contains(str, substr) },
  117. "hasPrefix": func(substr string, str string) bool { return strings.HasPrefix(str, substr) },
  118. "hasSuffix": func(substr string, str string) bool { return strings.HasSuffix(str, substr) },
  119. "quote": quote,
  120. "squote": squote,
  121. "cat": cat,
  122. "indent": indent,
  123. "nindent": nindent,
  124. "replace": replace,
  125. "plural": plural,
  126. "sha1sum": sha1sum,
  127. "sha256sum": sha256sum,
  128. "adler32sum": adler32sum,
  129. "toString": strval,
  130. // Wrap Atoi to stop errors.
  131. "atoi": func(a string) int { i, _ := strconv.Atoi(a); return i },
  132. "int64": toInt64,
  133. "int": toInt,
  134. "float64": toFloat64,
  135. "seq": seq,
  136. "toDecimal": toDecimal,
  137. //"gt": func(a, b int) bool {return a > b},
  138. //"gte": func(a, b int) bool {return a >= b},
  139. //"lt": func(a, b int) bool {return a < b},
  140. //"lte": func(a, b int) bool {return a <= b},
  141. // split "/" foo/bar returns map[int]string{0: foo, 1: bar}
  142. "split": split,
  143. "splitList": func(sep, orig string) []string { return strings.Split(orig, sep) },
  144. // splitn "/" foo/bar/fuu returns map[int]string{0: foo, 1: bar/fuu}
  145. "splitn": splitn,
  146. "toStrings": strslice,
  147. "until": until,
  148. "untilStep": untilStep,
  149. // VERY basic arithmetic.
  150. "add1": func(i interface{}) int64 { return toInt64(i) + 1 },
  151. "add": func(i ...interface{}) int64 {
  152. var a int64 = 0
  153. for _, b := range i {
  154. a += toInt64(b)
  155. }
  156. return a
  157. },
  158. "sub": func(a, b interface{}) int64 { return toInt64(a) - toInt64(b) },
  159. "div": func(a, b interface{}) int64 { return toInt64(a) / toInt64(b) },
  160. "mod": func(a, b interface{}) int64 { return toInt64(a) % toInt64(b) },
  161. "mul": func(a interface{}, v ...interface{}) int64 {
  162. val := toInt64(a)
  163. for _, b := range v {
  164. val = val * toInt64(b)
  165. }
  166. return val
  167. },
  168. "randInt": func(min, max int) int { return rand.Intn(max-min) + min },
  169. "biggest": max,
  170. "max": max,
  171. "min": min,
  172. "maxf": maxf,
  173. "minf": minf,
  174. "ceil": ceil,
  175. "floor": floor,
  176. "round": round,
  177. // string slices. Note that we reverse the order b/c that's better
  178. // for template processing.
  179. "join": join,
  180. "sortAlpha": sortAlpha,
  181. // Defaults
  182. "default": dfault,
  183. "empty": empty,
  184. "coalesce": coalesce,
  185. "all": all,
  186. "any": any,
  187. "compact": compact,
  188. "mustCompact": mustCompact,
  189. "fromJson": fromJson,
  190. "toJson": toJson,
  191. "toPrettyJson": toPrettyJson,
  192. "toRawJson": toRawJson,
  193. "mustFromJson": mustFromJson,
  194. "mustToJson": mustToJson,
  195. "mustToPrettyJson": mustToPrettyJson,
  196. "mustToRawJson": mustToRawJson,
  197. "ternary": ternary,
  198. // Reflection
  199. "typeOf": typeOf,
  200. "typeIs": typeIs,
  201. "typeIsLike": typeIsLike,
  202. "kindOf": kindOf,
  203. "kindIs": kindIs,
  204. "deepEqual": reflect.DeepEqual,
  205. // OS:
  206. "env": os.Getenv,
  207. "expandenv": os.ExpandEnv,
  208. // Network:
  209. "getHostByName": getHostByName,
  210. // Paths:
  211. "base": path.Base,
  212. "dir": path.Dir,
  213. "clean": path.Clean,
  214. "ext": path.Ext,
  215. "isAbs": path.IsAbs,
  216. // Filepaths:
  217. "osBase": filepath.Base,
  218. "osClean": filepath.Clean,
  219. "osDir": filepath.Dir,
  220. "osExt": filepath.Ext,
  221. "osIsAbs": filepath.IsAbs,
  222. // Encoding:
  223. "b64enc": base64encode,
  224. "b64dec": base64decode,
  225. "b32enc": base32encode,
  226. "b32dec": base32decode,
  227. // Data Structures:
  228. "tuple": list, // FIXME: with the addition of append/prepend these are no longer immutable.
  229. "list": list,
  230. "dict": dict,
  231. "get": get,
  232. "set": set,
  233. "unset": unset,
  234. "hasKey": hasKey,
  235. "pluck": pluck,
  236. "keys": keys,
  237. "pick": pick,
  238. "omit": omit,
  239. "values": values,
  240. "append": push, "push": push,
  241. "mustAppend": mustPush, "mustPush": mustPush,
  242. "prepend": prepend,
  243. "mustPrepend": mustPrepend,
  244. "first": first,
  245. "mustFirst": mustFirst,
  246. "rest": rest,
  247. "mustRest": mustRest,
  248. "last": last,
  249. "mustLast": mustLast,
  250. "initial": initial,
  251. "mustInitial": mustInitial,
  252. "reverse": reverse,
  253. "mustReverse": mustReverse,
  254. "uniq": uniq,
  255. "mustUniq": mustUniq,
  256. "without": without,
  257. "mustWithout": mustWithout,
  258. "has": has,
  259. "mustHas": mustHas,
  260. "slice": slice,
  261. "mustSlice": mustSlice,
  262. "concat": concat,
  263. "dig": dig,
  264. "chunk": chunk,
  265. "mustChunk": mustChunk,
  266. // Flow Control:
  267. "fail": func(msg string) (string, error) { return "", errors.New(msg) },
  268. // Regex
  269. "regexMatch": regexMatch,
  270. "mustRegexMatch": mustRegexMatch,
  271. "regexFindAll": regexFindAll,
  272. "mustRegexFindAll": mustRegexFindAll,
  273. "regexFind": regexFind,
  274. "mustRegexFind": mustRegexFind,
  275. "regexReplaceAll": regexReplaceAll,
  276. "mustRegexReplaceAll": mustRegexReplaceAll,
  277. "regexReplaceAllLiteral": regexReplaceAllLiteral,
  278. "mustRegexReplaceAllLiteral": mustRegexReplaceAllLiteral,
  279. "regexSplit": regexSplit,
  280. "mustRegexSplit": mustRegexSplit,
  281. "regexQuoteMeta": regexQuoteMeta,
  282. // URLs:
  283. "urlParse": urlParse,
  284. "urlJoin": urlJoin,
  285. }