Skip to content

proposal: text/template: add reverse Execute method (parse text into struct) #52473

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

Closed
ianling opened this issue Apr 21, 2022 · 8 comments
Closed

Comments

@ianling
Copy link

ianling commented Apr 21, 2022

text/template currently allows a developer to pass in a struct to print the struct's fields in an arbitrary format. Here is the example from the pkg.go.dev page that prints "17 items are made of wool"

type Inventory struct {
	Material string
	Count    uint
}
sweaters := Inventory{"wool", 17}
tmpl, _ := template.New("test").Parse("{{.Count}} items are made of {{.Material}}")
_ = tmpl.Execute(os.Stdout, sweaters)

I propose that a method be added that allows the reverse of this operation: parsing data from a body of text into the given struct, similar to unmarshalling JSON.

The following example parses the Count and Material fields out of the textToParse string and stores the values in sweaters. It would then print the data to stdout using the same template that was used to parse the data.

type Inventory struct {
	Material string
	Count    uint
}
tmpl, _ := template.New("test").Parse("{{.Count}} items are made of {{.Material}}")

var sweaters Inventory
textToParse := "17 items are made of wool"
_ = tmpl.ReverseExecute(textToParse, &sweaters) // this is the new thing being proposed
_ = tmpl.Execute(os.Stdout, sweaters)

(ReverseExecute is a bad name for this method and should be changed.)

Compared to alternative approaches, such as parsing values out of a string using regex, this has the benefit of using one thing (a template) to perform both the parsing and writing operations. Passing a struct into this method also removes much of the manual work and wheel re-invention a developer would need to go through to parse arbitrary text into a struct, with regards to type checks and casting.

Potential issues for discussion:

  • How to handle excess whitespace?
    • For example, what if the template is only expecting one newline character between values, but text is given with two newlines? Should there be an option to ignore extra whitespace?
  • Should the template be "fuzzy", or allow lines to appear out of order?
@gopherbot gopherbot added this to the Proposal milestone Apr 21, 2022
@ianlancetaylor ianlancetaylor changed the title proposal: text/template: Add reverse Execute method (parse text into struct) proposal: text/template: add reverse Execute method (parse text into struct) Apr 21, 2022
@ianlancetaylor
Copy link
Contributor

That is very different from how text/template operates today. This might be better implemented as a completely different package, perhaps using the text/template/parse package.

@ianling
Copy link
Author

ianling commented Apr 21, 2022

The more I think about it, the more I agree.

There are several features that seem pretty complicated to me at a glance that I think would be necessary to making this useful outside of extremely specific situations. I added a couple implementation problems to the bottom of the proposal.

To expand on the "fuzziness" issue: if I have the following text and I want to parse the two values on the right side:

PackageVersion: 2.11.1
PackageFileName: glibc-2.11.1.tar.gz

That's pretty easy. But how can a solution be implemented that allows the lines to appear in an arbitrary order, like reversed for example:

PackageFileName: glibc-2.11.1.tar.gz
PackageVersion: 2.11.1

Or what about with another thing in between them:

PackageFileName: glibc-2.11.1.tar.gz
PackageDownloadLocation: http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz
PackageVersion: 2.11.1

Or a simpler problem, what if there is just an extra newline in the first example:

PackageVersion: 2.11.1

PackageFileName: glibc-2.11.1.tar.gz

Lots of situations to consider that could hinder the usefulness of this feature if these things aren't accounted for.

@mvdan
Copy link
Member

mvdan commented Apr 21, 2022

This also seems practically impossible to implement in general. If a template has logic like range loops or conditionals, how is the "reverse execution" meant to handle those?

@rittneje
Copy link
Contributor

As @mvdan mentioned, reversing a template is impossible in general. For example, given the template "{{range $x := .}}{{$x}}{{end}}" and the template output "ab", was the original list []string{"a", "b"} or []string{"ab"}? There's also the matter of function calls: the text/template package could not possibly know how to reverse some user-defined function.

For trivial cases like your first example, named captured groups seem like a good approach. For example, "{{.Count}} items are made of {{.Material}}" essentially maps to "^(?P<count>.+) items are made of (?P<material>.+)$". So maybe just adding a convenience API around the existing regexp package would be advantageous.

With regards to the line order insensitivity issue you mentioned, for something like that probably you should just go line by line through the input and parse as you go. Seems like it would be a combination strings.SplitN (on :) and strings.TrimSpace.

@rsc
Copy link
Contributor

rsc commented May 4, 2022

This proposal has been added to the active column of the proposals project
and will now be reviewed at the weekly proposal review meetings.
— rsc for the proposal review group

@ianlancetaylor
Copy link
Contributor

Closing as infeasible.

@rsc
Copy link
Contributor

rsc commented May 11, 2022

This proposal has been added to the active column of the proposals project
and will now be reviewed at the weekly proposal review meetings.
— rsc for the proposal review group

@rsc rsc moved this to Declined in Proposals Aug 10, 2022
@rsc rsc added this to Proposals Aug 10, 2022
@AkihiroSuda
Copy link
Contributor

Seems almost accomplished by third-party packages such as https://github.com/natekfl/untemplate and https://github.com/laktek/extract-values

@rsc rsc removed this from Proposals May 10, 2023
@golang golang locked and limited conversation to collaborators Oct 8, 2023
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

7 participants