├── .gitignore ├── LICENSE ├── README.md ├── cmd ├── json.go ├── root.go ├── sql.go ├── time.go └── word.go ├── go.mod ├── go.sum ├── internal ├── json2struct │ ├── fields.go │ └── parser.go ├── sql2struct │ ├── mysql.go │ └── template.go ├── timer │ └── time.go └── word │ └── word.go └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tour(工具集) 2 | 3 | tour(工具集)是《Go 语言编程之旅:一起用 Go 做项目》中的项目,是第一章 [命令行应用:打造属于自己的工具集] 的附属源码。 4 | 5 | ## 关于本书 6 | 7 | 本书涵盖 Go 语言的各大经典实战,不介绍 Go 语言的语法基础,内容面向项目实践,同时会针对核心细节进行分析。而在实际项目迭代中,常常会出现或多或少的事故,因此本书也针对 Go 语言的大杀器(分析工具)以及常见问题进行了全面讲解。 8 | 9 | 本书适合已经大致学习了 Go 语言的基础语法后,想要跨越到下一个阶段的开发人员,可以填补该阶段的空白和进一步拓展你的思维方向。 10 | 11 | - 作者:陈剑煜(煎鱼),GitHub:[@eddycjy](https://github.com/eddycjy),微信公众号:脑子进煎鱼了。 12 | - 作者:徐新华(polaris),GitHub:[@polaris](https://github.com/polaris1119),微信公众号:Go语言中文网。 13 | 14 | ## 购买链接 15 | 16 | - 京东:https://item.jd.com/12685249.html 17 | - 当当:http://product.dangdang.com/28982027.html 18 | - 天猫:https://detail.tmall.com/item.htm?id=622185710833 19 | 20 | ## 目录 21 | 22 | - 第1章 命令行应用:打造属于自己的工具集 23 | - 1.1 工具之旅 24 | - 1.2 单词格式转换 25 | - 1.3 便捷的时间工具 26 | - 1.4 SQL语句到结构体的转换 27 | 28 | ## 功能清单 29 | 30 | - 单词格式转换 31 | - 便利的时间工具 32 | - SQL语句到结构体的转换 33 | - JSON 到结构体的转换 34 | 35 | ## 交流 36 | 37 | 如果你买了书,但还没进读者群或配套的免费星球,请关注我的公众号并点击相应的自定义菜单加入: 38 | 39 | ![image](https://image.eddycjy.com/7074be90379a121746146bc4229819f8.jpg) 40 | 41 | ## 后话 42 | 43 | - 如果本项目对你有所帮助欢迎进行 star,另外有任何的建议或意见欢迎随时提交 [issues](https://github.com/go-programming-tour-book/tour/issues/new) 或 [pr](https://github.com/go-programming-tour-book/tour/pulls)。 44 | 45 | - 如果你不是本书的读者,我正式向你推荐 《Go 语言编程之旅:一起用 Go 做项目》这本书,本书针对常见的项目类型,主要细分为 5 + 1 板块,分别是命令行、HTTP、RPC、Websocket 应用、进程内缓存以及 Go 语言中的大杀器,希望你能够喜欢。 46 | 47 | -------------------------------------------------------------------------------- /cmd/json.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/go-programming-tour-book/tour/internal/json2struct" 7 | 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | var jsonCmd = &cobra.Command{ 12 | Use: "json", 13 | Short: "json转换和处理", 14 | Long: "json转换和处理", 15 | Run: func(cmd *cobra.Command, args []string) {}, 16 | } 17 | 18 | var json2structCmd = &cobra.Command{ 19 | Use: "struct", 20 | Short: "json转换", 21 | Long: "json转换", 22 | Run: func(cmd *cobra.Command, args []string) { 23 | parser, err := json2struct.NewParser(str) 24 | if err != nil { 25 | log.Fatalf("json2struct.NewParser err: %v", err) 26 | } 27 | content := parser.Json2Struct() 28 | log.Printf("输出结果: %s", content) 29 | }, 30 | } 31 | 32 | func init() { 33 | jsonCmd.AddCommand(json2structCmd) 34 | json2structCmd.Flags().StringVarP(&str, "str", "s", "", "请输入json字符串") 35 | } 36 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | ) 6 | 7 | var rootCmd = &cobra.Command{ 8 | Use: "", 9 | Short: "", 10 | Long: "", 11 | } 12 | 13 | func Execute() error { 14 | return rootCmd.Execute() 15 | } 16 | 17 | func init() { 18 | rootCmd.AddCommand(wordCmd) 19 | rootCmd.AddCommand(timeCmd) 20 | rootCmd.AddCommand(jsonCmd) 21 | rootCmd.AddCommand(sqlCmd) 22 | } 23 | -------------------------------------------------------------------------------- /cmd/sql.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/go-programming-tour-book/tour/internal/sql2struct" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var username string 11 | var password string 12 | var host string 13 | var charset string 14 | var dbType string 15 | var dbName string 16 | var tableName string 17 | 18 | var sqlCmd = &cobra.Command{ 19 | Use: "sql", 20 | Short: "sql转换和处理", 21 | Long: "sql转换和处理", 22 | Run: func(cmd *cobra.Command, args []string) {}, 23 | } 24 | 25 | var sql2structCmd = &cobra.Command{ 26 | Use: "struct", 27 | Short: "sql转换", 28 | Long: "sql转换", 29 | Run: func(cmd *cobra.Command, args []string) { 30 | dbInfo := &sql2struct.DBInfo{ 31 | DBType: dbType, 32 | Host: host, 33 | UserName: username, 34 | Password: password, 35 | Charset: charset, 36 | } 37 | dbModel := sql2struct.NewDBModel(dbInfo) 38 | err := dbModel.Connect() 39 | if err != nil { 40 | log.Fatalf("dbModel.Connect err: %v", err) 41 | } 42 | columns, err := dbModel.GetColumns(dbName, tableName) 43 | if err != nil { 44 | log.Fatalf("dbModel.GetColumns err: %v", err) 45 | } 46 | 47 | template := sql2struct.NewStructTemplate() 48 | templateColumns := template.AssemblyColumns(columns) 49 | err = template.Generate(tableName, templateColumns) 50 | if err != nil { 51 | log.Fatalf("template.Generate err: %v", err) 52 | } 53 | }, 54 | } 55 | 56 | func init() { 57 | sqlCmd.AddCommand(sql2structCmd) 58 | sql2structCmd.Flags().StringVarP(&username, "username", "", "", "请输入数据库的账号") 59 | sql2structCmd.Flags().StringVarP(&password, "password", "", "", "请输入数据库的密码") 60 | sql2structCmd.Flags().StringVarP(&host, "host", "", "127.0.0.1:3306", "请输入数据库的HOST") 61 | sql2structCmd.Flags().StringVarP(&charset, "charset", "", "utf8mb4", "请输入数据库的编码") 62 | sql2structCmd.Flags().StringVarP(&dbType, "type", "", "mysql", "请输入数据库实例类型") 63 | sql2structCmd.Flags().StringVarP(&dbName, "db", "", "", "请输入数据库名称") 64 | sql2structCmd.Flags().StringVarP(&tableName, "table", "", "", "请输入表名称") 65 | } 66 | -------------------------------------------------------------------------------- /cmd/time.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "log" 5 | "strconv" 6 | "strings" 7 | "time" 8 | 9 | "github.com/go-programming-tour-book/tour/internal/timer" 10 | 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | var timeCmd = &cobra.Command{ 15 | Use: "time", 16 | Short: "时间格式处理", 17 | Long: "时间格式处理", 18 | Run: func(cmd *cobra.Command, args []string) {}, 19 | } 20 | 21 | var nowTimeCmd = &cobra.Command{ 22 | Use: "now", 23 | Short: "获取当前时间", 24 | Long: "获取当前时间", 25 | Run: func(cmd *cobra.Command, args []string) { 26 | nowTime := timer.GetNowTime() 27 | log.Printf("输出结果: %s, %d", nowTime.Format("2006-01-02 15:04:05"), nowTime.Unix()) 28 | }, 29 | } 30 | 31 | var calculateTime string 32 | var duration string 33 | 34 | var calculateTimeCmd = &cobra.Command{ 35 | Use: "calc", 36 | Short: "计算所需时间", 37 | Long: "计算所需时间", 38 | Run: func(cmd *cobra.Command, args []string) { 39 | var currentTimer time.Time 40 | var layout = "2006-01-02 15:04:05" 41 | if calculateTime == "" { 42 | currentTimer = timer.GetNowTime() 43 | } else { 44 | var err error 45 | space := strings.Count(calculateTime, " ") 46 | if space == 0 { 47 | layout = "2006-01-02" 48 | } 49 | if space == 1 { 50 | layout = "2006-01-02 15:04" 51 | } 52 | currentTimer, err = time.Parse(layout, calculateTime) 53 | if err != nil { 54 | t, _ := strconv.Atoi(calculateTime) 55 | currentTimer = time.Unix(int64(t), 0) 56 | } 57 | } 58 | t, err := timer.GetCalculateTime(currentTimer, duration) 59 | if err != nil { 60 | log.Fatalf("timer.GetCalculateTime err: %v", err) 61 | } 62 | 63 | log.Printf("输出结果: %s, %d", t.Format(layout), t.Unix()) 64 | }, 65 | } 66 | 67 | func init() { 68 | timeCmd.AddCommand(nowTimeCmd) 69 | timeCmd.AddCommand(calculateTimeCmd) 70 | 71 | calculateTimeCmd.Flags().StringVarP(&calculateTime, "calculate", "c", "", `需要计算的时间,有效单位为时间戳或已格式化后的时间`) 72 | calculateTimeCmd.Flags().StringVarP(&duration, "duration", "d", "", `持续时间,有效时间单位为"ns", "us" (or "µs"), "ms", "s", "m", "h"`) 73 | } 74 | -------------------------------------------------------------------------------- /cmd/word.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "log" 5 | "strings" 6 | 7 | "github.com/go-programming-tour-book/tour/internal/word" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | const ( 12 | ModeUpper = iota + 1 // 全部转大写 13 | ModeLower // 全部转小写 14 | ModeUnderscoreToUpperCamelCase // 下划线转大写驼峰 15 | ModeUnderscoreToLowerCamelCase // 下线线转小写驼峰 16 | ModeCamelCaseToUnderscore // 驼峰转下划线 17 | ) 18 | 19 | var str string 20 | var mode int8 21 | var desc = strings.Join([]string{ 22 | "该子命令支持各种单词格式转换,模式如下:", 23 | "1:全部转大写", 24 | "2:全部转小写", 25 | "3:下划线转大写驼峰", 26 | "4:下划线转小写驼峰", 27 | "5:驼峰转下划线", 28 | }, "\n") 29 | 30 | var wordCmd = &cobra.Command{ 31 | Use: "word", 32 | Short: "单词格式转换", 33 | Long: desc, 34 | Run: func(cmd *cobra.Command, args []string) { 35 | var content string 36 | switch mode { 37 | case ModeUpper: 38 | content = word.ToUpper(str) 39 | case ModeLower: 40 | content = word.ToLower(str) 41 | case ModeUnderscoreToUpperCamelCase: 42 | content = word.UnderscoreToUpperCamelCase(str) 43 | case ModeUnderscoreToLowerCamelCase: 44 | content = word.UnderscoreToLowerCamelCase(str) 45 | case ModeCamelCaseToUnderscore: 46 | content = word.CamelCaseToUnderscore(str) 47 | default: 48 | log.Fatalf("暂不支持该转换模式,请执行 help word 查看帮助文档") 49 | } 50 | 51 | log.Printf("输出结果: %s", content) 52 | }, 53 | } 54 | 55 | func init() { 56 | wordCmd.Flags().StringVarP(&str, "str", "s", "", "请输入单词内容") 57 | wordCmd.Flags().Int8VarP(&mode, "mode", "m", 0, "请输入单词转换的模式") 58 | } 59 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/go-programming-tour-book/tour 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/go-sql-driver/mysql v1.4.1 7 | github.com/spf13/cobra v1.0.0 8 | github.com/spf13/pflag v1.0.5 // indirect 9 | google.golang.org/appengine v1.6.1 // indirect 10 | ) 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 4 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 5 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 6 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 7 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 8 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 9 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 10 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 11 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 12 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 13 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 14 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 15 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 16 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 17 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 18 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 19 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 20 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 21 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 22 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 23 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 24 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 25 | github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= 26 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 27 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 28 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 29 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 30 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 31 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 32 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 33 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 34 | github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= 35 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 36 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 37 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 38 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 39 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 40 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 41 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 42 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 43 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 44 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 45 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 46 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 47 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 48 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 49 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 50 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 51 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 52 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 53 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 54 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 55 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 56 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 57 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 58 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 59 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 60 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 61 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 62 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 63 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 64 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 65 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 66 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 67 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 68 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 69 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 70 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 71 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 72 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 73 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 74 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 75 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 76 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 77 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 78 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 79 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 80 | github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= 81 | github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= 82 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 83 | github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= 84 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 85 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 86 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 87 | github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= 88 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 89 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 90 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 91 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= 92 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 93 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 94 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 95 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 96 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 97 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 98 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 99 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 100 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 101 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 102 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 103 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 104 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 105 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 106 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 107 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 108 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 109 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 110 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 111 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 112 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 113 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 114 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 115 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 116 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 117 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 118 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 119 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 120 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 121 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 122 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 123 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 124 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 125 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 126 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 127 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 128 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 129 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 130 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 131 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 132 | google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= 133 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 134 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 135 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 136 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 137 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 138 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 139 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 140 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 141 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 142 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 143 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 144 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 145 | -------------------------------------------------------------------------------- /internal/json2struct/fields.go: -------------------------------------------------------------------------------- 1 | package json2struct 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/go-programming-tour-book/tour/internal/word" 7 | ) 8 | 9 | type FieldSegment struct { 10 | Format string 11 | FieldValues []FieldValue 12 | } 13 | 14 | type FieldValue struct { 15 | CamelCase bool 16 | Value string 17 | } 18 | 19 | type Field struct { 20 | Name string 21 | Type string 22 | } 23 | 24 | type Fields []*Field 25 | 26 | func (f *Fields) appendSegment(name string, segment FieldSegment) { 27 | var s []interface{} 28 | for _, v := range segment.FieldValues { 29 | value := v.Value 30 | if v.CamelCase { 31 | value = word.UnderscoreToUpperCamelCase(v.Value) 32 | } 33 | 34 | s = append(s, value) 35 | } 36 | *f = append(*f, &Field{Name: word.UnderscoreToUpperCamelCase(name), Type: fmt.Sprintf(segment.Format, s...)}) 37 | } 38 | 39 | func (f *Fields) removeDuplicate() Fields { 40 | m := make(map[string]bool) 41 | fields := Fields{} 42 | for _, entry := range *f { 43 | if _, value := m[entry.Name]; !value { 44 | m[entry.Name] = true 45 | fields = append(fields, entry) 46 | } 47 | } 48 | return fields 49 | } 50 | -------------------------------------------------------------------------------- /internal/json2struct/parser.go: -------------------------------------------------------------------------------- 1 | package json2struct 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "reflect" 7 | "strings" 8 | 9 | "github.com/go-programming-tour-book/tour/internal/word" 10 | ) 11 | 12 | const ( 13 | TYPE_MAP_STRING_INTERFACE = "map[string]interface {}" 14 | TYPE_INTERFACE = "[]interface {}" 15 | ) 16 | 17 | type Parser struct { 18 | Source map[string]interface{} 19 | Output Output 20 | Children Output 21 | StructTag string 22 | StructName string 23 | } 24 | 25 | type Output []string 26 | 27 | func (o *Output) appendSegment(format, title string, args ...interface{}) { 28 | s := []interface{}{} 29 | s = append(s, word.UnderscoreToUpperCamelCase(title)) 30 | if len(args) != 0 { 31 | s = append(s, args...) 32 | format = " " + format 33 | } 34 | *o = append(*o, fmt.Sprintf(format, s...)) 35 | } 36 | 37 | func (o *Output) appendSuffix() { 38 | *o = append(*o, "}\n") 39 | } 40 | 41 | func NewParser(s string) (*Parser, error) { 42 | source := make(map[string]interface{}) 43 | if err := json.Unmarshal([]byte(s), &source); err != nil { 44 | return nil, err 45 | } 46 | return &Parser{ 47 | Source: source, 48 | StructTag: "type %s struct {", 49 | StructName: "tour", 50 | }, nil 51 | } 52 | 53 | func (p *Parser) Json2Struct() string { 54 | p.Output.appendSegment(p.StructTag, p.StructName) 55 | for parentName, parentValues := range p.Source { 56 | valueType := reflect.TypeOf(parentValues).String() 57 | if valueType == TYPE_INTERFACE { 58 | p.toParentList(parentName, parentValues.([]interface{}), true) 59 | } else { 60 | var fields Fields 61 | fields.appendSegment(parentName, FieldSegment{ 62 | Format: "%s", 63 | FieldValues: []FieldValue{ 64 | {CamelCase: false, Value: valueType}, 65 | }, 66 | }) 67 | p.Output.appendSegment("%s %s", fields[0].Name, fields[0].Type) 68 | } 69 | } 70 | p.Output.appendSuffix() 71 | return strings.Join(append(p.Output, p.Children...), "\n") 72 | } 73 | 74 | func (p *Parser) toChildrenStruct(parentName string, values map[string]interface{}) { 75 | p.Children.appendSegment(p.StructTag, parentName) 76 | for fieldName, fieldValue := range values { 77 | p.Children.appendSegment("%s %s", fieldName, reflect.TypeOf(fieldValue).String()) 78 | } 79 | p.Children.appendSuffix() 80 | } 81 | 82 | func (p *Parser) toParentList(parentName string, parentValues []interface{}, isTop bool) { 83 | var fields Fields 84 | for _, v := range parentValues { 85 | valueType := reflect.TypeOf(v).String() 86 | if valueType == TYPE_MAP_STRING_INTERFACE { 87 | fields = append(fields, p.handleParentTypeMapIface(v.(map[string]interface{}))...) 88 | p.Children.appendSegment(p.StructTag, parentName) 89 | for _, field := range fields.removeDuplicate() { 90 | p.Children.appendSegment("%s %s", field.Name, field.Type) 91 | } 92 | p.Children.appendSuffix() 93 | if isTop { 94 | valueType = word.UnderscoreToUpperCamelCase(parentName) 95 | } 96 | } 97 | 98 | if isTop { 99 | p.Output.appendSegment("%s %s%s", parentName, "[]", valueType) 100 | } 101 | break 102 | } 103 | } 104 | 105 | func (p *Parser) handleParentTypeMapIface(values map[string]interface{}) Fields { 106 | var fields Fields 107 | for fieldName, fieldValues := range values { 108 | var fieldValueType = reflect.TypeOf(fieldValues).String() 109 | var fieldSegment = FieldSegment{ 110 | Format: "%s", 111 | FieldValues: []FieldValue{{CamelCase: true, Value: fieldValueType}}, 112 | } 113 | switch fieldValueType { 114 | case TYPE_INTERFACE: 115 | fieldSegment = p.handleTypeIface(fieldName, fieldValues.([]interface{})) 116 | case TYPE_MAP_STRING_INTERFACE: 117 | fieldSegment = p.handleTypeMapIface(fieldName, fieldValues.(map[string]interface{})) 118 | } 119 | 120 | fields.appendSegment(fieldName, fieldSegment) 121 | } 122 | 123 | return fields 124 | } 125 | 126 | func (p *Parser) handleTypeIface(fieldName string, fieldValues []interface{}) FieldSegment { 127 | fieldSegment := FieldSegment{ 128 | Format: "%s%s", 129 | FieldValues: []FieldValue{ 130 | {CamelCase: false, Value: "[]"}, 131 | {CamelCase: true, Value: fieldName}, 132 | }, 133 | } 134 | p.toParentList(fieldName, fieldValues, false) 135 | return fieldSegment 136 | } 137 | 138 | func (p *Parser) handleTypeMapIface(fieldName string, fieldValues map[string]interface{}) FieldSegment { 139 | fieldSegment := FieldSegment{ 140 | Format: "%s", 141 | FieldValues: []FieldValue{ 142 | {CamelCase: true, Value: fieldName}, 143 | }, 144 | } 145 | p.toChildrenStruct(fieldName, fieldValues) 146 | return fieldSegment 147 | } 148 | -------------------------------------------------------------------------------- /internal/sql2struct/mysql.go: -------------------------------------------------------------------------------- 1 | package sql2struct 2 | 3 | import ( 4 | "database/sql" 5 | "errors" 6 | "fmt" 7 | 8 | _ "github.com/go-sql-driver/mysql" 9 | ) 10 | 11 | type DBModel struct { 12 | DBEngine *sql.DB 13 | DBInfo *DBInfo 14 | } 15 | 16 | type DBInfo struct { 17 | DBType string 18 | Host string 19 | UserName string 20 | Password string 21 | Charset string 22 | } 23 | 24 | type TableColumn struct { 25 | ColumnName string 26 | DataType string 27 | IsNullable string 28 | ColumnKey string 29 | ColumnType string 30 | ColumnComment string 31 | } 32 | 33 | var DBTypeToStructType = map[string]string{ 34 | "int": "int32", 35 | "tinyint": "int8", 36 | "smallint": "int", 37 | "mediumint": "int64", 38 | "bigint": "int64", 39 | "bit": "int", 40 | "bool": "bool", 41 | "enum": "string", 42 | "set": "string", 43 | "varchar": "string", 44 | "char": "string", 45 | "tinytext": "string", 46 | "mediumtext": "string", 47 | "text": "string", 48 | "longtext": "string", 49 | "blob": "string", 50 | "tinyblob": "string", 51 | "mediumblob": "string", 52 | "longblob": "string", 53 | "date": "time.Time", 54 | "datetime": "time.Time", 55 | "timestamp": "time.Time", 56 | "time": "time.Time", 57 | "float": "float64", 58 | "double": "float64", 59 | } 60 | 61 | func NewDBModel(info *DBInfo) *DBModel { 62 | return &DBModel{DBInfo: info} 63 | } 64 | 65 | func (m *DBModel) Connect() error { 66 | var err error 67 | s := "%s:%s@tcp(%s)/information_schema?" + 68 | "charset=%s&parseTime=True&loc=Local" 69 | dsn := fmt.Sprintf( 70 | s, 71 | m.DBInfo.UserName, 72 | m.DBInfo.Password, 73 | m.DBInfo.Host, 74 | m.DBInfo.Charset, 75 | ) 76 | m.DBEngine, err = sql.Open(m.DBInfo.DBType, dsn) 77 | if err != nil { 78 | return err 79 | } 80 | 81 | return nil 82 | } 83 | 84 | func (m *DBModel) GetColumns(dbName, tableName string) ([]*TableColumn, error) { 85 | query := "SELECT COLUMN_NAME, DATA_TYPE, COLUMN_KEY, " + 86 | "IS_NULLABLE, COLUMN_TYPE, COLUMN_COMMENT " + 87 | "FROM COLUMNS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? " 88 | rows, err := m.DBEngine.Query(query, dbName, tableName) 89 | if err != nil { 90 | return nil, err 91 | } 92 | if rows == nil { 93 | return nil, errors.New("没有数据") 94 | } 95 | defer rows.Close() 96 | 97 | var columns []*TableColumn 98 | for rows.Next() { 99 | var column TableColumn 100 | err := rows.Scan(&column.ColumnName, &column.DataType, &column.ColumnKey, &column.IsNullable, &column.ColumnType, &column.ColumnComment) 101 | if err != nil { 102 | return nil, err 103 | } 104 | 105 | columns = append(columns, &column) 106 | } 107 | 108 | return columns, nil 109 | } 110 | -------------------------------------------------------------------------------- /internal/sql2struct/template.go: -------------------------------------------------------------------------------- 1 | package sql2struct 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "text/template" 7 | 8 | "github.com/go-programming-tour-book/tour/internal/word" 9 | ) 10 | 11 | const strcutTpl = `type {{.TableName | ToCamelCase}} struct { 12 | {{range .Columns}} {{ $length := len .Comment}} {{ if gt $length 0 }}// {{.Comment}} {{else}}// {{.Name}} {{ end }} 13 | {{ $typeLen := len .Type }} {{ if gt $typeLen 0 }}{{.Name | ToCamelCase}} {{.Type}} {{.Tag}}{{ else }}{{.Name}}{{ end }} 14 | {{end}}} 15 | 16 | func (model {{.TableName | ToCamelCase}}) TableName() string { 17 | return "{{.TableName}}" 18 | }` 19 | 20 | type StructTemplate struct { 21 | strcutTpl string 22 | } 23 | 24 | type StructColumn struct { 25 | Name string 26 | Type string 27 | Tag string 28 | Comment string 29 | } 30 | 31 | type StructTemplateDB struct { 32 | TableName string 33 | Columns []*StructColumn 34 | } 35 | 36 | func NewStructTemplate() *StructTemplate { 37 | return &StructTemplate{strcutTpl: strcutTpl} 38 | } 39 | 40 | func (t *StructTemplate) AssemblyColumns(tbColumns []*TableColumn) []*StructColumn { 41 | tplColumns := make([]*StructColumn, 0, len(tbColumns)) 42 | for _, column := range tbColumns { 43 | tag := fmt.Sprintf("`"+"json:"+"\"%s\""+"`", column.ColumnName) 44 | tplColumns = append(tplColumns, &StructColumn{ 45 | Name: column.ColumnName, 46 | Type: DBTypeToStructType[column.DataType], 47 | Tag: tag, 48 | Comment: column.ColumnComment, 49 | }) 50 | } 51 | 52 | return tplColumns 53 | } 54 | 55 | func (t *StructTemplate) Generate(tableName string, tplColumns []*StructColumn) error { 56 | tpl := template.Must(template.New("sql2struct").Funcs(template.FuncMap{ 57 | "ToCamelCase": word.UnderscoreToUpperCamelCase, 58 | }).Parse(t.strcutTpl)) 59 | 60 | tplDB := StructTemplateDB{ 61 | TableName: tableName, 62 | Columns: tplColumns, 63 | } 64 | err := tpl.Execute(os.Stdout, tplDB) 65 | if err != nil { 66 | return err 67 | } 68 | 69 | return nil 70 | } 71 | -------------------------------------------------------------------------------- /internal/timer/time.go: -------------------------------------------------------------------------------- 1 | package timer 2 | 3 | import "time" 4 | 5 | func GetNowTime() time.Time { 6 | location, _ := time.LoadLocation("Asia/Shanghai") 7 | return time.Now().In(location) 8 | } 9 | 10 | func GetCalculateTime(currentTimer time.Time, d string) (time.Time, error) { 11 | duration, err := time.ParseDuration(d) 12 | if err != nil { 13 | return time.Time{}, err 14 | } 15 | 16 | return currentTimer.Add(duration), nil 17 | } 18 | -------------------------------------------------------------------------------- /internal/word/word.go: -------------------------------------------------------------------------------- 1 | package word 2 | 3 | import ( 4 | "strings" 5 | "unicode" 6 | ) 7 | 8 | func ToUpper(s string) string { 9 | return strings.ToUpper(s) 10 | } 11 | 12 | func ToLower(s string) string { 13 | return strings.ToLower(s) 14 | } 15 | 16 | func UnderscoreToUpperCamelCase(s string) string { 17 | s = strings.Replace(s, "_", " ", -1) 18 | s = strings.Title(s) 19 | return strings.Replace(s, " ", "", -1) 20 | } 21 | 22 | func UnderscoreToLowerCamelCase(s string) string { 23 | s = UnderscoreToUpperCamelCase(s) 24 | return string(unicode.ToLower(rune(s[0]))) + s[1:] 25 | } 26 | 27 | func CamelCaseToUnderscore(s string) string { 28 | var output []rune 29 | for i, r := range s { 30 | if i == 0 { 31 | output = append(output, unicode.ToLower(r)) 32 | continue 33 | } 34 | if unicode.IsUpper(r) { 35 | output = append(output, '_') 36 | } 37 | output = append(output, unicode.ToLower(r)) 38 | } 39 | return string(output) 40 | } 41 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/go-programming-tour-book/tour/cmd" 7 | ) 8 | 9 | func main() { 10 | err := cmd.Execute() 11 | if err != nil { 12 | log.Fatalf("cmd.Execute err: %v", err) 13 | } 14 | } 15 | --------------------------------------------------------------------------------