Skip to content

Commit

Permalink
👔 up: update some and add new sub package
Browse files Browse the repository at this point in the history
  • Loading branch information
inhere committed Dec 5, 2023
1 parent a9f0587 commit f866ea4
Show file tree
Hide file tree
Showing 4 changed files with 300 additions and 0 deletions.
37 changes: 37 additions & 0 deletions .github/changelog.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
title: '## Change Log'
# style allow: simple, markdown(mkdown), ghr(gh-release)
style: gh-release
# group names
names: [Refactor, Fixed, Feature, Update, Other]
# if empty will auto fetch by git remote
#repo_url: https://github.com/gookit/goutil

filters:
# message length should >= 12
- name: msg_len
min_len: 12
# message words should >= 3
- name: words_len
min_len: 3
- name: keyword
keyword: format code
exclude: true
- name: keywords
keywords: format code, action test
exclude: true

# group match rules
# not matched will use 'Other' group.
rules:
- name: Refactor
start_withs: [refactor, break]
contains: ['refactor:']
- name: Fixed
start_withs: [fix]
contains: ['fix:']
- name: Feature
start_withs: [feat, new]
contains: [feature, 'feat:']
- name: Update
start_withs: [up]
contains: ['update:', 'up:']
10 changes: 10 additions & 0 deletions extends.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package easytpl

type ExtendsTpl struct {
Renderer
}

// NewExtends create a new extends template instance
func NewExtends() *ExtendsTpl {
return &ExtendsTpl{}
}
189 changes: 189 additions & 0 deletions litetpl/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
package litetpl

import (
"bytes"
"errors"
"fmt"
"io"
"reflect"
"regexp"
"strings"
"text/template"

"github.com/gookit/goutil/basefn"
"github.com/gookit/goutil/fsutil"
"github.com/gookit/goutil/strutil"
"github.com/gookit/goutil/strutil/textutil"
)

// StrTemplate implement a simple string template
//
// - support replace vars
// - support pipeline filter handle
// - support default value
// - support custom func
type StrTemplate struct {
textutil.VarReplacer
// Funcs template funcs. refer the text/template.Funcs
Funcs map[string]func(string) string
}

// NewStrTemplate instance
func NewStrTemplate(opFns ...func(st *StrTemplate)) *StrTemplate {
st := &StrTemplate{}
// st.WithFormat(defaultVarFormat)
st.RenderFn = st.renderVars

Check failure on line 35 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.18 and ubuntu-latest

st.RenderFn undefined (type *StrTemplate has no field or method RenderFn)

Check failure on line 35 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.19 and ubuntu-latest

st.RenderFn undefined (type *StrTemplate has no field or method RenderFn)

Check failure on line 35 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.20 and ubuntu-latest

st.RenderFn undefined (type *StrTemplate has no field or method RenderFn)

for _, fn := range opFns {
fn(st)
}
return st
}

// Init StrTemplate
func (t *StrTemplate) Init() {
if t.init {

Check failure on line 45 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.18 and ubuntu-latest

t.init undefined (type *StrTemplate has no field or method init, but does have Init)

Check failure on line 45 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.19 and ubuntu-latest

t.init undefined (type *StrTemplate has no field or method init, but does have Init)

Check failure on line 45 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.20 and ubuntu-latest

t.init undefined (type *StrTemplate has no field or method init, but does have Init)
return
}

basefn.PanicIf(t.Right == "", "var format Right chars is required")

Check failure on line 49 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.18 and ubuntu-latest

undefined: basefn.PanicIf

Check failure on line 49 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.19 and ubuntu-latest

undefined: basefn.PanicIf

Check failure on line 49 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.20 and ubuntu-latest

undefined: basefn.PanicIf

t.lLen, t.rLen = len(t.Left), len(t.Right)

Check failure on line 51 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.18 and ubuntu-latest

t.lLen undefined (type *StrTemplate has no field or method lLen)

Check failure on line 51 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.18 and ubuntu-latest

t.rLen undefined (type *StrTemplate has no field or method rLen)

Check failure on line 51 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.19 and ubuntu-latest

t.lLen undefined (type *StrTemplate has no field or method lLen)

Check failure on line 51 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.19 and ubuntu-latest

t.rLen undefined (type *StrTemplate has no field or method rLen)

Check failure on line 51 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.20 and ubuntu-latest

t.lLen undefined (type *StrTemplate has no field or method lLen)

Check failure on line 51 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.20 and ubuntu-latest

t.rLen undefined (type *StrTemplate has no field or method rLen)
t.varReg = regexp.MustCompile(regexp.QuoteMeta(t.Left) + `(.+)` + regexp.QuoteMeta(t.Right))

Check failure on line 52 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.18 and ubuntu-latest

t.varReg undefined (type *StrTemplate has no field or method varReg)

Check failure on line 52 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.19 and ubuntu-latest

t.varReg undefined (type *StrTemplate has no field or method varReg)

Check failure on line 52 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.20 and ubuntu-latest

t.varReg undefined (type *StrTemplate has no field or method varReg)
}

func (t *StrTemplate) renderVars(s string, varMap map[string]string) string {
return t.varReg.ReplaceAllStringFunc(s, func(sub string) string {

Check failure on line 56 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.18 and ubuntu-latest

t.varReg undefined (type *StrTemplate has no field or method varReg)

Check failure on line 56 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.19 and ubuntu-latest

t.varReg undefined (type *StrTemplate has no field or method varReg)

Check failure on line 56 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.20 and ubuntu-latest

t.varReg undefined (type *StrTemplate has no field or method varReg)
name := strings.TrimSpace(sub[t.lLen : len(sub)-t.rLen])

Check failure on line 57 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.18 and ubuntu-latest

t.lLen undefined (type *StrTemplate has no field or method lLen)

Check failure on line 57 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.18 and ubuntu-latest

t.rLen undefined (type *StrTemplate has no field or method rLen)

Check failure on line 57 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.19 and ubuntu-latest

t.lLen undefined (type *StrTemplate has no field or method lLen)

Check failure on line 57 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.19 and ubuntu-latest

t.rLen undefined (type *StrTemplate has no field or method rLen)

Check failure on line 57 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.20 and ubuntu-latest

t.lLen undefined (type *StrTemplate has no field or method lLen)

Check failure on line 57 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.20 and ubuntu-latest

t.rLen undefined (type *StrTemplate has no field or method rLen)

var defVal string
if t.parseDef && strings.ContainsRune(name, '|') {

Check failure on line 60 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.18 and ubuntu-latest

t.parseDef undefined (type *StrTemplate has no field or method parseDef)

Check failure on line 60 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.19 and ubuntu-latest

t.parseDef undefined (type *StrTemplate has no field or method parseDef)

Check failure on line 60 in litetpl/template.go

View workflow job for this annotation

GitHub Actions / Test on go 1.20 and ubuntu-latest

t.parseDef undefined (type *StrTemplate has no field or method parseDef)
name, defVal = strutil.TrimCut(name, "|")
}

if val, ok := varMap[name]; ok {
return val
}

if t.NotFound != nil {
if val, ok := t.NotFound(name); ok {
return val
}
}

if len(defVal) > 0 {
return defVal
}
t.missVars = append(t.missVars, name)
return sub
})
}

// ErrFuncNotFound error
var ErrFuncNotFound = errors.New("template func not found")

func (t *StrTemplate) applyFilters(val string, filters []string) (string, error) {
// filters like: "trim|upper|substr:1,2" => ["trim", "upper", "substr:1,2"]
for _, filter := range filters {
if fn, ok := t.Funcs[filter]; ok {
val = fn(val)
} else {
return "", ErrFuncNotFound
}
}

return val, nil
}

var builtInFuncs = template.FuncMap{
// don't escape content
"raw": func(s string) string {
return s
},
"trim": func(s string) string {
return strings.TrimSpace(s)
},
// join strings
"join": func(ss []string, sep string) string {
return strings.Join(ss, sep)
},
// lower first char
"lcFirst": func(s string) string {
return strutil.LowerFirst(s)
},
// upper first char
"upFirst": func(s string) string {
return strutil.UpperFirst(s)
},
}

var (
errorType = reflect.TypeOf((*error)(nil)).Elem()
fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
reflectValueType = reflect.TypeOf((*reflect.Value)(nil)).Elem()
)

// goodFunc reports whether the function or method has the right result signature.
func goodFunc(typ reflect.Type) bool {
// We allow functions with 1 result or 2 results where the second is an error.
switch {
case typ.NumOut() == 1:
return true
case typ.NumOut() == 2 && typ.Out(1) == errorType:
return true
}
return false
}

// TextRenderOpt render text template options
type TextRenderOpt struct {
// Output use custom output writer
Output io.Writer
// Funcs add custom template functions
Funcs template.FuncMap
}

