Handle other slice and map types
This commit is contained in:
parent
07adf2d49e
commit
4a49e901ac
227
template_nest.go
227
template_nest.go
@ -6,6 +6,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -256,6 +257,10 @@ func (nest *TemplateNest) MustRender(toRender interface{}) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (nest *TemplateNest) Render(toRender interface{}) (string, error) {
|
func (nest *TemplateNest) Render(toRender interface{}) (string, error) {
|
||||||
|
if reflect.TypeOf(toRender).Kind() == reflect.Slice {
|
||||||
|
return nest.renderSlice(toRender)
|
||||||
|
}
|
||||||
|
|
||||||
switch v := toRender.(type) {
|
switch v := toRender.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
return "", nil
|
return "", nil
|
||||||
@ -272,114 +277,128 @@ func (nest *TemplateNest) Render(toRender interface{}) (string, error) {
|
|||||||
case float64, int, int64:
|
case float64, int, int64:
|
||||||
return fmt.Sprintf("%v", v), nil
|
return fmt.Sprintf("%v", v), nil
|
||||||
|
|
||||||
case []Hash:
|
|
||||||
var rendered strings.Builder
|
|
||||||
for _, item := range v {
|
|
||||||
renderedItem, err := nest.Render(item)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
rendered.WriteString(renderedItem)
|
|
||||||
}
|
|
||||||
return rendered.String(), nil
|
|
||||||
|
|
||||||
case Hash:
|
case Hash:
|
||||||
tLabel, ok := v[nest.option.NameLabel]
|
return nest.renderHash(v)
|
||||||
if !ok {
|
|
||||||
return "", fmt.Errorf(
|
|
||||||
"encountered hash with no name label (name label: `%s`)", nest.option.NameLabel,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
tName, ok := tLabel.(string)
|
case map[string]interface{}:
|
||||||
if !ok {
|
return nest.renderHash(v)
|
||||||
return "", fmt.Errorf(
|
|
||||||
"encountered hash with invalid name label type: %+v", tLabel,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
tFile := nest.getTemplateFilePath(tName)
|
|
||||||
fileInfo, err := os.Stat(tFile)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("error getting file info: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tIndex, exists := nest.cache[tName]
|
|
||||||
|
|
||||||
// If cache doesn't exist or has expired, re-index the file.
|
|
||||||
if !exists || fileInfo.ModTime().After(tIndex.LastModified) {
|
|
||||||
newIndex, err := nest.index(tFile)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("file index failed: %w", err)
|
|
||||||
}
|
|
||||||
tIndex = newIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
if nest.option.DieOnBadParams {
|
|
||||||
for k, _ := range v {
|
|
||||||
// If a variable in template hash is not present in template
|
|
||||||
// file and it's not the template label then it's a bad param.
|
|
||||||
_, exists := tIndex.VariableNames[k]
|
|
||||||
if !exists && k != nest.option.NameLabel {
|
|
||||||
return "", fmt.Errorf(
|
|
||||||
"bad params in template hash, variable not present in template file: `%s`", k,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rendered := tIndex.Contents
|
|
||||||
for i := len(tIndex.Variables) - 1; i >= 0; i-- {
|
|
||||||
variable := tIndex.Variables[i]
|
|
||||||
if variable.EscapedToken {
|
|
||||||
rendered = rendered[:variable.StartPosition] + rendered[variable.EndPosition:]
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the variable doesn't exist in template hash then replace it
|
|
||||||
// with an empty string.
|
|
||||||
replacement := ""
|
|
||||||
|
|
||||||
value, exists := v[variable.Name]
|
|
||||||
defaultValue, defaultExists := nest.defaultsFlat[variable.Name]
|
|
||||||
|
|
||||||
if exists || defaultExists {
|
|
||||||
if !exists {
|
|
||||||
value = defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
subRender, err := nest.Render(value)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if nest.option.FixedIndent && variable.IndentLevel != 0 {
|
|
||||||
indentReplacement := "\n" + strings.Repeat(" ", int(variable.IndentLevel))
|
|
||||||
subRender = strings.ReplaceAll(subRender, "\n", indentReplacement)
|
|
||||||
}
|
|
||||||
|
|
||||||
replacement = subRender
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace in rendered template
|
|
||||||
rendered = rendered[:variable.StartPosition] + replacement + rendered[variable.EndPosition:]
|
|
||||||
}
|
|
||||||
|
|
||||||
if nest.option.ShowLabels {
|
|
||||||
labelStart := fmt.Sprintf(
|
|
||||||
"%s BEGIN %s %s\n",
|
|
||||||
nest.option.CommentDelimiters[0], tName, nest.option.CommentDelimiters[1],
|
|
||||||
)
|
|
||||||
labelEnd := fmt.Sprintf(
|
|
||||||
"%s END %s %s\n",
|
|
||||||
nest.option.CommentDelimiters[0], tName, nest.option.CommentDelimiters[1],
|
|
||||||
)
|
|
||||||
rendered = labelStart + rendered + labelEnd
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.TrimSpace(rendered), nil
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return "", fmt.Errorf("unsupported template hash value type: %+v", v)
|
return "", fmt.Errorf("unsupported template hash value type: %+v", v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (nest *TemplateNest) renderSlice(toRender interface{}) (string, error) {
|
||||||
|
val := reflect.ValueOf(toRender)
|
||||||
|
if val.Kind() != reflect.Slice {
|
||||||
|
return "", fmt.Errorf("expected slice, got: %T", toRender)
|
||||||
|
}
|
||||||
|
|
||||||
|
var rendered strings.Builder
|
||||||
|
for i := 0; i < val.Len(); i++ {
|
||||||
|
element := val.Index(i).Interface()
|
||||||
|
subRender, err := nest.Render(element)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
rendered.WriteString(subRender)
|
||||||
|
}
|
||||||
|
return rendered.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nest *TemplateNest) renderHash(hash map[string]interface{}) (string, error) {
|
||||||
|
tLabel, ok := hash[nest.option.NameLabel]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf(
|
||||||
|
"encountered hash with no name label (name label: `%s`)", nest.option.NameLabel,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
tName, ok := tLabel.(string)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf(
|
||||||
|
"encountered hash with invalid name label type: %+v", tLabel,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
tFile := nest.getTemplateFilePath(tName)
|
||||||
|
fileInfo, err := os.Stat(tFile)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("error getting file info: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tIndex, exists := nest.cache[tName]
|
||||||
|
|
||||||
|
// If cache doesn't exist or has expired, re-index the file.
|
||||||
|
if !exists || fileInfo.ModTime().After(tIndex.LastModified) {
|
||||||
|
newIndex, err := nest.index(tFile)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("file index failed: %w", err)
|
||||||
|
}
|
||||||
|
tIndex = newIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
if nest.option.DieOnBadParams {
|
||||||
|
for k, _ := range hash {
|
||||||
|
// If a variable in template hash is not present in template
|
||||||
|
// file and it's not the template label then it's a bad param.
|
||||||
|
_, exists := tIndex.VariableNames[k]
|
||||||
|
if !exists && k != nest.option.NameLabel {
|
||||||
|
return "", fmt.Errorf(
|
||||||
|
"bad params in template hash, variable not present in template file: `%s`", k,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rendered := tIndex.Contents
|
||||||
|
for i := len(tIndex.Variables) - 1; i >= 0; i-- {
|
||||||
|
variable := tIndex.Variables[i]
|
||||||
|
if variable.EscapedToken {
|
||||||
|
rendered = rendered[:variable.StartPosition] + rendered[variable.EndPosition:]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the variable doesn't exist in template hash then replace it
|
||||||
|
// with an empty string.
|
||||||
|
replacement := ""
|
||||||
|
|
||||||
|
value, exists := hash[variable.Name]
|
||||||
|
defaultValue, defaultExists := nest.defaultsFlat[variable.Name]
|
||||||
|
|
||||||
|
if exists || defaultExists {
|
||||||
|
if !exists {
|
||||||
|
value = defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
subRender, err := nest.Render(value)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if nest.option.FixedIndent && variable.IndentLevel != 0 {
|
||||||
|
indentReplacement := "\n" + strings.Repeat(" ", int(variable.IndentLevel))
|
||||||
|
subRender = strings.ReplaceAll(subRender, "\n", indentReplacement)
|
||||||
|
}
|
||||||
|
|
||||||
|
replacement = subRender
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace in rendered template
|
||||||
|
rendered = rendered[:variable.StartPosition] + replacement + rendered[variable.EndPosition:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if nest.option.ShowLabels {
|
||||||
|
labelStart := fmt.Sprintf(
|
||||||
|
"%s BEGIN %s %s\n",
|
||||||
|
nest.option.CommentDelimiters[0], tName, nest.option.CommentDelimiters[1],
|
||||||
|
)
|
||||||
|
labelEnd := fmt.Sprintf(
|
||||||
|
"%s END %s %s\n",
|
||||||
|
nest.option.CommentDelimiters[0], tName, nest.option.CommentDelimiters[1],
|
||||||
|
)
|
||||||
|
rendered = labelStart + rendered + labelEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimSpace(rendered), nil
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user