Skip to content

Commit

Permalink
Merge pull request #15 from chezmoi/docs
Browse files Browse the repository at this point in the history
chore: Add more docs
  • Loading branch information
twpayne authored Mar 5, 2024
2 parents 62f7d15 + 4dff45a commit 1b0f851
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 1 deletion.
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# templatefuncs

Package templatefuncs provides a rich set of
[text/template](https://pkg.go.dev/text/template) functions.

templatefuncs is a modern alternative to
[github.com/masterminds/sprig](https://github.com/masterminds/sprig) with the
following goals:

* Flexible, practical typing of user-equivalent types (e.g. functions that
accept `string`s also accept `fmt.Stringer`s and `[]byte`s).
* Correct argument order, compatible with text/template's pipelines where the
most variable argument is passed last (e.g. so you can write `dict "key"
"value" | hasKey "key"` instead of `hasKey (dict "key" value") "key"`).
* Idiomatic Go naming conventions (e.g. `toJSON`, not `toJson`).
* Structure-preserving transformations (e.g. `toLower` converts a `string` to a
`string` and also converts a `[]string` to a `[]string`).
* Linkable documentation for individual template functions (so you can direct
users to the documentation for a single function, not just a page of
functions).
* Exported documentation which you can include in your own project (so you can
include a full list of template functions that your project supports).
* Actively maintained.

templatefuncs explicitly is *not* backwards compatible with
github.com/masterminds/sprig.

templatefuncs is currently in the experimental stage and is not suitable for
production use.

## License

MIT
2 changes: 1 addition & 1 deletion docs/templatefuncs.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ foobar

## `hexEncode` *string*

`hexEncode` returns the hexidecimal encoding of *string*.
`hexEncode` returns the hexadecimal encoding of *string*.

```text
{{ hexEncode "foobar" }}
Expand Down
37 changes: 37 additions & 0 deletions templatefuncs.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"text/template"
)

// fileModeTypeNames maps file mode types to human-readable strings.
var fileModeTypeNames = map[fs.FileMode]string{
0: "file",
fs.ModeDir: "dir",
Expand All @@ -24,6 +25,8 @@ var fileModeTypeNames = map[fs.FileMode]string{
fs.ModeCharDevice: "char device",
}

// NewFuncMap returns a new [text/template.FuncMap] containing all template
// functions.
func NewFuncMap() template.FuncMap {
return template.FuncMap{
"contains": reverseArgs2(strings.Contains),
Expand All @@ -49,6 +52,8 @@ func NewFuncMap() template.FuncMap {
}
}

// prefixLinesTemplateFunc is the core implementation of the `prefixLines`
// template function.
func prefixLinesTemplateFunc(prefix, s string) string {
type stateType int
const (
Expand Down Expand Up @@ -83,6 +88,8 @@ func prefixLinesTemplateFunc(prefix, s string) string {
return builder.String()
}

// eqFoldTemplateFunc is the core implementation of the `eqFold` template
// function.
func eqFoldTemplateFunc(first, second string, more ...string) bool {
if strings.EqualFold(first, second) {
return true
Expand All @@ -95,6 +102,8 @@ func eqFoldTemplateFunc(first, second string, more ...string) bool {
return false
}

// fromJSONTemplateFunc is the core implementation of the `fromJSON` template
// function.
func fromJSONTemplateFunc(data []byte) (any, error) {
var result any
if err := json.Unmarshal(data, &result); err != nil {
Expand All @@ -103,10 +112,13 @@ func fromJSONTemplateFunc(data []byte) (any, error) {
return result, nil
}

// listTemplateFunc is the core implementation of the `list` template function.
func listTemplateFunc(args ...any) []any {
return args
}

// lookPathTemplateFunc is the core implementation of the `lookPath` template
// function.
func lookPathTemplateFunc(file string) (string, error) {
switch path, err := exec.LookPath(file); {
case err == nil:
Expand All @@ -120,6 +132,8 @@ func lookPathTemplateFunc(file string) (string, error) {
}
}

// lstatTemplateFunc is the core implementation of the `lstat` template
// function.
func lstatTemplateFunc(name string) any {
switch fileInfo, err := os.Lstat(name); {
case err == nil:
Expand All @@ -131,10 +145,13 @@ func lstatTemplateFunc(name string) any {
}
}

// regexpReplaceAllTemplateFunc is the core implementation of the
// `regexpReplaceAll` template function.
func regexpReplaceAllTemplateFunc(expr, repl, s string) string {
return regexp.MustCompile(expr).ReplaceAllString(s, repl)
}

// statTemplateFunc is the core implementation of the `stat` template function.
func statTemplateFunc(name string) any {
switch fileInfo, err := os.Stat(name); {
case err == nil:
Expand All @@ -146,6 +163,8 @@ func statTemplateFunc(name string) any {
}
}

// toJSONTemplateFunc is the core implementation of the `toJSON` template
// function.
func toJSONTemplateFunc(arg any) []byte {
data, err := json.Marshal(arg)
if err != nil {
Expand All @@ -154,6 +173,8 @@ func toJSONTemplateFunc(arg any) []byte {
return data
}

// toStringTemplateFunc is the core implementation of the `toString` template
// function.
func toStringTemplateFunc(arg any) string {
// FIXME add more types
switch arg := arg.(type) {
Expand All @@ -178,6 +199,9 @@ func toStringTemplateFunc(arg any) string {
}
}

// eachByteSlice transforms a function that takes a single `[]byte` and returns
// a `T` to a function that takes zero or more `[]byte`-like arguments and
// returns zero or more `T`s.
func eachByteSlice[T any](f func([]byte) T) func(any) any {
return func(arg any) any {
switch arg := arg.(type) {
Expand All @@ -203,6 +227,9 @@ func eachByteSlice[T any](f func([]byte) T) func(any) any {
}
}

// eachByteSliceErr transforms a function that takes a single `[]byte` and
// returns a `T` and an `error` into a function that takes zero or more
// `[]byte`-like arguments and returns zero or more `Ts` and an error.
func eachByteSliceErr[T any](f func([]byte) (T, error)) func(any) any {
return func(arg any) any {
switch arg := arg.(type) {
Expand Down Expand Up @@ -244,6 +271,9 @@ func eachByteSliceErr[T any](f func([]byte) (T, error)) func(any) any {
}
}

// eachString transforms a function that takes a single `string`-like argument
// and returns a `T` into a function that takes zero or more `string`-like
// arguments and returns zero or more `T`s.
func eachString[T any](f func(string) T) func(any) any {
return func(arg any) any {
switch arg := arg.(type) {
Expand Down Expand Up @@ -282,6 +312,9 @@ func eachString[T any](f func(string) T) func(any) any {
}
}

// eachStringErr transforms a function that takes a single `string`-like argument
// and returns a `T` and an `error` into a function that takes zero or more
// `string`-like arguments and returns zero or more `T`s and an `error`.
func eachStringErr[T any](f func(string) (T, error)) func(any) any {
return func(arg any) any {
switch arg := arg.(type) {
Expand Down Expand Up @@ -323,6 +356,7 @@ func eachStringErr[T any](f func(string) (T, error)) func(any) any {
}
}

// fileInfoToMap returns a `map[string]any` of `fileInfo`'s fields.
func fileInfoToMap(fileInfo fs.FileInfo) map[string]any {
return map[string]any{
"name": fileInfo.Name(),
Expand All @@ -335,6 +369,9 @@ func fileInfoToMap(fileInfo fs.FileInfo) map[string]any {
}
}

// reverseArgs2 transforms a function that takes two arguments and returns an
// `R` into a function that takes the arguments in reverse order and returns an
// `R`.
func reverseArgs2[T1, T2, R any](f func(T1, T2) R) func(T2, T1) R {
return func(arg1 T2, arg2 T1) R {
return f(arg2, arg1)
Expand Down

0 comments on commit 1b0f851

Please sign in to comment.