├── .github └── workflows │ └── check.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── generator ├── anonymous_func.go ├── anonymous_func_doc_test.go ├── anonymous_func_signature.go ├── anonymous_func_signature_doc_test.go ├── anonymous_func_signature_test.go ├── anonymous_func_test.go ├── case.go ├── case_doc_test.go ├── case_test.go ├── code_block.go ├── code_block_doc_test.go ├── code_block_test.go ├── comment.go ├── comment_doc_test.go ├── comment_test.go ├── composite_literal.go ├── composite_literal_test.go ├── default_case.go ├── default_case_doc_test.go ├── default_case_test.go ├── else.go ├── else_doc_test.go ├── else_if.go ├── else_if_doc_test.go ├── else_if_test.go ├── else_test.go ├── for.go ├── for_doc_test.go ├── for_test.go ├── frame_fetcher.go ├── frame_fetcher_test.go ├── func.go ├── func_doc_test.go ├── func_invocation.go ├── func_invocation_doc_test.go ├── func_invocation_test.go ├── func_receiver.go ├── func_receiver_doc_test.go ├── func_receiver_test.go ├── func_signature.go ├── func_signature_doc_test.go ├── func_signature_test.go ├── func_test.go ├── if.go ├── if_doc_test.go ├── if_test.go ├── import.go ├── import_doc_test.go ├── import_test.go ├── interface.go ├── interface_doc_test.go ├── interface_test.go ├── newline.go ├── newline_doc_test.go ├── newline_test.go ├── package.go ├── package_doc_test.go ├── package_test.go ├── raw_statement.go ├── raw_statement_doc_test.go ├── raw_statement_test.go ├── return_statement.go ├── return_statement_doc_test.go ├── return_statement_test.go ├── root.go ├── root_doc_test.go ├── root_test.go ├── statement.go ├── struct.go ├── struct_doc_test.go ├── struct_test.go ├── switch.go ├── switch_doc_test.go ├── switch_test.go ├── type_parameters.go ├── type_parameters_doc_test.go └── type_parameters_test.go ├── go.mod ├── go.sum └── internal ├── errmsg ├── errmsg.go └── errs_errmsg_gen.go └── vendor.go /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | 4 | jobs: 5 | test: 6 | name: Check 7 | strategy: 8 | matrix: 9 | go-version: [1.18.x, 1.19.x] 10 | os: [ubuntu-latest] 11 | runs-on: ${{ matrix.os }} 12 | steps: 13 | - name: Set up Go 14 | uses: actions/setup-go@v3 15 | with: 16 | go-version: ${{ matrix.go-version }} 17 | - name: check out 18 | uses: actions/checkout@v3 19 | - name: install tools 20 | run: "go install golang.org/x/tools/cmd/goimports@latest && go install github.com/moznion/go-errgen/cmd/errgen@latest" 21 | - name: check 22 | run: make check-ci 23 | - name: golangci-lint 24 | uses: golangci/golangci-lint-action@v3 25 | with: 26 | version: latest 27 | - name: upload coverage 28 | uses: codecov/codecov-action@v3 29 | with: 30 | token: ${{ secrets.CODECOV_TOKEN }} 31 | 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.exe~ 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | *.test 8 | 9 | *.out 10 | cover.html 11 | coverage.txt 12 | 13 | /vendor/ 14 | /Godeps/ 15 | 16 | .idea/ 17 | *.iml 18 | 19 | /author/bin/* 20 | !/author/bin/.gitkeep 21 | 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright © 2019 moznion, http://moznion.net/ 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: errgen 2 | 3 | PKGS := $(shell go list ./... | grep -v go-errgen) 4 | 5 | check: test lint vet fmt-check 6 | check-ci: test vet fmt-check 7 | 8 | test: errgen 9 | go test -v -cover -race -coverprofile=coverage.txt -covermode=atomic $(PKGS) 10 | 11 | test-coverage: errgen 12 | go test -v -cover -coverprofile cover.out $(PKGS) 13 | go tool cover -html=cover.out -o cover.html 14 | 15 | lint: 16 | golangci-lint run ./... 17 | 18 | vet: 19 | go vet $(PKGS) 20 | 21 | fmt-check: 22 | gofmt -l -s **/*.go | grep [^*][.]go$$; \ 23 | EXIT_CODE=$$?; \ 24 | if [ $$EXIT_CODE -eq 0 ]; then exit 1; fi; \ 25 | goimports -l **/*.go | grep [^*][.]go$$; \ 26 | EXIT_CODE=$$?; \ 27 | if [ $$EXIT_CODE -eq 0 ]; then exit 1; fi \ 28 | 29 | fmt: 30 | gofmt -w -s **/*.go 31 | goimports -w **/*.go 32 | 33 | installdeps: 34 | go mod vendor 35 | go mod tidy 36 | 37 | bootstrap: installdeps 38 | go install golang.org/x/tools/cmd/goimports@latest 39 | go install github.com/moznion/go-errgen/cmd/errgen@latest 40 | 41 | errgen: 42 | go generate ./... 43 | 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | gowrtr [![CircleCI](https://circleci.com/gh/moznion/gowrtr.svg?style=svg)](https://circleci.com/gh/moznion/gowrtr) [![codecov](https://codecov.io/gh/moznion/gowrtr/branch/master/graph/badge.svg)](https://codecov.io/gh/moznion/gowrtr) [![GoDoc](https://godoc.org/github.com/moznion/gowrtr/generator?status.svg)](https://godoc.org/github.com/moznion/gowrtr/generator) [![Go Report Card](https://goreportcard.com/badge/github.com/moznion/gowrtr)](https://goreportcard.com/report/github.com/moznion/gowrtr) 2 | == 3 | 4 | gowrtr (pronunciation:`go writer`) is a library that supports golang code generation. This library is [go generics](https://go.dev/doc/tutorial/generics) ready! 5 | 6 | This library is inspired by [square/javapoet](https://github.com/square/javapoet). 7 | 8 | Synopsis 9 | -- 10 | 11 | Here is a simple example: 12 | 13 | ```go 14 | package main 15 | 16 | import ( 17 | "fmt" 18 | 19 | "github.com/moznion/gowrtr/generator" 20 | ) 21 | 22 | func main() { 23 | generator := generator.NewRoot( 24 | generator.NewComment(" THIS CODE WAS AUTO GENERATED"), 25 | generator.NewPackage("main"), 26 | generator.NewNewline(), 27 | ).AddStatements( 28 | generator.NewFunc( 29 | nil, 30 | generator.NewFuncSignature("main"), 31 | ).AddStatements( 32 | generator.NewRawStatement(`fmt.Println("hello, world!")`), 33 | ), 34 | ). 35 | Gofmt("-s"). 36 | Goimports() 37 | 38 | generated, err := generator.Generate(0) 39 | if err != nil { 40 | panic(err) 41 | } 42 | fmt.Println(generated) 43 | } 44 | ``` 45 | 46 | then it generates the golang code like so: 47 | 48 | ```go 49 | // THIS CODE WAS AUTO GENERATED 50 | package main 51 | 52 | import "fmt" 53 | 54 | func main() { 55 | fmt.Println("hello, world!") 56 | } 57 | ``` 58 | 59 | Generics example is here: 60 | 61 | ```go 62 | package main 63 | 64 | import ( 65 | "fmt" 66 | 67 | "github.com/moznion/gowrtr/generator" 68 | ) 69 | 70 | func main() { 71 | generated, err := generator.NewRoot( 72 | generator.NewComment(" THIS CODE WAS AUTO GENERATED"), 73 | generator.NewPackage("main"), 74 | generator.NewNewline(), 75 | generator.NewStruct("MyStruct"). 76 | TypeParameters(TypeParameters{ 77 | generator.NewTypeParameter("T", "any"), 78 | }).AddField("anything", "T"), 79 | generator.NewNewline(), 80 | generator.NewFunc( 81 | nil, 82 | generator.NewFuncSignature("foo"). 83 | TypeParameters(TypeParameters{ 84 | generator.NewTypeParameter("U", "any"), 85 | }). 86 | ReturnTypeStatements(generator.NewFuncReturnTypeWithGenerics("MyStruct", generator.TypeParameterNames{"U"})), 87 | ).AddStatements(generator.NewComment("do something")), 88 | generator.NewNewline(), 89 | generator.NewFunc( 90 | generator.NewFuncReceiverWithGenerics("s", "MyStruct", generator.TypeParameterNames{"T"}), 91 | generator.NewFuncSignature("bar"). 92 | ReturnTypeStatements(generator.NewFuncReturnTypeWithGenerics("MyStruct", generator.TypeParameterNames{"T"})), 93 | ).AddStatements(generator.NewComment("do something")), 94 | ). 95 | Gofmt("-s"). 96 | Goimports(). 97 | Generate(0) 98 | 99 | if err != nil { 100 | panic(err) 101 | } 102 | fmt.Println(generated) 103 | } 104 | ``` 105 | 106 | then it generates the following golang code: 107 | 108 | ```go 109 | // THIS CODE WAS AUTO GENERATED 110 | package main 111 | 112 | type MyStruct[T any] struct { 113 | anything T 114 | } 115 | 116 | func foo[U any]() MyStruct[U] { 117 | //do something 118 | } 119 | 120 | func (s MyStruct[T]) bar() MyStruct[T] { 121 | //do something 122 | } 123 | ``` 124 | 125 | And [GoDoc](https://godoc.org/github.com/moznion/gowrtr/generator) shows you a greater number of examples. 126 | 127 | Description 128 | -- 129 | 130 | Please refer to the godoc: [![GoDoc](https://godoc.org/github.com/moznion/gowrtr/generator?status.svg)](https://godoc.org/github.com/moznion/gowrtr/generator) 131 | 132 | ### Root 133 | 134 | - `Root` is an entry point to generate the go code. 135 | - `Root` supports following code formatting on code generating phase. It applies such formatters to generated code. 136 | - `gofmt`: with `Gofmt(gofmtOptions ...string)` 137 | - `goimports`: with `Goimports()` 138 | 139 | ### Immutability 140 | 141 | Methods of this library act as immutable. It means it doesn't change any internal state implicitly, so you can take a snapshot of the code generator. That is useful to reuse and derive the code generator instance. 142 | 143 | ### Debug friendly 144 | 145 | This library shows "where is a cause of the error" when code generator raises an error. This means each error message contains a pointer for the error source (i.e. file name and the line number). This should be helpful for debugging. 146 | 147 | Error messages example: 148 | 149 | ``` 150 | [GOWRTR-14] condition of case must not be empty, but it gets empty (caused at /tmp/main.go:22) 151 | ``` 152 | 153 | ### Supported syntax 154 | 155 | - [x] `package` 156 | - [x] `import` 157 | - [x] `struct` 158 | - [x] generics type parameters 159 | - [x] `interface` 160 | - [x] generics type parameters 161 | - [x] [composite literal](https://golang.org/doc/effective_go.html#composite_literals) 162 | - [x] `if` 163 | - [x] `else if` 164 | - [x] `else` 165 | - [x] `switch` 166 | - [x] `case` 167 | - [x] `default` 168 | - [x] `for` 169 | - [x] code block 170 | - [x] `func` 171 | - [x] generics type parameters on the signature 172 | - [x] generics type names on the receiver 173 | - [x] generics type names on the return types 174 | - [x] generics types on the invocation 175 | - [x] anonymous func 176 | - [x] immediately invoking 177 | - one line statement 178 | - [x] raw 179 | - [x] newline 180 | - [x] `return` 181 | - [x] `comment` 182 | - [x] union types in the generics type parameters 183 | 184 | For developers of this library 185 | -- 186 | 187 | ### Setup development environment 188 | 189 | ``` 190 | $ make bootstrap 191 | ``` 192 | 193 | ### How to define and generate error messages 194 | 195 | Please edit `internal/errmsg/errmsg.go` and execute `make errgen`. 196 | 197 | See also: [moznion/go-errgen](https://github.com/moznion/go-errgen) 198 | 199 | Blog posts 200 | -- 201 | 202 | - English: [gowrtr - a library that supports golang code generation](https://moznion.hatenablog.jp/entry/2019/01/15/094236) 203 | - Japanese: [gowrtr - goコード生成支援ライブラリ](https://moznion.hatenadiary.com/entry/2019/01/14/111719) 204 | 205 | License 206 | -- 207 | 208 | ``` 209 | The MIT License (MIT) 210 | Copyright © 2019 moznion, http://moznion.net/ 211 | 212 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 213 | 214 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 215 | 216 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 217 | ``` 218 | 219 | -------------------------------------------------------------------------------- /generator/anonymous_func.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "github.com/moznion/gowrtr/internal/errmsg" 5 | ) 6 | 7 | // AnonymousFunc represents a code generator for anonymous func. 8 | type AnonymousFunc struct { 9 | goFunc bool 10 | anonymousFuncSignature *AnonymousFuncSignature 11 | statements []Statement 12 | funcInvocation *FuncInvocation 13 | caller string 14 | } 15 | 16 | // NewAnonymousFunc returns a new `AnonymousFunc`. 17 | // If `goFunc` is true, the anonymous function will be `go func`. 18 | func NewAnonymousFunc(goFunc bool, signature *AnonymousFuncSignature, statements ...Statement) *AnonymousFunc { 19 | return &AnonymousFunc{ 20 | goFunc: goFunc, 21 | anonymousFuncSignature: signature, 22 | statements: statements, 23 | caller: fetchClientCallerLine(), 24 | } 25 | } 26 | 27 | // AddStatements adds statements for the function to `AnonymousFunc`. This does *not* set, just add. 28 | // This method returns a *new* `AnonymousFunc`; it means this method acts as immutable. 29 | func (ifg *AnonymousFunc) AddStatements(statements ...Statement) *AnonymousFunc { 30 | return &AnonymousFunc{ 31 | goFunc: ifg.goFunc, 32 | anonymousFuncSignature: ifg.anonymousFuncSignature, 33 | statements: append(ifg.statements, statements...), 34 | funcInvocation: ifg.funcInvocation, 35 | caller: ifg.caller, 36 | } 37 | } 38 | 39 | // Statements sets statements for the function to `AnonymousFunc`. This does *not* add, just set. 40 | // This method returns a *new* `AnonymousFunc`; it means this method acts as immutable. 41 | func (ifg *AnonymousFunc) Statements(statements ...Statement) *AnonymousFunc { 42 | return &AnonymousFunc{ 43 | goFunc: ifg.goFunc, 44 | anonymousFuncSignature: ifg.anonymousFuncSignature, 45 | statements: statements, 46 | funcInvocation: ifg.funcInvocation, 47 | caller: ifg.caller, 48 | } 49 | } 50 | 51 | // Invocation sets an invocation of the anonymous func to `AnonymousFunc`. 52 | // This method returns a *new* `AnonymousFunc`; it means this method acts as immutable. 53 | func (ifg *AnonymousFunc) Invocation(funcInvocation *FuncInvocation) *AnonymousFunc { 54 | return &AnonymousFunc{ 55 | goFunc: ifg.goFunc, 56 | anonymousFuncSignature: ifg.anonymousFuncSignature, 57 | statements: ifg.statements, 58 | funcInvocation: funcInvocation, 59 | caller: ifg.caller, 60 | } 61 | } 62 | 63 | // Generate generates an anonymous func as golang code. 64 | func (ifg *AnonymousFunc) Generate(indentLevel int) (string, error) { 65 | indent := BuildIndent(indentLevel) 66 | 67 | stmt := indent 68 | if ifg.goFunc { 69 | stmt += "go " 70 | } 71 | stmt += "func" 72 | 73 | if ifg.anonymousFuncSignature == nil { 74 | return "", errmsg.AnonymousFuncSignatureIsNilError(ifg.caller) 75 | } 76 | 77 | sig, err := ifg.anonymousFuncSignature.Generate(0) 78 | if err != nil { 79 | return "", err 80 | } 81 | stmt += sig + " {\n" 82 | 83 | nextIndentLevel := indentLevel + 1 84 | for _, generator := range ifg.statements { 85 | gen, err := generator.Generate(nextIndentLevel) 86 | if err != nil { 87 | return "", err 88 | } 89 | stmt += gen 90 | } 91 | 92 | stmt += indent + "}" 93 | 94 | if funcInvocation := ifg.funcInvocation; funcInvocation != nil { 95 | invocation, err := funcInvocation.Generate(0) 96 | if err != nil { 97 | return "", err 98 | } 99 | stmt += invocation 100 | } 101 | 102 | stmt += "\n" 103 | 104 | return stmt, nil 105 | } 106 | -------------------------------------------------------------------------------- /generator/anonymous_func_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExampleAnonymousFunc_Generate() { 9 | generator := NewAnonymousFunc( 10 | true, 11 | NewAnonymousFuncSignature(). 12 | AddParameters( 13 | NewFuncParameter("foo", "string"), 14 | NewFuncParameter("bar", "int64"), 15 | ). 16 | AddReturnTypes("string", "error"), 17 | NewComment(" do something"), 18 | NewRawStatement(`fmt.Printf("%d", i)`), 19 | ).Invocation(NewFuncInvocation("foo", "bar")) 20 | 21 | generated, err := generator.Generate(0) 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | fmt.Println(generated) 26 | } 27 | -------------------------------------------------------------------------------- /generator/anonymous_func_signature.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/moznion/gowrtr/internal/errmsg" 7 | ) 8 | 9 | // AnonymousFuncSignature represents a code generator for signature of anonymous func. 10 | type AnonymousFuncSignature struct { 11 | funcParameters []*FuncParameter 12 | returnTypes []string 13 | callers []string 14 | } 15 | 16 | // NewAnonymousFuncSignature returns a new `AnonymousFuncSignature`. 17 | func NewAnonymousFuncSignature() *AnonymousFuncSignature { 18 | return &AnonymousFuncSignature{} 19 | } 20 | 21 | // AddParameters adds parameters of function to `AnonymousFuncSignature`. This does "not" set, just add. 22 | // This method returns a *new* `AnonymousFuncSignature`; it means this method acts as immutable. 23 | func (f *AnonymousFuncSignature) AddParameters(funcParameters ...*FuncParameter) *AnonymousFuncSignature { 24 | return &AnonymousFuncSignature{ 25 | funcParameters: append(f.funcParameters, funcParameters...), 26 | returnTypes: f.returnTypes, 27 | callers: append(f.callers, fetchClientCallerLineAsSlice(len(funcParameters))...), 28 | } 29 | } 30 | 31 | // Parameters sets parameters of function to `AnonymousFuncSignature`. This does "not" add, just set. 32 | // This method returns a *new* `AnonymousFuncSignature`; it means this method acts as immutable. 33 | func (f *AnonymousFuncSignature) Parameters(funcParameters ...*FuncParameter) *AnonymousFuncSignature { 34 | return &AnonymousFuncSignature{ 35 | funcParameters: funcParameters, 36 | returnTypes: f.returnTypes, 37 | callers: fetchClientCallerLineAsSlice(len(funcParameters)), 38 | } 39 | } 40 | 41 | // AddReturnTypes adds return types of the function to `AnonymousFuncSignature`. This does "not" set, just add. 42 | // This method returns a *new* `AnonymousFuncSignature`; it means this method acts as immutable. 43 | func (f *AnonymousFuncSignature) AddReturnTypes(returnTypes ...string) *AnonymousFuncSignature { 44 | return &AnonymousFuncSignature{ 45 | funcParameters: f.funcParameters, 46 | returnTypes: append(f.returnTypes, returnTypes...), 47 | callers: f.callers, 48 | } 49 | } 50 | 51 | // ReturnTypes sets return types of the function to `AnonymousFuncSignature`. This does "not" add, just set. 52 | // This method returns a *new* `AnonymousFuncSignature`; it means this method acts as immutable. 53 | func (f *AnonymousFuncSignature) ReturnTypes(returnTypes ...string) *AnonymousFuncSignature { 54 | return &AnonymousFuncSignature{ 55 | funcParameters: f.funcParameters, 56 | returnTypes: returnTypes, 57 | callers: f.callers, 58 | } 59 | } 60 | 61 | // Generate generates a signature of the anonymous func as golang code. 62 | func (f *AnonymousFuncSignature) Generate(indentLevel int) (string, error) { 63 | stmt := "(" 64 | 65 | typeExisted := true 66 | typeMissingCaller := "" 67 | params := make([]string, len(f.funcParameters)) 68 | for i, param := range f.funcParameters { 69 | if param.name == "" { 70 | return "", errmsg.FuncParameterNameIsEmptyErr(f.callers[i]) 71 | } 72 | 73 | paramSet := param.name 74 | typeExisted = param.typ != "" 75 | if typeExisted { 76 | paramSet += " " + param.typ 77 | } 78 | if !typeExisted { 79 | typeMissingCaller = f.callers[i] 80 | } 81 | params[i] = paramSet 82 | } 83 | 84 | if !typeExisted { 85 | return "", errmsg.LastFuncParameterTypeIsEmptyErr(typeMissingCaller) 86 | } 87 | 88 | stmt += strings.Join(params, ", ") + ")" 89 | 90 | returnTypes := f.returnTypes 91 | switch len(returnTypes) { 92 | case 0: 93 | // NOP 94 | case 1: 95 | stmt += " " + returnTypes[0] 96 | default: 97 | stmt += " (" + strings.Join(returnTypes, ", ") + ")" 98 | } 99 | return stmt, nil 100 | } 101 | -------------------------------------------------------------------------------- /generator/anonymous_func_signature_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExampleAnonymousFuncSignature_Generate() { 9 | generator := NewAnonymousFuncSignature(). 10 | AddParameters( 11 | NewFuncParameter("foo", "string"), 12 | NewFuncParameter("bar", "int64"), 13 | ). 14 | AddReturnTypes("string", "error") 15 | 16 | generated, err := generator.Generate(0) 17 | if err != nil { 18 | log.Fatal(err) 19 | } 20 | fmt.Println(generated) 21 | } 22 | -------------------------------------------------------------------------------- /generator/anonymous_func_signature_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/moznion/gowrtr/internal/errmsg" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestShouldGenerateAnonymousFuncSignatureCode(t *testing.T) { 14 | generator := NewAnonymousFuncSignature() 15 | gen, err := generator.Generate(0) 16 | assert.NoError(t, err) 17 | assert.Equal(t, "()", gen) 18 | 19 | generator = generator. 20 | AddParameters(NewFuncParameter("foo", "string")). 21 | AddReturnTypes("string") 22 | gen, err = generator.Generate(0) 23 | assert.NoError(t, err) 24 | assert.Equal(t, "(foo string) string", gen) 25 | 26 | generator = NewAnonymousFuncSignature(). 27 | AddParameters( 28 | NewFuncParameter("foo", "string"), 29 | NewFuncParameter("bar", "int64"), 30 | ). 31 | AddReturnTypes("string", "error") 32 | gen, err = generator.Generate(0) 33 | assert.NoError(t, err) 34 | assert.Equal(t, "(foo string, bar int64) (string, error)", gen) 35 | 36 | gen, err = generator.Parameters(NewFuncParameter("buz", "error")).ReturnTypes("error").Generate(0) 37 | assert.NoError(t, err) 38 | assert.Equal(t, "(buz error) error", gen) 39 | } 40 | 41 | func TestShouldGenerateAnonymousFuncSignatureRaisesErrorWhenParamNameIsEmpty(t *testing.T) { 42 | generator := NewAnonymousFuncSignature().AddParameters( 43 | NewFuncParameter("", "string"), 44 | ) 45 | _, err := generator.Generate(0) 46 | assert.Regexp(t, regexp.MustCompile( 47 | `^\`+strings.Split(errmsg.FuncParameterNameIsEmptyErr("").Error(), " ")[0], 48 | ), err.Error()) 49 | } 50 | 51 | func TestShouldGenerateAnonymousFuncSignatureRaisesErrorWhenParamTypeIsEmpty(t *testing.T) { 52 | generator := NewAnonymousFuncSignature().AddParameters( 53 | NewFuncParameter("foo", ""), 54 | ) 55 | _, err := generator.Generate(0) 56 | assert.Regexp(t, regexp.MustCompile( 57 | `^\`+strings.Split(errmsg.LastFuncParameterTypeIsEmptyErr("").Error(), " ")[0], 58 | ), err.Error()) 59 | } 60 | -------------------------------------------------------------------------------- /generator/anonymous_func_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/moznion/gowrtr/internal/errmsg" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestShouldGenerateAnonymousFunc(t *testing.T) { 14 | generator := NewAnonymousFunc( 15 | false, 16 | NewAnonymousFuncSignature(), 17 | ) 18 | 19 | { 20 | expected := `func() { 21 | } 22 | ` 23 | gen, err := generator.Generate(0) 24 | assert.NoError(t, err) 25 | assert.Equal(t, expected, gen) 26 | } 27 | 28 | { 29 | expected := `func() { 30 | // do something 31 | fmt.Printf("%d", i) 32 | } 33 | ` 34 | generator = generator.AddStatements( 35 | NewComment(" do something"), 36 | NewRawStatement(`fmt.Printf("%d", i)`), 37 | ) 38 | gen, err := generator.Generate(0) 39 | assert.NoError(t, err) 40 | assert.Equal(t, expected, gen) 41 | } 42 | } 43 | 44 | func TestShouldGenerateAnonymousFuncWithSetterMethod(t *testing.T) { 45 | 46 | expected := `func() { 47 | // do something 48 | fmt.Printf("%d", i) 49 | } 50 | ` 51 | generator := NewAnonymousFunc(false, 52 | NewAnonymousFuncSignature(), 53 | NewComment(" do something"), 54 | NewRawStatement(`fmt.Printf("%d", i)`), 55 | ) 56 | gen, err := generator.Generate(0) 57 | assert.NoError(t, err) 58 | assert.Equal(t, expected, gen) 59 | 60 | generator = generator.Statements(NewComment("modified")) 61 | expected = `func() { 62 | //modified 63 | } 64 | ` 65 | gen, err = generator.Generate(0) 66 | assert.NoError(t, err) 67 | assert.Equal(t, expected, gen) 68 | } 69 | 70 | func TestShouldGenerateAnonymousFuncWithSignature(t *testing.T) { 71 | generator := NewAnonymousFunc( 72 | false, 73 | NewAnonymousFuncSignature(). 74 | AddParameters( 75 | NewFuncParameter("foo", "string"), 76 | NewFuncParameter("bar", "int64"), 77 | ). 78 | AddReturnTypes("string", "error"), 79 | NewComment(" do something"), 80 | NewRawStatement(`fmt.Printf("%d", i)`), 81 | ) 82 | 83 | expected := `func(foo string, bar int64) (string, error) { 84 | // do something 85 | fmt.Printf("%d", i) 86 | } 87 | ` 88 | gen, err := generator.Generate(0) 89 | assert.NoError(t, err) 90 | assert.Equal(t, expected, gen) 91 | } 92 | 93 | func TestShouldGenerateAnonymousGoFuncWithInvocation(t *testing.T) { 94 | generator := NewAnonymousFunc( 95 | true, 96 | NewAnonymousFuncSignature(). 97 | AddParameters( 98 | NewFuncParameter("foo", "string"), 99 | NewFuncParameter("bar", "int64"), 100 | ). 101 | AddReturnTypes("string", "error"), 102 | NewComment(" do something"), 103 | NewRawStatement(`fmt.Printf("%d", i)`), 104 | ).Invocation(NewFuncInvocation("foo", "bar")) 105 | 106 | expected := `go func(foo string, bar int64) (string, error) { 107 | // do something 108 | fmt.Printf("%d", i) 109 | }(foo, bar) 110 | ` 111 | gen, err := generator.Generate(0) 112 | assert.NoError(t, err) 113 | assert.Equal(t, expected, gen) 114 | } 115 | 116 | func TestShouldGenerateAnonymousFuncRaisesErrorWhenAnonymousFuncSignatureIsNil(t *testing.T) { 117 | generator := NewAnonymousFunc( 118 | false, 119 | nil, 120 | ) 121 | _, err := generator.Generate(0) 122 | assert.Regexp(t, regexp.MustCompile( 123 | `^\`+strings.Split(errmsg.AnonymousFuncSignatureIsNilError("").Error(), " ")[0], 124 | ), err.Error()) 125 | } 126 | 127 | func TestShouldGenerateAnonymousFuncRaisesErrorWhenAnonymousFuncSignatureRaisesError(t *testing.T) { 128 | generator := NewAnonymousFunc( 129 | false, 130 | NewAnonymousFuncSignature().AddParameters( 131 | NewFuncParameter("", "string"), 132 | ), 133 | ) 134 | _, err := generator.Generate(0) 135 | assert.Regexp(t, regexp.MustCompile( 136 | `^\`+strings.Split(errmsg.FuncParameterNameIsEmptyErr("").Error(), " ")[0], 137 | ), err.Error()) 138 | } 139 | 140 | func TestShouldGenerateAnonymousFuncRaisesErrorWhenStatementRaisesError(t *testing.T) { 141 | generator := NewAnonymousFunc( 142 | false, 143 | NewAnonymousFuncSignature(), 144 | NewFunc(nil, NewFuncSignature("")), 145 | ) 146 | 147 | _, err := generator.Generate(0) 148 | assert.Regexp(t, regexp.MustCompile( 149 | `^\`+strings.Split(errmsg.FuncNameIsEmptyError("").Error(), " ")[0], 150 | ), err.Error()) 151 | } 152 | 153 | func TestShouldGenerateAnonymousFuncRaisesErrorWhenFuncInvocationRaisesError(t *testing.T) { 154 | generator := NewAnonymousFunc( 155 | false, 156 | NewAnonymousFuncSignature(), 157 | ).Invocation(NewFuncInvocation("")) 158 | _, err := generator.Generate(0) 159 | assert.Regexp(t, regexp.MustCompile( 160 | `^\`+strings.Split(errmsg.FuncInvocationParameterIsEmptyError("").Error(), " ")[0], 161 | ), err.Error()) 162 | } 163 | -------------------------------------------------------------------------------- /generator/case.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/moznion/gowrtr/internal/errmsg" 7 | ) 8 | 9 | // Case represents a code generator for `case` statement. 10 | // See also: https://tour.golang.org/flowcontrol/9 11 | type Case struct { 12 | condition string 13 | statements []Statement 14 | caller string 15 | } 16 | 17 | // NewCase creates a new `Case`. 18 | func NewCase(condition string, statements ...Statement) *Case { 19 | return &Case{ 20 | condition: condition, 21 | statements: statements, 22 | caller: fetchClientCallerLine(), 23 | } 24 | } 25 | 26 | // AddStatements adds statements to `Case`. This does *not* set, just add. 27 | // This method returns a *new* `Case`; it means this method acts as immutable. 28 | func (c *Case) AddStatements(statements ...Statement) *Case { 29 | return &Case{ 30 | condition: c.condition, 31 | statements: append(c.statements, statements...), 32 | } 33 | } 34 | 35 | // Statements sets statements to `Case`. This does *not* add, just set. 36 | // This method returns a *new* `Case`; it means this method acts as immutable. 37 | func (c *Case) Statements(statements ...Statement) *Case { 38 | return &Case{ 39 | condition: c.condition, 40 | statements: statements, 41 | } 42 | } 43 | 44 | // Generate generates `case` statement as golang code. 45 | func (c *Case) Generate(indentLevel int) (string, error) { 46 | condition := c.condition 47 | if condition == "" { 48 | return "", errmsg.CaseConditionIsEmptyError(c.caller) 49 | } 50 | 51 | indent := BuildIndent(indentLevel) 52 | nextIndentLevel := indentLevel + 1 53 | 54 | stmt := fmt.Sprintf("%scase %s:\n", indent, condition) 55 | for _, statement := range c.statements { 56 | gen, err := statement.Generate(nextIndentLevel) 57 | if err != nil { 58 | return "", err 59 | } 60 | stmt += gen 61 | } 62 | 63 | return stmt, nil 64 | } 65 | -------------------------------------------------------------------------------- /generator/case_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExampleCase_Generate() { 9 | generator := NewCase(`"foo"`, NewComment(" this is foo")). 10 | AddStatements(NewRawStatement(`fmt.Printf("this is foo\n")`)) 11 | 12 | generated, err := generator.Generate(0) 13 | if err != nil { 14 | log.Fatal(err) 15 | } 16 | 17 | fmt.Println(generated) 18 | } 19 | -------------------------------------------------------------------------------- /generator/case_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/moznion/gowrtr/internal/errmsg" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestShouldGenerateCase(t *testing.T) { 14 | generator := NewCase( 15 | `"foo"`, 16 | NewComment(" XXX test test"), 17 | NewComment(" do something"), 18 | ).AddStatements(NewRawStatement(`fmt.Printf("test\n")`)) 19 | 20 | { 21 | gen, err := generator.Generate(0) 22 | assert.NoError(t, err) 23 | expected := `case "foo": 24 | // XXX test test 25 | // do something 26 | fmt.Printf("test\n") 27 | ` 28 | assert.Equal(t, expected, gen) 29 | } 30 | 31 | { 32 | gen, err := generator.Generate(2) 33 | assert.NoError(t, err) 34 | expected := ` case "foo": 35 | // XXX test test 36 | // do something 37 | fmt.Printf("test\n") 38 | ` 39 | assert.Equal(t, expected, gen) 40 | } 41 | 42 | { 43 | generator = generator.Statements(NewComment("modified")) 44 | gen, err := generator.Generate(0) 45 | assert.NoError(t, err) 46 | expected := `case "foo": 47 | //modified 48 | ` 49 | assert.Equal(t, expected, gen) 50 | } 51 | } 52 | 53 | func TestShouldGenerateCaseRaisesErrorWhenConditionIsEmpty(t *testing.T) { 54 | generator := NewCase("") 55 | _, err := generator.Generate(0) 56 | assert.Regexp(t, regexp.MustCompile( 57 | `^\`+strings.Split(errmsg.CaseConditionIsEmptyError("").Error(), " ")[0], 58 | ), err.Error()) 59 | } 60 | 61 | func TestShouldGenerateCaseRaisesErrorWhenStatementsRaisesError(t *testing.T) { 62 | generator := NewCase( 63 | `"foo"`, 64 | NewFunc(nil, NewFuncSignature("")), 65 | ) 66 | _, err := generator.Generate(0) 67 | assert.Regexp(t, regexp.MustCompile( 68 | `^\`+strings.Split(errmsg.FuncNameIsEmptyError("").Error(), " ")[0], 69 | ), err.Error()) 70 | } 71 | -------------------------------------------------------------------------------- /generator/code_block.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | // CodeBlock represents a code generator for plain code block. 4 | // 5 | // example: 6 | // 7 | // { 8 | // // do something 9 | // fmt.Println("blah blah") 10 | // } 11 | type CodeBlock struct { 12 | statements []Statement 13 | } 14 | 15 | // NewCodeBlock returns a new `CodeBlock`. 16 | func NewCodeBlock(statements ...Statement) *CodeBlock { 17 | return &CodeBlock{ 18 | statements: statements, 19 | } 20 | } 21 | 22 | // AddStatements adds statements to `CodeBlock`. This does *not* set, just add. 23 | // This method returns a *new* `CodeBlock`; it means this method acts as immutable. 24 | func (c *CodeBlock) AddStatements(statements ...Statement) *CodeBlock { 25 | return &CodeBlock{ 26 | statements: append(c.statements, statements...), 27 | } 28 | } 29 | 30 | // Statements sets statements to `CodeBlock`. This does *not* add, just set. 31 | // This method returns a *new* `CodeBlock`; it means this method acts as immutable. 32 | func (c *CodeBlock) Statements(statements ...Statement) *CodeBlock { 33 | return &CodeBlock{ 34 | statements: statements, 35 | } 36 | } 37 | 38 | // Generate generates plain code block as golang code. 39 | func (c *CodeBlock) Generate(indentLevel int) (string, error) { 40 | indent := BuildIndent(indentLevel) 41 | 42 | stmt := indent + "{\n" 43 | 44 | nextIndentLevel := indentLevel + 1 45 | for _, generator := range c.statements { 46 | gen, err := generator.Generate(nextIndentLevel) 47 | if err != nil { 48 | return "", err 49 | } 50 | stmt += gen 51 | } 52 | 53 | stmt += indent + "}\n" 54 | return stmt, nil 55 | } 56 | -------------------------------------------------------------------------------- /generator/code_block_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExampleCodeBlock_Generate() { 9 | generator := NewCodeBlock(NewComment(" do something")) 10 | generator = generator.AddStatements(NewRawStatement(`fmt.Printf("code block\n")`)) 11 | 12 | generated, err := generator.Generate(0) 13 | if err != nil { 14 | log.Fatal(err) 15 | } 16 | fmt.Println(generated) 17 | } 18 | -------------------------------------------------------------------------------- /generator/code_block_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/moznion/gowrtr/internal/errmsg" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestShouldGenerateCodeBlock(t *testing.T) { 14 | generator := NewCodeBlock( 15 | NewComment(" do something"), 16 | NewRawStatement(`fmt.Printf("%d", i)`), 17 | ) 18 | 19 | { 20 | expected := `{ 21 | // do something 22 | fmt.Printf("%d", i) 23 | } 24 | ` 25 | gen, err := generator.Generate(0) 26 | assert.NoError(t, err) 27 | assert.Equal(t, expected, gen) 28 | } 29 | 30 | { 31 | expected := ` { 32 | // do something 33 | fmt.Printf("%d", i) 34 | } 35 | ` 36 | gen, err := generator.Generate(2) 37 | assert.NoError(t, err) 38 | assert.Equal(t, expected, gen) 39 | } 40 | 41 | { 42 | generator = generator.Statements(NewComment("modified")) 43 | expected := `{ 44 | //modified 45 | } 46 | ` 47 | gen, err := generator.Generate(0) 48 | assert.NoError(t, err) 49 | assert.Equal(t, expected, gen) 50 | } 51 | } 52 | 53 | func TestShouldGenerateCodeBlockWithEmpty(t *testing.T) { 54 | generator := NewCodeBlock() 55 | 56 | { 57 | expected := `{ 58 | } 59 | ` 60 | gen, err := generator.Generate(0) 61 | assert.NoError(t, err) 62 | assert.Equal(t, expected, gen) 63 | } 64 | 65 | { 66 | expected := ` { 67 | } 68 | ` 69 | gen, err := generator.Generate(2) 70 | assert.NoError(t, err) 71 | assert.Equal(t, expected, gen) 72 | } 73 | } 74 | 75 | func TestShouldGenerateCodeBlockWithExpandingMethod(t *testing.T) { 76 | generator := NewCodeBlock().AddStatements( 77 | NewComment(" XXX: test test"), 78 | NewComment(" do something"), 79 | ).AddStatements( 80 | NewRawStatement(`fmt.Printf("%d", i)`), 81 | ) 82 | 83 | expected := `{ 84 | // XXX: test test 85 | // do something 86 | fmt.Printf("%d", i) 87 | } 88 | ` 89 | gen, err := generator.Generate(0) 90 | assert.NoError(t, err) 91 | assert.Equal(t, expected, gen) 92 | } 93 | 94 | func TestShouldGenerateCodeBlockGiveUpWhenStatementRaisesError(t *testing.T) { 95 | generator := NewCodeBlock( 96 | NewFunc(nil, NewFuncSignature("")), 97 | ) 98 | _, err := generator.Generate(0) 99 | assert.Regexp(t, regexp.MustCompile( 100 | `^\`+strings.Split(errmsg.FuncNameIsEmptyError("").Error(), " ")[0], 101 | ), err.Error()) 102 | } 103 | -------------------------------------------------------------------------------- /generator/comment.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import "fmt" 4 | 5 | // Comment represents a code generator for one line comment. 6 | type Comment struct { 7 | comment string 8 | } 9 | 10 | // NewComment returns a new `Comment`. 11 | func NewComment(comment string) *Comment { 12 | return &Comment{ 13 | comment: comment, 14 | } 15 | } 16 | 17 | // NewCommentf returns a new `Comment` with formatting. 18 | // If `args` is not empty, this method formats `stmt` with `args` by `fmt.Sprintf`. 19 | func NewCommentf(comment string, args ...interface{}) *Comment { 20 | return &Comment{ 21 | comment: fmt.Sprintf(comment, args...), 22 | } 23 | } 24 | 25 | // Generate generates one line comment statement. 26 | func (c *Comment) Generate(indentLevel int) (string, error) { 27 | indent := BuildIndent(indentLevel) 28 | return fmt.Sprintf("%s//%s\n", indent, c.comment), nil 29 | } 30 | -------------------------------------------------------------------------------- /generator/comment_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExampleComment_Generate() { 9 | generator := NewComment("this is one line comment") 10 | 11 | generated, err := generator.Generate(0) 12 | if err != nil { 13 | log.Fatal(err) 14 | } 15 | fmt.Println(generated) 16 | } 17 | -------------------------------------------------------------------------------- /generator/comment_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestShouldGenerateCommentStatement(t *testing.T) { 10 | { 11 | generator := NewComment("this is a comment") 12 | gen, err := generator.Generate(0) 13 | assert.NoError(t, err) 14 | assert.Equal(t, "//this is a comment\n", gen) 15 | } 16 | 17 | { 18 | generator := NewComment(" this is a comment") 19 | gen, err := generator.Generate(2) 20 | assert.NoError(t, err) 21 | assert.Equal(t, "\t\t// this is a comment\n", gen) 22 | } 23 | } 24 | 25 | func TestShouldGenerateCommentStatementWithFormatting(t *testing.T) { 26 | generator := NewCommentf("this is a %s", "comment") 27 | gen, err := generator.Generate(0) 28 | assert.NoError(t, err) 29 | assert.Equal(t, "//this is a comment\n", gen) 30 | } 31 | -------------------------------------------------------------------------------- /generator/composite_literal.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/moznion/gowrtr/internal/errmsg" 8 | ) 9 | 10 | type compositeLiteralField struct { 11 | key string 12 | value Statement 13 | } 14 | 15 | // CompositeLiteral represents a code generator for composite literal. 16 | // Please see also: https://golang.org/doc/effective_go.html#composite_literals 17 | type CompositeLiteral struct { 18 | typ string 19 | fields []*compositeLiteralField 20 | callers []string 21 | } 22 | 23 | // NewCompositeLiteral returns a new `CompositeLiteral`. 24 | func NewCompositeLiteral(typ string) *CompositeLiteral { 25 | return &CompositeLiteral{ 26 | typ: typ, 27 | } 28 | } 29 | 30 | // AddField adds a field as `Statement` to `ComposeLiteral`. 31 | // This method returns a *new* `Struct`; it means this method acts as immutable. 32 | func (c *CompositeLiteral) AddField(key string, value Statement) *CompositeLiteral { 33 | return &CompositeLiteral{ 34 | typ: c.typ, 35 | fields: append(c.fields, &compositeLiteralField{ 36 | key: key, 37 | value: value, 38 | }), 39 | callers: append(c.callers, fetchClientCallerLine()), 40 | } 41 | } 42 | 43 | // AddFieldStr adds a field as string to `ComposeLiteral`. 44 | // This method returns a *new* `Struct`; it means this method acts as immutable. 45 | func (c *CompositeLiteral) AddFieldStr(key string, value string) *CompositeLiteral { 46 | return &CompositeLiteral{ 47 | typ: c.typ, 48 | fields: append(c.fields, &compositeLiteralField{ 49 | key: key, 50 | value: NewRawStatement(fmt.Sprintf(`"%s"`, value)), 51 | }), 52 | callers: append(c.callers, fetchClientCallerLine()), 53 | } 54 | } 55 | 56 | // AddFieldRaw adds a field as raw text to `ComposeLiteral`. 57 | // This method returns a *new* `Struct`; it means this method acts as immutable. 58 | func (c *CompositeLiteral) AddFieldRaw(key string, value interface{}) *CompositeLiteral { 59 | return &CompositeLiteral{ 60 | typ: c.typ, 61 | fields: append(c.fields, &compositeLiteralField{ 62 | key: key, 63 | value: NewRawStatement(fmt.Sprintf("%v", value)), 64 | }), 65 | callers: append(c.callers, fetchClientCallerLine()), 66 | } 67 | } 68 | 69 | // Generate generates composite literal block as golang code. 70 | func (c *CompositeLiteral) Generate(indentLevel int) (string, error) { 71 | indent := BuildIndent(indentLevel) 72 | nextLevelIndent := BuildIndent(indentLevel + 1) 73 | 74 | stmt := fmt.Sprintf("%s%s{\n", indent, c.typ) 75 | for i, field := range c.fields { 76 | genValue, err := field.value.Generate(indentLevel + 1) 77 | if err != nil { 78 | return "", err 79 | } 80 | 81 | genValue = strings.TrimSpace(genValue) 82 | 83 | stmt += nextLevelIndent 84 | 85 | if key := field.key; key != "" { 86 | stmt += key + ": " 87 | } 88 | if genValue == "" { 89 | return "", errmsg.ValueOfCompositeLiteralIsEmptyError(c.callers[i]) 90 | } 91 | stmt += fmt.Sprintf("%s,\n", genValue) 92 | } 93 | stmt += fmt.Sprintf("%s}\n", indent) 94 | 95 | return stmt, nil 96 | } 97 | -------------------------------------------------------------------------------- /generator/composite_literal_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/moznion/gowrtr/internal/errmsg" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestShouldGenerateCompositeLiteralBeSuccess(t *testing.T) { 14 | composeGenerator := NewCompositeLiteral("&Struct"). 15 | AddField("foo", NewRawStatement(`"foo-value"`)). 16 | AddFieldStr("bar", "bar-value"). 17 | AddField("buz", NewAnonymousFunc( 18 | false, 19 | NewAnonymousFuncSignature().ReturnTypes("bool"), 20 | NewReturnStatement("true"), 21 | ).Invocation(NewFuncInvocation())). 22 | AddFieldRaw("qux", 12345). 23 | AddFieldRaw("foobar", false) 24 | 25 | { 26 | gen, err := composeGenerator.Generate(0) 27 | 28 | assert.NoError(t, err) 29 | expected := `&Struct{ 30 | foo: "foo-value", 31 | bar: "bar-value", 32 | buz: func() bool { 33 | return true 34 | }(), 35 | qux: 12345, 36 | foobar: false, 37 | } 38 | ` 39 | assert.Equal(t, expected, gen) 40 | } 41 | 42 | { 43 | gen, err := composeGenerator.Generate(2) 44 | 45 | assert.NoError(t, err) 46 | expected := ` &Struct{ 47 | foo: "foo-value", 48 | bar: "bar-value", 49 | buz: func() bool { 50 | return true 51 | }(), 52 | qux: 12345, 53 | foobar: false, 54 | } 55 | ` 56 | assert.Equal(t, expected, gen) 57 | } 58 | } 59 | 60 | func TestShouldGenerateCompositeLiteralWithEmptyKey(t *testing.T) { 61 | composeGenerator := NewCompositeLiteral("[]string"). 62 | AddFieldStr("", "foo"). 63 | AddFieldStr("", "bar"). 64 | AddFieldStr("", "buz") 65 | gen, err := composeGenerator.Generate(0) 66 | expected := `[]string{ 67 | "foo", 68 | "bar", 69 | "buz", 70 | } 71 | ` 72 | assert.NoError(t, err) 73 | assert.Equal(t, expected, gen) 74 | } 75 | 76 | func TestShouldGenerateCompositeLiteralRaiseError(t *testing.T) { 77 | _, err := NewCompositeLiteral("").AddField("foo", NewIf("")).Generate(0) 78 | assert.Regexp(t, regexp.MustCompile( 79 | `^\`+strings.Split(errmsg.IfConditionIsEmptyError("").Error(), " ")[0], 80 | ), err.Error()) 81 | } 82 | 83 | func TestShouldGenerateCompositeLiteralRaiseErrorWhenValueIsEmpty(t *testing.T) { 84 | _, err := NewCompositeLiteral("[]string").AddField("foo", NewRawStatement("")).Generate(0) 85 | assert.Regexp(t, regexp.MustCompile( 86 | `^\`+strings.Split(errmsg.ValueOfCompositeLiteralIsEmptyError("").Error(), " ")[0], 87 | ), err.Error()) 88 | } 89 | -------------------------------------------------------------------------------- /generator/default_case.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import "fmt" 4 | 5 | // DefaultCase represents a code generator for `default` block of `switch-case` notation. 6 | type DefaultCase struct { 7 | statements []Statement 8 | } 9 | 10 | // NewDefaultCase returns a new `DefaultCase`. 11 | func NewDefaultCase(statements ...Statement) *DefaultCase { 12 | return &DefaultCase{ 13 | statements: statements, 14 | } 15 | } 16 | 17 | // AddStatements adds statements for `default` block to `DefaultCase`. This does *not* set, just add. 18 | // This method returns a *new* `DefaultCase`; it means this method acts as immutable. 19 | func (d *DefaultCase) AddStatements(statements ...Statement) *DefaultCase { 20 | return &DefaultCase{ 21 | statements: append(d.statements, statements...), 22 | } 23 | } 24 | 25 | // Statements sets statements for `default` block to `DefaultCase`. This does *not* add, just set. 26 | // This method returns a *new* `DefaultCase`; it means this method acts as immutable. 27 | func (d *DefaultCase) Statements(statements ...Statement) *DefaultCase { 28 | return &DefaultCase{ 29 | statements: statements, 30 | } 31 | } 32 | 33 | // Generate generates `default` block as golang code. 34 | func (d *DefaultCase) Generate(indentLevel int) (string, error) { 35 | indent := BuildIndent(indentLevel) 36 | nextIndentLevel := indentLevel + 1 37 | 38 | stmt := fmt.Sprintf("%sdefault:\n", indent) 39 | for _, statement := range d.statements { 40 | gen, err := statement.Generate(nextIndentLevel) 41 | if err != nil { 42 | return "", err 43 | } 44 | stmt += gen 45 | } 46 | 47 | return stmt, nil 48 | } 49 | -------------------------------------------------------------------------------- /generator/default_case_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExampleDefaultCase_Generate() { 9 | generator := NewDefaultCase( 10 | NewComment(" XXX test test"), 11 | NewComment(" do something"), 12 | ).AddStatements(NewRawStatement(`fmt.Printf("test\n")`)) 13 | 14 | generated, err := generator.Generate(0) 15 | if err != nil { 16 | log.Fatal(err) 17 | } 18 | fmt.Println(generated) 19 | } 20 | -------------------------------------------------------------------------------- /generator/default_case_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/moznion/gowrtr/internal/errmsg" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestShouldGenerateDefaultCase(t *testing.T) { 14 | generator := NewDefaultCase( 15 | NewComment(" XXX test test"), 16 | NewComment(" do something"), 17 | ).AddStatements(NewRawStatement(`fmt.Printf("test\n")`)) 18 | 19 | { 20 | gen, err := generator.Generate(0) 21 | assert.NoError(t, err) 22 | expected := `default: 23 | // XXX test test 24 | // do something 25 | fmt.Printf("test\n") 26 | ` 27 | assert.Equal(t, expected, gen) 28 | } 29 | 30 | { 31 | gen, err := generator.Generate(2) 32 | assert.NoError(t, err) 33 | expected := ` default: 34 | // XXX test test 35 | // do something 36 | fmt.Printf("test\n") 37 | ` 38 | assert.Equal(t, expected, gen) 39 | } 40 | 41 | { 42 | generator = generator.Statements(NewComment("modified")) 43 | gen, err := generator.Generate(0) 44 | assert.NoError(t, err) 45 | expected := `default: 46 | //modified 47 | ` 48 | assert.Equal(t, expected, gen) 49 | } 50 | } 51 | 52 | func TestShouldGenerateDefaultCaseRaisesErrorWhenStatementsRaisesError(t *testing.T) { 53 | generator := NewDefaultCase( 54 | NewFunc(nil, NewFuncSignature("")), 55 | ) 56 | _, err := generator.Generate(0) 57 | assert.Regexp(t, regexp.MustCompile( 58 | `^\`+strings.Split(errmsg.FuncNameIsEmptyError("").Error(), " ")[0], 59 | ), err.Error()) 60 | } 61 | -------------------------------------------------------------------------------- /generator/else.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import "fmt" 4 | 5 | // Else represents a code generator for `else` block. 6 | type Else struct { 7 | statements []Statement 8 | } 9 | 10 | // NewElse returns a new `Else`. 11 | func NewElse(statements ...Statement) *Else { 12 | return &Else{ 13 | statements: statements, 14 | } 15 | } 16 | 17 | // AddStatements adds statements for `else` block to `Else`. This does *not* set, just add. 18 | // This method returns a *new* `Else`; it means this method acts as immutable. 19 | func (e *Else) AddStatements(statements ...Statement) *Else { 20 | return &Else{ 21 | statements: append(e.statements, statements...), 22 | } 23 | } 24 | 25 | // Statements sets statements for `else` block to `Else`. This does *not* add, just set. 26 | // This method returns a *new* `Else`; it means this method acts as immutable. 27 | func (e *Else) Statements(statements ...Statement) *Else { 28 | return &Else{ 29 | statements: statements, 30 | } 31 | } 32 | 33 | // Generate generates `else` block as golang code. 34 | func (e *Else) Generate(indentLevel int) (string, error) { 35 | stmt := " else {\n" 36 | 37 | indent := BuildIndent(indentLevel) 38 | nextIndentLevel := indentLevel + 1 39 | for _, c := range e.statements { 40 | gen, err := c.Generate(nextIndentLevel) 41 | if err != nil { 42 | return "", err 43 | } 44 | stmt += gen 45 | } 46 | stmt += fmt.Sprintf("%s}", indent) 47 | 48 | return stmt, nil 49 | } 50 | -------------------------------------------------------------------------------- /generator/else_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExampleElse_Generate() { 9 | generator := NewElse( 10 | NewComment(" XXX test test"), 11 | NewComment(" do something"), 12 | ).AddStatements( 13 | NewRawStatement(`fmt.Printf("%d", i)`), 14 | ) 15 | 16 | generated, err := generator.Generate(0) 17 | if err != nil { 18 | log.Fatal(err) 19 | } 20 | fmt.Println(generated) 21 | } 22 | -------------------------------------------------------------------------------- /generator/else_if.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import "fmt" 4 | 5 | // ElseIf represents a code generator for `else-if` block. 6 | type ElseIf struct { 7 | condition string 8 | statements []Statement 9 | } 10 | 11 | // NewElseIf returns a new `ElseIf`. 12 | func NewElseIf(condition string, statements ...Statement) *ElseIf { 13 | return &ElseIf{ 14 | condition: condition, 15 | statements: statements, 16 | } 17 | } 18 | 19 | // AddStatements adds statements for the `else-if` block to `ElseIf`. This does *not* set, just add. 20 | // This method returns a *new* `ElseIf`; it means this method acts as immutable. 21 | func (ei *ElseIf) AddStatements(statements ...Statement) *ElseIf { 22 | return &ElseIf{ 23 | condition: ei.condition, 24 | statements: append(ei.statements, statements...), 25 | } 26 | } 27 | 28 | // Statements sets statements for the `else-if` block to `ElseIf`. This does *not* add, just set. 29 | // This method returns a *new* `ElseIf`; it means this method acts as immutable. 30 | func (ei *ElseIf) Statements(statements ...Statement) *ElseIf { 31 | return &ElseIf{ 32 | condition: ei.condition, 33 | statements: statements, 34 | } 35 | } 36 | 37 | // Generate generates `else-if` block as golang code. 38 | func (ei *ElseIf) Generate(indentLevel int) (string, error) { 39 | indent := BuildIndent(indentLevel) 40 | 41 | stmt := fmt.Sprintf(" else if %s {\n", ei.condition) 42 | 43 | nextIndentLevel := indentLevel + 1 44 | for _, c := range ei.statements { 45 | gen, err := c.Generate(nextIndentLevel) 46 | if err != nil { 47 | return "", err 48 | } 49 | stmt += gen 50 | } 51 | stmt += fmt.Sprintf("%s}", indent) 52 | 53 | return stmt, nil 54 | } 55 | -------------------------------------------------------------------------------- /generator/else_if_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExampleElseIf_Generate() { 9 | generator := NewElseIf("i > 0"). 10 | AddStatements( 11 | NewComment(" XXX: test test"), 12 | NewComment(" do something"), 13 | ). 14 | AddStatements(NewRawStatement(`fmt.Printf("%d", i)`)) 15 | 16 | generated, err := generator.Generate(0) 17 | if err != nil { 18 | log.Fatal(err) 19 | } 20 | fmt.Println(generated) 21 | } 22 | -------------------------------------------------------------------------------- /generator/else_if_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/moznion/gowrtr/internal/errmsg" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestShouldGenerateElseIfCode(t *testing.T) { 14 | generator := NewElseIf("i > 0", 15 | NewComment(" do something"), 16 | NewRawStatement(`fmt.Printf("%d", i)`), 17 | ) 18 | 19 | { 20 | expected := ` else if i > 0 { 21 | // do something 22 | fmt.Printf("%d", i) 23 | }` 24 | gen, err := generator.Generate(0) 25 | assert.NoError(t, err) 26 | assert.Equal(t, expected, gen) 27 | } 28 | 29 | { 30 | expected := ` else if i > 0 { 31 | // do something 32 | fmt.Printf("%d", i) 33 | }` 34 | gen, err := generator.Generate(2) 35 | assert.NoError(t, err) 36 | assert.Equal(t, expected, gen) 37 | } 38 | 39 | { 40 | generator = generator.Statements(NewComment("modified")) 41 | expected := ` else if i > 0 { 42 | //modified 43 | }` 44 | gen, err := generator.Generate(0) 45 | assert.NoError(t, err) 46 | assert.Equal(t, expected, gen) 47 | } 48 | } 49 | 50 | func TestShouldGenerateElseIfWithExpandingMethod(t *testing.T) { 51 | generator := NewElseIf("i > 0"). 52 | AddStatements( 53 | NewComment(" XXX: test test"), 54 | NewComment(" do something"), 55 | ). 56 | AddStatements(NewRawStatement(`fmt.Printf("%d", i)`)) 57 | 58 | expected := ` else if i > 0 { 59 | // XXX: test test 60 | // do something 61 | fmt.Printf("%d", i) 62 | }` 63 | gen, err := generator.Generate(0) 64 | assert.NoError(t, err) 65 | assert.Equal(t, expected, gen) 66 | } 67 | 68 | func TestShouldGenerateElseIfRaisesError(t *testing.T) { 69 | generator := NewElseIf( 70 | "i > 0", 71 | NewFunc(nil, NewFuncSignature("")), 72 | ) 73 | 74 | _, err := generator.Generate(0) 75 | assert.Regexp(t, regexp.MustCompile( 76 | `^\`+strings.Split(errmsg.FuncNameIsEmptyError("").Error(), " ")[0], 77 | ), err.Error()) 78 | } 79 | -------------------------------------------------------------------------------- /generator/else_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/moznion/gowrtr/internal/errmsg" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestShouldGenerateElseCode(t *testing.T) { 14 | generator := NewElse( 15 | NewComment(" XXX test test"), 16 | NewComment(" do something"), 17 | ).AddStatements( 18 | NewRawStatement(`fmt.Printf("%d", i)`), 19 | ) 20 | 21 | { 22 | gen, err := generator.Generate(0) 23 | assert.NoError(t, err) 24 | expected := ` else { 25 | // XXX test test 26 | // do something 27 | fmt.Printf("%d", i) 28 | }` 29 | assert.Equal(t, expected, gen) 30 | } 31 | 32 | { 33 | gen, err := generator.Generate(2) 34 | assert.NoError(t, err) 35 | expected := ` else { 36 | // XXX test test 37 | // do something 38 | fmt.Printf("%d", i) 39 | }` 40 | assert.Equal(t, expected, gen) 41 | } 42 | 43 | { 44 | generator = generator.Statements(NewComment("modified")) 45 | gen, err := generator.Generate(0) 46 | assert.NoError(t, err) 47 | expected := ` else { 48 | //modified 49 | }` 50 | assert.Equal(t, expected, gen) 51 | } 52 | } 53 | 54 | func TestShouldGenerateElseCodeRaisesError(t *testing.T) { 55 | generator := NewElse( 56 | NewFunc(nil, NewFuncSignature("")), 57 | ) 58 | _, err := generator.Generate(0) 59 | assert.Regexp(t, regexp.MustCompile( 60 | `^\`+strings.Split(errmsg.FuncNameIsEmptyError("").Error(), " ")[0], 61 | ), err.Error()) 62 | } 63 | -------------------------------------------------------------------------------- /generator/for.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import "fmt" 4 | 5 | // For represents a code generator for `for` block. 6 | type For struct { 7 | condition string 8 | statements []Statement 9 | } 10 | 11 | // NewFor returns a new `For`. 12 | func NewFor(condition string, statements ...Statement) *For { 13 | return &For{ 14 | condition: condition, 15 | statements: statements, 16 | } 17 | } 18 | 19 | // AddStatements adds statements for `for` block to `For`. This does *not* set, just add. 20 | // This method returns a *new* `For`; it means this method acts as immutable. 21 | func (fg *For) AddStatements(statements ...Statement) *For { 22 | return &For{ 23 | condition: fg.condition, 24 | statements: append(fg.statements, statements...), 25 | } 26 | } 27 | 28 | // Statements sets statements for `for` block to `For`. This does *not* add, just set. 29 | // This method returns a *new* `For`; it means this method acts as immutable. 30 | func (fg *For) Statements(statements ...Statement) *For { 31 | return &For{ 32 | condition: fg.condition, 33 | statements: statements, 34 | } 35 | } 36 | 37 | // Generate generates a `for` block as golang code. 38 | func (fg *For) Generate(indentLevel int) (string, error) { 39 | indent := BuildIndent(indentLevel) 40 | 41 | cond := fg.condition 42 | stmt := fmt.Sprintf("%sfor %s", indent, cond) 43 | if cond != "" { 44 | stmt += " " 45 | } 46 | stmt += "{\n" 47 | 48 | nextIndentLevel := indentLevel + 1 49 | for _, c := range fg.statements { 50 | gen, err := c.Generate(nextIndentLevel) 51 | if err != nil { 52 | return "", err 53 | } 54 | stmt += gen 55 | } 56 | stmt += fmt.Sprintf("%s}\n", indent) 57 | 58 | return stmt, nil 59 | } 60 | -------------------------------------------------------------------------------- /generator/for_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExampleFor_Generate() { 9 | generator := NewFor( 10 | "i := 0; i < foo; i++", 11 | NewComment(" do something"), 12 | ).AddStatements(NewRawStatement(`fmt.Printf("%d", i)`)) 13 | 14 | generated, err := generator.Generate(0) 15 | if err != nil { 16 | log.Fatal(err) 17 | } 18 | fmt.Println(generated) 19 | } 20 | -------------------------------------------------------------------------------- /generator/for_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/moznion/gowrtr/internal/errmsg" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestShouldGenerateForCode(t *testing.T) { 13 | generator := NewFor( 14 | "i := 0; i < foo; i++", 15 | NewComment(" do something"), 16 | NewRawStatement(`fmt.Printf("%d", i)`), 17 | ) 18 | 19 | { 20 | expected := `for i := 0; i < foo; i++ { 21 | // do something 22 | fmt.Printf("%d", i) 23 | } 24 | ` 25 | gen, err := generator.Generate(0) 26 | assert.NoError(t, err) 27 | assert.Equal(t, expected, gen) 28 | } 29 | 30 | { 31 | expected := ` for i := 0; i < foo; i++ { 32 | // do something 33 | fmt.Printf("%d", i) 34 | } 35 | ` 36 | gen, err := generator.Generate(2) 37 | assert.NoError(t, err) 38 | assert.Equal(t, expected, gen) 39 | } 40 | 41 | { 42 | generator = generator.Statements(NewComment("modified")) 43 | expected := `for i := 0; i < foo; i++ { 44 | //modified 45 | } 46 | ` 47 | gen, err := generator.Generate(0) 48 | assert.NoError(t, err) 49 | assert.Equal(t, expected, gen) 50 | } 51 | } 52 | 53 | func TestShouldGenerateForCodeWithExpandingMethod(t *testing.T) { 54 | generator := NewFor("i := 0; i < foo; i++"). 55 | AddStatements( 56 | NewComment(" XXX: test test"), 57 | NewComment(" do something"), 58 | ). 59 | AddStatements(NewRawStatement(`fmt.Printf("%d", i)`)) 60 | 61 | expected := `for i := 0; i < foo; i++ { 62 | // XXX: test test 63 | // do something 64 | fmt.Printf("%d", i) 65 | } 66 | ` 67 | gen, err := generator.Generate(0) 68 | assert.NoError(t, err) 69 | assert.Equal(t, expected, gen) 70 | } 71 | 72 | func TestShouldGenerateForCodeGiveUpWhenStatementRaisesError(t *testing.T) { 73 | generator := NewFor( 74 | "i := 0; i < foo; i++", 75 | NewFunc(nil, NewFuncSignature("")), 76 | ) 77 | _, err := generator.Generate(0) 78 | assert.Regexp(t, regexp.MustCompile( 79 | `^\`+strings.Split(errmsg.FuncNameIsEmptyError("").Error(), " ")[0], 80 | ), err.Error()) 81 | } 82 | -------------------------------------------------------------------------------- /generator/frame_fetcher.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "strings" 7 | ) 8 | 9 | func fetchClientCallerLine(skip ...int) string { 10 | s := 2 11 | if len(skip) > 0 { 12 | s = skip[0] 13 | } 14 | 15 | caller := "" 16 | for { 17 | pc, file, line, ok := runtime.Caller(s) 18 | f := runtime.FuncForPC(pc) 19 | if strings.Contains(f.Name(), "github.com/moznion/gowrtr") { 20 | s++ 21 | continue 22 | } 23 | 24 | if !ok { 25 | break 26 | } 27 | 28 | caller = fmt.Sprintf("%s:%d", file, line) 29 | break 30 | } 31 | 32 | return caller 33 | } 34 | 35 | func fetchClientCallerLineAsSlice(size int, skip ...int) []string { 36 | s := 3 37 | if len(skip) > 0 { 38 | s = skip[0] 39 | } 40 | 41 | caller := fetchClientCallerLine(s) 42 | callers := make([]string, size) 43 | for i := 0; i < size; i++ { 44 | callers[i] = caller 45 | } 46 | return callers 47 | } 48 | -------------------------------------------------------------------------------- /generator/frame_fetcher_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestFetchClientCallerLineShouldBeNG(t *testing.T) { 10 | caller := fetchClientCallerLine(10000) 11 | assert.Empty(t, caller) 12 | } 13 | 14 | func TestFetchCallerLineAsSliceShouldBeNG(t *testing.T) { 15 | callers := fetchClientCallerLineAsSlice(1, 10000) 16 | assert.Len(t, callers, 1) 17 | assert.Empty(t, callers[0]) 18 | } 19 | -------------------------------------------------------------------------------- /generator/func.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "github.com/moznion/gowrtr/internal/errmsg" 5 | ) 6 | 7 | // Func represents a code generator for the func. 8 | type Func struct { 9 | funcReceiver *FuncReceiver 10 | funcSignature *FuncSignature 11 | statements []Statement 12 | caller string 13 | } 14 | 15 | // NewFunc returns a new `Func`. 16 | func NewFunc(receiver *FuncReceiver, signature *FuncSignature, statements ...Statement) *Func { 17 | return &Func{ 18 | funcReceiver: receiver, 19 | funcSignature: signature, 20 | statements: statements, 21 | caller: fetchClientCallerLine(), 22 | } 23 | } 24 | 25 | // AddStatements adds statements for the func to `Func`. This does *not* set, just add. 26 | // This method returns a *new* `Func`; it means this method acts as immutable. 27 | func (fg *Func) AddStatements(statements ...Statement) *Func { 28 | return &Func{ 29 | funcReceiver: fg.funcReceiver, 30 | funcSignature: fg.funcSignature, 31 | statements: append(fg.statements, statements...), 32 | } 33 | } 34 | 35 | // Statements sets statements for the func to `Func`. This does *not* add, just set. 36 | // This method returns a *new* `Func`; it means this method acts as immutable. 37 | func (fg *Func) Statements(statements ...Statement) *Func { 38 | return &Func{ 39 | funcReceiver: fg.funcReceiver, 40 | funcSignature: fg.funcSignature, 41 | statements: statements, 42 | } 43 | } 44 | 45 | // Generate generates a func block as golang code. 46 | func (fg *Func) Generate(indentLevel int) (string, error) { 47 | indent := BuildIndent(indentLevel) 48 | 49 | stmt := indent + "func " 50 | 51 | receiver := "" 52 | if fg.funcReceiver != nil { 53 | var err error 54 | receiver, err = fg.funcReceiver.Generate(0) 55 | if err != nil { 56 | return "", err 57 | } 58 | } 59 | if receiver != "" { 60 | stmt += receiver + " " 61 | } 62 | 63 | if fg.funcSignature == nil { 64 | return "", errmsg.FuncSignatureIsNilError(fg.caller) 65 | } 66 | sig, err := fg.funcSignature.Generate(0) 67 | if err != nil { 68 | return "", err 69 | } 70 | stmt += sig + " {\n" 71 | 72 | nextIndentLevel := indentLevel + 1 73 | for _, c := range fg.statements { 74 | gen, err := c.Generate(nextIndentLevel) 75 | if err != nil { 76 | return "", err 77 | } 78 | stmt += gen 79 | } 80 | 81 | stmt += indent + "}\n" 82 | 83 | return stmt, nil 84 | } 85 | -------------------------------------------------------------------------------- /generator/func_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExampleFunc_Generate() { 9 | generator := NewFunc( 10 | NewFuncReceiver("m", "*MyStruct"), 11 | NewFuncSignature("myFunc"). 12 | AddParameters( 13 | NewFuncParameter("foo", ""), 14 | NewFuncParameter("bar", "string"), 15 | ). 16 | AddReturnTypes("string", "error"), 17 | ).AddStatements( 18 | NewComment(" do something"), 19 | NewNewline(), 20 | NewReturnStatement("foo+bar", "nil"), 21 | ) 22 | 23 | generated, err := generator.Generate(0) 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | fmt.Println(generated) 28 | } 29 | -------------------------------------------------------------------------------- /generator/func_invocation.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/moznion/gowrtr/internal/errmsg" 7 | ) 8 | 9 | // FuncInvocation represents a code generator for func invocation. 10 | type FuncInvocation struct { 11 | parameters []string 12 | callers []string 13 | genericsTypes TypeArguments 14 | } 15 | 16 | // NewFuncInvocation returns a new `FuncInvocation`. 17 | func NewFuncInvocation(parameters ...string) *FuncInvocation { 18 | return &FuncInvocation{ 19 | parameters: parameters, 20 | callers: fetchClientCallerLineAsSlice(len(parameters)), 21 | genericsTypes: TypeArguments{}, 22 | } 23 | } 24 | 25 | // AddParameters adds parameters of func invocation to `FuncInvocation`. This does *not* set, just add. 26 | // This method returns a *new* `FuncInvocation`; it means this method acts as immutable. 27 | func (fig *FuncInvocation) AddParameters(parameters ...string) *FuncInvocation { 28 | return &FuncInvocation{ 29 | parameters: append(fig.parameters, parameters...), 30 | callers: append(fig.callers, fetchClientCallerLineAsSlice(len(parameters))...), 31 | genericsTypes: fig.genericsTypes, 32 | } 33 | } 34 | 35 | // Parameters sets parameters of func invocation to `FuncInvocation`. This does *not* add, just set. 36 | // This method returns a *new* `FuncInvocation`; it means this method acts as immutable. 37 | func (fig *FuncInvocation) Parameters(parameters ...string) *FuncInvocation { 38 | return &FuncInvocation{ 39 | parameters: parameters, 40 | callers: fetchClientCallerLineAsSlice(len(parameters)), 41 | genericsTypes: fig.genericsTypes, 42 | } 43 | } 44 | 45 | // GenericsTypes makes a new FuncInvocation value that is based on the receiver value with the given generics type names. 46 | func (fig *FuncInvocation) GenericsTypes(typeNames TypeArguments) *FuncInvocation { 47 | return &FuncInvocation{ 48 | parameters: fig.parameters, 49 | callers: fig.callers, 50 | genericsTypes: typeNames, 51 | } 52 | } 53 | 54 | // Generate generates the func invocation as golang code. 55 | func (fig *FuncInvocation) Generate(indentLevel int) (string, error) { 56 | for i, param := range fig.parameters { 57 | if param == "" { 58 | return "", errmsg.FuncInvocationParameterIsEmptyError(fig.callers[i]) 59 | } 60 | } 61 | 62 | generics := "" 63 | if len(fig.genericsTypes) > 0 { 64 | generics, _ = fig.genericsTypes.Generate(0) 65 | } 66 | 67 | return generics + "(" + strings.Join(fig.parameters, ", ") + ")", nil 68 | } 69 | -------------------------------------------------------------------------------- /generator/func_invocation_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExampleFuncInvocation_Generate() { 9 | generator := NewFuncInvocation("foo").AddParameters("bar") 10 | 11 | generated, err := generator.Generate(0) 12 | if err != nil { 13 | log.Fatal(err) 14 | } 15 | fmt.Println(generated) 16 | } 17 | 18 | func ExampleFuncInvocation_Generate_withGenericsTypes() { 19 | generator := NewFuncInvocation("foo").AddParameters("bar").GenericsTypes(TypeArguments{"string", "int64"}) 20 | 21 | generated, err := generator.Generate(0) 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | fmt.Println(generated) 26 | } 27 | -------------------------------------------------------------------------------- /generator/func_invocation_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/moznion/gowrtr/internal/errmsg" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestShouldGenerateFuncInvocationCode(t *testing.T) { 14 | generator := NewFuncInvocation() 15 | 16 | gen, err := generator.Generate(0) 17 | assert.NoError(t, err) 18 | assert.Equal(t, "()", gen) 19 | 20 | generator = generator.AddParameters("foo") 21 | gen, err = generator.Generate(0) 22 | assert.NoError(t, err) 23 | assert.Equal(t, "(foo)", gen) 24 | 25 | generator = generator.AddParameters("bar") 26 | gen, err = generator.Generate(0) 27 | assert.NoError(t, err) 28 | assert.Equal(t, "(foo, bar)", gen) 29 | 30 | generator = generator.Parameters("buz") 31 | gen, err = generator.Generate(0) 32 | assert.NoError(t, err) 33 | assert.Equal(t, "(buz)", gen) 34 | 35 | generator = generator.GenericsTypes(TypeArguments{"string"}) 36 | gen, err = generator.Generate(0) 37 | assert.NoError(t, err) 38 | assert.Equal(t, "[string](buz)", gen) 39 | 40 | generator = generator.GenericsTypes(TypeArguments{"string", "int"}) 41 | gen, err = generator.Generate(0) 42 | assert.NoError(t, err) 43 | assert.Equal(t, "[string, int](buz)", gen) 44 | } 45 | 46 | func TestShouldGenerateFuncInvocationRaisesErrorWhenParameterIsEmpty(t *testing.T) { 47 | generator := NewFuncInvocation("foo", "", "bar") 48 | _, err := generator.Generate(0) 49 | assert.Regexp(t, regexp.MustCompile( 50 | `^\`+strings.Split(errmsg.FuncInvocationParameterIsEmptyError("").Error(), " ")[0], 51 | ), err.Error()) 52 | } 53 | -------------------------------------------------------------------------------- /generator/func_receiver.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/moznion/gowrtr/internal/errmsg" 7 | ) 8 | 9 | // FuncReceiver represents a code generator for the receiver of the func. 10 | type FuncReceiver struct { 11 | name string 12 | typ string 13 | caller string 14 | genericsTypeParameterNames TypeParameterNames 15 | } 16 | 17 | // NewFuncReceiver returns a new `FuncReceiver`. 18 | func NewFuncReceiver(name string, typ string) *FuncReceiver { 19 | return NewFuncReceiverWithGenerics(name, typ, TypeParameterNames{}) 20 | } 21 | 22 | func NewFuncReceiverWithGenerics(name string, typ string, genericsTypeParameterNames TypeParameterNames) *FuncReceiver { 23 | return &FuncReceiver{ 24 | name: name, 25 | typ: typ, 26 | caller: fetchClientCallerLine(), 27 | genericsTypeParameterNames: genericsTypeParameterNames, 28 | } 29 | } 30 | 31 | // Generate generates a receiver of the func as golang code. 32 | func (f *FuncReceiver) Generate(indentLevel int) (string, error) { 33 | name := f.name 34 | typ := f.typ 35 | 36 | if typ == "" && name == "" { 37 | return "", nil 38 | } 39 | 40 | if name == "" { 41 | return "", errmsg.FuncReceiverNameIsEmptyError(f.caller) 42 | } 43 | 44 | if typ == "" { 45 | return "", errmsg.FuncReceiverTypeIsEmptyError(f.caller) 46 | } 47 | 48 | genericsTypeParam := "" 49 | if len(f.genericsTypeParameterNames) > 0 { 50 | genericsTypeParam, _ = f.genericsTypeParameterNames.Generate(0) 51 | } 52 | 53 | return fmt.Sprintf("(%s %s%s)", name, typ, genericsTypeParam), nil 54 | } 55 | -------------------------------------------------------------------------------- /generator/func_receiver_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExampleFuncReceiver_Generate() { 9 | funcReceiver := NewFuncReceiver("f", "*Foo") 10 | 11 | generated, err := funcReceiver.Generate(0) 12 | if err != nil { 13 | log.Fatal(err) 14 | } 15 | fmt.Println(generated) 16 | } 17 | 18 | func ExampleFuncReceiver_Generate_withGenericsTypeParameterNames() { 19 | funcReceiver := NewFuncReceiverWithGenerics("f", "*Foo", TypeParameterNames{"T", "U"}) 20 | 21 | generated, err := funcReceiver.Generate(0) 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | fmt.Println(generated) 26 | } 27 | -------------------------------------------------------------------------------- /generator/func_receiver_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/moznion/gowrtr/internal/errmsg" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestShouldGeneratingFuncReceiverCodeBeSuccessful(t *testing.T) { 14 | funcReceiver := NewFuncReceiver("f", "*Foo") 15 | gen, err := funcReceiver.Generate(0) 16 | assert.NoError(t, err) 17 | assert.Equal(t, "(f *Foo)", gen) 18 | } 19 | 20 | func TestShouldGeneratingFuncReceiverCodeBeSuccessfulWithEmpty(t *testing.T) { 21 | funcReceiver := NewFuncReceiver("", "") 22 | gen, err := funcReceiver.Generate(0) 23 | assert.NoError(t, err) 24 | assert.Equal(t, "", gen) 25 | } 26 | 27 | func TestShouldGeneratingFuncReceiverRaisesErrorWhenFuncReceiverNameIsEmpty(t *testing.T) { 28 | funcReceiver := NewFuncReceiver("", "*Foo") 29 | _, err := funcReceiver.Generate(0) 30 | assert.Regexp(t, regexp.MustCompile( 31 | `^\`+strings.Split(errmsg.FuncReceiverNameIsEmptyError("").Error(), " ")[0], 32 | ), err.Error()) 33 | } 34 | 35 | func TestShouldGeneratingFuncReceiverRaisesErrorWhenFuncReceiverTypeIsEmpty(t *testing.T) { 36 | funcReceiver := NewFuncReceiver("f", "") 37 | _, err := funcReceiver.Generate(0) 38 | assert.Regexp(t, regexp.MustCompile( 39 | `^\`+strings.Split(errmsg.FuncReceiverTypeIsEmptyError("").Error(), " ")[0], 40 | ), err.Error()) 41 | } 42 | 43 | func TestShouldGeneratingFuncReceiverCodeWithGenericsTypeParamNameSuccessfully(t *testing.T) { 44 | funcReceiver := NewFuncReceiverWithGenerics("f", "*Foo", TypeParameterNames{"T"}) 45 | gen, err := funcReceiver.Generate(0) 46 | assert.NoError(t, err) 47 | assert.Equal(t, "(f *Foo[T])", gen) 48 | } 49 | 50 | func TestShouldGeneratingFuncReceiverCodeWithGenericsTypeParamNamesSuccessfully(t *testing.T) { 51 | funcReceiver := NewFuncReceiverWithGenerics("f", "*Foo", TypeParameterNames{"T", "U"}) 52 | gen, err := funcReceiver.Generate(0) 53 | assert.NoError(t, err) 54 | assert.Equal(t, "(f *Foo[T, U])", gen) 55 | } 56 | -------------------------------------------------------------------------------- /generator/func_signature.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | 7 | "github.com/moznion/gowrtr/internal/errmsg" 8 | ) 9 | 10 | // FuncParameter represents a parameter of the func. 11 | type FuncParameter struct { 12 | name string 13 | typ string 14 | } 15 | 16 | // FuncReturnType represents a return type of the func. 17 | type FuncReturnType struct { 18 | name string 19 | typ string 20 | genericsTypeParameterNames TypeParameterNames 21 | } 22 | 23 | // Generate generates a return type of the func as golang code. 24 | func (frt *FuncReturnType) Generate(indentLevel int) (string, error) { 25 | name := frt.name 26 | typ := frt.typ 27 | 28 | stmt := name 29 | if name != "" && typ != "" { 30 | stmt += " " 31 | } 32 | stmt += typ 33 | 34 | if len(frt.genericsTypeParameterNames) > 0 { 35 | paramNamesStmt, _ := frt.genericsTypeParameterNames.Generate(0) 36 | stmt += paramNamesStmt 37 | } 38 | 39 | return stmt, nil 40 | } 41 | 42 | // FuncSignature represents a code generator for the signature of the func. 43 | type FuncSignature struct { 44 | funcName string 45 | funcParameters []*FuncParameter 46 | returnTypes []*FuncReturnType 47 | paramCallers []string 48 | funcNameCaller string 49 | returnTypesCallers []string 50 | typeParameters TypeParameters 51 | } 52 | 53 | // NewFuncParameter returns a new `FuncSignature`. 54 | func NewFuncParameter(name string, typ string) *FuncParameter { 55 | return &FuncParameter{ 56 | name: name, 57 | typ: typ, 58 | } 59 | } 60 | 61 | // NewFuncReturnType returns a new `FuncReturnType`. 62 | // `name` is an optional parameter. If this parameter is specified, FuncReturnType generates code as named return type. 63 | func NewFuncReturnType(typ string, name ...string) *FuncReturnType { 64 | return NewFuncReturnTypeWithGenerics(typ, []string{}, name...) 65 | } 66 | 67 | // NewFuncReturnTypeWithGenerics ret urns a new `FuncReturnType` with the generics type parameter name, e.g. `T`. 68 | func NewFuncReturnTypeWithGenerics(typ string, genericsTypeParameterNames TypeParameterNames, name ...string) *FuncReturnType { 69 | n := "" 70 | if len(name) > 0 { 71 | n = name[0] 72 | } 73 | 74 | return &FuncReturnType{ 75 | name: n, 76 | typ: typ, 77 | genericsTypeParameterNames: genericsTypeParameterNames, 78 | } 79 | } 80 | 81 | // NewFuncSignature returns a new `FuncSignature`. 82 | func NewFuncSignature(funcName string) *FuncSignature { 83 | return &FuncSignature{ 84 | funcName: funcName, 85 | funcNameCaller: fetchClientCallerLine(), 86 | } 87 | } 88 | 89 | // AddParameters adds parameters of the func to `FuncSignature`. This does *not* set, just add. 90 | // This method returns a *new* `FuncSignature`; it means this method acts as immutable. 91 | func (f *FuncSignature) AddParameters(funcParameters ...*FuncParameter) *FuncSignature { 92 | return &FuncSignature{ 93 | funcName: f.funcName, 94 | funcParameters: append(f.funcParameters, funcParameters...), 95 | returnTypes: f.returnTypes, 96 | paramCallers: append(f.paramCallers, fetchClientCallerLineAsSlice(len(funcParameters))...), 97 | funcNameCaller: f.funcNameCaller, 98 | returnTypesCallers: f.returnTypesCallers, 99 | typeParameters: f.typeParameters, 100 | } 101 | } 102 | 103 | // Parameters sets parameters of the func to `FuncSignature`. This does *not* add, just set. 104 | // This method returns a *new* `FuncSignature`; it means this method acts as immutable. 105 | func (f *FuncSignature) Parameters(funcParameters ...*FuncParameter) *FuncSignature { 106 | return &FuncSignature{ 107 | funcName: f.funcName, 108 | funcParameters: funcParameters, 109 | returnTypes: f.returnTypes, 110 | paramCallers: fetchClientCallerLineAsSlice(len(funcParameters)), 111 | funcNameCaller: f.funcNameCaller, 112 | returnTypesCallers: f.returnTypesCallers, 113 | typeParameters: f.typeParameters, 114 | } 115 | } 116 | 117 | // AddReturnTypes adds return types of the func to `FuncSignature`. This does *not* set, just add. 118 | // 119 | // This method accepts a return type as `string`. If you want to use the parameter as named one, 120 | // please consider using `AddReturnTypeStatements()` instead of this (or use this method with string parameter like: `err error`). 121 | // 122 | // This method returns a *new* `FuncSignature`; it means this method acts as immutable. 123 | func (f *FuncSignature) AddReturnTypes(returnTypes ...string) *FuncSignature { 124 | types := make([]*FuncReturnType, len(returnTypes)) 125 | for i, typ := range returnTypes { 126 | types[i] = NewFuncReturnType(typ) 127 | } 128 | return f.AddReturnTypeStatements(types...) 129 | } 130 | 131 | // AddReturnTypeStatements sets return types of the func to `FuncSignature`. This does *not* add, just set. 132 | // This method returns a *new* `FuncSignature`; it means this method acts as immutable. 133 | func (f *FuncSignature) AddReturnTypeStatements(returnTypes ...*FuncReturnType) *FuncSignature { 134 | return &FuncSignature{ 135 | funcName: f.funcName, 136 | funcParameters: f.funcParameters, 137 | returnTypes: append(f.returnTypes, returnTypes...), 138 | paramCallers: f.paramCallers, 139 | funcNameCaller: f.funcNameCaller, 140 | returnTypesCallers: append(f.returnTypesCallers, fetchClientCallerLineAsSlice(len(returnTypes))...), 141 | typeParameters: f.typeParameters, 142 | } 143 | } 144 | 145 | // ReturnTypes sets return types of the func to `FuncSignature`. This does *not* add, just set. 146 | // 147 | // This method accepts a return type as `string`. If you want to use the parameter as named one, 148 | // please consider using `ReturnTypeStatements()` instead of this (or use this method with string parameter like: `err error`). 149 | // 150 | // This method returns a *new* `FuncSignature`; it means this method acts as immutable. 151 | func (f *FuncSignature) ReturnTypes(returnTypes ...string) *FuncSignature { 152 | types := make([]*FuncReturnType, len(returnTypes)) 153 | for i, typ := range returnTypes { 154 | types[i] = NewFuncReturnType(typ) 155 | } 156 | return f.ReturnTypeStatements(types...) 157 | } 158 | 159 | // ReturnTypeStatements sets return types of the func to `FuncSignature`. This does *not* add, just set. 160 | // This method returns a *new* `FuncSignature`; it means this method acts as immutable. 161 | func (f *FuncSignature) ReturnTypeStatements(returnTypes ...*FuncReturnType) *FuncSignature { 162 | return &FuncSignature{ 163 | funcName: f.funcName, 164 | funcParameters: f.funcParameters, 165 | returnTypes: returnTypes, 166 | paramCallers: f.paramCallers, 167 | funcNameCaller: f.funcNameCaller, 168 | returnTypesCallers: fetchClientCallerLineAsSlice(len(returnTypes)), 169 | typeParameters: f.typeParameters, 170 | } 171 | } 172 | 173 | // TypeParameters sets the TypeParameters onto the caller FuncSignature. 174 | func (f *FuncSignature) TypeParameters(typeParameters TypeParameters) *FuncSignature { 175 | return &FuncSignature{ 176 | funcName: f.funcName, 177 | funcParameters: f.funcParameters, 178 | returnTypes: f.returnTypes, 179 | paramCallers: f.paramCallers, 180 | funcNameCaller: f.funcNameCaller, 181 | returnTypesCallers: f.returnTypesCallers, 182 | typeParameters: typeParameters, 183 | } 184 | } 185 | 186 | // Generate generates a signature of the func as golang code. 187 | func (f *FuncSignature) Generate(indentLevel int) (string, error) { 188 | if f.funcName == "" { 189 | return "", errmsg.FuncNameIsEmptyError(f.funcNameCaller) 190 | } 191 | 192 | stmt := f.funcName 193 | 194 | if f.typeParameters != nil && len(f.typeParameters) > 0 { 195 | typeParametersStmt, err := f.typeParameters.Generate(indentLevel) 196 | if err != nil { 197 | return "", err 198 | } 199 | stmt += typeParametersStmt 200 | } 201 | 202 | var typeBoundaries []int 203 | typeExisted := true 204 | typeMissingCaller := "" 205 | for i, param := range f.funcParameters { 206 | if param.name == "" { 207 | return "", errmsg.FuncParameterNameIsEmptyErr(f.paramCallers[i]) 208 | } 209 | 210 | typeExisted = param.typ != "" 211 | if typeExisted { 212 | typeBoundaries = append(typeBoundaries, i) 213 | } 214 | } 215 | 216 | if !typeExisted { 217 | return "", errmsg.LastFuncParameterTypeIsEmptyErr(typeMissingCaller) 218 | } 219 | 220 | stmt += "(" 221 | 222 | groups := make([]string, len(typeBoundaries)) 223 | prevBoundary := 0 224 | for groupIndex, boundary := range typeBoundaries { 225 | group := f.funcParameters[prevBoundary : boundary+1] 226 | 227 | chunks := make([]string, len(group)) 228 | for i, param := range group { 229 | chunk := param.name 230 | if param.typ != "" { 231 | chunk += " " + param.typ 232 | } 233 | chunks[i] = chunk 234 | } 235 | 236 | groups[groupIndex] = strings.Join(chunks, ", ") 237 | 238 | prevBoundary = boundary + 1 239 | } 240 | 241 | if len(groups) > 1 { 242 | indent := BuildIndent(indentLevel) 243 | nextIndent := BuildIndent(indentLevel + 1) 244 | 245 | stmt += "\n" + indent + nextIndent + strings.Join(groups, ",\n"+nextIndent) + ",\n" + indent 246 | } else if len(groups) == 1 { 247 | stmt += groups[0] 248 | } 249 | 250 | stmt += ")" 251 | 252 | returnTypes := f.returnTypes 253 | switch len(returnTypes) { 254 | case 0: 255 | // NOP 256 | case 1: 257 | retType, _ := returnTypes[0].Generate(0) 258 | openingLit := " " 259 | closingLit := "" 260 | if strings.Contains(retType, " ") { 261 | openingLit = " (" 262 | closingLit = ")" 263 | } 264 | stmt += openingLit + retType + closingLit 265 | default: 266 | namedRetTypeAppeared := false 267 | retTypes := make([]string, len(returnTypes)) 268 | for i, r := range returnTypes { 269 | retType, _ := r.Generate(0) 270 | retTypes[i] = retType 271 | 272 | genericsTrimmedRetType := regexp.MustCompile(`\[[^]]*]`).ReplaceAllString(retType, "") 273 | isNamedRetType := strings.Contains(genericsTrimmedRetType, " ") 274 | if !namedRetTypeAppeared { 275 | namedRetTypeAppeared = isNamedRetType 276 | } 277 | if namedRetTypeAppeared && !isNamedRetType { 278 | return "", errmsg.UnnamedReturnTypeAppearsAfterNamedReturnTypeError(f.returnTypesCallers[i]) 279 | } 280 | } 281 | stmt += " (" + strings.Join(retTypes, ", ") + ")" 282 | } 283 | return stmt, nil 284 | } 285 | -------------------------------------------------------------------------------- /generator/func_signature_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExampleFuncSignature_Generate() { 9 | generator := NewFuncSignature( 10 | "myFunc", 11 | ).TypeParameters(TypeParameters{ 12 | NewTypeParameter("T", "string"), 13 | }).AddParameters( 14 | NewFuncParameter("foo", "T"), 15 | NewFuncParameter("bar", "int"), 16 | ).AddReturnTypeStatements( 17 | NewFuncReturnTypeWithGenerics("MyStruct", TypeParameterNames{"T", "U"}), 18 | NewFuncReturnType("error"), 19 | ) 20 | 21 | generated, err := generator.Generate(0) 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | fmt.Println(generated) 26 | } 27 | -------------------------------------------------------------------------------- /generator/func_signature_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/moznion/gowrtr/internal/errmsg" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestShouldGeneratingFuncSignatureBeSuccessful(t *testing.T) { 14 | dataset := map[string]*FuncSignature{ 15 | "myFunc()": NewFuncSignature( 16 | "myFunc", 17 | ), 18 | 19 | "myFunc(foo string)": NewFuncSignature( 20 | "myFunc", 21 | ).AddParameters(NewFuncParameter("foo", "string")), 22 | 23 | "myFunc(\n\tfoo string,\n\tbar int,\n)": NewFuncSignature( 24 | "myFunc", 25 | ).AddParameters( 26 | NewFuncParameter("foo", "string"), 27 | NewFuncParameter("bar", "int"), 28 | ), 29 | 30 | "myFunc(foo, bar string)": NewFuncSignature( 31 | "myFunc", 32 | ).AddParameters( 33 | NewFuncParameter("foo", ""), 34 | NewFuncParameter("bar", "string"), 35 | ), 36 | 37 | "myFunc(\n\tfoo string,\n\tbar int,\n) string": NewFuncSignature( 38 | "myFunc", 39 | ).AddParameters( 40 | NewFuncParameter("foo", "string"), 41 | NewFuncParameter("bar", "int"), 42 | ).AddReturnTypes("string"), 43 | 44 | "myFunc(\n\tfoo string,\n\tbar int,\n) (string, error)": NewFuncSignature( 45 | "myFunc", 46 | ).AddParameters( 47 | NewFuncParameter("foo", "string"), 48 | NewFuncParameter("bar", "int"), 49 | ).AddReturnTypes("string", "error"), 50 | 51 | "myFunc(\n\tfoo string,\n\tbux, bar int,\n) (string, error)": NewFuncSignature( 52 | "myFunc", 53 | ).AddParameters( 54 | NewFuncParameter("foo", "string"), 55 | NewFuncParameter("bux", ""), 56 | NewFuncParameter("bar", "int"), 57 | ).AddReturnTypes("string", "error"), 58 | 59 | "myFunc(\n\tfoo, bux string,\n\tbar int,\n) (string, error)": NewFuncSignature( 60 | "myFunc", 61 | ).AddParameters( 62 | NewFuncParameter("foo", ""), 63 | NewFuncParameter("bux", "string"), 64 | NewFuncParameter("bar", "int"), 65 | ).AddReturnTypes("string", "error"), 66 | 67 | "myFunc(buz error) int64": NewFuncSignature( 68 | "myFunc", 69 | ).AddParameters( 70 | NewFuncParameter("foo", "string"), 71 | NewFuncParameter("bar", "int"), 72 | ).AddReturnTypes("string", "error"). 73 | Parameters(NewFuncParameter("buz", "error")). 74 | ReturnTypes("int64"), 75 | 76 | "myFunc[T string](\n\tfoo T,\n\tbar int,\n) (T, error)": NewFuncSignature( 77 | "myFunc", 78 | ).TypeParameters(TypeParameters{ 79 | NewTypeParameter("T", "string"), 80 | }).AddParameters( 81 | NewFuncParameter("foo", "T"), 82 | NewFuncParameter("bar", "int"), 83 | ).AddReturnTypes("T", "error"), 84 | 85 | "myFunc[T string, U int64](\n\tfoo T,\n\tbar U,\n) (T, error)": NewFuncSignature( 86 | "myFunc", 87 | ).TypeParameters(TypeParameters{ 88 | NewTypeParameter("T", "string"), 89 | NewTypeParameter("U", "int64"), 90 | }).AddParameters( 91 | NewFuncParameter("foo", "T"), 92 | NewFuncParameter("bar", "U"), 93 | ).AddReturnTypes("T", "error"), 94 | 95 | "myFunc[T string, U int64](\n\tfoo T,\n\tbar U,\n) (MyStruct[T], error)": NewFuncSignature( 96 | "myFunc", 97 | ).TypeParameters(TypeParameters{ 98 | NewTypeParameter("T", "string"), 99 | NewTypeParameter("U", "int64"), 100 | }).AddParameters( 101 | NewFuncParameter("foo", "T"), 102 | NewFuncParameter("bar", "U"), 103 | ).AddReturnTypeStatements( 104 | NewFuncReturnTypeWithGenerics("MyStruct", TypeParameterNames{"T"}), 105 | NewFuncReturnType("error"), 106 | ), 107 | 108 | "myFunc[T string, U int64](\n\tfoo T,\n\tbar U,\n) (MyStruct[T, U], error)": NewFuncSignature( 109 | "myFunc", 110 | ).TypeParameters(TypeParameters{ 111 | NewTypeParameter("T", "string"), 112 | NewTypeParameter("U", "int64"), 113 | }).AddParameters( 114 | NewFuncParameter("foo", "T"), 115 | NewFuncParameter("bar", "U"), 116 | ).AddReturnTypeStatements( 117 | NewFuncReturnTypeWithGenerics("MyStruct", TypeParameterNames{"T", "U"}), 118 | NewFuncReturnType("error"), 119 | ), 120 | } 121 | 122 | for expected, signature := range dataset { 123 | gen, err := signature.Generate(0) 124 | assert.NoError(t, err) 125 | assert.Equal(t, expected, gen) 126 | } 127 | } 128 | 129 | func TestShouldRaiseErrorWhenFuncNameIsEmpty(t *testing.T) { 130 | sig := NewFuncSignature("") 131 | 132 | _, err := sig.Generate(0) 133 | assert.Regexp(t, regexp.MustCompile( 134 | `^\`+strings.Split(errmsg.FuncNameIsEmptyError("").Error(), " ")[0], 135 | ), err.Error()) 136 | } 137 | 138 | func TestShouldRaiseErrorWhenFuncParameterNameIsEmpty(t *testing.T) { 139 | sig := NewFuncSignature("myFunc").AddParameters( 140 | NewFuncParameter("foo", "string"), 141 | NewFuncParameter("", "int"), 142 | NewFuncParameter("buz", "error"), 143 | ) 144 | 145 | _, err := sig.Generate(0) 146 | assert.Regexp(t, regexp.MustCompile( 147 | `^\`+strings.Split(errmsg.FuncParameterNameIsEmptyErr("").Error(), " ")[0], 148 | ), err.Error()) 149 | } 150 | 151 | func TestShouldRaiseErrorWhenLastFuncParameterTypeIsEmpty(t *testing.T) { 152 | sig := NewFuncSignature("myFunc").AddParameters( 153 | NewFuncParameter("foo", "string"), 154 | NewFuncParameter("bar", ""), 155 | NewFuncParameter("buz", ""), 156 | ) 157 | 158 | _, err := sig.Generate(0) 159 | assert.Regexp(t, regexp.MustCompile( 160 | `^\`+strings.Split(errmsg.LastFuncParameterTypeIsEmptyErr("").Error(), " ")[0], 161 | ), err.Error()) 162 | } 163 | 164 | func TestShouldGeneratingFuncSignatureWithNamedReturnValue(t *testing.T) { 165 | { 166 | sig, err := NewFuncSignature("myFunc").ReturnTypes("err error").Generate(0) 167 | assert.NoError(t, err) 168 | assert.Equal(t, "myFunc() (err error)", sig) 169 | } 170 | 171 | { 172 | sig, err := NewFuncSignature("myFunc").ReturnTypes("s string", "err error").Generate(0) 173 | assert.NoError(t, err) 174 | assert.Equal(t, "myFunc() (s string, err error)", sig) 175 | } 176 | } 177 | 178 | func TestShouldGeneratingFuncSignatureWithReturnTypeStructs(t *testing.T) { 179 | { 180 | generator := NewFuncSignature("myFunc") 181 | { 182 | generator = generator.AddReturnTypeStatements(NewFuncReturnType("string")) 183 | sig, err := generator.Generate(0) 184 | assert.NoError(t, err) 185 | assert.Equal(t, "myFunc() string", sig) 186 | } 187 | 188 | { 189 | generator = generator.AddReturnTypeStatements(NewFuncReturnType("error")) 190 | sig, err := generator.Generate(0) 191 | assert.NoError(t, err) 192 | assert.Equal(t, "myFunc() (string, error)", sig) 193 | } 194 | 195 | { 196 | generator = generator.ReturnTypeStatements(NewFuncReturnType("error")) 197 | sig, err := generator.Generate(0) 198 | assert.NoError(t, err) 199 | assert.Equal(t, "myFunc() error", sig) 200 | } 201 | } 202 | 203 | { 204 | generator := NewFuncSignature("myFunc") 205 | { 206 | generator = generator.AddReturnTypeStatements(NewFuncReturnType("string", "foo")) 207 | sig, err := generator.Generate(0) 208 | assert.NoError(t, err) 209 | assert.Equal(t, "myFunc() (foo string)", sig) 210 | } 211 | 212 | { 213 | generator = generator.AddReturnTypeStatements(NewFuncReturnType("error", "bar")) 214 | sig, err := generator.Generate(0) 215 | assert.NoError(t, err) 216 | assert.Equal(t, "myFunc() (foo string, bar error)", sig) 217 | } 218 | 219 | { 220 | generator = generator.ReturnTypeStatements(NewFuncReturnType("error", "foo")) 221 | sig, err := generator.Generate(0) 222 | assert.NoError(t, err) 223 | assert.Equal(t, "myFunc() (foo error)", sig) 224 | } 225 | } 226 | 227 | { 228 | generator := NewFuncSignature("myFunc"). 229 | AddReturnTypeStatements(NewFuncReturnType("", "foo")). 230 | AddReturnTypeStatements(NewFuncReturnType("string", "bar")) 231 | sig, err := generator.Generate(0) 232 | assert.NoError(t, err) 233 | assert.Equal(t, "myFunc() (foo, bar string)", sig) 234 | } 235 | } 236 | 237 | func TestShouldGeneratingFuncSignatureRaisesUnnamedRetTypeIsAfterNamedRetType(t *testing.T) { 238 | generator := NewFuncSignature("myFunc"). 239 | AddReturnTypeStatements(NewFuncReturnType("string", "foo")). 240 | AddReturnTypeStatements(NewFuncReturnType("error", "")) 241 | _, err := generator.Generate(0) 242 | assert.Regexp(t, regexp.MustCompile( 243 | `^\`+strings.Split(errmsg.UnnamedReturnTypeAppearsAfterNamedReturnTypeError("").Error(), " ")[0], 244 | ), err.Error()) 245 | } 246 | 247 | func TestShouldGenerateFuncSignatureRaiseErrorWhenInvalidTypeParameterHasGiven(t *testing.T) { 248 | _, err := NewFuncSignature( 249 | "myFunc", 250 | ).TypeParameters(TypeParameters{ 251 | NewTypeParameter("T", ""), 252 | }).AddParameters( 253 | NewFuncParameter("foo", "T"), 254 | ).AddReturnTypes("T", "error").Generate(0) 255 | 256 | assert.Error(t, err) 257 | assert.Equal(t, errmsg.TypeParameterTypeIsEmptyErrType, errmsg.IdentifyErrs(err)) 258 | } 259 | -------------------------------------------------------------------------------- /generator/func_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/moznion/gowrtr/internal/errmsg" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestShouldGenerateFuncCode(t *testing.T) { 14 | generator := NewFunc( 15 | NewFuncReceiver("m", "*MyStruct"), 16 | NewFuncSignature("myFunc"). 17 | AddParameters( 18 | NewFuncParameter("foo", ""), 19 | NewFuncParameter("bar", "string"), 20 | ). 21 | AddReturnTypes("string", "error"), 22 | ).AddStatements( 23 | NewComment(" do something"), 24 | NewNewline(), 25 | NewReturnStatement("foo+bar", "nil"), 26 | ) 27 | 28 | { 29 | expected := `func (m *MyStruct) myFunc(foo, bar string) (string, error) { 30 | // do something 31 | 32 | return foo+bar, nil 33 | } 34 | ` 35 | gen, err := generator.Generate(0) 36 | assert.NoError(t, err) 37 | assert.Equal(t, expected, gen) 38 | } 39 | 40 | { 41 | expected := ` func (m *MyStruct) myFunc(foo, bar string) (string, error) { 42 | // do something 43 | 44 | return foo+bar, nil 45 | } 46 | ` 47 | gen, err := generator.Generate(2) 48 | assert.NoError(t, err) 49 | assert.Equal(t, expected, gen) 50 | } 51 | 52 | { 53 | generator = generator.Statements(NewComment("modified")) 54 | expected := `func (m *MyStruct) myFunc(foo, bar string) (string, error) { 55 | //modified 56 | } 57 | ` 58 | gen, err := generator.Generate(0) 59 | assert.NoError(t, err) 60 | assert.Equal(t, expected, gen) 61 | } 62 | } 63 | 64 | func TestShouldGenerateFuncCodeGiveUpWhenFuncNameIsEmpty(t *testing.T) { 65 | generator := NewFunc( 66 | nil, 67 | NewFuncSignature(""). 68 | AddParameters( 69 | NewFuncParameter("foo", ""), 70 | NewFuncParameter("bar", "string"), 71 | ), 72 | ) 73 | 74 | _, err := generator.Generate(0) 75 | assert.Regexp(t, regexp.MustCompile( 76 | `^\`+strings.Split(errmsg.FuncNameIsEmptyError("").Error(), " ")[0], 77 | ), err.Error()) 78 | } 79 | 80 | func TestShouldGenerateFuncCodeGiveUpWhenFuncSignatureIsNil(t *testing.T) { 81 | generator := NewFunc( 82 | nil, 83 | nil, 84 | ) 85 | 86 | _, err := generator.Generate(0) 87 | assert.Regexp(t, regexp.MustCompile( 88 | `^\`+strings.Split(errmsg.FuncSignatureIsNilError("").Error(), " ")[0], 89 | ), err.Error()) 90 | } 91 | 92 | func TestShouldGenerateFuncCodeGiveUpWhenFuncReceiverRaisesError(t *testing.T) { 93 | generator := NewFunc( 94 | NewFuncReceiver("", "*Foo"), 95 | NewFuncSignature("myFunc"). 96 | AddParameters( 97 | NewFuncParameter("foo", ""), 98 | NewFuncParameter("bar", "string"), 99 | ), 100 | ) 101 | 102 | _, err := generator.Generate(0) 103 | assert.Regexp(t, regexp.MustCompile( 104 | `^\`+strings.Split(errmsg.FuncReceiverNameIsEmptyError("").Error(), " ")[0], 105 | ), err.Error()) 106 | } 107 | 108 | func TestShouldGenerateFuncCodeGiveUpWhenStatementRaisesError(t *testing.T) { 109 | generator := NewFunc( 110 | nil, 111 | NewFuncSignature("myFunc"). 112 | AddParameters( 113 | NewFuncParameter("foo", ""), 114 | NewFuncParameter("bar", "string"), 115 | ). 116 | AddReturnTypes("string", "error"), 117 | NewFunc(nil, NewFuncSignature("")), 118 | ) 119 | 120 | _, err := generator.Generate(0) 121 | assert.Regexp(t, regexp.MustCompile( 122 | `^\`+strings.Split(errmsg.FuncNameIsEmptyError("").Error(), " ")[0], 123 | ), err.Error()) 124 | } 125 | -------------------------------------------------------------------------------- /generator/if.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/moznion/gowrtr/internal/errmsg" 7 | ) 8 | 9 | // If represents a code generator for `if`, `else-if` and `else` block. 10 | type If struct { 11 | condition string 12 | statements []Statement 13 | elseIfBlocks []*ElseIf 14 | elseBlock *Else 15 | caller string 16 | } 17 | 18 | // NewIf returns a new `If`. 19 | func NewIf(condition string, statements ...Statement) *If { 20 | return &If{ 21 | condition: condition, 22 | statements: statements, 23 | caller: fetchClientCallerLine(), 24 | } 25 | } 26 | 27 | // AddStatements adds statements for `if` block to `If`. This does *not* set, just add. 28 | // This method returns a *new* `If`; it means this method acts as immutable. 29 | func (ig *If) AddStatements(statements ...Statement) *If { 30 | return &If{ 31 | condition: ig.condition, 32 | statements: append(ig.statements, statements...), 33 | elseIfBlocks: ig.elseIfBlocks, 34 | elseBlock: ig.elseBlock, 35 | caller: ig.caller, 36 | } 37 | } 38 | 39 | // Statements sets statements for `if` block to `If`. This does *not* add, just set. 40 | // This method returns a *new* `If`; it means this method acts as immutable. 41 | func (ig *If) Statements(statements ...Statement) *If { 42 | return &If{ 43 | condition: ig.condition, 44 | statements: statements, 45 | elseIfBlocks: ig.elseIfBlocks, 46 | elseBlock: ig.elseBlock, 47 | caller: ig.caller, 48 | } 49 | } 50 | 51 | // AddElseIf adds `else-if` block to `If`. This does *not* set, just add. 52 | // This method returns a *new* `If`; it means this method acts as immutable. 53 | func (ig *If) AddElseIf(blocks ...*ElseIf) *If { 54 | return &If{ 55 | condition: ig.condition, 56 | statements: ig.statements, 57 | elseIfBlocks: append(ig.elseIfBlocks, blocks...), 58 | elseBlock: ig.elseBlock, 59 | caller: ig.caller, 60 | } 61 | } 62 | 63 | // ElseIf sets `else-if` block to `If`. This does *not* add, just set. 64 | // This method returns a *new* `If`; it means this method acts as immutable. 65 | func (ig *If) ElseIf(blocks ...*ElseIf) *If { 66 | return &If{ 67 | condition: ig.condition, 68 | statements: ig.statements, 69 | elseIfBlocks: blocks, 70 | elseBlock: ig.elseBlock, 71 | caller: ig.caller, 72 | } 73 | } 74 | 75 | // Else sets `else` block to `If`. 76 | // This method returns a *new* `If`; it means this method acts as immutable. 77 | func (ig *If) Else(block *Else) *If { 78 | return &If{ 79 | condition: ig.condition, 80 | statements: ig.statements, 81 | elseIfBlocks: ig.elseIfBlocks, 82 | elseBlock: block, 83 | caller: ig.caller, 84 | } 85 | } 86 | 87 | // Generate generates `if` block as golang code. 88 | func (ig *If) Generate(indentLevel int) (string, error) { 89 | indent := BuildIndent(indentLevel) 90 | 91 | if ig.condition == "" { 92 | return "", errmsg.IfConditionIsEmptyError(ig.caller) 93 | } 94 | 95 | stmt := fmt.Sprintf("%sif %s {\n", indent, ig.condition) 96 | 97 | nextIndentLevel := indentLevel + 1 98 | for _, c := range ig.statements { 99 | gen, err := c.Generate(nextIndentLevel) 100 | if err != nil { 101 | return "", err 102 | } 103 | stmt += gen 104 | } 105 | 106 | stmt += fmt.Sprintf("%s}", indent) 107 | 108 | for _, elseIfBlock := range ig.elseIfBlocks { 109 | if elseIfBlock == nil { 110 | continue 111 | } 112 | 113 | elseIfCode, err := elseIfBlock.Generate(indentLevel) 114 | if err != nil { 115 | return "", err 116 | } 117 | stmt += elseIfCode 118 | } 119 | 120 | if elseBlock := ig.elseBlock; elseBlock != nil { 121 | elseCode, err := elseBlock.Generate(indentLevel) 122 | if err != nil { 123 | return "", err 124 | } 125 | stmt += elseCode 126 | } 127 | 128 | stmt += "\n" 129 | 130 | return stmt, nil 131 | } 132 | -------------------------------------------------------------------------------- /generator/if_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExampleIf_Generate() { 9 | generator := NewIf("i == 0", 10 | NewComment(" if"), 11 | ).AddElseIf( 12 | NewElseIf("i < 0", NewComment(" else if 1")), 13 | nil, 14 | NewElseIf("i > 0", NewComment(" else if 2")), 15 | ).Else(NewElse( 16 | NewComment(" else"), 17 | )) 18 | 19 | generated, err := generator.Generate(0) 20 | if err != nil { 21 | log.Fatal(err) 22 | } 23 | fmt.Println(generated) 24 | } 25 | -------------------------------------------------------------------------------- /generator/if_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/moznion/gowrtr/internal/errmsg" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestShouldGenerateIfCode(t *testing.T) { 14 | generator := NewIf("i > 0", 15 | NewComment(" do something"), 16 | NewRawStatement(`fmt.Printf("%d", i)`), 17 | ) 18 | 19 | { 20 | expected := `if i > 0 { 21 | // do something 22 | fmt.Printf("%d", i) 23 | } 24 | ` 25 | gen, err := generator.Generate(0) 26 | assert.NoError(t, err) 27 | assert.Equal(t, expected, gen) 28 | } 29 | 30 | { 31 | expected := ` if i > 0 { 32 | // do something 33 | fmt.Printf("%d", i) 34 | } 35 | ` 36 | gen, err := generator.Generate(2) 37 | assert.NoError(t, err) 38 | assert.Equal(t, expected, gen) 39 | } 40 | 41 | { 42 | generator = generator.Statements(NewComment("modified")) 43 | expected := `if i > 0 { 44 | //modified 45 | } 46 | ` 47 | gen, err := generator.Generate(0) 48 | assert.NoError(t, err) 49 | assert.Equal(t, expected, gen) 50 | } 51 | } 52 | 53 | func TestShouldGenerateIfCodeWithExpandingMethod(t *testing.T) { 54 | generator := NewIf("i > 0"). 55 | AddStatements( 56 | NewComment(" XXX: test test"), 57 | NewComment(" do something"), 58 | ). 59 | AddStatements(NewRawStatement(`fmt.Printf("%d", i)`)) 60 | 61 | expected := `if i > 0 { 62 | // XXX: test test 63 | // do something 64 | fmt.Printf("%d", i) 65 | } 66 | ` 67 | gen, err := generator.Generate(0) 68 | assert.NoError(t, err) 69 | assert.Equal(t, expected, gen) 70 | } 71 | 72 | func TestShouldGenerateIfCodeGiveUpWhenStatementRaisesError(t *testing.T) { 73 | generator := NewIf( 74 | "i > 0", 75 | NewFunc(nil, NewFuncSignature("")), 76 | ) 77 | 78 | _, err := generator.Generate(0) 79 | assert.Regexp(t, regexp.MustCompile( 80 | `^\`+strings.Split(errmsg.FuncNameIsEmptyError("").Error(), " ")[0], 81 | ), err.Error()) 82 | } 83 | 84 | func TestShouldGenerateIfAndElseIfAndElseCode(t *testing.T) { 85 | generator := NewIf("i == 0", 86 | NewComment(" if"), 87 | ).AddElseIf( 88 | NewElseIf("i < 0", NewComment(" else if 1")), 89 | nil, 90 | NewElseIf("i > 0", NewComment(" else if 2")), 91 | ).Else(NewElse( 92 | NewComment(" else"), 93 | )) 94 | 95 | { 96 | gen, err := generator.Generate(0) 97 | assert.NoError(t, err) 98 | expected := `if i == 0 { 99 | // if 100 | } else if i < 0 { 101 | // else if 1 102 | } else if i > 0 { 103 | // else if 2 104 | } else { 105 | // else 106 | } 107 | ` 108 | assert.Equal(t, expected, gen) 109 | } 110 | 111 | { 112 | gen, err := generator.Generate(2) 113 | assert.NoError(t, err) 114 | expected := ` if i == 0 { 115 | // if 116 | } else if i < 0 { 117 | // else if 1 118 | } else if i > 0 { 119 | // else if 2 120 | } else { 121 | // else 122 | } 123 | ` 124 | assert.Equal(t, expected, gen) 125 | } 126 | 127 | { 128 | generator = generator.ElseIf( 129 | NewElseIf("ii == 0").Statements(NewComment(" modified")), 130 | ) 131 | gen, err := generator.Generate(0) 132 | 133 | assert.NoError(t, err) 134 | expected := `if i == 0 { 135 | // if 136 | } else if ii == 0 { 137 | // modified 138 | } else { 139 | // else 140 | } 141 | ` 142 | assert.Equal(t, expected, gen) 143 | } 144 | 145 | } 146 | 147 | func TestShouldGenerateIfRaisesError(t *testing.T) { 148 | _, err := NewIf("").Generate(0) 149 | assert.Regexp(t, regexp.MustCompile( 150 | `^\`+strings.Split(errmsg.IfConditionIsEmptyError("").Error(), " ")[0], 151 | ), err.Error()) 152 | } 153 | 154 | func TestShouldGenerateIfElseIfRaisesError(t *testing.T) { 155 | generator := NewIf("i == 0", 156 | NewComment(" if"), 157 | ).AddElseIf( 158 | NewElseIf("i < 0", NewFuncSignature("")), 159 | ) 160 | 161 | _, err := generator.Generate(0) 162 | assert.Regexp(t, regexp.MustCompile( 163 | `^\`+strings.Split(errmsg.FuncNameIsEmptyError("").Error(), " ")[0], 164 | ), err.Error()) 165 | } 166 | 167 | func TestShouldGenerateIfElseRaisesError(t *testing.T) { 168 | generator := NewIf("i == 0", 169 | NewComment(" if"), 170 | ).Else( 171 | NewElse(NewFuncSignature("")), 172 | ) 173 | 174 | _, err := generator.Generate(0) 175 | assert.Regexp(t, regexp.MustCompile( 176 | `^\`+strings.Split(errmsg.FuncNameIsEmptyError("").Error(), " ")[0], 177 | ), err.Error()) 178 | } 179 | -------------------------------------------------------------------------------- /generator/import.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Import represents a code generator for `import` statement. 8 | type Import struct { 9 | names []string 10 | } 11 | 12 | // NewImport returns a new `Import`. 13 | func NewImport(names ...string) *Import { 14 | return &Import{ 15 | names: names, 16 | } 17 | } 18 | 19 | // AddImports adds import items to `Import`. This does *not* set, just add. 20 | // This method returns a *new* `Import`; it means this method acts as immutable. 21 | func (ig *Import) AddImports(imps ...string) *Import { 22 | return &Import{ 23 | names: append(ig.names, imps...), 24 | } 25 | } 26 | 27 | // Imports sets import items to `Import`. This does *not* add, just set. 28 | // This method returns a *new* `Import`; it means this method acts as immutable. 29 | func (ig *Import) Imports(imps ...string) *Import { 30 | return &Import{ 31 | names: imps, 32 | } 33 | } 34 | 35 | // Generate generates `import` statement as golang code. 36 | func (ig *Import) Generate(indentLevel int) (string, error) { 37 | if len(ig.names) <= 0 { 38 | return "", nil 39 | } 40 | 41 | indent := BuildIndent(indentLevel) 42 | stmt := indent + "import (\n" 43 | for _, name := range ig.names { 44 | if name == "" { 45 | continue 46 | } 47 | stmt += fmt.Sprintf("%s\t\"%s\"\n", indent, name) 48 | } 49 | stmt += indent + ")\n" 50 | 51 | return stmt, nil 52 | } 53 | -------------------------------------------------------------------------------- /generator/import_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExampleImport_Generate() { 9 | generator := NewImport("fmt", "os"). 10 | AddImports("exec", "math") 11 | 12 | generated, err := generator.Generate(0) 13 | if err != nil { 14 | log.Fatal(err) 15 | } 16 | fmt.Println(generated) 17 | } 18 | -------------------------------------------------------------------------------- /generator/import_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestShouldGenerateImportStatementBeSucceeded(t *testing.T) { 10 | importGenerator := NewImport("fmt", "", "math").AddImports("os") 11 | 12 | expected := `import ( 13 | "fmt" 14 | "math" 15 | "os" 16 | ) 17 | ` 18 | 19 | gen, err := importGenerator.Generate(0) 20 | assert.NoError(t, err) 21 | assert.Equal(t, expected, gen) 22 | 23 | importGenerator = importGenerator.Imports("foo", "bar") 24 | expected = `import ( 25 | "foo" 26 | "bar" 27 | ) 28 | ` 29 | gen, err = importGenerator.Generate(0) 30 | assert.NoError(t, err) 31 | assert.Equal(t, expected, gen) 32 | } 33 | 34 | func TestShouldGenerateImportStatementBeSucceededWithSingleImportee(t *testing.T) { 35 | importGenerator := NewImport().AddImports("fmt") 36 | 37 | expected := `import ( 38 | "fmt" 39 | ) 40 | ` 41 | 42 | gen, err := importGenerator.Generate(0) 43 | assert.NoError(t, err) 44 | assert.Equal(t, expected, gen) 45 | } 46 | 47 | func TestShouldGenerateImportStatementBeEmpty(t *testing.T) { 48 | importGenerator := NewImport() 49 | 50 | gen, err := importGenerator.Generate(0) 51 | assert.NoError(t, err) 52 | assert.Equal(t, "", gen) 53 | } 54 | -------------------------------------------------------------------------------- /generator/interface.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/moznion/gowrtr/internal/errmsg" 7 | ) 8 | 9 | // Interface represents a code generator for `interface` block. 10 | type Interface struct { 11 | name string 12 | funcSignatures []*FuncSignature 13 | caller string 14 | typeParameters TypeParameters 15 | } 16 | 17 | // NewInterface returns a new `Interface`. 18 | func NewInterface(name string, funcSignatures ...*FuncSignature) *Interface { 19 | return &Interface{ 20 | name: name, 21 | funcSignatures: funcSignatures, 22 | caller: fetchClientCallerLine(), 23 | } 24 | } 25 | 26 | // AddSignatures adds signatures of the func to `Interface`. This does *not* set, just add. 27 | // This method returns a *new* `Interface`; it means this method acts as immutable. 28 | func (ig *Interface) AddSignatures(sig ...*FuncSignature) *Interface { 29 | return &Interface{ 30 | name: ig.name, 31 | funcSignatures: append(ig.funcSignatures, sig...), 32 | caller: ig.caller, 33 | } 34 | } 35 | 36 | // Signatures sets signatures of the func to `Interface`. This does *not* add, just set. 37 | // This method returns a *new* `Interface`; it means this method acts as immutable. 38 | func (ig *Interface) Signatures(sig ...*FuncSignature) *Interface { 39 | return &Interface{ 40 | name: ig.name, 41 | funcSignatures: sig, 42 | caller: ig.caller, 43 | } 44 | } 45 | 46 | // TypeParameters makes a new Interface value based on the receiver value with the given generics TypeParameters. 47 | func (ig *Interface) TypeParameters(typeParameters TypeParameters) *Interface { 48 | return &Interface{ 49 | name: ig.name, 50 | funcSignatures: ig.funcSignatures, 51 | caller: ig.caller, 52 | typeParameters: typeParameters, 53 | } 54 | } 55 | 56 | // Generate generates `interface` block as golang code. 57 | func (ig *Interface) Generate(indentLevel int) (string, error) { 58 | if ig.name == "" { 59 | return "", errmsg.InterfaceNameIsEmptyError(ig.caller) 60 | } 61 | 62 | indent := BuildIndent(indentLevel) 63 | 64 | typeParamStmt := "" 65 | if len(ig.typeParameters) > 0 { 66 | var err error 67 | typeParamStmt, err = ig.typeParameters.Generate(0) 68 | if err != nil { 69 | return "", err 70 | } 71 | } 72 | 73 | nextIndentLevel := indentLevel + 1 74 | stmt := fmt.Sprintf("%stype %s%s interface {\n", indent, ig.name, typeParamStmt) 75 | for _, sig := range ig.funcSignatures { 76 | signatureStr, err := sig.Generate(nextIndentLevel) 77 | if err != nil { 78 | return "", err 79 | } 80 | stmt += fmt.Sprintf("%s\t%s\n", indent, signatureStr) 81 | } 82 | stmt += fmt.Sprintf("%s}\n", indent) 83 | 84 | return stmt, nil 85 | } 86 | -------------------------------------------------------------------------------- /generator/interface_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExampleInterface_Generate() { 9 | generator := NewInterface( 10 | "MyInterface", 11 | NewFuncSignature("fooFunc"). 12 | AddParameters(NewFuncParameter("foo", "T")). 13 | AddReturnTypes("string", "error"), 14 | ).AddSignatures( 15 | NewFuncSignature("barFunc"), 16 | ).TypeParameters(TypeParameters{ 17 | NewTypeParameter("T", "any"), 18 | }) 19 | 20 | generated, err := generator.Generate(0) 21 | if err != nil { 22 | log.Fatal(err) 23 | } 24 | fmt.Println(generated) 25 | } 26 | -------------------------------------------------------------------------------- /generator/interface_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/moznion/gowrtr/internal/errmsg" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestShouldGeneratingInterfaceCodeBeSuccessful(t *testing.T) { 14 | exp1 := `type myInterface interface { 15 | } 16 | ` 17 | exp2 := `type myInterface interface { 18 | myFunc() 19 | } 20 | ` 21 | exp3 := `type myInterface interface { 22 | myFunc1() 23 | myFunc2(foo string) (string, error) 24 | } 25 | ` 26 | dataset := map[string]*Interface{ 27 | exp1: NewInterface("myInterface"), 28 | exp2: NewInterface("myInterface"). 29 | AddSignatures(NewFuncSignature("myFunc")), 30 | exp3: NewInterface( 31 | "myInterface", 32 | NewFuncSignature("myFunc1"), 33 | ).AddSignatures( 34 | NewFuncSignature("myFunc2"). 35 | AddParameters(NewFuncParameter("foo", "string")). 36 | AddReturnTypes("string", "error"), 37 | ), 38 | } 39 | 40 | for expected, in := range dataset { 41 | got, err := in.Generate(0) 42 | assert.NoError(t, err) 43 | assert.Equal(t, expected, got) 44 | } 45 | } 46 | 47 | func TestShouldGeneratingInterfaceCodeWithGenericsSuccessfully(t *testing.T) { 48 | exp1 := `type myInterface[T string] interface { 49 | myFunc1(foo T) 50 | myFunc2(foo int64) (string, error) 51 | } 52 | ` 53 | exp2 := `type myInterface[T string, U int64] interface { 54 | myFunc1(foo T) 55 | myFunc2(foo U) (string, error) 56 | } 57 | ` 58 | 59 | dataset := map[string]*Interface{ 60 | exp1: NewInterface( 61 | "myInterface", 62 | NewFuncSignature("myFunc1"). 63 | AddParameters(NewFuncParameter("foo", "T")), 64 | ).AddSignatures( 65 | NewFuncSignature("myFunc2"). 66 | AddParameters(NewFuncParameter("foo", "int64")). 67 | AddReturnTypes("string", "error"), 68 | ).TypeParameters(TypeParameters{ 69 | NewTypeParameter("T", "string"), 70 | }), 71 | exp2: NewInterface( 72 | "myInterface", 73 | NewFuncSignature("myFunc1"). 74 | AddParameters(NewFuncParameter("foo", "T")), 75 | ).AddSignatures( 76 | NewFuncSignature("myFunc2"). 77 | AddParameters(NewFuncParameter("foo", "U")). 78 | AddReturnTypes("string", "error"), 79 | ).TypeParameters( 80 | TypeParameters{ 81 | NewTypeParameter("T", "string"), 82 | NewTypeParameter("U", "int64"), 83 | }, 84 | ), 85 | } 86 | 87 | for expected, in := range dataset { 88 | got, err := in.Generate(0) 89 | assert.NoError(t, err) 90 | assert.Equal(t, expected, got) 91 | } 92 | } 93 | 94 | func TestShouldGeneratingInterfaceCodeWithSetter(t *testing.T) { 95 | generator := NewInterface( 96 | "myInterface", 97 | NewFuncSignature("myFunc1"), 98 | ).AddSignatures( 99 | NewFuncSignature("myFunc2"). 100 | AddParameters(NewFuncParameter("foo", "string")). 101 | AddReturnTypes("string", "error"), 102 | ) 103 | 104 | expected := `type myInterface interface { 105 | myFunc1() 106 | myFunc2(foo string) (string, error) 107 | } 108 | ` 109 | got, err := generator.Generate(0) 110 | assert.NoError(t, err) 111 | assert.Equal(t, expected, got) 112 | 113 | generator = generator.Signatures(NewFuncSignature("myFunc3")) 114 | expected = `type myInterface interface { 115 | myFunc3() 116 | } 117 | ` 118 | got, err = generator.Generate(0) 119 | assert.NoError(t, err) 120 | assert.Equal(t, expected, got) 121 | } 122 | 123 | func TestShouldGeneratingInterfaceCodeWithIndentBeSuccessful(t *testing.T) { 124 | exp1 := ` type myInterface interface { 125 | } 126 | ` 127 | exp2 := ` type myInterface interface { 128 | myFunc() 129 | } 130 | ` 131 | exp3 := ` type myInterface interface { 132 | myFunc1() 133 | myFunc2(foo string) (string, error) 134 | } 135 | ` 136 | dataset := map[string]*Interface{ 137 | exp1: NewInterface("myInterface"), 138 | exp2: NewInterface("myInterface"). 139 | AddSignatures(NewFuncSignature("myFunc")), 140 | exp3: NewInterface( 141 | "myInterface", 142 | NewFuncSignature("myFunc1"), 143 | ).AddSignatures( 144 | NewFuncSignature("myFunc2"). 145 | AddParameters(NewFuncParameter("foo", "string")). 146 | AddReturnTypes("string", "error"), 147 | ), 148 | } 149 | 150 | for expected, in := range dataset { 151 | got, err := in.Generate(2) 152 | assert.NoError(t, err) 153 | assert.Equal(t, expected, got) 154 | } 155 | } 156 | 157 | func TestShouldRaiseErrorWhenInterfaceNameIsEmpty(t *testing.T) { 158 | in := NewInterface("") 159 | _, err := in.Generate(0) 160 | assert.Regexp(t, regexp.MustCompile( 161 | `^\`+strings.Split(errmsg.InterfaceNameIsEmptyError("").Error(), " ")[0], 162 | ), err.Error()) 163 | } 164 | 165 | func TestShouldRaiseErrorWhenFuncSignatureRaisesError(t *testing.T) { 166 | in := NewInterface( 167 | "myInterface", 168 | NewFuncSignature(""), 169 | ) 170 | _, err := in.Generate(0) 171 | assert.Regexp(t, regexp.MustCompile( 172 | `^\`+strings.Split(errmsg.FuncNameIsEmptyError("").Error(), " ")[0], 173 | ), err.Error()) 174 | } 175 | 176 | func TestShouldRaiseErrorWhenGenericsTypeParameterIsInvalid(t *testing.T) { 177 | _, err := NewInterface( 178 | "myInterface", 179 | NewFuncSignature("myFunc1"). 180 | AddParameters(NewFuncParameter("foo", "T")), 181 | ).TypeParameters(TypeParameters{ 182 | NewTypeParameter("", "string"), 183 | }).Generate(0) 184 | assert.Regexp(t, regexp.MustCompile( 185 | `^\`+strings.Split(errmsg.TypeParameterParameterIsEmptyErr("").Error(), " ")[0], 186 | ), err.Error()) 187 | } 188 | -------------------------------------------------------------------------------- /generator/newline.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | // Newline represents a code generator for newline character. 4 | type Newline struct { 5 | } 6 | 7 | // NewNewline returns a new `Newline`. 8 | func NewNewline() *Newline { 9 | return &Newline{} 10 | } 11 | 12 | // Generate generates a newline statement as golang code. 13 | func (n *Newline) Generate(indentLevel int) (string, error) { 14 | return "\n", nil 15 | } 16 | -------------------------------------------------------------------------------- /generator/newline_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExampleNewline_Generate() { 9 | generator := NewNewline() 10 | 11 | generated, err := generator.Generate(0) 12 | if err != nil { 13 | log.Fatal(err) 14 | } 15 | fmt.Println(generated) 16 | } 17 | -------------------------------------------------------------------------------- /generator/newline_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestShouldGenerateNewlineSuccessful(t *testing.T) { 10 | generator := NewNewline() 11 | gen, err := generator.Generate(0) 12 | assert.NoError(t, err) 13 | assert.Equal(t, "\n", gen) 14 | } 15 | -------------------------------------------------------------------------------- /generator/package.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import "fmt" 4 | 5 | // Package represents a code generator for `package` statement. 6 | type Package struct { 7 | name string 8 | } 9 | 10 | // NewPackage returns a new `Package`. 11 | func NewPackage(packageName string) *Package { 12 | return &Package{ 13 | name: packageName, 14 | } 15 | } 16 | 17 | // Generate generates a package statement. 18 | func (pg *Package) Generate(indentLevel int) (string, error) { 19 | indent := BuildIndent(indentLevel) 20 | return fmt.Sprintf("%spackage %s\n", indent, pg.name), nil 21 | } 22 | -------------------------------------------------------------------------------- /generator/package_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExamplePackage_Generate() { 9 | generator := NewPackage("mypkg") 10 | 11 | generated, err := generator.Generate(0) 12 | if err != nil { 13 | log.Fatal(err) 14 | } 15 | fmt.Println(generated) 16 | } 17 | -------------------------------------------------------------------------------- /generator/package_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestShouldPackageStringifyBeSucceeded(t *testing.T) { 11 | packageName := "foobar" 12 | packageComponent := NewPackage(packageName) 13 | 14 | gen, err := packageComponent.Generate(0) 15 | assert.NoError(t, err) 16 | assert.Equal(t, fmt.Sprintf("package %s\n", packageName), gen) 17 | } 18 | -------------------------------------------------------------------------------- /generator/raw_statement.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import "fmt" 4 | 5 | // RawStatement represents a code generator for `raw statement`. 6 | // `raw statement` means plain text statement. 7 | type RawStatement struct { 8 | statement string 9 | withNewline bool 10 | } 11 | 12 | // NewRawStatement returns a new `RawStatement`. 13 | func NewRawStatement(stmt string) *RawStatement { 14 | return &RawStatement{ 15 | statement: stmt, 16 | withNewline: true, 17 | } 18 | } 19 | 20 | // NewRawStatementf returns a new `RawStatement` with formatting. 21 | // If `args` is not empty, this method formats `stmt` with `args` by `fmt.Sprintf`. 22 | func NewRawStatementf(stmt string, args ...interface{}) *RawStatement { 23 | return &RawStatement{ 24 | statement: fmt.Sprintf(stmt, args...), 25 | withNewline: true, 26 | } 27 | } 28 | 29 | // WithNewline specifies whether append newline or not. 30 | // Default value is `true`, so this method might be used when you want to suppress to break the line. 31 | func (r *RawStatement) WithNewline(with bool) *RawStatement { 32 | return &RawStatement{ 33 | statement: r.statement, 34 | withNewline: with, 35 | } 36 | } 37 | 38 | // Generate generates a raw statement. 39 | func (r *RawStatement) Generate(indentLevel int) (string, error) { 40 | indent := BuildIndent(indentLevel) 41 | 42 | newline := "" 43 | if r.withNewline { 44 | newline = "\n" 45 | } 46 | 47 | return indent + r.statement + newline, nil 48 | } 49 | -------------------------------------------------------------------------------- /generator/raw_statement_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExampleRawStatement_Generate() { 9 | generator := NewRawStatement("i := 1 + 1") 10 | generated, err := generator.Generate(0) 11 | if err != nil { 12 | log.Fatal(err) 13 | } 14 | fmt.Println(generated) 15 | } 16 | -------------------------------------------------------------------------------- /generator/raw_statement_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestShouldGenerateRawStatementSuccessful(t *testing.T) { 10 | generator := NewRawStatement(`i := 0`) 11 | 12 | gen, err := generator.Generate(0) 13 | assert.NoError(t, err) 14 | assert.Equal(t, "i := 0\n", gen) 15 | 16 | gen, err = generator.Generate(2) 17 | assert.NoError(t, err) 18 | assert.Equal(t, "\t\ti := 0\n", gen) 19 | } 20 | 21 | func TestShouldGenerateRawStatementWithFormattingSuccessful(t *testing.T) { 22 | generator := NewRawStatementf(`s := "%s"`, "test-str") 23 | 24 | gen, err := generator.Generate(0) 25 | assert.NoError(t, err) 26 | assert.Equal(t, "s := \"test-str\"\n", gen) 27 | } 28 | 29 | func TestShouldGenerateRawStatementWithNewlineOption(t *testing.T) { 30 | { 31 | generator := NewRawStatement(`i := 0`).WithNewline(true) 32 | 33 | gen, err := generator.Generate(0) 34 | assert.NoError(t, err) 35 | assert.Equal(t, "i := 0\n", gen) 36 | 37 | gen, err = generator.Generate(2) 38 | assert.NoError(t, err) 39 | assert.Equal(t, "\t\ti := 0\n", gen) 40 | } 41 | 42 | { 43 | generator := NewRawStatement(`i := 0`).WithNewline(false) 44 | 45 | gen, err := generator.Generate(0) 46 | assert.NoError(t, err) 47 | assert.Equal(t, "i := 0", gen) 48 | 49 | gen, err = generator.Generate(2) 50 | assert.NoError(t, err) 51 | assert.Equal(t, "\t\ti := 0", gen) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /generator/return_statement.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import "strings" 4 | 5 | // ReturnStatement represents a code generator for `return` statement. 6 | type ReturnStatement struct { 7 | returnItems []string 8 | } 9 | 10 | // NewReturnStatement returns a new `ReturnStatement`. 11 | func NewReturnStatement(returnItems ...string) *ReturnStatement { 12 | return &ReturnStatement{ 13 | returnItems: returnItems, 14 | } 15 | } 16 | 17 | // AddReturnItems adds return items to `ReturnStatement`. This does *not* set, just add. 18 | // This method returns a *new* `ReturnStatement`; it means this method acts as immutable. 19 | func (r *ReturnStatement) AddReturnItems(returnItems ...string) *ReturnStatement { 20 | return &ReturnStatement{ 21 | returnItems: append(r.returnItems, returnItems...), 22 | } 23 | } 24 | 25 | // ReturnItems sets return items to `ReturnStatement`. This does *not* add, just set. 26 | // This method returns a *new* `ReturnStatement`; it means this method acts as immutable. 27 | func (r *ReturnStatement) ReturnItems(returnItems ...string) *ReturnStatement { 28 | return &ReturnStatement{ 29 | returnItems: returnItems, 30 | } 31 | } 32 | 33 | // Generate generates `return` statement as golang code. 34 | func (r *ReturnStatement) Generate(indentLevel int) (string, error) { 35 | indent := BuildIndent(indentLevel) 36 | 37 | stmt := indent + "return" 38 | if ret := strings.Join(r.returnItems, ", "); ret != "" { 39 | stmt += " " + ret 40 | } 41 | stmt += "\n" 42 | 43 | return stmt, nil 44 | } 45 | -------------------------------------------------------------------------------- /generator/return_statement_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExampleReturnStatement_Generate() { 9 | generator := NewReturnStatement("foo") 10 | generator = generator.AddReturnItems("err") 11 | 12 | generated, err := generator.Generate(0) 13 | if err != nil { 14 | log.Fatal(err) 15 | } 16 | fmt.Println(generated) 17 | } 18 | -------------------------------------------------------------------------------- /generator/return_statement_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestShouldGenerateReturnStatement(t *testing.T) { 10 | generator := NewReturnStatement() 11 | gen, err := generator.Generate(0) 12 | assert.NoError(t, err) 13 | assert.Equal(t, "return\n", gen) 14 | 15 | generator = generator.AddReturnItems("foo", "err") 16 | gen, err = generator.Generate(0) 17 | assert.NoError(t, err) 18 | assert.Equal(t, "return foo, err\n", gen) 19 | 20 | generator = generator.ReturnItems("bar") 21 | gen, err = generator.Generate(0) 22 | assert.NoError(t, err) 23 | assert.Equal(t, "return bar\n", gen) 24 | } 25 | -------------------------------------------------------------------------------- /generator/root.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "os/exec" 7 | "strings" 8 | 9 | "github.com/moznion/gowrtr/internal/errmsg" 10 | ) 11 | 12 | // Root is a code generator for the entry point. 13 | type Root struct { 14 | statements []Statement 15 | gofmt bool 16 | gofmtOptions []string 17 | goimports bool 18 | syntaxChecking bool 19 | } 20 | 21 | // NewRoot generates a new `Root`. 22 | func NewRoot(statements ...Statement) *Root { 23 | return &Root{ 24 | statements: statements, 25 | } 26 | } 27 | 28 | // AddStatements adds statements to Root. This does *not* set, just add. 29 | // This method returns a *new* `Root`; it means this method acts as immutable. 30 | func (g *Root) AddStatements(statements ...Statement) *Root { 31 | return &Root{ 32 | statements: append(g.statements, statements...), 33 | gofmt: g.gofmt, 34 | gofmtOptions: g.gofmtOptions, 35 | goimports: g.goimports, 36 | syntaxChecking: g.syntaxChecking, 37 | } 38 | } 39 | 40 | // Statements sets statements to Root. This does *not* add, just set. 41 | // This method returns a *new* `Root`; it means this method acts as immutable. 42 | func (g *Root) Statements(statements ...Statement) *Root { 43 | return &Root{ 44 | statements: statements, 45 | gofmt: g.gofmt, 46 | gofmtOptions: g.gofmtOptions, 47 | goimports: g.goimports, 48 | syntaxChecking: g.syntaxChecking, 49 | } 50 | } 51 | 52 | // Gofmt enables `gofmt`. If `gofmt` is enabled, it applies `gofmt` on code generation phase. 53 | // This method returns a *new* `Root`; it means this method acts as immutable. 54 | func (g *Root) Gofmt(gofmtOptions ...string) *Root { 55 | return &Root{ 56 | statements: g.statements, 57 | gofmt: true, 58 | gofmtOptions: gofmtOptions, 59 | goimports: g.goimports, 60 | syntaxChecking: g.syntaxChecking, 61 | } 62 | } 63 | 64 | // Goimports enables `goimports`. If `goimports` is enabled, it applies `goimports` on code generation phase. 65 | // This method returns a *new* `Root`; it means this method acts as immutable. 66 | func (g *Root) Goimports() *Root { 67 | return &Root{ 68 | statements: g.statements, 69 | gofmt: g.gofmt, 70 | gofmtOptions: g.gofmtOptions, 71 | goimports: true, 72 | syntaxChecking: g.syntaxChecking, 73 | } 74 | } 75 | 76 | // EnableSyntaxChecking enables syntax checking. If this option is enabled, it checks the syntax of the code on code generation phase. 77 | // This method returns a *new* `Root`; it means this method acts as immutable. 78 | func (g *Root) EnableSyntaxChecking() *Root { 79 | return &Root{ 80 | statements: g.statements, 81 | gofmt: g.gofmt, 82 | gofmtOptions: g.gofmtOptions, 83 | goimports: g.goimports, 84 | syntaxChecking: true, 85 | } 86 | } 87 | 88 | // Generate generates golang code according to registered statements. 89 | func (g *Root) Generate(indentLevel int) (string, error) { 90 | generatedCode := "" 91 | 92 | for _, statement := range g.statements { 93 | gen, err := statement.Generate(indentLevel) 94 | if err != nil { 95 | return "", err 96 | } 97 | generatedCode += gen 98 | } 99 | 100 | if g.syntaxChecking { 101 | _, err := g.applyGofmt(generatedCode, "-e") 102 | if err != nil { 103 | return "", err 104 | } 105 | } 106 | 107 | if g.gofmt { 108 | var err error 109 | generatedCode, err = g.applyGofmt(generatedCode, g.gofmtOptions...) 110 | if err != nil { 111 | return "", err 112 | } 113 | } 114 | 115 | if g.goimports { 116 | var err error 117 | generatedCode, err = g.applyGoimports(generatedCode) 118 | if err != nil { 119 | return "", err 120 | } 121 | } 122 | 123 | return generatedCode, nil 124 | } 125 | 126 | func (g *Root) applyGofmt(generatedCode string, gofmtOptions ...string) (string, error) { 127 | return applyCodeFormatter(generatedCode, "gofmt", gofmtOptions...) 128 | } 129 | 130 | func (g *Root) applyGoimports(generatedCode string) (string, error) { 131 | return applyCodeFormatter(generatedCode, "goimports") 132 | } 133 | 134 | func applyCodeFormatter(generatedCode string, formatterCmdName string, formatterOpts ...string) (string, error) { 135 | formatterCmd := exec.Command(formatterCmdName, formatterOpts...) 136 | stdinPipe, _ := formatterCmd.StdinPipe() 137 | 138 | var out, errout bytes.Buffer 139 | formatterCmd.Stdout = &out 140 | formatterCmd.Stderr = &errout 141 | 142 | err := formatterCmd.Start() 143 | if err != nil { 144 | cmds := []string{formatterCmdName} 145 | return "", errmsg.CodeFormatterError(strings.Join(append(cmds, formatterOpts...), " "), errout.String(), err) 146 | } 147 | 148 | _, err = io.WriteString(stdinPipe, generatedCode) 149 | if err != nil { 150 | return "", err 151 | } 152 | err = stdinPipe.Close() 153 | if err != nil { 154 | return "", err 155 | } 156 | 157 | err = formatterCmd.Wait() 158 | if err != nil { 159 | cmds := []string{formatterCmdName} 160 | return "", errmsg.CodeFormatterError(strings.Join(append(cmds, formatterOpts...), " "), errout.String(), err) 161 | } 162 | 163 | return out.String(), err 164 | } 165 | -------------------------------------------------------------------------------- /generator/root_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExampleRoot_Generate() { 9 | myFuncSignature := NewFuncSignature("MyFunc"). 10 | AddParameters( 11 | NewFuncParameter("foo", "string"), 12 | ). 13 | AddReturnTypes("string", "error") 14 | 15 | generator := NewRoot( 16 | NewComment(" THIS CODE WAS AUTO GENERATED"), 17 | NewNewline(), 18 | NewPackage("mypkg"), 19 | NewInterface("MyInterface"). 20 | AddSignatures(myFuncSignature), 21 | NewNewline(), 22 | NewStruct("MyStruct"). 23 | AddField("Foo", "string"). 24 | AddField("Bar", "int64"), 25 | NewNewline(), 26 | ).AddStatements( 27 | NewFunc( 28 | NewFuncReceiver("m", "*MyStruct"), 29 | NewFuncSignature("MyFunc"). 30 | AddParameters( 31 | NewFuncParameter("foo", "string"), 32 | ). 33 | AddReturnTypes("string", "error"), 34 | ).AddStatements( 35 | NewCodeBlock( 36 | NewRawStatement("str := ").WithNewline(false), 37 | NewAnonymousFunc( 38 | false, 39 | NewAnonymousFuncSignature(). 40 | AddParameters(NewFuncParameter("bar", "string")). 41 | AddReturnTypes("string"), 42 | NewReturnStatement("bar"), 43 | ).Invocation(NewFuncInvocation("foo")), 44 | NewNewline(), 45 | NewIf(`str == ""`). 46 | AddStatements( 47 | NewFor(`i := 0; i < 3; i++`).AddStatements( 48 | NewRawStatement(`fmt.Printf("%d\n", i)`), 49 | ), 50 | ), 51 | NewNewline(), 52 | NewSwitch("str"). 53 | AddCase( 54 | NewCase( 55 | `""`, 56 | NewComment(" empty string"), 57 | ), 58 | NewCase( 59 | `"foo"`, 60 | NewComment(" foo string"), 61 | ), 62 | ). 63 | Default( 64 | NewDefaultCase(NewComment(" default")), 65 | ), 66 | NewNewline(), 67 | NewReturnStatement("str", "nil"), 68 | ), 69 | ), 70 | ) 71 | 72 | generated, err := generator. 73 | Gofmt("-s"). 74 | Goimports(). 75 | Generate(0) 76 | if err != nil { 77 | log.Fatal(err) 78 | } 79 | fmt.Println(generated) 80 | } 81 | -------------------------------------------------------------------------------- /generator/root_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | "strings" 7 | "testing" 8 | 9 | "github.com/moznion/gowrtr/internal/errmsg" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestShouldGenerateCode(t *testing.T) { 14 | expected := `// THIS CODE WAS AUTO GENERATED 15 | 16 | package mypkg 17 | 18 | import ( 19 | "fmt" 20 | ) 21 | 22 | type MyInterface interface { 23 | MyFunc(foo string) (string, error) 24 | } 25 | 26 | type MyStruct struct { 27 | Foo string 28 | Bar int64 29 | } 30 | 31 | func (m *MyStruct) MyFunc(foo string) (string, error) { 32 | { 33 | str := func(bar string) string { 34 | return bar 35 | }(foo) 36 | 37 | if str == "" { 38 | for i := 0; i < 3; i++ { 39 | fmt.Printf("%d\n", i) 40 | } 41 | } 42 | 43 | switch str { 44 | case "": 45 | // empty string 46 | case "foo": 47 | // foo string 48 | default: 49 | // default 50 | } 51 | 52 | return str, nil 53 | } 54 | } 55 | ` 56 | myFuncSignature := NewFuncSignature("MyFunc"). 57 | AddParameters( 58 | NewFuncParameter("foo", "string"), 59 | ). 60 | AddReturnTypes("string", "error") 61 | 62 | generator := NewRoot( 63 | NewComment(" THIS CODE WAS AUTO GENERATED"), 64 | NewNewline(), 65 | NewPackage("mypkg"), 66 | NewNewline(), 67 | NewImport("fmt"), 68 | NewNewline(), 69 | NewInterface("MyInterface"). 70 | AddSignatures(myFuncSignature), 71 | NewNewline(), 72 | NewStruct("MyStruct"). 73 | AddField("Foo", "string"). 74 | AddField("Bar", "int64"), 75 | NewNewline(), 76 | ).AddStatements( 77 | NewFunc( 78 | NewFuncReceiver("m", "*MyStruct"), 79 | NewFuncSignature("MyFunc"). 80 | AddParameters( 81 | NewFuncParameter("foo", "string"), 82 | ). 83 | AddReturnTypes("string", "error"), 84 | ).AddStatements( 85 | NewCodeBlock( 86 | NewRawStatement("str := ").WithNewline(false), 87 | NewAnonymousFunc( 88 | false, 89 | NewAnonymousFuncSignature(). 90 | AddParameters(NewFuncParameter("bar", "string")). 91 | AddReturnTypes("string"), 92 | NewReturnStatement("bar"), 93 | ).Invocation(NewFuncInvocation("foo")), 94 | NewNewline(), 95 | NewIf(`str == ""`). 96 | AddStatements( 97 | NewFor(`i := 0; i < 3; i++`).AddStatements( 98 | NewRawStatement(`fmt.Printf("%d\n", i)`), 99 | ), 100 | ), 101 | NewNewline(), 102 | NewSwitch("str"). 103 | AddCase( 104 | NewCase( 105 | `""`, 106 | NewComment(" empty string"), 107 | ), 108 | NewCase( 109 | `"foo"`, 110 | NewComment(" foo string"), 111 | ), 112 | ). 113 | Default( 114 | NewDefaultCase(NewComment(" default")), 115 | ), 116 | NewNewline(), 117 | NewReturnStatement("str", "nil"), 118 | ), 119 | ), 120 | ) 121 | 122 | generated, err := generator.EnableSyntaxChecking().Generate(0) 123 | assert.NoError(t, err) 124 | assert.Equal(t, expected, generated) 125 | 126 | generated, err = generator.Statements(NewComment("modified")).Generate(0) 127 | assert.NoError(t, err) 128 | assert.Equal(t, "//modified\n", generated) 129 | } 130 | 131 | func TestShouldGenerateCodeWithIndent(t *testing.T) { 132 | expected := ` // THIS CODE WAS AUTO GENERATED 133 | 134 | package mypkg 135 | 136 | import ( 137 | "fmt" 138 | ) 139 | 140 | type MyInterface interface { 141 | MyFunc(foo string) (string, error) 142 | } 143 | 144 | type MyStruct struct { 145 | Foo string 146 | Bar int64 147 | } 148 | 149 | func (m *MyStruct) MyFunc(foo string) (string, error) { 150 | { 151 | str := func(bar string) string { 152 | return bar 153 | }(foo) 154 | 155 | if str == "" { 156 | for i := 0; i < 3; i++ { 157 | fmt.Printf("%d\n", i) 158 | } 159 | } 160 | 161 | switch str { 162 | case "": 163 | // empty string 164 | case "foo": 165 | // foo string 166 | default: 167 | // default 168 | } 169 | 170 | return str, nil 171 | } 172 | } 173 | ` 174 | myFuncSignature := NewFuncSignature("MyFunc"). 175 | AddParameters( 176 | NewFuncParameter("foo", "string"), 177 | ). 178 | AddReturnTypes("string", "error") 179 | 180 | generator := NewRoot( 181 | NewComment(" THIS CODE WAS AUTO GENERATED"), 182 | NewNewline(), 183 | NewPackage("mypkg"), 184 | NewNewline(), 185 | NewImport("fmt"), 186 | NewNewline(), 187 | NewInterface("MyInterface"). 188 | AddSignatures(myFuncSignature), 189 | NewNewline(), 190 | NewStruct("MyStruct"). 191 | AddField("Foo", "string"). 192 | AddField("Bar", "int64"), 193 | NewNewline(), 194 | ).AddStatements( 195 | NewFunc( 196 | NewFuncReceiver("m", "*MyStruct"), 197 | NewFuncSignature("MyFunc"). 198 | AddParameters( 199 | NewFuncParameter("foo", "string"), 200 | ). 201 | AddReturnTypes("string", "error"), 202 | ).AddStatements( 203 | NewCodeBlock( 204 | NewRawStatement("str := ").WithNewline(false), 205 | NewAnonymousFunc( 206 | false, 207 | NewAnonymousFuncSignature(). 208 | AddParameters(NewFuncParameter("bar", "string")). 209 | AddReturnTypes("string"), 210 | NewReturnStatement("bar"), 211 | ).Invocation(NewFuncInvocation("foo")), 212 | NewNewline(), 213 | NewIf(`str == ""`). 214 | AddStatements( 215 | NewFor(`i := 0; i < 3; i++`).AddStatements( 216 | NewRawStatement(`fmt.Printf("%d\n", i)`), 217 | ), 218 | ), 219 | NewNewline(), 220 | NewSwitch("str"). 221 | AddCase( 222 | NewCase( 223 | `""`, 224 | NewComment(" empty string"), 225 | ), 226 | NewCase( 227 | `"foo"`, 228 | NewComment(" foo string"), 229 | ), 230 | ). 231 | Default( 232 | NewDefaultCase(NewComment(" default")), 233 | ), 234 | NewNewline(), 235 | NewReturnStatement("str", "nil"), 236 | ), 237 | ), 238 | ) 239 | 240 | generated, err := generator.Generate(2) 241 | 242 | assert.NoError(t, err) 243 | assert.Equal(t, expected, generated) 244 | } 245 | 246 | func TestShouldGenerateCodeWithGofmt(t *testing.T) { 247 | expected := `// THIS CODE WAS AUTO GENERATED 248 | 249 | package mypkg 250 | 251 | import ( 252 | "fmt" 253 | ) 254 | 255 | type MyInterface interface { 256 | MyFunc(foo string) (string, error) 257 | } 258 | 259 | type MyStruct struct { 260 | Foo string 261 | Bar int64 262 | } 263 | 264 | func (m *MyStruct) MyFunc(foo string) (string, error) { 265 | { 266 | str := func(bar string) string { 267 | return bar 268 | }(foo) 269 | 270 | if str == "" { 271 | for i := 0; i < 3; i++ { 272 | fmt.Printf("%d\n", i) 273 | } 274 | } 275 | 276 | switch str { 277 | case "": 278 | // empty string 279 | case "foo": 280 | // foo string 281 | default: 282 | // default 283 | } 284 | 285 | return str, nil 286 | } 287 | } 288 | ` 289 | myFuncSignature := NewFuncSignature("MyFunc"). 290 | AddParameters( 291 | NewFuncParameter("foo", "string"), 292 | ). 293 | AddReturnTypes("string", "error") 294 | 295 | generator := NewRoot( 296 | NewComment(" THIS CODE WAS AUTO GENERATED"), 297 | NewNewline(), 298 | NewPackage("mypkg"), 299 | NewNewline(), 300 | NewImport("fmt"), 301 | NewNewline(), 302 | NewInterface("MyInterface"). 303 | AddSignatures(myFuncSignature), 304 | NewNewline(), 305 | NewStruct("MyStruct"). 306 | AddField("Foo", "string"). 307 | AddField("Bar", "int64"), 308 | NewNewline(), 309 | NewFunc( 310 | NewFuncReceiver("m", "*MyStruct"), 311 | NewFuncSignature("MyFunc"). 312 | AddParameters( 313 | NewFuncParameter("foo", "string"), 314 | ). 315 | AddReturnTypes("string", "error"), 316 | ).AddStatements( 317 | NewCodeBlock( 318 | NewRawStatement("str := ").WithNewline(false), 319 | NewAnonymousFunc( 320 | false, 321 | NewAnonymousFuncSignature(). 322 | AddParameters(NewFuncParameter("bar", "string")). 323 | AddReturnTypes("string"), 324 | NewReturnStatement("bar"), 325 | ).Invocation(NewFuncInvocation("foo")), 326 | NewNewline(), 327 | NewIf(`str == ""`). 328 | AddStatements( 329 | NewFor(`i := 0; i < 3; i++`).AddStatements( 330 | NewRawStatement(`fmt.Printf("%d\n", i)`), 331 | ), 332 | ), 333 | NewNewline(), 334 | NewSwitch("str"). 335 | AddCase( 336 | NewCase( 337 | `""`, 338 | NewComment(" empty string"), 339 | ), 340 | NewCase( 341 | `"foo"`, 342 | NewComment(" foo string"), 343 | ), 344 | ). 345 | Default( 346 | NewDefaultCase(NewComment(" default")), 347 | ), 348 | NewNewline(), 349 | NewReturnStatement("str", "nil"), 350 | ), 351 | ), 352 | ).Gofmt("-s") 353 | 354 | generated, err := generator.Generate(0) 355 | 356 | assert.NoError(t, err) 357 | assert.Equal(t, expected, generated) 358 | } 359 | 360 | func TestShouldGenerateCodeWithGoimport(t *testing.T) { 361 | expected := `// THIS CODE WAS AUTO GENERATED 362 | 363 | package mypkg 364 | 365 | import "fmt" 366 | 367 | type MyInterface interface { 368 | MyFunc(foo string) (string, error) 369 | } 370 | 371 | type MyStruct struct { 372 | Foo string 373 | Bar int64 374 | } 375 | 376 | func (m *MyStruct) MyFunc(foo string) (string, error) { 377 | { 378 | str := func(bar string) string { 379 | return bar 380 | }(foo) 381 | 382 | if str == "" { 383 | for i := 0; i < 3; i++ { 384 | fmt.Printf("%d\n", i) 385 | } 386 | } 387 | 388 | switch str { 389 | case "": 390 | // empty string 391 | case "foo": 392 | // foo string 393 | default: 394 | // default 395 | } 396 | 397 | return str, nil 398 | } 399 | } 400 | ` 401 | myFuncSignature := NewFuncSignature("MyFunc"). 402 | AddParameters( 403 | NewFuncParameter("foo", "string"), 404 | ). 405 | AddReturnTypes("string", "error") 406 | 407 | generator := NewRoot( 408 | NewComment(" THIS CODE WAS AUTO GENERATED"), 409 | NewNewline(), 410 | NewPackage("mypkg"), 411 | NewInterface("MyInterface"). 412 | AddSignatures(myFuncSignature), 413 | NewNewline(), 414 | NewStruct("MyStruct"). 415 | AddField("Foo", "string"). 416 | AddField("Bar", "int64"), 417 | NewNewline(), 418 | NewFunc( 419 | NewFuncReceiver("m", "*MyStruct"), 420 | NewFuncSignature("MyFunc"). 421 | AddParameters( 422 | NewFuncParameter("foo", "string"), 423 | ). 424 | AddReturnTypes("string", "error"), 425 | ).AddStatements( 426 | NewCodeBlock( 427 | NewRawStatement("str := ").WithNewline(false), 428 | NewAnonymousFunc( 429 | false, 430 | NewAnonymousFuncSignature(). 431 | AddParameters(NewFuncParameter("bar", "string")). 432 | AddReturnTypes("string"), 433 | NewReturnStatement("bar"), 434 | ).Invocation(NewFuncInvocation("foo")), 435 | NewNewline(), 436 | NewIf(`str == ""`). 437 | AddStatements( 438 | NewFor(`i := 0; i < 3; i++`).AddStatements( 439 | NewRawStatement(`fmt.Printf("%d\n", i)`), 440 | ), 441 | ), 442 | NewNewline(), 443 | NewSwitch("str"). 444 | AddCase( 445 | NewCase( 446 | `""`, 447 | NewComment(" empty string"), 448 | ), 449 | NewCase( 450 | `"foo"`, 451 | NewComment(" foo string"), 452 | ), 453 | ). 454 | Default( 455 | NewDefaultCase(NewComment(" default")), 456 | ), 457 | NewNewline(), 458 | NewReturnStatement("str", "nil"), 459 | ), 460 | ), 461 | ).Goimports() 462 | 463 | generated, err := generator.Generate(0) 464 | 465 | assert.NoError(t, err) 466 | assert.Equal(t, expected, generated) 467 | } 468 | 469 | func TestShouldGenerateCodeRaisesError(t *testing.T) { 470 | generator := NewRoot( 471 | NewComment(" THIS CODE WAS AUTO GENERATED"), 472 | NewNewline(), 473 | NewPackage("mypkg"), 474 | NewNewline(), 475 | NewFunc( 476 | nil, 477 | nil, 478 | ), 479 | ) 480 | 481 | _, err := generator.Generate(0) 482 | assert.Regexp(t, regexp.MustCompile( 483 | `^\`+strings.Split(errmsg.FuncSignatureIsNilError("").Error(), " ")[0], 484 | ), err.Error()) 485 | } 486 | 487 | func TestShouldGenerateCodeRaiseErrorWhenCodeFormatterIsExited(t *testing.T) { 488 | { 489 | generator := NewRoot( 490 | NewRawStatement("\timport something"), 491 | ).EnableSyntaxChecking() 492 | 493 | _, err := generator.Generate(0) 494 | assert.Regexp(t, regexp.MustCompile(`^\[GOWRTR-13\] code formatter raises error: command='gofmt -e'.+`), err.Error()) 495 | } 496 | 497 | { 498 | generator := NewRoot( 499 | NewRawStatement("\timport something"), 500 | ).Gofmt() 501 | 502 | _, err := generator.Generate(0) 503 | assert.Regexp(t, regexp.MustCompile(`^\[GOWRTR-13\] code formatter raises error: command='gofmt'.+`), err.Error()) 504 | } 505 | 506 | { 507 | generator := NewRoot( 508 | NewRawStatement("\timport something"), 509 | ).Goimports() 510 | 511 | _, err := generator.Generate(0) 512 | assert.Regexp(t, regexp.MustCompile(`^\[GOWRTR-13\] code formatter raises error: command='goimports'.+`), err.Error()) 513 | } 514 | 515 | { 516 | _, err := applyCodeFormatter("", "not-existed-cmd") 517 | assert.Regexp(t, regexp.MustCompile(`^\[GOWRTR-13\] code formatter raises error: command='not-existed-cmd'.+`), err.Error()) 518 | } 519 | } 520 | 521 | // to check STDIN buffer behavior with code formatter 522 | func TestMassiveAmountCode(t *testing.T) { 523 | generator := NewRoot( 524 | NewComment(" THIS CODE WAS AUTO GENERATED"), 525 | NewNewline(), 526 | NewPackage("mypkg"), 527 | NewNewline(), 528 | NewImport("fmt"), 529 | NewNewline(), 530 | ) 531 | 532 | for i := 0; i <= 1000; i++ { 533 | generator = generator.AddStatements( 534 | NewFunc( 535 | nil, 536 | NewFuncSignature(fmt.Sprintf("f%d", i)), 537 | NewRawStatement( 538 | `fmt.Println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")`, 539 | ).WithNewline(true), 540 | ), 541 | ) 542 | } 543 | 544 | generated, err := generator.Gofmt().Generate(0) 545 | assert.NoError(t, err) 546 | assert.NotEmpty(t, generated) 547 | } 548 | -------------------------------------------------------------------------------- /generator/statement.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | // Statement is an interface that has a responsibility to generate the golang code. 4 | type Statement interface { 5 | Generate(indentLevel int) (string, error) 6 | } 7 | 8 | // BuildIndent returns indent block (i.e. \t characters) according to given level. 9 | func BuildIndent(indentLevel int) string { 10 | indent := "" 11 | for i := 0; i < indentLevel; i++ { 12 | indent += "\t" 13 | } 14 | return indent 15 | } 16 | -------------------------------------------------------------------------------- /generator/struct.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/moznion/gowrtr/internal/errmsg" 7 | ) 8 | 9 | // StructField represents a field of the struct. 10 | type StructField struct { 11 | name string 12 | typ string 13 | tag string 14 | } 15 | 16 | // Struct represents a code generator for `struct` notation. 17 | type Struct struct { 18 | name string 19 | fields []*StructField 20 | nameCaller string 21 | fieldsCallers []string 22 | typeParameters TypeParameters 23 | } 24 | 25 | // NewStruct returns a new `Struct`. 26 | func NewStruct(name string) *Struct { 27 | return &Struct{ 28 | name: name, 29 | nameCaller: fetchClientCallerLine(), 30 | } 31 | } 32 | 33 | // AddField adds a struct field to `Struct`. 34 | // This method returns a *new* `Struct`; it means this method acts as immutable. 35 | func (sg *Struct) AddField(name string, typ string, tag ...string) *Struct { 36 | l := len(tag) 37 | t := "" 38 | if l > 0 { 39 | t = tag[0] 40 | } 41 | 42 | return &Struct{ 43 | name: sg.name, 44 | fields: append(sg.fields, &StructField{ 45 | name: name, 46 | typ: typ, 47 | tag: t, 48 | }), 49 | nameCaller: sg.nameCaller, 50 | fieldsCallers: append(sg.fieldsCallers, fetchClientCallerLine()), 51 | typeParameters: sg.typeParameters, 52 | } 53 | } 54 | 55 | // TypeParameters sets the TypeParameters onto the current caller Struct. 56 | func (sg *Struct) TypeParameters(typeParameters TypeParameters) *Struct { 57 | return &Struct{ 58 | name: sg.name, 59 | fields: sg.fields, 60 | nameCaller: sg.nameCaller, 61 | fieldsCallers: sg.fieldsCallers, 62 | typeParameters: typeParameters, 63 | } 64 | } 65 | 66 | // Generate generates `struct` block as golang code. 67 | func (sg *Struct) Generate(indentLevel int) (string, error) { 68 | if sg.name == "" { 69 | return "", errmsg.StructNameIsNilErr(sg.nameCaller) 70 | } 71 | 72 | indent := BuildIndent(indentLevel) 73 | 74 | typeParametersStmt := "" 75 | if sg.typeParameters != nil && len(sg.typeParameters) > 0 { 76 | var err error 77 | typeParametersStmt, err = sg.typeParameters.Generate(indentLevel) 78 | if err != nil { 79 | return "", err 80 | } 81 | } 82 | 83 | stmt := fmt.Sprintf("%stype %s%s struct {\n", indent, sg.name, typeParametersStmt) 84 | 85 | for i, field := range sg.fields { 86 | if field.name == "" { 87 | return "", errmsg.StructFieldNameIsEmptyErr(sg.fieldsCallers[i]) 88 | } 89 | if field.typ == "" { 90 | return "", errmsg.StructFieldTypeIsEmptyErr(sg.fieldsCallers[i]) 91 | } 92 | 93 | stmt += fmt.Sprintf("%s\t%s %s", indent, field.name, field.typ) 94 | if tag := field.tag; tag != "" { 95 | stmt += fmt.Sprintf(" `%s`", tag) 96 | } 97 | stmt += "\n" 98 | } 99 | stmt += fmt.Sprintf("%s}\n", indent) 100 | 101 | return stmt, nil 102 | } 103 | -------------------------------------------------------------------------------- /generator/struct_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExampleStruct_Generate() { 9 | generator := NewStruct("MyStruct") 10 | generator = generator. 11 | TypeParameters(TypeParameters{NewTypeParameter("T", "string")}). 12 | AddField("foo", "T"). 13 | AddField("bar", "int64", `custom:"tag"`) 14 | 15 | generated, err := generator.Generate(0) 16 | if err != nil { 17 | log.Fatal(err) 18 | } 19 | fmt.Println(generated) 20 | } 21 | -------------------------------------------------------------------------------- /generator/struct_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/moznion/gowrtr/internal/errmsg" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestShouldGenerateStructStatementBeSucceeded(t *testing.T) { 14 | structName := "TestStruct" 15 | 16 | structGenerator := NewStruct(structName). 17 | AddField("Foo", "string"). 18 | AddField("Bar", "int64", `json:"bar"`). 19 | AddField("buz", "[]byte") 20 | 21 | { 22 | gen, err := structGenerator.Generate(0) 23 | expected := "type TestStruct struct {\n" + 24 | " Foo string\n" + 25 | " Bar int64 `json:\"bar\"`\n" + 26 | " buz []byte\n" + 27 | "}\n" 28 | assert.NoError(t, err) 29 | assert.Equal(t, expected, gen) 30 | } 31 | 32 | { 33 | gen, err := structGenerator.Generate(2) 34 | expected := "\t\ttype TestStruct struct {\n" + 35 | "\t\t\tFoo string\n" + 36 | "\t\t\tBar int64 `json:\"bar\"`\n" + 37 | "\t\t\tbuz []byte\n" + 38 | "\t\t}\n" 39 | assert.NoError(t, err) 40 | assert.Equal(t, expected, gen) 41 | } 42 | } 43 | 44 | func TestShouldGenerateStructStatementWithTypeParameterSuccessfully(t *testing.T) { 45 | generator := NewStruct("TestStruct") 46 | generator = generator. 47 | TypeParameters(TypeParameters{NewTypeParameter("T", "string")}). 48 | AddField("Foo", "T"). 49 | AddField("Bar", "int64", `json:"bar"`) 50 | 51 | gen, err := generator.Generate(0) 52 | expected := "type TestStruct[T string] struct {\n" + 53 | " Foo T\n" + 54 | " Bar int64 `json:\"bar\"`\n" + 55 | "}\n" 56 | assert.NoError(t, err) 57 | assert.Equal(t, expected, gen) 58 | } 59 | 60 | func TestShouldGenerateStructStatementWithTypeParametersSuccessfully(t *testing.T) { 61 | generator := NewStruct("TestStruct") 62 | generator = generator. 63 | TypeParameters(TypeParameters{ 64 | NewTypeParameter("T", "string"), 65 | NewTypeParameter("U", "int64"), 66 | }). 67 | AddField("Foo", "T"). 68 | AddField("Bar", "U", `json:"bar"`) 69 | 70 | gen, err := generator.Generate(0) 71 | expected := "type TestStruct[T string, U int64] struct {\n" + 72 | " Foo T\n" + 73 | " Bar U `json:\"bar\"`\n" + 74 | "}\n" 75 | assert.NoError(t, err) 76 | assert.Equal(t, expected, gen) 77 | } 78 | 79 | func TestShouldRaiseErrorWhenStructNameIsEmpty(t *testing.T) { 80 | structGenerator := NewStruct("") 81 | 82 | _, err := structGenerator.Generate(0) 83 | assert.Regexp(t, regexp.MustCompile( 84 | `^\`+strings.Split(errmsg.StructNameIsNilErr("").Error(), " ")[0], 85 | ), err.Error()) 86 | } 87 | 88 | func TestShouldRaiseErrorWhenFieldNameIsEmpty(t *testing.T) { 89 | structGenerator := NewStruct("TestStruct").AddField("", "string") 90 | _, err := structGenerator.Generate(0) 91 | assert.Regexp(t, regexp.MustCompile( 92 | `^\`+strings.Split(errmsg.StructFieldNameIsEmptyErr("").Error(), " ")[0], 93 | ), err.Error()) 94 | } 95 | 96 | func TestShouldRaiseErrorWhenFieldTypeIsEmpty(t *testing.T) { 97 | structGenerator := NewStruct("TestStruct").AddField("Foo", "") 98 | _, err := structGenerator.Generate(0) 99 | assert.Regexp(t, regexp.MustCompile( 100 | `^\`+strings.Split(errmsg.StructFieldTypeIsEmptyErr("").Error(), " ")[0], 101 | ), err.Error()) 102 | } 103 | 104 | func TestShouldRaiseErrorWhenInvalidTypeParameterHasGiven(t *testing.T) { 105 | generator := NewStruct("TestStruct") 106 | generator = generator. 107 | TypeParameters(TypeParameters{NewTypeParameter("", "string")}). 108 | AddField("Foo", "T") 109 | _, err := generator.Generate(0) 110 | assert.Error(t, err) 111 | assert.Equal(t, errmsg.TypeParameterParameterIsEmptyErrType, errmsg.IdentifyErrs(err)) 112 | } 113 | -------------------------------------------------------------------------------- /generator/switch.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import "fmt" 4 | 5 | // Switch represents a code generator for `switch` statement. 6 | // See also: https://tour.golang.org/flowcontrol/9 7 | type Switch struct { 8 | condition string 9 | caseStatements []*Case 10 | defaultStatement *DefaultCase 11 | } 12 | 13 | // NewSwitch returns a new `Switch`. 14 | func NewSwitch(condition string) *Switch { 15 | return &Switch{ 16 | condition: condition, 17 | } 18 | } 19 | 20 | // AddCase adds `case` statements to `Switch`. This does *not* set, just add. 21 | // This method returns a *new* `Switch`; it means this method acts as immutable. 22 | func (s *Switch) AddCase(statements ...*Case) *Switch { 23 | return &Switch{ 24 | condition: s.condition, 25 | caseStatements: append(s.caseStatements, statements...), 26 | defaultStatement: s.defaultStatement, 27 | } 28 | } 29 | 30 | // Case sets `case` statements to `Switch`. This does *not* add, just set. 31 | // This method returns a *new* `Switch`; it means this method acts as immutable. 32 | func (s *Switch) Case(statements ...*Case) *Switch { 33 | return &Switch{ 34 | condition: s.condition, 35 | caseStatements: statements, 36 | defaultStatement: s.defaultStatement, 37 | } 38 | } 39 | 40 | // Default sets a `default` statement to `Switch`. 41 | // This method returns a *new* `Switch`; it means this method acts as immutable. 42 | func (s *Switch) Default(statement *DefaultCase) *Switch { 43 | return &Switch{ 44 | condition: s.condition, 45 | caseStatements: s.caseStatements, 46 | defaultStatement: statement, 47 | } 48 | } 49 | 50 | // Generate generates `switch` statement as golang code. 51 | func (s *Switch) Generate(indentLevel int) (string, error) { 52 | indent := BuildIndent(indentLevel) 53 | 54 | stmt := fmt.Sprintf("%sswitch %s {\n", indent, s.condition) 55 | for _, statement := range s.caseStatements { 56 | if statement == nil { 57 | continue 58 | } 59 | gen, err := statement.Generate(indentLevel) 60 | if err != nil { 61 | return "", err 62 | } 63 | stmt += gen 64 | } 65 | 66 | if defaultStatement := s.defaultStatement; defaultStatement != nil { 67 | gen, err := defaultStatement.Generate(indentLevel) 68 | if err != nil { 69 | return "", err 70 | } 71 | stmt += gen 72 | } 73 | 74 | stmt += fmt.Sprintf("%s}\n", indent) 75 | 76 | return stmt, nil 77 | } 78 | -------------------------------------------------------------------------------- /generator/switch_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExampleSwitch_Generate() { 9 | generator := NewSwitch("str") 10 | generator = generator.AddCase( 11 | NewCase(`"foo"`, NewRawStatement(`fmt.Printf("str is foo\n")`)), 12 | NewCase(`"bar"`, NewRawStatement(`fmt.Printf("str is bar\n")`)), 13 | ) 14 | generator = generator.Default( 15 | NewDefaultCase(NewRawStatement(`fmt.Printf("here is default\n")`)), 16 | ) 17 | 18 | generated, err := generator.Generate(0) 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | fmt.Println(generated) 23 | } 24 | -------------------------------------------------------------------------------- /generator/switch_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/moznion/gowrtr/internal/errmsg" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestShouldGenerateSwitch(t *testing.T) { 13 | generator := NewSwitch("foo") 14 | 15 | { 16 | gen, err := generator.Generate(0) 17 | assert.NoError(t, err) 18 | expected := `switch foo { 19 | } 20 | ` 21 | assert.Equal(t, expected, gen) 22 | } 23 | 24 | generator = generator.AddCase( 25 | NewCase("1", NewComment(" one")), 26 | nil, 27 | NewCase("2", NewComment(" two")), 28 | ).Default( 29 | NewDefaultCase(NewComment(" default")), 30 | ) 31 | 32 | { 33 | gen, err := generator.Generate(0) 34 | assert.NoError(t, err) 35 | expected := `switch foo { 36 | case 1: 37 | // one 38 | case 2: 39 | // two 40 | default: 41 | // default 42 | } 43 | ` 44 | assert.Equal(t, expected, gen) 45 | } 46 | 47 | { 48 | gen, err := generator.Generate(2) 49 | assert.NoError(t, err) 50 | expected := ` switch foo { 51 | case 1: 52 | // one 53 | case 2: 54 | // two 55 | default: 56 | // default 57 | } 58 | ` 59 | assert.Equal(t, expected, gen) 60 | } 61 | 62 | { 63 | generator = generator.Case(NewCase("123", NewComment("modified"))) 64 | gen, err := generator.Generate(0) 65 | assert.NoError(t, err) 66 | expected := `switch foo { 67 | case 123: 68 | //modified 69 | default: 70 | // default 71 | } 72 | ` 73 | assert.Equal(t, expected, gen) 74 | } 75 | } 76 | 77 | func TestShouldGenerateSwitchRaisesErrorWhenCaseRaisesError(t *testing.T) { 78 | generator := NewSwitch("foo").AddCase( 79 | NewCase(""), 80 | ) 81 | _, err := generator.Generate(0) 82 | assert.Regexp(t, regexp.MustCompile( 83 | `^\`+strings.Split(errmsg.CaseConditionIsEmptyError("").Error(), " ")[0], 84 | ), err.Error()) 85 | } 86 | 87 | func TestShouldGenerateSwitchRaisesErrorWhenDefaultRaisesError(t *testing.T) { 88 | generator := NewSwitch("foo").Default( 89 | NewDefaultCase( 90 | NewFunc(nil, NewFuncSignature("")), 91 | ), 92 | ) 93 | _, err := generator.Generate(0) 94 | assert.Regexp(t, regexp.MustCompile( 95 | `^\`+strings.Split(errmsg.FuncNameIsEmptyError("").Error(), " ")[0], 96 | ), err.Error()) 97 | } 98 | -------------------------------------------------------------------------------- /generator/type_parameters.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/moznion/gowrtr/internal/errmsg" 7 | ) 8 | 9 | // TypeParameter is a "generic" type parameter. e,g. `T string`; T is a parameter and int is type. 10 | type TypeParameter struct { 11 | parameter string 12 | types []string 13 | caller string 14 | } 15 | 16 | // NewTypeParameter returns a new TypeParameter. 17 | func NewTypeParameter(parameter string, typ string) *TypeParameter { 18 | return NewTypeParameters(parameter, []string{typ}) 19 | } 20 | 21 | // NewTypeParameters returns a new TypeParameter. If the `types` argument has the multiple types, those types become a union type. 22 | func NewTypeParameters(parameter string, types []string) *TypeParameter { 23 | return &TypeParameter{ 24 | parameter: parameter, 25 | types: types, 26 | caller: fetchClientCallerLine(), 27 | } 28 | } 29 | 30 | // TypeParameters is an array of TypeParameter pointers for go generics. 31 | type TypeParameters []*TypeParameter 32 | 33 | // Generate generates the type-parameters as golang code. 34 | func (tps TypeParameters) Generate(indentLevel int) (string, error) { 35 | typeParameterStmts := make([]string, len(tps)) 36 | for i, tp := range tps { 37 | if tp.parameter == "" { 38 | return "", errmsg.TypeParameterParameterIsEmptyErr(tp.caller) 39 | } 40 | 41 | if len(tp.types) <= 0 { 42 | return "", errmsg.TypeParameterTypeIsEmptyErr(tp.caller) 43 | } 44 | 45 | isBlank := true 46 | for _, typ := range tp.types { 47 | if typ != "" { 48 | isBlank = false 49 | break 50 | } 51 | } 52 | if isBlank { 53 | return "", errmsg.TypeParameterTypeIsEmptyErr(tp.caller) 54 | } 55 | 56 | typeParameterStmts[i] = tp.parameter + " " + strings.Join(tp.types, " | ") 57 | } 58 | 59 | return "[" + strings.Join(typeParameterStmts, ", ") + "]", nil 60 | } 61 | 62 | // TypeArguments is an array of type argument for go generics. 63 | type TypeArguments []string 64 | 65 | // Generate generates the type-arguments as golang code. 66 | func (tas TypeArguments) Generate(indentLevel int) (string, error) { 67 | return "[" + strings.Join(tas, ", ") + "]", nil 68 | } 69 | 70 | // TypeParameterNames is an array of type parameter names (e.g. `T`) for go generics. 71 | type TypeParameterNames []string 72 | 73 | // Generate generates the type parameter names as golang code. 74 | func (tpn TypeParameterNames) Generate(indentLevel int) (string, error) { 75 | return "[" + strings.Join(tpn, ", ") + "]", nil 76 | } 77 | -------------------------------------------------------------------------------- /generator/type_parameters_doc_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | func ExampleTypeArguments_Generate() { 9 | generated, err := TypeArguments{"int", "string"}.Generate(0) 10 | if err != nil { 11 | log.Fatal(err) 12 | } 13 | fmt.Println("// example: f(T, U)") 14 | fmt.Printf("f%s(intVar, strVar)\n", generated) 15 | } 16 | -------------------------------------------------------------------------------- /generator/type_parameters_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/moznion/gowrtr/internal/errmsg" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestShouldGenerateTypeParametersSuccessfully(t *testing.T) { 12 | stmt, err := TypeParameters{NewTypeParameter("T", "string")}.Generate(0) 13 | assert.NoError(t, err) 14 | assert.Equal(t, "[T string]", stmt) 15 | 16 | stmt, err = TypeParameters{NewTypeParameter("T", "string"), NewTypeParameter("U", "int")}.Generate(0) 17 | assert.NoError(t, err) 18 | assert.Equal(t, "[T string, U int]", stmt) 19 | } 20 | 21 | func TestShouldFailToGenerateTypeParametersWhenParameterIsEmpty(t *testing.T) { 22 | _, err := TypeParameters{NewTypeParameter("", "string")}.Generate(0) 23 | assert.Error(t, err) 24 | assert.Equal(t, errmsg.TypeParameterParameterIsEmptyErrType, errmsg.IdentifyErrs(err)) 25 | } 26 | 27 | func TestShouldGenerateUnionTypeParameterSuccessfully(t *testing.T) { 28 | stmt, err := TypeParameters{NewTypeParameters("T", []string{"int", "uint"})}.Generate(0) 29 | assert.NoError(t, err) 30 | assert.Equal(t, "[T int | uint]", stmt) 31 | } 32 | 33 | func TestShouldFailToGenerateTypeParametersWhenTypeIsEmpty(t *testing.T) { 34 | _, err := TypeParameters{NewTypeParameter("T", "")}.Generate(0) 35 | assert.Error(t, err) 36 | assert.Equal(t, errmsg.TypeParameterTypeIsEmptyErrType, errmsg.IdentifyErrs(err)) 37 | } 38 | 39 | func TestShouldGenerateUnionTypeParametersWhenTypesSliceIsEmpty(t *testing.T) { 40 | _, err := TypeParameters{NewTypeParameters("T", []string{})}.Generate(0) 41 | assert.Error(t, err) 42 | assert.Equal(t, errmsg.TypeParameterTypeIsEmptyErrType, errmsg.IdentifyErrs(err)) 43 | } 44 | 45 | func TestShouldGenerateTypeArgumentsSuccessfully(t *testing.T) { 46 | stmt, err := TypeArguments{"string"}.Generate(0) 47 | assert.NoError(t, err) 48 | assert.Equal(t, "[string]", stmt) 49 | 50 | stmt, err = TypeArguments{"string", "int"}.Generate(0) 51 | assert.NoError(t, err) 52 | assert.Equal(t, "[string, int]", stmt) 53 | } 54 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/moznion/gowrtr 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/moznion/go-errgen v1.8.1 7 | github.com/pkg/errors v0.9.1 8 | github.com/stretchr/testify v1.6.0 9 | ) 10 | 11 | require ( 12 | github.com/davecgh/go-spew v1.1.0 // indirect 13 | github.com/iancoleman/strcase v0.1.3 // indirect 14 | github.com/pmezard/go-difflib v1.0.0 // indirect 15 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/iancoleman/strcase v0.0.0-20180726023541-3605ed457bf7/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= 4 | github.com/iancoleman/strcase v0.1.3 h1:dJBk1m2/qjL1twPLf68JND55vvivMupZ4wIzE8CTdBw= 5 | github.com/iancoleman/strcase v0.1.3/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= 6 | github.com/moznion/go-errgen v1.3.1/go.mod h1:+BkSRLNc98WWAJmdtxYcxxucQ7sl8m4sroepmHio33Y= 7 | github.com/moznion/go-errgen v1.8.1 h1:jxaMIi1WiX/N9xnOecBN3ZQUhbBgnrvedSjNmU7EXFo= 8 | github.com/moznion/go-errgen v1.8.1/go.mod h1:3mE7v+d7czyIqbfd3jOK/wLgRn5lTOtNRNEeCHXHaCk= 9 | github.com/moznion/gowrtr v0.0.0-20190121085203-08e30cf60446/go.mod h1:u2ZFWAHT1c56nmSP3w0C4N9MZtI2UE7YwbkItJAsF78= 10 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 11 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 12 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 13 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 14 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 15 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 16 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 17 | github.com/stretchr/testify v1.6.0 h1:jlIyCplCJFULU/01vCkhKuTyc3OorI3bJFuw6obfgho= 18 | github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 19 | golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 20 | golang.org/x/tools v0.0.0-20190111214448-fc1d57b08d7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 21 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 22 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 23 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 24 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 25 | -------------------------------------------------------------------------------- /internal/errmsg/errmsg.go: -------------------------------------------------------------------------------- 1 | package errmsg 2 | 3 | //go:generate errgen -type=errs -prefix=GOWRTR- 4 | type Errs struct { 5 | StructNameIsNilErr error `errmsg:"struct name must not be empty, but it gets empty (caused at %s)" vars:"caller string"` 6 | StructFieldNameIsEmptyErr error `errmsg:"field name must not be empty, but it gets empty (caused at %s)" vars:"caller string"` 7 | StructFieldTypeIsEmptyErr error `errmsg:"field type must not be empty, but it gets empty (caused at %s)" vars:"caller string"` 8 | FuncParameterNameIsEmptyErr error `errmsg:"func parameter name must not be empty, but it gets empty (caused at %s)" vars:"caller string"` 9 | LastFuncParameterTypeIsEmptyErr error `errmsg:"the last func parameter type must not be empty, but it gets empty (caused at %s)" vars:"caller string"` 10 | FuncNameIsEmptyError error `errmsg:"name of func must not be empty, but it gets empty (caused at %s)" vars:"caller string"` 11 | InterfaceNameIsEmptyError error `errmsg:"name of interface must not be empty, but it gets empty (caused at %s)" vars:"caller string"` 12 | FuncReceiverNameIsEmptyError error `errmsg:"name of func receiver must not be empty, but it gets empty (caused at %s)" vars:"caller string"` 13 | FuncReceiverTypeIsEmptyError error `errmsg:"type of func receiver must not be empty, but it gets empty (caused at %s)" vars:"caller string"` 14 | FuncSignatureIsNilError error `errmsg:"func signature must not be nil, bit it gets nil (caused at %s)" vars:"caller string"` 15 | AnonymousFuncSignatureIsNilError error `errmsg:"anonymous func signature must not be nil, bit it gets nil (caused at %s)" vars:"caller string"` 16 | FuncInvocationParameterIsEmptyError error `errmsg:"a parameter of function invocation must not be nil, but it gets nil (caused at %s)" vars:"caller string"` 17 | CodeFormatterError error `errmsg:"code formatter raises error: command='%s', err='%s', msg='%s'" vars:"cmd string, msg string, fmterr error"` 18 | CaseConditionIsEmptyError error `errmsg:"condition of case must not be empty, but it gets empty (caused at %s)" vars:"caller string"` 19 | IfConditionIsEmptyError error `errmsg:"condition of if must not be empty, but it gets empty (caused at %s)" vars:"caller string"` 20 | UnnamedReturnTypeAppearsAfterNamedReturnTypeError error `errmsg:"unnamed return type appears after named return type (caused at %s)" vars:"caller string"` 21 | ValueOfCompositeLiteralIsEmptyError error `errmsg:"a value of composite literal must not be empty, but it gets empty (caused at %s)" vars:"caller string"` 22 | TypeParameterParameterIsEmptyErr error `errmsg:"type-parameter's parameter must not be empty, but it gets empty (caused at %s)" vars:"caller string"` 23 | TypeParameterTypeIsEmptyErr error `errmsg:"type-parameter's type must not be empty, but it gets empty (caused at %s)" vars:"caller string"` 24 | } 25 | -------------------------------------------------------------------------------- /internal/errmsg/errs_errmsg_gen.go: -------------------------------------------------------------------------------- 1 | // This package was auto generated. 2 | // DO NOT EDIT BY YOUR HAND! 3 | 4 | package errmsg 5 | 6 | import ( 7 | "fmt" 8 | "strings" 9 | 10 | "github.com/pkg/errors" 11 | ) 12 | 13 | // StructNameIsNilErr returns the error. 14 | func StructNameIsNilErr(caller string) error { 15 | return fmt.Errorf(`[GOWRTR-1] struct name must not be empty, but it gets empty (caused at %s)`, caller) 16 | } 17 | 18 | // StructNameIsNilErrWrap wraps the error. 19 | func StructNameIsNilErrWrap(caller string, err error) error { 20 | return errors.Wrap(err, "[GOWRTR-1] struct name must not be empty, but it gets empty (caused at %s)") 21 | } 22 | 23 | // StructFieldNameIsEmptyErr returns the error. 24 | func StructFieldNameIsEmptyErr(caller string) error { 25 | return fmt.Errorf(`[GOWRTR-2] field name must not be empty, but it gets empty (caused at %s)`, caller) 26 | } 27 | 28 | // StructFieldNameIsEmptyErrWrap wraps the error. 29 | func StructFieldNameIsEmptyErrWrap(caller string, err error) error { 30 | return errors.Wrap(err, "[GOWRTR-2] field name must not be empty, but it gets empty (caused at %s)") 31 | } 32 | 33 | // StructFieldTypeIsEmptyErr returns the error. 34 | func StructFieldTypeIsEmptyErr(caller string) error { 35 | return fmt.Errorf(`[GOWRTR-3] field type must not be empty, but it gets empty (caused at %s)`, caller) 36 | } 37 | 38 | // StructFieldTypeIsEmptyErrWrap wraps the error. 39 | func StructFieldTypeIsEmptyErrWrap(caller string, err error) error { 40 | return errors.Wrap(err, "[GOWRTR-3] field type must not be empty, but it gets empty (caused at %s)") 41 | } 42 | 43 | // FuncParameterNameIsEmptyErr returns the error. 44 | func FuncParameterNameIsEmptyErr(caller string) error { 45 | return fmt.Errorf(`[GOWRTR-4] func parameter name must not be empty, but it gets empty (caused at %s)`, caller) 46 | } 47 | 48 | // FuncParameterNameIsEmptyErrWrap wraps the error. 49 | func FuncParameterNameIsEmptyErrWrap(caller string, err error) error { 50 | return errors.Wrap(err, "[GOWRTR-4] func parameter name must not be empty, but it gets empty (caused at %s)") 51 | } 52 | 53 | // LastFuncParameterTypeIsEmptyErr returns the error. 54 | func LastFuncParameterTypeIsEmptyErr(caller string) error { 55 | return fmt.Errorf(`[GOWRTR-5] the last func parameter type must not be empty, but it gets empty (caused at %s)`, caller) 56 | } 57 | 58 | // LastFuncParameterTypeIsEmptyErrWrap wraps the error. 59 | func LastFuncParameterTypeIsEmptyErrWrap(caller string, err error) error { 60 | return errors.Wrap(err, "[GOWRTR-5] the last func parameter type must not be empty, but it gets empty (caused at %s)") 61 | } 62 | 63 | // FuncNameIsEmptyError returns the error. 64 | func FuncNameIsEmptyError(caller string) error { 65 | return fmt.Errorf(`[GOWRTR-6] name of func must not be empty, but it gets empty (caused at %s)`, caller) 66 | } 67 | 68 | // FuncNameIsEmptyErrorWrap wraps the error. 69 | func FuncNameIsEmptyErrorWrap(caller string, err error) error { 70 | return errors.Wrap(err, "[GOWRTR-6] name of func must not be empty, but it gets empty (caused at %s)") 71 | } 72 | 73 | // InterfaceNameIsEmptyError returns the error. 74 | func InterfaceNameIsEmptyError(caller string) error { 75 | return fmt.Errorf(`[GOWRTR-7] name of interface must not be empty, but it gets empty (caused at %s)`, caller) 76 | } 77 | 78 | // InterfaceNameIsEmptyErrorWrap wraps the error. 79 | func InterfaceNameIsEmptyErrorWrap(caller string, err error) error { 80 | return errors.Wrap(err, "[GOWRTR-7] name of interface must not be empty, but it gets empty (caused at %s)") 81 | } 82 | 83 | // FuncReceiverNameIsEmptyError returns the error. 84 | func FuncReceiverNameIsEmptyError(caller string) error { 85 | return fmt.Errorf(`[GOWRTR-8] name of func receiver must not be empty, but it gets empty (caused at %s)`, caller) 86 | } 87 | 88 | // FuncReceiverNameIsEmptyErrorWrap wraps the error. 89 | func FuncReceiverNameIsEmptyErrorWrap(caller string, err error) error { 90 | return errors.Wrap(err, "[GOWRTR-8] name of func receiver must not be empty, but it gets empty (caused at %s)") 91 | } 92 | 93 | // FuncReceiverTypeIsEmptyError returns the error. 94 | func FuncReceiverTypeIsEmptyError(caller string) error { 95 | return fmt.Errorf(`[GOWRTR-9] type of func receiver must not be empty, but it gets empty (caused at %s)`, caller) 96 | } 97 | 98 | // FuncReceiverTypeIsEmptyErrorWrap wraps the error. 99 | func FuncReceiverTypeIsEmptyErrorWrap(caller string, err error) error { 100 | return errors.Wrap(err, "[GOWRTR-9] type of func receiver must not be empty, but it gets empty (caused at %s)") 101 | } 102 | 103 | // FuncSignatureIsNilError returns the error. 104 | func FuncSignatureIsNilError(caller string) error { 105 | return fmt.Errorf(`[GOWRTR-10] func signature must not be nil, bit it gets nil (caused at %s)`, caller) 106 | } 107 | 108 | // FuncSignatureIsNilErrorWrap wraps the error. 109 | func FuncSignatureIsNilErrorWrap(caller string, err error) error { 110 | return errors.Wrap(err, "[GOWRTR-10] func signature must not be nil, bit it gets nil (caused at %s)") 111 | } 112 | 113 | // AnonymousFuncSignatureIsNilError returns the error. 114 | func AnonymousFuncSignatureIsNilError(caller string) error { 115 | return fmt.Errorf(`[GOWRTR-11] anonymous func signature must not be nil, bit it gets nil (caused at %s)`, caller) 116 | } 117 | 118 | // AnonymousFuncSignatureIsNilErrorWrap wraps the error. 119 | func AnonymousFuncSignatureIsNilErrorWrap(caller string, err error) error { 120 | return errors.Wrap(err, "[GOWRTR-11] anonymous func signature must not be nil, bit it gets nil (caused at %s)") 121 | } 122 | 123 | // FuncInvocationParameterIsEmptyError returns the error. 124 | func FuncInvocationParameterIsEmptyError(caller string) error { 125 | return fmt.Errorf(`[GOWRTR-12] a parameter of function invocation must not be nil, but it gets nil (caused at %s)`, caller) 126 | } 127 | 128 | // FuncInvocationParameterIsEmptyErrorWrap wraps the error. 129 | func FuncInvocationParameterIsEmptyErrorWrap(caller string, err error) error { 130 | return errors.Wrap(err, "[GOWRTR-12] a parameter of function invocation must not be nil, but it gets nil (caused at %s)") 131 | } 132 | 133 | // CodeFormatterError returns the error. 134 | func CodeFormatterError(cmd string, msg string, fmterr error) error { 135 | return fmt.Errorf(`[GOWRTR-13] code formatter raises error: command='%s', err='%s', msg='%s'`, cmd, msg, fmterr) 136 | } 137 | 138 | // CodeFormatterErrorWrap wraps the error. 139 | func CodeFormatterErrorWrap(cmd string, msg string, fmterr error, err error) error { 140 | return errors.Wrap(err, "[GOWRTR-13] code formatter raises error: command='%s', err='%s', msg='%s'") 141 | } 142 | 143 | // CaseConditionIsEmptyError returns the error. 144 | func CaseConditionIsEmptyError(caller string) error { 145 | return fmt.Errorf(`[GOWRTR-14] condition of case must not be empty, but it gets empty (caused at %s)`, caller) 146 | } 147 | 148 | // CaseConditionIsEmptyErrorWrap wraps the error. 149 | func CaseConditionIsEmptyErrorWrap(caller string, err error) error { 150 | return errors.Wrap(err, "[GOWRTR-14] condition of case must not be empty, but it gets empty (caused at %s)") 151 | } 152 | 153 | // IfConditionIsEmptyError returns the error. 154 | func IfConditionIsEmptyError(caller string) error { 155 | return fmt.Errorf(`[GOWRTR-15] condition of if must not be empty, but it gets empty (caused at %s)`, caller) 156 | } 157 | 158 | // IfConditionIsEmptyErrorWrap wraps the error. 159 | func IfConditionIsEmptyErrorWrap(caller string, err error) error { 160 | return errors.Wrap(err, "[GOWRTR-15] condition of if must not be empty, but it gets empty (caused at %s)") 161 | } 162 | 163 | // UnnamedReturnTypeAppearsAfterNamedReturnTypeError returns the error. 164 | func UnnamedReturnTypeAppearsAfterNamedReturnTypeError(caller string) error { 165 | return fmt.Errorf(`[GOWRTR-16] unnamed return type appears after named return type (caused at %s)`, caller) 166 | } 167 | 168 | // UnnamedReturnTypeAppearsAfterNamedReturnTypeErrorWrap wraps the error. 169 | func UnnamedReturnTypeAppearsAfterNamedReturnTypeErrorWrap(caller string, err error) error { 170 | return errors.Wrap(err, "[GOWRTR-16] unnamed return type appears after named return type (caused at %s)") 171 | } 172 | 173 | // ValueOfCompositeLiteralIsEmptyError returns the error. 174 | func ValueOfCompositeLiteralIsEmptyError(caller string) error { 175 | return fmt.Errorf(`[GOWRTR-17] a value of composite literal must not be empty, but it gets empty (caused at %s)`, caller) 176 | } 177 | 178 | // ValueOfCompositeLiteralIsEmptyErrorWrap wraps the error. 179 | func ValueOfCompositeLiteralIsEmptyErrorWrap(caller string, err error) error { 180 | return errors.Wrap(err, "[GOWRTR-17] a value of composite literal must not be empty, but it gets empty (caused at %s)") 181 | } 182 | 183 | // TypeParameterParameterIsEmptyErr returns the error. 184 | func TypeParameterParameterIsEmptyErr(caller string) error { 185 | return fmt.Errorf(`[GOWRTR-18] type-parameter's parameter must not be empty, but it gets empty (caused at %s)`, caller) 186 | } 187 | 188 | // TypeParameterParameterIsEmptyErrWrap wraps the error. 189 | func TypeParameterParameterIsEmptyErrWrap(caller string, err error) error { 190 | return errors.Wrap(err, "[GOWRTR-18] type-parameter's parameter must not be empty, but it gets empty (caused at %s)") 191 | } 192 | 193 | // TypeParameterTypeIsEmptyErr returns the error. 194 | func TypeParameterTypeIsEmptyErr(caller string) error { 195 | return fmt.Errorf(`[GOWRTR-19] type-parameter's type must not be empty, but it gets empty (caused at %s)`, caller) 196 | } 197 | 198 | // TypeParameterTypeIsEmptyErrWrap wraps the error. 199 | func TypeParameterTypeIsEmptyErrWrap(caller string, err error) error { 200 | return errors.Wrap(err, "[GOWRTR-19] type-parameter's type must not be empty, but it gets empty (caused at %s)") 201 | } 202 | 203 | // ErrsType represents the error type. 204 | type ErrsType int 205 | 206 | const ( 207 | 208 | // StructNameIsNilErrType represents the error type for StructNameIsNilErr. 209 | StructNameIsNilErrType ErrsType = iota 210 | // StructFieldNameIsEmptyErrType represents the error type for StructFieldNameIsEmptyErr. 211 | StructFieldNameIsEmptyErrType 212 | // StructFieldTypeIsEmptyErrType represents the error type for StructFieldTypeIsEmptyErr. 213 | StructFieldTypeIsEmptyErrType 214 | // FuncParameterNameIsEmptyErrType represents the error type for FuncParameterNameIsEmptyErr. 215 | FuncParameterNameIsEmptyErrType 216 | // LastFuncParameterTypeIsEmptyErrType represents the error type for LastFuncParameterTypeIsEmptyErr. 217 | LastFuncParameterTypeIsEmptyErrType 218 | // FuncNameIsEmptyErrorType represents the error type for FuncNameIsEmptyError. 219 | FuncNameIsEmptyErrorType 220 | // InterfaceNameIsEmptyErrorType represents the error type for InterfaceNameIsEmptyError. 221 | InterfaceNameIsEmptyErrorType 222 | // FuncReceiverNameIsEmptyErrorType represents the error type for FuncReceiverNameIsEmptyError. 223 | FuncReceiverNameIsEmptyErrorType 224 | // FuncReceiverTypeIsEmptyErrorType represents the error type for FuncReceiverTypeIsEmptyError. 225 | FuncReceiverTypeIsEmptyErrorType 226 | // FuncSignatureIsNilErrorType represents the error type for FuncSignatureIsNilError. 227 | FuncSignatureIsNilErrorType 228 | // AnonymousFuncSignatureIsNilErrorType represents the error type for AnonymousFuncSignatureIsNilError. 229 | AnonymousFuncSignatureIsNilErrorType 230 | // FuncInvocationParameterIsEmptyErrorType represents the error type for FuncInvocationParameterIsEmptyError. 231 | FuncInvocationParameterIsEmptyErrorType 232 | // CodeFormatterErrorType represents the error type for CodeFormatterError. 233 | CodeFormatterErrorType 234 | // CaseConditionIsEmptyErrorType represents the error type for CaseConditionIsEmptyError. 235 | CaseConditionIsEmptyErrorType 236 | // IfConditionIsEmptyErrorType represents the error type for IfConditionIsEmptyError. 237 | IfConditionIsEmptyErrorType 238 | // UnnamedReturnTypeAppearsAfterNamedReturnTypeErrorType represents the error type for UnnamedReturnTypeAppearsAfterNamedReturnTypeError. 239 | UnnamedReturnTypeAppearsAfterNamedReturnTypeErrorType 240 | // ValueOfCompositeLiteralIsEmptyErrorType represents the error type for ValueOfCompositeLiteralIsEmptyError. 241 | ValueOfCompositeLiteralIsEmptyErrorType 242 | // TypeParameterParameterIsEmptyErrType represents the error type for TypeParameterParameterIsEmptyErr. 243 | TypeParameterParameterIsEmptyErrType 244 | // TypeParameterTypeIsEmptyErrType represents the error type for TypeParameterTypeIsEmptyErr. 245 | TypeParameterTypeIsEmptyErrType 246 | // ErrsUnknownType represents unknown type for Errs 247 | ErrsUnknownType 248 | ) 249 | 250 | // ListErrs returns the list of errors. 251 | func ListErrs() []string { 252 | return []string{"[GOWRTR-1] struct name must not be empty, but it gets empty (caused at %s)", "[GOWRTR-2] field name must not be empty, but it gets empty (caused at %s)", "[GOWRTR-3] field type must not be empty, but it gets empty (caused at %s)", "[GOWRTR-4] func parameter name must not be empty, but it gets empty (caused at %s)", "[GOWRTR-5] the last func parameter type must not be empty, but it gets empty (caused at %s)", "[GOWRTR-6] name of func must not be empty, but it gets empty (caused at %s)", "[GOWRTR-7] name of interface must not be empty, but it gets empty (caused at %s)", "[GOWRTR-8] name of func receiver must not be empty, but it gets empty (caused at %s)", "[GOWRTR-9] type of func receiver must not be empty, but it gets empty (caused at %s)", "[GOWRTR-10] func signature must not be nil, bit it gets nil (caused at %s)", "[GOWRTR-11] anonymous func signature must not be nil, bit it gets nil (caused at %s)", "[GOWRTR-12] a parameter of function invocation must not be nil, but it gets nil (caused at %s)", "[GOWRTR-13] code formatter raises error: command='%s', err='%s', msg='%s'", "[GOWRTR-14] condition of case must not be empty, but it gets empty (caused at %s)", "[GOWRTR-15] condition of if must not be empty, but it gets empty (caused at %s)", "[GOWRTR-16] unnamed return type appears after named return type (caused at %s)", "[GOWRTR-17] a value of composite literal must not be empty, but it gets empty (caused at %s)", "[GOWRTR-18] type-parameter's parameter must not be empty, but it gets empty (caused at %s)", "[GOWRTR-19] type-parameter's type must not be empty, but it gets empty (caused at %s)"} 253 | } 254 | 255 | // IdentifyErrs checks the identity of an error 256 | func IdentifyErrs(err error) ErrsType { 257 | errStr := err.Error() 258 | switch { 259 | case strings.HasPrefix(errStr, "[GOWRTR-1]"): 260 | return StructNameIsNilErrType 261 | case strings.HasPrefix(errStr, "[GOWRTR-2]"): 262 | return StructFieldNameIsEmptyErrType 263 | case strings.HasPrefix(errStr, "[GOWRTR-3]"): 264 | return StructFieldTypeIsEmptyErrType 265 | case strings.HasPrefix(errStr, "[GOWRTR-4]"): 266 | return FuncParameterNameIsEmptyErrType 267 | case strings.HasPrefix(errStr, "[GOWRTR-5]"): 268 | return LastFuncParameterTypeIsEmptyErrType 269 | case strings.HasPrefix(errStr, "[GOWRTR-6]"): 270 | return FuncNameIsEmptyErrorType 271 | case strings.HasPrefix(errStr, "[GOWRTR-7]"): 272 | return InterfaceNameIsEmptyErrorType 273 | case strings.HasPrefix(errStr, "[GOWRTR-8]"): 274 | return FuncReceiverNameIsEmptyErrorType 275 | case strings.HasPrefix(errStr, "[GOWRTR-9]"): 276 | return FuncReceiverTypeIsEmptyErrorType 277 | case strings.HasPrefix(errStr, "[GOWRTR-10]"): 278 | return FuncSignatureIsNilErrorType 279 | case strings.HasPrefix(errStr, "[GOWRTR-11]"): 280 | return AnonymousFuncSignatureIsNilErrorType 281 | case strings.HasPrefix(errStr, "[GOWRTR-12]"): 282 | return FuncInvocationParameterIsEmptyErrorType 283 | case strings.HasPrefix(errStr, "[GOWRTR-13]"): 284 | return CodeFormatterErrorType 285 | case strings.HasPrefix(errStr, "[GOWRTR-14]"): 286 | return CaseConditionIsEmptyErrorType 287 | case strings.HasPrefix(errStr, "[GOWRTR-15]"): 288 | return IfConditionIsEmptyErrorType 289 | case strings.HasPrefix(errStr, "[GOWRTR-16]"): 290 | return UnnamedReturnTypeAppearsAfterNamedReturnTypeErrorType 291 | case strings.HasPrefix(errStr, "[GOWRTR-17]"): 292 | return ValueOfCompositeLiteralIsEmptyErrorType 293 | case strings.HasPrefix(errStr, "[GOWRTR-18]"): 294 | return TypeParameterParameterIsEmptyErrType 295 | case strings.HasPrefix(errStr, "[GOWRTR-19]"): 296 | return TypeParameterTypeIsEmptyErrType 297 | default: 298 | return ErrsUnknownType 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /internal/vendor.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | // For vendoring 5 | _ "github.com/moznion/go-errgen" 6 | ) 7 | --------------------------------------------------------------------------------