Help us shape the future of Go! Take the 2026 Go Developer Survey

twist

package module
v1.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 6, 2025 License: MIT Imports: 6 Imported by: 0

README

🌀 twist

A reversible template package for Go — render structured text and parse it back into data.

Overview

Twist is a lightweight Go library for creating round-trippable string templates. You can use it to:

  • Render text from Go structs or maps
  • Parse that text back into structured data It's ideal for cases like logs, filenames, simple DSLs, and more.

Why Twist?

Traditional templating libraries let you generate text, but don't help you turn that text back into structured data. Twist bridges that gap, making it easy to round-trip between text and Go data structures

Examples

See the go docs for all examples.

A basic example is illustrated below:

twist := MustNew("{{ Greeting }}, {{ Subject }}!")

data := map[string]string{
	"Greeting": "Hello",
	"Subject":  "World",
}
message := twist.MustExecute(data)
fmt.Printf("%#v\n", message)
// "Hello, World!"

fields, _ := twist.ParseToMap("Hello, World!")
fmt.Printf("%#v\n", fields)
// map[string]string{"Greeting":"Hello", "Subject":"World"}

Features

  • Simple template syntax using {{field}} delimiters (configuration exists for custom delimiters).
  • Generate strings from templates using struct or map data.
  • Parse strings back into struct or map data.
  • Handle ambiguous matches, if a string matches the template in multiple ways, Twist returns all possible structured interpretations.
  • Type-safe parsing into Go structs.

Installation

go get github.com/shbroster/twist

License

The project is licensed under the MIT License.

Documentation

Overview

Example
data := map[string]string{
	"Greeting": "Hello",
	"Subject":  "World",
}
twist := MustNew("{{ Greeting }}, {{ Subject }}!")
message := twist.MustExecute(data)
fmt.Printf("%#v\n", message)

fields, _ := twist.ParseToMap(message)
fmt.Printf("%#v\n", fields)
Output:
"Hello, World!"
map[string]string{"Greeting":"Hello", "Subject":"World"}
Example (With_structs)
type Data struct {
	Greeting string
	Person   string
	Age      int
}

input := Data{
	Greeting: "Happy Birthday",
	Person:   "Annie",
	Age:      6,
}
twist := MustNew("{{ Greeting }}, {{ Person }}. You're {{ Age }} years old today!")
message := twist.MustExecute(input)
fmt.Printf("%#v\n", message)

var output Data
twist.Parse(message, &output)
fmt.Printf("%#v\n", output)
Output:
"Happy Birthday, Annie. You're 6 years old today!"
twist.Data{Greeting:"Happy Birthday", Person:"Annie", Age:6}

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrTwist is the base error for all twist errors.
	ErrTwist = errors.New("twist error")

	// ErrInvalidConfig is returned when the TwistConfig is invalid.
	ErrInvalidConfig = fmt.Errorf("%w: invalid config", ErrTwist)

	// ErrInvalidTemplate is returned when creating a new twist with an invalid template.
	ErrInvalidTemplate = fmt.Errorf("%w: invalid template", ErrTwist)

	// ErrInvalidData is returned when attemping to execute or parse a template with invalid data.
	ErrInvalidData = fmt.Errorf("%w: invalid data", ErrTwist)

	// ErrTemplateMismatch is returned when parsing a string which does not match the original template.
	ErrTemplateMismatch = fmt.Errorf("%w: template mismatch", ErrTwist)

	// ErrAmbiguousTemplate is returned when attempting to get a unique data set from a template
	// which has multiple possible data sets.
	ErrAmbiguousTemplate = fmt.Errorf("%w: template is ambiguous", ErrTwist)
)

Functions

func WithDelimiters

func WithDelimiters(delimiters [2]string) twistOption

When creating a 'twist' with `New` this function creates a twist with a custom set of delimeters, by default '{{' and '}}' are used as delimeters.

func WithUnique

func WithUnique() executeOption

When `Executing` a template this option causes an error to be returned if the resulting string cannot be used to recreate the original data passed into the template.

Types

type Twist added in v1.0.0

type Twist struct {
	// contains filtered or unexported fields
}

Twist - a reversible template

func MustNew

func MustNew(s string, opts ...twistOption) Twist

MustNew is a convenience function that wraps `New` and panics if the template is invalid.

func New

func New(s string, opts ...twistOption) (Twist, error)

New creates a 'twist' and errors if the template is invald.

Twists are reversible templates that can be used to create basic string template using {{ and }} as delimeters by default.

Example (Custom_delimeters)
data := map[string]string{
	"Greeting": "Hello",
	"Subject":  "World",
}
twist := MustNew("[ Greeting ], [ Subject]!", WithDelimiters([2]string{"[", "]"}))
message := twist.MustExecute(data)
fmt.Printf("%#v\n", message)
Output:
"Hello, World!"
Example (Error)
_, err := New("{{ Greeting }}, {{ Subject!")
fmt.Println(err)
Output:
unmatched delimiters: twist error: invalid template

func (Twist) Execute added in v1.0.0

func (t Twist) Execute(data any, opts ...executeOption) (string, error)

Execute executes the template with the given data and returns the generated string.

Example (Error_missing_field)
data := map[string]string{
	"Greeting": "Hello",
}
twist, _ := New("{{ Greeting }}, {{ Subject }}!")
_, err := twist.Execute(data)
fmt.Println(err)
Output:
field 'Subject' is missing: twist error: invalid data
Example (Error_not_unique)
data := map[string]string{
	"Greeting": "Good Night",
	"Subject":  "Mr. Tom",
}
twist := MustNew("{{ Greeting }} {{ Subject }}!")
_, err := twist.Execute(data, WithUnique())
fmt.Println(err)
Output:
multiple mathces: twist error: template is ambiguous

func (Twist) MustExecute added in v1.0.0

func (t Twist) MustExecute(data any, opts ...executeOption) string

MustExecute is a convenience function that wraps `Execute` and panics if it returns an error.

func (Twist) Parse added in v1.0.0

func (t Twist) Parse(s string, out any) error

Parse takes a string generated by executing a template and returns the original data that was used when executing the template.

The parsed data is cast to the appropriate type and stored in the provided struct.

If the provided string could have been generated using multiple different data sets then this function errors. The ParseToMaps function can be used to get all possible data sets.

func (Twist) ParseToMap added in v1.0.0

func (t Twist) ParseToMap(s string) (map[string]string, error)

ParseToMap takes a string generated by executing a template and returns the unique set of data that was used to generate the given string from the template.

If there is not a unique set of data then this function errors. The ParseToMaps function can be used to get all possible data sets.

Example (Error_ambiguous)
message := "Good Night Mr. Tom!"
twist := MustNew("{{ Greeting }} {{ Subject }}!")
_, err := twist.ParseToMap(message)
fmt.Println(err)
Output:
multiple matches: twist error: template is ambiguous

func (Twist) ParseToMaps added in v1.0.0

func (t Twist) ParseToMaps(s string) ([]map[string]string, error)

ParseToMaps takes a string generated by executing a template and returns all possible data sets could have generatd string from the given template.

Example
message := "Good Night Mr. Tom!"
twist := MustNew("{{ Greeting }} {{ Subject }}!")
fieldMaps, _ := twist.ParseToMaps(message)
for _, fieldMap := range fieldMaps {
	fmt.Println(fieldMap)
}
Output:
map[Greeting:Good Subject:Night Mr. Tom]
map[Greeting:Good Night Subject:Mr. Tom]
map[Greeting:Good Night Mr. Subject:Tom]

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL