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

proposal: text/template: Throw an error on missing variable while parsing the template #60073

Closed
droslean opened this issue May 9, 2023 · 5 comments

Comments

@droslean
Copy link

droslean commented May 9, 2023

Currently the text/template package forces the user to use maps in order to force an error on missing variables.

missingkey=error option works only when trying to parse a template string by passing a map of values.

With this issue, I am proposing to add a new option for empty or missing variables in the struct that is given as input to parse the template.

Working example using a map:

package main

import (
	"bytes"
	"text/template"
)

func main() {
	data := map[string]interface{}{
		"TestVar1": "test-1",
		"TestVar2": "test-2",
	}

	inputText := "{{.TestVar1}} {{.TestVar2}} {{.TestVar3}}"
	t, err := template.New("input").Parse(inputText)
	if err != nil {
		panic(err)
	}

	var buff []byte
	outputBuffer := bytes.NewBuffer(buff)

	if err := t.Option("missingkey=error").Execute(outputBuffer, data); err != nil {
		panic(err)
	}
}

Non-working example using a struct:

package main

import (
	"bytes"
	"text/template"
)

type Data struct {
	TestVar1 string
	TestVar2 string
	TestVar3 string
}

func main() {
	data := Data{
		TestVar1: "test-1",
		TestVar2: "test-2",
	}

	inputText := "{{.TestVar1}} {{.TestVar2}} {{.TestVar3}}"
	t, err := template.New("input").Parse(inputText)
	if err != nil {
		panic(err)
	}

	var buff []byte
	outputBuffer := bytes.NewBuffer(buff)

	if err := t.Option("missingkey=error").Execute(outputBuffer, data); err != nil {
		panic(err)
	}
}
@gopherbot gopherbot added this to the Proposal milestone May 9, 2023
@seankhliao
Copy link
Member

the struct field has a value, it's zero value the empty string
I don't see anything to do here.

@seankhliao seankhliao closed this as not planned Won't fix, can't repro, duplicate, stale May 9, 2023
@droslean
Copy link
Author

droslean commented May 9, 2023

@seankhliao Thanks for the response. Let me be more detailed in my explanation. In the examples, I added in the description, the struct field has an empty string. But the problem in question is the same with nil values too.

What I am suggesting here, is to add a new template Option to force an error on empty/nil values. Currently, the template will throw an error if there is a Variable in the input text that doesn't exist in the struct as a field, but it doesn't cover the case where the field actually exists but is empty/nil based on the type of the field.

It would be nice if there was an Option something like emptyvalues=error

@seankhliao
Copy link
Member

I don't think this is something that belongs in options, which more closely reflect go language constructs.
Something like helm's required function should be closer to what you want, and doesn't need to be in the template language itself

@droslean
Copy link
Author

@seankhliao But we already support this idea for maps. And since the template can accept a struct as well, I don't see why we shouldn't support the option for structs as well.

An example workaround that we are forced to use is something like this:

func structToMap(input interface{}) map[string]interface{} {
	result := make(map[string]interface{})
	v := reflect.ValueOf(input)
	if v.Kind() == reflect.Ptr {
		v = v.Elem()
	}
	if v.Kind() != reflect.Struct {
		panic("Input is not a struct")
	}
	typ := v.Type()
	for i := 0; i < v.NumField(); i++ {
		field := v.Field(i)
		if !field.IsZero() {
			result[typ.Field(i).Name] = field.Interface()
		}
	}
	return result
}

I don't see any other way to make sure that the template was successfully parsed since the package doesn't even return an error for missing data values.

@seankhliao
Copy link
Member

No, we support the equivalent of v, ok := m[key]; !ok, not v := m[key]; v != ""

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants