Skip to content

Commit

Permalink
✨ feat(str/textutil): text.VarReplacer support string-map as replace …
Browse files Browse the repository at this point in the history
…context

- support replace var only with left mark
- update and enhance replace logic
  • Loading branch information
inhere committed Mar 2, 2023
1 parent 69dbc9a commit 9374e19
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 8 deletions.
2 changes: 2 additions & 0 deletions strutil/convert.go
Expand Up @@ -41,6 +41,8 @@ var (
func Quote(s string) string { return strconv.Quote(s) }

// Unquote remove start and end quotes by single-quote or double-quote
//
// tip: strconv.Unquote cannot unquote single-quote
func Unquote(s string) string {
ln := len(s)
if ln < 2 {
Expand Down
4 changes: 2 additions & 2 deletions strutil/strutil.go
Expand Up @@ -19,11 +19,11 @@ func OrCond(cond bool, s1, s2 string) string {
}

// OrElse return s OR nv(new-value) on s is empty
func OrElse(s, newVal string) string {
func OrElse(s, orVal string) string {
if s != "" {
return s
}
return newVal
return orVal
}

// OrHandle return fn(s) on s is not empty.
Expand Down
7 changes: 7 additions & 0 deletions strutil/textutil/textutil.go
Expand Up @@ -17,6 +17,13 @@ func ReplaceVars(text string, vars map[string]any, format string) string {
return NewVarReplacer(format).Replace(text, vars)
}

// RenderSMap by regex replace given tpl vars.
//
// If format is empty, will use {const defaultVarFormat}
func RenderSMap(text string, vars map[string]string, format string) string {
return NewVarReplacer(format).RenderSimple(text, vars)
}

// IsMatchAll keywords in the give text string.
//
// TIP: can use ^ for exclude match.
Expand Down
40 changes: 38 additions & 2 deletions strutil/textutil/textutil_test.go
Expand Up @@ -9,7 +9,6 @@ import (
)

func TestReplaceVars(t *testing.T) {
format := ""
tplVars := map[string]any{
"name": "inhere",
"key_01": "inhere",
Expand All @@ -33,17 +32,54 @@ func TestReplaceVars(t *testing.T) {

for i, tt := range tests {
t.Run(strutil.JoinAny(" ", "case", i), func(t *testing.T) {
if got := textutil.ReplaceVars(tt.tplText, tplVars, format); got != tt.want {
if got := textutil.ReplaceVars(tt.tplText, tplVars, ""); got != tt.want {
t.Errorf("ReplaceVars() = %v, want = %v", got, tt.want)
}
})
}

// custom format
assert.Equal(t, "hi inhere", textutil.ReplaceVars("hi {$name}", tplVars, "{$,}"))
assert.Equal(t, "hi inhere age is 230", textutil.ReplaceVars("hi $name age is $info.age", tplVars, "$,"))
assert.Equal(t, "hi {$name}", textutil.ReplaceVars("hi {$name}", nil, "{$,}"))
}

func TestRenderSMap(t *testing.T) {
tplVars := map[string]string{
"name": "inhere",
"age": "234",
"key_01": "inhere",
"key-02": "inhere",
}

tests := []struct {
tplText string
want string
}{
{"hi inhere", "hi inhere"},
{"hi {{name}}", "hi inhere"},
{"hi {{ name}}", "hi inhere"},
{"hi {{name }}", "hi inhere"},
{"hi {{ name }}", "hi inhere"},
{"hi {{ key_01 }}", "hi inhere"},
{"hi {{ key-02 }}", "hi inhere"},
}

for i, tt := range tests {
t.Run(strutil.JoinAny(" ", "case", i), func(t *testing.T) {
if got := textutil.RenderSMap(tt.tplText, tplVars, ""); got != tt.want {
t.Errorf("RenderSMap() = %v, want = %v", got, tt.want)
}
})
}

// custom format
assert.Equal(t, "hi inhere", textutil.RenderSMap("hi {$name}", tplVars, "{$,}"))
assert.Equal(t, "hi inhere age is 234", textutil.RenderSMap("hi $name age is $age", tplVars, "$,"))
assert.Equal(t, "hi inhere age is 234.", textutil.RenderSMap("hi $name age is $age.", tplVars, "$,"))
assert.Equal(t, "hi {$name}", textutil.RenderSMap("hi {$name}", nil, "{$,}"))
}

func TestIsMatchAll(t *testing.T) {
str := "hi inhere, age is 120"
assert.True(t, textutil.IsMatchAll(str, []string{"hi", "inhere"}))
Expand Down
25 changes: 21 additions & 4 deletions strutil/textutil/var_replacer.go
Expand Up @@ -35,25 +35,42 @@ func (r *VarReplacer) WithFormat(format string) *VarReplacer {
func (r *VarReplacer) Init() *VarReplacer {
if !r.init {
r.lLen, r.rLen = len(r.Left), len(r.Right)
r.varReg = regexp.MustCompile(regexp.QuoteMeta(r.Left) + `([\w\s.-]+)` + regexp.QuoteMeta(r.Right))
if r.Right != "" {
r.varReg = regexp.MustCompile(regexp.QuoteMeta(r.Left) + `([\w\s.-]+)` + regexp.QuoteMeta(r.Right))
} else {
// no right tag. eg: $name, $user.age
// r.varReg = regexp.MustCompile(regexp.QuoteMeta(r.Left) + `([\w.-]+)`)
r.varReg = regexp.MustCompile(regexp.QuoteMeta(r.Left) + `(\w[\w-]*(?:\.[\w-]+)*)`)
}
}

return r
}

// Replace vars in the text contents
// Replace any-map vars in the text contents
func (r *VarReplacer) Replace(s string, tplVars map[string]any) string {
if len(tplVars) == 0 || !strings.Contains(s, r.Left) {
return s
}

r.Init()

varMap := make(map[string]string, len(tplVars)*2)
maputil.FlatWithFunc(tplVars, func(path string, val reflect.Value) {
varMap[path] = strutil.QuietString(val.Interface())
})

return r.Init().doReplace(s, varMap)
}

// RenderSimple string-map vars in the text contents
func (r *VarReplacer) RenderSimple(s string, varMap map[string]string) string {
if len(varMap) == 0 || !strings.Contains(s, r.Left) {
return s
}
return r.Init().doReplace(s, varMap)
}

// Replace string-map vars in the text contents
func (r *VarReplacer) doReplace(s string, varMap map[string]string) string {
return r.varReg.ReplaceAllStringFunc(s, func(sub string) string {
varName := strings.TrimSpace(sub[r.lLen : len(sub)-r.rLen])
if val, ok := varMap[varName]; ok {
Expand Down

0 comments on commit 9374e19

Please sign in to comment.