Skip to content

Instantly share code, notes, and snippets.

@kougazhang
Last active February 13, 2022 08:13
Show Gist options
  • Save kougazhang/4b996d733a454b8b34201a7a6683c54a to your computer and use it in GitHub Desktop.
Save kougazhang/4b996d733a454b8b34201a7a6683c54a to your computer and use it in GitHub Desktop.
[golang 模板] golang 模板 #golang #template
package main
import (
"encoding/json"
"flag"
"fmt"
"io/fs"
"log"
"os"
"path/filepath"
"strings"
"text/template"
)
var (
tplPath = flag.String("tpl", "", "the template path")
cfgPath = flag.String("cfg", "", "the config path")
destPath = flag.String("dest", "", "the dest path")
)
func main() {
flag.Parse()
// parse config
data, err := os.ReadFile(*cfgPath)
if err != nil {
log.Fatal(err)
}
var cfg cfg
err = json.Unmarshal(data, &cfg)
if err != nil {
log.Fatal(err)
}
// walk though files in tplPath, render them with cfg one by one
filepath.Walk(*tplPath, func(path string, info fs.FileInfo, err error) error {
if info.IsDir() {
return nil
}
srcFilepath := newFilePath(path)
dest := mkDest(srcFilepath, cfg)
render(path, dest, cfg)
return nil
})
remainder(cfg)
}
type cfg struct {
Host string `json:"host"`
Job string `json:"job"`
JobNum string `json:"job_num"`
// If true modify job-kuaikan by manual.
IsDynamicDomains bool `json:"is_dynamic_domains"`
}
type filePath struct {
dir string
base string
firstPartOfBase string
secPartOfBase string
}
func (f filePath) String() string {
if len(f.secPartOfBase) > 0 {
return filepath.Join(f.dir, fmt.Sprintf("%s.%s", f.firstPartOfBase, f.secPartOfBase))
}
return filepath.Join(f.dir, f.firstPartOfBase)
}
func mkDest(srcFilepath filePath, cfg cfg) string {
// retain the directory struct of template
subPath := strings.TrimPrefix(srcFilepath.dir, *tplPath)
srcFilepath.dir = filepath.Join(*destPath, subPath)
// rename
srcFilepath.firstPartOfBase = newName(srcFilepath.firstPartOfBase, cfg)
return srcFilepath.String()
}
func newFilePath(path string) (f filePath) {
f = filePath{
dir: filepath.Dir(path),
base: filepath.Base(path),
}
filename := strings.SplitN(f.base, ".", 2)
f.firstPartOfBase = filename[0]
if len(filename) > 1 {
f.secPartOfBase = filename[1]
}
return
}
func newName(name string, cfg cfg) string {
switch name {
case "-job":
return cfg.Job
default:
return name
}
}
func render(src, dest string, data interface{}) {
tpl, err := template.New(filepath.Base(src)).Funcs(template.FuncMap{
"capital": strings.Title,
}).ParseFiles(src)
if err != nil {
log.Fatal(err)
}
// create dest file
os.MkdirAll(filepath.Dir(dest), 0777)
file, err := os.Create(dest)
if err != nil {
log.Fatal(err)
}
// output render result to dest file
err = tpl.Execute(file, data)
if err != nil {
log.Fatal(err)
}
file.Close()
}
func remainder(cfg cfg) {
if cfg.IsDynamicDomains {
log.Println("WARN: IsDynamicDomains is true, you may need to modify the config of job-kuaikan manually.")
}
}
@kougazhang
Copy link
Author

kougazhang commented Feb 13, 2022

对 render 函数做了一些封装

func main() {
	if len(os.Args) < 3 {
		log.Fatal("need action and param")
	}
	switch os.Args[1] {
	case "render":
		if len(os.Args) < 5 {
			log.Fatal("need param")
		}
		data := make(map[string]string, len(os.Args[4:]))
		for _, item := range os.Args[4:] {
			split := strings.SplitN(item, "=", 2)
			data[split[0]] = split[1]
		}
		render(os.Args[2], os.Args[3], data)
	}
}

模板文件 ./config/a.json:

{{ .job }}

执行命令:

./script/script render ./config/a.json ./config/b.json job=hello

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment