├── .gitignore ├── Makefile ├── README.md ├── cmd ├── generate.go └── root.go ├── configs └── template │ └── repository.go.tpl ├── go.mod ├── go.sum ├── main.go ├── pkg ├── formatter │ └── formatter.go ├── generator │ ├── build_variables.go │ ├── build_variables_test.go │ ├── entity_parser.go │ ├── go.go │ ├── go_formatter.go │ ├── template_reader.go │ └── testdata │ │ └── user.go ├── model │ ├── column.go │ ├── entity.go │ └── primary_key.go ├── parser │ ├── ast.go │ ├── ast_test.go │ ├── entity.go │ ├── file_reader.go │ └── testdata │ │ └── user.go ├── reader │ ├── file.go │ ├── template.go │ └── template_test.go ├── strutil │ ├── name.go │ ├── name_test.go │ ├── plural.go │ └── plural_test.go └── writer │ └── file.go └── script └── development └── generate.sh /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | local 3 | .envrc 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | clean: 3 | rm -f ./repository_from_sqlboiler 4 | 5 | dry-run: clean build 6 | ./script/development/generate.sh 7 | 8 | build: 9 | go build -o repository_from_sqlboiler 10 | 11 | source: 12 | cp $(SOURCE_SQLBOILER_PATH)/*.go local/source 13 | 14 | test: 15 | go test ./pkg/... 16 | 17 | install: 18 | go install github.com/bannzai/repository_from_sqlboiler 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # repository_from_sqlboiler 2 | repository_from_sqlboiler can be generated `Repository Pattern Go` source code for from [sqlboiler](https://github.com/volatiletech/sqlboiler)'s generated `Go` code. 3 | 4 | ## Setup 5 | It should prepare`local/source` and `local/destination`directory and put on [sqlboiler](https://github.com/volatiletech/sqlboiler)'s generated `Go` model code. 6 | ```bash 7 | $ mkdir -p local/source local/destination. 8 | $ export SOURCE_SQLBOILER_MODEL_PATH=$GOPATH/src/github.com/YOUR_APP/model 9 | $ cp $SOURCE_SQLBOILER_MODEL_PATH/*.go local/destination 10 | ``` 11 | 12 | ## Run 13 | If you want to try ./repository_from_sqlboiler, You exec `make dry-run`. It is generated file written by`Go` code to `local/destination/repository.go`. 14 | ```bash 15 | $ make dry-run 16 | ``` 17 | 18 | -------------------------------------------------------------------------------- /cmd/generate.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2019 NAME HERE 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package cmd 17 | 18 | import ( 19 | "github.com/bannzai/repository_from_sqlboiler/pkg/formatter" 20 | "github.com/bannzai/repository_from_sqlboiler/pkg/generator" 21 | "github.com/bannzai/repository_from_sqlboiler/pkg/parser" 22 | "github.com/bannzai/repository_from_sqlboiler/pkg/reader" 23 | "github.com/bannzai/repository_from_sqlboiler/pkg/writer" 24 | "github.com/spf13/cobra" 25 | ) 26 | 27 | type GenerateOptions struct { 28 | templateFilePath string 29 | sourceFilePath string 30 | destinationFilePath string 31 | } 32 | 33 | var generateOptions = GenerateOptions{} 34 | 35 | // generateCmd represents the generate command 36 | var generateCmd = &cobra.Command{ 37 | Use: "generate", 38 | Short: "A brief description of your command", 39 | Long: `A longer description that spans multiple lines and likely contains examples 40 | and usage of using your command. For example: 41 | 42 | Cobra is a CLI library for Go that empowers applications. 43 | This application is a tool to generate the needed files 44 | to quickly create a Cobra application.`, 45 | Run: func(cmd *cobra.Command, args []string) { 46 | source := generateOptions.sourceFilePath 47 | destination := generateOptions.destinationFilePath 48 | tmpl := generateOptions.templateFilePath 49 | g := generator.GoCodeGenerator{ 50 | DestinationFilePath: destination, 51 | EntityParser: parser.Entity{ 52 | FilePath: source, 53 | }, 54 | GoFormatter: formatter.GoFormatter{ 55 | FilePath: destination, 56 | }, 57 | TemplateReader: reader.Template{ 58 | FilePath: tmpl, 59 | }, 60 | Writer: writer.File{ 61 | FilePath: destination, 62 | }, 63 | } 64 | g.Generate() 65 | }, 66 | } 67 | 68 | func init() { 69 | rootCmd.AddCommand(generateCmd) 70 | const invalidGenerateValue = "" 71 | generateCmd.Flags().StringVar(&generateOptions.sourceFilePath, "source", invalidGenerateValue, "source entity file for generate repository.") 72 | generateCmd.Flags().StringVar(&generateOptions.destinationFilePath, "destination", invalidGenerateValue, "destination repository file path.") 73 | generateCmd.Flags().StringVar(&generateOptions.templateFilePath, "template", invalidGenerateValue, "template file path for generate repository source code.") 74 | 75 | } 76 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2019 NAME HERE 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package cmd 17 | 18 | import ( 19 | "fmt" 20 | "os" 21 | 22 | "github.com/spf13/cobra" 23 | ) 24 | 25 | // rootCmd represents the root command 26 | var rootCmd = &cobra.Command{ 27 | Use: "root", 28 | Short: "A brief description of your command", 29 | Long: `A longer description that spans multiple lines and likely contains examples 30 | and usage of using your command. For example: 31 | 32 | Cobra is a CLI library for Go that empowers applications. 33 | This application is a tool to generate the needed files 34 | to quickly create a Cobra application.`, 35 | Run: func(cmd *cobra.Command, args []string) { 36 | fmt.Println("root called") 37 | }, 38 | } 39 | 40 | func Execute() { 41 | if err := rootCmd.Execute(); err != nil { 42 | os.Exit(1) 43 | } 44 | } 45 | 46 | func init() { 47 | // Here you will define your flags and configuration settings. 48 | 49 | // Cobra supports Persistent Flags which will work for this command 50 | // and all subcommands, e.g.: 51 | // rootCmd.PersistentFlags().String("foo", "", "A help for foo") 52 | 53 | // Cobra supports local flags which will only run when this command 54 | // is called directly, e.g.: 55 | // rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 56 | } 57 | -------------------------------------------------------------------------------- /configs/template/repository.go.tpl: -------------------------------------------------------------------------------- 1 | {{- $entity := .Entity -}} 2 | {{- $entityName := $entity.Name -}} 3 | {{- $entityLocalVarName := golangVariableCase $entityName -}} 4 | {{- $entityStructName := golangStructNameCase $entityName -}} 5 | {{- $entitySelectorName := entitySelectorName $entityName -}} 6 | 7 | package repository 8 | 9 | type {{$entityStructName}}Repository interface { 10 | {{ .FetchByPrimaryKey }} 11 | FetchBy(ctx context.Context, parameter parameter.ParameterType) (entity.{{$entityStructName}}, error) 12 | FetchList(ctx context.Context, parameter parameter.ParameterType) ([]entity.{{$entityStructName}}, error) 13 | FetchAll(ctx context.Context) ([]entity.{{$entityStructName}} , error) 14 | Exists(ctx context.Context, parameter parameter.ParameterType) (bool, error) 15 | } 16 | 17 | // {{$entityStructName}} is interface of datastore (e.g RDB). 18 | type {{$entityStructName}} struct { } 19 | func ({{$entityStructName}}) {{ .FetchByPrimaryKey }} { 20 | e, err := entity.{{$entitySelectorName}}({{.SQLQueryArguments}}).OneG(ctx) 21 | 22 | if err != nil { 23 | return entity.{{$entityStructName}}{}, errors.Wrap(err, "") 24 | } 25 | 26 | return *e, nil 27 | } 28 | 29 | // FetchBy is find {{$entityStructName}} entity from primary key({{.PrimaryKeys}}). When not found `ResourceNotFound` error. 30 | func ({{$entityStructName}}) FetchBy(ctx context.Context, parameter parameter.ParameterType) (entity.{{$entityStructName}}, error) { 31 | {{$entityLocalVarName}}, err := entity.{{$entitySelectorName}}(parameter).OneG(ctx) 32 | 33 | if err != nil { 34 | if err == sql.ErrNoRows { 35 | return entity.{{$entityStructName}}{}, errors.Wrap(ResourceNotFound{err}, "") 36 | } 37 | return entity.{{$entityStructName}}{}, errors.Wrap(err, "") 38 | } 39 | 40 | return *{{$entityLocalVarName}}, nil 41 | } 42 | 43 | // FetchAll is fetch all for {{$entityStructName}} entity. 44 | func ({{$entityStructName}}) FetchList(ctx context.Context, parameter parameter.ParameterType) ([]entity.{{$entityStructName}}, error) { 45 | {{$entityLocalVarName}}, err := entity.{{$entitySelectorName}}(parameter).AllG(ctx) 46 | 47 | if err != nil { 48 | return []entity.{{$entityStructName}}{}, errors.Wrap(err, "") 49 | } 50 | 51 | list := make([]entity.{{$entityStructName}}, len({{$entityLocalVarName}})) 52 | for i, e := range {{$entityLocalVarName}} { 53 | list[i] = *e 54 | } 55 | 56 | return list, nil 57 | } 58 | 59 | // FetchAll is fetch all for {{$entityStructName}} entity. 60 | func ({{$entityStructName}}) FetchAll(ctx context.Context) ([]entity.{{$entityStructName}}, error) { 61 | {{$entityLocalVarName}}, err := entity.{{$entitySelectorName}}().AllG(ctx) 62 | 63 | if err != nil { 64 | return []entity.{{$entityStructName}}{}, errors.Wrap(err, "") 65 | } 66 | 67 | list := make([]entity.{{$entityStructName}}, len({{$entityLocalVarName}})) 68 | for i, e := range {{$entityLocalVarName}} { 69 | list[i] = *e 70 | } 71 | 72 | return list, nil 73 | } 74 | 75 | // Exists is fetch all for {{$entityStructName}} entity. 76 | func ({{$entityStructName}}) Exists(ctx context.Context, parameter parameter.ParameterType) (bool, error) { 77 | exists, err := entity.{{$entitySelectorName}}(parameter).ExistsG(ctx) 78 | 79 | if err != nil { 80 | return false, errors.Wrap(err, "") 81 | } 82 | 83 | return exists, nil 84 | } 85 | 86 | func New{{$entityStructName}}Repository() {{$entityStructName}}Repository { 87 | return {{$entityStructName}}{} 88 | } 89 | 90 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bannzai/repository_from_sqlboiler 2 | 3 | go 1.12 4 | 5 | require ( 6 | cloud.google.com/go v0.41.0 // indirect 7 | github.com/coreos/bbolt v1.3.3 // indirect 8 | github.com/coreos/etcd v3.3.13+incompatible // indirect 9 | github.com/coreos/go-semver v0.3.0 // indirect 10 | github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a // indirect 11 | github.com/go-kit/kit v0.9.0 // indirect 12 | github.com/gofrs/uuid v3.2.0+incompatible // indirect 13 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect 14 | github.com/grpc-ecosystem/grpc-gateway v1.9.3 // indirect 15 | github.com/kisielk/errcheck v1.2.0 // indirect 16 | github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect 17 | github.com/kr/pty v1.1.8 // indirect 18 | github.com/magiconair/properties v1.8.1 // indirect 19 | github.com/pelletier/go-toml v1.4.0 // indirect 20 | github.com/pkg/errors v0.8.1 21 | github.com/prometheus/common v0.6.0 // indirect 22 | github.com/prometheus/procfs v0.0.3 // indirect 23 | github.com/rogpeppe/fastuuid v1.1.0 // indirect 24 | github.com/russross/blackfriday v2.0.0+incompatible // indirect 25 | github.com/sirupsen/logrus v1.4.2 // indirect 26 | github.com/spf13/afero v1.2.2 // indirect 27 | github.com/spf13/cobra v0.0.5 28 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 29 | github.com/spf13/viper v1.4.0 // indirect 30 | github.com/stretchr/objx v0.2.0 // indirect 31 | github.com/ugorji/go v1.1.7 // indirect 32 | github.com/volatiletech/inflect v0.0.0-20170731032912-e7201282ae8d // indirect 33 | github.com/volatiletech/null v8.0.0+incompatible 34 | github.com/volatiletech/sqlboiler v3.4.0+incompatible 35 | go.etcd.io/bbolt v1.3.3 // indirect 36 | golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 // indirect 37 | golang.org/x/exp v0.0.0-20190627132806-fd42eb6b336f // indirect 38 | golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9 // indirect 39 | golang.org/x/mobile v0.0.0-20190607214518-6fa95d984e88 // indirect 40 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7 // indirect 41 | golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb // indirect 42 | golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db // indirect 43 | google.golang.org/genproto v0.0.0-20190701230453-710ae3a149df // indirect 44 | google.golang.org/grpc v1.22.0 // indirect 45 | ) 46 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.41.0/go.mod h1:OauMR7DV8fzvZIl2qg6rkaIhD/vmgk4iwEw/h6ercmg= 5 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 6 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 7 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 8 | github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= 9 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 10 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 11 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 12 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 13 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 14 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 15 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 16 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 17 | github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 18 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 19 | github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 20 | github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= 21 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 22 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 23 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 24 | github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 25 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 26 | github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= 27 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 28 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 29 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 30 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 31 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 32 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 33 | github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 34 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 35 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 36 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 37 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 38 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 39 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 40 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 41 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 42 | github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= 43 | github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 44 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 45 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 46 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 47 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 48 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 49 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 50 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 51 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 52 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 53 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 54 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 55 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 56 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 57 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 58 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 59 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 60 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 61 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 62 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 63 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 64 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 65 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 66 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 67 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 68 | github.com/grpc-ecosystem/grpc-gateway v1.9.3/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 69 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 70 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 71 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 72 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 73 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 74 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 75 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 76 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 77 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 78 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 79 | github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 80 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 81 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 82 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 83 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 84 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 85 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 86 | github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 87 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 88 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 89 | github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= 90 | github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 91 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 92 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 93 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 94 | github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= 95 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 96 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 97 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 98 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 99 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 100 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 101 | github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg= 102 | github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= 103 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 104 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 105 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 106 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 107 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 108 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 109 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 110 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 111 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 112 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 113 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 114 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 115 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 116 | github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= 117 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 118 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 119 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 120 | github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= 121 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 122 | github.com/prometheus/tsdb v0.9.1/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4= 123 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 124 | github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 125 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 126 | github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 127 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 128 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 129 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 130 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 131 | github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 132 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 133 | github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= 134 | github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= 135 | github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= 136 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 137 | github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= 138 | github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= 139 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 140 | github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= 141 | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 142 | github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= 143 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 144 | github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= 145 | github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= 146 | github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= 147 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 148 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 149 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 150 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 151 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 152 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 153 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 154 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= 155 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 156 | github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 157 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 158 | github.com/volatiletech/inflect v0.0.0-20170731032912-e7201282ae8d h1:gI4/tqP6lCY5k6Sg+4k9qSoBXmPwG+xXgMpK7jivD4M= 159 | github.com/volatiletech/inflect v0.0.0-20170731032912-e7201282ae8d/go.mod h1:jspfvgf53t5NLUT4o9L1IX0kIBNKamGq1tWc/MgWK9Q= 160 | github.com/volatiletech/null v8.0.0+incompatible h1:7wP8m5d/gZ6kW/9GnrLtMCRre2dlEnaQ9Km5OXlK4zg= 161 | github.com/volatiletech/null v8.0.0+incompatible/go.mod h1:0wD98JzdqB+rLyZ70fN05VDbXbafIb0KU0MdVhCzmOQ= 162 | github.com/volatiletech/sqlboiler v3.4.0+incompatible h1:saQ6WxZ9wEJp33q3w/DHs7an7SYi1H7Yzf4/moxCbJU= 163 | github.com/volatiletech/sqlboiler v3.4.0+incompatible/go.mod h1:jLfDkkHWPbS2cWRLkyC20vQWaIQsASEY7gM7zSo11Yw= 164 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 165 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 166 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 167 | go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 168 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 169 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 170 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 171 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 172 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 173 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 174 | golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 175 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 176 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 177 | golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 178 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 179 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 180 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 181 | golang.org/x/exp v0.0.0-20190627132806-fd42eb6b336f/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 182 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 183 | golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 184 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 185 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 186 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 187 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 188 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 189 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 190 | golang.org/x/mobile v0.0.0-20190607214518-6fa95d984e88/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 191 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 192 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 193 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 194 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 195 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 196 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 197 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 198 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 199 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 200 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 201 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 202 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 203 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 204 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 205 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 206 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 207 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 208 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 209 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 210 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 211 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 212 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 213 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 214 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 215 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 216 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 217 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 218 | golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 219 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 220 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 221 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 222 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 223 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 224 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 225 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 226 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 227 | golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k= 228 | golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 229 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 230 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 231 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 232 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 233 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 234 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 235 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 236 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 237 | golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 238 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 239 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 240 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 241 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 242 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 243 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 244 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 245 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 246 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 247 | golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 248 | golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= 249 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 250 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 251 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 252 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 253 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 254 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 255 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 256 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 257 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 258 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 259 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 260 | google.golang.org/genproto v0.0.0-20190626174449-989357319d63/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= 261 | google.golang.org/genproto v0.0.0-20190701230453-710ae3a149df/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= 262 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 263 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 264 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 265 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 266 | google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 267 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 268 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 269 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 270 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 271 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 272 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 273 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 274 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 275 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 276 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 277 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 278 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 279 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 280 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/bannzai/repository_from_sqlboiler/cmd" 4 | 5 | func main() { 6 | cmd.Execute() 7 | } 8 | -------------------------------------------------------------------------------- /pkg/formatter/formatter.go: -------------------------------------------------------------------------------- 1 | package formatter 2 | 3 | import ( 4 | "fmt" 5 | "os/exec" 6 | ) 7 | 8 | type GoFormatter struct { 9 | FilePath string 10 | } 11 | 12 | func (formatter GoFormatter) GoFormat() { 13 | if err := exec.Command("gofmt", "-w", formatter.FilePath).Run(); err != nil { 14 | panic(fmt.Errorf("[ERROR] gofmt -w %v", formatter.FilePath)) 15 | } 16 | } 17 | 18 | func (formatter GoFormatter) GoImports() { 19 | if err := exec.Command("goimports", "-w", formatter.FilePath).Run(); err != nil { 20 | panic(fmt.Errorf("[ERROR] goimports -w %v", formatter.FilePath)) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /pkg/generator/build_variables.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/bannzai/repository_from_sqlboiler/pkg/model" 8 | "github.com/bannzai/repository_from_sqlboiler/pkg/strutil" 9 | ) 10 | 11 | func fetchByPrimaryKey(entity model.Entity) string { 12 | return fmt.Sprintf( 13 | "%v(%v) (entity.%v, error)", 14 | fetchByPrimaryKeyFunctionName(entity), 15 | fetchByPrimaryKeyFunctionArgument(entity), 16 | entity.Name, 17 | ) 18 | } 19 | 20 | func fetchByPrimaryKeyFunctionName(entity model.Entity) string { 21 | const prefix = "FetchBy" 22 | suffix := "" 23 | for i, primaryKey := range entity.PrimaryKeys { 24 | if i > 0 { 25 | suffix += "And" 26 | } 27 | suffix += strutil.SpecializeUpperCamelCase(primaryKey.Name) 28 | } 29 | return prefix + suffix 30 | } 31 | 32 | func fetchByPrimaryKeyFunctionArgument(entity model.Entity) string { 33 | findPrimaryKeyType := func(primaryKey model.PrimaryKey, entity model.Entity) string { 34 | typeName := "" 35 | for _, column := range entity.Fields { 36 | // Remove _ and same character size. 37 | lhs := strings.ToLower(strutil.LowerCamelCase(column.Name)) 38 | rhs := strings.ToLower(strutil.LowerCamelCase(primaryKey.Name)) 39 | if lhs == rhs { 40 | typeName = column.TypeName 41 | } 42 | } 43 | return typeName 44 | } 45 | 46 | condition := "ctx context.Context, " 47 | for i, primaryKey := range entity.PrimaryKeys { 48 | if i > 0 { 49 | condition += ", " 50 | } 51 | column := strutil.SpecializeLowerCamelCase(primaryKey.Name) 52 | column = strutil.EscapedReservedWord(column) 53 | typeName := findPrimaryKeyType(primaryKey, entity) 54 | condition += fmt.Sprintf("%v %v", column, typeName) 55 | } 56 | 57 | return condition 58 | } 59 | 60 | func listOfPrimaryKeys(entity model.Entity) string { 61 | content := "" 62 | for i, primaryKey := range entity.PrimaryKeys { 63 | if i > 0 { 64 | content += ", " 65 | } 66 | content += strutil.EscapedReservedWord(primaryKey.Name) 67 | } 68 | return content 69 | } 70 | 71 | func sqlQueryArguments(entity model.Entity) string { 72 | condition := "" 73 | for i, primaryKey := range entity.PrimaryKeys { 74 | if i > 0 { 75 | condition += ", " 76 | } 77 | column := strutil.SnakeCase(primaryKey.Name) 78 | variable := strutil.LowerCamelCase(primaryKey.Name) 79 | variable = strutil.EscapedReservedWord(variable) 80 | condition += fmt.Sprintf("qm.Where(\"%v=?\", %v)", column, variable) 81 | } 82 | 83 | return condition 84 | } 85 | -------------------------------------------------------------------------------- /pkg/generator/build_variables_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/bannzai/repository_from_sqlboiler/pkg/model" 8 | "github.com/bannzai/repository_from_sqlboiler/pkg/parser" 9 | ) 10 | 11 | func Test_fetchByPrimaryKey(t *testing.T) { 12 | workingDirectory, _ := os.Getwd() 13 | type args struct { 14 | entity model.Entity 15 | } 16 | tests := []struct { 17 | name string 18 | args args 19 | want string 20 | }{ 21 | { 22 | name: "Mapped primary key information", 23 | args: args{ 24 | entity: parser.Entity{ 25 | FilePath: workingDirectory + "/testdata/user.go", 26 | }.Parse(), 27 | }, 28 | want: "FetchByIDAndTypeAndFullName(ctx context.Context, id uint, _type string, fullName string) (entity.User, error)", 29 | }, 30 | } 31 | for _, tt := range tests { 32 | t.Run(tt.name, func(t *testing.T) { 33 | if got := fetchByPrimaryKey(tt.args.entity); got != tt.want { 34 | t.Errorf("fetchByPrimaryKey() = %v, want %v", got, tt.want) 35 | } 36 | }) 37 | } 38 | } 39 | 40 | func Test_fetchByPrimaryKeyFunctionName(t *testing.T) { 41 | workingDirectory, _ := os.Getwd() 42 | type args struct { 43 | entity model.Entity 44 | } 45 | tests := []struct { 46 | name string 47 | args args 48 | want string 49 | }{ 50 | { 51 | name: "Mapped primary key information", 52 | args: args{ 53 | entity: parser.Entity{ 54 | FilePath: workingDirectory + "/testdata/user.go", 55 | }.Parse(), 56 | }, 57 | want: "FetchByIDAndTypeAndFullName", 58 | }, 59 | } 60 | for _, tt := range tests { 61 | t.Run(tt.name, func(t *testing.T) { 62 | if got := fetchByPrimaryKeyFunctionName(tt.args.entity); got != tt.want { 63 | t.Errorf("fetchByPrimaryKeyFunctionName() = %v, want %v", got, tt.want) 64 | } 65 | }) 66 | } 67 | } 68 | 69 | func Test_fetchByPrimaryKeyFunctionArgument(t *testing.T) { 70 | workingDirectory, _ := os.Getwd() 71 | type args struct { 72 | entity model.Entity 73 | } 74 | tests := []struct { 75 | name string 76 | args args 77 | want string 78 | }{ 79 | { 80 | name: "Mapped primary key information", 81 | args: args{ 82 | entity: parser.Entity{ 83 | FilePath: workingDirectory + "/testdata/user.go", 84 | }.Parse(), 85 | }, 86 | want: "ctx context.Context, id uint, _type string, fullName string", 87 | }, 88 | } 89 | for _, tt := range tests { 90 | t.Run(tt.name, func(t *testing.T) { 91 | if got := fetchByPrimaryKeyFunctionArgument(tt.args.entity); got != tt.want { 92 | t.Errorf("fetchByPrimaryKeyFunctionArgument() = %v, want %v", got, tt.want) 93 | } 94 | }) 95 | } 96 | } 97 | 98 | func Test_listOfPrimaryKeys(t *testing.T) { 99 | workingDirectory, _ := os.Getwd() 100 | type args struct { 101 | entity model.Entity 102 | } 103 | tests := []struct { 104 | name string 105 | args args 106 | want string 107 | }{ 108 | { 109 | name: "Mapped primary key information", 110 | args: args{ 111 | entity: parser.Entity{ 112 | FilePath: workingDirectory + "/testdata/user.go", 113 | }.Parse(), 114 | }, 115 | want: "id, _type, full_name", 116 | }, 117 | } 118 | for _, tt := range tests { 119 | t.Run(tt.name, func(t *testing.T) { 120 | if got := listOfPrimaryKeys(tt.args.entity); got != tt.want { 121 | t.Errorf("listOfPrimaryKeys() = %v, want %v", got, tt.want) 122 | } 123 | }) 124 | } 125 | } 126 | 127 | func Test_sqlQueryArguments(t *testing.T) { 128 | workingDirectory, _ := os.Getwd() 129 | type args struct { 130 | entity model.Entity 131 | } 132 | tests := []struct { 133 | name string 134 | args args 135 | want string 136 | }{ 137 | { 138 | name: "Mapped primary key informations for where condition", 139 | args: args{ 140 | entity: parser.Entity{ 141 | FilePath: workingDirectory + "/testdata/user.go", 142 | }.Parse(), 143 | }, 144 | want: "qm.Where(\"id=?\", id), qm.Where(\"type=?\", _type), qm.Where(\"full_name=?\", fullName)", 145 | }, 146 | } 147 | for _, tt := range tests { 148 | t.Run(tt.name, func(t *testing.T) { 149 | if got := sqlQueryArguments(tt.args.entity); got != tt.want { 150 | t.Errorf("sqlQueryArguments() = %v, want %v", got, tt.want) 151 | } 152 | }) 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /pkg/generator/entity_parser.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import "github.com/bannzai/repository_from_sqlboiler/pkg/model" 4 | 5 | type EntityParser interface { 6 | Parse() model.Entity 7 | } 8 | -------------------------------------------------------------------------------- /pkg/generator/go.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "bytes" 5 | 6 | "github.com/bannzai/repository_from_sqlboiler/pkg/model" 7 | "github.com/bannzai/repository_from_sqlboiler/pkg/writer" 8 | ) 9 | 10 | type GoCodeGenerator struct { 11 | DestinationFilePath string 12 | EntityParser 13 | GoFormatter 14 | TemplateReader 15 | Writer writer.FileWriter 16 | } 17 | 18 | func (generator GoCodeGenerator) Generate() { 19 | entity := generator.EntityParser.Parse() 20 | content := generator.content(entity) 21 | generator.Writer.Write(content) 22 | generator.GoImports() 23 | generator.GoFormat() 24 | } 25 | 26 | func (generator GoCodeGenerator) content(entity model.Entity) string { 27 | buf := &bytes.Buffer{} 28 | if err := generator.TemplateReader.Read().Execute(buf, map[string]interface{}{ 29 | "Entity": entity, 30 | "FetchByPrimaryKey": fetchByPrimaryKey(entity), 31 | "SQLQueryArguments": sqlQueryArguments(entity), 32 | "PrimaryKeys": listOfPrimaryKeys(entity), 33 | }); err != nil { 34 | panic(err) 35 | } 36 | return buf.String() 37 | } 38 | -------------------------------------------------------------------------------- /pkg/generator/go_formatter.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | type GoFormatter interface { 4 | GoFormat() 5 | GoImports() 6 | } 7 | -------------------------------------------------------------------------------- /pkg/generator/template_reader.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import "text/template" 4 | 5 | type TemplateReader interface { 6 | Read() *template.Template 7 | } 8 | -------------------------------------------------------------------------------- /pkg/generator/testdata/user.go: -------------------------------------------------------------------------------- 1 | ../../parser/testdata/user.go -------------------------------------------------------------------------------- /pkg/model/column.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Field struct { 4 | Name string 5 | TypeName string 6 | } 7 | -------------------------------------------------------------------------------- /pkg/model/entity.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Entity struct { 4 | Name string 5 | Fields []Field 6 | PrimaryKeys []PrimaryKey 7 | } 8 | -------------------------------------------------------------------------------- /pkg/model/primary_key.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type PrimaryKey struct { 4 | Name string 5 | } 6 | -------------------------------------------------------------------------------- /pkg/parser/ast.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | "go/ast" 6 | "go/parser" 7 | "go/token" 8 | "io/ioutil" 9 | "strings" 10 | ) 11 | 12 | const ( 13 | endTraverse bool = false 14 | continueTraverse bool = true 15 | ) 16 | 17 | func parseASTFile(filePath string) *ast.File { 18 | data, err := ioutil.ReadFile(filePath) 19 | if err != nil { 20 | panic(fmt.Errorf("Can not read %v, and got error :%v", filePath, err)) 21 | } 22 | fileSet := token.NewFileSet() 23 | astFile, err := parser.ParseFile(fileSet, filePath, data, 0) 24 | if err != nil { 25 | panic(err) 26 | } 27 | return astFile 28 | } 29 | 30 | func parseASTPrimaryKeyFields(file *ast.File) []string { 31 | uniqueKeys := []string{} 32 | 33 | ast.Inspect(file, func(node ast.Node) bool { 34 | lastChildNode := node == nil 35 | if lastChildNode { 36 | return endTraverse 37 | } 38 | 39 | valueSpec, ok := node.(*ast.ValueSpec) 40 | if !ok { 41 | return continueTraverse 42 | } 43 | 44 | if len(valueSpec.Names) != 1 { 45 | return endTraverse 46 | } 47 | 48 | if !strings.HasSuffix(valueSpec.Names[0].Name, "PrimaryKeyColumns") { 49 | return endTraverse 50 | } 51 | 52 | fieldsNamesExpr, ok := valueSpec.Values[0].(*ast.CompositeLit) 53 | if !ok { 54 | return continueTraverse 55 | } 56 | 57 | for _, element := range fieldsNamesExpr.Elts { 58 | if basicLit, ok := element.(*ast.BasicLit); ok { 59 | uniqueKeys = append(uniqueKeys, strings.ReplaceAll(basicLit.Value, "\"", "")) 60 | } 61 | } 62 | 63 | return continueTraverse 64 | }) 65 | 66 | return uniqueKeys 67 | } 68 | 69 | func parseASTFieldAndType(file *ast.File, entityName string) map[string]string { 70 | fieldAndType := map[string]string{} 71 | ast.Inspect(file, func(node ast.Node) bool { 72 | lastChildNode := node == nil 73 | if lastChildNode { 74 | return endTraverse 75 | } 76 | 77 | typeSpec, ok := node.(*ast.TypeSpec) 78 | if !ok { 79 | return continueTraverse 80 | } 81 | 82 | if typeSpec.Name.Name != entityName { 83 | return continueTraverse 84 | } 85 | 86 | structType, ok := typeSpec.Type.(*ast.StructType) 87 | if !ok { 88 | return continueTraverse 89 | } 90 | 91 | for _, f := range structType.Fields.List { 92 | fieldName := f.Names[0].Name 93 | if fieldName == "R" { 94 | continue 95 | } 96 | if fieldName == "L" { 97 | continue 98 | } 99 | switch fieldType := f.Type.(type) { 100 | case *ast.Ident: 101 | fieldAndType[fieldName] = fieldType.Name 102 | case *ast.SelectorExpr: 103 | packageName, ok := fieldType.X.(*ast.Ident) 104 | if !ok { 105 | fmt.Printf("[WARNING]⚠ Unexpected field type : %v\n", f.Type) 106 | continue 107 | } 108 | selector := fieldType.Sel 109 | fieldAndType[fieldName] = packageName.Name + "." + selector.Name 110 | default: 111 | fmt.Printf("[WARNING]⚠ Unexpected field type : %v\n", f.Type) 112 | continue 113 | } 114 | } 115 | 116 | return continueTraverse 117 | }) 118 | 119 | return fieldAndType 120 | } 121 | 122 | func parseASTBaseStructName(file *ast.File) string { 123 | name := "" 124 | ast.Inspect(file, func(node ast.Node) bool { 125 | if len(name) > 0 { 126 | return endTraverse 127 | } 128 | lastChildNode := node == nil 129 | if lastChildNode { 130 | return endTraverse 131 | } 132 | 133 | genDecl, ok := node.(*ast.GenDecl) 134 | if !ok { 135 | return continueTraverse 136 | } 137 | 138 | typeSpec, ok := genDecl.Specs[0].(*ast.TypeSpec) 139 | if !ok { 140 | return continueTraverse 141 | } 142 | 143 | // FIXME: Get first typeSpec.Name.Name. But want other good idea. 144 | name = typeSpec.Name.Name 145 | return continueTraverse 146 | }) 147 | 148 | return name 149 | } 150 | -------------------------------------------------------------------------------- /pkg/parser/ast_test.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "go/ast" 5 | "os" 6 | "reflect" 7 | "testing" 8 | ) 9 | 10 | func Test_parseASTFile(t *testing.T) { 11 | type args struct { 12 | filePath string 13 | } 14 | tests := []struct { 15 | name string 16 | args args 17 | want *ast.File 18 | }{ 19 | // TODO: Add test cases. 20 | } 21 | for _, tt := range tests { 22 | t.Run(tt.name, func(t *testing.T) { 23 | if got := parseASTFile(tt.args.filePath); !reflect.DeepEqual(got, tt.want) { 24 | t.Errorf("parseASTFile() = %v, want %v", got, tt.want) 25 | } 26 | }) 27 | } 28 | } 29 | 30 | func Test_parseASTPrimaryKeyFields(t *testing.T) { 31 | workingDirectory, _ := os.Getwd() 32 | type args struct { 33 | file *ast.File 34 | } 35 | tests := []struct { 36 | name string 37 | args args 38 | want []string 39 | }{ 40 | { 41 | name: "Get primary keys from testdata/user.go User", 42 | args: args{ 43 | file: parseASTFile(workingDirectory + "/testdata/user.go"), 44 | }, 45 | want: []string{"id", "type", "full_name"}, 46 | }, 47 | } 48 | for _, tt := range tests { 49 | t.Run(tt.name, func(t *testing.T) { 50 | if got := parseASTPrimaryKeyFields(tt.args.file); !reflect.DeepEqual(got, tt.want) { 51 | t.Errorf("parseASTPrimaryKeyFields() = %v, want %v", got, tt.want) 52 | } 53 | }) 54 | } 55 | } 56 | 57 | func Test_parseASTFieldAndType(t *testing.T) { 58 | workingDirectory, _ := os.Getwd() 59 | type args struct { 60 | file *ast.File 61 | entityName string 62 | } 63 | tests := []struct { 64 | name string 65 | args args 66 | want map[string]string 67 | }{ 68 | { 69 | name: "Get fields from testdata/user.go User", 70 | args: args{ 71 | file: parseASTFile(workingDirectory + "/testdata/user.go"), 72 | entityName: "User", 73 | }, 74 | want: map[string]string{ 75 | "ID": "uint", 76 | "Type": "string", 77 | "FullName": "string", 78 | "CreatedAt": "time.Time", 79 | }, 80 | }, 81 | } 82 | for _, tt := range tests { 83 | t.Run(tt.name, func(t *testing.T) { 84 | if got := parseASTFieldAndType(tt.args.file, tt.args.entityName); !reflect.DeepEqual(got, tt.want) { 85 | t.Errorf("parseASTFieldAndType() = %v, want %v", got, tt.want) 86 | } 87 | }) 88 | } 89 | } 90 | 91 | func Test_parseASTBaseStructName(t *testing.T) { 92 | workingDirectory, _ := os.Getwd() 93 | type args struct { 94 | file *ast.File 95 | } 96 | tests := []struct { 97 | name string 98 | args args 99 | want string 100 | }{ 101 | { 102 | name: "Get entity name from testdata/user.go User", 103 | args: args{ 104 | file: parseASTFile(workingDirectory + "/testdata/user.go"), 105 | }, 106 | want: "User", 107 | }, 108 | } 109 | for _, tt := range tests { 110 | t.Run(tt.name, func(t *testing.T) { 111 | if got := parseASTBaseStructName(tt.args.file); got != tt.want { 112 | t.Errorf("parseASTBaseStructName() = %v, want %v", got, tt.want) 113 | } 114 | }) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /pkg/parser/entity.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/bannzai/repository_from_sqlboiler/pkg/model" 5 | ) 6 | 7 | type Entity struct { 8 | FilePath string 9 | } 10 | 11 | func (p Entity) Parse() model.Entity { 12 | return model.Entity{ 13 | Name: p.extractEntityName(), 14 | Fields: p.mapFields(), 15 | PrimaryKeys: p.mapPrimaryKeys(), 16 | } 17 | } 18 | 19 | func (p Entity) mapPrimaryKeys() []model.PrimaryKey { 20 | primaryKeys := []model.PrimaryKey{} 21 | for _, primaryKeyName := range parseASTPrimaryKeyFields(parseASTFile(p.FilePath)) { 22 | primaryKeys = append( 23 | primaryKeys, 24 | model.PrimaryKey{ 25 | Name: primaryKeyName, 26 | }) 27 | } 28 | return primaryKeys 29 | } 30 | 31 | func (p Entity) mapFields() []model.Field { 32 | fields := []model.Field{} 33 | for fieldName, typeName := range parseASTFieldAndType(parseASTFile(p.FilePath), p.extractEntityName()) { 34 | fields = append(fields, model.Field{ 35 | Name: fieldName, 36 | TypeName: typeName, 37 | }) 38 | } 39 | return fields 40 | } 41 | 42 | func (e Entity) extractEntityName() string { 43 | return parseASTBaseStructName(parseASTFile(e.FilePath)) 44 | } 45 | -------------------------------------------------------------------------------- /pkg/parser/file_reader.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | type FileReader interface { 4 | Read(filePath string) string 5 | } 6 | -------------------------------------------------------------------------------- /pkg/parser/testdata/user.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package entity 5 | 6 | import ( 7 | "time" 8 | ) 9 | 10 | // User is an object representing the database table. 11 | type User struct { 12 | ID uint `boil:"id" json:"id" toml:"id" yaml:"id"` 13 | Type string `boil:"type" json:"type" toml:"type" yaml:"type"` 14 | FullName string `boil:"full_name" json:"full_name" toml:"full_name" yaml:"full_name"` 15 | CreatedAt time.Time `boil:"created_at" json:"created_at" toml:"created_at" yaml:"created_at"` 16 | 17 | R *userR `boil:"-" json:"-" toml:"-" yaml:"-"` 18 | L userL `boil:"-" json:"-" toml:"-" yaml:"-"` 19 | } 20 | 21 | var ( 22 | userPrimaryKeyColumns = []string{"id", "type", "full_name"} 23 | ) 24 | 25 | type userR struct{} 26 | type userL struct{} 27 | -------------------------------------------------------------------------------- /pkg/reader/file.go: -------------------------------------------------------------------------------- 1 | package reader 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | ) 7 | 8 | type File struct{} 9 | 10 | func (File) Read(filePath string) string { 11 | data, err := ioutil.ReadFile(filePath) 12 | if err != nil { 13 | panic(fmt.Errorf("Can not read %v, and got error :%v", filePath, err)) 14 | } 15 | return string(data) 16 | } 17 | -------------------------------------------------------------------------------- /pkg/reader/template.go: -------------------------------------------------------------------------------- 1 | package reader 2 | 3 | import ( 4 | "path/filepath" 5 | "strings" 6 | "text/template" 7 | 8 | "github.com/bannzai/repository_from_sqlboiler/pkg/strutil" 9 | ) 10 | 11 | type Template struct { 12 | FilePath string 13 | } 14 | 15 | var functions = template.FuncMap{ 16 | "sqlParameterCase": strutil.SnakeCase, 17 | "golangArgumentCase": golangArgumentCase, 18 | "golangVariableCase": golangVariableCase, 19 | "golangStructNameCase": golangStructNameCase, 20 | "plural": strutil.PluralSuffix, 21 | "entitySelectorName": entitySelectorName, 22 | } 23 | 24 | // reference: https://github.com/volatiletech/sqlboiler/blob/4dea9a5f77e3ec6090ad455df3d408e47e077700/strmangle/strmangle.go#L25 25 | var lowercaseWords = map[string]string{ 26 | "ACL": "Acl", 27 | "API": "Api", 28 | "ASCII": "Ascii", 29 | "CPU": "Cpu", 30 | "EOF": "Eof", 31 | "GUID": "Guid", 32 | "ID": "Id", 33 | "IP": "Ip", 34 | "JSON": "Json", 35 | "RAM": "Ram", 36 | "SLA": "Sla", 37 | "UDP": "Udp", 38 | "UI": "Ui", 39 | "UID": "Uid", 40 | "UUID": "Uuid", 41 | "URI": "Uri", 42 | "URL": "Url", 43 | "UTF8": "Utf8", 44 | } 45 | 46 | func golangStructNameCase(entityName string) string { 47 | return entityName 48 | } 49 | 50 | func entitySelectorName(str string) string { 51 | for keyword, converted := range lowercaseWords { 52 | if strings.Contains(str, keyword) { 53 | str = strings.ReplaceAll(str, keyword, converted) 54 | } 55 | } 56 | 57 | return strutil.PluralSuffix(str) 58 | } 59 | 60 | func golangArgumentCase(str string) string { 61 | return golangVariableCase(str) 62 | } 63 | 64 | func golangVariableCase(str string) string { 65 | str = strutil.SpecializeLowerCamelCase(str) 66 | str = strutil.EscapedReservedWord(str) 67 | return str 68 | } 69 | 70 | func (t Template) Read() *template.Template { 71 | filePath := t.FilePath 72 | base := filepath.Base(filePath) 73 | return template.Must(template.New(base).Funcs(functions).ParseFiles(filePath)) 74 | } 75 | -------------------------------------------------------------------------------- /pkg/reader/template_test.go: -------------------------------------------------------------------------------- 1 | package reader 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_entitySelectorName(t *testing.T) { 8 | type args struct { 9 | str string 10 | } 11 | tests := []struct { 12 | name string 13 | args args 14 | want string 15 | }{ 16 | { 17 | name: "User to Users", 18 | args: args{ 19 | str: "User", 20 | }, 21 | want: "Users", 22 | }, 23 | { 24 | name: "CoordinateItem to CoordinateItems", 25 | args: args{ 26 | str: "CoordinateItem", 27 | }, 28 | want: "CoordinateItems", 29 | }, 30 | { 31 | name: "XyzJSON to XyzJsons", 32 | args: args{ 33 | str: "XyzJSON", 34 | }, 35 | want: "XyzJsons", 36 | }, 37 | { 38 | name: "XJSON to XJsons", 39 | args: args{ 40 | str: "XJSON", 41 | }, 42 | want: "XJsons", 43 | }, 44 | } 45 | for _, tt := range tests { 46 | t.Run(tt.name, func(t *testing.T) { 47 | if got := entitySelectorName(tt.args.str); got != tt.want { 48 | t.Errorf("entitySelectorName() = %v, want %v", got, tt.want) 49 | } 50 | }) 51 | } 52 | } 53 | 54 | func Test_golangArgumentCase(t *testing.T) { 55 | type args struct { 56 | str string 57 | } 58 | tests := []struct { 59 | name string 60 | args args 61 | want string 62 | }{ 63 | // TODO: Add test cases. 64 | } 65 | for _, tt := range tests { 66 | t.Run(tt.name, func(t *testing.T) { 67 | if got := golangArgumentCase(tt.args.str); got != tt.want { 68 | t.Errorf("golangArgumentCase() = %v, want %v", got, tt.want) 69 | } 70 | }) 71 | } 72 | } 73 | 74 | func Test_golangVariableCase(t *testing.T) { 75 | type args struct { 76 | str string 77 | } 78 | tests := []struct { 79 | name string 80 | args args 81 | want string 82 | }{ 83 | // TODO: Add test cases. 84 | } 85 | for _, tt := range tests { 86 | t.Run(tt.name, func(t *testing.T) { 87 | if got := golangVariableCase(tt.args.str); got != tt.want { 88 | t.Errorf("golangVariableCase() = %v, want %v", got, tt.want) 89 | } 90 | }) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /pkg/strutil/name.go: -------------------------------------------------------------------------------- 1 | package strutil 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | ) 7 | 8 | func SnakeCase(str string) string { 9 | matchFirstCap := regexp.MustCompile("(.)([A-Z][a-z]+)") 10 | matchAllCap := regexp.MustCompile("([a-z0-9])([A-Z])") 11 | snake := matchFirstCap.ReplaceAllString(str, "${1}_${2}") 12 | snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}") 13 | return strings.ToLower(snake) 14 | } 15 | 16 | // To Lower Camel case 17 | func camelCase(str string) string { 18 | if len(str) <= 1 { 19 | return strings.ToLower(str) 20 | } 21 | firstString := strings.ToLower(str[:1]) 22 | droppedFirstString := str[1:] 23 | matchRegex := regexp.MustCompile("(_[a-zA-Z])") 24 | tailContent := matchRegex.ReplaceAllStringFunc(droppedFirstString, func(matched string) string { 25 | underscore := "_" 26 | return strings.ToUpper(matched[len(underscore):]) 27 | }) 28 | return firstString + tailContent 29 | } 30 | 31 | func LowerCamelCase(str string) string { 32 | return camelCase(str) 33 | } 34 | 35 | func UpperCamelCase(target string) string { 36 | firstString := strings.ToUpper(target[:1]) 37 | tailContent := camelCase(target[1:]) 38 | return firstString + tailContent 39 | } 40 | 41 | var ( 42 | lowerSpecializeKeywords = []string{"url", "os", "id", "uid"} 43 | upperSpecializeKeywords = func() []string { 44 | uppers := make([]string, len(lowerSpecializeKeywords)) 45 | for i, lower := range lowerSpecializeKeywords { 46 | uppers[i] = strings.ToUpper(lower) 47 | } 48 | return uppers 49 | }() 50 | reservedWords = []string{"type"} 51 | ) 52 | 53 | func EscapedReservedWord(str string) string { 54 | for _, word := range reservedWords { 55 | if word == str { 56 | str = "_" + str 57 | } 58 | } 59 | return str 60 | } 61 | 62 | func SpecializeLowerCamelCase(str string) string { 63 | for _, keyword := range upperSpecializeKeywords { 64 | if keyword == str { 65 | return strings.ToLower(str) 66 | } 67 | } 68 | return LowerCamelCase(str) 69 | } 70 | 71 | func SpecializeUpperCamelCase(str string) string { 72 | for _, keyword := range lowerSpecializeKeywords { 73 | if keyword == str { 74 | return strings.ToUpper(str) 75 | } 76 | } 77 | return UpperCamelCase(str) 78 | } 79 | -------------------------------------------------------------------------------- /pkg/strutil/name_test.go: -------------------------------------------------------------------------------- 1 | package strutil 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestSnakeCase(t *testing.T) { 8 | type args struct { 9 | str string 10 | } 11 | tests := []struct { 12 | name string 13 | args args 14 | want string 15 | }{ 16 | { 17 | name: "lcc to snake", 18 | args: args{ 19 | str: "userRepository", 20 | }, 21 | want: "user_repository", 22 | }, 23 | { 24 | name: "ucc to snake", 25 | args: args{ 26 | str: "UserRepository", 27 | }, 28 | want: "user_repository", 29 | }, 30 | } 31 | for _, tt := range tests { 32 | t.Run(tt.name, func(t *testing.T) { 33 | if got := SnakeCase(tt.args.str); got != tt.want { 34 | t.Errorf("SnakeCase() = %v, want %v", got, tt.want) 35 | } 36 | }) 37 | } 38 | } 39 | 40 | func Test_camelCase(t *testing.T) { 41 | type args struct { 42 | str string 43 | } 44 | tests := []struct { 45 | name string 46 | args args 47 | want string 48 | }{ 49 | { 50 | name: "ucc to lcc", 51 | args: args{ 52 | str: "UserRepository", 53 | }, 54 | want: "userRepository", 55 | }, 56 | { 57 | name: "snake to lcc", 58 | args: args{ 59 | str: "user_repository", 60 | }, 61 | want: "userRepository", 62 | }, 63 | } 64 | for _, tt := range tests { 65 | t.Run(tt.name, func(t *testing.T) { 66 | if got := camelCase(tt.args.str); got != tt.want { 67 | t.Errorf("camelCase() = %v, want %v", got, tt.want) 68 | } 69 | }) 70 | } 71 | } 72 | 73 | func TestLowerCamelCase(t *testing.T) { 74 | type args struct { 75 | str string 76 | } 77 | tests := []struct { 78 | name string 79 | args args 80 | want string 81 | }{ 82 | { 83 | name: "ucc to lcc", 84 | args: args{ 85 | str: "UserRepository", 86 | }, 87 | want: "userRepository", 88 | }, 89 | { 90 | name: "snake to lcc", 91 | args: args{ 92 | str: "user_repository", 93 | }, 94 | want: "userRepository", 95 | }, 96 | } 97 | for _, tt := range tests { 98 | t.Run(tt.name, func(t *testing.T) { 99 | if got := LowerCamelCase(tt.args.str); got != tt.want { 100 | t.Errorf("LowerCamelCase() = %v, want %v", got, tt.want) 101 | } 102 | }) 103 | } 104 | } 105 | 106 | func TestUpperCamelCase(t *testing.T) { 107 | type args struct { 108 | target string 109 | } 110 | tests := []struct { 111 | name string 112 | args args 113 | want string 114 | }{ 115 | { 116 | name: "lcc to ucc", 117 | args: args{ 118 | target: "userRepository", 119 | }, 120 | want: "UserRepository", 121 | }, 122 | { 123 | name: "snake to ucc", 124 | args: args{ 125 | target: "user_repository", 126 | }, 127 | want: "UserRepository", 128 | }, 129 | } 130 | for _, tt := range tests { 131 | t.Run(tt.name, func(t *testing.T) { 132 | if got := UpperCamelCase(tt.args.target); got != tt.want { 133 | t.Errorf("UpperCamelCase() = %v, want %v", got, tt.want) 134 | } 135 | }) 136 | } 137 | } 138 | 139 | func TestSpecializeLowerCamelCase(t *testing.T) { 140 | type args struct { 141 | str string 142 | } 143 | tests := []struct { 144 | name string 145 | args args 146 | want string 147 | }{ 148 | { 149 | name: "URL to url", 150 | args: args{ 151 | str: "URL", 152 | }, 153 | want: "url", 154 | }, 155 | } 156 | for _, tt := range tests { 157 | t.Run(tt.name, func(t *testing.T) { 158 | if got := SpecializeLowerCamelCase(tt.args.str); got != tt.want { 159 | t.Errorf("SpecializeLowerCamelCase() = %v, want %v", got, tt.want) 160 | } 161 | }) 162 | } 163 | } 164 | 165 | func TestSpecializeUpperCamelCase(t *testing.T) { 166 | type args struct { 167 | str string 168 | } 169 | tests := []struct { 170 | name string 171 | args args 172 | want string 173 | }{ 174 | { 175 | name: "url to URL", 176 | args: args{ 177 | str: "url", 178 | }, 179 | want: "URL", 180 | }, 181 | } 182 | for _, tt := range tests { 183 | t.Run(tt.name, func(t *testing.T) { 184 | if got := SpecializeUpperCamelCase(tt.args.str); got != tt.want { 185 | t.Errorf("SpecializeUpperCamelCase() = %v, want %v", got, tt.want) 186 | } 187 | }) 188 | } 189 | } 190 | 191 | func Test_EscapedReservedWord(t *testing.T) { 192 | type args struct { 193 | str string 194 | } 195 | tests := []struct { 196 | name string 197 | args args 198 | want string 199 | }{ 200 | { 201 | name: "Add _ for Reserved word", 202 | args: args{ 203 | str: "type", 204 | }, 205 | want: "_type", 206 | }, 207 | { 208 | name: "Not convert keyword", 209 | args: args{ 210 | str: "xx", 211 | }, 212 | want: "xx", 213 | }, 214 | } 215 | for _, tt := range tests { 216 | t.Run(tt.name, func(t *testing.T) { 217 | if got := EscapedReservedWord(tt.args.str); got != tt.want { 218 | t.Errorf("EscapedReservedWord() = %v, want %v", got, tt.want) 219 | } 220 | }) 221 | } 222 | } 223 | 224 | func TestEscapedReservedWord(t *testing.T) { 225 | type args struct { 226 | str string 227 | } 228 | tests := []struct { 229 | name string 230 | args args 231 | want string 232 | }{ 233 | // TODO: Add test cases. 234 | } 235 | for _, tt := range tests { 236 | t.Run(tt.name, func(t *testing.T) { 237 | if got := EscapedReservedWord(tt.args.str); got != tt.want { 238 | t.Errorf("EscapedReservedWord() = %v, want %v", got, tt.want) 239 | } 240 | }) 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /pkg/strutil/plural.go: -------------------------------------------------------------------------------- 1 | package strutil 2 | 3 | import ( 4 | "regexp" 5 | "strconv" 6 | ) 7 | 8 | // NOTE: reference https://github.com/volatiletech/inflect/blob/e7201282ae8da26cd97aed2e516f75c0bd91bb93/inflect_test.go#L17 9 | var SingularToPlural = map[string]string{ 10 | "datum": "data", 11 | } 12 | 13 | func PluralSuffix(str string) string { 14 | // specialize Rule 15 | ruleSets := SingularToPlural 16 | for key, converted := range ruleSets { 17 | if len(key) > len(str) { 18 | continue 19 | } 20 | if len(key) <= 0 { 21 | continue 22 | } 23 | if key == str[len(str)-len(key):] { 24 | replaced := str[:len(str)-len(key)] + converted 25 | return replaced 26 | } 27 | if UpperCamelCase(key) == str[len(str)-len(key):] { 28 | replaced := str[:len(str)-len(key)] + UpperCamelCase(converted) 29 | return replaced 30 | } 31 | } 32 | // normalize convert plural 33 | if len(str) < 2 { 34 | return str 35 | } 36 | 37 | if _, err := strconv.Atoi(str[len(str)-1:]); err == nil { 38 | return str + "S" 39 | } 40 | 41 | if str[len(str)-1:] == "s" || 42 | str[len(str)-2:] == "sh" || 43 | str[len(str)-2:] == "ch" || 44 | str[len(str)-1:] == "o" || 45 | str[len(str)-1:] == "x" { 46 | return str + "es" 47 | } 48 | if str[len(str)-1:] == "f" { 49 | return str[0:len(str)-1] + "ves" 50 | } 51 | if str[len(str)-2:] == "fe" { 52 | return str[0:len(str)-2] + "ves" 53 | } 54 | isExists, _ := regexp.MatchString(".+[^aiueo]y$", str) 55 | if isExists { 56 | return str[0:len(str)-1] + "ies" 57 | } 58 | 59 | return str + "s" 60 | } 61 | -------------------------------------------------------------------------------- /pkg/strutil/plural_test.go: -------------------------------------------------------------------------------- 1 | package strutil 2 | 3 | import "testing" 4 | 5 | func TestPluralSuffix(t *testing.T) { 6 | type args struct { 7 | str string 8 | } 9 | tests := []struct { 10 | name string 11 | args args 12 | want string 13 | }{ 14 | { 15 | name: "user to users", 16 | args: args{ 17 | str: "user", 18 | }, 19 | want: "users", 20 | }, 21 | { 22 | name: "community to communities", 23 | args: args{ 24 | str: "community", 25 | }, 26 | want: "communities", 27 | }, 28 | { 29 | name: "datum to data", 30 | args: args{ 31 | str: "datum", 32 | }, 33 | want: "data", 34 | }, 35 | { 36 | name: "Datum to Data", 37 | args: args{ 38 | str: "Datum", 39 | }, 40 | want: "Data", 41 | }, 42 | { 43 | name: "ganma to ganmas", 44 | args: args{ 45 | str: "ganma", 46 | }, 47 | want: "ganmas", 48 | }, 49 | { 50 | name: "Ganma to Ganmas", 51 | args: args{ 52 | str: "Ganma", 53 | }, 54 | want: "Ganmas", 55 | }, 56 | { 57 | name: "JSON to JSONs", 58 | args: args{ 59 | str: "JSON", 60 | }, 61 | want: "JSONs", 62 | }, 63 | { 64 | name: "A1 to A1S", 65 | args: args{ 66 | str: "A1", 67 | }, 68 | want: "A1S", 69 | }, 70 | } 71 | for _, tt := range tests { 72 | t.Run(tt.name, func(t *testing.T) { 73 | if got := PluralSuffix(tt.args.str); got != tt.want { 74 | t.Errorf("PluralSuffix() = %v, want %v", got, tt.want) 75 | } 76 | }) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /pkg/writer/file.go: -------------------------------------------------------------------------------- 1 | package writer 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | ) 8 | 9 | type File struct { 10 | FilePath string 11 | } 12 | 13 | type FileWriter interface { 14 | Write(content string) 15 | } 16 | 17 | func (f File) Write(content string) { 18 | if err := ioutil.WriteFile(f.FilePath, []byte(content), 0644); err != nil { 19 | panic(err) 20 | } 21 | fmt.Fprintf(os.Stdout, "Generated %s \n", f.FilePath) 22 | } 23 | -------------------------------------------------------------------------------- /script/development/generate.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | set -eu 3 | set -o pipefail 4 | 5 | PWD=`dirname $0` 6 | APP_DIR="$PWD/../../" 7 | cd $APP_DIR 8 | 9 | ./repository_from_sqlboiler generate --source=local/source/source.go --destination=local/destination/repository.go --template=configs/template/repository.go.tpl 10 | --------------------------------------------------------------------------------