Skip to content

Commit

Permalink
✨ feat(str/text): replacer support disable flatten vars map
Browse files Browse the repository at this point in the history
- add new method DisableFlatten(), ParseVars(), Render()
  • Loading branch information
inhere committed Mar 18, 2023
1 parent 9454259 commit f4abba1
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 14 deletions.
10 changes: 10 additions & 0 deletions strutil/check.go
Expand Up @@ -113,6 +113,16 @@ func HasSuffix(s string, suffix string) bool { return strings.HasSuffix(s, suffi
// IsEndOf alias of the strings.HasSuffix
func IsEndOf(s, suffix string) bool { return strings.HasSuffix(s, suffix) }

// HasOneSuffix the string end withs one of the subs
func HasOneSuffix(s string, suffixes []string) bool {
for _, suffix := range suffixes {
if strings.HasSuffix(s, suffix) {
return true
}
}
return false
}

// IsValidUtf8 valid utf8 string check
func IsValidUtf8(s string) bool { return utf8.ValidString(s) }

Expand Down
19 changes: 19 additions & 0 deletions strutil/textutil/textutil_test.go
Expand Up @@ -80,6 +80,25 @@ func TestRenderSMap(t *testing.T) {
assert.Equal(t, "hi {$name}", textutil.RenderSMap("hi {$name}", nil, "{$,}"))
}

func TestVarReplacer_ParseVars(t *testing.T) {
vp := textutil.NewVarReplacer("")
str := "hi {{ name }}, age {{age}}, age {{age }}"
ss := vp.ParseVars(str)

assert.NotEmpty(t, ss)
assert.Len(t, ss, 2)
assert.Contains(t, ss, "name")
assert.Contains(t, ss, "age")

tplVars := map[string]any{
"name": "inhere",
"age": 234,
}
assert.Equal(t, "hi inhere, age 234, age 234", vp.Render(str, tplVars))
vp.DisableFlatten()
assert.Equal(t, "hi inhere, age 234, age 234", vp.Render(str, tplVars))
}

func TestIsMatchAll(t *testing.T) {
str := "hi inhere, age is 120"
assert.True(t, textutil.IsMatchAll(str, []string{"hi", "inhere"}))
Expand Down
68 changes: 54 additions & 14 deletions strutil/textutil/var_replacer.go
Expand Up @@ -5,6 +5,7 @@ import (
"regexp"
"strings"

"github.com/gookit/goutil/arrutil"
"github.com/gookit/goutil/internal/comfunc"
"github.com/gookit/goutil/maputil"
"github.com/gookit/goutil/strutil"
Expand All @@ -14,20 +15,30 @@ const defaultVarFormat = "{{,}}"

// VarReplacer struct
type VarReplacer struct {
init bool
init bool

Left, Right string
lLen, rLen int

varReg *regexp.Regexp
// flatten sub map in vars
flatSubs bool
parseEnv bool
varReg *regexp.Regexp
missVars []string
}

// NewVarReplacer instance
func NewVarReplacer(format string) *VarReplacer {
return (&VarReplacer{}).WithFormat(format)
return (&VarReplacer{flatSubs: true}).WithFormat(format)
}

// WithParseEnv custom var template
// DisableFlatten on the input vars map
func (r *VarReplacer) DisableFlatten() *VarReplacer {
r.flatSubs = false
return r
}

// WithParseEnv on the input vars value
func (r *VarReplacer) WithParseEnv() *VarReplacer {
r.parseEnv = true
return r
Expand Down Expand Up @@ -56,24 +67,44 @@ func (r *VarReplacer) Init() *VarReplacer {
return r
}

// ParseVars the text contents and collect vars
func (r *VarReplacer) ParseVars(s string) []string {
ss := arrutil.StringsMap(r.varReg.FindAllString(s, -1), func(val string) string {
return strings.TrimSpace(val[r.lLen : len(val)-r.rLen])
})

return arrutil.Unique(ss)
}

// Render any-map vars in the text contents
func (r *VarReplacer) Render(s string, tplVars map[string]any) string {
return r.Replace(s, tplVars)
}

// 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
}

varMap := make(map[string]string, len(tplVars)*2)
maputil.FlatWithFunc(tplVars, func(path string, val reflect.Value) {
if val.Kind() == reflect.String {
if r.parseEnv {
varMap[path] = comfunc.ParseEnvVar(val.String(), nil)
var varMap map[string]string

if r.flatSubs {
varMap = make(map[string]string, len(tplVars)*2)
maputil.FlatWithFunc(tplVars, func(path string, val reflect.Value) {
if val.Kind() == reflect.String {
if r.parseEnv {
varMap[path] = comfunc.ParseEnvVar(val.String(), nil)
} else {
varMap[path] = val.String()
}
} else {
varMap[path] = val.String()
varMap[path] = strutil.QuietString(val.Interface())
}
} else {
varMap[path] = strutil.QuietString(val.Interface())
}
})
})
} else {
varMap = maputil.ToStringMap(tplVars)
}

return r.Init().doReplace(s, varMap)
}
Expand All @@ -98,13 +129,22 @@ func (r *VarReplacer) RenderSimple(s string, varMap map[string]string) string {
return r.Init().doReplace(s, varMap)
}

// MissVars list
func (r *VarReplacer) MissVars() []string {
return r.missVars
}

// Replace string-map vars in the text contents
func (r *VarReplacer) doReplace(s string, varMap map[string]string) string {
r.missVars = make([]string, 0) // clear each replace

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 {
return val
}

r.missVars = append(r.missVars, varName)
return sub
})
}

0 comments on commit f4abba1

Please sign in to comment.