pax_global_header00006660000000000000000000000064145566752610014533gustar00rootroot0000000000000052 comment=39bacfdaf0b760c27bd33b881b880f504228da25 golang-github-go-task-slim-sprig-3.0.0/000077500000000000000000000000001455667526100177115ustar00rootroot00000000000000golang-github-go-task-slim-sprig-3.0.0/.editorconfig000066400000000000000000000003211455667526100223620ustar00rootroot00000000000000# editorconfig.org root = true [*] insert_final_newline = true charset = utf-8 trim_trailing_whitespace = true indent_style = tab indent_size = 8 [*.{md,yml,yaml,json}] indent_style = space indent_size = 2 golang-github-go-task-slim-sprig-3.0.0/.gitattributes000066400000000000000000000000141455667526100225770ustar00rootroot00000000000000* text=auto golang-github-go-task-slim-sprig-3.0.0/.github/000077500000000000000000000000001455667526100212515ustar00rootroot00000000000000golang-github-go-task-slim-sprig-3.0.0/.github/ISSUE_TEMPLATE.md000066400000000000000000000003731455667526100237610ustar00rootroot00000000000000 golang-github-go-task-slim-sprig-3.0.0/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000003001455667526100250430ustar00rootroot00000000000000 golang-github-go-task-slim-sprig-3.0.0/.github/workflows/000077500000000000000000000000001455667526100233065ustar00rootroot00000000000000golang-github-go-task-slim-sprig-3.0.0/.github/workflows/test.yaml000066400000000000000000000007451455667526100251570ustar00rootroot00000000000000on: [push, pull_request] name: Tests jobs: test: strategy: matrix: go-version: [1.17.x, 1.18.x, 1.19.x] platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: - name: Install Go uses: actions/setup-go@v3 with: go-version: ${{ matrix.go-version }} - name: Checkout code uses: actions/checkout@v3 - name: Test env: GO111MODULE: on run: go test -cover . golang-github-go-task-slim-sprig-3.0.0/.gitignore000066400000000000000000000000201455667526100216710ustar00rootroot00000000000000vendor/ /.glide golang-github-go-task-slim-sprig-3.0.0/CHANGELOG.md000066400000000000000000000322711455667526100215270ustar00rootroot00000000000000# Changelog ## Release 3.2.3 (2022-11-29) ### Changed - Updated docs (thanks @book987 @aJetHorn @neelayu @pellizzetti @apricote @SaigyoujiYuyuko233 @AlekSi) - #348: Updated huandu/xstrings which fixed a snake case bug (thanks @yxxhero) - #353: Updated masterminds/semver which included bug fixes - #354: Updated golang.org/x/crypto which included bug fixes ## Release 3.2.2 (2021-02-04) This is a re-release of 3.2.1 to satisfy something with the Go module system. ## Release 3.2.1 (2021-02-04) ### Changed - Upgraded `Masterminds/goutils` to `v1.1.1`. see the [Security Advisory](https://github.com/Masterminds/goutils/security/advisories/GHSA-xg2h-wx96-xgxr) ## Release 3.2.0 (2020-12-14) ### Added - #211: Added randInt function (thanks @kochurovro) - #223: Added fromJson and mustFromJson functions (thanks @mholt) - #242: Added a bcrypt function (thanks @robbiet480) - #253: Added randBytes function (thanks @MikaelSmith) - #254: Added dig function for dicts (thanks @nyarly) - #257: Added regexQuoteMeta for quoting regex metadata (thanks @rheaton) - #261: Added filepath functions osBase, osDir, osExt, osClean, osIsAbs (thanks @zugl) - #268: Added and and all functions for testing conditions (thanks @phuslu) - #181: Added float64 arithmetic addf, add1f, subf, divf, mulf, maxf, and minf (thanks @andrewmostello) - #265: Added chunk function to split array into smaller arrays (thanks @karelbilek) - #270: Extend certificate functions to handle non-RSA keys + add support for ed25519 keys (thanks @misberner) ### Changed - Removed testing and support for Go 1.12. ed25519 support requires Go 1.13 or newer - Using semver 3.1.1 and mergo 0.3.11 ### Fixed - #249: Fix htmlDateInZone example (thanks @spawnia) NOTE: The dependency github.com/imdario/mergo reverted the breaking change in 0.3.9 via 0.3.10 release. ## Release 3.1.0 (2020-04-16) NOTE: The dependency github.com/imdario/mergo made a behavior change in 0.3.9 that impacts sprig functionality. Do not use sprig with a version newer than 0.3.8. ### Added - #225: Added support for generating htpasswd hash (thanks @rustycl0ck) - #224: Added duration filter (thanks @frebib) - #205: Added `seq` function (thanks @thadc23) ### Changed - #203: Unlambda functions with correct signature (thanks @muesli) - #236: Updated the license formatting for GitHub display purposes - #238: Updated package dependency versions. Note, mergo not updated to 0.3.9 as it causes a breaking change for sprig. That issue is tracked at https://github.com/imdario/mergo/issues/139 ### Fixed - #229: Fix `seq` example in docs (thanks @kalmant) ## Release 3.0.2 (2019-12-13) ### Fixed - #220: Updating to semver v3.0.3 to fix issue with <= ranges - #218: fix typo elyptical->elliptic in ecdsa key description (thanks @laverya) ## Release 3.0.1 (2019-12-08) ### Fixed - #212: Updated semver fixing broken constraint checking with ^0.0 ## Release 3.0.0 (2019-10-02) ### Added - #187: Added durationRound function (thanks @yjp20) - #189: Added numerous template functions that return errors rather than panic (thanks @nrvnrvn) - #193: Added toRawJson support (thanks @Dean-Coakley) - #197: Added get support to dicts (thanks @Dean-Coakley) ### Changed - #186: Moving dependency management to Go modules - #186: Updated semver to v3. This has changes in the way ^ is handled - #194: Updated documentation on merging and how it copies. Added example using deepCopy - #196: trunc now supports negative values (thanks @Dean-Coakley) ## Release 2.22.0 (2019-10-02) ### Added - #173: Added getHostByName function to resolve dns names to ips (thanks @fcgravalos) - #195: Added deepCopy function for use with dicts ### Changed - Updated merge and mergeOverwrite documentation to explain copying and how to use deepCopy with it ## Release 2.21.0 (2019-09-18) ### Added - #122: Added encryptAES/decryptAES functions (thanks @n0madic) - #128: Added toDecimal support (thanks @Dean-Coakley) - #169: Added list contcat (thanks @astorath) - #174: Added deepEqual function (thanks @bonifaido) - #170: Added url parse and join functions (thanks @astorath) ### Changed - #171: Updated glide config for Google UUID to v1 and to add ranges to semver and testify ### Fixed - #172: Fix semver wildcard example (thanks @piepmatz) - #175: Fix dateInZone doc example (thanks @s3than) ## Release 2.20.0 (2019-06-18) ### Added - #164: Adding function to get unix epoch for a time (@mattfarina) - #166: Adding tests for date_in_zone (@mattfarina) ### Changed - #144: Fix function comments based on best practices from Effective Go (@CodeLingoTeam) - #150: Handles pointer type for time.Time in "htmlDate" (@mapreal19) - #161, #157, #160, #153, #158, #156, #155, #159, #152 documentation updates (@badeadan) ### Fixed ## Release 2.19.0 (2019-03-02) IMPORTANT: This release reverts a change from 2.18.0 In the previous release (2.18), we prematurely merged a partial change to the crypto functions that led to creating two sets of crypto functions (I blame @technosophos -- since that's me). This release rolls back that change, and does what was originally intended: It alters the existing crypto functions to use secure random. We debated whether this classifies as a change worthy of major revision, but given the proximity to the last release, we have decided that treating 2.18 as a faulty release is the correct course of action. We apologize for any inconvenience. ### Changed - Fix substr panic 35fb796 (Alexey igrychev) - Remove extra period 1eb7729 (Matthew Lorimor) - Make random string functions use crypto by default 6ceff26 (Matthew Lorimor) - README edits/fixes/suggestions 08fe136 (Lauri Apple) ## Release 2.18.0 (2019-02-12) ### Added - Added mergeOverwrite function - cryptographic functions that use secure random (see fe1de12) ### Changed - Improve documentation of regexMatch function, resolves #139 90b89ce (Jan Tagscherer) - Handle has for nil list 9c10885 (Daniel Cohen) - Document behaviour of mergeOverwrite fe0dbe9 (Lukas Rieder) - doc: adds missing documentation. 4b871e6 (Fernandez Ludovic) - Replace outdated goutils imports 01893d2 (Matthew Lorimor) - Surface crypto secure random strings from goutils fe1de12 (Matthew Lorimor) - Handle untyped nil values as paramters to string functions 2b2ec8f (Morten Torkildsen) ### Fixed - Fix dict merge issue and provide mergeOverwrite .dst .src1 to overwrite from src -> dst 4c59c12 (Lukas Rieder) - Fix substr var names and comments d581f80 (Dean Coakley) - Fix substr documentation 2737203 (Dean Coakley) ## Release 2.17.1 (2019-01-03) ### Fixed The 2.17.0 release did not have a version pinned for xstrings, which caused compilation failures when xstrings < 1.2 was used. This adds the correct version string to glide.yaml. ## Release 2.17.0 (2019-01-03) ### Added - adds alder32sum function and test 6908fc2 (marshallford) - Added kebabcase function ca331a1 (Ilyes512) ### Changed - Update goutils to 1.1.0 4e1125d (Matt Butcher) ### Fixed - Fix 'has' documentation e3f2a85 (dean-coakley) - docs(dict): fix typo in pick example dc424f9 (Dustin Specker) - fixes spelling errors... not sure how that happened 4cf188a (marshallford) ## Release 2.16.0 (2018-08-13) ### Added - add splitn function fccb0b0 (Helgi Þorbjörnsson) - Add slice func df28ca7 (gongdo) - Generate serial number a3bdffd (Cody Coons) - Extract values of dict with values function df39312 (Lawrence Jones) ### Changed - Modify panic message for list.slice ae38335 (gongdo) - Minor improvement in code quality - Removed an unreachable piece of code at defaults.go#L26:6 - Resolve formatting issues. 5834241 (Abhishek Kashyap) - Remove duplicated documentation 1d97af1 (Matthew Fisher) - Test on go 1.11 49df809 (Helgi Þormar Þorbjörnsson) ### Fixed - Fix file permissions c5f40b5 (gongdo) - Fix example for buildCustomCert 7779e0d (Tin Lam) ## Release 2.15.0 (2018-04-02) ### Added - #68 and #69: Add json helpers to docs (thanks @arunvelsriram) - #66: Add ternary function (thanks @binoculars) - #67: Allow keys function to take multiple dicts (thanks @binoculars) - #89: Added sha1sum to crypto function (thanks @benkeil) - #81: Allow customizing Root CA that used by genSignedCert (thanks @chenzhiwei) - #92: Add travis testing for go 1.10 - #93: Adding appveyor config for windows testing ### Changed - #90: Updating to more recent dependencies - #73: replace satori/go.uuid with google/uuid (thanks @petterw) ### Fixed - #76: Fixed documentation typos (thanks @Thiht) - Fixed rounding issue on the `ago` function. Note, the removes support for Go 1.8 and older ## Release 2.14.1 (2017-12-01) ### Fixed - #60: Fix typo in function name documentation (thanks @neil-ca-moore) - #61: Removing line with {{ due to blocking github pages genertion - #64: Update the list functions to handle int, string, and other slices for compatibility ## Release 2.14.0 (2017-10-06) This new version of Sprig adds a set of functions for generating and working with SSL certificates. - `genCA` generates an SSL Certificate Authority - `genSelfSignedCert` generates an SSL self-signed certificate - `genSignedCert` generates an SSL certificate and key based on a given CA ## Release 2.13.0 (2017-09-18) This release adds new functions, including: - `regexMatch`, `regexFindAll`, `regexFind`, `regexReplaceAll`, `regexReplaceAllLiteral`, and `regexSplit` to work with regular expressions - `floor`, `ceil`, and `round` math functions - `toDate` converts a string to a date - `nindent` is just like `indent` but also prepends a new line - `ago` returns the time from `time.Now` ### Added - #40: Added basic regex functionality (thanks @alanquillin) - #41: Added ceil floor and round functions (thanks @alanquillin) - #48: Added toDate function (thanks @andreynering) - #50: Added nindent function (thanks @binoculars) - #46: Added ago function (thanks @slayer) ### Changed - #51: Updated godocs to include new string functions (thanks @curtisallen) - #49: Added ability to merge multiple dicts (thanks @binoculars) ## Release 2.12.0 (2017-05-17) - `snakecase`, `camelcase`, and `shuffle` are three new string functions - `fail` allows you to bail out of a template render when conditions are not met ## Release 2.11.0 (2017-05-02) - Added `toJson` and `toPrettyJson` - Added `merge` - Refactored documentation ## Release 2.10.0 (2017-03-15) - Added `semver` and `semverCompare` for Semantic Versions - `list` replaces `tuple` - Fixed issue with `join` - Added `first`, `last`, `intial`, `rest`, `prepend`, `append`, `toString`, `toStrings`, `sortAlpha`, `reverse`, `coalesce`, `pluck`, `pick`, `compact`, `keys`, `omit`, `uniq`, `has`, `without` ## Release 2.9.0 (2017-02-23) - Added `splitList` to split a list - Added crypto functions of `genPrivateKey` and `derivePassword` ## Release 2.8.0 (2016-12-21) - Added access to several path functions (`base`, `dir`, `clean`, `ext`, and `abs`) - Added functions for _mutating_ dictionaries (`set`, `unset`, `hasKey`) ## Release 2.7.0 (2016-12-01) - Added `sha256sum` to generate a hash of an input - Added functions to convert a numeric or string to `int`, `int64`, `float64` ## Release 2.6.0 (2016-10-03) - Added a `uuidv4` template function for generating UUIDs inside of a template. ## Release 2.5.0 (2016-08-19) - New `trimSuffix`, `trimPrefix`, `hasSuffix`, and `hasPrefix` functions - New aliases have been added for a few functions that didn't follow the naming conventions (`trimAll` and `abbrevBoth`) - `trimall` and `abbrevboth` (notice the case) are deprecated and will be removed in 3.0.0 ## Release 2.4.0 (2016-08-16) - Adds two functions: `until` and `untilStep` ## Release 2.3.0 (2016-06-21) - cat: Concatenate strings with whitespace separators. - replace: Replace parts of a string: `replace " " "-" "Me First"` renders "Me-First" - plural: Format plurals: `len "foo" | plural "one foo" "many foos"` renders "many foos" - indent: Indent blocks of text in a way that is sensitive to "\n" characters. ## Release 2.2.0 (2016-04-21) - Added a `genPrivateKey` function (Thanks @bacongobbler) ## Release 2.1.0 (2016-03-30) - `default` now prints the default value when it does not receive a value down the pipeline. It is much safer now to do `{{.Foo | default "bar"}}`. - Added accessors for "hermetic" functions. These return only functions that, when given the same input, produce the same output. ## Release 2.0.0 (2016-03-29) Because we switched from `int` to `int64` as the return value for all integer math functions, the library's major version number has been incremented. - `min` complements `max` (formerly `biggest`) - `empty` indicates that a value is the empty value for its type - `tuple` creates a tuple inside of a template: `{{$t := tuple "a", "b" "c"}}` - `dict` creates a dictionary inside of a template `{{$d := dict "key1" "val1" "key2" "val2"}}` - Date formatters have been added for HTML dates (as used in `date` input fields) - Integer math functions can convert from a number of types, including `string` (via `strconv.ParseInt`). ## Release 1.2.0 (2016-02-01) - Added quote and squote - Added b32enc and b32dec - add now takes varargs - biggest now takes varargs ## Release 1.1.0 (2015-12-29) - Added #4: Added contains function. strings.Contains, but with the arguments switched to simplify common pipelines. (thanks krancour) - Added Travis-CI testing support ## Release 1.0.0 (2015-12-23) - Initial release golang-github-go-task-slim-sprig-3.0.0/LICENSE.txt000066400000000000000000000020441455667526100215340ustar00rootroot00000000000000Copyright (C) 2013-2020 Masterminds Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-go-task-slim-sprig-3.0.0/README.md000066400000000000000000000050501455667526100211700ustar00rootroot00000000000000# Slim-Sprig: Template functions for Go templates [![Go Reference](https://pkg.go.dev/badge/github.com/go-task/slim-sprig/v3.svg)](https://pkg.go.dev/github.com/go-task/slim-sprig/v3) Slim-Sprig is a fork of [Sprig](https://github.com/Masterminds/sprig), but with all functions that depend on external (non standard library) or crypto packages removed. The reason for this is to make this library more lightweight. Most of these functions (specially crypto ones) are not needed on most apps, but costs a lot in terms of binary size and compilation time. ## Usage **Template developers**: Please use Slim-Sprig's [function documentation](https://go-task.github.io/slim-sprig/) for detailed instructions and code snippets for the >100 template functions available. **Go developers**: If you'd like to include Slim-Sprig as a library in your program, our API documentation is available [at GoDoc.org](http://godoc.org/github.com/go-task/slim-sprig). For standard usage, read on. ### Load the Slim-Sprig library To load the Slim-Sprig `FuncMap`: ```go import ( "html/template" "github.com/go-task/slim-sprig" ) // This example illustrates that the FuncMap *must* be set before the // templates themselves are loaded. tpl := template.Must( template.New("base").Funcs(sprig.FuncMap()).ParseGlob("*.html") ) ``` ### Calling the functions inside of templates By convention, all functions are lowercase. This seems to follow the Go idiom for template functions (as opposed to template methods, which are TitleCase). For example, this: ``` {{ "hello!" | upper | repeat 5 }} ``` produces this: ``` HELLO!HELLO!HELLO!HELLO!HELLO! ``` ## Principles Driving Our Function Selection We followed these principles to decide which functions to add and how to implement them: - Use template functions to build layout. The following types of operations are within the domain of template functions: - Formatting - Layout - Simple type conversions - Utilities that assist in handling common formatting and layout needs (e.g. arithmetic) - Template functions should not return errors unless there is no way to print a sensible value. For example, converting a string to an integer should not produce an error if conversion fails. Instead, it should display a default value. - Simple math is necessary for grid layouts, pagers, and so on. Complex math (anything other than arithmetic) should be done outside of templates. - Template functions only deal with the data passed into them. They never retrieve data from a source. - Finally, do not override core Go template functions. golang-github-go-task-slim-sprig-3.0.0/Taskfile.yml000066400000000000000000000001751455667526100222010ustar00rootroot00000000000000# https://taskfile.dev version: '3' tasks: default: cmds: - task: test test: cmds: - go test -v . golang-github-go-task-slim-sprig-3.0.0/crypto.go000066400000000000000000000006631455667526100215650ustar00rootroot00000000000000package sprig import ( "crypto/sha1" "crypto/sha256" "encoding/hex" "fmt" "hash/adler32" ) func sha256sum(input string) string { hash := sha256.Sum256([]byte(input)) return hex.EncodeToString(hash[:]) } func sha1sum(input string) string { hash := sha1.Sum([]byte(input)) return hex.EncodeToString(hash[:]) } func adler32sum(input string) string { hash := adler32.Checksum([]byte(input)) return fmt.Sprintf("%d", hash) } golang-github-go-task-slim-sprig-3.0.0/crypto_test.go000066400000000000000000000013221455667526100226150ustar00rootroot00000000000000package sprig import ( "testing" ) var ( // fastCertKeyAlgos is the list of private key algorithms that are supported for certificate use, and // are fast to generate. fastCertKeyAlgos = []string{ "ecdsa", "ed25519", } ) func TestSha256Sum(t *testing.T) { tpl := `{{"abc" | sha256sum}}` if err := runt(tpl, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"); err != nil { t.Error(err) } } func TestSha1Sum(t *testing.T) { tpl := `{{"abc" | sha1sum}}` if err := runt(tpl, "a9993e364706816aba3e25717850c26c9cd0d89d"); err != nil { t.Error(err) } } func TestAdler32Sum(t *testing.T) { tpl := `{{"abc" | adler32sum}}` if err := runt(tpl, "38600999"); err != nil { t.Error(err) } } golang-github-go-task-slim-sprig-3.0.0/date.go000066400000000000000000000060551455667526100211630ustar00rootroot00000000000000package sprig import ( "strconv" "time" ) // Given a format and a date, format the date string. // // Date can be a `time.Time` or an `int, int32, int64`. // In the later case, it is treated as seconds since UNIX // epoch. func date(fmt string, date interface{}) string { return dateInZone(fmt, date, "Local") } func htmlDate(date interface{}) string { return dateInZone("2006-01-02", date, "Local") } func htmlDateInZone(date interface{}, zone string) string { return dateInZone("2006-01-02", date, zone) } func dateInZone(fmt string, date interface{}, zone string) string { var t time.Time switch date := date.(type) { default: t = time.Now() case time.Time: t = date case *time.Time: t = *date case int64: t = time.Unix(date, 0) case int: t = time.Unix(int64(date), 0) case int32: t = time.Unix(int64(date), 0) } loc, err := time.LoadLocation(zone) if err != nil { loc, _ = time.LoadLocation("UTC") } return t.In(loc).Format(fmt) } func dateModify(fmt string, date time.Time) time.Time { d, err := time.ParseDuration(fmt) if err != nil { return date } return date.Add(d) } func mustDateModify(fmt string, date time.Time) (time.Time, error) { d, err := time.ParseDuration(fmt) if err != nil { return time.Time{}, err } return date.Add(d), nil } func dateAgo(date interface{}) string { var t time.Time switch date := date.(type) { default: t = time.Now() case time.Time: t = date case int64: t = time.Unix(date, 0) case int: t = time.Unix(int64(date), 0) } // Drop resolution to seconds duration := time.Since(t).Round(time.Second) return duration.String() } func duration(sec interface{}) string { var n int64 switch value := sec.(type) { default: n = 0 case string: n, _ = strconv.ParseInt(value, 10, 64) case int64: n = value } return (time.Duration(n) * time.Second).String() } func durationRound(duration interface{}) string { var d time.Duration switch duration := duration.(type) { default: d = 0 case string: d, _ = time.ParseDuration(duration) case int64: d = time.Duration(duration) case time.Time: d = time.Since(duration) } u := uint64(d) neg := d < 0 if neg { u = -u } var ( year = uint64(time.Hour) * 24 * 365 month = uint64(time.Hour) * 24 * 30 day = uint64(time.Hour) * 24 hour = uint64(time.Hour) minute = uint64(time.Minute) second = uint64(time.Second) ) switch { case u > year: return strconv.FormatUint(u/year, 10) + "y" case u > month: return strconv.FormatUint(u/month, 10) + "mo" case u > day: return strconv.FormatUint(u/day, 10) + "d" case u > hour: return strconv.FormatUint(u/hour, 10) + "h" case u > minute: return strconv.FormatUint(u/minute, 10) + "m" case u > second: return strconv.FormatUint(u/second, 10) + "s" } return "0s" } func toDate(fmt, str string) time.Time { t, _ := time.ParseInLocation(fmt, str, time.Local) return t } func mustToDate(fmt, str string) (time.Time, error) { return time.ParseInLocation(fmt, str, time.Local) } func unixEpoch(date time.Time) string { return strconv.FormatInt(date.Unix(), 10) } golang-github-go-task-slim-sprig-3.0.0/date_test.go000066400000000000000000000064431455667526100222230ustar00rootroot00000000000000package sprig import ( "testing" "time" ) func TestHtmlDate(t *testing.T) { t.Skip() tpl := `{{ htmlDate 0}}` if err := runt(tpl, "1970-01-01"); err != nil { t.Error(err) } } func TestAgo(t *testing.T) { tpl := "{{ ago .Time }}" if err := runtv(tpl, "2m5s", map[string]interface{}{"Time": time.Now().Add(-125 * time.Second)}); err != nil { t.Error(err) } if err := runtv(tpl, "2h34m17s", map[string]interface{}{"Time": time.Now().Add(-(2*3600 + 34*60 + 17) * time.Second)}); err != nil { t.Error(err) } if err := runtv(tpl, "-5s", map[string]interface{}{"Time": time.Now().Add(5 * time.Second)}); err != nil { t.Error(err) } } func TestToDate(t *testing.T) { tpl := `{{toDate "2006-01-02" "2017-12-31" | date "02/01/2006"}}` if err := runt(tpl, "31/12/2017"); err != nil { t.Error(err) } } func TestUnixEpoch(t *testing.T) { tm, err := time.Parse("02 Jan 06 15:04:05 MST", "13 Jun 19 20:39:39 GMT") if err != nil { t.Error(err) } tpl := `{{unixEpoch .Time}}` if err = runtv(tpl, "1560458379", map[string]interface{}{"Time": tm}); err != nil { t.Error(err) } } func TestDateInZone(t *testing.T) { tm, err := time.Parse("02 Jan 06 15:04:05 MST", "13 Jun 19 20:39:39 GMT") if err != nil { t.Error(err) } tpl := `{{ date_in_zone "02 Jan 06 15:04 -0700" .Time "UTC" }}` // Test time.Time input if err = runtv(tpl, "13 Jun 19 20:39 +0000", map[string]interface{}{"Time": tm}); err != nil { t.Error(err) } // Test pointer to time.Time input if err = runtv(tpl, "13 Jun 19 20:39 +0000", map[string]interface{}{"Time": &tm}); err != nil { t.Error(err) } // Test no time input. This should be close enough to time.Now() we can test loc, _ := time.LoadLocation("UTC") if err = runtv(tpl, time.Now().In(loc).Format("02 Jan 06 15:04 -0700"), map[string]interface{}{"Time": ""}); err != nil { t.Error(err) } // Test unix timestamp as int64 if err = runtv(tpl, "13 Jun 19 20:39 +0000", map[string]interface{}{"Time": int64(1560458379)}); err != nil { t.Error(err) } // Test unix timestamp as int32 if err = runtv(tpl, "13 Jun 19 20:39 +0000", map[string]interface{}{"Time": int32(1560458379)}); err != nil { t.Error(err) } // Test unix timestamp as int if err = runtv(tpl, "13 Jun 19 20:39 +0000", map[string]interface{}{"Time": int(1560458379)}); err != nil { t.Error(err) } // Test case of invalid timezone tpl = `{{ date_in_zone "02 Jan 06 15:04 -0700" .Time "foobar" }}` if err = runtv(tpl, "13 Jun 19 20:39 +0000", map[string]interface{}{"Time": tm}); err != nil { t.Error(err) } } func TestDuration(t *testing.T) { tpl := "{{ duration .Secs }}" if err := runtv(tpl, "1m1s", map[string]interface{}{"Secs": "61"}); err != nil { t.Error(err) } if err := runtv(tpl, "1h0m0s", map[string]interface{}{"Secs": "3600"}); err != nil { t.Error(err) } // 1d2h3m4s but go is opinionated if err := runtv(tpl, "26h3m4s", map[string]interface{}{"Secs": "93784"}); err != nil { t.Error(err) } } func TestDurationRound(t *testing.T) { tpl := "{{ durationRound .Time }}" if err := runtv(tpl, "2h", map[string]interface{}{"Time": "2h5s"}); err != nil { t.Error(err) } if err := runtv(tpl, "1d", map[string]interface{}{"Time": "24h5s"}); err != nil { t.Error(err) } if err := runtv(tpl, "3mo", map[string]interface{}{"Time": "2400h5s"}); err != nil { t.Error(err) } } golang-github-go-task-slim-sprig-3.0.0/defaults.go000066400000000000000000000074251455667526100220570ustar00rootroot00000000000000package sprig import ( "bytes" "encoding/json" "math/rand" "reflect" "strings" "time" ) func init() { rand.Seed(time.Now().UnixNano()) } // dfault checks whether `given` is set, and returns default if not set. // // This returns `d` if `given` appears not to be set, and `given` otherwise. // // For numeric types 0 is unset. // For strings, maps, arrays, and slices, len() = 0 is considered unset. // For bool, false is unset. // Structs are never considered unset. // // For everything else, including pointers, a nil value is unset. func dfault(d interface{}, given ...interface{}) interface{} { if empty(given) || empty(given[0]) { return d } return given[0] } // empty returns true if the given value has the zero value for its type. func empty(given interface{}) bool { g := reflect.ValueOf(given) if !g.IsValid() { return true } // Basically adapted from text/template.isTrue switch g.Kind() { default: return g.IsNil() case reflect.Array, reflect.Slice, reflect.Map, reflect.String: return g.Len() == 0 case reflect.Bool: return !g.Bool() case reflect.Complex64, reflect.Complex128: return g.Complex() == 0 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return g.Int() == 0 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return g.Uint() == 0 case reflect.Float32, reflect.Float64: return g.Float() == 0 case reflect.Struct: return false } } // coalesce returns the first non-empty value. func coalesce(v ...interface{}) interface{} { for _, val := range v { if !empty(val) { return val } } return nil } // all returns true if empty(x) is false for all values x in the list. // If the list is empty, return true. func all(v ...interface{}) bool { for _, val := range v { if empty(val) { return false } } return true } // any returns true if empty(x) is false for any x in the list. // If the list is empty, return false. func any(v ...interface{}) bool { for _, val := range v { if !empty(val) { return true } } return false } // fromJson decodes JSON into a structured value, ignoring errors. func fromJson(v string) interface{} { output, _ := mustFromJson(v) return output } // mustFromJson decodes JSON into a structured value, returning errors. func mustFromJson(v string) (interface{}, error) { var output interface{} err := json.Unmarshal([]byte(v), &output) return output, err } // toJson encodes an item into a JSON string func toJson(v interface{}) string { output, _ := json.Marshal(v) return string(output) } func mustToJson(v interface{}) (string, error) { output, err := json.Marshal(v) if err != nil { return "", err } return string(output), nil } // toPrettyJson encodes an item into a pretty (indented) JSON string func toPrettyJson(v interface{}) string { output, _ := json.MarshalIndent(v, "", " ") return string(output) } func mustToPrettyJson(v interface{}) (string, error) { output, err := json.MarshalIndent(v, "", " ") if err != nil { return "", err } return string(output), nil } // toRawJson encodes an item into a JSON string with no escaping of HTML characters. func toRawJson(v interface{}) string { output, err := mustToRawJson(v) if err != nil { panic(err) } return string(output) } // mustToRawJson encodes an item into a JSON string with no escaping of HTML characters. func mustToRawJson(v interface{}) (string, error) { buf := new(bytes.Buffer) enc := json.NewEncoder(buf) enc.SetEscapeHTML(false) err := enc.Encode(&v) if err != nil { return "", err } return strings.TrimSuffix(buf.String(), "\n"), nil } // ternary returns the first value if the last value is true, otherwise returns the second value. func ternary(vt interface{}, vf interface{}, v bool) interface{} { if v { return vt } return vf } golang-github-go-task-slim-sprig-3.0.0/defaults_test.go000066400000000000000000000120611455667526100231060ustar00rootroot00000000000000package sprig import ( "testing" "github.com/stretchr/testify/assert" ) func TestDefault(t *testing.T) { tpl := `{{"" | default "foo"}}` if err := runt(tpl, "foo"); err != nil { t.Error(err) } tpl = `{{default "foo" 234}}` if err := runt(tpl, "234"); err != nil { t.Error(err) } tpl = `{{default "foo" 2.34}}` if err := runt(tpl, "2.34"); err != nil { t.Error(err) } tpl = `{{ .Nothing | default "123" }}` if err := runt(tpl, "123"); err != nil { t.Error(err) } tpl = `{{ default "123" }}` if err := runt(tpl, "123"); err != nil { t.Error(err) } } func TestEmpty(t *testing.T) { tpl := `{{if empty 1}}1{{else}}0{{end}}` if err := runt(tpl, "0"); err != nil { t.Error(err) } tpl = `{{if empty 0}}1{{else}}0{{end}}` if err := runt(tpl, "1"); err != nil { t.Error(err) } tpl = `{{if empty ""}}1{{else}}0{{end}}` if err := runt(tpl, "1"); err != nil { t.Error(err) } tpl = `{{if empty 0.0}}1{{else}}0{{end}}` if err := runt(tpl, "1"); err != nil { t.Error(err) } tpl = `{{if empty false}}1{{else}}0{{end}}` if err := runt(tpl, "1"); err != nil { t.Error(err) } dict := map[string]interface{}{"top": map[string]interface{}{}} tpl = `{{if empty .top.NoSuchThing}}1{{else}}0{{end}}` if err := runtv(tpl, "1", dict); err != nil { t.Error(err) } tpl = `{{if empty .bottom.NoSuchThing}}1{{else}}0{{end}}` if err := runtv(tpl, "1", dict); err != nil { t.Error(err) } } func TestCoalesce(t *testing.T) { tests := map[string]string{ `{{ coalesce 1 }}`: "1", `{{ coalesce "" 0 nil 2 }}`: "2", `{{ $two := 2 }}{{ coalesce "" 0 nil $two }}`: "2", `{{ $two := 2 }}{{ coalesce "" $two 0 0 0 }}`: "2", `{{ $two := 2 }}{{ coalesce "" $two 3 4 5 }}`: "2", `{{ coalesce }}`: "", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } dict := map[string]interface{}{"top": map[string]interface{}{}} tpl := `{{ coalesce .top.NoSuchThing .bottom .bottom.dollar "airplane"}}` if err := runtv(tpl, "airplane", dict); err != nil { t.Error(err) } } func TestAll(t *testing.T) { tests := map[string]string{ `{{ all 1 }}`: "true", `{{ all "" 0 nil 2 }}`: "false", `{{ $two := 2 }}{{ all "" 0 nil $two }}`: "false", `{{ $two := 2 }}{{ all "" $two 0 0 0 }}`: "false", `{{ $two := 2 }}{{ all "" $two 3 4 5 }}`: "false", `{{ all }}`: "true", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } dict := map[string]interface{}{"top": map[string]interface{}{}} tpl := `{{ all .top.NoSuchThing .bottom .bottom.dollar "airplane"}}` if err := runtv(tpl, "false", dict); err != nil { t.Error(err) } } func TestAny(t *testing.T) { tests := map[string]string{ `{{ any 1 }}`: "true", `{{ any "" 0 nil 2 }}`: "true", `{{ $two := 2 }}{{ any "" 0 nil $two }}`: "true", `{{ $two := 2 }}{{ any "" $two 3 4 5 }}`: "true", `{{ $zero := 0 }}{{ any "" $zero 0 0 0 }}`: "false", `{{ any }}`: "false", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } dict := map[string]interface{}{"top": map[string]interface{}{}} tpl := `{{ any .top.NoSuchThing .bottom .bottom.dollar "airplane"}}` if err := runtv(tpl, "true", dict); err != nil { t.Error(err) } } func TestFromJson(t *testing.T) { dict := map[string]interface{}{"Input": `{"foo": 55}`} tpl := `{{.Input | fromJson}}` expected := `map[foo:55]` if err := runtv(tpl, expected, dict); err != nil { t.Error(err) } tpl = `{{(.Input | fromJson).foo}}` expected = `55` if err := runtv(tpl, expected, dict); err != nil { t.Error(err) } } func TestToJson(t *testing.T) { dict := map[string]interface{}{"Top": map[string]interface{}{"bool": true, "string": "test", "number": 42}} tpl := `{{.Top | toJson}}` expected := `{"bool":true,"number":42,"string":"test"}` if err := runtv(tpl, expected, dict); err != nil { t.Error(err) } } func TestToPrettyJson(t *testing.T) { dict := map[string]interface{}{"Top": map[string]interface{}{"bool": true, "string": "test", "number": 42}} tpl := `{{.Top | toPrettyJson}}` expected := `{ "bool": true, "number": 42, "string": "test" }` if err := runtv(tpl, expected, dict); err != nil { t.Error(err) } } func TestToRawJson(t *testing.T) { dict := map[string]interface{}{"Top": map[string]interface{}{"bool": true, "string": "test", "number": 42, "html": ""}} tpl := `{{.Top | toRawJson}}` expected := `{"bool":true,"html":"","number":42,"string":"test"}` if err := runtv(tpl, expected, dict); err != nil { t.Error(err) } } func TestTernary(t *testing.T) { tpl := `{{true | ternary "foo" "bar"}}` if err := runt(tpl, "foo"); err != nil { t.Error(err) } tpl = `{{ternary "foo" "bar" true}}` if err := runt(tpl, "foo"); err != nil { t.Error(err) } tpl = `{{false | ternary "foo" "bar"}}` if err := runt(tpl, "bar"); err != nil { t.Error(err) } tpl = `{{ternary "foo" "bar" false}}` if err := runt(tpl, "bar"); err != nil { t.Error(err) } } golang-github-go-task-slim-sprig-3.0.0/dict.go000066400000000000000000000044311455667526100211650ustar00rootroot00000000000000package sprig func get(d map[string]interface{}, key string) interface{} { if val, ok := d[key]; ok { return val } return "" } func set(d map[string]interface{}, key string, value interface{}) map[string]interface{} { d[key] = value return d } func unset(d map[string]interface{}, key string) map[string]interface{} { delete(d, key) return d } func hasKey(d map[string]interface{}, key string) bool { _, ok := d[key] return ok } func pluck(key string, d ...map[string]interface{}) []interface{} { res := []interface{}{} for _, dict := range d { if val, ok := dict[key]; ok { res = append(res, val) } } return res } func keys(dicts ...map[string]interface{}) []string { k := []string{} for _, dict := range dicts { for key := range dict { k = append(k, key) } } return k } func pick(dict map[string]interface{}, keys ...string) map[string]interface{} { res := map[string]interface{}{} for _, k := range keys { if v, ok := dict[k]; ok { res[k] = v } } return res } func omit(dict map[string]interface{}, keys ...string) map[string]interface{} { res := map[string]interface{}{} omit := make(map[string]bool, len(keys)) for _, k := range keys { omit[k] = true } for k, v := range dict { if _, ok := omit[k]; !ok { res[k] = v } } return res } func dict(v ...interface{}) map[string]interface{} { dict := map[string]interface{}{} lenv := len(v) for i := 0; i < lenv; i += 2 { key := strval(v[i]) if i+1 >= lenv { dict[key] = "" continue } dict[key] = v[i+1] } return dict } func values(dict map[string]interface{}) []interface{} { values := []interface{}{} for _, value := range dict { values = append(values, value) } return values } func dig(ps ...interface{}) (interface{}, error) { if len(ps) < 3 { panic("dig needs at least three arguments") } dict := ps[len(ps)-1].(map[string]interface{}) def := ps[len(ps)-2] ks := make([]string, len(ps)-2) for i := 0; i < len(ks); i++ { ks[i] = ps[i].(string) } return digFromDict(dict, def, ks) } func digFromDict(dict map[string]interface{}, d interface{}, ks []string) (interface{}, error) { k, ns := ks[0], ks[1:len(ks)] step, has := dict[k] if !has { return d, nil } if len(ns) == 0 { return step, nil } return digFromDict(step.(map[string]interface{}), d, ns) } golang-github-go-task-slim-sprig-3.0.0/dict_test.go000066400000000000000000000111301455667526100222160ustar00rootroot00000000000000package sprig import ( "strings" "testing" ) func TestDict(t *testing.T) { tpl := `{{$d := dict 1 2 "three" "four" 5}}{{range $k, $v := $d}}{{$k}}{{$v}}{{end}}` out, err := runRaw(tpl, nil) if err != nil { t.Error(err) } if len(out) != 12 { t.Errorf("Expected length 12, got %d", len(out)) } // dict does not guarantee ordering because it is backed by a map. if !strings.Contains(out, "12") { t.Error("Expected grouping 12") } if !strings.Contains(out, "threefour") { t.Error("Expected grouping threefour") } if !strings.Contains(out, "5") { t.Error("Expected 5") } tpl = `{{$t := dict "I" "shot" "the" "albatross"}}{{$t.the}} {{$t.I}}` if err := runt(tpl, "albatross shot"); err != nil { t.Error(err) } } func TestUnset(t *testing.T) { tpl := `{{- $d := dict "one" 1 "two" 222222 -}} {{- $_ := unset $d "two" -}} {{- range $k, $v := $d}}{{$k}}{{$v}}{{- end -}} ` expect := "one1" if err := runt(tpl, expect); err != nil { t.Error(err) } } func TestHasKey(t *testing.T) { tpl := `{{- $d := dict "one" 1 "two" 222222 -}} {{- if hasKey $d "one" -}}1{{- end -}} ` expect := "1" if err := runt(tpl, expect); err != nil { t.Error(err) } } func TestPluck(t *testing.T) { tpl := ` {{- $d := dict "one" 1 "two" 222222 -}} {{- $d2 := dict "one" 1 "two" 33333 -}} {{- $d3 := dict "one" 1 -}} {{- $d4 := dict "one" 1 "two" 4444 -}} {{- pluck "two" $d $d2 $d3 $d4 -}} ` expect := "[222222 33333 4444]" if err := runt(tpl, expect); err != nil { t.Error(err) } } func TestKeys(t *testing.T) { tests := map[string]string{ `{{ dict "foo" 1 "bar" 2 | keys | sortAlpha }}`: "[bar foo]", `{{ dict | keys }}`: "[]", `{{ keys (dict "foo" 1) (dict "bar" 2) (dict "bar" 3) | uniq | sortAlpha }}`: "[bar foo]", } for tpl, expect := range tests { if err := runt(tpl, expect); err != nil { t.Error(err) } } } func TestPick(t *testing.T) { tests := map[string]string{ `{{- $d := dict "one" 1 "two" 222222 }}{{ pick $d "two" | len -}}`: "1", `{{- $d := dict "one" 1 "two" 222222 }}{{ pick $d "two" -}}`: "map[two:222222]", `{{- $d := dict "one" 1 "two" 222222 }}{{ pick $d "one" "two" | len -}}`: "2", `{{- $d := dict "one" 1 "two" 222222 }}{{ pick $d "one" "two" "three" | len -}}`: "2", `{{- $d := dict }}{{ pick $d "two" | len -}}`: "0", } for tpl, expect := range tests { if err := runt(tpl, expect); err != nil { t.Error(err) } } } func TestOmit(t *testing.T) { tests := map[string]string{ `{{- $d := dict "one" 1 "two" 222222 }}{{ omit $d "one" | len -}}`: "1", `{{- $d := dict "one" 1 "two" 222222 }}{{ omit $d "one" -}}`: "map[two:222222]", `{{- $d := dict "one" 1 "two" 222222 }}{{ omit $d "one" "two" | len -}}`: "0", `{{- $d := dict "one" 1 "two" 222222 }}{{ omit $d "two" "three" | len -}}`: "1", `{{- $d := dict }}{{ omit $d "two" | len -}}`: "0", } for tpl, expect := range tests { if err := runt(tpl, expect); err != nil { t.Error(err) } } } func TestGet(t *testing.T) { tests := map[string]string{ `{{- $d := dict "one" 1 }}{{ get $d "one" -}}`: "1", `{{- $d := dict "one" 1 "two" "2" }}{{ get $d "two" -}}`: "2", `{{- $d := dict }}{{ get $d "two" -}}`: "", } for tpl, expect := range tests { if err := runt(tpl, expect); err != nil { t.Error(err) } } } func TestSet(t *testing.T) { tpl := `{{- $d := dict "one" 1 "two" 222222 -}} {{- $_ := set $d "two" 2 -}} {{- $_ := set $d "three" 3 -}} {{- if hasKey $d "one" -}}{{$d.one}}{{- end -}} {{- if hasKey $d "two" -}}{{$d.two}}{{- end -}} {{- if hasKey $d "three" -}}{{$d.three}}{{- end -}} ` expect := "123" if err := runt(tpl, expect); err != nil { t.Error(err) } } func TestValues(t *testing.T) { tests := map[string]string{ `{{- $d := dict "a" 1 "b" 2 }}{{ values $d | sortAlpha | join "," }}`: "1,2", `{{- $d := dict "a" "first" "b" 2 }}{{ values $d | sortAlpha | join "," }}`: "2,first", } for tpl, expect := range tests { if err := runt(tpl, expect); err != nil { t.Error(err) } } } func TestDig(t *testing.T) { tests := map[string]string{ `{{- $d := dict "a" (dict "b" (dict "c" 1)) }}{{ dig "a" "b" "c" "" $d }}`: "1", `{{- $d := dict "a" (dict "b" (dict "c" 1)) }}{{ dig "a" "b" "z" "2" $d }}`: "2", `{{ dict "a" 1 | dig "a" "" }}`: "1", `{{ dict "a" 1 | dig "z" "2" }}`: "2", } for tpl, expect := range tests { if err := runt(tpl, expect); err != nil { t.Error(err) } } } golang-github-go-task-slim-sprig-3.0.0/doc.go000066400000000000000000000012371455667526100210100ustar00rootroot00000000000000/* Package sprig provides template functions for Go. This package contains a number of utility functions for working with data inside of Go `html/template` and `text/template` files. To add these functions, use the `template.Funcs()` method: t := templates.New("foo").Funcs(sprig.FuncMap()) Note that you should add the function map before you parse any template files. In several cases, Sprig reverses the order of arguments from the way they appear in the standard library. This is to make it easier to pipe arguments into functions. See http://masterminds.github.io/sprig/ for more detailed documentation on each of the available functions. */ package sprig golang-github-go-task-slim-sprig-3.0.0/docs/000077500000000000000000000000001455667526100206415ustar00rootroot00000000000000golang-github-go-task-slim-sprig-3.0.0/docs/_config.yml000066400000000000000000000000311455667526100227620ustar00rootroot00000000000000theme: jekyll-theme-slategolang-github-go-task-slim-sprig-3.0.0/docs/conversion.md000066400000000000000000000017421455667526100233540ustar00rootroot00000000000000# Type Conversion Functions The following type conversion functions are provided by Sprig: - `atoi`: Convert a string to an integer. - `float64`: Convert to a `float64`. - `int`: Convert to an `int` at the system's width. - `int64`: Convert to an `int64`. - `toDecimal`: Convert a unix octal to a `int64`. - `toString`: Convert to a string. - `toStrings`: Convert a list, slice, or array to a list of strings. Only `atoi` requires that the input be a specific type. The others will attempt to convert from any type to the destination type. For example, `int64` can convert floats to ints, and it can also convert strings to ints. ## toStrings Given a list-like collection, produce a slice of strings. ``` list 1 2 3 | toStrings ``` The above converts `1` to `"1"`, `2` to `"2"`, and so on, and then returns them as a list. ## toDecimal Given a unix octal permission, produce a decimal. ``` "0777" | toDecimal ``` The above converts `0777` to `511` and returns the value as an int64. golang-github-go-task-slim-sprig-3.0.0/docs/crypto.md000066400000000000000000000010711455667526100225020ustar00rootroot00000000000000# Cryptographic and Security Functions Sprig provides a couple of advanced cryptographic functions. ## sha1sum The `sha1sum` function receives a string, and computes it's SHA1 digest. ``` sha1sum "Hello world!" ``` ## sha256sum The `sha256sum` function receives a string, and computes it's SHA256 digest. ``` sha256sum "Hello world!" ``` The above will compute the SHA 256 sum in an "ASCII armored" format that is safe to print. ## adler32sum The `adler32sum` function receives a string, and computes its Adler-32 checksum. ``` adler32sum "Hello world!" ``` golang-github-go-task-slim-sprig-3.0.0/docs/date.md000066400000000000000000000043631455667526100221060ustar00rootroot00000000000000# Date Functions ## now The current date/time. Use this in conjunction with other date functions. ## ago The `ago` function returns duration from time.Now in seconds resolution. ``` ago .CreatedAt" ``` returns in `time.Duration` String() format ``` 2h34m7s ``` ## date The `date` function formats a date. Format the date to YEAR-MONTH-DAY: ``` now | date "2006-01-02" ``` Date formatting in Go is a [little bit different](https://pauladamsmith.com/blog/2011/05/go_time.html). In short, take this as the base date: ``` Mon Jan 2 15:04:05 MST 2006 ``` Write it in the format you want. Above, `2006-01-02` is the same date, but in the format we want. ## dateInZone Same as `date`, but with a timezone. ``` dateInZone "2006-01-02" (now) "UTC" ``` ## duration Formats a given amount of seconds as a `time.Duration`. This returns 1m35s ``` duration "95" ``` ## durationRound Rounds a given duration to the most significant unit. Strings and `time.Duration` gets parsed as a duration, while a `time.Time` is calculated as the duration since. This return 2h ``` durationRound "2h10m5s" ``` This returns 3mo ``` durationRound "2400h10m5s" ``` ## unixEpoch Returns the seconds since the unix epoch for a `time.Time`. ``` now | unixEpoch ``` ## dateModify, mustDateModify The `dateModify` takes a modification and a date and returns the timestamp. Subtract an hour and thirty minutes from the current time: ``` now | date_modify "-1.5h" ``` If the modification format is wrong `dateModify` will return the date unmodified. `mustDateModify` will return an error otherwise. ## htmlDate The `htmlDate` function formats a date for inserting into an HTML date picker input field. ``` now | htmlDate ``` ## htmlDateInZone Same as htmlDate, but with a timezone. ``` htmlDateInZone (now) "UTC" ``` ## toDate, mustToDate `toDate` converts a string to a date. The first argument is the date layout and the second the date string. If the string can't be convert it returns the zero value. `mustToDate` will return an error in case the string cannot be converted. This is useful when you want to convert a string date to another format (using pipe). The example below converts "2017-12-31" to "31/12/2017". ``` toDate "2006-01-02" "2017-12-31" | date "02/01/2006" ``` golang-github-go-task-slim-sprig-3.0.0/docs/defaults.md000066400000000000000000000071171455667526100230000ustar00rootroot00000000000000# Default Functions Sprig provides tools for setting default values for templates. ## default To set a simple default value, use `default`: ``` default "foo" .Bar ``` In the above, if `.Bar` evaluates to a non-empty value, it will be used. But if it is empty, `foo` will be returned instead. The definition of "empty" depends on type: - Numeric: 0 - String: "" - Lists: `[]` - Dicts: `{}` - Boolean: `false` - And always `nil` (aka null) For structs, there is no definition of empty, so a struct will never return the default. ## empty The `empty` function returns `true` if the given value is considered empty, and `false` otherwise. The empty values are listed in the `default` section. ``` empty .Foo ``` Note that in Go template conditionals, emptiness is calculated for you. Thus, you rarely need `if empty .Foo`. Instead, just use `if .Foo`. ## coalesce The `coalesce` function takes a list of values and returns the first non-empty one. ``` coalesce 0 1 2 ``` The above returns `1`. This function is useful for scanning through multiple variables or values: ``` coalesce .name .parent.name "Matt" ``` The above will first check to see if `.name` is empty. If it is not, it will return that value. If it _is_ empty, `coalesce` will evaluate `.parent.name` for emptiness. Finally, if both `.name` and `.parent.name` are empty, it will return `Matt`. ## all The `all` function takes a list of values and returns true if all values are non-empty. ``` all 0 1 2 ``` The above returns `false`. This function is useful for evaluating multiple conditions of variables or values: ``` all (eq .Request.TLS.Version 0x0304) (.Request.ProtoAtLeast 2 0) (eq .Request.Method "POST") ``` The above will check http.Request is POST with tls 1.3 and http/2. ## any The `any` function takes a list of values and returns true if any value is non-empty. ``` any 0 1 2 ``` The above returns `true`. This function is useful for evaluating multiple conditions of variables or values: ``` any (eq .Request.Method "GET") (eq .Request.Method "POST") (eq .Request.Method "OPTIONS") ``` The above will check http.Request method is one of GET/POST/OPTIONS. ## fromJson, mustFromJson `fromJson` decodes a JSON document into a structure. If the input cannot be decoded as JSON the function will return an empty string. `mustFromJson` will return an error in case the JSON is invalid. ``` fromJson "{\"foo\": 55}" ``` ## toJson, mustToJson The `toJson` function encodes an item into a JSON string. If the item cannot be converted to JSON the function will return an empty string. `mustToJson` will return an error in case the item cannot be encoded in JSON. ``` toJson .Item ``` The above returns JSON string representation of `.Item`. ## toPrettyJson, mustToPrettyJson The `toPrettyJson` function encodes an item into a pretty (indented) JSON string. ``` toPrettyJson .Item ``` The above returns indented JSON string representation of `.Item`. ## toRawJson, mustToRawJson The `toRawJson` function encodes an item into JSON string with HTML characters unescaped. ``` toRawJson .Item ``` The above returns unescaped JSON string representation of `.Item`. ## ternary The `ternary` function takes two values, and a test value. If the test value is true, the first value will be returned. If the test value is empty, the second value will be returned. This is similar to the c ternary operator. ### true test value ``` ternary "foo" "bar" true ``` or ``` true | ternary "foo" "bar" ``` The above returns `"foo"`. ### false test value ``` ternary "foo" "bar" false ``` or ``` false | ternary "foo" "bar" ``` The above returns `"bar"`. golang-github-go-task-slim-sprig-3.0.0/docs/dicts.md000066400000000000000000000122141455667526100222710ustar00rootroot00000000000000# Dictionaries and Dict Functions Sprig provides a key/value storage type called a `dict` (short for "dictionary", as in Python). A `dict` is an _unorder_ type. The key to a dictionary **must be a string**. However, the value can be any type, even another `dict` or `list`. Unlike `list`s, `dict`s are not immutable. The `set` and `unset` functions will modify the contents of a dictionary. ## dict Creating dictionaries is done by calling the `dict` function and passing it a list of pairs. The following creates a dictionary with three items: ``` $myDict := dict "name1" "value1" "name2" "value2" "name3" "value 3" ``` ## get Given a map and a key, get the value from the map. ``` get $myDict "key1" ``` The above returns `"value1"` Note that if the key is not found, this operation will simply return `""`. No error will be generated. ## set Use `set` to add a new key/value pair to a dictionary. ``` $_ := set $myDict "name4" "value4" ``` Note that `set` _returns the dictionary_ (a requirement of Go template functions), so you may need to trap the value as done above with the `$_` assignment. ## unset Given a map and a key, delete the key from the map. ``` $_ := unset $myDict "name4" ``` As with `set`, this returns the dictionary. Note that if the key is not found, this operation will simply return. No error will be generated. ## hasKey The `hasKey` function returns `true` if the given dict contains the given key. ``` hasKey $myDict "name1" ``` If the key is not found, this returns `false`. ## pluck The `pluck` function makes it possible to give one key and multiple maps, and get a list of all of the matches: ``` pluck "name1" $myDict $myOtherDict ``` The above will return a `list` containing every found value (`[value1 otherValue1]`). If the give key is _not found_ in a map, that map will not have an item in the list (and the length of the returned list will be less than the number of dicts in the call to `pluck`. If the key is _found_ but the value is an empty value, that value will be inserted. A common idiom in Sprig templates is to uses `pluck... | first` to get the first matching key out of a collection of dictionaries. ## dig The `dig` function traverses a nested set of dicts, selecting keys from a list of values. It returns a default value if any of the keys are not found at the associated dict. ``` dig "user" "role" "humanName" "guest" $dict ``` Given a dict structured like ``` { user: { role: { humanName: "curator" } } } ``` the above would return `"curator"`. If the dict lacked even a `user` field, the result would be `"guest"`. Dig can be very useful in cases where you'd like to avoid guard clauses, especially since Go's template package's `and` doesn't shortcut. For instance `and a.maybeNil a.maybeNil.iNeedThis` will always evaluate `a.maybeNil.iNeedThis`, and panic if `a` lacks a `maybeNil` field.) `dig` accepts its dict argument last in order to support pipelining. For instance: ``` merge a b c | dig "one" "two" "three" "" ``` ## merge, mustMerge Merge two or more dictionaries into one, giving precedence to the dest dictionary: ``` $newdict := merge $dest $source1 $source2 ``` `mustMerge` will return an error in case of unsuccessful merge. ## mergeOverwrite, mustMergeOverwrite Merge two or more dictionaries into one, giving precedence from **right to left**, effectively overwriting values in the dest dictionary: Given: ``` dst: default: default overwrite: me key: true src: overwrite: overwritten key: false ``` will result in: ``` newdict: default: default overwrite: overwritten key: false ``` ``` $newdict := mergeOverwrite $dest $source1 $source2 ``` `mustMergeOverwrite` will return an error in case of unsuccessful merge. ## keys The `keys` function will return a `list` of all of the keys in one or more `dict` types. Since a dictionary is _unordered_, the keys will not be in a predictable order. They can be sorted with `sortAlpha`. ``` keys $myDict | sortAlpha ``` When supplying multiple dictionaries, the keys will be concatenated. Use the `uniq` function along with `sortAlpha` to get a unqiue, sorted list of keys. ``` keys $myDict $myOtherDict | uniq | sortAlpha ``` ## pick The `pick` function selects just the given keys out of a dictionary, creating a new `dict`. ``` $new := pick $myDict "name1" "name2" ``` The above returns `{name1: value1, name2: value2}` ## omit The `omit` function is similar to `pick`, except it returns a new `dict` with all the keys that _do not_ match the given keys. ``` $new := omit $myDict "name1" "name3" ``` The above returns `{name2: value2}` ## values The `values` function is similar to `keys`, except it returns a new `list` with all the values of the source `dict` (only one dictionary is supported). ``` $vals := values $myDict ``` The above returns `list["value1", "value2", "value 3"]`. Note that the `values` function gives no guarantees about the result ordering- if you care about this, then use `sortAlpha`. ## A Note on Dict Internals A `dict` is implemented in Go as a `map[string]interface{}`. Go developers can pass `map[string]interface{}` values into the context to make them available to templates as `dict`s. golang-github-go-task-slim-sprig-3.0.0/docs/encoding.md000066400000000000000000000002641455667526100227530ustar00rootroot00000000000000# Encoding Functions Sprig has the following encoding and decoding functions: - `b64enc`/`b64dec`: Encode or decode with Base64 - `b32enc`/`b32dec`: Encode or decode with Base32 golang-github-go-task-slim-sprig-3.0.0/docs/flow_control.md000066400000000000000000000004341455667526100236730ustar00rootroot00000000000000# Flow Control Functions ## fail Unconditionally returns an empty `string` and an `error` with the specified text. This is useful in scenarios where other conditionals have determined that template rendering should fail. ``` fail "Please accept the end user license agreement" ``` golang-github-go-task-slim-sprig-3.0.0/docs/index.md000066400000000000000000000023771455667526100223030ustar00rootroot00000000000000# Slim-Sprig Function Documentation The Slim-Sprig library provides over 70 template functions for Go's template language. - [String Functions](strings.md): `trim`, `plural`, etc. - [String List Functions](string_slice.md): `splitList`, `sortAlpha`, etc. - [Integer Math Functions](math.md): `add`, `max`, `mul`, etc. - [Integer Slice Functions](integer_slice.md): `until`, `untilStep` - [Date Functions](date.md): `now`, `date`, etc. - [Defaults Functions](defaults.md): `default`, `empty`, `coalesce`, `fromJson`, `toJson`, `toPrettyJson`, `toRawJson`, `ternary` - [Encoding Functions](encoding.md): `b64enc`, `b64dec`, etc. - [Lists and List Functions](lists.md): `list`, `first`, `uniq`, etc. - [Dictionaries and Dict Functions](dicts.md): `get`, `set`, `dict`, `hasKey`, `pluck`, `dig`, etc. - [Type Conversion Functions](conversion.md): `atoi`, `int64`, `toString`, etc. - [Path and Filepath Functions](paths.md): `base`, `dir`, `ext`, `clean`, `isAbs`, `osBase`, `osDir`, `osExt`, `osClean`, `osIsAbs` - [Flow Control Functions](flow_control.md): `fail` - Advanced Functions - [OS Functions](os.md): `env`, `expandenv` - [Reflection](reflection.md): `typeOf`, `kindIs`, `typeIsLike`, etc. - [Cryptographic and Security Functions](crypto.md): `sha256sum`, etc. golang-github-go-task-slim-sprig-3.0.0/docs/integer_slice.md000066400000000000000000000021121455667526100237730ustar00rootroot00000000000000# Integer Slice Functions ## until The `until` function builds a range of integers. ``` until 5 ``` The above generates the list `[0, 1, 2, 3, 4]`. This is useful for looping with `range $i, $e := until 5`. ## untilStep Like `until`, `untilStep` generates a list of counting integers. But it allows you to define a start, stop, and step: ``` untilStep 3 6 2 ``` The above will produce `[3 5]` by starting with 3, and adding 2 until it is equal or greater than 6. This is similar to Python's `range` function. ## seq Works like the bash `seq` command. * 1 parameter (end) - will generate all counting integers between 1 and `end` inclusive. * 2 parameters (start, end) - will generate all counting integers between `start` and `end` inclusive incrementing or decrementing by 1. * 3 parameters (start, step, end) - will generate all counting integers between `start` and `end` inclusive incrementing or decrementing by `step`. ``` seq 5 => 1 2 3 4 5 seq -3 => 1 0 -1 -2 -3 seq 0 2 => 0 1 2 seq 2 -2 => 2 1 0 -1 -2 seq 0 2 10 => 0 2 4 6 8 10 seq 0 -2 -5 => 0 -2 -4 ``` golang-github-go-task-slim-sprig-3.0.0/docs/lists.md000066400000000000000000000106301455667526100223210ustar00rootroot00000000000000# Lists and List Functions Sprig provides a simple `list` type that can contain arbitrary sequential lists of data. This is similar to arrays or slices, but lists are designed to be used as immutable data types. Create a list of integers: ``` $myList := list 1 2 3 4 5 ``` The above creates a list of `[1 2 3 4 5]`. ## first, mustFirst To get the head item on a list, use `first`. `first $myList` returns `1` `first` panics if there is a problem while `mustFirst` returns an error to the template engine if there is a problem. ## rest, mustRest To get the tail of the list (everything but the first item), use `rest`. `rest $myList` returns `[2 3 4 5]` `rest` panics if there is a problem while `mustRest` returns an error to the template engine if there is a problem. ## last, mustLast To get the last item on a list, use `last`: `last $myList` returns `5`. This is roughly analogous to reversing a list and then calling `first`. ## initial, mustInitial This compliments `last` by returning all _but_ the last element. `initial $myList` returns `[1 2 3 4]`. `initial` panics if there is a problem while `mustInitial` returns an error to the template engine if there is a problem. ## append, mustAppend Append a new item to an existing list, creating a new list. ``` $new = append $myList 6 ``` The above would set `$new` to `[1 2 3 4 5 6]`. `$myList` would remain unaltered. `append` panics if there is a problem while `mustAppend` returns an error to the template engine if there is a problem. ## prepend, mustPrepend Push an element onto the front of a list, creating a new list. ``` prepend $myList 0 ``` The above would produce `[0 1 2 3 4 5]`. `$myList` would remain unaltered. `prepend` panics if there is a problem while `mustPrepend` returns an error to the template engine if there is a problem. ## concat Concatenate arbitrary number of lists into one. ``` concat $myList ( list 6 7 ) ( list 8 ) ``` The above would produce `[1 2 3 4 5 6 7 8]`. `$myList` would remain unaltered. ## reverse, mustReverse Produce a new list with the reversed elements of the given list. ``` reverse $myList ``` The above would generate the list `[5 4 3 2 1]`. `reverse` panics if there is a problem while `mustReverse` returns an error to the template engine if there is a problem. ## uniq, mustUniq Generate a list with all of the duplicates removed. ``` list 1 1 1 2 | uniq ``` The above would produce `[1 2]` `uniq` panics if there is a problem while `mustUniq` returns an error to the template engine if there is a problem. ## without, mustWithout The `without` function filters items out of a list. ``` without $myList 3 ``` The above would produce `[1 2 4 5]` Without can take more than one filter: ``` without $myList 1 3 5 ``` That would produce `[2 4]` `without` panics if there is a problem while `mustWithout` returns an error to the template engine if there is a problem. ## has, mustHas Test to see if a list has a particular element. ``` has 4 $myList ``` The above would return `true`, while `has "hello" $myList` would return false. `has` panics if there is a problem while `mustHas` returns an error to the template engine if there is a problem. ## compact, mustCompact Accepts a list and removes entries with empty values. ``` $list := list 1 "a" "foo" "" $copy := compact $list ``` `compact` will return a new list with the empty (i.e., "") item removed. `compact` panics if there is a problem and `mustCompact` returns an error to the template engine if there is a problem. ## slice, mustSlice To get partial elements of a list, use `slice list [n] [m]`. It is equivalent of `list[n:m]`. - `slice $myList` returns `[1 2 3 4 5]`. It is same as `myList[:]`. - `slice $myList 3` returns `[4 5]`. It is same as `myList[3:]`. - `slice $myList 1 3` returns `[2 3]`. It is same as `myList[1:3]`. - `slice $myList 0 3` returns `[1 2 3]`. It is same as `myList[:3]`. `slice` panics if there is a problem while `mustSlice` returns an error to the template engine if there is a problem. ## chunk To split a list into chunks of given size, use `chunk size list`. This is useful for pagination. ``` chunk 3 (list 1 2 3 4 5 6 7 8) ``` This produces list of lists `[ [ 1 2 3 ] [ 4 5 6 ] [ 7 8 ] ]`. ## A Note on List Internals A list is implemented in Go as a `[]interface{}`. For Go developers embedding Sprig, you may pass `[]interface{}` items into your template context and be able to use all of the `list` functions on those items. golang-github-go-task-slim-sprig-3.0.0/docs/math.md000066400000000000000000000021411455667526100221120ustar00rootroot00000000000000# Integer Math Functions The following math functions operate on `int64` values. ## add Sum numbers with `add`. Accepts two or more inputs. ``` add 1 2 3 ``` ## add1 To increment by 1, use `add1` ## sub To subtract, use `sub` ## div Perform integer division with `div` ## mod Modulo with `mod` ## mul Multiply with `mul`. Accepts two or more inputs. ``` mul 1 2 3 ``` ## max Return the largest of a series of integers: This will return `3`: ``` max 1 2 3 ``` ## min Return the smallest of a series of integers. `min 1 2 3` will return `1` ## floor Returns the greatest float value less than or equal to input value `floor 123.9999` will return `123.0` ## ceil Returns the greatest float value greater than or equal to input value `ceil 123.001` will return `124.0` ## round Returns a float value with the remainder rounded to the given number to digits after the decimal point. `round 123.555555 3` will return `123.556` ## randInt Returns a random integer value from min (inclusive) to max (exclusive). ``` randInt 12 30 ``` The above will produce a random number in the range [12,30]. golang-github-go-task-slim-sprig-3.0.0/docs/mathf.md000066400000000000000000000021151455667526100222610ustar00rootroot00000000000000# Float Math Functions All math functions operate on `float64` values. ## addf Sum numbers with `addf` This will return `5.5`: ``` addf 1.5 2 2 ``` ## add1f To increment by 1, use `add1f` ## subf To subtract, use `subf` This is equivalent to `7.5 - 2 - 3` and will return `2.5`: ``` subf 7.5 2 3 ``` ## divf Perform integer division with `divf` This is equivalent to `10 / 2 / 4` and will return `1.25`: ``` divf 10 2 4 ``` ## mulf Multiply with `mulf` This will return `6`: ``` mulf 1.5 2 2 ``` ## maxf Return the largest of a series of floats: This will return `3`: ``` maxf 1 2.5 3 ``` ## minf Return the smallest of a series of floats. This will return `1.5`: ``` minf 1.5 2 3 ``` ## floor Returns the greatest float value less than or equal to input value `floor 123.9999` will return `123.0` ## ceil Returns the greatest float value greater than or equal to input value `ceil 123.001` will return `124.0` ## round Returns a float value with the remainder rounded to the given number to digits after the decimal point. `round 123.555555` will return `123.556` golang-github-go-task-slim-sprig-3.0.0/docs/network.md000066400000000000000000000003711455667526100226550ustar00rootroot00000000000000# Network Functions Sprig network manipulation functions. ## getHostByName The `getHostByName` receives a domain name and returns the ip address. ``` getHostByName "www.google.com" would return the corresponding ip address of www.google.com ``` golang-github-go-task-slim-sprig-3.0.0/docs/os.md000066400000000000000000000007111455667526100216030ustar00rootroot00000000000000# OS Functions _WARNING:_ These functions can lead to information leakage if not used appropriately. _WARNING:_ Some notable implementations of Sprig (such as [Kubernetes Helm](http://helm.sh) _do not provide these functions for security reasons_. ## env The `env` function reads an environment variable: ``` env "HOME" ``` ## expandenv To substitute environment variables in a string, use `expandenv`: ``` expandenv "Your path is set to $PATH" ``` golang-github-go-task-slim-sprig-3.0.0/docs/paths.md000066400000000000000000000046331455667526100223100ustar00rootroot00000000000000# Path and Filepath Functions While Sprig does not grant access to the filesystem, it does provide functions for working with strings that follow file path conventions. ## Paths Paths separated by the slash character (`/`), processed by the `path` package. Examples: - The [Linux](https://en.wikipedia.org/wiki/Linux) and [MacOS](https://en.wikipedia.org/wiki/MacOS) [filesystems](https://en.wikipedia.org/wiki/File_system): `/home/user/file`, `/etc/config`; - The path component of [URIs](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier): `https://example.com/some/content/`, `ftp://example.com/file/`. ### base Return the last element of a path. ``` base "foo/bar/baz" ``` The above prints "baz". ### dir Return the directory, stripping the last part of the path. So `dir "foo/bar/baz"` returns `foo/bar`. ### clean Clean up a path. ``` clean "foo/bar/../baz" ``` The above resolves the `..` and returns `foo/baz`. ### ext Return the file extension. ``` ext "foo.bar" ``` The above returns `.bar`. ### isAbs To check whether a path is absolute, use `isAbs`. ## Filepaths Paths separated by the `os.PathSeparator` variable, processed by the `path/filepath` package. These are the recommended functions to use when parsing paths of local filesystems, usually when dealing with local files, directories, etc. Examples: - Running on Linux or MacOS the filesystem path is separated by the slash character (`/`): `/home/user/file`, `/etc/config`; - Running on [Windows](https://en.wikipedia.org/wiki/Microsoft_Windows) the filesystem path is separated by the backslash character (`\`): `C:\Users\Username\`, `C:\Program Files\Application\`; ### osBase Return the last element of a filepath. ``` osBase "/foo/bar/baz" osBase "C:\\foo\\bar\\baz" ``` The above prints "baz" on Linux and Windows, respectively. ### osDir Return the directory, stripping the last part of the path. So `osDir "/foo/bar/baz"` returns `/foo/bar` on Linux, and `osDir "C:\\foo\\bar\\baz"` returns `C:\\foo\\bar` on Windows. ### osClean Clean up a path. ``` osClean "/foo/bar/../baz" osClean "C:\\foo\\bar\\..\\baz" ``` The above resolves the `..` and returns `foo/baz` on Linux and `C:\\foo\\baz` on Windows. ### osExt Return the file extension. ``` osExt "/foo.bar" osExt "C:\\foo.bar" ``` The above returns `.bar` on Linux and Windows, respectively. ### osIsAbs To check whether a file path is absolute, use `osIsAbs`. golang-github-go-task-slim-sprig-3.0.0/docs/reflection.md000066400000000000000000000026461455667526100233250ustar00rootroot00000000000000# Reflection Functions Sprig provides rudimentary reflection tools. These help advanced template developers understand the underlying Go type information for a particular value. Go has several primitive _kinds_, like `string`, `slice`, `int64`, and `bool`. Go has an open _type_ system that allows developers to create their own types. Sprig provides a set of functions for each. ## Kind Functions There are two Kind functions: `kindOf` returns the kind of an object. ``` kindOf "hello" ``` The above would return `string`. For simple tests (like in `if` blocks), the `kindIs` function will let you verify that a value is a particular kind: ``` kindIs "int" 123 ``` The above will return `true` ## Type Functions Types are slightly harder to work with, so there are three different functions: - `typeOf` returns the underlying type of a value: `typeOf $foo` - `typeIs` is like `kindIs`, but for types: `typeIs "*io.Buffer" $myVal` - `typeIsLike` works as `typeIs`, except that it also dereferences pointers. **Note:** None of these can test whether or not something implements a given interface, since doing so would require compiling the interface in ahead of time. ## deepEqual `deepEqual` returns true if two values are ["deeply equal"](https://golang.org/pkg/reflect/#DeepEqual) Works for non-primitive types as well (compared to the built-in `eq`). ``` deepEqual (list 1 2 3) (list 1 2 3) ``` The above will return `true` golang-github-go-task-slim-sprig-3.0.0/docs/string_slice.md000066400000000000000000000025611455667526100236540ustar00rootroot00000000000000# String Slice Functions These function operate on or generate slices of strings. In Go, a slice is a growable array. In Sprig, it's a special case of a `list`. ## join Join a list of strings into a single string, with the given separator. ``` list "hello" "world" | join "_" ``` The above will produce `hello_world` `join` will try to convert non-strings to a string value: ``` list 1 2 3 | join "+" ``` The above will produce `1+2+3` ## splitList and split Split a string into a list of strings: ``` splitList "$" "foo$bar$baz" ``` The above will return `[foo bar baz]` The older `split` function splits a string into a `dict`. It is designed to make it easy to use template dot notation for accessing members: ``` $a := split "$" "foo$bar$baz" ``` The above produces a map with index keys. `{_0: foo, _1: bar, _2: baz}` ``` $a._0 ``` The above produces `foo` ## splitn `splitn` function splits a string into a `dict`. It is designed to make it easy to use template dot notation for accessing members: ``` $a := splitn "$" 2 "foo$bar$baz" ``` The above produces a map with index keys. `{_0: foo, _1: bar$baz}` ``` $a._0 ``` The above produces `foo` ## sortAlpha The `sortAlpha` function sorts a list of strings into alphabetical (lexicographical) order. It does _not_ sort in place, but returns a sorted copy of the list, in keeping with the immutability of lists. golang-github-go-task-slim-sprig-3.0.0/docs/strings.md000066400000000000000000000142641455667526100226630ustar00rootroot00000000000000# String Functions Sprig has a number of string manipulation functions. ## trim The `trim` function removes space from either side of a string: ``` trim " hello " ``` The above produces `hello` ## trimAll Remove given characters from the front or back of a string: ``` trimAll "$" "$5.00" ``` The above returns `5.00` (as a string). ## trimSuffix Trim just the suffix from a string: ``` trimSuffix "-" "hello-" ``` The above returns `hello` ## trimPrefix Trim just the prefix from a string: ``` trimPrefix "-" "-hello" ``` The above returns `hello` ## upper Convert the entire string to uppercase: ``` upper "hello" ``` The above returns `HELLO` ## lower Convert the entire string to lowercase: ``` lower "HELLO" ``` The above returns `hello` ## title Convert to title case: ``` title "hello world" ``` The above returns `Hello World` ## repeat Repeat a string multiple times: ``` repeat 3 "hello" ``` The above returns `hellohellohello` ## substr Get a substring from a string. It takes three parameters: - start (int) - end (int) - string (string) ``` substr 0 5 "hello world" ``` The above returns `hello` ## trunc Truncate a string (and add no suffix) ``` trunc 5 "hello world" ``` The above produces `hello`. ``` trunc -5 "hello world" ``` The above produces `world`. ## contains Test to see if one string is contained inside of another: ``` contains "cat" "catch" ``` The above returns `true` because `catch` contains `cat`. ## hasPrefix and hasSuffix The `hasPrefix` and `hasSuffix` functions test whether a string has a given prefix or suffix: ``` hasPrefix "cat" "catch" ``` The above returns `true` because `catch` has the prefix `cat`. ## quote and squote These functions wrap a string in double quotes (`quote`) or single quotes (`squote`). ## cat The `cat` function concatenates multiple strings together into one, separating them with spaces: ``` cat "hello" "beautiful" "world" ``` The above produces `hello beautiful world` ## indent The `indent` function indents every line in a given string to the specified indent width. This is useful when aligning multi-line strings: ``` indent 4 $lots_of_text ``` The above will indent every line of text by 4 space characters. ## nindent The `nindent` function is the same as the indent function, but prepends a new line to the beginning of the string. ``` nindent 4 $lots_of_text ``` The above will indent every line of text by 4 space characters and add a new line to the beginning. ## replace Perform simple string replacement. It takes three arguments: - string to replace - string to replace with - source string ``` "I Am Henry VIII" | replace " " "-" ``` The above will produce `I-Am-Henry-VIII` ## plural Pluralize a string. ``` len $fish | plural "one anchovy" "many anchovies" ``` In the above, if the length of the string is 1, the first argument will be printed (`one anchovy`). Otherwise, the second argument will be printed (`many anchovies`). The arguments are: - singular string - plural string - length integer NOTE: Sprig does not currently support languages with more complex pluralization rules. And `0` is considered a plural because the English language treats it as such (`zero anchovies`). The Sprig developers are working on a solution for better internationalization. ## regexMatch, mustRegexMatch Returns true if the input string contains any match of the regular expression. ``` regexMatch "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$" "test@acme.com" ``` The above produces `true` `regexMatch` panics if there is a problem and `mustRegexMatch` returns an error to the template engine if there is a problem. ## regexFindAll, mustRegexFindAll Returns a slice of all matches of the regular expression in the input string. The last parameter n determines the number of substrings to return, where -1 means return all matches ``` regexFindAll "[2,4,6,8]" "123456789" -1 ``` The above produces `[2 4 6 8]` `regexFindAll` panics if there is a problem and `mustRegexFindAll` returns an error to the template engine if there is a problem. ## regexFind, mustRegexFind Return the first (left most) match of the regular expression in the input string ``` regexFind "[a-zA-Z][1-9]" "abcd1234" ``` The above produces `d1` `regexFind` panics if there is a problem and `mustRegexFind` returns an error to the template engine if there is a problem. ## regexReplaceAll, mustRegexReplaceAll Returns a copy of the input string, replacing matches of the Regexp with the replacement string replacement. Inside string replacement, $ signs are interpreted as in Expand, so for instance $1 represents the text of the first submatch ``` regexReplaceAll "a(x*)b" "-ab-axxb-" "${1}W" ``` The above produces `-W-xxW-` `regexReplaceAll` panics if there is a problem and `mustRegexReplaceAll` returns an error to the template engine if there is a problem. ## regexReplaceAllLiteral, mustRegexReplaceAllLiteral Returns a copy of the input string, replacing matches of the Regexp with the replacement string replacement The replacement string is substituted directly, without using Expand ``` regexReplaceAllLiteral "a(x*)b" "-ab-axxb-" "${1}" ``` The above produces `-${1}-${1}-` `regexReplaceAllLiteral` panics if there is a problem and `mustRegexReplaceAllLiteral` returns an error to the template engine if there is a problem. ## regexSplit, mustRegexSplit Slices the input string into substrings separated by the expression and returns a slice of the substrings between those expression matches. The last parameter `n` determines the number of substrings to return, where `-1` means return all matches ``` regexSplit "z+" "pizza" -1 ``` The above produces `[pi a]` `regexSplit` panics if there is a problem and `mustRegexSplit` returns an error to the template engine if there is a problem. ## regexQuoteMeta Returns a string that escapes all regular expression metacharacters inside the argument text; the returned string is a regular expression matching the literal text. ``` regexQuoteMeta "1.2.3" ``` The above produces `1\.2\.3` ## See Also... The [Conversion Functions](conversion.html) contain functions for converting strings. The [String Slice Functions](string_slice.html) contains functions for working with an array of strings. golang-github-go-task-slim-sprig-3.0.0/docs/url.md000066400000000000000000000012611455667526100217650ustar00rootroot00000000000000# URL Functions ## urlParse Parses string for URL and produces dict with URL parts ``` urlParse "http://admin:secret@server.com:8080/api?list=false#anchor" ``` The above returns a dict, containing URL object: ```yaml scheme: 'http' host: 'server.com:8080' path: '/api' query: 'list=false' opaque: nil fragment: 'anchor' userinfo: 'admin:secret' ``` For more info, check https://golang.org/pkg/net/url/#URL ## urlJoin Joins map (produced by `urlParse`) to produce URL string ``` urlJoin (dict "fragment" "fragment" "host" "host:80" "path" "/path" "query" "query" "scheme" "http") ``` The above returns the following string: ``` proto://host:80/path?query#fragment ``` golang-github-go-task-slim-sprig-3.0.0/example_test.go000066400000000000000000000010161455667526100227300ustar00rootroot00000000000000package sprig import ( "fmt" "os" "text/template" ) func Example() { // Set up variables and template. vars := map[string]interface{}{"Name": " John Jacob Jingleheimer Schmidt "} tpl := `Hello {{.Name | trim | lower}}` // Get the Sprig function map. fmap := TxtFuncMap() t := template.Must(template.New("test").Funcs(fmap).Parse(tpl)) err := t.Execute(os.Stdout, vars) if err != nil { fmt.Printf("Error during template execution: %s", err) return } // Output: // Hello john jacob jingleheimer schmidt } golang-github-go-task-slim-sprig-3.0.0/flow_control_test.go000066400000000000000000000004271455667526100240110ustar00rootroot00000000000000package sprig import ( "fmt" "testing" "github.com/stretchr/testify/assert" ) func TestFail(t *testing.T) { const msg = "This is an error!" tpl := fmt.Sprintf(`{{fail "%s"}}`, msg) _, err := runRaw(tpl, nil) assert.Error(t, err) assert.Contains(t, err.Error(), msg) } golang-github-go-task-slim-sprig-3.0.0/functions.go000066400000000000000000000202271455667526100222530ustar00rootroot00000000000000package sprig import ( "errors" "html/template" "math/rand" "os" "path" "path/filepath" "reflect" "strconv" "strings" ttemplate "text/template" "time" ) // 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 "trunc": trunc, "trim": strings.TrimSpace, "upper": strings.ToUpper, "lower": strings.ToLower, "title": strings.Title, "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) }, // 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 }, "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, // 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, "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, // 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, } golang-github-go-task-slim-sprig-3.0.0/functions_linux_test.go000066400000000000000000000011621455667526100245260ustar00rootroot00000000000000package sprig import ( "testing" "github.com/stretchr/testify/assert" ) func TestOsBase(t *testing.T) { assert.NoError(t, runt(`{{ osBase "foo/bar" }}`, "bar")) } func TestOsDir(t *testing.T) { assert.NoError(t, runt(`{{ osDir "foo/bar/baz" }}`, "foo/bar")) } func TestOsIsAbs(t *testing.T) { assert.NoError(t, runt(`{{ osIsAbs "/foo" }}`, "true")) assert.NoError(t, runt(`{{ osIsAbs "foo" }}`, "false")) } func TestOsClean(t *testing.T) { assert.NoError(t, runt(`{{ osClean "/foo/../foo/../bar" }}`, "/bar")) } func TestOsExt(t *testing.T) { assert.NoError(t, runt(`{{ osExt "/foo/bar/baz.txt" }}`, ".txt")) } golang-github-go-task-slim-sprig-3.0.0/functions_test.go000066400000000000000000000041141455667526100233070ustar00rootroot00000000000000package sprig import ( "bytes" "fmt" "os" "testing" "text/template" "github.com/stretchr/testify/assert" ) func TestEnv(t *testing.T) { os.Setenv("FOO", "bar") tpl := `{{env "FOO"}}` if err := runt(tpl, "bar"); err != nil { t.Error(err) } } func TestExpandEnv(t *testing.T) { os.Setenv("FOO", "bar") tpl := `{{expandenv "Hello $FOO"}}` if err := runt(tpl, "Hello bar"); err != nil { t.Error(err) } } func TestBase(t *testing.T) { assert.NoError(t, runt(`{{ base "foo/bar" }}`, "bar")) } func TestDir(t *testing.T) { assert.NoError(t, runt(`{{ dir "foo/bar/baz" }}`, "foo/bar")) } func TestIsAbs(t *testing.T) { assert.NoError(t, runt(`{{ isAbs "/foo" }}`, "true")) assert.NoError(t, runt(`{{ isAbs "foo" }}`, "false")) } func TestClean(t *testing.T) { assert.NoError(t, runt(`{{ clean "/foo/../foo/../bar" }}`, "/bar")) } func TestExt(t *testing.T) { assert.NoError(t, runt(`{{ ext "/foo/bar/baz.txt" }}`, ".txt")) } func TestRegex(t *testing.T) { assert.NoError(t, runt(`{{ regexQuoteMeta "1.2.3" }}`, "1\\.2\\.3")) assert.NoError(t, runt(`{{ regexQuoteMeta "pretzel" }}`, "pretzel")) } // runt runs a template and checks that the output exactly matches the expected string. func runt(tpl, expect string) error { return runtv(tpl, expect, map[string]string{}) } // runtv takes a template, and expected return, and values for substitution. // // It runs the template and verifies that the output is an exact match. func runtv(tpl, expect string, vars interface{}) error { fmap := TxtFuncMap() t := template.Must(template.New("test").Funcs(fmap).Parse(tpl)) var b bytes.Buffer err := t.Execute(&b, vars) if err != nil { return err } if expect != b.String() { return fmt.Errorf("Expected '%s', got '%s'", expect, b.String()) } return nil } // runRaw runs a template with the given variables and returns the result. func runRaw(tpl string, vars interface{}) (string, error) { fmap := TxtFuncMap() t := template.Must(template.New("test").Funcs(fmap).Parse(tpl)) var b bytes.Buffer err := t.Execute(&b, vars) if err != nil { return "", err } return b.String(), nil } golang-github-go-task-slim-sprig-3.0.0/functions_windows_test.go000066400000000000000000000012241455667526100250600ustar00rootroot00000000000000package sprig import ( "testing" "github.com/stretchr/testify/assert" ) func TestOsBase(t *testing.T) { assert.NoError(t, runt(`{{ osBase "C:\\foo\\bar" }}`, "bar")) } func TestOsDir(t *testing.T) { assert.NoError(t, runt(`{{ osDir "C:\\foo\\bar\\baz" }}`, "C:\\foo\\bar")) } func TestOsIsAbs(t *testing.T) { assert.NoError(t, runt(`{{ osIsAbs "C:\\foo" }}`, "true")) assert.NoError(t, runt(`{{ osIsAbs "foo" }}`, "false")) } func TestOsClean(t *testing.T) { assert.NoError(t, runt(`{{ osClean "C:\\foo\\..\\foo\\..\\bar" }}`, "C:\\bar")) } func TestOsExt(t *testing.T) { assert.NoError(t, runt(`{{ osExt "C:\\foo\\bar\\baz.txt" }}`, ".txt")) } golang-github-go-task-slim-sprig-3.0.0/go.mod000066400000000000000000000003601455667526100210160ustar00rootroot00000000000000module github.com/go-task/slim-sprig/v3 go 1.20 require github.com/stretchr/testify v1.8.4 require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) golang-github-go-task-slim-sprig-3.0.0/go.sum000066400000000000000000000015611455667526100210470ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-github-go-task-slim-sprig-3.0.0/list.go000066400000000000000000000215231455667526100212160ustar00rootroot00000000000000package sprig import ( "fmt" "math" "reflect" "sort" ) // Reflection is used in these functions so that slices and arrays of strings, // ints, and other types not implementing []interface{} can be worked with. // For example, this is useful if you need to work on the output of regexs. func list(v ...interface{}) []interface{} { return v } func push(list interface{}, v interface{}) []interface{} { l, err := mustPush(list, v) if err != nil { panic(err) } return l } func mustPush(list interface{}, v interface{}) ([]interface{}, error) { tp := reflect.TypeOf(list).Kind() switch tp { case reflect.Slice, reflect.Array: l2 := reflect.ValueOf(list) l := l2.Len() nl := make([]interface{}, l) for i := 0; i < l; i++ { nl[i] = l2.Index(i).Interface() } return append(nl, v), nil default: return nil, fmt.Errorf("Cannot push on type %s", tp) } } func prepend(list interface{}, v interface{}) []interface{} { l, err := mustPrepend(list, v) if err != nil { panic(err) } return l } func mustPrepend(list interface{}, v interface{}) ([]interface{}, error) { //return append([]interface{}{v}, list...) tp := reflect.TypeOf(list).Kind() switch tp { case reflect.Slice, reflect.Array: l2 := reflect.ValueOf(list) l := l2.Len() nl := make([]interface{}, l) for i := 0; i < l; i++ { nl[i] = l2.Index(i).Interface() } return append([]interface{}{v}, nl...), nil default: return nil, fmt.Errorf("Cannot prepend on type %s", tp) } } func chunk(size int, list interface{}) [][]interface{} { l, err := mustChunk(size, list) if err != nil { panic(err) } return l } func mustChunk(size int, list interface{}) ([][]interface{}, error) { tp := reflect.TypeOf(list).Kind() switch tp { case reflect.Slice, reflect.Array: l2 := reflect.ValueOf(list) l := l2.Len() cs := int(math.Floor(float64(l-1)/float64(size)) + 1) nl := make([][]interface{}, cs) for i := 0; i < cs; i++ { clen := size if i == cs-1 { clen = int(math.Floor(math.Mod(float64(l), float64(size)))) if clen == 0 { clen = size } } nl[i] = make([]interface{}, clen) for j := 0; j < clen; j++ { ix := i*size + j nl[i][j] = l2.Index(ix).Interface() } } return nl, nil default: return nil, fmt.Errorf("Cannot chunk type %s", tp) } } func last(list interface{}) interface{} { l, err := mustLast(list) if err != nil { panic(err) } return l } func mustLast(list interface{}) (interface{}, error) { tp := reflect.TypeOf(list).Kind() switch tp { case reflect.Slice, reflect.Array: l2 := reflect.ValueOf(list) l := l2.Len() if l == 0 { return nil, nil } return l2.Index(l - 1).Interface(), nil default: return nil, fmt.Errorf("Cannot find last on type %s", tp) } } func first(list interface{}) interface{} { l, err := mustFirst(list) if err != nil { panic(err) } return l } func mustFirst(list interface{}) (interface{}, error) { tp := reflect.TypeOf(list).Kind() switch tp { case reflect.Slice, reflect.Array: l2 := reflect.ValueOf(list) l := l2.Len() if l == 0 { return nil, nil } return l2.Index(0).Interface(), nil default: return nil, fmt.Errorf("Cannot find first on type %s", tp) } } func rest(list interface{}) []interface{} { l, err := mustRest(list) if err != nil { panic(err) } return l } func mustRest(list interface{}) ([]interface{}, error) { tp := reflect.TypeOf(list).Kind() switch tp { case reflect.Slice, reflect.Array: l2 := reflect.ValueOf(list) l := l2.Len() if l == 0 { return nil, nil } nl := make([]interface{}, l-1) for i := 1; i < l; i++ { nl[i-1] = l2.Index(i).Interface() } return nl, nil default: return nil, fmt.Errorf("Cannot find rest on type %s", tp) } } func initial(list interface{}) []interface{} { l, err := mustInitial(list) if err != nil { panic(err) } return l } func mustInitial(list interface{}) ([]interface{}, error) { tp := reflect.TypeOf(list).Kind() switch tp { case reflect.Slice, reflect.Array: l2 := reflect.ValueOf(list) l := l2.Len() if l == 0 { return nil, nil } nl := make([]interface{}, l-1) for i := 0; i < l-1; i++ { nl[i] = l2.Index(i).Interface() } return nl, nil default: return nil, fmt.Errorf("Cannot find initial on type %s", tp) } } func sortAlpha(list interface{}) []string { k := reflect.Indirect(reflect.ValueOf(list)).Kind() switch k { case reflect.Slice, reflect.Array: a := strslice(list) s := sort.StringSlice(a) s.Sort() return s } return []string{strval(list)} } func reverse(v interface{}) []interface{} { l, err := mustReverse(v) if err != nil { panic(err) } return l } func mustReverse(v interface{}) ([]interface{}, error) { tp := reflect.TypeOf(v).Kind() switch tp { case reflect.Slice, reflect.Array: l2 := reflect.ValueOf(v) l := l2.Len() // We do not sort in place because the incoming array should not be altered. nl := make([]interface{}, l) for i := 0; i < l; i++ { nl[l-i-1] = l2.Index(i).Interface() } return nl, nil default: return nil, fmt.Errorf("Cannot find reverse on type %s", tp) } } func compact(list interface{}) []interface{} { l, err := mustCompact(list) if err != nil { panic(err) } return l } func mustCompact(list interface{}) ([]interface{}, error) { tp := reflect.TypeOf(list).Kind() switch tp { case reflect.Slice, reflect.Array: l2 := reflect.ValueOf(list) l := l2.Len() nl := []interface{}{} var item interface{} for i := 0; i < l; i++ { item = l2.Index(i).Interface() if !empty(item) { nl = append(nl, item) } } return nl, nil default: return nil, fmt.Errorf("Cannot compact on type %s", tp) } } func uniq(list interface{}) []interface{} { l, err := mustUniq(list) if err != nil { panic(err) } return l } func mustUniq(list interface{}) ([]interface{}, error) { tp := reflect.TypeOf(list).Kind() switch tp { case reflect.Slice, reflect.Array: l2 := reflect.ValueOf(list) l := l2.Len() dest := []interface{}{} var item interface{} for i := 0; i < l; i++ { item = l2.Index(i).Interface() if !inList(dest, item) { dest = append(dest, item) } } return dest, nil default: return nil, fmt.Errorf("Cannot find uniq on type %s", tp) } } func inList(haystack []interface{}, needle interface{}) bool { for _, h := range haystack { if reflect.DeepEqual(needle, h) { return true } } return false } func without(list interface{}, omit ...interface{}) []interface{} { l, err := mustWithout(list, omit...) if err != nil { panic(err) } return l } func mustWithout(list interface{}, omit ...interface{}) ([]interface{}, error) { tp := reflect.TypeOf(list).Kind() switch tp { case reflect.Slice, reflect.Array: l2 := reflect.ValueOf(list) l := l2.Len() res := []interface{}{} var item interface{} for i := 0; i < l; i++ { item = l2.Index(i).Interface() if !inList(omit, item) { res = append(res, item) } } return res, nil default: return nil, fmt.Errorf("Cannot find without on type %s", tp) } } func has(needle interface{}, haystack interface{}) bool { l, err := mustHas(needle, haystack) if err != nil { panic(err) } return l } func mustHas(needle interface{}, haystack interface{}) (bool, error) { if haystack == nil { return false, nil } tp := reflect.TypeOf(haystack).Kind() switch tp { case reflect.Slice, reflect.Array: l2 := reflect.ValueOf(haystack) var item interface{} l := l2.Len() for i := 0; i < l; i++ { item = l2.Index(i).Interface() if reflect.DeepEqual(needle, item) { return true, nil } } return false, nil default: return false, fmt.Errorf("Cannot find has on type %s", tp) } } // $list := [1, 2, 3, 4, 5] // slice $list -> list[0:5] = list[:] // slice $list 0 3 -> list[0:3] = list[:3] // slice $list 3 5 -> list[3:5] // slice $list 3 -> list[3:5] = list[3:] func slice(list interface{}, indices ...interface{}) interface{} { l, err := mustSlice(list, indices...) if err != nil { panic(err) } return l } func mustSlice(list interface{}, indices ...interface{}) (interface{}, error) { tp := reflect.TypeOf(list).Kind() switch tp { case reflect.Slice, reflect.Array: l2 := reflect.ValueOf(list) l := l2.Len() if l == 0 { return nil, nil } var start, end int if len(indices) > 0 { start = toInt(indices[0]) } if len(indices) < 2 { end = l } else { end = toInt(indices[1]) } return l2.Slice(start, end).Interface(), nil default: return nil, fmt.Errorf("list should be type of slice or array but %s", tp) } } func concat(lists ...interface{}) interface{} { var res []interface{} for _, list := range lists { tp := reflect.TypeOf(list).Kind() switch tp { case reflect.Slice, reflect.Array: l2 := reflect.ValueOf(list) for i := 0; i < l2.Len(); i++ { res = append(res, l2.Index(i).Interface()) } default: panic(fmt.Sprintf("Cannot concat type %s as list", tp)) } } return res } golang-github-go-task-slim-sprig-3.0.0/list_test.go000066400000000000000000000316011455667526100222530ustar00rootroot00000000000000package sprig import ( "testing" "github.com/stretchr/testify/assert" ) func TestTuple(t *testing.T) { tpl := `{{$t := tuple 1 "a" "foo"}}{{index $t 2}}{{index $t 0 }}{{index $t 1}}` if err := runt(tpl, "foo1a"); err != nil { t.Error(err) } } func TestList(t *testing.T) { tpl := `{{$t := list 1 "a" "foo"}}{{index $t 2}}{{index $t 0 }}{{index $t 1}}` if err := runt(tpl, "foo1a"); err != nil { t.Error(err) } } func TestPush(t *testing.T) { // Named `append` in the function map tests := map[string]string{ `{{ $t := tuple 1 2 3 }}{{ append $t 4 | len }}`: "4", `{{ $t := tuple 1 2 3 4 }}{{ append $t 5 | join "-" }}`: "1-2-3-4-5", `{{ $t := regexSplit "/" "foo/bar/baz" -1 }}{{ append $t "qux" | join "-" }}`: "foo-bar-baz-qux", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestMustPush(t *testing.T) { // Named `append` in the function map tests := map[string]string{ `{{ $t := tuple 1 2 3 }}{{ mustAppend $t 4 | len }}`: "4", `{{ $t := tuple 1 2 3 4 }}{{ mustAppend $t 5 | join "-" }}`: "1-2-3-4-5", `{{ $t := regexSplit "/" "foo/bar/baz" -1 }}{{ mustPush $t "qux" | join "-" }}`: "foo-bar-baz-qux", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestChunk(t *testing.T) { tests := map[string]string{ `{{ tuple 1 2 3 4 5 6 7 | chunk 3 | len }}`: "3", `{{ tuple | chunk 3 | len }}`: "0", `{{ range ( tuple 1 2 3 4 5 6 7 8 9 | chunk 3 ) }}{{. | join "-"}}|{{end}}`: "1-2-3|4-5-6|7-8-9|", `{{ range ( tuple 1 2 3 4 5 6 7 8 | chunk 3 ) }}{{. | join "-"}}|{{end}}`: "1-2-3|4-5-6|7-8|", `{{ range ( tuple 1 2 | chunk 3 ) }}{{. | join "-"}}|{{end}}`: "1-2|", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestMustChunk(t *testing.T) { tests := map[string]string{ `{{ tuple 1 2 3 4 5 6 7 | mustChunk 3 | len }}`: "3", `{{ tuple | mustChunk 3 | len }}`: "0", `{{ range ( tuple 1 2 3 4 5 6 7 8 9 | mustChunk 3 ) }}{{. | join "-"}}|{{end}}`: "1-2-3|4-5-6|7-8-9|", `{{ range ( tuple 1 2 3 4 5 6 7 8 | mustChunk 3 ) }}{{. | join "-"}}|{{end}}`: "1-2-3|4-5-6|7-8|", `{{ range ( tuple 1 2 | mustChunk 3 ) }}{{. | join "-"}}|{{end}}`: "1-2|", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestPrepend(t *testing.T) { tests := map[string]string{ `{{ $t := tuple 1 2 3 }}{{ prepend $t 0 | len }}`: "4", `{{ $t := tuple 1 2 3 4 }}{{ prepend $t 0 | join "-" }}`: "0-1-2-3-4", `{{ $t := regexSplit "/" "foo/bar/baz" -1 }}{{ prepend $t "qux" | join "-" }}`: "qux-foo-bar-baz", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestMustPrepend(t *testing.T) { tests := map[string]string{ `{{ $t := tuple 1 2 3 }}{{ mustPrepend $t 0 | len }}`: "4", `{{ $t := tuple 1 2 3 4 }}{{ mustPrepend $t 0 | join "-" }}`: "0-1-2-3-4", `{{ $t := regexSplit "/" "foo/bar/baz" -1 }}{{ mustPrepend $t "qux" | join "-" }}`: "qux-foo-bar-baz", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestFirst(t *testing.T) { tests := map[string]string{ `{{ list 1 2 3 | first }}`: "1", `{{ list | first }}`: "", `{{ regexSplit "/src/" "foo/src/bar" -1 | first }}`: "foo", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestMustFirst(t *testing.T) { tests := map[string]string{ `{{ list 1 2 3 | mustFirst }}`: "1", `{{ list | mustFirst }}`: "", `{{ regexSplit "/src/" "foo/src/bar" -1 | mustFirst }}`: "foo", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestLast(t *testing.T) { tests := map[string]string{ `{{ list 1 2 3 | last }}`: "3", `{{ list | last }}`: "", `{{ regexSplit "/src/" "foo/src/bar" -1 | last }}`: "bar", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestMustLast(t *testing.T) { tests := map[string]string{ `{{ list 1 2 3 | mustLast }}`: "3", `{{ list | mustLast }}`: "", `{{ regexSplit "/src/" "foo/src/bar" -1 | mustLast }}`: "bar", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestInitial(t *testing.T) { tests := map[string]string{ `{{ list 1 2 3 | initial | len }}`: "2", `{{ list 1 2 3 | initial | last }}`: "2", `{{ list 1 2 3 | initial | first }}`: "1", `{{ list | initial }}`: "[]", `{{ regexSplit "/" "foo/bar/baz" -1 | initial }}`: "[foo bar]", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestMustInitial(t *testing.T) { tests := map[string]string{ `{{ list 1 2 3 | mustInitial | len }}`: "2", `{{ list 1 2 3 | mustInitial | last }}`: "2", `{{ list 1 2 3 | mustInitial | first }}`: "1", `{{ list | mustInitial }}`: "[]", `{{ regexSplit "/" "foo/bar/baz" -1 | mustInitial }}`: "[foo bar]", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestRest(t *testing.T) { tests := map[string]string{ `{{ list 1 2 3 | rest | len }}`: "2", `{{ list 1 2 3 | rest | last }}`: "3", `{{ list 1 2 3 | rest | first }}`: "2", `{{ list | rest }}`: "[]", `{{ regexSplit "/" "foo/bar/baz" -1 | rest }}`: "[bar baz]", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestMustRest(t *testing.T) { tests := map[string]string{ `{{ list 1 2 3 | mustRest | len }}`: "2", `{{ list 1 2 3 | mustRest | last }}`: "3", `{{ list 1 2 3 | mustRest | first }}`: "2", `{{ list | mustRest }}`: "[]", `{{ regexSplit "/" "foo/bar/baz" -1 | mustRest }}`: "[bar baz]", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestReverse(t *testing.T) { tests := map[string]string{ `{{ list 1 2 3 | reverse | first }}`: "3", `{{ list 1 2 3 | reverse | rest | first }}`: "2", `{{ list 1 2 3 | reverse | last }}`: "1", `{{ list 1 2 3 4 | reverse }}`: "[4 3 2 1]", `{{ list 1 | reverse }}`: "[1]", `{{ list | reverse }}`: "[]", `{{ regexSplit "/" "foo/bar/baz" -1 | reverse }}`: "[baz bar foo]", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestMustReverse(t *testing.T) { tests := map[string]string{ `{{ list 1 2 3 | mustReverse | first }}`: "3", `{{ list 1 2 3 | mustReverse | rest | first }}`: "2", `{{ list 1 2 3 | mustReverse | last }}`: "1", `{{ list 1 2 3 4 | mustReverse }}`: "[4 3 2 1]", `{{ list 1 | mustReverse }}`: "[1]", `{{ list | mustReverse }}`: "[]", `{{ regexSplit "/" "foo/bar/baz" -1 | mustReverse }}`: "[baz bar foo]", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestCompact(t *testing.T) { tests := map[string]string{ `{{ list 1 0 "" "hello" | compact }}`: `[1 hello]`, `{{ list "" "" | compact }}`: `[]`, `{{ list | compact }}`: `[]`, `{{ regexSplit "/" "foo//bar" -1 | compact }}`: "[foo bar]", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestMustCompact(t *testing.T) { tests := map[string]string{ `{{ list 1 0 "" "hello" | mustCompact }}`: `[1 hello]`, `{{ list "" "" | mustCompact }}`: `[]`, `{{ list | mustCompact }}`: `[]`, `{{ regexSplit "/" "foo//bar" -1 | mustCompact }}`: "[foo bar]", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestUniq(t *testing.T) { tests := map[string]string{ `{{ list 1 2 3 4 | uniq }}`: `[1 2 3 4]`, `{{ list "a" "b" "c" "d" | uniq }}`: `[a b c d]`, `{{ list 1 1 1 1 2 2 2 2 | uniq }}`: `[1 2]`, `{{ list "foo" 1 1 1 1 "foo" "foo" | uniq }}`: `[foo 1]`, `{{ list | uniq }}`: `[]`, `{{ regexSplit "/" "foo/foo/bar" -1 | uniq }}`: "[foo bar]", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestMustUniq(t *testing.T) { tests := map[string]string{ `{{ list 1 2 3 4 | mustUniq }}`: `[1 2 3 4]`, `{{ list "a" "b" "c" "d" | mustUniq }}`: `[a b c d]`, `{{ list 1 1 1 1 2 2 2 2 | mustUniq }}`: `[1 2]`, `{{ list "foo" 1 1 1 1 "foo" "foo" | mustUniq }}`: `[foo 1]`, `{{ list | mustUniq }}`: `[]`, `{{ regexSplit "/" "foo/foo/bar" -1 | mustUniq }}`: "[foo bar]", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestWithout(t *testing.T) { tests := map[string]string{ `{{ without (list 1 2 3 4) 1 }}`: `[2 3 4]`, `{{ without (list "a" "b" "c" "d") "a" }}`: `[b c d]`, `{{ without (list 1 1 1 1 2) 1 }}`: `[2]`, `{{ without (list) 1 }}`: `[]`, `{{ without (list 1 2 3) }}`: `[1 2 3]`, `{{ without list }}`: `[]`, `{{ without (regexSplit "/" "foo/bar/baz" -1 ) "foo" }}`: "[bar baz]", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestMustWithout(t *testing.T) { tests := map[string]string{ `{{ mustWithout (list 1 2 3 4) 1 }}`: `[2 3 4]`, `{{ mustWithout (list "a" "b" "c" "d") "a" }}`: `[b c d]`, `{{ mustWithout (list 1 1 1 1 2) 1 }}`: `[2]`, `{{ mustWithout (list) 1 }}`: `[]`, `{{ mustWithout (list 1 2 3) }}`: `[1 2 3]`, `{{ mustWithout list }}`: `[]`, `{{ mustWithout (regexSplit "/" "foo/bar/baz" -1 ) "foo" }}`: "[bar baz]", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestHas(t *testing.T) { tests := map[string]string{ `{{ list 1 2 3 | has 1 }}`: `true`, `{{ list 1 2 3 | has 4 }}`: `false`, `{{ regexSplit "/" "foo/bar/baz" -1 | has "bar" }}`: `true`, `{{ has "bar" nil }}`: `false`, } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestMustHas(t *testing.T) { tests := map[string]string{ `{{ list 1 2 3 | mustHas 1 }}`: `true`, `{{ list 1 2 3 | mustHas 4 }}`: `false`, `{{ regexSplit "/" "foo/bar/baz" -1 | mustHas "bar" }}`: `true`, `{{ mustHas "bar" nil }}`: `false`, } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestSlice(t *testing.T) { tests := map[string]string{ `{{ slice (list 1 2 3) }}`: "[1 2 3]", `{{ slice (list 1 2 3) 0 1 }}`: "[1]", `{{ slice (list 1 2 3) 1 3 }}`: "[2 3]", `{{ slice (list 1 2 3) 1 }}`: "[2 3]", `{{ slice (regexSplit "/" "foo/bar/baz" -1) 1 2 }}`: "[bar]", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestMustSlice(t *testing.T) { tests := map[string]string{ `{{ mustSlice (list 1 2 3) }}`: "[1 2 3]", `{{ mustSlice (list 1 2 3) 0 1 }}`: "[1]", `{{ mustSlice (list 1 2 3) 1 3 }}`: "[2 3]", `{{ mustSlice (list 1 2 3) 1 }}`: "[2 3]", `{{ mustSlice (regexSplit "/" "foo/bar/baz" -1) 1 2 }}`: "[bar]", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestConcat(t *testing.T) { tests := map[string]string{ `{{ concat (list 1 2 3) }}`: "[1 2 3]", `{{ concat (list 1 2 3) (list 4 5) }}`: "[1 2 3 4 5]", `{{ concat (list 1 2 3) (list 4 5) (list) }}`: "[1 2 3 4 5]", `{{ concat (list 1 2 3) (list 4 5) (list nil) }}`: "[1 2 3 4 5 ]", `{{ concat (list 1 2 3) (list 4 5) (list ( list "foo" ) ) }}`: "[1 2 3 4 5 [foo]]", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } golang-github-go-task-slim-sprig-3.0.0/network.go000066400000000000000000000003261455667526100217320ustar00rootroot00000000000000package sprig import ( "math/rand" "net" ) func getHostByName(name string) string { addrs, _ := net.LookupHost(name) //TODO: add error handing when release v3 comes out return addrs[rand.Intn(len(addrs))] } golang-github-go-task-slim-sprig-3.0.0/network_test.go000066400000000000000000000004361455667526100227730ustar00rootroot00000000000000package sprig import ( "net" "testing" "github.com/stretchr/testify/assert" ) func TestGetHostByName(t *testing.T) { tpl := `{{"www.google.com" | getHostByName}}` resolvedIP, _ := runRaw(tpl, nil) ip := net.ParseIP(resolvedIP) assert.NotNil(t, ip) assert.NotEmpty(t, ip) } golang-github-go-task-slim-sprig-3.0.0/numeric.go000066400000000000000000000101661455667526100217060ustar00rootroot00000000000000package sprig import ( "fmt" "math" "reflect" "strconv" "strings" ) // toFloat64 converts 64-bit floats func toFloat64(v interface{}) float64 { if str, ok := v.(string); ok { iv, err := strconv.ParseFloat(str, 64) if err != nil { return 0 } return iv } val := reflect.Indirect(reflect.ValueOf(v)) switch val.Kind() { case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: return float64(val.Int()) case reflect.Uint8, reflect.Uint16, reflect.Uint32: return float64(val.Uint()) case reflect.Uint, reflect.Uint64: return float64(val.Uint()) case reflect.Float32, reflect.Float64: return val.Float() case reflect.Bool: if val.Bool() { return 1 } return 0 default: return 0 } } func toInt(v interface{}) int { //It's not optimal. Bud I don't want duplicate toInt64 code. return int(toInt64(v)) } // toInt64 converts integer types to 64-bit integers func toInt64(v interface{}) int64 { if str, ok := v.(string); ok { iv, err := strconv.ParseInt(str, 10, 64) if err != nil { return 0 } return iv } val := reflect.Indirect(reflect.ValueOf(v)) switch val.Kind() { case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: return val.Int() case reflect.Uint8, reflect.Uint16, reflect.Uint32: return int64(val.Uint()) case reflect.Uint, reflect.Uint64: tv := val.Uint() if tv <= math.MaxInt64 { return int64(tv) } // TODO: What is the sensible thing to do here? return math.MaxInt64 case reflect.Float32, reflect.Float64: return int64(val.Float()) case reflect.Bool: if val.Bool() { return 1 } return 0 default: return 0 } } func max(a interface{}, i ...interface{}) int64 { aa := toInt64(a) for _, b := range i { bb := toInt64(b) if bb > aa { aa = bb } } return aa } func maxf(a interface{}, i ...interface{}) float64 { aa := toFloat64(a) for _, b := range i { bb := toFloat64(b) aa = math.Max(aa, bb) } return aa } func min(a interface{}, i ...interface{}) int64 { aa := toInt64(a) for _, b := range i { bb := toInt64(b) if bb < aa { aa = bb } } return aa } func minf(a interface{}, i ...interface{}) float64 { aa := toFloat64(a) for _, b := range i { bb := toFloat64(b) aa = math.Min(aa, bb) } return aa } func until(count int) []int { step := 1 if count < 0 { step = -1 } return untilStep(0, count, step) } func untilStep(start, stop, step int) []int { v := []int{} if stop < start { if step >= 0 { return v } for i := start; i > stop; i += step { v = append(v, i) } return v } if step <= 0 { return v } for i := start; i < stop; i += step { v = append(v, i) } return v } func floor(a interface{}) float64 { aa := toFloat64(a) return math.Floor(aa) } func ceil(a interface{}) float64 { aa := toFloat64(a) return math.Ceil(aa) } func round(a interface{}, p int, rOpt ...float64) float64 { roundOn := .5 if len(rOpt) > 0 { roundOn = rOpt[0] } val := toFloat64(a) places := toFloat64(p) var round float64 pow := math.Pow(10, places) digit := pow * val _, div := math.Modf(digit) if div >= roundOn { round = math.Ceil(digit) } else { round = math.Floor(digit) } return round / pow } // converts unix octal to decimal func toDecimal(v interface{}) int64 { result, err := strconv.ParseInt(fmt.Sprint(v), 8, 64) if err != nil { return 0 } return result } func seq(params ...int) string { increment := 1 switch len(params) { case 0: return "" case 1: start := 1 end := params[0] if end < start { increment = -1 } return intArrayToString(untilStep(start, end+increment, increment), " ") case 3: start := params[0] end := params[2] step := params[1] if end < start { increment = -1 if step > 0 { return "" } } return intArrayToString(untilStep(start, end+increment, step), " ") case 2: start := params[0] end := params[1] step := 1 if end < start { step = -1 } return intArrayToString(untilStep(start, end+step, step), " ") default: return "" } } func intArrayToString(slice []int, delimeter string) string { return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(slice)), delimeter), "[]") } golang-github-go-task-slim-sprig-3.0.0/numeric_test.go000066400000000000000000000151261455667526100227460ustar00rootroot00000000000000package sprig import ( "fmt" "strconv" "testing" "github.com/stretchr/testify/assert" ) func TestUntil(t *testing.T) { tests := map[string]string{ `{{range $i, $e := until 5}}{{$i}}{{$e}}{{end}}`: "0011223344", `{{range $i, $e := until -5}}{{$i}}{{$e}} {{end}}`: "00 1-1 2-2 3-3 4-4 ", } for tpl, expect := range tests { if err := runt(tpl, expect); err != nil { t.Error(err) } } } func TestUntilStep(t *testing.T) { tests := map[string]string{ `{{range $i, $e := untilStep 0 5 1}}{{$i}}{{$e}}{{end}}`: "0011223344", `{{range $i, $e := untilStep 3 6 1}}{{$i}}{{$e}}{{end}}`: "031425", `{{range $i, $e := untilStep 0 -10 -2}}{{$i}}{{$e}} {{end}}`: "00 1-2 2-4 3-6 4-8 ", `{{range $i, $e := untilStep 3 0 1}}{{$i}}{{$e}}{{end}}`: "", `{{range $i, $e := untilStep 3 99 0}}{{$i}}{{$e}}{{end}}`: "", `{{range $i, $e := untilStep 3 99 -1}}{{$i}}{{$e}}{{end}}`: "", `{{range $i, $e := untilStep 3 0 0}}{{$i}}{{$e}}{{end}}`: "", } for tpl, expect := range tests { if err := runt(tpl, expect); err != nil { t.Error(err) } } } func TestBiggest(t *testing.T) { tpl := `{{ biggest 1 2 3 345 5 6 7}}` if err := runt(tpl, `345`); err != nil { t.Error(err) } tpl = `{{ max 345}}` if err := runt(tpl, `345`); err != nil { t.Error(err) } } func TestMaxf(t *testing.T) { tpl := `{{ maxf 1 2 3 345.7 5 6 7}}` if err := runt(tpl, `345.7`); err != nil { t.Error(err) } tpl = `{{ max 345 }}` if err := runt(tpl, `345`); err != nil { t.Error(err) } } func TestMin(t *testing.T) { tpl := `{{ min 1 2 3 345 5 6 7}}` if err := runt(tpl, `1`); err != nil { t.Error(err) } tpl = `{{ min 345}}` if err := runt(tpl, `345`); err != nil { t.Error(err) } } func TestMinf(t *testing.T) { tpl := `{{ minf 1.4 2 3 345.6 5 6 7}}` if err := runt(tpl, `1.4`); err != nil { t.Error(err) } tpl = `{{ minf 345 }}` if err := runt(tpl, `345`); err != nil { t.Error(err) } } func TestToFloat64(t *testing.T) { target := float64(102) if target != toFloat64(int8(102)) { t.Errorf("Expected 102") } if target != toFloat64(int(102)) { t.Errorf("Expected 102") } if target != toFloat64(int32(102)) { t.Errorf("Expected 102") } if target != toFloat64(int16(102)) { t.Errorf("Expected 102") } if target != toFloat64(int64(102)) { t.Errorf("Expected 102") } if target != toFloat64("102") { t.Errorf("Expected 102") } if 0 != toFloat64("frankie") { t.Errorf("Expected 0") } if target != toFloat64(uint16(102)) { t.Errorf("Expected 102") } if target != toFloat64(uint64(102)) { t.Errorf("Expected 102") } if 102.1234 != toFloat64(float64(102.1234)) { t.Errorf("Expected 102.1234") } if 1 != toFloat64(true) { t.Errorf("Expected 102") } } func TestToInt64(t *testing.T) { target := int64(102) if target != toInt64(int8(102)) { t.Errorf("Expected 102") } if target != toInt64(int(102)) { t.Errorf("Expected 102") } if target != toInt64(int32(102)) { t.Errorf("Expected 102") } if target != toInt64(int16(102)) { t.Errorf("Expected 102") } if target != toInt64(int64(102)) { t.Errorf("Expected 102") } if target != toInt64("102") { t.Errorf("Expected 102") } if 0 != toInt64("frankie") { t.Errorf("Expected 0") } if target != toInt64(uint16(102)) { t.Errorf("Expected 102") } if target != toInt64(uint64(102)) { t.Errorf("Expected 102") } if target != toInt64(float64(102.1234)) { t.Errorf("Expected 102") } if 1 != toInt64(true) { t.Errorf("Expected 102") } } func TestToInt(t *testing.T) { target := int(102) if target != toInt(int8(102)) { t.Errorf("Expected 102") } if target != toInt(int(102)) { t.Errorf("Expected 102") } if target != toInt(int32(102)) { t.Errorf("Expected 102") } if target != toInt(int16(102)) { t.Errorf("Expected 102") } if target != toInt(int64(102)) { t.Errorf("Expected 102") } if target != toInt("102") { t.Errorf("Expected 102") } if 0 != toInt("frankie") { t.Errorf("Expected 0") } if target != toInt(uint16(102)) { t.Errorf("Expected 102") } if target != toInt(uint64(102)) { t.Errorf("Expected 102") } if target != toInt(float64(102.1234)) { t.Errorf("Expected 102") } if 1 != toInt(true) { t.Errorf("Expected 102") } } func TestToDecimal(t *testing.T) { tests := map[interface{}]int64{ "777": 511, 777: 511, 770: 504, 755: 493, } for input, expectedResult := range tests { result := toDecimal(input) if result != expectedResult { t.Errorf("Expected %v but got %v", expectedResult, result) } } } func TestAdd1(t *testing.T) { tpl := `{{ 3 | add1 }}` if err := runt(tpl, `4`); err != nil { t.Error(err) } } func TestAdd(t *testing.T) { tpl := `{{ 3 | add 1 2}}` if err := runt(tpl, `6`); err != nil { t.Error(err) } } func TestDiv(t *testing.T) { tpl := `{{ 4 | div 5 }}` if err := runt(tpl, `1`); err != nil { t.Error(err) } } func TestMul(t *testing.T) { tpl := `{{ 1 | mul "2" 3 "4"}}` if err := runt(tpl, `24`); err != nil { t.Error(err) } } func TestSub(t *testing.T) { tpl := `{{ 3 | sub 14 }}` if err := runt(tpl, `11`); err != nil { t.Error(err) } } func TestCeil(t *testing.T) { assert.Equal(t, 123.0, ceil(123)) assert.Equal(t, 123.0, ceil("123")) assert.Equal(t, 124.0, ceil(123.01)) assert.Equal(t, 124.0, ceil("123.01")) } func TestFloor(t *testing.T) { assert.Equal(t, 123.0, floor(123)) assert.Equal(t, 123.0, floor("123")) assert.Equal(t, 123.0, floor(123.9999)) assert.Equal(t, 123.0, floor("123.9999")) } func TestRound(t *testing.T) { assert.Equal(t, 123.556, round(123.5555, 3)) assert.Equal(t, 123.556, round("123.55555", 3)) assert.Equal(t, 124.0, round(123.500001, 0)) assert.Equal(t, 123.0, round(123.49999999, 0)) assert.Equal(t, 123.23, round(123.2329999, 2, .3)) assert.Equal(t, 123.24, round(123.233, 2, .3)) } func TestRandomInt(t *testing.T) { var tests = []struct { min int max int }{ {10, 11}, {10, 13}, {0, 1}, {5, 50}, } for _, v := range tests { x, _ := runRaw(fmt.Sprintf(`{{ randInt %d %d }}`, v.min, v.max), nil) r, err := strconv.Atoi(x) assert.NoError(t, err) assert.True(t, func(min, max, r int) bool { return r >= v.min && r < v.max }(v.min, v.max, r)) } } func TestSeq(t *testing.T) { tests := map[string]string{ `{{seq 0 1 3}}`: "0 1 2 3", `{{seq 0 3 10}}`: "0 3 6 9", `{{seq 3 3 2}}`: "", `{{seq 3 -3 2}}`: "3", `{{seq}}`: "", `{{seq 0 4}}`: "0 1 2 3 4", `{{seq 5}}`: "1 2 3 4 5", `{{seq -5}}`: "1 0 -1 -2 -3 -4 -5", `{{seq 0}}`: "1 0", `{{seq 0 1 2 3}}`: "", `{{seq 0 -4}}`: "0 -1 -2 -3 -4", } for tpl, expect := range tests { if err := runt(tpl, expect); err != nil { t.Error(err) } } } golang-github-go-task-slim-sprig-3.0.0/reflect.go000066400000000000000000000010441455667526100216630ustar00rootroot00000000000000package sprig import ( "fmt" "reflect" ) // typeIs returns true if the src is the type named in target. func typeIs(target string, src interface{}) bool { return target == typeOf(src) } func typeIsLike(target string, src interface{}) bool { t := typeOf(src) return target == t || "*"+target == t } func typeOf(src interface{}) string { return fmt.Sprintf("%T", src) } func kindIs(target string, src interface{}) bool { return target == kindOf(src) } func kindOf(src interface{}) string { return reflect.ValueOf(src).Kind().String() } golang-github-go-task-slim-sprig-3.0.0/reflect_test.go000066400000000000000000000026011455667526100227220ustar00rootroot00000000000000package sprig import ( "testing" ) type fixtureTO struct { Name, Value string } func TestTypeOf(t *testing.T) { f := &fixtureTO{"hello", "world"} tpl := `{{typeOf .}}` if err := runtv(tpl, "*sprig.fixtureTO", f); err != nil { t.Error(err) } } func TestKindOf(t *testing.T) { tpl := `{{kindOf .}}` f := fixtureTO{"hello", "world"} if err := runtv(tpl, "struct", f); err != nil { t.Error(err) } f2 := []string{"hello"} if err := runtv(tpl, "slice", f2); err != nil { t.Error(err) } var f3 *fixtureTO if err := runtv(tpl, "ptr", f3); err != nil { t.Error(err) } } func TestTypeIs(t *testing.T) { f := &fixtureTO{"hello", "world"} tpl := `{{if typeIs "*sprig.fixtureTO" .}}t{{else}}f{{end}}` if err := runtv(tpl, "t", f); err != nil { t.Error(err) } f2 := "hello" if err := runtv(tpl, "f", f2); err != nil { t.Error(err) } } func TestTypeIsLike(t *testing.T) { f := "foo" tpl := `{{if typeIsLike "string" .}}t{{else}}f{{end}}` if err := runtv(tpl, "t", f); err != nil { t.Error(err) } // Now make a pointer. Should still match. f2 := &f if err := runtv(tpl, "t", f2); err != nil { t.Error(err) } } func TestKindIs(t *testing.T) { f := &fixtureTO{"hello", "world"} tpl := `{{if kindIs "ptr" .}}t{{else}}f{{end}}` if err := runtv(tpl, "t", f); err != nil { t.Error(err) } f2 := "hello" if err := runtv(tpl, "f", f2); err != nil { t.Error(err) } } golang-github-go-task-slim-sprig-3.0.0/regex.go000066400000000000000000000035321455667526100213550ustar00rootroot00000000000000package sprig import ( "regexp" ) func regexMatch(regex string, s string) bool { match, _ := regexp.MatchString(regex, s) return match } func mustRegexMatch(regex string, s string) (bool, error) { return regexp.MatchString(regex, s) } func regexFindAll(regex string, s string, n int) []string { r := regexp.MustCompile(regex) return r.FindAllString(s, n) } func mustRegexFindAll(regex string, s string, n int) ([]string, error) { r, err := regexp.Compile(regex) if err != nil { return []string{}, err } return r.FindAllString(s, n), nil } func regexFind(regex string, s string) string { r := regexp.MustCompile(regex) return r.FindString(s) } func mustRegexFind(regex string, s string) (string, error) { r, err := regexp.Compile(regex) if err != nil { return "", err } return r.FindString(s), nil } func regexReplaceAll(regex string, s string, repl string) string { r := regexp.MustCompile(regex) return r.ReplaceAllString(s, repl) } func mustRegexReplaceAll(regex string, s string, repl string) (string, error) { r, err := regexp.Compile(regex) if err != nil { return "", err } return r.ReplaceAllString(s, repl), nil } func regexReplaceAllLiteral(regex string, s string, repl string) string { r := regexp.MustCompile(regex) return r.ReplaceAllLiteralString(s, repl) } func mustRegexReplaceAllLiteral(regex string, s string, repl string) (string, error) { r, err := regexp.Compile(regex) if err != nil { return "", err } return r.ReplaceAllLiteralString(s, repl), nil } func regexSplit(regex string, s string, n int) []string { r := regexp.MustCompile(regex) return r.Split(s, n) } func mustRegexSplit(regex string, s string, n int) ([]string, error) { r, err := regexp.Compile(regex) if err != nil { return []string{}, err } return r.Split(s, n), nil } func regexQuoteMeta(s string) string { return regexp.QuoteMeta(s) } golang-github-go-task-slim-sprig-3.0.0/regex_test.go000066400000000000000000000127661455667526100224250ustar00rootroot00000000000000package sprig import ( "testing" "github.com/stretchr/testify/assert" ) func TestRegexMatch(t *testing.T) { regex := "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}" assert.True(t, regexMatch(regex, "test@acme.com")) assert.True(t, regexMatch(regex, "Test@Acme.Com")) assert.False(t, regexMatch(regex, "test")) assert.False(t, regexMatch(regex, "test.com")) assert.False(t, regexMatch(regex, "test@acme")) } func TestMustRegexMatch(t *testing.T) { regex := "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}" o, err := mustRegexMatch(regex, "test@acme.com") assert.True(t, o) assert.Nil(t, err) o, err = mustRegexMatch(regex, "Test@Acme.Com") assert.True(t, o) assert.Nil(t, err) o, err = mustRegexMatch(regex, "test") assert.False(t, o) assert.Nil(t, err) o, err = mustRegexMatch(regex, "test.com") assert.False(t, o) assert.Nil(t, err) o, err = mustRegexMatch(regex, "test@acme") assert.False(t, o) assert.Nil(t, err) } func TestRegexFindAll(t *testing.T) { regex := "a{2}" assert.Equal(t, 1, len(regexFindAll(regex, "aa", -1))) assert.Equal(t, 1, len(regexFindAll(regex, "aaaaaaaa", 1))) assert.Equal(t, 2, len(regexFindAll(regex, "aaaa", -1))) assert.Equal(t, 0, len(regexFindAll(regex, "none", -1))) } func TestMustRegexFindAll(t *testing.T) { type args struct { regex, s string n int } cases := []struct { expected int args args }{ {1, args{"a{2}", "aa", -1}}, {1, args{"a{2}", "aaaaaaaa", 1}}, {2, args{"a{2}", "aaaa", -1}}, {0, args{"a{2}", "none", -1}}, } for _, c := range cases { res, err := mustRegexFindAll(c.args.regex, c.args.s, c.args.n) if err != nil { t.Errorf("regexFindAll test case %v failed with err %s", c, err) } assert.Equal(t, c.expected, len(res), "case %#v", c.args) } } func TestRegexFindl(t *testing.T) { regex := "fo.?" assert.Equal(t, "foo", regexFind(regex, "foorbar")) assert.Equal(t, "foo", regexFind(regex, "foo foe fome")) assert.Equal(t, "", regexFind(regex, "none")) } func TestMustRegexFindl(t *testing.T) { type args struct{ regex, s string } cases := []struct { expected string args args }{ {"foo", args{"fo.?", "foorbar"}}, {"foo", args{"fo.?", "foo foe fome"}}, {"", args{"fo.?", "none"}}, } for _, c := range cases { res, err := mustRegexFind(c.args.regex, c.args.s) if err != nil { t.Errorf("regexFind test case %v failed with err %s", c, err) } assert.Equal(t, c.expected, res, "case %#v", c.args) } } func TestRegexReplaceAll(t *testing.T) { regex := "a(x*)b" assert.Equal(t, "-T-T-", regexReplaceAll(regex, "-ab-axxb-", "T")) assert.Equal(t, "--xx-", regexReplaceAll(regex, "-ab-axxb-", "$1")) assert.Equal(t, "---", regexReplaceAll(regex, "-ab-axxb-", "$1W")) assert.Equal(t, "-W-xxW-", regexReplaceAll(regex, "-ab-axxb-", "${1}W")) } func TestMustRegexReplaceAll(t *testing.T) { type args struct{ regex, s, repl string } cases := []struct { expected string args args }{ {"-T-T-", args{"a(x*)b", "-ab-axxb-", "T"}}, {"--xx-", args{"a(x*)b", "-ab-axxb-", "$1"}}, {"---", args{"a(x*)b", "-ab-axxb-", "$1W"}}, {"-W-xxW-", args{"a(x*)b", "-ab-axxb-", "${1}W"}}, } for _, c := range cases { res, err := mustRegexReplaceAll(c.args.regex, c.args.s, c.args.repl) if err != nil { t.Errorf("regexReplaceAll test case %v failed with err %s", c, err) } assert.Equal(t, c.expected, res, "case %#v", c.args) } } func TestRegexReplaceAllLiteral(t *testing.T) { regex := "a(x*)b" assert.Equal(t, "-T-T-", regexReplaceAllLiteral(regex, "-ab-axxb-", "T")) assert.Equal(t, "-$1-$1-", regexReplaceAllLiteral(regex, "-ab-axxb-", "$1")) assert.Equal(t, "-${1}-${1}-", regexReplaceAllLiteral(regex, "-ab-axxb-", "${1}")) } func TestMustRegexReplaceAllLiteral(t *testing.T) { type args struct{ regex, s, repl string } cases := []struct { expected string args args }{ {"-T-T-", args{"a(x*)b", "-ab-axxb-", "T"}}, {"-$1-$1-", args{"a(x*)b", "-ab-axxb-", "$1"}}, {"-${1}-${1}-", args{"a(x*)b", "-ab-axxb-", "${1}"}}, } for _, c := range cases { res, err := mustRegexReplaceAllLiteral(c.args.regex, c.args.s, c.args.repl) if err != nil { t.Errorf("regexReplaceAllLiteral test case %v failed with err %s", c, err) } assert.Equal(t, c.expected, res, "case %#v", c.args) } } func TestRegexSplit(t *testing.T) { regex := "a" assert.Equal(t, 4, len(regexSplit(regex, "banana", -1))) assert.Equal(t, 0, len(regexSplit(regex, "banana", 0))) assert.Equal(t, 1, len(regexSplit(regex, "banana", 1))) assert.Equal(t, 2, len(regexSplit(regex, "banana", 2))) regex = "z+" assert.Equal(t, 2, len(regexSplit(regex, "pizza", -1))) assert.Equal(t, 0, len(regexSplit(regex, "pizza", 0))) assert.Equal(t, 1, len(regexSplit(regex, "pizza", 1))) assert.Equal(t, 2, len(regexSplit(regex, "pizza", 2))) } func TestMustRegexSplit(t *testing.T) { type args struct { regex, s string n int } cases := []struct { expected int args args }{ {4, args{"a", "banana", -1}}, {0, args{"a", "banana", 0}}, {1, args{"a", "banana", 1}}, {2, args{"a", "banana", 2}}, {2, args{"z+", "pizza", -1}}, {0, args{"z+", "pizza", 0}}, {1, args{"z+", "pizza", 1}}, {2, args{"z+", "pizza", 2}}, } for _, c := range cases { res, err := mustRegexSplit(c.args.regex, c.args.s, c.args.n) if err != nil { t.Errorf("regexSplit test case %v failed with err %s", c, err) } assert.Equal(t, c.expected, len(res), "case %#v", c.args) } } func TestRegexQuoteMeta(t *testing.T) { assert.Equal(t, "1\\.2\\.3", regexQuoteMeta("1.2.3")) assert.Equal(t, "pretzel", regexQuoteMeta("pretzel")) } golang-github-go-task-slim-sprig-3.0.0/strings.go000066400000000000000000000071371455667526100217410ustar00rootroot00000000000000package sprig import ( "encoding/base32" "encoding/base64" "fmt" "reflect" "strconv" "strings" ) func base64encode(v string) string { return base64.StdEncoding.EncodeToString([]byte(v)) } func base64decode(v string) string { data, err := base64.StdEncoding.DecodeString(v) if err != nil { return err.Error() } return string(data) } func base32encode(v string) string { return base32.StdEncoding.EncodeToString([]byte(v)) } func base32decode(v string) string { data, err := base32.StdEncoding.DecodeString(v) if err != nil { return err.Error() } return string(data) } func quote(str ...interface{}) string { out := make([]string, 0, len(str)) for _, s := range str { if s != nil { out = append(out, fmt.Sprintf("%q", strval(s))) } } return strings.Join(out, " ") } func squote(str ...interface{}) string { out := make([]string, 0, len(str)) for _, s := range str { if s != nil { out = append(out, fmt.Sprintf("'%v'", s)) } } return strings.Join(out, " ") } func cat(v ...interface{}) string { v = removeNilElements(v) r := strings.TrimSpace(strings.Repeat("%v ", len(v))) return fmt.Sprintf(r, v...) } func indent(spaces int, v string) string { pad := strings.Repeat(" ", spaces) return pad + strings.Replace(v, "\n", "\n"+pad, -1) } func nindent(spaces int, v string) string { return "\n" + indent(spaces, v) } func replace(old, new, src string) string { return strings.Replace(src, old, new, -1) } func plural(one, many string, count int) string { if count == 1 { return one } return many } func strslice(v interface{}) []string { switch v := v.(type) { case []string: return v case []interface{}: b := make([]string, 0, len(v)) for _, s := range v { if s != nil { b = append(b, strval(s)) } } return b default: val := reflect.ValueOf(v) switch val.Kind() { case reflect.Array, reflect.Slice: l := val.Len() b := make([]string, 0, l) for i := 0; i < l; i++ { value := val.Index(i).Interface() if value != nil { b = append(b, strval(value)) } } return b default: if v == nil { return []string{} } return []string{strval(v)} } } } func removeNilElements(v []interface{}) []interface{} { newSlice := make([]interface{}, 0, len(v)) for _, i := range v { if i != nil { newSlice = append(newSlice, i) } } return newSlice } func strval(v interface{}) string { switch v := v.(type) { case string: return v case []byte: return string(v) case error: return v.Error() case fmt.Stringer: return v.String() default: return fmt.Sprintf("%v", v) } } func trunc(c int, s string) string { if c < 0 && len(s)+c > 0 { return s[len(s)+c:] } if c >= 0 && len(s) > c { return s[:c] } return s } func join(sep string, v interface{}) string { return strings.Join(strslice(v), sep) } func split(sep, orig string) map[string]string { parts := strings.Split(orig, sep) res := make(map[string]string, len(parts)) for i, v := range parts { res["_"+strconv.Itoa(i)] = v } return res } func splitn(sep string, n int, orig string) map[string]string { parts := strings.SplitN(orig, sep, n) res := make(map[string]string, len(parts)) for i, v := range parts { res["_"+strconv.Itoa(i)] = v } return res } // substring creates a substring of the given string. // // If start is < 0, this calls string[:end]. // // If start is >= 0 and end < 0 or end bigger than s length, this calls string[start:] // // Otherwise, this calls string[start, end]. func substring(start, end int, s string) string { if start < 0 { return s[:end] } if end < 0 || end > len(s) { return s[start:] } return s[start:end] } golang-github-go-task-slim-sprig-3.0.0/strings_test.go000066400000000000000000000131421455667526100227710ustar00rootroot00000000000000package sprig import ( "encoding/base32" "encoding/base64" "fmt" "testing" "github.com/stretchr/testify/assert" ) func TestSubstr(t *testing.T) { tpl := `{{"fooo" | substr 0 3 }}` if err := runt(tpl, "foo"); err != nil { t.Error(err) } } func TestSubstr_shorterString(t *testing.T) { tpl := `{{"foo" | substr 0 10 }}` if err := runt(tpl, "foo"); err != nil { t.Error(err) } } func TestTrunc(t *testing.T) { tpl := `{{ "foooooo" | trunc 3 }}` if err := runt(tpl, "foo"); err != nil { t.Error(err) } tpl = `{{ "baaaaaar" | trunc -3 }}` if err := runt(tpl, "aar"); err != nil { t.Error(err) } tpl = `{{ "baaaaaar" | trunc -999 }}` if err := runt(tpl, "baaaaaar"); err != nil { t.Error(err) } tpl = `{{ "baaaaaz" | trunc 0 }}` if err := runt(tpl, ""); err != nil { t.Error(err) } } func TestQuote(t *testing.T) { tpl := `{{quote "a" "b" "c"}}` if err := runt(tpl, `"a" "b" "c"`); err != nil { t.Error(err) } tpl = `{{quote "\"a\"" "b" "c"}}` if err := runt(tpl, `"\"a\"" "b" "c"`); err != nil { t.Error(err) } tpl = `{{quote 1 2 3 }}` if err := runt(tpl, `"1" "2" "3"`); err != nil { t.Error(err) } tpl = `{{ .value | quote }}` values := map[string]interface{}{"value": nil} if err := runtv(tpl, ``, values); err != nil { t.Error(err) } } func TestSquote(t *testing.T) { tpl := `{{squote "a" "b" "c"}}` if err := runt(tpl, `'a' 'b' 'c'`); err != nil { t.Error(err) } tpl = `{{squote 1 2 3 }}` if err := runt(tpl, `'1' '2' '3'`); err != nil { t.Error(err) } tpl = `{{ .value | squote }}` values := map[string]interface{}{"value": nil} if err := runtv(tpl, ``, values); err != nil { t.Error(err) } } func TestContains(t *testing.T) { // Mainly, we're just verifying the paramater order swap. tests := []string{ `{{if contains "cat" "fair catch"}}1{{end}}`, `{{if hasPrefix "cat" "catch"}}1{{end}}`, `{{if hasSuffix "cat" "ducat"}}1{{end}}`, } for _, tt := range tests { if err := runt(tt, "1"); err != nil { t.Error(err) } } } func TestTrim(t *testing.T) { tests := []string{ `{{trim " 5.00 "}}`, `{{trimAll "$" "$5.00$"}}`, `{{trimPrefix "$" "$5.00"}}`, `{{trimSuffix "$" "5.00$"}}`, } for _, tt := range tests { if err := runt(tt, "5.00"); err != nil { t.Error(err) } } } func TestSplit(t *testing.T) { tpl := `{{$v := "foo$bar$baz" | split "$"}}{{$v._0}}` if err := runt(tpl, "foo"); err != nil { t.Error(err) } } func TestSplitn(t *testing.T) { tpl := `{{$v := "foo$bar$baz" | splitn "$" 2}}{{$v._0}}` if err := runt(tpl, "foo"); err != nil { t.Error(err) } } func TestToString(t *testing.T) { tpl := `{{ toString 1 | kindOf }}` assert.NoError(t, runt(tpl, "string")) } func TestToStrings(t *testing.T) { tpl := `{{ $s := list 1 2 3 | toStrings }}{{ index $s 1 | kindOf }}` assert.NoError(t, runt(tpl, "string")) tpl = `{{ list 1 .value 2 | toStrings }}` values := map[string]interface{}{"value": nil} if err := runtv(tpl, `[1 2]`, values); err != nil { t.Error(err) } } func TestJoin(t *testing.T) { assert.NoError(t, runt(`{{ tuple "a" "b" "c" | join "-" }}`, "a-b-c")) assert.NoError(t, runt(`{{ tuple 1 2 3 | join "-" }}`, "1-2-3")) assert.NoError(t, runtv(`{{ join "-" .V }}`, "a-b-c", map[string]interface{}{"V": []string{"a", "b", "c"}})) assert.NoError(t, runtv(`{{ join "-" .V }}`, "abc", map[string]interface{}{"V": "abc"})) assert.NoError(t, runtv(`{{ join "-" .V }}`, "1-2-3", map[string]interface{}{"V": []int{1, 2, 3}})) assert.NoError(t, runtv(`{{ join "-" .value }}`, "1-2", map[string]interface{}{"value": []interface{}{"1", nil, "2"}})) } func TestSortAlpha(t *testing.T) { // Named `append` in the function map tests := map[string]string{ `{{ list "c" "a" "b" | sortAlpha | join "" }}`: "abc", `{{ list 2 1 4 3 | sortAlpha | join "" }}`: "1234", } for tpl, expect := range tests { assert.NoError(t, runt(tpl, expect)) } } func TestBase64EncodeDecode(t *testing.T) { magicWord := "coffee" expect := base64.StdEncoding.EncodeToString([]byte(magicWord)) if expect == magicWord { t.Fatal("Encoder doesn't work.") } tpl := `{{b64enc "coffee"}}` if err := runt(tpl, expect); err != nil { t.Error(err) } tpl = fmt.Sprintf("{{b64dec %q}}", expect) if err := runt(tpl, magicWord); err != nil { t.Error(err) } } func TestBase32EncodeDecode(t *testing.T) { magicWord := "coffee" expect := base32.StdEncoding.EncodeToString([]byte(magicWord)) if expect == magicWord { t.Fatal("Encoder doesn't work.") } tpl := `{{b32enc "coffee"}}` if err := runt(tpl, expect); err != nil { t.Error(err) } tpl = fmt.Sprintf("{{b32dec %q}}", expect) if err := runt(tpl, magicWord); err != nil { t.Error(err) } } func TestCat(t *testing.T) { tpl := `{{$b := "b"}}{{"c" | cat "a" $b}}` if err := runt(tpl, "a b c"); err != nil { t.Error(err) } tpl = `{{ .value | cat "a" "b"}}` values := map[string]interface{}{"value": nil} if err := runtv(tpl, "a b", values); err != nil { t.Error(err) } } func TestIndent(t *testing.T) { tpl := `{{indent 4 "a\nb\nc"}}` if err := runt(tpl, " a\n b\n c"); err != nil { t.Error(err) } } func TestNindent(t *testing.T) { tpl := `{{nindent 4 "a\nb\nc"}}` if err := runt(tpl, "\n a\n b\n c"); err != nil { t.Error(err) } } func TestReplace(t *testing.T) { tpl := `{{"I Am Henry VIII" | replace " " "-"}}` if err := runt(tpl, "I-Am-Henry-VIII"); err != nil { t.Error(err) } } func TestPlural(t *testing.T) { tpl := `{{$num := len "two"}}{{$num}} {{$num | plural "1 char" "chars"}}` if err := runt(tpl, "3 chars"); err != nil { t.Error(err) } tpl = `{{len "t" | plural "cheese" "%d chars"}}` if err := runt(tpl, "cheese"); err != nil { t.Error(err) } } golang-github-go-task-slim-sprig-3.0.0/url.go000066400000000000000000000031721455667526100210450ustar00rootroot00000000000000package sprig import ( "fmt" "net/url" "reflect" ) func dictGetOrEmpty(dict map[string]interface{}, key string) string { value, ok := dict[key] if !ok { return "" } tp := reflect.TypeOf(value).Kind() if tp != reflect.String { panic(fmt.Sprintf("unable to parse %s key, must be of type string, but %s found", key, tp.String())) } return reflect.ValueOf(value).String() } // parses given URL to return dict object func urlParse(v string) map[string]interface{} { dict := map[string]interface{}{} parsedURL, err := url.Parse(v) if err != nil { panic(fmt.Sprintf("unable to parse url: %s", err)) } dict["scheme"] = parsedURL.Scheme dict["host"] = parsedURL.Host dict["hostname"] = parsedURL.Hostname() dict["path"] = parsedURL.Path dict["query"] = parsedURL.RawQuery dict["opaque"] = parsedURL.Opaque dict["fragment"] = parsedURL.Fragment if parsedURL.User != nil { dict["userinfo"] = parsedURL.User.String() } else { dict["userinfo"] = "" } return dict } // join given dict to URL string func urlJoin(d map[string]interface{}) string { resURL := url.URL{ Scheme: dictGetOrEmpty(d, "scheme"), Host: dictGetOrEmpty(d, "host"), Path: dictGetOrEmpty(d, "path"), RawQuery: dictGetOrEmpty(d, "query"), Opaque: dictGetOrEmpty(d, "opaque"), Fragment: dictGetOrEmpty(d, "fragment"), } userinfo := dictGetOrEmpty(d, "userinfo") var user *url.Userinfo if userinfo != "" { tempURL, err := url.Parse(fmt.Sprintf("proto://%s@host", userinfo)) if err != nil { panic(fmt.Sprintf("unable to parse userinfo in dict: %s", err)) } user = tempURL.User } resURL.User = user return resURL.String() } golang-github-go-task-slim-sprig-3.0.0/url_test.go000066400000000000000000000040371455667526100221050ustar00rootroot00000000000000package sprig import ( "testing" "github.com/stretchr/testify/assert" ) var urlTests = map[string]map[string]interface{}{ "proto://auth@host:80/path?query#fragment": { "fragment": "fragment", "host": "host:80", "hostname": "host", "opaque": "", "path": "/path", "query": "query", "scheme": "proto", "userinfo": "auth", }, "proto://host:80/path": { "fragment": "", "host": "host:80", "hostname": "host", "opaque": "", "path": "/path", "query": "", "scheme": "proto", "userinfo": "", }, "something": { "fragment": "", "host": "", "hostname": "", "opaque": "", "path": "something", "query": "", "scheme": "", "userinfo": "", }, "proto://user:passwor%20d@host:80/path": { "fragment": "", "host": "host:80", "hostname": "host", "opaque": "", "path": "/path", "query": "", "scheme": "proto", "userinfo": "user:passwor%20d", }, "proto://host:80/pa%20th?key=val%20ue": { "fragment": "", "host": "host:80", "hostname": "host", "opaque": "", "path": "/pa th", "query": "key=val%20ue", "scheme": "proto", "userinfo": "", }, } func TestUrlParse(t *testing.T) { // testing that function is exported and working properly assert.NoError(t, runt( `{{ index ( urlParse "proto://auth@host:80/path?query#fragment" ) "host" }}`, "host:80")) // testing scenarios for url, expected := range urlTests { assert.EqualValues(t, expected, urlParse(url)) } } func TestUrlJoin(t *testing.T) { tests := map[string]string{ `{{ urlJoin (dict "fragment" "fragment" "host" "host:80" "path" "/path" "query" "query" "scheme" "proto") }}`: "proto://host:80/path?query#fragment", `{{ urlJoin (dict "fragment" "fragment" "host" "host:80" "path" "/path" "scheme" "proto" "userinfo" "ASDJKJSD") }}`: "proto://ASDJKJSD@host:80/path#fragment", } for tpl, expected := range tests { assert.NoError(t, runt(tpl, expected)) } for expected, urlMap := range urlTests { assert.EqualValues(t, expected, urlJoin(urlMap)) } }