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

Additional functions in template #93

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ variables within a template with `.Env`.
There are a few built in functions as well:

* `default $var $default` - Returns a default value for one that does not exist. `{{ default .Env.VERSION "0.1.2" }}`
* `defaultEmpty $var $default` - Returns a default value for one that does not exist or is empty in case of string. `{{ default .Env.VERSION "0.1.2" }}`
* `required $val` Returns a value passed as argument or error if value does not exist. `{{ required .Env.VERSION }}`
* `requiredEmpty $val` Returns a value passed as argument or error if value does not exist or is empty in case of string. `{{ requiredEmpty .Env.VERSION }}`
* `contains $map $key` - Returns true if a string is within another string
* `exists $path` - Determines if a file path exists or not. `{{ exists "/etc/default/myapp" }}`
* `split $string $sep` - Splits a string into an array using a separator string. Alias for [`strings.Split`][go.string.Split]. `{{ split .Env.PATH ":" }}`
Expand All @@ -178,6 +181,7 @@ There are a few built in functions as well:
* `upper $value` - Uppercase a string.
* `jsonQuery $json $query` - Returns the result of a selection query against a json document.
* `loop` - Create for loops.
* `envSlice` - Returns the array filled with maps of environment variables passed as collection (variables that contains numeric suffix) and matched to $prefix `{{ envSlice "VARIABLE_NAME" }}`

### jsonQuery

Expand Down Expand Up @@ -224,6 +228,34 @@ i = {{ $i }}
{{ end }}
```

### envSlice

`envSlice` can be useful if we want to pass multiple values of the same property by environment variables. It can be also used to pass different values with same index

For variables:
```
EXAMPLEVAR_SUBVAR_1
EXAMPLEVAR_SUBVAR_2
EXAMPLEVAR_ANOTHERSUBVAR_1
EXAMPLEVAR_ANOTHERSUBVAR_2
```
Let's assume that we call envSlice with $prefix = "EXAMPLEVAR". Function return an array. Each entry will have a map of variables that starts with `EXAMPLEVAR` and having same index
Returned collection can be used in loop. For example:

```
{{ range $i := envSlice ("EXAMPLEVAR") }}

subvar value: {{ $i.EXAMPLEVAR_SUBVAR }}
anotherSubvar value: {{ $i.EXAMPLEVAR_ANOTHERSUBVAR }}

---

```

##### Limitations
Note that the last index of array will be highest index found in variables matched by prefix. So if we define EXAMPLEVAR_1 and EXAMPLEVAR_100 the loop will be iterate 100 times with filled values only in first and last index


## License

MIT
Expand Down
145 changes: 117 additions & 28 deletions template.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,30 +33,70 @@ func contains(item map[string]string, key string) bool {
return false
}

func _checkDefaultValueArgs(args interface{}) error {
vargs, _ := args.([]interface{})

if len(vargs) == 0 {
return fmt.Errorf("default called with no values!")
}

if len(vargs) < 2 {
return fmt.Errorf("default called with no default value")
}

if vargs[1] == nil {
return fmt.Errorf("default called with nil default value!")
}

if _, ok := vargs[1].(string); !ok {
return fmt.Errorf("default is not a string value. hint: surround it w/ double quotes.")
}

return nil
}

func defaultValue(args ...interface{}) (string, error) {
if len(args) == 0 {
return "", fmt.Errorf("default called with no values!")
if err := _checkDefaultValueArgs(args); err != nil {
return "", err
}

if len(args) > 0 {
if args[0] != nil {
return args[0].(string), nil
}
if args[0] != nil {
return args[0].(string), nil
}

if len(args) > 1 {
if args[1] == nil {
return "", fmt.Errorf("default called with nil default value!")
}
return args[1].(string), nil
}

if _, ok := args[1].(string); !ok {
return "", fmt.Errorf("default is not a string value. hint: surround it w/ double quotes.")
}
func defaultValueEmpty(args ...interface{}) (string, error) {
if err := _checkDefaultValueArgs(args); err != nil {
return "", err
}

if args[0] == nil || len(args[0].(string)) == 0 {
return args[1].(string), nil
}

return "", fmt.Errorf("default called with no default value")
return args[0].(string), nil
}

func requiredValue(val interface{}) (interface{}, error) {
if val != nil {
return val, nil
}

return nil, fmt.Errorf("Required value is nil")
}

func requiredValueEmpty(val interface{}) (interface{}, error) {
if _, err := requiredValue(val); err != nil {
return nil, err
}

if val, ok := val.(string); ok && len(val) == 0 {
return nil, fmt.Errorf("Required value is empty")
}

return val, nil
}

func parseUrl(rawurl string) *url.URL {
Expand Down Expand Up @@ -101,7 +141,7 @@ func loop(args ...int) (<-chan int, error) {
case 3:
start, stop, step = args[0], args[1], args[2]
default:
return nil, fmt.Errorf("wrong number of arguments, expected 1-3"+
return nil, fmt.Errorf("wrong number of arguments, expected 1-3" +
", but got %d", len(args))
}

Expand All @@ -115,21 +155,70 @@ func loop(args ...int) (<-chan int, error) {
return c, nil
}

func envSlice(envParamPrefix string) ([]map[string]interface{}, error) {
variables := make(map[string]interface{})

biggestIndex := 0

for _, i := range os.Environ() {
sep := strings.Index(i, "=")
key := i[0:sep]
val := i[sep + 1:]

if strings.HasPrefix(key, envParamPrefix) {
suffix := key[strings.LastIndex(key, "_") + 1:]
if num, ok := strconv.Atoi(suffix); ok == nil {
if biggestIndex < num {
biggestIndex = num
}
variables[key] = val
}
}
}

var result []map[string]interface{}
if biggestIndex > 0 {
result = make([]map[string]interface{}, biggestIndex)

for i := 0; i < biggestIndex; i++ {
result[i] = make(map[string]interface{})
}

for key, value := range variables {
indexStr := key[strings.LastIndex(key, "_") + 1:]
resultIndex, _ := strconv.Atoi(indexStr)
resultIndex -= 1
resultKey := key[0:strings.LastIndex(key, "_")]

result[resultIndex][resultKey] = value
}

} else {
result = make([]map[string]interface{}, 0)
}

return result, nil
}

func generateFile(templatePath, destPath string) bool {
tmpl := template.New(filepath.Base(templatePath)).Funcs(template.FuncMap{
"contains": contains,
"exists": exists,
"split": strings.Split,
"replace": strings.Replace,
"default": defaultValue,
"parseUrl": parseUrl,
"atoi": strconv.Atoi,
"add": add,
"isTrue": isTrue,
"lower": strings.ToLower,
"upper": strings.ToUpper,
"jsonQuery": jsonQuery,
"loop": loop,
"contains": contains,
"exists": exists,
"split": strings.Split,
"replace": strings.Replace,
"default": defaultValue,
"defaultEmpty": defaultValueEmpty,
"required": requiredValue,
"requiredEmpty": requiredValueEmpty,
"parseUrl": parseUrl,
"atoi": strconv.Atoi,
"add": add,
"isTrue": isTrue,
"lower": strings.ToLower,
"upper": strings.ToUpper,
"jsonQuery": jsonQuery,
"loop": loop,
"envSlice": envSlice,
})

if len(delims) > 0 {
Expand Down