From 312f37f563224febae216717f4b1a1a8d300ca88 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 28 Dec 2019 16:54:49 +0800 Subject: [PATCH] init project --- .drone.yml | 9 + .gitignore | 1 + LICENSE | 27 +++ README.md | 83 +++++++++ README_CN.md | 83 +++++++++ cmd/reverse.go | 328 +++++++++++++++++++++++++++++++++++ cmd/reverse_test.go | 33 ++++ cmd/root.go | 49 ++++++ example/custom.yml | 35 ++++ example/goxorm.yml | 14 ++ example/template/go.tmpl | 16 ++ example/template/goxorm.tmpl | 16 ++ go.mod | 17 ++ go.sum | 176 +++++++++++++++++++ language/golang.go | 233 +++++++++++++++++++++++++ language/language.go | 35 ++++ main.go | 17 ++ models/models.go | 9 + testdata/test.db | Bin 0 -> 20480 bytes 19 files changed, 1181 insertions(+) create mode 100644 .drone.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 README_CN.md create mode 100644 cmd/reverse.go create mode 100644 cmd/reverse_test.go create mode 100644 cmd/root.go create mode 100644 example/custom.yml create mode 100644 example/goxorm.yml create mode 100644 example/template/go.tmpl create mode 100644 example/template/goxorm.tmpl create mode 100644 go.mod create mode 100644 go.sum create mode 100644 language/golang.go create mode 100644 language/language.go create mode 100644 main.go create mode 100644 models/models.go create mode 100644 testdata/test.db diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..a4f6fe2 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,9 @@ +kind: pipeline +name: default + +steps: +- name: test + image: golang:1.12 + commands: + - go build + - go test -v -race -coverprofile=coverage.txt -covermode=atomic ./... \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2f63e9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +reverse \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7db882c --- /dev/null +++ b/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2019 The Xorm Authors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the {organization} nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ccee86e --- /dev/null +++ b/README.md @@ -0,0 +1,83 @@ +# Reverse + +A flexsible and powerful command line tool to convert database to codes. + +## Installation + +``` +go get xorm.io/reverse +``` + +## Usage + +``` +reverse -f example/custom.yml +``` + +## Configuration File + +How does the simplest configuration file look like? + +```yml +kind: reverse +name: mydb +source: + database: sqlite3 + conn_str: '../testdata/test.db' +targets: +- type: codes + language: golang + output_dir: ../models +``` + +A `language` defines some default configuration items, also you can define all yourselves. + +```yml +kind: reverse +name: mydb +source: + database: sqlite + conn_str: ../testdata/test.db +targets: +- type: codes + include_tables: # tables included, you can use ** + - a + - b + exclude_tables: # tables excluded, you can use ** + - c + table_mapper: snake # how table name map to class or struct name + column_mapper: snake # how column name map to class or struct field name + table_prefix: "" # table prefix + multiple_files: true # generate multiple files or one + template: | # template for code file, it has higher perior than template_path + package models + + {{$ilen := len .Imports}} + {{if gt $ilen 0}} + import ( + {{range .Imports}}"{{.}}"{{end}} + ) + {{end}} + + {{range .Tables}} + type {{TableMapper .Name}} struct { + {{$table := .}} + {{range .ColumnsSeq}}{{$col := $table.GetColumn .}} {{ColumnMapper $col.Name}} {{Type $col}} `{{Tag $table $col}}` + {{end}} + } + {{end}} + template_path: ./template/goxorm.tmpl # template path for code file, it has higher perior than template field on language + output_dir: ./models # code output directory +``` + +## Template Funcs + +- *UnTitle*: Convert first charator of the word to lower. +- *Upper*: Convert word to all upper. +- *TableMapper*: Mapper method to convert table name to class/struct name. +- *ColumnMapper*: Mapper method to convert column name to class/struct field name. + +## Template Vars + +- *Tables*: All tables. +- *Imports*: All imports needed. \ No newline at end of file diff --git a/README_CN.md b/README_CN.md new file mode 100644 index 0000000..abd9e63 --- /dev/null +++ b/README_CN.md @@ -0,0 +1,83 @@ +# Reverse + +一个灵活高效的数据库反转工具。 + +## 安装 + +``` +go get xorm.io/reverse +``` + +## 使用 + +``` +reverse -f example/custom.yml +``` + +## 配置文件 + +一个最简单的配置文件看起来如下: + +```yml +kind: reverse +name: mydb +source: + database: sqlite3 + conn_str: '../testdata/test.db' +targets: +- type: codes + language: golang + output_dir: ../models +``` + +`language` 定义了很多默认的配置,你也可以自己来进行配置。其中的模板是 Go 模板语法。 + +```yml +kind: reverse +name: mydb +source: + database: sqlite + conn_str: ../testdata/test.db +targets: +- type: codes + include_tables: # 包含的表,以下可以用 ** + - a + - b + exclude_tables: # 排除的表,以下可以用 ** + - c + table_mapper: snake # 表名到代码类或结构体的映射关系 + column_mapper: snake # 字段名到代码或结构体成员的映射关系 + table_prefix: "" # 表前缀 + multiple_files: true # 是否生成多个文件 + template: | # 生成模板,如果这里定义了,优先级比 template_path 高 + package models + + {{$ilen := len .Imports}} + {{if gt $ilen 0}} + import ( + {{range .Imports}}"{{.}}"{{end}} + ) + {{end}} + + {{range .Tables}} + type {{TableMapper .Name}} struct { + {{$table := .}} + {{range .ColumnsSeq}}{{$col := $table.GetColumn .}} {{ColumnMapper $col.Name}} {{Type $col}} `{{Tag $table $col}}` + {{end}} + } + {{end}} + template_path: ./template/goxorm.tmpl # 生成的模板的路径,优先级比 template 低,但比 language 中的默认模板高 + output_dir: ./models # 代码生成目录 +``` + +## 模板函数 + +- *UnTitle*: 将单词的第一个字母大写。 +- *Upper*: 将单词转为全部大写。 +- *TableMapper*: 将表名转为结构体名的映射函数。 +- *ColumnMapper*: 将字段名转为结构体成员名的函数。 + +## 模板变量 + +- *Tables*: 所有表。 +- *Imports*: 所有需要的导入。 \ No newline at end of file diff --git a/cmd/reverse.go b/cmd/reverse.go new file mode 100644 index 0000000..73d6a86 --- /dev/null +++ b/cmd/reverse.go @@ -0,0 +1,328 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "bytes" + "errors" + "html/template" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "xorm.io/reverse/language" + + "gitea.com/lunny/log" + _ "github.com/denisenkom/go-mssqldb" + _ "github.com/go-sql-driver/mysql" + "github.com/gobwas/glob" + _ "github.com/lib/pq" + _ "github.com/mattn/go-sqlite3" + "gopkg.in/yaml.v2" + "xorm.io/core" + "xorm.io/xorm" +) + +func reverse(rFile string) error { + f, err := os.Open(rFile) + if err != nil { + return err + } + defer f.Close() + + var cfg ReverseConfig + err = yaml.NewDecoder(f).Decode(&cfg) + if err != nil { + return err + } + for _, target := range cfg.Targets { + if err := runReverse(&cfg.Source, &target); err != nil { + return err + } + } + + return nil +} + +type ReverseSource struct { + Database string `yaml:"database"` + ConnStr string `yaml:"conn_str"` +} + +// ReverseTarget represents a reverse target +type ReverseTarget struct { + Type string `yaml:"type"` + IncludeTables []string `yaml:"include_tables"` + ExcludeTables []string `yaml:"exclude_tables"` + TableMapper string `yaml:"table_mapper"` + ColumnMapper string `yaml:"column_mapper"` + TemplatePath string `yaml:"template_path"` + Template string `yaml:"template"` + MultipleFiles bool `yaml:"multiple_files"` + OutputDir string `yaml:"output_dir"` + TablePrefix string `yaml:"table_prefix"` + Language string `yaml:"language"` + + Funcs map[string]string `yaml:"funcs"` + Formatter string `yaml:"formatter"` + Importter string `yaml:"importter"` + ExtName string `yaml:"ext_name"` +} + +// ReverseConfig represents a reverse configuration +type ReverseConfig struct { + Kind string `yaml:"kind"` + Name string `yaml:"name"` + Source ReverseSource `yaml:"source"` + Targets []ReverseTarget `yaml:"targets"` +} + +var ( + formatters = map[string]func(string) (string, error){} + importters = map[string]func([]*core.Table) []string{} + defaultFuncs = template.FuncMap{ + "UnTitle": unTitle, + "Upper": upTitle, + } +) + +func unTitle(src string) string { + if src == "" { + return "" + } + + if len(src) == 1 { + return strings.ToLower(string(src[0])) + } else { + return strings.ToLower(string(src[0])) + src[1:] + } +} + +func upTitle(src string) string { + if src == "" { + return "" + } + + return strings.ToUpper(src) +} + +func filterTables(tables []*core.Table, target *ReverseTarget) []*core.Table { + var res = make([]*core.Table, 0, len(tables)) + for _, tb := range tables { + var remove bool + for _, exclude := range target.ExcludeTables { + s, _ := glob.Compile(exclude) + remove = s.Match(tb.Name) + if remove { + break + } + } + if remove { + continue + } + if len(target.IncludeTables) == 0 { + res = append(res, tb) + continue + } + + var keep bool + for _, include := range target.IncludeTables { + s, _ := glob.Compile(include) + keep = s.Match(tb.Name) + if keep { + break + } + } + if keep { + res = append(res, tb) + } + } + return res +} + +func newFuncs() template.FuncMap { + var m = make(template.FuncMap) + for k, v := range defaultFuncs { + m[k] = v + } + return m +} + +func runReverse(source *ReverseSource, target *ReverseTarget) error { + orm, err := xorm.NewEngine(source.Database, source.ConnStr) + if err != nil { + return err + } + + tables, err := orm.DBMetas() + if err != nil { + return err + } + + // filter tables according includes and excludes + tables = filterTables(tables, target) + + // load configuration from language + lang := language.GetLanguage(target.Language) + funcs := newFuncs() + formatter := formatters[target.Formatter] + importter := importters[target.Importter] + + // load template + var bs []byte + if target.Template != "" { + bs = []byte(target.Template) + } else if target.TemplatePath != "" { + bs, err = ioutil.ReadFile(target.TemplatePath) + if err != nil { + return err + } + } + + if lang != nil { + if bs == nil { + bs = []byte(lang.Template) + } + for k, v := range lang.Funcs { + funcs[k] = v + } + if formatter == nil { + formatter = lang.Formatter + } + if importter == nil { + importter = lang.Importter + } + target.ExtName = lang.ExtName + } + if !strings.HasPrefix(target.ExtName, ".") { + target.ExtName = "." + target.ExtName + } + + var tableMapper, colMapper core.IMapper + switch target.TableMapper { + case "gonic": + tableMapper = core.LintGonicMapper + case "same": + tableMapper = core.SameMapper{} + default: + tableMapper = core.SnakeMapper{} + } + switch target.ColumnMapper { + case "gonic": + colMapper = core.LintGonicMapper + case "same": + colMapper = core.SameMapper{} + default: + colMapper = core.SnakeMapper{} + } + funcs["TableMapper"] = tableMapper.Table2Obj + funcs["ColumnMapper"] = colMapper.Table2Obj + + if bs == nil { + return errors.New("You have to indicate template / template path or a language") + } + + t := template.New("reverse") + t.Funcs(funcs) + + tmpl, err := t.Parse(string(bs)) + if err != nil { + return err + } + + for _, table := range tables { + if target.TablePrefix != "" { + table.Name = strings.TrimPrefix(table.Name, target.TablePrefix) + } + for _, col := range table.Columns() { + col.FieldName = colMapper.Table2Obj(col.Name) + } + } + + err = os.MkdirAll(target.OutputDir, os.ModePerm) + if err != nil { + return err + } + + var w *os.File + if !target.MultipleFiles { + w, err = os.Create(filepath.Join(target.OutputDir, "models"+target.ExtName)) + if err != nil { + return err + } + defer w.Close() + + imports := importter(tables) + + newbytes := bytes.NewBufferString("") + err = tmpl.Execute(newbytes, map[string]interface{}{ + "Tables": tables, + "Imports": imports, + }) + if err != nil { + return err + } + + tplcontent, err := ioutil.ReadAll(newbytes) + if err != nil { + return err + } + var source string + if formatter != nil { + source, err = formatter(string(tplcontent)) + if err != nil { + log.Warnf("%v", err) + source = string(tplcontent) + } + } else { + source = string(tplcontent) + } + + w.WriteString(source) + w.Close() + } else { + for _, table := range tables { + // imports + tbs := []*core.Table{table} + imports := importter(tbs) + + w, err := os.Create(filepath.Join(target.OutputDir, table.Name+target.ExtName)) + if err != nil { + return err + } + defer w.Close() + + newbytes := bytes.NewBufferString("") + err = tmpl.Execute(newbytes, map[string]interface{}{ + "Tables": tbs, + "Imports": imports, + }) + if err != nil { + return err + } + + tplcontent, err := ioutil.ReadAll(newbytes) + if err != nil { + return err + } + var source string + if formatter != nil { + source, err = formatter(string(tplcontent)) + if err != nil { + log.Warnf("%v", err) + source = string(tplcontent) + } + } else { + source = string(tplcontent) + } + + w.WriteString(source) + w.Close() + } + } + + return nil +} diff --git a/cmd/reverse_test.go b/cmd/reverse_test.go new file mode 100644 index 0000000..85326fe --- /dev/null +++ b/cmd/reverse_test.go @@ -0,0 +1,33 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "fmt" + "io/ioutil" + "testing" + + "github.com/stretchr/testify/assert" +) + +var result = fmt.Sprintf(`package models + +type A struct { + Id int %sxorm:"integer"%s +} + +type B struct { + Id int %sxorm:"INTEGER"%s +} +`, "`", "`", "`", "`") + +func TestReverse(t *testing.T) { + err := reverse("../example/goxorm.yml") + assert.NoError(t, err) + + bs, err := ioutil.ReadFile("../models/models.go") + assert.NoError(t, err) + assert.EqualValues(t, result, string(bs)) +} diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..05b2c39 --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,49 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +var ( + Version = "0.1+dev" + + reverseFile string + versionFlag *bool + + rootCmd = &cobra.Command{ + Version: Version, + Use: "reverse", + Short: "Reverse is a database reverse command line tool", + Long: `A flexsible and powerful command line tool to generate codes/docs from databases(SQLITE/Mysql/Postgres/MSSQL)`, + Run: func(cmd *cobra.Command, args []string) { + if versionFlag != nil && *versionFlag { + fmt.Printf("Reverse %s\n", Version) + return + } + if reverseFile == "" { + fmt.Println("Need reverse file") + return + } + + err := reverse(reverseFile) + if err != nil { + fmt.Println(err) + } + }, + } +) + +func init() { + versionFlag = rootCmd.Flags().BoolP("version", "v", false, "version of the tool") + rootCmd.Flags().StringVarP(&reverseFile, "file", "f", "", "yml file to apply for reverse") +} + +func Execute() error { + return rootCmd.Execute() +} diff --git a/example/custom.yml b/example/custom.yml new file mode 100644 index 0000000..d37c3f2 --- /dev/null +++ b/example/custom.yml @@ -0,0 +1,35 @@ +kind: reverse +name: mydb +source: + database: sqlite + conn_str: ../testdata/test.db +targets: +- type: codes + include_tables: + - a + - b + exclude_tables: + - c + table_mapper: snake + column_mapper: snake + table_prefix: "" + multiple_files: true + template: | + package models + + {{$ilen := len .Imports}} + {{if gt $ilen 0}} + import ( + {{range .Imports}}"{{.}}"{{end}} + ) + {{end}} + + {{range .Tables}} + type {{TableMapper .Name}} struct { + {{$table := .}} + {{range .ColumnsSeq}}{{$col := $table.GetColumn .}} {{ColumnMapper $col.Name}} {{Type $col}} `{{Tag $table $col}}` + {{end}} + } + {{end}} + template_path: ./template/goxorm.tmpl + output_dir: ./models \ No newline at end of file diff --git a/example/goxorm.yml b/example/goxorm.yml new file mode 100644 index 0000000..d16f424 --- /dev/null +++ b/example/goxorm.yml @@ -0,0 +1,14 @@ +kind: reverse +name: mydb +source: + database: sqlite3 + conn_str: '../testdata/test.db' +targets: +- type: codes + include_tables: + - a + - b + exclude_tables: + - c + language: golang + output_dir: ../models \ No newline at end of file diff --git a/example/template/go.tmpl b/example/template/go.tmpl new file mode 100644 index 0000000..9193036 --- /dev/null +++ b/example/template/go.tmpl @@ -0,0 +1,16 @@ +package models + +{{$ilen := len .Imports}} +{{if gt $ilen 0}} +import ( + {{range .Imports}}"{{.}}"{{end}} +) +{{end}} + +{{range .Tables}} +type {{TableMapper .Name}} struct { +{{$table := .}} +{{range .ColumnsSeq}}{{$col := $table.GetColumn .}} {{ColumnMapper $col.Name}} {{Type $col}} +{{end}} +} +{{end}} \ No newline at end of file diff --git a/example/template/goxorm.tmpl b/example/template/goxorm.tmpl new file mode 100644 index 0000000..867d653 --- /dev/null +++ b/example/template/goxorm.tmpl @@ -0,0 +1,16 @@ +package models + +{{$ilen := len .Imports}} +{{if gt $ilen 0}} +import ( + {{range .Imports}}"{{.}}"{{end}} +) +{{end}} + +{{range .Tables}} +type {{TableMapper .Name}} struct { +{{$table := .}} +{{range .ColumnsSeq}}{{$col := $table.GetColumn .}} {{ColumnMapper $col.Name}} {{Type $col}} `{{Tag $table $col}}` +{{end}} +} +{{end}} \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c7e801e --- /dev/null +++ b/go.mod @@ -0,0 +1,17 @@ +module xorm.io/reverse + +go 1.13 + +require ( + gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e + github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4 + github.com/go-sql-driver/mysql v1.4.1 + github.com/gobwas/glob v0.2.3 + github.com/lib/pq v1.0.0 + github.com/mattn/go-sqlite3 v1.10.0 + github.com/spf13/cobra v0.0.5 + github.com/stretchr/testify v1.4.0 + gopkg.in/yaml.v2 v2.2.2 + xorm.io/core v0.7.2 + xorm.io/xorm v0.8.1 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..40deebb --- /dev/null +++ b/go.sum @@ -0,0 +1,176 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU= +cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= +gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e h1:r1en/D7xJmcY24VkHkjkcJFa+7ZWubVWPBrvsHkmHxk= +gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e/go.mod h1:uJEsN4LQpeGYRCjuPXPZBClU7N5pWzGuyF4uqLpE/e0= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4 h1:YcpmyvADGYw5LqMnHqSkyIELsHCGF6PkrmM31V8rF7o= +github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-flutter-desktop/hover v0.37.0 h1:sO6M+HpegV3u2ihKtlyF4Xlc1xR54iUdqC2LIMs0sWc= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= +github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= +github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8= +xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU= +xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw= +xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM= +xorm.io/xorm v0.8.1 h1:4f2KXuQxVdaX3RdI3Fw81NzMiSpZeyCZt8m3sEVeIkQ= +xorm.io/xorm v0.8.1/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY= diff --git a/language/golang.go b/language/golang.go new file mode 100644 index 0000000..7c8404a --- /dev/null +++ b/language/golang.go @@ -0,0 +1,233 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package language + +import ( + "errors" + "fmt" + "go/format" + "html/template" + "reflect" + "sort" + "strings" + + "xorm.io/core" +) + +// Golang represents a golang language +var Golang = Language{ + Name: "golang", + Template: defaultGolangTemplate, + Types: map[string]string{}, + Funcs: template.FuncMap{ + "Type": typestring, + "Tag": tag, + }, + Formatter: formatGo, + Importter: genGoImports, + ExtName: ".go", +} + +func init() { + RegisterLanguage(&Golang) +} + +var ( + errBadComparisonType = errors.New("invalid type for comparison") + errBadComparison = errors.New("incompatible types for comparison") + errNoComparison = errors.New("missing argument for comparison") + defaultGolangTemplate = fmt.Sprintf(`package models + +{{$ilen := len .Imports}}{{if gt $ilen 0}}import ( + {{range .Imports}}"{{.}}"{{end}} +){{end}} + +{{range .Tables}} +type {{TableMapper .Name}} struct { +{{$table := .}}{{range .ColumnsSeq}}{{$col := $table.GetColumn .}} {{ColumnMapper $col.Name}} {{Type $col}} %s{{Tag $table $col}}%s +{{end}} +} +{{end}} +`, "`", "`") +) + +type kind int + +const ( + invalidKind kind = iota + boolKind + complexKind + intKind + floatKind + integerKind + stringKind + uintKind +) + +func basicKind(v reflect.Value) (kind, error) { + switch v.Kind() { + case reflect.Bool: + return boolKind, nil + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return intKind, nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return uintKind, nil + case reflect.Float32, reflect.Float64: + return floatKind, nil + case reflect.Complex64, reflect.Complex128: + return complexKind, nil + case reflect.String: + return stringKind, nil + } + return invalidKind, errBadComparisonType +} + +func getCol(cols map[string]*core.Column, name string) *core.Column { + return cols[strings.ToLower(name)] +} + +func formatGo(src string) (string, error) { + source, err := format.Source([]byte(src)) + if err != nil { + return "", err + } + return string(source), nil +} + +func genGoImports(tables []*core.Table) []string { + imports := make(map[string]string) + results := make([]string, 0) + for _, table := range tables { + for _, col := range table.Columns() { + if typestring(col) == "time.Time" { + if _, ok := imports["time"]; !ok { + imports["time"] = "time" + results = append(results, "time") + } + } + } + } + return results +} + +func typestring(col *core.Column) string { + st := col.SQLType + t := core.SQLType2Type(st) + s := t.String() + if s == "[]uint8" { + return "[]byte" + } + return s +} + +func tag(table *core.Table, col *core.Column) template.HTML { + isNameId := col.FieldName == "Id" + isIdPk := isNameId && typestring(col) == "int64" + + var res []string + if !col.Nullable { + if !isIdPk { + res = append(res, "not null") + } + } + if col.IsPrimaryKey { + res = append(res, "pk") + } + if col.Default != "" { + res = append(res, "default "+col.Default) + } + if col.IsAutoIncrement { + res = append(res, "autoincr") + } + + /*if col.SQLType.IsTime() && include(created, col.Name) { + res = append(res, "created") + } + + if col.SQLType.IsTime() && include(updated, col.Name) { + res = append(res, "updated") + } + + if col.SQLType.IsTime() && include(deleted, col.Name) { + res = append(res, "deleted") + }*/ + + if /*supportComment &&*/ col.Comment != "" { + res = append(res, fmt.Sprintf("comment('%s')", col.Comment)) + } + + names := make([]string, 0, len(col.Indexes)) + for name := range col.Indexes { + names = append(names, name) + } + sort.Strings(names) + + for _, name := range names { + index := table.Indexes[name] + var uistr string + if index.Type == core.UniqueType { + uistr = "unique" + } else if index.Type == core.IndexType { + uistr = "index" + } + if len(index.Cols) > 1 { + uistr += "(" + index.Name + ")" + } + res = append(res, uistr) + } + + nstr := col.SQLType.Name + if col.Length != 0 { + if col.Length2 != 0 { + nstr += fmt.Sprintf("(%v,%v)", col.Length, col.Length2) + } else { + nstr += fmt.Sprintf("(%v)", col.Length) + } + } else if len(col.EnumOptions) > 0 { //enum + nstr += "(" + opts := "" + + enumOptions := make([]string, 0, len(col.EnumOptions)) + for enumOption := range col.EnumOptions { + enumOptions = append(enumOptions, enumOption) + } + sort.Strings(enumOptions) + + for _, v := range enumOptions { + opts += fmt.Sprintf(",'%v'", v) + } + nstr += strings.TrimLeft(opts, ",") + nstr += ")" + } else if len(col.SetOptions) > 0 { //enum + nstr += "(" + opts := "" + + setOptions := make([]string, 0, len(col.SetOptions)) + for setOption := range col.SetOptions { + setOptions = append(setOptions, setOption) + } + sort.Strings(setOptions) + + for _, v := range setOptions { + opts += fmt.Sprintf(",'%v'", v) + } + nstr += strings.TrimLeft(opts, ",") + nstr += ")" + } + res = append(res, nstr) + if len(res) > 0 { + return template.HTML(fmt.Sprintf(`xorm:"%s"`, strings.Join(res, " "))) + } + return "" +} + +func include(source []string, target string) bool { + for _, s := range source { + if s == target { + return true + } + } + return false +} diff --git a/language/language.go b/language/language.go new file mode 100644 index 0000000..4013479 --- /dev/null +++ b/language/language.go @@ -0,0 +1,35 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package language + +import ( + "html/template" + + "xorm.io/core" +) + +// Language represents a languages supported when reverse codes +type Language struct { + Name string + Template string + Types map[string]string + Funcs template.FuncMap + Formatter func(string) (string, error) + Importter func([]*core.Table) []string + ExtName string +} + +var ( + languages = make(map[string]*Language) +) + +func RegisterLanguage(l *Language) { + languages[l.Name] = l +} + +// GetLanguage returns a language if exists +func GetLanguage(name string) *Language { + return languages[name] +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..84f4a3d --- /dev/null +++ b/main.go @@ -0,0 +1,17 @@ +// Copyright 2019 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + + "xorm.io/reverse/cmd" +) + +func main() { + if err := cmd.Execute(); err != nil { + fmt.Println(err) + } +} diff --git a/models/models.go b/models/models.go new file mode 100644 index 0000000..5e3eb97 --- /dev/null +++ b/models/models.go @@ -0,0 +1,9 @@ +package models + +type A struct { + Id int `xorm:"integer"` +} + +type B struct { + Id int `xorm:"INTEGER"` +} diff --git a/testdata/test.db b/testdata/test.db new file mode 100644 index 0000000000000000000000000000000000000000..9042adf2dd1335f62abe357a3aa827e83184f495 GIT binary patch literal 20480 zcmeI#&1%9x6o%m$9bE)uy6g6kl`dRJy#TcgMUaA;8<_l51b;wsHQn`sdO6*90u3Y} z-5cHmhnzsZ#XOVE*>5+;vXGDK?d(f=QBO*1wGdHCnb3pKbye?G*QM*>pQlmNpRZB) zz}UI6-}ciU!ayN_00IagfB*srAbTQsS5 z^RV<&2q1s}0tg_000IagfB*srAkbj}_WurFE=NNE0R#|0009ILKmY**5J2Ehfc>9e u0RaRMKmY**5I_I{1Q0*~f$j^i|9Ah6IUxcFAb