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.
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.
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
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)
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
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
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
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
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