// RenderOptFn render option func
type RenderOptFn func(opt *TextRenderOpt)

// NewRenderOpt create a new render options
func NewRenderOpt(optFns []RenderOptFn) *TextRenderOpt {
opt := &TextRenderOpt{}
for _, fn := range optFns {
fn(opt)
}
return opt
}

// RenderTpl render go template string or file.
func RenderTpl(input string, data any, optFns ...RenderOptFn) string {
return RenderGoTpl(input, data, optFns...)
}

// RenderGoTpl render input text or template file.
func RenderGoTpl(input string, data any, optFns ...RenderOptFn) string {
opt := NewRenderOpt(optFns)

t := template.New("text-renderer")
t.Funcs(builtInFuncs)
if len(opt.Funcs) > 0 {
t.Funcs(opt.Funcs)
}

if !strings.Contains(input, "{{") && fsutil.IsFile(input) {
template.Must(t.ParseFiles(input))
} else {
template.Must(t.Parse(input))
}

// use custom output writer
if opt.Output != nil {
basefn.MustOK(t.Execute(opt.Output, data))
return "" // return empty string
}

// use buffer receive rendered content
buf := new(bytes.Buffer)
basefn.MustOK(t.Execute(buf, data))
return buf.String()
}
64 changes: 64 additions & 0 deletions tplfunc/tplfunc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Package tplfunc provides a default FuncMap for use with text/template and html/template.
//
// - string functions: join, trim, trimLeft, trimRight, trimPrefix, trimSuffix, trimSpace, repeat, replace, replaceAll, toUpper, toLower, title, toTitle, toCamel, toSnake, toKebab, toLowerCamel, toLowerSnake, toLowerKebab, toCamelLower, toSnakeLower, toKebabLower, toLowerCamelLower, toLowerSnakeLower, toLowerKebabLower, toCamelUpper, toSnakeUpper, toKebabUpper, toLowerCamelUpper, toLowerSnakeUpper, toLowerKebabUpper, toCamelTitle, toSnakeTitle, toKebabTitle
// - math functions: add, max, mul, min, sub, div, mod, ceil, floor, round, roundEven, trunc, sqrt, pow, rand, randInt, randIntRange, randFloat, randFloatRange, randPerm, randShuffle, randChoice, randChoices, randSample, randSamples, randString, randStrings, randBytes, randByte, randRune, randRunes, randTime, randDate, randDateTime, randTimeDuration, randDateDuration, randDateTimeDuration, randTimeUnix, randDateUnix, randDateTimeUnix
// - list functions: list, first, last, len, reverse, sort, shuffle, unique, contains, in, has, keys, values, chunk, chunkBy, chunkByNum, chunkBySize
// - encoding functions: b64enc, b64dec, b32enc, b32dec
// - path functions: base, dir, ext, clean, isAbs, osBase, osDir, osExt, osClean, osIsAbs
// - hash functions: uuid, md5, sha1, sha256, sha512, crc32, crc64
// - other functions: default, empty, coalesce, fromJson, toJson, toPrettyJson, toRawJson, dump
//
// Example:
//
// import (
// "github.com/gookit/easytpl/tplfunc"
// "html/template"
// )
//
// // This example illustrates that the FuncMap *must* be set before the
// // templates themselves are loaded.
// tpl := template.Must(
// template.New("base").Funcs(tplfunc.FuncMap()).ParseGlob("path/to/*.tpl")
// )
//
// refer: https://github.com/Masterminds/sprig
package tplfunc

import (
"os"
"path"
"path/filepath"
"strings"
"text/template"
)

// FuncMap returns the default FuncMap.
func FuncMap() template.FuncMap {
return stdFuncMap
}

var stdFuncMap = map[string]any{
// String
"join": strings.Join,
"trim": strings.TrimSpace,
"upper": strings.ToUpper,
"lower": strings.ToLower,

// OS:
"env": os.Getenv,
"expandenv": os.ExpandEnv,

// Paths:
"base": path.Base,
"dir": path.Dir,
"clean": path.Clean,
"ext": path.Ext,
"isAbs": path.IsAbs,

// File paths:
"osBase": filepath.Base,
"osClean": filepath.Clean,
"osDir": filepath.Dir,
"osExt": filepath.Ext,
"osIsAbs": filepath.IsAbs,
}

0 comments on commit f866ea4

Please sign in to comment.