Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Add more docs #15

Merged
merged 3 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading