/tempe

Functional templating library for go

Primary LanguageGo

tempe

go get github.com/RazorSh4rk/tempe

Replace string templates with functions

Tempe (tem-pee) is a small library that lets you create chainable replacers with dynamic functions embedded in them to transform text. Originally made as part of a terminal chatGPT client, but I extracted the code because it could be useful for other apps too.

Note: like 3 minutes after I made this I found fasttemplate which does kinda the same thing, so if you prefer that syntax or just hate me personally, that's a good alternative.

Docs

Tempe comes with 2 structs:

type Sub struct {
	Key      string
	Value    string
	Function func(string, int) string
	Repeat   bool
	Regex    bool
}

Sub{...}.Apply(&yourString)

This is the base of the library, it represents one substitution.

  • Key: string or regex string to replace

  • Value: string to replace with, or empty

  • Function: a function that gets called for a replacement. The inputs are the found match, and the index of the found match. Return any string to act as a replacement.

If you are using regex, the entire match will be returned, for example {{myKey}}

If you are NOT using regex, the function will be called with ("", 0) every time. If you want to have access to these variables, just set Regex to true, it will still match standard strings.

  • Repeat: replace the first hit (false) or all of them (true)

  • Regex: use regex matching (true) or simple string matching (false)

type Subs struct {
	Subs        []Sub
	FailOnErr   bool
	ErrCallback func(error, int)
}

Subs{...}.ApplyAll(&yourString)

This is a chain of replacers that can all be called in order and can be supplied with a callback in case an error happens. Useful if the replacers are user supplied or if you are building a template engine.

  • ErrCallback: will receive the error and the index of the replacer which received the error.

Examples

There are a lot of examples in the test file, but here are the basics:

There is a real world usage example in the /example folder

Replace a thing with an other thing

s := "hello world"
template := tempe.Sub{
	Key:   "hello",
	Value: "bye",
}
template.Apply(&s)
// bye world

or, without storing the sub:

s := "hello world"
(&tempe.Sub{
	Key:   "hello",
	Value: "bye",
}).Apply(&s)

replace a string with a function

s := "host is: /hname/"
(&tempe.Sub{
	Key: "/hname/",
	Function: func(s string, i int) string {
		name, _ := os.Hostname()
		return name
	},
}).Apply(&s)
// host is: yourhost.name

replace the first match of a regex with a function based on the matched value

s := "cats cats dog cats"
(&tempe.Sub{
	Key:    "dog(s?)",
	Regex:  true,
	Repeat: false,
	Function: func(s string, i int) string {
		switch s {
		case "dog":
			return "cat"
		case "dogs":
			return "cats"
		}
		return ""
	},
}).Apply(&s)
// cats cats cat cats

replace a regex with the index of where it was found

s := "let's count num num num num num"
template := tempe.Sub{
	Key:    "num",
	Regex:  true,
	Repeat: true,
	Function: func(s string, i int) string {
		return fmt.Sprint(i)
	},
}
template.Apply(&s)
// let's count 0 1 2 3 4

// setting the Regex to false here would result in
// let's count 0 0 0 0 0

replace multiple regex matches based on their values

s := "{name1} and {name2} and {name3}"
template := tempe.Sub{
	Key:    "{name[1-3]}",
	Regex:  true,
	Repeat: true,
	Function: func(s string, i int) string {
		var name string
		switch s {
		case "{name1}":
			name = "Joe Swanson"
		case "{name2}":
			name = "Peter Griffin"
		case "{name3}":
			name = "Glenn Quagmire"
		}
		return name
	},
}
template.Apply(&s)
// Joe Swanson and Peter Griffin and Glenn Quagmire

chain multiple replaces

This works with any of the previous replacers too

s := "the time is {{hour}}h {{minute}}m"
templates := tempe.Subs{
	Subs: []tempe.Sub{
		{
			Key:   "{{hour}}",
			Value: "12",
		},
		{
			Key:   "{{minute}}",
			Value: "00",
		},
	},
}
templates.ApplyAll(&s)
// the time is 12h 00m