From 49c328837a54aa9b8671d5011d57d555661de797 Mon Sep 17 00:00:00 2001 From: OhYee Date: Mon, 9 Sep 2019 21:02:21 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=88=20update:=20add=20some=20useful=20?= =?UTF-8?q?function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- README.md | 16 ++++-- README_CN.md | 10 +++- example/compare/data.json | 2 +- gcg.go | 102 +++++++++++++++++++++++--------------- gcg_test.go | 84 +++++++++++++++++++++++++++++++ 6 files changed, 170 insertions(+), 47 deletions(-) create mode 100644 gcg_test.go diff --git a/.gitignore b/.gitignore index c5e82d7..ef9a4e4 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -bin \ No newline at end of file +bin +.vscode \ No newline at end of file diff --git a/README.md b/README.md index ace9124..2027e40 100644 --- a/README.md +++ b/README.md @@ -6,29 +6,37 @@ Using json file and go template to generate go code. ## Installation -``` +```bash go get github.com/OhYee/gcg go install github.com/OhYee/gcg ``` ## Usage -``` +```bash gcg data.json compare.go ``` -## document +## Document The json file must have these field: |name|type|| -|:---:|:---:|:---:| +|:---|:---|:---| |package|string|package name| |import|`[]string` or `[][]string`|import part of the file| |body|`[]strut{template string, args interface{}}` or `[]struct{template []string, args interface{}}`|the go file body| The `template` and `args` in `body` will be the arguments of go `text/template` +### Function + +|name|args|| +|:---|:---|:---| +|lower|`string`|make all letters to their lower case| +|upper|`string`|make all letters to their upper case| +|upperFirstChar|`string`|make the first letter to its upper case, keep others| + ## Example See [number compare package](https://github.com/OhYee/gcg/tree/master/example/compare), you can delete `compare.go` and using `gcg data.json compare.go` or `go generate g.go` to re-generate it. diff --git a/README_CN.md b/README_CN.md index 8ff2c78..e34ba0f 100644 --- a/README_CN.md +++ b/README_CN.md @@ -22,13 +22,21 @@ gcg data.json compare.go JSON文件字段要求 |名称|类型|| -|:---:|:---:|:---:| +|:---|:---|:---| |package|string|生成文件的包名| |import|`[]string` or `[][]string`|Go文件的引入包部分| |body|`[]strut{template string, args interface{}}` or `[]struct{template []string, args interface{}}`|Go文件主体| `body`的`template` 和 `args` 将用于 Go `text/template` 的参数 +### 函数 + +|名称|参数|| +|:---|:---|:---| +|lower|`string`|使所有字母小写| +|upper|`string`|使所有字母大写| +|upperFirstChar|`string`|使首字母大写| + ## 样例 详情见 [大小比较包](https://github.com/OhYee/gcg/tree/master/example/compare), 你可以先删除 `compare.go` 然后使用 `gcg data.json compare.go` 或者 `go generate g.go` 来重新生成它. diff --git a/example/compare/data.json b/example/compare/data.json index 95ed42b..ea9bbbc 100644 --- a/example/compare/data.json +++ b/example/compare/data.json @@ -10,7 +10,7 @@ ], "body": [ { - "template": "const.tpl", + "template": "/home/ohyee/projects/gcg/example/compare/const.tpl", "args": ["-"] }, { diff --git a/gcg.go b/gcg.go index 7ed71d8..564fc79 100644 --- a/gcg.go +++ b/gcg.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "go/format" + "io" "io/ioutil" "os" "strings" @@ -12,7 +13,7 @@ import ( ) const ( - version = "0.0.2" + version = "0.0.3" helpText = "Using `gcg []` to generate go file\nSuch as `gcg data.json` or `gcg data.json ../add.go`" ) @@ -35,6 +36,18 @@ type bodyArea struct { } type jsonMap = map[string]interface{} +var funcMap = template.FuncMap{ + "upperFirstChar": func(text string) string { + return fmt.Sprintf("%s%s", strings.ToUpper(text[0:1]), text[1:len(text)]) + }, + "upper": func(text string) string { + return strings.ToUpper(text) + }, + "lower": func(text string) string { + return strings.ToLower(text) + }, +} + // exitWhenError exit the program when error is not null func exitWhenError(err error) { if err != nil { @@ -88,28 +101,28 @@ func importPackage(x interface{}) (s string) { return } -func main() { - var inputFile string - var outputFile *os.File - switch len(os.Args) { - case 1: - exitWhenFalse(false, helpText) - case 2: - if os.Args[1] == "-h" || os.Args[1] == "--help" { - exitWhenFalse(false, helpText) - } else if os.Args[1] == "-v" || os.Args[1] == "--version" { - exitWhenFalse(false, fmt.Sprintf("Go Code Generator version: %s\n", version)) - } - inputFile = os.Args[1] - outputFile = os.Stdout - default: - var err error - inputFile = os.Args[1] - outputFile, err = os.Create(os.Args[2]) - exitWhenError(err) +// getFilename from a path (go template name can not be a path) +func getFileName(path string) (filename string) { + pathList := strings.Split(path, "/") + filename = pathList[len(pathList)-1] + return +} + +// renderTemplate render the block template +func renderTemplate(buf io.Writer, templates []string, args []interface{}) { + templateName := getFileName(templates[0]) + tpl, err := template.New(templateName).Funcs(funcMap).ParseFiles(templates...) + exitWhenError(err) + for _, arg := range args { + // tpl.Execute(buf, arg) + tpl.ExecuteTemplate(buf, templateName, arg) + buf.Write([]byte{'\n'}) } - args := readData(inputFile) +} +// renderContent render generated file content +func renderContent(args arguments) []byte { + // header buf := bytes.NewBufferString("// generated by Go Code Generator(https://github.com/OhYee/gcg); DO NOT EDIT\n\n") buf.WriteString(fmt.Sprintf("package %s\n\n", args.PackageName)) buf.WriteString(fmt.Sprintf("import (\n%s\n)\n\n", @@ -127,6 +140,7 @@ func main() { }(args.ImportedPackage), )) + // body for _, block := range args.Body { var templates []string switch block.Template.(type) { @@ -144,31 +158,39 @@ func main() { default: exitWhenFalse(false, "template must be string or []string") } - - tpl, err := template.New(templates[0]).Funcs( - template.FuncMap{ - "upperFirstChar": func(text string) string { - return strings.ToTitle(text) - }, - "upper": func(text string) string { - return strings.ToUpper(text) - }, - "lower": func(text string) string { - return strings.ToLower(text) - }, - }, - ).ParseFiles(templates...) - exitWhenError(err) - for _, arg := range block.Args { - tpl.Execute(buf, arg) - buf.WriteString("\n\n") - } + renderTemplate(buf, templates, block.Args) } + // code format b, err := format.Source(buf.Bytes()) if err != nil { fmt.Printf("goformat error\n%s\n", err) b = buf.Bytes() } + return b +} + +func main() { + var inputFile string + var outputFile *os.File + switch len(os.Args) { + case 1: + exitWhenFalse(false, helpText) + case 2: + if os.Args[1] == "-h" || os.Args[1] == "--help" { + exitWhenFalse(false, helpText) + } else if os.Args[1] == "-v" || os.Args[1] == "--version" { + exitWhenFalse(false, fmt.Sprintf("Go Code Generator version: %s\n", version)) + } + inputFile = os.Args[1] + outputFile = os.Stdout + default: + var err error + inputFile = os.Args[1] + outputFile, err = os.Create(os.Args[2]) + exitWhenError(err) + } + args := readData(inputFile) + b := renderContent(args) outputFile.Write(b) } diff --git a/gcg_test.go b/gcg_test.go new file mode 100644 index 0000000..8febb97 --- /dev/null +++ b/gcg_test.go @@ -0,0 +1,84 @@ +package main + +import ( + "bytes" + "io/ioutil" + "os" + "testing" +) + +func writeToTempFile(t *testing.T, content string) string { + file, err := ioutil.TempFile("", "test") + if err != nil { + t.Errorf("Create temp file error: %v", err) + } + _, err = file.WriteString(content) + if err != nil { + t.Errorf("Write to temp file error: %v", err) + } + file.Close() + return file.Name() +} + +func Test_renderTemplate(t *testing.T) { + tests := []struct { + name string + template string + args []interface{} + result string + }{ + { + name: "test upper", + template: "{{upper .text}}", + args: []interface{}{ + map[string]interface{}{ + "text": "ABcdefg1234567", + }, + }, + result: "ABCDEFG1234567\n", + }, + { + name: "test lower", + template: "{{lower .text}}", + args: []interface{}{ + map[string]interface{}{ + "text": "ABcdefg1234567", + }, + }, + result: "abcdefg1234567\n", + }, + { + name: "test upperFirstChar", + template: "{{upperFirstChar .text}}", + args: []interface{}{ + map[string]interface{}{ + "text": "ABcdefg1234567", + }, + }, + result: "ABcdefg1234567\n", + }, + { + name: "test upperFirstChar 2", + template: "{{upperFirstChar .text}}", + args: []interface{}{ + map[string]interface{}{ + "text": "abc", + }, + }, + result: "Abc\n", + }, + } + + buf := bytes.NewBuffer([]byte{}) + for _, tt := range tests { + buf.Reset() + filename := writeToTempFile(t, tt.template) + t.Run(tt.name, func(t *testing.T) { + renderTemplate(buf, []string{filename}, tt.args) + if buf.String() != tt.result { + t.Errorf("want %v, but got %v", tt.result, buf.String()) + } + }) + os.Remove(filename) + } +}