Allow defaults for template parameter

This commit is contained in:
Ingo Oppermann 2022-12-27 13:41:07 +01:00
parent 1a9ef8b7c9
commit ee2a188be8
No known key found for this signature in database
GPG key ID: 2AB32426E9DD229E
2 changed files with 71 additions and 16 deletions

View file

@ -9,12 +9,13 @@ import (
type Replacer interface { type Replacer interface {
// RegisterTemplate registers a template for a specific placeholder. Template // RegisterTemplate registers a template for a specific placeholder. Template
// may contain placeholders as well of the form {name}. They will be replaced // may contain placeholders as well of the form {name}. They will be replaced
// by the parameters of the placeholder (see Replace). // by the parameters of the placeholder (see Replace). If a parameter is not of
RegisterTemplate(placeholder, template string) // a template is not present, default values can be provided.
RegisterTemplate(placeholder, template string, defaults map[string]string)
// RegisterTemplateFunc does the same as RegisterTemplate, but the template // RegisterTemplateFunc does the same as RegisterTemplate, but the template
// is returned by the template function. // is returned by the template function.
RegisterTemplateFunc(placeholder string, template func() string) RegisterTemplateFunc(placeholder string, template func() string, defaults map[string]string)
// Replace replaces all occurences of placeholder in str with value. The placeholder is of the // Replace replaces all occurences of placeholder in str with value. The placeholder is of the
// form {placeholder}. It is possible to escape a characters in value with \\ by appending a ^ // form {placeholder}. It is possible to escape a characters in value with \\ by appending a ^
@ -28,8 +29,13 @@ type Replacer interface {
Replace(str, placeholder, value string) string Replace(str, placeholder, value string) string
} }
type template struct {
fn func() string
defaults map[string]string
}
type replacer struct { type replacer struct {
templates map[string]func() string templates map[string]template
re *regexp.Regexp re *regexp.Regexp
templateRe *regexp.Regexp templateRe *regexp.Regexp
@ -38,7 +44,7 @@ type replacer struct {
// New returns a Replacer // New returns a Replacer
func New() Replacer { func New() Replacer {
r := &replacer{ r := &replacer{
templates: make(map[string]func() string), templates: make(map[string]template),
re: regexp.MustCompile(`{([a-z]+)(?:\^(.))?(?:,(.*?))?}`), re: regexp.MustCompile(`{([a-z]+)(?:\^(.))?(?:,(.*?))?}`),
templateRe: regexp.MustCompile(`{([a-z]+)}`), templateRe: regexp.MustCompile(`{([a-z]+)}`),
} }
@ -46,12 +52,18 @@ func New() Replacer {
return r return r
} }
func (r *replacer) RegisterTemplate(placeholder, template string) { func (r *replacer) RegisterTemplate(placeholder, tmpl string, defaults map[string]string) {
r.templates[placeholder] = func() string { return template } r.templates[placeholder] = template{
fn: func() string { return tmpl },
defaults: defaults,
}
} }
func (r *replacer) RegisterTemplateFunc(placeholder string, template func() string) { func (r *replacer) RegisterTemplateFunc(placeholder string, tmplFn func() string, defaults map[string]string) {
r.templates[placeholder] = template r.templates[placeholder] = template{
fn: tmplFn,
defaults: defaults,
}
} }
func (r *replacer) Replace(str, placeholder, value string) string { func (r *replacer) Replace(str, placeholder, value string) string {
@ -63,16 +75,20 @@ func (r *replacer) Replace(str, placeholder, value string) string {
// We need a copy from the value // We need a copy from the value
v := value v := value
var tmpl template = template{
fn: func() string { return v },
}
// Check for a registered template // Check for a registered template
if len(v) == 0 { if len(v) == 0 {
tmplFunc, ok := r.templates[placeholder] t, ok := r.templates[placeholder]
if ok { if ok {
v = tmplFunc() tmpl = t
} }
} }
v = r.compileTemplate(v, matches[3]) v = tmpl.fn()
v = r.compileTemplate(v, matches[3], tmpl.defaults)
if len(matches[2]) != 0 { if len(matches[2]) != 0 {
// If there's a character to escape, we also have to escape the // If there's a character to escape, we also have to escape the
@ -97,13 +113,18 @@ func (r *replacer) Replace(str, placeholder, value string) string {
// placeholder name and will be replaced with the value. The resulting string is "Hello World!". // placeholder name and will be replaced with the value. The resulting string is "Hello World!".
// If a placeholder name is not present in the params string, it will not be replaced. The key // If a placeholder name is not present in the params string, it will not be replaced. The key
// and values can be escaped as in net/url.QueryEscape. // and values can be escaped as in net/url.QueryEscape.
func (r *replacer) compileTemplate(str, params string) string { func (r *replacer) compileTemplate(str, params string, defaults map[string]string) string {
if len(params) == 0 { if len(params) == 0 && len(defaults) == 0 {
return str return str
} }
p := make(map[string]string) p := make(map[string]string)
// Copy the defaults
for key, value := range defaults {
p[key] = value
}
// taken from net/url.ParseQuery // taken from net/url.ParseQuery
for params != "" { for params != "" {
var key string var key string

View file

@ -34,7 +34,7 @@ func TestReplace(t *testing.T) {
func TestReplaceTemplate(t *testing.T) { func TestReplaceTemplate(t *testing.T) {
r := New() r := New()
r.RegisterTemplate("foobar", "Hello {who}! {what}?") r.RegisterTemplate("foobar", "Hello {who}! {what}?", nil)
replaced := r.Replace("{foobar,who=World}", "foobar", "") replaced := r.Replace("{foobar,who=World}", "foobar", "")
require.Equal(t, "Hello World! {what}?", replaced) require.Equal(t, "Hello World! {what}?", replaced)
@ -46,6 +46,20 @@ func TestReplaceTemplate(t *testing.T) {
require.Equal(t, "Hello World! E=mc\\\\:2?", replaced) require.Equal(t, "Hello World! E=mc\\\\:2?", replaced)
} }
func TestReplaceTemplateDefaults(t *testing.T) {
r := New()
r.RegisterTemplate("foobar", "Hello {who}! {what}?", map[string]string{
"who": "someone",
"what": "something",
})
replaced := r.Replace("{foobar}", "foobar", "")
require.Equal(t, "Hello someone! something?", replaced)
replaced = r.Replace("{foobar,who=World}", "foobar", "")
require.Equal(t, "Hello World! something?", replaced)
}
func TestReplaceCompileTemplate(t *testing.T) { func TestReplaceCompileTemplate(t *testing.T) {
samples := [][3]string{ samples := [][3]string{
{"Hello {who}!", "who=World", "Hello World!"}, {"Hello {who}!", "who=World", "Hello World!"},
@ -58,7 +72,27 @@ func TestReplaceCompileTemplate(t *testing.T) {
r := New().(*replacer) r := New().(*replacer)
for _, e := range samples { for _, e := range samples {
replaced := r.compileTemplate(e[0], e[1]) replaced := r.compileTemplate(e[0], e[1], nil)
require.Equal(t, e[2], replaced, e[0])
}
}
func TestReplaceCompileTemplateDefaults(t *testing.T) {
samples := [][3]string{
{"Hello {who}!", "", "Hello someone!"},
{"Hello {who}!", "who=World", "Hello World!"},
{"Hello {who}! {what}?", "who=World", "Hello World! something?"},
{"Hello {who}! {what}?", "who=World,what=Yeah", "Hello World! Yeah?"},
{"Hello {who}! {what}?", "who=World,what=", "Hello World! ?"},
}
r := New().(*replacer)
for _, e := range samples {
replaced := r.compileTemplate(e[0], e[1], map[string]string{
"who": "someone",
"what": "something",
})
require.Equal(t, e[2], replaced, e[0]) require.Equal(t, e[2], replaced, e[0])
} }
} }