├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── example_test.go ├── export_test.go ├── flag.gif ├── flag.go ├── flag_slice.go ├── flag_slice_test.go ├── flag_test.go ├── match_var.go ├── match_var_test.go ├── opt.go ├── opt_test.go ├── struct.go ├── struct_test.go ├── subcommand.go ├── subcommand_test.go └── type.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | *.swp 8 | *~ 9 | 10 | # Test binary, build with `go test -c` 11 | *.test 12 | 13 | # Output of the go coverage tool, specifically when used with LiteIDE 14 | *.out 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.12.x 4 | script: 5 | - go test -v ./... 6 | -------------------------------------------------------------------------------- /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 | #### 简介 2 | flag库是标准库的增强版本,让你的命令行程序飞起来 3 | 4 | [![Build Status](https://travis-ci.org/guonaihong/flag.png)](https://travis-ci.org/guonaihong/flag) 5 | #### 演示 6 | ![flag](./flag.gif) 7 | ### 功能 8 | * [兼容go标准库](#兼容go标准库) 9 | * [数组类型选项](#数组类型选项) 10 | * [多选项支持](#多选项支持) 11 | * [MatchVar](#matchvar) 12 | * [posix风格](#posix风格) 13 | * [子母命令](#子母命令) 14 | * [泛型接口函数](#泛型接口函数) 15 | * [绑定结构体](#绑定结构体) 16 | 17 | #### 兼容go标准库 18 | ```golang 19 | package main 20 | 21 | import ( 22 | "fmt" 23 | "github.com/guonaihong/flag" 24 | ) 25 | 26 | func main() { 27 | op := flag.String("dir", "", "open dir") 28 | flag.Parse() 29 | fmt.Printf("op = %s\n", *op) 30 | } 31 | // 运行 32 | // go run main.go -dir test.file 33 | // 输出 34 | // op = test.file 35 | ``` 36 | 37 | #### 数组类型选项 38 | 很方便写出curl -H选项效果 39 | ```golang 40 | package main 41 | 42 | import ( 43 | "fmt" 44 | "github.com/guonaihong/flag" 45 | ) 46 | 47 | func main() { 48 | h := flag.StringSlice("H, header", []string{}, "http header") 49 | flag.Parse() 50 | fmt.Printf("%#v\n", *h) 51 | } 52 | 53 | // 运行 54 | // go run main.go -H "appkey:123" -H "User-Agent: main" --header "Accept: */*" 55 | // 输出 56 | // []string{"appkey:123", "User-Agent: main", "Accept: */*"} 57 | ``` 58 | #### 多选项支持 59 | ```golang 60 | package main 61 | 62 | import ( 63 | "fmt" 64 | "github.com/guonaihong/flag" 65 | ) 66 | 67 | func main() { 68 | long := flag.String("l, long", "", "short long option") 69 | flag.Parse() 70 | fmt.Printf("long = %s\n", *long) 71 | } 72 | 73 | // 运行 74 | // go run main.go -l hello 75 | // go run main.go -long hello 76 | // 输出 77 | // long = hello 78 | ``` 79 | #### matchvar 80 | 详情 [#9](https://github.com/guonaihong/flag/issues/9) 81 | 很多时候使用bool选项,都是命令行传递bool选项 代码里面设置一个默认值,如下。 82 | ```go 83 | var openNull bool 84 | delimiter := byte('\n') 85 | command.Opt("0, null", "end each output line with NUL, not newline"). 86 | Flags(flag.Posix). 87 | Var(&openNull) 88 | if openNull { 89 | delimiter = byte(0) 90 | } 91 | ``` 92 | 93 | 现在可以使用MatchVar函数简化类似代码,如果命令函数传递-0或者-null,delimiter的值为'\n' 94 | ```go 95 | package main 96 | 97 | import ( 98 | "fmt" 99 | "github.com/guonaihong/flag" 100 | ) 101 | 102 | func main() { 103 | 104 | delimiter := byte(0) 105 | 106 | flag.Opt("0, null", "end each output line with NUL, not newline"). 107 | Flags(flag.Posix). 108 | MatchVar(&delimiter, byte('\n')) 109 | flag.Parse() 110 | 111 | fmt.Printf("%d\n", delimiter) 112 | } 113 | 114 | // 运行 115 | // go run main.go -0 116 | // 输出 117 | // 10 118 | ``` 119 | 120 | #### posix风格 121 | * 命令组合 122 | ```golang 123 | package main 124 | 125 | import ( 126 | "fmt" 127 | "github.com/guonaihong/flag" 128 | ) 129 | 130 | func main() { 131 | showEnds := flag.Opt("E, show-end", "display $ at end of each line"). 132 | Flags(flag.PosixShort).NewBool(false) 133 | 134 | showTabs := flag.Opt("T, show-tabs", "display TAB characters as ^I"). 135 | Flags(flag.PosixShort).NewBool(false) 136 | 137 | flag.Parse() 138 | fmt.Printf("showEnds:%t, showTabs:%t\n", *showEnds, *showTabs) 139 | } 140 | 141 | // 运行 142 | // go run main.go -TE 143 | // 输出 144 | // showEnds:true, showTabs:true 145 | ``` 146 | * 贪婪模式 147 | ```golang 148 | package main 149 | 150 | import ( 151 | "fmt" 152 | "github.com/guonaihong/flag" 153 | ) 154 | 155 | func main() { 156 | var heads []string 157 | 158 | flag.Opt("H, header", "Pass custom header LINE to server (H)"). 159 | Flags(flag.GreedyMode).Var(&heads) 160 | 161 | flag.Parse() 162 | fmt.Printf("%#v\n", heads) 163 | } 164 | 165 | // 运行 166 | // go run main.go -H appkey:test hello:world love:you 167 | // 输出 168 | // []string{"appkey:test", "hello:world", "love:you"} 169 | ``` 170 | * -vvv 或者-v -v -v 171 | ```go 172 | 173 | package main 174 | 175 | import ( 176 | "fmt" 177 | "github.com/guonaihong/flag" 178 | ) 179 | 180 | func main() { 181 | var bs []bool 182 | 183 | flag.Opt("v", "test bool slice").Flags(flag.PosixShort).Var(&bs) 184 | 185 | flag.Parse() 186 | 187 | switch len(bs) { 188 | case 0: 189 | fmt.Printf("No verbose info\n") 190 | case 1: 191 | fmt.Printf("Some verbose info\n") 192 | case 2: 193 | fmt.Printf("Tons of verbose info\n") 194 | default: 195 | fmt.Printf("别闹了\n") 196 | } 197 | 198 | } 199 | 200 | // 运行 201 | // go run main.go -v 202 | // 输出 203 | // Some verbose info 204 | 205 | // 运行 206 | // go run main.go -vv 207 | // 输出 208 | // Tons of verbose info 209 | 210 | // 运行 211 | // go run main.go -vvv 212 | // 输出 213 | // 别闹了 214 | 215 | ``` 216 | #### 子母命令 217 | 如果要实现一个主命令包含3个子命令(http, websocket, tcp),其中http子命令又包含5个命令行选项,可以使用如下用法 218 | ``` golang 219 | package main 220 | 221 | import ( 222 | "fmt" 223 | "github.com/guonaihong/flag" 224 | "os" 225 | ) 226 | 227 | type httpOption struct { 228 | Header []string 229 | Forms []string 230 | FormString string 231 | Url string 232 | Data string 233 | } 234 | 235 | type wsOption struct { 236 | Ac int 237 | An int 238 | } 239 | 240 | type streamOption struct { 241 | } 242 | 243 | func main() { 244 | parent := flag.NewParentCommand(os.Args[0]) 245 | 246 | parent.SubCommand("http", "Use the http subcommand", func() { 247 | argv0 := os.Args[0] 248 | argv := parent.Args() 249 | 250 | command := flag.NewFlagSet(argv0, flag.ExitOnError) 251 | 252 | ho := httpOption{} 253 | 254 | command.Opt("H, header", "Pass custom header LINE to server (H)").Var(&ho.Header) 255 | command.Opt("F, form", "Specify HTTP multipart POST data (H)").Var(&ho.Forms) 256 | command.Opt("form-string", "Specify HTTP multipart POST data (H)").Var(&ho.Forms) 257 | command.Opt("url", "Specify a URL to fetch").Var(&ho.Url) 258 | command.Opt("d, data", "HTTP POST data").Var(&ho.Data) 259 | 260 | command.Parse(argv) 261 | }) 262 | 263 | parent.SubCommand("ws, websocket", "Use the websocket subcommand", func() { 264 | argv := parent.Args() 265 | command := flag.NewFlagSet(os.Args[0], flag.ExitOnError) 266 | 267 | wo := wsOption{} 268 | 269 | command.Opt("an", "Number of requests to perform").DefaultVar(&wo.An, 1) 270 | command.Opt("ac", "Number of multiple requests to make").DefaultVar(&wo.Ac, 1) 271 | 272 | command.Parse(argv) 273 | 274 | fmt.Printf("option = %#v\n", wo) 275 | }) 276 | 277 | parent.SubCommand("tcp, udp", "Use the tcp or udp subcommand", func() { 278 | //conn.Main(os.Args[0], parent.Args()) 279 | }) 280 | 281 | parent.Parse(os.Args[1:]) 282 | } 283 | 284 | // 运行帮助信息 285 | // go run main.go -h 286 | // 输出 287 | // Usage of /tmp/go-build387723503/b001/exe/main: 288 | // http Use the http subcommand 289 | // tcp, udp Use the tcp or udp subcommand 290 | // ws, websocket Use the websocket subcommand 291 | 292 | // 运行websocket子命令帮助信息 293 | // go run 294 | // Usage of /tmp/go-build229109289/b001/exe/main: 295 | // -ac int 296 | // Number of multiple requests to make 297 | // -an int 298 | // Number of requests to perform 299 | 300 | // 运行websocket子命令 301 | // go run main.go websocket -ac 2 -an 2 302 | // 输出 303 | // option = main.wsOption{Ac:2, An:2} 304 | ``` 305 | #### 泛型接口函数 306 | 307 | ```golang 308 | package main 309 | 310 | import ( 311 | "fmt" 312 | "github.com/guonaihong/flag" 313 | _ "os" 314 | ) 315 | 316 | type TestOption struct { 317 | Int int 318 | Int64 int64 319 | Strings []string 320 | Int64s []int64 321 | 322 | Int2 int 323 | } 324 | 325 | func main() { 326 | option := TestOption{} 327 | 328 | // DefaultVar是带默认值的泛型函数 329 | // Var是不带默认值的泛型函数 330 | flag.Opt("i, int", "test int").DefaultVar(&option.Int, 0) 331 | flag.Opt("i64, int64", "test int64").DefaultVar(&option.Int64, int64(0)) 332 | flag.Opt("s, strings", "test []string").DefaultVar(&option.Strings, []string{}) 333 | flag.Opt("i64s, int64s", "test []int64").DefaultVar(&option.Int64s, []int64{}) 334 | flag.Opt("i2", "test int2").Var(&option.Int2) 335 | flag.Parse() 336 | 337 | fmt.Printf("%#v\n", option) 338 | } 339 | 340 | // 运行 341 | // go run main.go -i 3 -i64 64 -i64s 64 -i64s 1 -i64s 2 -i64s 3 -s a -s b -s c 342 | // 输出 343 | // main.TestOption{Int:3, Int64:64, Strings:[]string{"a", "b", "c"}, Int64s:[]int64{64, 1, 2, 3}, Int2:0} 344 | ``` 345 | 346 | #### 绑定结构体 347 | ```golang 348 | package main 349 | 350 | import ( 351 | "fmt" 352 | "github.com/guonaihong/flag" 353 | _ "os" 354 | ) 355 | 356 | type TestOption struct { 357 | Int int `opt:"i, int" usage:"test int"` 358 | Int64 int64 `opt:"i64, int64" usage:"test int64"` 359 | Strings []string `opt:"s, strings" usage:"test []string"` 360 | Int64s []int64 `opt:"i64s, int64s" usage:"test []int64"` 361 | 362 | Int2 int `opt:"i2" usage:"test int2"` 363 | } 364 | 365 | func main() { 366 | option := TestOption{} 367 | 368 | flag.ParseStruct(&option) 369 | 370 | fmt.Printf("%#v\n", option) 371 | } 372 | 373 | // 运行 374 | // go run main.go -i 3 -i64 64 -i64s 64 -i64s 1 -i64s 2 -i64s 3 -s a -s b -s c 375 | // 输出 376 | // main.TestOption{Int:3, Int64:64, Strings:[]string{"a", "b", "c"}, Int64s:[]int64{64, 1, 2, 3}, Int2:0} 377 | ``` 378 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // These examples demonstrate more intricate uses of the flag package. 6 | package flag_test 7 | 8 | import ( 9 | "errors" 10 | "flag" 11 | "fmt" 12 | "strings" 13 | "time" 14 | ) 15 | 16 | // Example 1: A single string flag called "species" with default value "gopher". 17 | var species = flag.String("species", "gopher", "the species we are studying") 18 | 19 | // Example 2: Two flags sharing a variable, so we can have a shorthand. 20 | // The order of initialization is undefined, so make sure both use the 21 | // same default value. They must be set up with an init function. 22 | var gopherType string 23 | 24 | func init() { 25 | const ( 26 | defaultGopher = "pocket" 27 | usage = "the variety of gopher" 28 | ) 29 | flag.StringVar(&gopherType, "gopher_type", defaultGopher, usage) 30 | flag.StringVar(&gopherType, "g", defaultGopher, usage+" (shorthand)") 31 | } 32 | 33 | // Example 3: A user-defined flag type, a slice of durations. 34 | type interval []time.Duration 35 | 36 | // String is the method to format the flag's value, part of the flag.Value interface. 37 | // The String method's output will be used in diagnostics. 38 | func (i *interval) String() string { 39 | return fmt.Sprint(*i) 40 | } 41 | 42 | // Set is the method to set the flag value, part of the flag.Value interface. 43 | // Set's argument is a string to be parsed to set the flag. 44 | // It's a comma-separated list, so we split it. 45 | func (i *interval) Set(value string) error { 46 | // If we wanted to allow the flag to be set multiple times, 47 | // accumulating values, we would delete this if statement. 48 | // That would permit usages such as 49 | // -deltaT 10s -deltaT 15s 50 | // and other combinations. 51 | if len(*i) > 0 { 52 | return errors.New("interval flag already set") 53 | } 54 | for _, dt := range strings.Split(value, ",") { 55 | duration, err := time.ParseDuration(dt) 56 | if err != nil { 57 | return err 58 | } 59 | *i = append(*i, duration) 60 | } 61 | return nil 62 | } 63 | 64 | // Define a flag to accumulate durations. Because it has a special type, 65 | // we need to use the Var function and therefore create the flag during 66 | // init. 67 | 68 | var intervalFlag interval 69 | 70 | func init() { 71 | // Tie the command-line flag to the intervalFlag variable and 72 | // set a usage message. 73 | flag.Var(&intervalFlag, "deltaT", "comma-separated list of intervals to use between events") 74 | } 75 | 76 | func Example() { 77 | // All the interesting pieces are with the variables declared above, but 78 | // to enable the flag package to see the flags defined there, one must 79 | // execute, typically at the start of main (not init!): 80 | // flag.Parse() 81 | // We don't run it here because this is not a main function and 82 | // the testing suite has already parsed the flags. 83 | } 84 | -------------------------------------------------------------------------------- /export_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package flag 6 | 7 | import "os" 8 | 9 | // Additional routines compiled into the package only during testing. 10 | 11 | var DefaultUsage = Usage 12 | 13 | // ResetForTesting clears all flag state and sets the usage function as directed. 14 | // After calling ResetForTesting, parse errors in flag handling will not 15 | // exit the program. 16 | func ResetForTesting(usage func()) { 17 | CommandLine = NewFlagSet(os.Args[0], ContinueOnError) 18 | CommandLine.Usage = commandLineUsage 19 | Usage = usage 20 | } 21 | -------------------------------------------------------------------------------- /flag.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guonaihong/flag/a7f6954004ec9e9ca42b5ac80629e6f6f6a46ff4/flag.gif -------------------------------------------------------------------------------- /flag.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package flag implements command-line flag parsing. 7 | 8 | Usage: 9 | 10 | Define flags using flag.String(), Bool(), Int(), etc. 11 | 12 | This declares an integer flag, -flagname, stored in the pointer ip, with type *int. 13 | import "flag" 14 | var ip = flag.Int("flagname", 1234, "help message for flagname") 15 | If you like, you can bind the flag to a variable using the Var() functions. 16 | var flagvar int 17 | func init() { 18 | flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname") 19 | } 20 | Or you can create custom flags that satisfy the Value interface (with 21 | pointer receivers) and couple them to flag parsing by 22 | flag.Var(&flagVal, "name", "help message for flagname") 23 | For such flags, the default value is just the initial value of the variable. 24 | 25 | After all flags are defined, call 26 | flag.Parse() 27 | to parse the command line into the defined flags. 28 | 29 | Flags may then be used directly. If you're using the flags themselves, 30 | they are all pointers; if you bind to variables, they're values. 31 | fmt.Println("ip has value ", *ip) 32 | fmt.Println("flagvar has value ", flagvar) 33 | 34 | After parsing, the arguments following the flags are available as the 35 | slice flag.Args() or individually as flag.Arg(i). 36 | The arguments are indexed from 0 through flag.NArg()-1. 37 | 38 | Command line flag syntax: 39 | -flag 40 | -flag=x 41 | -flag x // non-boolean flags only 42 | One or two minus signs may be used; they are equivalent. 43 | The last form is not permitted for boolean flags because the 44 | meaning of the command 45 | cmd -x * 46 | where * is a Unix shell wildcard, will change if there is a file 47 | called 0, false, etc. You must use the -flag=false form to turn 48 | off a boolean flag. 49 | 50 | Flag parsing stops just before the first non-flag argument 51 | ("-" is a non-flag argument) or after the terminator "--". 52 | 53 | Integer flags accept 1234, 0664, 0x1234 and may be negative. 54 | Boolean flags may be: 55 | 1, 0, t, f, T, F, true, false, TRUE, FALSE, True, False 56 | Duration flags accept any input valid for time.ParseDuration. 57 | 58 | The default set of command-line flags is controlled by 59 | top-level functions. The FlagSet type allows one to define 60 | independent sets of flags, such as to implement subcommands 61 | in a command-line interface. The methods of FlagSet are 62 | analogous to the top-level functions for the command-line 63 | flag set. 64 | */ 65 | package flag 66 | 67 | import ( 68 | "errors" 69 | "fmt" 70 | "io" 71 | "os" 72 | "reflect" 73 | "regexp" 74 | "sort" 75 | "strings" 76 | "time" 77 | ) 78 | 79 | // ErrHelp is the error returned if the -help or -h flag is invoked 80 | // but no such flag is defined. 81 | var ErrHelp = errors.New("flag: help requested") 82 | var ErrVersion = errors.New("flag: version requested") 83 | 84 | // Value is the interface to the dynamic value stored in a flag. 85 | // (The default value is represented as a string.) 86 | // 87 | // If a Value has an IsBoolFlag() bool method returning true, 88 | // the command-line parser makes -name equivalent to -name=true 89 | // rather than using the next command-line argument. 90 | // 91 | // Set is called once, in command line order, for each flag present. 92 | // The flag package may call the String method with a zero-valued receiver, 93 | // such as a nil pointer. 94 | type Value interface { 95 | String() string 96 | Set(string) error 97 | } 98 | 99 | // Getter is an interface that allows the contents of a Value to be retrieved. 100 | // It wraps the Value interface, rather than being part of it, because it 101 | // appeared after Go 1 and its compatibility rules. All Value types provided 102 | // by this package satisfy the Getter interface. 103 | type Getter interface { 104 | Value 105 | Get() interface{} 106 | } 107 | 108 | type Flags int 109 | 110 | const ( 111 | PosixShort Flags = 1 << iota 112 | GreedyMode 113 | RegexKeyIsValue 114 | NotValue 115 | ) 116 | 117 | // alias 118 | const ( 119 | Posix = PosixShort 120 | Greedy = GreedyMode 121 | ) 122 | 123 | // ErrorHandling defines how FlagSet.Parse behaves if the parse fails. 124 | type ErrorHandling int 125 | 126 | // These constants cause FlagSet.Parse to behave as described if the parse fails. 127 | const ( 128 | ContinueOnError ErrorHandling = iota // Return a descriptive error. 129 | ExitOnError // Call os.Exit(2). 130 | PanicOnError // Call panic with a descriptive error. 131 | ) 132 | 133 | // A FlagSet represents a set of defined flags. The zero value of a FlagSet 134 | // has no name and has ContinueOnError error handling. 135 | type FlagSet struct { 136 | // Usage is the function called when an error occurs while parsing flags. 137 | // The field is a function (not a method) that may be changed to point to 138 | // a custom error handler. What happens after Usage is called depends 139 | // on the ErrorHandling setting; for the command line, this defaults 140 | // to ExitOnError, which exits the program after calling Usage. 141 | Usage func() 142 | 143 | name string 144 | version string 145 | author string 146 | parsed bool 147 | actual map[string]*Flag 148 | formal map[string]*Flag 149 | shortLong map[string]*Flag 150 | regex map[string]*Flag 151 | 152 | args []string // arguments after flags 153 | unkownArgs []string // Unresolvable parameters 154 | errorHandling ErrorHandling 155 | output io.Writer // nil means stderr; use out() accessor 156 | openPosixShort bool 157 | } 158 | 159 | // A Flag represents the state of a flag. 160 | type Flag struct { 161 | Name string // name as it appears on command line 162 | Usage string // help message 163 | Value Value // value as set 164 | DefValue string // default value (as text); for usage message 165 | 166 | pointer interface{} 167 | // 如果命令行匹配到Name,并且设置NotValue值,则使用MatchValue里面的值 168 | matchValue interface{} // (flags & NotValue) == NotValue 169 | 170 | parent *FlagSet 171 | flags Flags 172 | 173 | Regex string 174 | Short []string 175 | Long []string 176 | isOptOpt bool 177 | } 178 | 179 | // sortFlags returns the flags as a slice in lexicographical sorted order. 180 | func sortFlags(flags map[string]*Flag) []*Flag { 181 | list := make(sort.StringSlice, len(flags)) 182 | i := 0 183 | for _, f := range flags { 184 | list[i] = f.Name 185 | i++ 186 | } 187 | list.Sort() 188 | result := make([]*Flag, len(list)) 189 | for i, name := range list { 190 | result[i] = flags[name] 191 | } 192 | return result 193 | } 194 | 195 | // Output returns the destination for usage and error messages. os.Stderr is returned if 196 | // output was not set or was set to nil. 197 | func (f *FlagSet) Output() io.Writer { 198 | if f.output == nil { 199 | return os.Stderr 200 | } 201 | return f.output 202 | } 203 | 204 | // Name returns the name of the flag set. 205 | func (f *FlagSet) Name() string { 206 | return f.name 207 | } 208 | 209 | // ErrorHandling returns the error handling behavior of the flag set. 210 | func (f *FlagSet) ErrorHandling() ErrorHandling { 211 | return f.errorHandling 212 | } 213 | 214 | // SetOutput sets the destination for usage and error messages. 215 | // If output is nil, os.Stderr is used. 216 | func (f *FlagSet) SetOutput(output io.Writer) { 217 | f.output = output 218 | } 219 | 220 | // VisitAll visits the flags in lexicographical order, calling fn for each. 221 | // It visits all flags, even those not set. 222 | func (f *FlagSet) VisitAll(fn func(*Flag)) { 223 | for _, flag := range sortFlags(f.formal) { 224 | fn(flag) 225 | } 226 | } 227 | 228 | // VisitAll visits the command-line flags in lexicographical order, calling 229 | // fn for each. It visits all flags, even those not set. 230 | func VisitAll(fn func(*Flag)) { 231 | CommandLine.VisitAll(fn) 232 | } 233 | 234 | // Visit visits the flags in lexicographical order, calling fn for each. 235 | // It visits only those flags that have been set. 236 | func (f *FlagSet) Visit(fn func(*Flag)) { 237 | for _, flag := range sortFlags(f.actual) { 238 | fn(flag) 239 | } 240 | } 241 | 242 | // Visit visits the command-line flags in lexicographical order, calling fn 243 | // for each. It visits only those flags that have been set. 244 | func Visit(fn func(*Flag)) { 245 | CommandLine.Visit(fn) 246 | } 247 | 248 | // Lookup returns the Flag structure of the named flag, returning nil if none exists. 249 | func (f *FlagSet) Lookup(name string) *Flag { 250 | return f.formal[name] 251 | } 252 | 253 | // Lookup returns the Flag structure of the named command-line flag, 254 | // returning nil if none exists. 255 | func Lookup(name string) *Flag { 256 | return CommandLine.formal[name] 257 | } 258 | 259 | // Set sets the value of the named flag. 260 | func (f *FlagSet) Set(name, value string) error { 261 | flag, ok := f.formal[name] 262 | if !ok { 263 | return fmt.Errorf("no such flag -%v", name) 264 | } 265 | err := flag.Value.Set(value) 266 | if err != nil { 267 | return err 268 | } 269 | if f.actual == nil { 270 | f.actual = make(map[string]*Flag) 271 | } 272 | f.actual[name] = flag 273 | return nil 274 | } 275 | 276 | // Set sets the value of the named command-line flag. 277 | func Set(name, value string) error { 278 | return CommandLine.Set(name, value) 279 | } 280 | 281 | // isZeroValue guesses whether the string represents the zero 282 | // value for a flag. It is not accurate but in practice works OK. 283 | func isZeroValue(flag *Flag, value string) bool { 284 | // Build a zero value of the flag's Value type, and see if the 285 | // result of calling its String method equals the value passed in. 286 | // This works unless the Value type is itself an interface type. 287 | typ := reflect.TypeOf(flag.Value) 288 | var z reflect.Value 289 | if typ.Kind() == reflect.Ptr { 290 | z = reflect.New(typ.Elem()) 291 | } else { 292 | z = reflect.Zero(typ) 293 | } 294 | if value == z.Interface().(Value).String() { 295 | return true 296 | } 297 | 298 | switch value { 299 | case "false", "", "0": 300 | return true 301 | } 302 | return false 303 | } 304 | 305 | // UnquoteUsage extracts a back-quoted name from the usage 306 | // string for a flag and returns it and the un-quoted usage. 307 | // Given "a `name` to show" it returns ("name", "a name to show"). 308 | // If there are no back quotes, the name is an educated guess of the 309 | // type of the flag's value, or the empty string if the flag is boolean. 310 | func UnquoteUsage(flag *Flag) (name string, usage string) { 311 | // Look for a back-quoted name, but avoid the strings package. 312 | usage = flag.Usage 313 | for i := 0; i < len(usage); i++ { 314 | if usage[i] == '`' { 315 | for j := i + 1; j < len(usage); j++ { 316 | if usage[j] == '`' { 317 | name = usage[i+1 : j] 318 | usage = usage[:i] + name + usage[j+1:] 319 | return name, usage 320 | } 321 | } 322 | break // Only one back quote; use type name. 323 | } 324 | } 325 | // No explicit name, so use type if we can find one. 326 | name = "value" 327 | switch flag.Value.(type) { 328 | case boolFlag: 329 | name = "" 330 | case *durationValue: 331 | name = "duration" 332 | case *durationSliceValue: 333 | name = "duration[]" 334 | case *float64Value: 335 | name = "float" 336 | case *intValue, *int64Value: 337 | name = "int" 338 | case *stringValue: 339 | name = "string" 340 | case *stringSliceValue: 341 | name = "string[]" 342 | case *uintValue, *uint64Value: 343 | name = "uint" 344 | } 345 | return 346 | } 347 | 348 | // PrintDefaults prints, to standard error unless configured otherwise, the 349 | // default values of all defined command-line flags in the set. See the 350 | // documentation for the global function PrintDefaults for more information. 351 | func (f *FlagSet) PrintDefaults() { 352 | f.VisitAll(func(flag *Flag) { 353 | name := strings.Replace(flag.Name, ", ", ", --", -1) 354 | s := fmt.Sprintf(" -%s", name) // Two spaces before -; see next two comments. 355 | name, usage := UnquoteUsage(flag) 356 | if len(name) > 0 { 357 | s += " " + name 358 | } 359 | // Boolean flags of one ASCII letter are so common we 360 | // treat them specially, putting their usage on the same line. 361 | if len(s) <= 4 { // space, space, '-', 'x'. 362 | s += "\t" 363 | } else { 364 | // Four spaces before the tab triggers good alignment 365 | // for both 4- and 8-space tab stops. 366 | s += "\n \t" 367 | } 368 | s += strings.Replace(usage, "\n", "\n \t", -1) 369 | 370 | if !isZeroValue(flag, flag.DefValue) { 371 | if _, ok := flag.Value.(*stringValue); ok { 372 | // put quotes on the value 373 | s += fmt.Sprintf(" (default %q)", flag.DefValue) 374 | } else { 375 | s += fmt.Sprintf(" (default %v)", flag.DefValue) 376 | } 377 | } 378 | fmt.Fprint(f.Output(), s, "\n") 379 | }) 380 | } 381 | 382 | // PrintDefaults prints, to standard error unless configured otherwise, 383 | // a usage message showing the default settings of all defined 384 | // command-line flags. 385 | // For an integer valued flag x, the default output has the form 386 | // -x int 387 | // usage-message-for-x (default 7) 388 | // The usage message will appear on a separate line for anything but 389 | // a bool flag with a one-byte name. For bool flags, the type is 390 | // omitted and if the flag name is one byte the usage message appears 391 | // on the same line. The parenthetical default is omitted if the 392 | // default is the zero value for the type. The listed type, here int, 393 | // can be changed by placing a back-quoted name in the flag's usage 394 | // string; the first such item in the message is taken to be a parameter 395 | // name to show in the message and the back quotes are stripped from 396 | // the message when displayed. For instance, given 397 | // flag.String("I", "", "search `directory` for include files") 398 | // the output will be 399 | // -I directory 400 | // search directory for include files. 401 | func PrintDefaults() { 402 | CommandLine.PrintDefaults() 403 | } 404 | 405 | func (f *FlagSet) printVersion() { 406 | name := f.name 407 | if name != "" { 408 | name += " " 409 | } 410 | 411 | fmt.Fprintf(f.Output(), "%s%s\n", name, f.version) 412 | 413 | } 414 | 415 | func (f *FlagSet) printVersionAuthor() { 416 | if f.author != "" { 417 | fmt.Fprintf(f.Output(), "%s\n\n", f.author) 418 | } 419 | } 420 | 421 | // defaultUsage is the default function to print a usage message. 422 | func (f *FlagSet) defaultUsage() { 423 | 424 | f.printVersionAuthor() 425 | 426 | if f.name == "" { 427 | fmt.Fprintf(f.Output(), "Usage:\n") 428 | } else { 429 | fmt.Fprintf(f.Output(), "Usage of %s:\n", f.name) 430 | } 431 | f.PrintDefaults() 432 | } 433 | 434 | // NOTE: Usage is not just defaultUsage(CommandLine) 435 | // because it serves (via godoc flag Usage) as the example 436 | // for how to write your own usage function. 437 | 438 | // Usage prints a usage message documenting all defined command-line flags 439 | // to CommandLine's output, which by default is os.Stderr. 440 | // It is called when an error occurs while parsing flags. 441 | // The function is a variable that may be changed to point to a custom function. 442 | // By default it prints a simple header and calls PrintDefaults; for details about the 443 | // format of the output and how to control it, see the documentation for PrintDefaults. 444 | // Custom usage functions may choose to exit the program; by default exiting 445 | // happens anyway as the command line's error handling strategy is set to 446 | // ExitOnError. 447 | var Usage = func() { 448 | CommandLine.printVersionAuthor() 449 | fmt.Fprintf(CommandLine.Output(), "Usage of %s:\n", os.Args[0]) 450 | PrintDefaults() 451 | } 452 | 453 | // NFlag returns the number of flags that have been set. 454 | func (f *FlagSet) NFlag() int { return len(f.actual) } 455 | 456 | // NFlag returns the number of command-line flags that have been set. 457 | func NFlag() int { return len(CommandLine.actual) } 458 | 459 | // Arg returns the i'th argument. Arg(0) is the first remaining argument 460 | // after flags have been processed. Arg returns an empty string if the 461 | // requested element does not exist. 462 | func (f *FlagSet) Arg(i int) string { 463 | if i < 0 || i >= len(f.args) { 464 | return "" 465 | } 466 | return f.args[i] 467 | } 468 | 469 | // Arg returns the i'th command-line argument. Arg(0) is the first remaining argument 470 | // after flags have been processed. Arg returns an empty string if the 471 | // requested element does not exist. 472 | func Arg(i int) string { 473 | return CommandLine.Arg(i) 474 | } 475 | 476 | // NArg is the number of arguments remaining after flags have been processed. 477 | func (f *FlagSet) NArg() int { return len(f.args) } 478 | 479 | // NArg is the number of arguments remaining after flags have been processed. 480 | func NArg() int { return len(CommandLine.args) } 481 | 482 | // Args returns the non-flag arguments. 483 | func (f *FlagSet) Args() []string { return f.args } 484 | 485 | // Args returns the non-flag command-line arguments. 486 | func Args() []string { return CommandLine.args } 487 | 488 | // BoolVar defines a bool flag with specified name, default value, and usage string. 489 | // The argument p points to a bool variable in which to store the value of the flag. 490 | func (f *FlagSet) BoolVar(p *bool, name string, value bool, usage string) { 491 | f.Var(newBoolValue(value, p), name, usage) 492 | } 493 | 494 | // BoolVar defines a bool flag with specified name, default value, and usage string. 495 | // The argument p points to a bool variable in which to store the value of the flag. 496 | func BoolVar(p *bool, name string, value bool, usage string) { 497 | CommandLine.Var(newBoolValue(value, p), name, usage) 498 | } 499 | 500 | // Bool defines a bool flag with specified name, default value, and usage string. 501 | // The return value is the address of a bool variable that stores the value of the flag. 502 | func (f *FlagSet) Bool(name string, value bool, usage string) *bool { 503 | p := new(bool) 504 | f.BoolVar(p, name, value, usage) 505 | return p 506 | } 507 | 508 | // Bool defines a bool flag with specified name, default value, and usage string. 509 | // The return value is the address of a bool variable that stores the value of the flag. 510 | func Bool(name string, value bool, usage string) *bool { 511 | return CommandLine.Bool(name, value, usage) 512 | } 513 | 514 | // IntVar defines an int flag with specified name, default value, and usage string. 515 | // The argument p points to an int variable in which to store the value of the flag. 516 | func (f *FlagSet) IntVar(p *int, name string, value int, usage string) { 517 | f.Var(newIntValue(value, p), name, usage) 518 | } 519 | 520 | // IntVar defines an int flag with specified name, default value, and usage string. 521 | // The argument p points to an int variable in which to store the value of the flag. 522 | func IntVar(p *int, name string, value int, usage string) { 523 | CommandLine.Var(newIntValue(value, p), name, usage) 524 | } 525 | 526 | // Int defines an int flag with specified name, default value, and usage string. 527 | // The return value is the address of an int variable that stores the value of the flag. 528 | func (f *FlagSet) Int(name string, value int, usage string) *int { 529 | p := new(int) 530 | f.IntVar(p, name, value, usage) 531 | return p 532 | } 533 | 534 | // Int defines an int flag with specified name, default value, and usage string. 535 | // The return value is the address of an int variable that stores the value of the flag. 536 | func Int(name string, value int, usage string) *int { 537 | return CommandLine.Int(name, value, usage) 538 | } 539 | 540 | // Int64Var defines an int64 flag with specified name, default value, and usage string. 541 | // The argument p points to an int64 variable in which to store the value of the flag. 542 | func (f *FlagSet) Int64Var(p *int64, name string, value int64, usage string) { 543 | f.Var(newInt64Value(value, p), name, usage) 544 | } 545 | 546 | // Int64Var defines an int64 flag with specified name, default value, and usage string. 547 | // The argument p points to an int64 variable in which to store the value of the flag. 548 | func Int64Var(p *int64, name string, value int64, usage string) { 549 | CommandLine.Var(newInt64Value(value, p), name, usage) 550 | } 551 | 552 | // Int64 defines an int64 flag with specified name, default value, and usage string. 553 | // The return value is the address of an int64 variable that stores the value of the flag. 554 | func (f *FlagSet) Int64(name string, value int64, usage string) *int64 { 555 | p := new(int64) 556 | f.Int64Var(p, name, value, usage) 557 | return p 558 | } 559 | 560 | // Int64 defines an int64 flag with specified name, default value, and usage string. 561 | // The return value is the address of an int64 variable that stores the value of the flag. 562 | func Int64(name string, value int64, usage string) *int64 { 563 | return CommandLine.Int64(name, value, usage) 564 | } 565 | 566 | // UintVar defines a uint flag with specified name, default value, and usage string. 567 | // The argument p points to a uint variable in which to store the value of the flag. 568 | func (f *FlagSet) UintVar(p *uint, name string, value uint, usage string) { 569 | f.Var(newUintValue(value, p), name, usage) 570 | } 571 | 572 | // UintVar defines a uint flag with specified name, default value, and usage string. 573 | // The argument p points to a uint variable in which to store the value of the flag. 574 | func UintVar(p *uint, name string, value uint, usage string) { 575 | CommandLine.Var(newUintValue(value, p), name, usage) 576 | } 577 | 578 | // Uint defines a uint flag with specified name, default value, and usage string. 579 | // The return value is the address of a uint variable that stores the value of the flag. 580 | func (f *FlagSet) Uint(name string, value uint, usage string) *uint { 581 | p := new(uint) 582 | f.UintVar(p, name, value, usage) 583 | return p 584 | } 585 | 586 | // Uint defines a uint flag with specified name, default value, and usage string. 587 | // The return value is the address of a uint variable that stores the value of the flag. 588 | func Uint(name string, value uint, usage string) *uint { 589 | return CommandLine.Uint(name, value, usage) 590 | } 591 | 592 | // Uint64Var defines a uint64 flag with specified name, default value, and usage string. 593 | // The argument p points to a uint64 variable in which to store the value of the flag. 594 | func (f *FlagSet) Uint64Var(p *uint64, name string, value uint64, usage string) { 595 | f.Var(newUint64Value(value, p), name, usage) 596 | } 597 | 598 | // Uint64Var defines a uint64 flag with specified name, default value, and usage string. 599 | // The argument p points to a uint64 variable in which to store the value of the flag. 600 | func Uint64Var(p *uint64, name string, value uint64, usage string) { 601 | CommandLine.Var(newUint64Value(value, p), name, usage) 602 | } 603 | 604 | // Uint64 defines a uint64 flag with specified name, default value, and usage string. 605 | // The return value is the address of a uint64 variable that stores the value of the flag. 606 | func (f *FlagSet) Uint64(name string, value uint64, usage string) *uint64 { 607 | p := new(uint64) 608 | f.Uint64Var(p, name, value, usage) 609 | return p 610 | } 611 | 612 | // Uint64 defines a uint64 flag with specified name, default value, and usage string. 613 | // The return value is the address of a uint64 variable that stores the value of the flag. 614 | func Uint64(name string, value uint64, usage string) *uint64 { 615 | return CommandLine.Uint64(name, value, usage) 616 | } 617 | 618 | // StringVar defines a string flag with specified name, default value, and usage string. 619 | // The argument p points to a string variable in which to store the value of the flag. 620 | func (f *FlagSet) StringVar(p *string, name string, value string, usage string) { 621 | f.Var(newStringValue(value, p), name, usage) 622 | } 623 | 624 | // StringVar defines a string flag with specified name, default value, and usage string. 625 | // The argument p points to a string variable in which to store the value of the flag. 626 | func StringVar(p *string, name string, value string, usage string) { 627 | CommandLine.Var(newStringValue(value, p), name, usage) 628 | } 629 | 630 | // String defines a string flag with specified name, default value, and usage string. 631 | // The return value is the address of a string variable that stores the value of the flag. 632 | func (f *FlagSet) String(name string, value string, usage string) *string { 633 | p := new(string) 634 | f.StringVar(p, name, value, usage) 635 | return p 636 | } 637 | 638 | // String defines a string flag with specified name, default value, and usage string. 639 | // The return value is the address of a string variable that stores the value of the flag. 640 | func String(name string, value string, usage string) *string { 641 | return CommandLine.String(name, value, usage) 642 | } 643 | 644 | // Float64Var defines a float64 flag with specified name, default value, and usage string. 645 | // The argument p points to a float64 variable in which to store the value of the flag. 646 | func (f *FlagSet) Float64Var(p *float64, name string, value float64, usage string) { 647 | f.Var(newFloat64Value(value, p), name, usage) 648 | } 649 | 650 | // Float64Var defines a float64 flag with specified name, default value, and usage string. 651 | // The argument p points to a float64 variable in which to store the value of the flag. 652 | func Float64Var(p *float64, name string, value float64, usage string) { 653 | CommandLine.Var(newFloat64Value(value, p), name, usage) 654 | } 655 | 656 | // Float64 defines a float64 flag with specified name, default value, and usage string. 657 | // The return value is the address of a float64 variable that stores the value of the flag. 658 | func (f *FlagSet) Float64(name string, value float64, usage string) *float64 { 659 | p := new(float64) 660 | f.Float64Var(p, name, value, usage) 661 | return p 662 | } 663 | 664 | // Float64 defines a float64 flag with specified name, default value, and usage string. 665 | // The return value is the address of a float64 variable that stores the value of the flag. 666 | func Float64(name string, value float64, usage string) *float64 { 667 | return CommandLine.Float64(name, value, usage) 668 | } 669 | 670 | // DurationVar defines a time.Duration flag with specified name, default value, and usage string. 671 | // The argument p points to a time.Duration variable in which to store the value of the flag. 672 | // The flag accepts a value acceptable to time.ParseDuration. 673 | func (f *FlagSet) DurationVar(p *time.Duration, name string, value time.Duration, usage string) { 674 | f.Var(newDurationValue(value, p), name, usage) 675 | } 676 | 677 | // DurationVar defines a time.Duration flag with specified name, default value, and usage string. 678 | // The argument p points to a time.Duration variable in which to store the value of the flag. 679 | // The flag accepts a value acceptable to time.ParseDuration. 680 | func DurationVar(p *time.Duration, name string, value time.Duration, usage string) { 681 | CommandLine.Var(newDurationValue(value, p), name, usage) 682 | } 683 | 684 | // Duration defines a time.Duration flag with specified name, default value, and usage string. 685 | // The return value is the address of a time.Duration variable that stores the value of the flag. 686 | // The flag accepts a value acceptable to time.ParseDuration. 687 | func (f *FlagSet) Duration(name string, value time.Duration, usage string) *time.Duration { 688 | p := new(time.Duration) 689 | f.DurationVar(p, name, value, usage) 690 | return p 691 | } 692 | 693 | // Duration defines a time.Duration flag with specified name, default value, and usage string. 694 | // The return value is the address of a time.Duration variable that stores the value of the flag. 695 | // The flag accepts a value acceptable to time.ParseDuration. 696 | func Duration(name string, value time.Duration, usage string) *time.Duration { 697 | return CommandLine.Duration(name, value, usage) 698 | } 699 | 700 | // DurationSliceVar defines a time.Duration flag with specified name, default value, and usage string. 701 | // The argument p points to a time.Duration slice variable in which to store the value of the flag. 702 | // The flag accepts a value acceptable to time.ParseDuration. 703 | func (f *FlagSet) DurationSliceVar(p *[]time.Duration, name string, value []time.Duration, usage string) { 704 | f.Var(newDurationSliceValue(value, p), name, usage) 705 | } 706 | 707 | // DurationSliceVar defines a time.Duration flag with specified name, default value, and usage string. 708 | // The argument p points to a time.Duration slice variable in which to store the value of the flag. 709 | // The flag accepts a value acceptable to time.ParseDuration. 710 | func DurationSliceVar(p *[]time.Duration, name string, value []time.Duration, usage string) { 711 | CommandLine.Var(newDurationSliceValue(value, p), name, usage) 712 | } 713 | 714 | // DurationSlice defines a time.Duration flag with specified name, default value, and usage string. 715 | // The return value is the address of a time.Duration slice variable that stores the value of the flag. 716 | // The flag accepts a value acceptable to time.ParseDuration. 717 | func (f *FlagSet) DurationSlice(name string, value []time.Duration, usage string) *[]time.Duration { 718 | p := new([]time.Duration) 719 | f.DurationSliceVar(p, name, value, usage) 720 | return p 721 | } 722 | 723 | // DurationSlice defines a time.Duration flag with specified name, default value, and usage string. 724 | // The return value is the address of a time.Duration slice variable that stores the value of the flag. 725 | // The flag accepts a value acceptable to time.ParseDuration. 726 | func DurationSlice(name string, value []time.Duration, usage string) *[]time.Duration { 727 | return CommandLine.DurationSlice(name, value, usage) 728 | } 729 | 730 | func newName(name string) (string, []string, bool) { 731 | pos := strings.Index(name, ",") 732 | if pos == -1 { 733 | return name, nil, false 734 | } 735 | 736 | names := strings.Split(name, ",") 737 | for k, _ := range names { 738 | names[k] = strings.TrimSpace(names[k]) 739 | } 740 | 741 | sort.Slice(names, func(i, j int) bool { 742 | return len(names[i]) < len(names[j]) 743 | }) 744 | 745 | return strings.Join(names, ", "), names, true 746 | } 747 | 748 | func initFormal(formal *map[string]*Flag) { 749 | if *formal == nil { 750 | *formal = make(map[string]*Flag) 751 | } 752 | } 753 | 754 | func (f *FlagSet) alreadythereError(name string) { 755 | switch name { 756 | case "h", "help", "V", "version": 757 | return 758 | } 759 | var msg string 760 | if f.name == "" { 761 | msg = fmt.Sprintf("flag redefined: %s", name) 762 | } else { 763 | msg = fmt.Sprintf("%s flag redefined: %s", f.name, name) 764 | } 765 | fmt.Fprintln(f.Output(), msg) 766 | panic(msg) // Happens only if flags are declared with identical names 767 | } 768 | 769 | // Var defines a flag with the specified name and usage string. The type and 770 | // value of the flag are represented by the first argument, of type Value, which 771 | // typically holds a user-defined implementation of Value. For instance, the 772 | // caller could create a flag that turns a comma-separated string into a slice 773 | // of strings by giving the slice the methods of Value; in particular, Set would 774 | // decompose the comma-separated string into the slice. 775 | func (f *FlagSet) Var(value Value, name string, usage string) { 776 | // Remember the default value as a string; it won't change. 777 | name, names, ok := newName(name) 778 | flag := &Flag{Name: name, Usage: usage, Value: value, DefValue: value.String()} 779 | if ok { 780 | initFormal(&f.shortLong) 781 | for _, v := range names { 782 | _, alreadythere := f.shortLong[v] 783 | if alreadythere { 784 | f.alreadythereError(v) 785 | } 786 | 787 | f.shortLong[v] = &Flag{Name: v, 788 | Usage: usage, 789 | Value: value, 790 | DefValue: value.String(), 791 | } 792 | } 793 | } 794 | _, alreadythere := f.formal[name] 795 | if alreadythere { 796 | f.alreadythereError(name) 797 | } 798 | 799 | initFormal(&f.formal) 800 | 801 | f.formal[name] = flag 802 | } 803 | 804 | // Var defines a flag with the specified name and usage string. The type and 805 | // value of the flag are represented by the first argument, of type Value, which 806 | // typically holds a user-defined implementation of Value. For instance, the 807 | // caller could create a flag that turns a comma-separated string into a slice 808 | // of strings by giving the slice the methods of Value; in particular, Set would 809 | // decompose the comma-separated string into the slice. 810 | func Var(value Value, name string, usage string) { 811 | CommandLine.Var(value, name, usage) 812 | } 813 | 814 | // failf prints to standard error a formatted error and usage message and 815 | // returns the error. 816 | func (f *FlagSet) failf(format string, a ...interface{}) error { 817 | err := fmt.Errorf(format, a...) 818 | fmt.Fprintln(f.Output(), err) 819 | f.usage() 820 | return err 821 | } 822 | 823 | // usage calls the Usage method for the flag set if one is specified, 824 | // or the appropriate default usage function otherwise. 825 | func (f *FlagSet) usage() { 826 | if f.Usage == nil { 827 | f.defaultUsage() 828 | } else { 829 | f.Usage() 830 | } 831 | } 832 | 833 | func parseNameValue(name string) (string, bool, string) { 834 | for i := 1; i < len(name); i++ { // equals cannot be first 835 | if name[i] == '=' { 836 | return name[0:i], true, name[i+1:] 837 | } 838 | } 839 | 840 | return name, false, "" 841 | } 842 | 843 | func (f *FlagSet) getFlag(name string) (*Flag, bool, error) { 844 | formals := make([]map[string]*Flag, 0, 4) 845 | formals = append(formals, f.formal, f.shortLong, f.regex) 846 | 847 | if name == "help" || name == "h" { // special case for nice help message. 848 | f.usage() 849 | return nil, false, ErrHelp 850 | } 851 | 852 | if name == "version" || name == "V" { 853 | f.printVersion() 854 | return nil, false, ErrVersion 855 | } 856 | 857 | for k, formal := range formals { 858 | if k == len(formals)-1 && len(formal) > 0 { //range regexp map 859 | for k, v := range formal { 860 | //TODO: Compile and the full Regexp interface. 861 | matched, _ := regexp.Match(k, []byte(name)) 862 | if matched { 863 | return v, true, nil 864 | } 865 | } 866 | continue 867 | } 868 | 869 | flag, alreadythere := formal[name] // BUG 870 | if !alreadythere { 871 | continue 872 | } 873 | 874 | return flag, true, nil 875 | } 876 | 877 | return nil, false, fmt.Errorf("flag provided but not defined: -%s", name) 878 | } 879 | 880 | func (f *FlagSet) setFlag(flag *Flag, name string, hasValue bool, value string) (bool, error) { 881 | 882 | if seen, err := f.setValue(flag, name, hasValue, value); err != nil { 883 | return seen, err 884 | } 885 | 886 | if f.actual == nil { 887 | f.actual = make(map[string]*Flag) 888 | } 889 | f.actual[name] = flag 890 | return true, nil 891 | } 892 | 893 | // 核心函数 894 | func (f *FlagSet) setValue(flag *Flag, name string, hasValue bool, value string) (bool, error) { 895 | if flag.flags&NotValue > 0 { 896 | reflect.ValueOf(flag.pointer).Elem().Set(reflect.ValueOf(flag.matchValue)) 897 | return true, nil 898 | } 899 | 900 | if fv, ok := flag.Value.(boolFlag); ok && fv.IsBoolFlag() { // special case: doesn't need an arg 901 | if hasValue { 902 | if err := fv.Set(value); err != nil { 903 | return false, f.failf("invalid boolean value %q for -%s: %v", value, name, err) 904 | } 905 | } else { 906 | if err := fv.Set("true"); err != nil { 907 | return false, f.failf("invalid boolean flag %s: %v", name, err) 908 | } 909 | } 910 | return true, nil 911 | } 912 | 913 | if fv, ok := flag.Value.(*boolSlice); ok { 914 | if err := fv.Set("true"); err != nil { 915 | return false, f.failf("invalid boolean flag %s: %v", name, err) 916 | } 917 | return true, nil 918 | } 919 | // It must have a value, which might be the next argument. 920 | if !hasValue && len(f.args) > 0 { 921 | // value is the next arg 922 | hasValue = true 923 | value, f.args = f.args[0], f.args[1:] 924 | } 925 | if !hasValue { 926 | return false, f.failf("flag needs an argument: -%s", name) 927 | } 928 | 929 | if err := flag.Value.Set(value); err != nil { 930 | return false, f.failf("invalid value %q for flag -%s: %v", value, name, err) 931 | } 932 | 933 | return true, nil 934 | } 935 | 936 | func (f *FlagSet) getName(numMinuses *int, name *string, flags Flags) (bool, bool, error) { 937 | if len(f.args) == 0 { 938 | return false, false, nil 939 | } 940 | s := f.args[0] 941 | if len(s) < 2 || s[0] != '-' { 942 | if flags&GreedyMode != GreedyMode { 943 | f.unkownArgs = append(f.unkownArgs, s) 944 | } 945 | f.args = f.args[1:] 946 | *name = s 947 | return false, true, nil 948 | } 949 | 950 | *numMinuses = 1 951 | if s[1] == '-' { 952 | (*numMinuses)++ 953 | if len(s) == 2 { // "--" terminates the flags 954 | f.args = f.args[1:] 955 | return false, false, nil 956 | } 957 | } 958 | 959 | *name = s[*numMinuses:] 960 | if len(*name) == 0 || (*name)[0] == '-' || (*name)[0] == '=' { 961 | return false, false, f.failf("bad flag syntax: %s", s) 962 | } 963 | 964 | return true, true, nil 965 | } 966 | 967 | func (f *FlagSet) setPosixShortOpt(numMinuses int, name string) (bool, bool, error) { 968 | count := 0 969 | for i := range name { 970 | newName := string(name[i]) 971 | 972 | flag, seen, err := f.getFlag(newName) 973 | if err != nil { 974 | continue 975 | } 976 | 977 | if (flag.flags & PosixShort) == 0 { 978 | continue 979 | } 980 | 981 | hasValue := false 982 | value := "" 983 | isBool := true 984 | 985 | // tail -c+3 986 | if fv, ok := flag.Value.(boolFlag); !(ok && fv.IsBoolFlag()) { 987 | 988 | if i+1 != len(name) { 989 | hasValue = true 990 | value = name[i+1:] 991 | } 992 | 993 | isBool = false 994 | } 995 | 996 | if flag.flags&RegexKeyIsValue > 0 { 997 | value = name[i:] 998 | hasValue = true 999 | } 1000 | 1001 | seen, err = f.setFlag(flag, newName, hasValue, value) 1002 | if err != nil { 1003 | return false, seen, err 1004 | } 1005 | 1006 | if !isBool && !hasValue { 1007 | return false, true, nil 1008 | } 1009 | 1010 | count++ 1011 | } 1012 | 1013 | if count > 0 { 1014 | return false, true, nil 1015 | } 1016 | 1017 | return true, true, nil 1018 | } 1019 | 1020 | func (f *FlagSet) setPosix(seen bool, err error, numMinuses int, name string) (bool, bool, error) { 1021 | if err == ErrHelp { 1022 | return false, false, err 1023 | } 1024 | 1025 | if !f.openPosixShort { 1026 | return false, seen, err 1027 | } 1028 | 1029 | if numMinuses == 1 { 1030 | next, seen, err := f.setPosixShortOpt(numMinuses, name) 1031 | if !next { 1032 | return next, seen, err 1033 | } 1034 | } 1035 | return false, false, f.failf("%s", err.Error()) 1036 | } 1037 | 1038 | // parseOne parses one flag. It reports whether a flag was seen. 1039 | func (f *FlagSet) parseOne() (bool, error) { 1040 | 1041 | name, numMinuses := "", 0 1042 | 1043 | next, seen, err := f.getName(&numMinuses, &name, 0) 1044 | if !next { 1045 | return seen, err 1046 | } 1047 | // it's a flag. does it have an argument? 1048 | f.args = f.args[1:] 1049 | 1050 | name, hasValue, value := parseNameValue(name) 1051 | 1052 | var ( 1053 | flag *Flag 1054 | err0 error 1055 | ) 1056 | 1057 | if flag, seen, err0 = f.getFlag(name); err0 != nil { 1058 | if next, seen, err0 = f.setPosix(seen, err0, numMinuses, name); !next { 1059 | return seen, err0 1060 | } 1061 | } 1062 | 1063 | try: 1064 | if flag.flags&GreedyMode > 0 { 1065 | for len(f.args) > 0 { 1066 | 1067 | name, numMinuses = "", 0 1068 | 1069 | seen, err = f.setFlag(flag, name, hasValue, value) 1070 | if err != nil { 1071 | return seen, err 1072 | } 1073 | next, seen, err = f.getName(&numMinuses, &name, GreedyMode) 1074 | if !seen { 1075 | return seen, err 1076 | } 1077 | if next { 1078 | name, hasValue, value = parseNameValue(name) 1079 | //fmt.Printf("---> name(%s), hasValue(%t), value(%s) args(%s)\n", name, hasValue, value, f.args) 1080 | if flag, seen, err0 = f.getFlag(name); err0 != nil { 1081 | if next, seen, err0 = f.setPosix(seen, err0, numMinuses, name); !next { 1082 | return seen, err0 1083 | } 1084 | } 1085 | 1086 | f.args = f.args[1:] 1087 | goto try 1088 | } 1089 | hasValue = true 1090 | value = name 1091 | } 1092 | } 1093 | 1094 | if flag.flags&RegexKeyIsValue > 0 { 1095 | value = name 1096 | hasValue = true 1097 | } 1098 | return f.setFlag(flag, name, hasValue, value) 1099 | 1100 | } 1101 | 1102 | // Auto-generated Help, Version, and Usage information 1103 | func (f *FlagSet) generatedOpt() { 1104 | f.Bool("h, help", false, "display this help and exit") 1105 | f.Bool("V, version", false, "output version information and exit") 1106 | } 1107 | 1108 | // Parse parses flag definitions from the argument list, which should not 1109 | // include the command name. Must be called after all flags in the FlagSet 1110 | // are defined and before flags are accessed by the program. 1111 | // The return value will be ErrHelp if -help or -h were set but not defined. 1112 | func (f *FlagSet) Parse(arguments []string) error { 1113 | 1114 | f.parsed = true 1115 | f.args = arguments 1116 | 1117 | defer func() { 1118 | f.args = append(f.unkownArgs, f.args...) 1119 | }() 1120 | 1121 | for { 1122 | seen, err := f.parseOne() 1123 | if seen { 1124 | continue 1125 | } 1126 | if err == nil { 1127 | break 1128 | } 1129 | switch f.errorHandling { 1130 | case ContinueOnError: 1131 | return err 1132 | case ExitOnError: 1133 | os.Exit(2) 1134 | case PanicOnError: 1135 | panic(err) 1136 | } 1137 | } 1138 | return nil 1139 | } 1140 | 1141 | // Parsed reports whether f.Parse has been called. 1142 | func (f *FlagSet) Parsed() bool { 1143 | return f.parsed 1144 | } 1145 | 1146 | // Parse parses the command-line flags from os.Args[1:]. Must be called 1147 | // after all flags are defined and before flags are accessed by the program. 1148 | func Parse() { 1149 | // Ignore errors; CommandLine is set for ExitOnError. 1150 | CommandLine.Parse(os.Args[1:]) 1151 | } 1152 | 1153 | // Parsed reports whether the command-line flags have been parsed. 1154 | func Parsed() bool { 1155 | return CommandLine.Parsed() 1156 | } 1157 | 1158 | // CommandLine is the default set of command-line flags, parsed from os.Args. 1159 | // The top-level functions such as BoolVar, Arg, and so on are wrappers for the 1160 | // methods of CommandLine. 1161 | var CommandLine = NewFlagSet(os.Args[0], ExitOnError) 1162 | 1163 | func init() { 1164 | // Override generic FlagSet default Usage with call to global Usage. 1165 | // Note: This is not CommandLine.Usage = Usage, 1166 | // because we want any eventual call to use any updated value of Usage, 1167 | // not the value it has when this line is run. 1168 | CommandLine.Usage = commandLineUsage 1169 | } 1170 | 1171 | func commandLineUsage() { 1172 | Usage() 1173 | } 1174 | 1175 | // NewFlagSet returns a new, empty flag set with the specified name and 1176 | // error handling property. 1177 | func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet { 1178 | f := &FlagSet{ 1179 | name: name, 1180 | errorHandling: errorHandling, 1181 | } 1182 | f.Usage = f.defaultUsage 1183 | f.generatedOpt() 1184 | return f 1185 | } 1186 | 1187 | // 1188 | // 1189 | func (f *FlagSet) Version(version string) *FlagSet { 1190 | f.version = version 1191 | return f 1192 | } 1193 | 1194 | func (f *FlagSet) Author(author string) *FlagSet { 1195 | f.author = author 1196 | return f 1197 | } 1198 | 1199 | func Version(version string) { 1200 | CommandLine.Version(version) 1201 | } 1202 | 1203 | func Author(author string) { 1204 | CommandLine.Author(author) 1205 | } 1206 | 1207 | // Init sets the name and error handling property for a flag set. 1208 | // By default, the zero FlagSet uses an empty name and the 1209 | // ContinueOnError error handling policy. 1210 | func (f *FlagSet) Init(name string, errorHandling ErrorHandling) { 1211 | f.name = name 1212 | f.errorHandling = errorHandling 1213 | } 1214 | -------------------------------------------------------------------------------- /flag_slice.go: -------------------------------------------------------------------------------- 1 | package flag 2 | 3 | import ( 4 | "encoding/json" 5 | "strconv" 6 | ) 7 | 8 | type boolSlice []bool 9 | 10 | func newBoolSliceValue(val []bool, p *[]bool) *boolSlice { 11 | *p = val 12 | return (*boolSlice)(p) 13 | } 14 | 15 | func (b *boolSlice) Set(s string) error { 16 | v, err := strconv.ParseBool(s) 17 | if err != nil { 18 | return err 19 | } 20 | 21 | *b = append(*b, v) 22 | return nil 23 | } 24 | 25 | func (b *boolSlice) String() string { 26 | all, err := json.Marshal(b) 27 | if err != nil { 28 | panic(err.Error()) 29 | } 30 | 31 | return string(all) 32 | } 33 | 34 | func (b *boolSlice) Get() interface{} { 35 | return []bool(*b) 36 | } 37 | 38 | type int64SliceValue []int64 39 | 40 | func newInt64SliceValue(val []int64, p *[]int64) *int64SliceValue { 41 | *p = val 42 | return (*int64SliceValue)(p) 43 | } 44 | 45 | func (i *int64SliceValue) Set(val string) error { 46 | var iv int64Value 47 | 48 | err := iv.Set(val) 49 | if err != nil { 50 | return err 51 | } 52 | 53 | *i = append(*i, int64(iv)) 54 | return nil 55 | } 56 | 57 | func (i *int64SliceValue) Get() interface{} { 58 | return []int64(*i) 59 | } 60 | 61 | func (i *int64SliceValue) String() string { 62 | all, err := json.Marshal(i) 63 | if err != nil { 64 | panic(err.Error()) 65 | } 66 | return string(all) 67 | } 68 | 69 | // Int64SliceVar defines an int64 flag with specified name, default value, and usage string. 70 | // The argument p points to an int64 slice variable in which to store the value of the flag. 71 | func (f *FlagSet) Int64SliceVar(p *[]int64, name string, value []int64, usage string) { 72 | f.Var(newInt64SliceValue(value, p), name, usage) 73 | } 74 | 75 | // Int64SliceVar defines an int64 flag with specified name, default value, and usage string. 76 | // The argument p points to an int64 slice variable in which to store the value of the flag. 77 | func Int64SliceVar(p *[]int64, name string, value []int64, usage string) { 78 | CommandLine.Var(newInt64SliceValue(value, p), name, usage) 79 | } 80 | 81 | // Int64Slice defines an int64 flag with specified name, default value, and usage string. 82 | // The return value is the address of an int64 slice variable that stores the value of the flag. 83 | func (f *FlagSet) Int64Slice(name string, value []int64, usage string) *[]int64 { 84 | p := new([]int64) 85 | f.Int64SliceVar(p, name, value, usage) 86 | return p 87 | } 88 | 89 | // Int64Slice defines an int64 flag with specified name, default value, and usage string. 90 | // The return value is the address of an int64 slice variable that stores the value of the flag. 91 | func Int64Slice(name string, value []int64, usage string) *[]int64 { 92 | return CommandLine.Int64Slice(name, value, usage) 93 | } 94 | 95 | // -- string slice value 96 | type stringSliceValue []string 97 | 98 | func newStringSliceValue(val []string, p *[]string) *stringSliceValue { 99 | *p = val 100 | return (*stringSliceValue)(p) 101 | } 102 | 103 | func (s *stringSliceValue) Set(val string) error { 104 | *s = append(*s, val) 105 | return nil 106 | } 107 | 108 | func (s *stringSliceValue) Get() interface{} { 109 | return []string(*s) 110 | } 111 | 112 | func (s *stringSliceValue) String() string { 113 | all, err := json.Marshal(s) 114 | if err != nil { 115 | panic(err.Error()) 116 | } 117 | return string(all) 118 | } 119 | 120 | // StringSliceVar defines a string flag with specified name, default value, and usage string. 121 | // The argument p points to a string slice variable in which to store the value of the flag. 122 | func (f *FlagSet) StringSliceVar(p *[]string, name string, value []string, usage string) { 123 | f.Var(newStringSliceValue(value, p), name, usage) 124 | } 125 | 126 | // StringSliceVar defines a string flag with specified name, default value, and usage string. 127 | // The argument p points to a string slice variable in which to store the value of the flag. 128 | func StringSliceVar(p *[]string, name string, value []string, usage string) { 129 | CommandLine.Var(newStringSliceValue(value, p), name, usage) 130 | } 131 | 132 | // StringSlice defines a string flag with specified name, default value, and usage string. 133 | // The return value is the address of a string slice variable that stores the value of the flag. 134 | func (f *FlagSet) StringSlice(name string, value []string, usage string) *[]string { 135 | p := new([]string) 136 | f.StringSliceVar(p, name, value, usage) 137 | return p 138 | } 139 | 140 | // StringSlice defines a string flag with specified name, default value, and usage string. 141 | // The return value is the address of a string slice variable that stores the value of the flag. 142 | func StringSlice(name string, value []string, usage string) *[]string { 143 | return CommandLine.StringSlice(name, value, usage) 144 | } 145 | -------------------------------------------------------------------------------- /flag_slice_test.go: -------------------------------------------------------------------------------- 1 | package flag 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func intSliceCmp(int64Slice0, int64Slice1 []int64) bool { 8 | if len(int64Slice0) != len(int64Slice1) { 9 | return false 10 | } 11 | 12 | for k, _ := range int64Slice0 { 13 | if int64Slice0[k] != int64Slice1[k] { 14 | return false 15 | } 16 | } 17 | 18 | return true 19 | } 20 | 21 | func stringSliceCmp(stringSlice0, stringSlice1 []string) bool { 22 | if len(stringSlice0) != len(stringSlice1) { 23 | return false 24 | } 25 | 26 | for k, _ := range stringSlice0 { 27 | if stringSlice0[k] != stringSlice1[k] { 28 | return false 29 | } 30 | } 31 | 32 | return true 33 | } 34 | func testSliceParse(f *FlagSet, t *testing.T) { 35 | intSliceFlag := f.Int64Slice("int64slice", []int64{}, "int 64 slice") 36 | stringSliceFlag := f.StringSlice("stringslice", []string{}, "string slice") 37 | args := []string{ 38 | "--int64slice", "1", 39 | "--int64slice", "2", 40 | "--int64slice", "3", 41 | "--int64slice", "4", 42 | "--stringslice", "header1", 43 | "--stringslice", "header2", 44 | "--stringslice", "header3", 45 | } 46 | 47 | if err := f.Parse(args); err != nil { 48 | t.Fatal(err) 49 | } 50 | 51 | needIntSliceValue := []int64{1, 2, 3, 4} 52 | if !intSliceCmp(*intSliceFlag, needIntSliceValue) { 53 | t.Errorf("int slice falg shout be %v, is %v ", needIntSliceValue, intSliceFlag) 54 | } 55 | 56 | needStringSliceValue := []string{"header1", "header2", "header3"} 57 | if !stringSliceCmp(*stringSliceFlag, needStringSliceValue) { 58 | t.Errorf("string slice falg shout be %v, is %v ", needStringSliceValue, stringSliceFlag) 59 | } 60 | } 61 | 62 | func TestSliceParse(t *testing.T) { 63 | testSliceParse(CommandLine, t) 64 | } 65 | -------------------------------------------------------------------------------- /flag_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package flag_test 6 | 7 | import ( 8 | //"bytes" 9 | "fmt" 10 | . "github.com/guonaihong/flag" 11 | //"io" 12 | //"os" 13 | "sort" 14 | "strconv" 15 | //"strings" 16 | "testing" 17 | "time" 18 | ) 19 | 20 | func boolString(s string) string { 21 | if s == "0" { 22 | return "false" 23 | } 24 | return "true" 25 | } 26 | 27 | func TestEverything(t *testing.T) { 28 | Bool("test_bool", false, "bool value") 29 | Int("test_int", 0, "int value") 30 | Int64("test_int64", 0, "int64 value") 31 | Uint("test_uint", 0, "uint value") 32 | Uint64("test_uint64", 0, "uint64 value") 33 | String("test_string", "0", "string value") 34 | Float64("test_float64", 0, "float64 value") 35 | Duration("test_duration", 0, "time.Duration value") 36 | 37 | m := make(map[string]*Flag) 38 | desired := "0" 39 | visitor := func(f *Flag) { 40 | if len(f.Name) > 5 && f.Name[0:5] == "test_" { 41 | m[f.Name] = f 42 | ok := false 43 | switch { 44 | case f.Value.String() == desired: 45 | ok = true 46 | case f.Name == "test_bool" && f.Value.String() == boolString(desired): 47 | ok = true 48 | case f.Name == "test_duration" && f.Value.String() == desired+"s": 49 | ok = true 50 | } 51 | if !ok { 52 | t.Error("Visit: bad value", f.Value.String(), "for", f.Name) 53 | } 54 | } 55 | } 56 | VisitAll(visitor) 57 | if len(m) != 8 { 58 | t.Error("VisitAll misses some flags") 59 | for k, v := range m { 60 | t.Log(k, *v) 61 | } 62 | } 63 | m = make(map[string]*Flag) 64 | Visit(visitor) 65 | if len(m) != 0 { 66 | t.Errorf("Visit sees unset flags") 67 | for k, v := range m { 68 | t.Log(k, *v) 69 | } 70 | } 71 | // Now set all flags 72 | Set("test_bool", "true") 73 | Set("test_int", "1") 74 | Set("test_int64", "1") 75 | Set("test_uint", "1") 76 | Set("test_uint64", "1") 77 | Set("test_string", "1") 78 | Set("test_float64", "1") 79 | Set("test_duration", "1s") 80 | desired = "1" 81 | Visit(visitor) 82 | if len(m) != 8 { 83 | t.Error("Visit fails after set") 84 | for k, v := range m { 85 | t.Log(k, *v) 86 | } 87 | } 88 | // Now test they're visited in sort order. 89 | var flagNames []string 90 | Visit(func(f *Flag) { flagNames = append(flagNames, f.Name) }) 91 | if !sort.StringsAreSorted(flagNames) { 92 | t.Errorf("flag names not sorted: %v", flagNames) 93 | } 94 | } 95 | 96 | /* 97 | func TestGet(t *testing.T) { 98 | Bool("test_bool", true, "bool value") 99 | Int("test_int", 1, "int value") 100 | Int64("test_int64", 2, "int64 value") 101 | Uint("test_uint", 3, "uint value") 102 | Uint64("test_uint64", 4, "uint64 value") 103 | String("test_string", "5", "string value") 104 | Float64("test_float64", 6, "float64 value") 105 | Duration("test_duration", 7, "time.Duration value") 106 | 107 | visitor := func(f *Flag) { 108 | if len(f.Name) > 5 && f.Name[0:5] == "test_" { 109 | g, ok := f.Value.(Getter) 110 | if !ok { 111 | t.Errorf("Visit: value does not satisfy Getter: %T", f.Value) 112 | return 113 | } 114 | switch f.Name { 115 | case "test_bool": 116 | ok = g.Get() == true 117 | case "test_int": 118 | ok = g.Get() == int(1) 119 | case "test_int64": 120 | ok = g.Get() == int64(2) 121 | case "test_uint": 122 | ok = g.Get() == uint(3) 123 | case "test_uint64": 124 | ok = g.Get() == uint64(4) 125 | case "test_string": 126 | ok = g.Get() == "5" 127 | case "test_float64": 128 | ok = g.Get() == float64(6) 129 | case "test_duration": 130 | ok = g.Get() == time.Duration(7) 131 | } 132 | if !ok { 133 | t.Errorf("Visit: bad value %T(%v) for %s", g.Get(), g.Get(), f.Name) 134 | } 135 | } 136 | } 137 | VisitAll(visitor) 138 | } 139 | */ 140 | 141 | /* 142 | 修改语义,为了支持贪婪模式,这点和标准库的行为不一样 143 | func TestUsage(t *testing.T) { 144 | called := false 145 | if CommandLine.Parse([]string{"-x"}) == nil { 146 | t.Error("parse did not fail for unknown flag") 147 | } 148 | if !called { 149 | t.Error("did not call Usage for unknown flag") 150 | } 151 | } 152 | */ 153 | 154 | func testParse(f *FlagSet, t *testing.T) { 155 | if f.Parsed() { 156 | t.Error("f.Parse() = true before Parse") 157 | } 158 | 159 | boolFlag := f.Bool("bool", false, "bool value") 160 | bool2Flag := f.Bool("bool2", false, "bool2 value") 161 | intFlag := f.Int("int", 0, "int value") 162 | int64Flag := f.Int64("int64", 0, "int64 value") 163 | uintFlag := f.Uint("uint", 0, "uint value") 164 | uint64Flag := f.Uint64("uint64", 0, "uint64 value") 165 | stringFlag := f.String("string", "0", "string value") 166 | float64Flag := f.Float64("float64", 0, "float64 value") 167 | durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value") 168 | extra := "one-extra-argument" 169 | args := []string{ 170 | "-bool", 171 | "-bool2=true", 172 | "--int", "22", 173 | "--int64", "0x23", 174 | "-uint", "24", 175 | "--uint64", "25", 176 | "-string", "hello", 177 | "-float64", "2718e28", 178 | "-duration", "2m", 179 | extra, 180 | } 181 | if err := f.Parse(args); err != nil { 182 | t.Fatal(err) 183 | } 184 | if !f.Parsed() { 185 | t.Error("f.Parse() = false after Parse") 186 | } 187 | if *boolFlag != true { 188 | t.Error("bool flag should be true, is ", *boolFlag) 189 | } 190 | if *bool2Flag != true { 191 | t.Error("bool2 flag should be true, is ", *bool2Flag) 192 | } 193 | if *intFlag != 22 { 194 | t.Error("int flag should be 22, is ", *intFlag) 195 | } 196 | if *int64Flag != 0x23 { 197 | t.Error("int64 flag should be 0x23, is ", *int64Flag) 198 | } 199 | if *uintFlag != 24 { 200 | t.Error("uint flag should be 24, is ", *uintFlag) 201 | } 202 | if *uint64Flag != 25 { 203 | t.Error("uint64 flag should be 25, is ", *uint64Flag) 204 | } 205 | if *stringFlag != "hello" { 206 | t.Error("string flag should be `hello`, is ", *stringFlag) 207 | } 208 | if *float64Flag != 2718e28 { 209 | t.Error("float64 flag should be 2718e28, is ", *float64Flag) 210 | } 211 | if *durationFlag != 2*time.Minute { 212 | t.Error("duration flag should be 2m, is ", *durationFlag) 213 | } 214 | if len(f.Args()) != 1 { 215 | t.Error("expected one argument, got", len(f.Args())) 216 | } else if f.Args()[0] != extra { 217 | t.Errorf("expected argument %q got %q", extra, f.Args()[0]) 218 | } 219 | } 220 | 221 | func TestParse(t *testing.T) { 222 | //testParse(CommandLine, t) 223 | } 224 | 225 | func TestFlagSetParse(t *testing.T) { 226 | testParse(NewFlagSet("test", ContinueOnError), t) 227 | } 228 | 229 | // Declare a user-defined flag type. 230 | type flagVar []string 231 | 232 | func (f *flagVar) String() string { 233 | return fmt.Sprint([]string(*f)) 234 | } 235 | 236 | func (f *flagVar) Set(value string) error { 237 | *f = append(*f, value) 238 | return nil 239 | } 240 | 241 | func TestUserDefined(t *testing.T) { 242 | var flags FlagSet 243 | flags.Init("test", ContinueOnError) 244 | var v flagVar 245 | flags.Var(&v, "v", "usage") 246 | if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil { 247 | t.Error(err) 248 | } 249 | if len(v) != 3 { 250 | t.Fatal("expected 3 args; got ", len(v)) 251 | } 252 | expect := "[1 2 3]" 253 | if v.String() != expect { 254 | t.Errorf("expected value %q got %q", expect, v.String()) 255 | } 256 | } 257 | 258 | /* 259 | func TestUserDefinedForCommandLine(t *testing.T) { 260 | const help = "HELP" 261 | var result string 262 | Usage() 263 | if result != help { 264 | t.Fatalf("got %q; expected %q", result, help) 265 | } 266 | } 267 | */ 268 | 269 | // Declare a user-defined boolean flag type. 270 | type boolFlagVar struct { 271 | count int 272 | } 273 | 274 | func (b *boolFlagVar) String() string { 275 | return fmt.Sprintf("%d", b.count) 276 | } 277 | 278 | func (b *boolFlagVar) Set(value string) error { 279 | if value == "true" { 280 | b.count++ 281 | } 282 | return nil 283 | } 284 | 285 | func (b *boolFlagVar) IsBoolFlag() bool { 286 | return b.count < 4 287 | } 288 | 289 | func TestUserDefinedBool(t *testing.T) { 290 | var flags FlagSet 291 | flags.Init("test", ContinueOnError) 292 | var b boolFlagVar 293 | var err error 294 | flags.Var(&b, "b", "usage") 295 | if err = flags.Parse([]string{"-b", "-b", "-b", "-b=true", "-b=false", "-b", "barg", "-b"}); err != nil { 296 | if b.count < 4 { 297 | t.Error(err) 298 | } 299 | } 300 | 301 | if b.count != 4 { 302 | t.Errorf("want: %d; got: %d", 4, b.count) 303 | } 304 | 305 | if err == nil { 306 | t.Error("expected error; got none") 307 | } 308 | } 309 | 310 | func TestSetOutput(t *testing.T) { 311 | /* 312 | var flags FlagSet 313 | var buf bytes.Buffer 314 | flags.SetOutput(&buf) 315 | flags.Init("test", ContinueOnError) 316 | flags.Parse([]string{"-unknown"}) 317 | if out := buf.String(); !strings.Contains(out, "-unknown") { 318 | t.Logf("expected output mentioning unknown; got %q", out) 319 | } 320 | */ 321 | } 322 | 323 | // This tests that one can reset the flags. This still works but not well, and is 324 | // superseded by FlagSet. 325 | func TestChangingArgs(t *testing.T) { 326 | /* 327 | oldArgs := os.Args 328 | defer func() { os.Args = oldArgs }() 329 | os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"} 330 | before := Bool("before", false, "") 331 | if err := CommandLine.Parse(os.Args[1:]); err != nil { 332 | t.Fatal(err) 333 | } 334 | cmd := Arg(0) 335 | os.Args = Args() 336 | after := Bool("after", false, "") 337 | Parse() 338 | args := Args() 339 | 340 | if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" { 341 | t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args) 342 | } 343 | */ 344 | } 345 | 346 | // Test that -help invokes the usage message and returns ErrHelp. 347 | func TestHelp(t *testing.T) { 348 | /* 349 | var helpCalled = false 350 | fs := NewFlagSet("help test", ContinueOnError) 351 | fs.Usage = func() { helpCalled = true } 352 | var flag bool 353 | fs.BoolVar(&flag, "flag", false, "regular flag") 354 | // Regular flag invocation should work 355 | err := fs.Parse([]string{"-flag=true"}) 356 | if err != nil { 357 | t.Fatal("expected no error; got ", err) 358 | } 359 | if !flag { 360 | t.Error("flag was not set by -flag") 361 | } 362 | if helpCalled { 363 | t.Error("help called for regular flag") 364 | helpCalled = false // reset for next test 365 | } 366 | // Help flag should work as expected. 367 | err = fs.Parse([]string{"-help"}) 368 | if err == nil { 369 | t.Fatal("error expected") 370 | } 371 | if err != ErrHelp { 372 | t.Fatal("expected ErrHelp; got ", err) 373 | } 374 | if !helpCalled { 375 | t.Fatal("help was not called") 376 | } 377 | // If we define a help flag, that should override. 378 | var help bool 379 | fs.BoolVar(&help, "help", false, "help flag") 380 | helpCalled = false 381 | err = fs.Parse([]string{"-help"}) 382 | if err != nil { 383 | t.Fatal("expected no error for defined -help; got ", err) 384 | } 385 | if helpCalled { 386 | t.Fatal("help was called; should not have been for defined help flag") 387 | } 388 | */ 389 | } 390 | 391 | const defaultOutput = ` -A for bootstrapping, allow 'any' type 392 | -Alongflagname 393 | disable bounds checking 394 | -C a boolean defaulting to true (default true) 395 | -D path 396 | set relative path for local imports 397 | -F number 398 | a non-zero number (default 2.7) 399 | -G float 400 | a float that defaults to zero 401 | -M string 402 | a multiline 403 | help 404 | string 405 | -N int 406 | a non-zero int (default 27) 407 | -O a flag 408 | multiline help string (default true) 409 | -Z int 410 | an int that defaults to zero 411 | -maxT timeout 412 | set timeout for dial 413 | ` 414 | 415 | func TestPrintDefaults(t *testing.T) { 416 | /* 417 | fs := NewFlagSet("print defaults test", ContinueOnError) 418 | var buf bytes.Buffer 419 | fs.SetOutput(&buf) 420 | fs.Bool("A", false, "for bootstrapping, allow 'any' type") 421 | fs.Bool("Alongflagname", false, "disable bounds checking") 422 | fs.Bool("C", true, "a boolean defaulting to true") 423 | fs.String("D", "", "set relative `path` for local imports") 424 | fs.Float64("F", 2.7, "a non-zero `number`") 425 | fs.Float64("G", 0, "a float that defaults to zero") 426 | fs.String("M", "", "a multiline\nhelp\nstring") 427 | fs.Int("N", 27, "a non-zero int") 428 | fs.Bool("O", true, "a flag\nmultiline help string") 429 | fs.Int("Z", 0, "an int that defaults to zero") 430 | fs.Duration("maxT", 0, "set `timeout` for dial") 431 | fs.PrintDefaults() 432 | got := buf.String() 433 | if got != defaultOutput { 434 | t.Errorf("got %q want %q\n", got, defaultOutput) 435 | } 436 | */ 437 | } 438 | 439 | // Issue 19230: validate range of Int and Uint flag values. 440 | func TestIntFlagOverflow(t *testing.T) { 441 | if strconv.IntSize != 32 { 442 | return 443 | } 444 | Int("i", 0, "") 445 | Uint("u", 0, "") 446 | if err := Set("i", "2147483648"); err == nil { 447 | t.Error("unexpected success setting Int") 448 | } 449 | if err := Set("u", "4294967296"); err == nil { 450 | t.Error("unexpected success setting Uint") 451 | } 452 | } 453 | 454 | // Issue 20998: Usage should respect CommandLine.output. 455 | func TestUsageOutput(t *testing.T) { 456 | /* 457 | var buf bytes.Buffer 458 | CommandLine.SetOutput(&buf) 459 | defer func(old []string) { os.Args = old }(os.Args) 460 | os.Args = []string{"app", "-i=1", "-unknown"} 461 | Parse() 462 | const want = "flag provided but not defined: -i\nUsage of app:\n" 463 | if got := buf.String(); got != want { 464 | t.Errorf("output = %q; want %q", got, want) 465 | } 466 | */ 467 | } 468 | 469 | func TestGetters(t *testing.T) { 470 | /* 471 | expectedName := "flag set" 472 | expectedErrorHandling := ContinueOnError 473 | expectedOutput := io.Writer(os.Stderr) 474 | fs := NewFlagSet(expectedName, expectedErrorHandling) 475 | 476 | if fs.Name() != expectedName { 477 | t.Errorf("unexpected name: got %s, expected %s", fs.Name(), expectedName) 478 | } 479 | if fs.ErrorHandling() != expectedErrorHandling { 480 | t.Errorf("unexpected ErrorHandling: got %d, expected %d", fs.ErrorHandling(), expectedErrorHandling) 481 | } 482 | if fs.Output() != expectedOutput { 483 | t.Errorf("unexpected output: got %#v, expected %#v", fs.Output(), expectedOutput) 484 | } 485 | 486 | expectedName = "gopher" 487 | expectedErrorHandling = ExitOnError 488 | expectedOutput = os.Stdout 489 | fs.Init(expectedName, expectedErrorHandling) 490 | fs.SetOutput(expectedOutput) 491 | 492 | if fs.Name() != expectedName { 493 | t.Errorf("unexpected name: got %s, expected %s", fs.Name(), expectedName) 494 | } 495 | if fs.ErrorHandling() != expectedErrorHandling { 496 | t.Errorf("unexpected ErrorHandling: got %d, expected %d", fs.ErrorHandling(), expectedErrorHandling) 497 | } 498 | if fs.Output() != expectedOutput { 499 | t.Errorf("unexpected output: got %v, expected %v", fs.Output(), expectedOutput) 500 | } 501 | */ 502 | } 503 | -------------------------------------------------------------------------------- /match_var.go: -------------------------------------------------------------------------------- 1 | package flag 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | func matchCheckValue(p, matchValue interface{}, pValue reflect.Value) { 9 | if pValue.Kind() != reflect.Ptr || pValue.IsNil() { 10 | panic((&InvalidVarError{reflect.TypeOf(p)}).Error()) 11 | } 12 | 13 | if reflect.TypeOf(matchValue) != pValue.Elem().Type() { 14 | panic(fmt.Sprintf("matchvalue type is %v: value type is %v\n", 15 | reflect.TypeOf(matchValue), pValue.Elem().Type())) 16 | } 17 | } 18 | 19 | func (f *Flag) MatchVar(p, matchValue interface{}) { 20 | rv := reflect.ValueOf(p) 21 | 22 | f.flags |= NotValue 23 | 24 | f.pointer = p 25 | f.matchValue = matchValue 26 | 27 | matchCheckValue(p, matchValue, rv) 28 | 29 | f.setVar(rv.Elem(), rv) 30 | } 31 | -------------------------------------------------------------------------------- /match_var_test.go: -------------------------------------------------------------------------------- 1 | package flag 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | type testMatch struct { 10 | optName string 11 | help string 12 | ptr interface{} 13 | want interface{} 14 | } 15 | 16 | func TestMatchVar(t *testing.T) { 17 | fs := NewFlagSet("match var test", ContinueOnError) 18 | 19 | var ( 20 | bt byte 21 | s string 22 | b bool 23 | ui uint 24 | u64 uint64 25 | i int 26 | i64 int64 27 | d time.Duration 28 | f64 float64 29 | strSlice []string 30 | intSlice []int64 31 | boolSlice []bool 32 | ) 33 | 34 | tv := []testMatch{ 35 | {"0, null", "test byte", &bt, byte('\n')}, 36 | {"s", "test string", &s, "open debug"}, 37 | {"b", "test bool", &b, true}, 38 | {"ui", "test uint", &ui, uint(314)}, 39 | {"u64", "test uint64", &u64, uint64(314)}, 40 | {"i", "test int", &i, 0xff}, 41 | {"i64", "test int 64", &i64, int64(33)}, 42 | {"d", "test int", &d, time.Second}, 43 | {"f64", "test float 64", &f64, 3.14}, 44 | {"strSlice", "test string slice", &strSlice, []string{"1", "2", "3"}}, 45 | {"int64Slice", "test int slice", &intSlice, []int64{1, 2, 3}}, 46 | {"boolSlice", "test bool slice", &boolSlice, []bool{true, true, true}}, 47 | } 48 | 49 | for k := range tv { 50 | fs.Opt(tv[k].optName, tv[k].help).Flags(Posix).MatchVar(tv[k].ptr, tv[k].want) 51 | } 52 | 53 | fs.Parse([]string{"-0", "-s", "-b", "-ui", "-u64", "-i", "-i64", "-d", "-f64", "-strSlice", "-int64Slice", "-boolSlice"}) 54 | 55 | for k := range tv { 56 | if !reflect.DeepEqual(reflect.ValueOf(tv[k].ptr).Elem().Interface(), tv[k].want) { 57 | t.Errorf("TestMatchVar %T fail ptr:%p,got:%v, want:%v\n", tv[k].ptr, tv[k].ptr, reflect.ValueOf(tv[k].ptr).Elem().Interface(), tv[k].want) 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /opt.go: -------------------------------------------------------------------------------- 1 | package flag 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "reflect" 7 | "time" 8 | ) 9 | 10 | var boolSliceType = reflect.TypeOf([]bool{}) 11 | 12 | var stringSliceType = reflect.TypeOf([]string{}) 13 | 14 | var int64SliceType = reflect.TypeOf([]int64{}) 15 | 16 | var durationType = reflect.TypeOf(time.Duration(1)) 17 | 18 | func (f *FlagSet) setNamesToMap(m *map[string]*Flag, names []string, flag *Flag) { 19 | 20 | initFormal(m) 21 | for _, v := range names { 22 | _, alreadythere := (*m)[v] 23 | if alreadythere { 24 | f.alreadythereError(v) 25 | } 26 | 27 | newFlag := *flag 28 | newFlag.Name = v 29 | (*m)[v] = &newFlag 30 | } 31 | } 32 | 33 | func (f *FlagSet) flagVar(flag *Flag) { 34 | 35 | if flag.flags&PosixShort > 0 && flag.flags&GreedyMode > 0 { 36 | panic("Cannot set both PosixShort and GreedyMode") 37 | } 38 | 39 | name := flag.Name 40 | var names []string 41 | var ok bool 42 | 43 | if flag.isOptOpt { 44 | f.setNamesToMap(&f.regex, []string{flag.Regex}, flag) 45 | flag.flags ^= RegexKeyIsValue 46 | f.setNamesToMap(&f.shortLong, flag.Short, flag) 47 | f.setNamesToMap(&f.shortLong, flag.Long, flag) 48 | name = flag.Name 49 | } else { 50 | name, names, ok = newName(name) 51 | if ok { 52 | f.setNamesToMap(&f.shortLong, names, flag) 53 | } 54 | flag.Name = name 55 | } 56 | 57 | _, alreadythere := f.formal[name] 58 | if alreadythere { 59 | f.alreadythereError(name) 60 | } 61 | 62 | initFormal(&f.formal) 63 | 64 | f.formal[name] = flag 65 | } 66 | 67 | func (f *FlagSet) OptOpt(opt Flag) *Flag { 68 | var buf bytes.Buffer 69 | 70 | if len(opt.Name) > 0 { 71 | panic("OptOpt function does not support setting Name") 72 | } 73 | 74 | if len(opt.Regex) > 0 { 75 | buf.WriteString(opt.Regex) 76 | } 77 | 78 | if len(opt.Short) > 0 { 79 | if buf.Len() > 0 { 80 | buf.WriteString(", ") 81 | } 82 | 83 | for k, v := range opt.Short { 84 | buf.WriteString(v) 85 | if len(opt.Short) != k+1 { 86 | buf.WriteString(", ") 87 | } 88 | } 89 | } 90 | 91 | if len(opt.Long) > 0 { 92 | if buf.Len() > 0 { 93 | buf.WriteString(", ") 94 | } 95 | 96 | for k, v := range opt.Long { 97 | buf.WriteString(v) 98 | if len(opt.Long) != k+1 { 99 | buf.WriteString(", ") 100 | } 101 | } 102 | } 103 | 104 | if buf.Len() > 0 { 105 | opt.isOptOpt = true 106 | opt.Name = buf.String() 107 | } 108 | 109 | opt.parent = f 110 | return &opt 111 | } 112 | 113 | func (f *FlagSet) Opt(name string, usage string) *Flag { 114 | return &Flag{Name: name, Usage: usage, parent: f} 115 | } 116 | 117 | func (f *Flag) Flags(flag Flags) *Flag { 118 | f.flags |= flag 119 | if flag&PosixShort == PosixShort { 120 | f.parent.openPosixShort = true 121 | } 122 | return f 123 | } 124 | 125 | type InvalidVarError struct { 126 | Type reflect.Type 127 | } 128 | 129 | func (e *InvalidVarError) Error() string { 130 | if e.Type == nil { 131 | return "flag: Var(nil)" 132 | } 133 | 134 | if e.Type.Kind() != reflect.Ptr { 135 | return "flag: Var(non-pointer " + e.Type.String() + ")" 136 | } 137 | 138 | return "flag: Var(nil " + e.Type.String() + ")" 139 | } 140 | 141 | func (f *Flag) setVar(defValue, p reflect.Value) { 142 | vt := p.Elem().Type() 143 | v := p.Elem().Type() 144 | 145 | switch v.Kind() { 146 | case reflect.Uint8: 147 | if vt == reflect.TypeOf(byte(0)) { 148 | f.Value = newByteValue(defValue.Interface().(byte), p.Interface().(*byte)) 149 | } else { 150 | panic("unkown type") 151 | } 152 | case reflect.String: 153 | f.Value = newStringValue(defValue.Interface().(string), p.Interface().(*string)) 154 | case reflect.Bool: 155 | f.Value = newBoolValue(defValue.Interface().(bool), p.Interface().(*bool)) 156 | case reflect.Uint: 157 | f.Value = newUintValue(defValue.Interface().(uint), p.Interface().(*uint)) 158 | case reflect.Uint64: 159 | f.Value = newUint64Value(defValue.Interface().(uint64), p.Interface().(*uint64)) 160 | case reflect.Int: 161 | f.Value = newIntValue(defValue.Interface().(int), p.Interface().(*int)) 162 | case reflect.Int64: 163 | if durationType == vt { 164 | f.Value = newDurationValue(defValue.Interface().(time.Duration), p.Interface().(*time.Duration)) 165 | } else { 166 | f.Value = newInt64Value(defValue.Interface().(int64), p.Interface().(*int64)) 167 | } 168 | case reflect.Float64: 169 | f.Value = newFloat64Value(defValue.Interface().(float64), p.Interface().(*float64)) 170 | case reflect.Slice: 171 | switch vt { 172 | case stringSliceType: 173 | f.Value = newStringSliceValue(defValue.Interface().([]string), p.Interface().(*[]string)) 174 | case int64SliceType: 175 | f.Value = newInt64SliceValue(defValue.Interface().([]int64), p.Interface().(*[]int64)) 176 | case boolSliceType: 177 | f.Value = newBoolSliceValue(defValue.Interface().([]bool), p.Interface().(*[]bool)) 178 | default: 179 | panic(fmt.Sprintf("%v:Unsupported type", vt)) 180 | } 181 | default: 182 | panic("unkown type") 183 | } 184 | 185 | f.parent.flagVar(f) 186 | } 187 | 188 | func checkValue(p, defValue interface{}, rv reflect.Value) { 189 | if rv.Kind() != reflect.Ptr || rv.IsNil() { 190 | p := &InvalidVarError{reflect.TypeOf(p)} 191 | panic(p.Error()) 192 | } 193 | 194 | if reflect.TypeOf(defValue) != rv.Elem().Type() { 195 | panic(fmt.Sprintf("defvalue type is %v: value type is %v\n", 196 | reflect.TypeOf(defValue), rv.Elem().Type())) 197 | } 198 | } 199 | 200 | func (f *Flag) Var(p interface{}) { 201 | rv := reflect.ValueOf(p) 202 | 203 | if rv.Kind() != reflect.Ptr || rv.IsNil() { 204 | p := &InvalidVarError{reflect.TypeOf(p)} 205 | panic(p.Error()) 206 | } 207 | 208 | f.setVar(reflect.Zero(rv.Elem().Type()), rv) 209 | } 210 | 211 | func (f *Flag) DefaultVar(p, defValue interface{}) { 212 | rv := reflect.ValueOf(p) 213 | 214 | checkValue(p, defValue, rv) 215 | 216 | f.setVar(reflect.ValueOf(defValue), rv) 217 | } 218 | 219 | func (f *Flag) NewBool(defValue bool) *bool { 220 | p := new(bool) 221 | f.Value = newBoolValue(defValue, p) 222 | f.parent.flagVar(f) 223 | return p 224 | } 225 | 226 | func (f *Flag) NewString(defValue string) *string { 227 | p := new(string) 228 | f.Value = newStringValue(defValue, p) 229 | f.parent.flagVar(f) 230 | return p 231 | } 232 | 233 | func (f *Flag) NewUint(defValue uint) *uint { 234 | p := new(uint) 235 | f.Value = newUintValue(defValue, p) 236 | f.parent.flagVar(f) 237 | return p 238 | } 239 | 240 | func (f *Flag) NewUint64(defValue uint64) *uint64 { 241 | p := new(uint64) 242 | f.Value = newUint64Value(defValue, p) 243 | f.parent.flagVar(f) 244 | return p 245 | } 246 | 247 | func (f *Flag) NewInt(defValue int) *int { 248 | p := new(int) 249 | f.Value = newIntValue(defValue, p) 250 | f.parent.flagVar(f) 251 | return p 252 | } 253 | 254 | func (f *Flag) NewInt64(defValue int64) *int64 { 255 | p := new(int64) 256 | f.Value = newInt64Value(defValue, p) 257 | f.parent.flagVar(f) 258 | return p 259 | } 260 | 261 | func (f *Flag) NewFloat64(defValue float64) *float64 { 262 | p := new(float64) 263 | f.Value = newFloat64Value(defValue, p) 264 | f.parent.flagVar(f) 265 | return p 266 | } 267 | 268 | func (f *Flag) NewDuration(defValue time.Duration) *time.Duration { 269 | p := new(time.Duration) 270 | f.Value = newDurationValue(defValue, p) 271 | f.parent.flagVar(f) 272 | return p 273 | } 274 | 275 | func (f *Flag) NewInt64Slice(defValue []int64) *[]int64 { 276 | p := new([]int64) 277 | f.Value = newInt64SliceValue(defValue, p) 278 | f.parent.flagVar(f) 279 | return p 280 | } 281 | 282 | func (f *Flag) NewStringSlice(defValue []string) *[]string { 283 | p := new([]string) 284 | f.Value = newStringSliceValue(defValue, p) 285 | f.parent.flagVar(f) 286 | return p 287 | } 288 | 289 | func (f *Flag) NewBoolSlice(defValue []bool) *[]bool { 290 | p := new([]bool) 291 | f.Value = newBoolSliceValue(defValue, p) 292 | f.parent.flagVar(f) 293 | return p 294 | } 295 | 296 | func Opt(name string, usage string) *Flag { 297 | return CommandLine.Opt(name, usage) 298 | } 299 | -------------------------------------------------------------------------------- /opt_test.go: -------------------------------------------------------------------------------- 1 | package flag 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | //env GOPATH=`pwd` go test -test.run=^TestOpt github.com/guonaihong/flag -count=1 -v 9 | 10 | func newFlagSet() (*FlagSet, *int) { 11 | fs := NewFlagSet("tail", ContinueOnError) 12 | lines := fs.OptOpt( 13 | Flag{ 14 | Regex: `^\d+$`, 15 | Short: []string{"n"}, 16 | Long: []string{"lines"}, 17 | Usage: "print the first NUM lines instead of the first 10;" + 18 | "with the leading '-', print all but the last" + 19 | "NUM lines of each file"}). 20 | Flags(RegexKeyIsValue | PosixShort). 21 | NewInt(0) 22 | return fs, lines 23 | } 24 | 25 | func TestOptOpt(t *testing.T) { 26 | 27 | fs, lines := newFlagSet() 28 | fs.Parse([]string{"-1"}) 29 | 30 | if *lines != 1 { 31 | t.Errorf("flag was not set by -1, the actual value is (%d)\n", *lines) 32 | } 33 | 34 | fs, lines = newFlagSet() 35 | fs.Parse([]string{"-2"}) 36 | if *lines != 2 { 37 | t.Errorf("flag was not set by -1, the actual value is (%d)\n", *lines) 38 | } 39 | 40 | fs, lines = newFlagSet() 41 | fs.Parse([]string{"-3"}) 42 | if *lines != 3 { 43 | t.Errorf("flag was not set by -3, the actual value is (%d)\n", *lines) 44 | } 45 | 46 | fs, lines = newFlagSet() 47 | fs.Parse([]string{"-n", "11"}) 48 | if *lines != 11 { 49 | t.Errorf("flag was not set by -11, the actual value is (%d)\n", *lines) 50 | } 51 | 52 | fs, lines = newFlagSet() 53 | fs.Parse([]string{"-n+3"}) 54 | if *lines != 3 { 55 | t.Fatalf("flag was not set by +3, the actual value is (%d)\n", *lines) 56 | } 57 | 58 | fs, lines = newFlagSet() 59 | fs.Parse([]string{"-n4"}) 60 | if *lines != 4 { 61 | t.Fatalf("flag was not set by 4, the actual value is (%d)\n", *lines) 62 | } 63 | } 64 | 65 | func TestOptPosixShortString(t *testing.T) { 66 | 67 | var ignoreCase *bool 68 | var afterContext *string 69 | 70 | fs := NewFlagSet("grep", ContinueOnError) 71 | addOption := func(fs *FlagSet) { 72 | ignoreCase = fs.Opt("i, ignore-case", "Ignore case distinctions,"+ 73 | "so that characters that differ only in case match each other."). 74 | Flags(PosixShort).NewBool(false) 75 | 76 | afterContext = fs.Opt("A, after-context", "Print NUM lines of trailing context after matching lines."+ 77 | "Places a line containing a group separator (--) between contiguous groups of matches. "+ 78 | "With the -o or --only-matching option, this has no effect and a warning is given."). 79 | Flags(PosixShort).NewString("") 80 | } 81 | 82 | addOption(fs) 83 | fs.Parse([]string{"-iA5"}) 84 | 85 | if !*ignoreCase || *afterContext != "5" { 86 | t.Error("flag was not set by -iA5") 87 | } 88 | 89 | fs = NewFlagSet("grep", ContinueOnError) 90 | 91 | addOption(fs) 92 | fs.Parse([]string{"-iA", "66"}) 93 | 94 | if !*ignoreCase || *afterContext != "66" { 95 | t.Error("flag was not set by -iA 66") 96 | } 97 | } 98 | 99 | func TestOptPosixShortBool(t *testing.T) { 100 | fs := NewFlagSet("cat", ContinueOnError) 101 | showNonprinting := fs.Opt("v, show-nonprinting", "use ^ and M- notation, except for LFD and TAB"). 102 | Flags(PosixShort).NewBool(false) 103 | 104 | showTabs := fs.Opt("T, show-tabs", "display TAB characters as ^I").Flags(PosixShort).NewBool(false) 105 | fs.Parse([]string{"-Tv"}) 106 | 107 | if *showNonprinting != true || *showTabs != true { 108 | t.Error("flag was not set by -Tv") 109 | } 110 | 111 | *showNonprinting = false 112 | *showTabs = false 113 | fs.Parse([]string{"-Tv", "--show-nonprinting", "--show-tabs"}) 114 | if *showNonprinting != true && *showTabs != true { 115 | t.Error("flag was not set by --show-nonprinting --show-tabs") 116 | } 117 | 118 | opts := make([]*bool, 7) 119 | opts[0] = fs.Opt("A, show-all", "equivalent to -vET"). 120 | Flags(PosixShort).NewBool(false) 121 | opts[1] = fs.Opt("b, number-nonblank", "number nonempty output lines, overrides -n"). 122 | Flags(PosixShort).NewBool(false) 123 | opts[2] = fs.Opt("e", "equivalent to -vE"). 124 | Flags(PosixShort).NewBool(false) 125 | opts[3] = fs.Opt("E, show-end", "display $ at end of each line"). 126 | Flags(PosixShort).NewBool(false) 127 | opts[4] = fs.Opt("n, numbe", "number all output line"). 128 | Flags(PosixShort).NewBool(false) 129 | opts[5] = fs.Opt("s, squeeze-blank", "suppress repeated empty output lines"). 130 | Flags(PosixShort).NewBool(false) 131 | opts[6] = fs.Opt("t", "equivalent to -vT"). 132 | Flags(PosixShort).NewBool(false) 133 | 134 | fs.Parse([]string{"-tsnEebA"}) 135 | 136 | for _, v := range opts { 137 | if !*v { 138 | t.Error("flag was not set by -tsnEebA") 139 | } 140 | } 141 | 142 | err := fs.Parse([]string{"-m"}) 143 | if err == nil { 144 | t.Fatal("error expected") 145 | } 146 | 147 | err = fs.Parse([]string{"-mA"}) 148 | if err != nil { 149 | t.Fatal("error expected") 150 | } 151 | } 152 | 153 | func TestOptHelp(t *testing.T) { 154 | fs := NewFlagSet("cat", ContinueOnError) 155 | _ = fs.Opt("T, show-tabs", "display TAB characters as ^I").Flags(PosixShort).NewBool(false) 156 | err := fs.Parse([]string{"-vT, -help"}) 157 | if err != nil { 158 | t.Fatal("expected no error; got ", err) 159 | } 160 | 161 | err = fs.Parse([]string{"-h"}) 162 | if err != ErrHelp { 163 | t.Fatal("expected no error; got ", err) 164 | } 165 | } 166 | 167 | // https://github.com/guonaihong/flag/issues/1 168 | type option struct { 169 | Color bool 170 | Version bool 171 | Query []string 172 | } 173 | 174 | func TestOptGreedyMode2(t *testing.T) { 175 | fs := NewFlagSet("test-greedy", ContinueOnError) 176 | 177 | o := option{} 178 | fs.Opt("c, color", "color").Flags(PosixShort).Var(&o.Color) 179 | fs.Opt("v, version", "version").Flags(PosixShort).Var(&o.Version) 180 | fs.Opt("q, query", "query").Flags(GreedyMode).Var(&o.Query) 181 | 182 | fs.Parse([]string{"-q", "hello", "world", "12346", "-cv"}) 183 | 184 | if !o.Color || !o.Version { 185 | t.Errorf("got (%t:%t : want(true:true)\n", o.Color, o.Version) 186 | } 187 | 188 | if len(o.Query) != 3 || o.Query[0] != "hello" && o.Query[1] != "world" && o.Query[2] != "12346" { 189 | t.Errorf("got (%s): want('hello', 'world')\n", o.Query) 190 | } 191 | } 192 | 193 | func TestOptGreedyMode(t *testing.T) { 194 | fs := NewFlagSet("test-custonslice", ContinueOnError) 195 | 196 | header := fs.Opt("H", "http header").Flags(GreedyMode). 197 | NewStringSlice([]string{}) 198 | url := fs.Opt("url", "http url").NewString("") 199 | 200 | fs.Parse([]string{"-H", "sid:sid1234", "time:time-value", "score:1.0", "-url", "test.com"}) 201 | 202 | testHeader := []string{"sid:sid1234", "time:time-value", "score:1.0"} 203 | 204 | if len(*header) != len(testHeader) { 205 | t.Fatal("The parsed header is inconsistent with testHeader", 206 | header, "\n", 207 | testHeader, "\n") 208 | } 209 | 210 | for k := range *header { 211 | if (*header)[k] != testHeader[k] { 212 | t.Fatal("The parsed header is inconsistent with testHeader", 213 | header, "\n", 214 | testHeader, "\n") 215 | } 216 | } 217 | 218 | if *url != "test.com" { 219 | t.Fatal("url fail->", *url, "\n") 220 | } 221 | 222 | args := fs.Args() 223 | if len(args) != 0 { 224 | t.Fatalf("len(args) != 0\n") 225 | } 226 | 227 | // fs2 := NewFlagSet("jm"). 228 | } 229 | 230 | func TestOptParse(t *testing.T) { 231 | fs := NewFlagSet("test", ContinueOnError) 232 | 233 | boolFlag := fs.Opt("bool", "bool value").NewBool(false) 234 | bool2Flag := fs.Opt("bool2", "bool2 value").NewBool(false) 235 | intFlag := fs.Opt("int", "int value").NewInt(0) 236 | int64Flag := fs.Opt("int64", "int64 value").NewInt64(0) 237 | uintFlag := fs.Opt("uint", "uint value").NewUint(0) 238 | uint64Flag := fs.Opt("uint64", "uint64 value").NewUint64(0) 239 | stringFlag := fs.Opt("string", "string value").NewString("0") 240 | float64Flag := fs.Opt("float64", "float64 value").NewFloat64(0) 241 | durationFlag := fs.Opt("duration", "time.Duration value").NewDuration(5 * time.Second) 242 | 243 | extra := "one-extra-argument" 244 | args := []string{ 245 | "-bool", 246 | "-bool2=true", 247 | "--int", "22", 248 | "--int64", "0x23", 249 | "-uint", "24", 250 | "--uint64", "25", 251 | "-string", "hello", 252 | "-float64", "2718e28", 253 | "-duration", "2m", 254 | extra, 255 | } 256 | 257 | if err := fs.Parse(args); err != nil { 258 | t.Fatal(err) 259 | } 260 | if !fs.Parsed() { 261 | t.Error("f.Parse() = false after Parse") 262 | } 263 | if *boolFlag != true { 264 | t.Error("bool flag should be true, is ", *boolFlag) 265 | } 266 | if *bool2Flag != true { 267 | t.Error("bool2 flag should be true, is ", *bool2Flag) 268 | } 269 | if *intFlag != 22 { 270 | t.Error("int flag should be 22, is ", *intFlag) 271 | } 272 | if *int64Flag != 0x23 { 273 | t.Error("int64 flag should be 0x23, is ", *int64Flag) 274 | } 275 | if *uintFlag != 24 { 276 | t.Error("uint flag should be 24, is ", *uintFlag) 277 | } 278 | if *uint64Flag != 25 { 279 | t.Error("uint64 flag should be 25, is ", *uint64Flag) 280 | } 281 | if *stringFlag != "hello" { 282 | t.Error("string flag should be `hello`, is ", *stringFlag) 283 | } 284 | if *float64Flag != 2718e28 { 285 | t.Error("float64 flag should be 2718e28, is ", *float64Flag) 286 | } 287 | if *durationFlag != 2*time.Minute { 288 | t.Error("duration flag should be 2m, is ", *durationFlag) 289 | } 290 | 291 | if len(fs.Args()) != 1 { 292 | t.Error("expected one argument, got", len(fs.Args())) 293 | } else if fs.Args()[0] != extra { 294 | t.Errorf("expected argument %q got %q", extra, fs.Args()[0]) 295 | } 296 | 297 | } 298 | 299 | func testOptBoolSlice1(t *testing.T) { 300 | fs := NewFlagSet("test-bool-slice", ContinueOnError) 301 | var bs []bool 302 | 303 | fs.Opt("v", "test bool slice").Var(&bs) 304 | 305 | args := []string{"-v"} 306 | 307 | fs.Parse(args) 308 | 309 | if len(bs) != 1 { 310 | t.Errorf("got len %d want len 1\n", len(bs)) 311 | } 312 | } 313 | 314 | func testOptBoolSlice2(t *testing.T) { 315 | fs := NewFlagSet("test-bool-slice", ContinueOnError) 316 | var bs []bool 317 | 318 | fs.Opt("v", "test bool slice").Var(&bs) 319 | 320 | args := []string{"-v", "-v"} 321 | 322 | fs.Parse(args) 323 | 324 | if len(bs) != 2 { 325 | t.Errorf("got len %d want len 2\n", len(bs)) 326 | } 327 | } 328 | 329 | // todo debug 330 | func testOptBoolSlice3(t *testing.T) { 331 | fs := NewFlagSet("test-bool-slice", ContinueOnError) 332 | var bs []bool 333 | 334 | fs.Opt("v", "test bool slice").Flags(PosixShort).Var(&bs) 335 | 336 | args := []string{"-vvv"} 337 | 338 | fs.Parse(args) 339 | 340 | if len(bs) != 3 { 341 | t.Errorf("got len %d want len 3\n", len(bs)) 342 | } 343 | } 344 | 345 | func TestOptBoolSlice(t *testing.T) { 346 | testOptBoolSlice1(t) 347 | testOptBoolSlice2(t) 348 | testOptBoolSlice3(t) 349 | } 350 | -------------------------------------------------------------------------------- /struct.go: -------------------------------------------------------------------------------- 1 | package flag 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "reflect" 7 | "strconv" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | func parseFlags(s string) (f Flags) { 13 | 14 | fs := strings.Split(s, "|") 15 | for _, v := range fs { 16 | switch v { 17 | case "posix", "Posix": 18 | f |= PosixShort 19 | case "greedy", "Greedy": 20 | f |= GreedyMode 21 | case "notValue", "NotValue": 22 | f |= NotValue 23 | } 24 | } 25 | return 26 | } 27 | 28 | func parseByte(s string) (b byte, err error) { 29 | 30 | switch s { 31 | case `\a`: 32 | b = '\a' 33 | case `\b`: 34 | b = '\b' 35 | case `\e`: 36 | b = '\x1B' 37 | case `\f`: 38 | b = '\f' 39 | case `\n`: 40 | b = '\n' 41 | case `\r`: 42 | b = '\r' 43 | case `\v`: 44 | b = '\v' 45 | 46 | default: 47 | var u64 uint64 48 | switch { 49 | case strings.HasPrefix(s, "x"): 50 | if u64, err = strconv.ParseUint(s[1:], 16, 16); err != nil { 51 | return 0, err 52 | } 53 | 54 | case strings.HasPrefix(s, "0"): 55 | if u64, err = strconv.ParseUint(s[1:], 8, 16); err != nil { 56 | return 0, err 57 | } 58 | default: 59 | if u64, err = strconv.ParseUint(s[1:], 10, 16); err != nil { 60 | return 0, err 61 | } 62 | 63 | } 64 | 65 | b = byte(u64) 66 | } 67 | 68 | return 69 | 70 | } 71 | 72 | func parseDefValue(v reflect.Value, defValue string, sep string) (rv interface{}) { 73 | var err error 74 | switch v.Kind() { 75 | case reflect.Slice: 76 | if sep == "" { 77 | sep = "," 78 | } 79 | 80 | switch v.Type() { 81 | case stringSliceType: 82 | rv = strings.Split(defValue, sep) 83 | case int64SliceType: 84 | rs := strings.Split(defValue, sep) 85 | int64s := make([]int64, len(rs)) 86 | for k, v := range rs { 87 | i64, err := strconv.ParseInt(v, 10, 0) 88 | if err != nil { 89 | panic(err.Error()) 90 | } 91 | int64s[k] = i64 92 | } 93 | rv = int64s 94 | default: 95 | panic(fmt.Sprintf("unkown slice type:%v #support []stirng and []int64 types", v.Type())) 96 | } 97 | 98 | case reflect.Uint, reflect.Uint64: 99 | n := uint64(0) 100 | n, err = strconv.ParseUint(defValue, 10, 0) 101 | rv = n 102 | if v.Kind() == reflect.Uint { 103 | rv = uint(n) 104 | } 105 | 106 | case reflect.Int: 107 | rv, err = strconv.Atoi(defValue) 108 | case reflect.Int64: 109 | if v.Type() == durationType { 110 | rv, err = time.ParseDuration(defValue) 111 | } else { 112 | rv, err = strconv.ParseInt(defValue, 10, 0) 113 | } 114 | case reflect.Float64: 115 | rv, err = strconv.ParseFloat(defValue, 0) 116 | case reflect.Bool: 117 | rv, err = strconv.ParseBool(defValue) 118 | case reflect.String: 119 | rv = defValue 120 | case reflect.Uint8: 121 | if reflect.TypeOf(byte(0)) == v.Type() { 122 | rv, err = parseByte(defValue) 123 | } else { 124 | panic("invalid type") 125 | } 126 | default: 127 | panic("invalid type") 128 | } 129 | 130 | if err != nil { 131 | panic(err.Error()) 132 | } 133 | 134 | return 135 | } 136 | 137 | func (f *FlagSet) parseStruct(v reflect.Value) bool { 138 | 139 | if v.Kind() == reflect.Ptr { 140 | v = v.Elem() 141 | } 142 | st := v.Type() 143 | 144 | for i := 0; i < st.NumField(); i++ { 145 | sf := st.Field(i) 146 | 147 | if sf.PkgPath != "" && !sf.Anonymous { 148 | continue 149 | } 150 | 151 | sv := v.Field(i) 152 | if sv.Kind() == reflect.Struct { 153 | f.parseStruct(sv) 154 | continue 155 | } 156 | 157 | opt := sf.Tag.Get("opt") 158 | usage := sf.Tag.Get("usage") 159 | defValue := sf.Tag.Get("defValue") 160 | flags := sf.Tag.Get("flags") 161 | 162 | if opt == "" || usage == "" { 163 | continue 164 | } 165 | 166 | if defValue != "" { 167 | f.Opt(opt, usage). 168 | Flags(parseFlags(flags)). 169 | DefaultVar(sv.Addr().Interface(), parseDefValue(sv, defValue, sf.Tag.Get("sep"))) 170 | } else { 171 | f.Opt(opt, usage). 172 | Flags(parseFlags(flags)). 173 | Var(sv.Addr().Interface()) 174 | } 175 | } 176 | return true 177 | } 178 | 179 | func (f *FlagSet) ParseStruct(arguments []string, s interface{}) error { 180 | 181 | v := reflect.ValueOf(s) 182 | 183 | if v.Kind() != reflect.Ptr || v.IsNil() || v.Elem().Kind() != reflect.Struct { 184 | panic("The argument to the function must be a structure pointer") 185 | } 186 | 187 | f.parseStruct(v) 188 | 189 | return f.Parse(arguments) 190 | } 191 | 192 | func ParseStruct(s interface{}) { 193 | // Ignore errors; CommandLine is set for ExitOnError 194 | CommandLine.ParseStruct(os.Args[1:], s) 195 | } 196 | -------------------------------------------------------------------------------- /struct_test.go: -------------------------------------------------------------------------------- 1 | package flag 2 | 3 | import ( 4 | "sort" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | type structOptiont1 struct { 10 | Y int `opt:"y,year" defValue:"2019" usage:"test y"` 11 | } 12 | 13 | type structOptiont2 struct { 14 | M int `opt:"M,month" usage:"test m"` 15 | } 16 | 17 | type structOption struct { 18 | Header []string `opt:"H, header" flags:"posix" usage:"http header"` 19 | MaxThreads int `opt:"m, max" flags:"posix" defValue:"1" usage:"max threads"` 20 | Packet []string `opt:"p, packet" flags:"greedy" usage:"websocket packet"` 21 | Debug bool `opt:"d, debug" defValue:"true" usage:"open debug mode"` 22 | Int int `opt:"i, int" defValue:"31" usage:"test int type"` 23 | Int64 int64 `opt:"i64, int64" defValue:"33" usage:"test int64 type"` 24 | Float64 float64 `opt:"f64, float64" defValue:"1.1" usage:"test floa64 type"` 25 | String string `opt:"s, string" defValue:"test string" usage:"test string type"` 26 | Uint uint `opt:"u, uint" defValue:"17" usage:"test uint type"` 27 | Uint64 uint64 `opt:"u64, uint64" defValue:"13" usage:"test uint64 type"` 28 | StringSlice []string `opt:"ss, string-slice" defValue:"aa,bb" usage:"string slice type"` 29 | StringSlice2 []string `opt:"ss2, string-slice2" defValue:"aa,bb#cc,dd" sep:"#" usage:"string slice type"` 30 | Int64Slice []int64 `opt:"i64s, int64-slice" defValue:"1,2" usage:"int slice type"` 31 | T time.Duration `opt:"t, time" defValue:"1s" usage:"test time.Duration type"` 32 | structOptiont1 33 | structOptiont2 34 | } 35 | 36 | // Test Module-name func-name 37 | func TestStructParse(t *testing.T) { 38 | fs := NewFlagSet("test", ContinueOnError) 39 | 40 | o := structOption{} 41 | 42 | fs.ParseStruct([]string{"-H", "appkey:value1", "-H", "user:username", "-H", "passwd:passwd", "-m", "10", "-p", "@./test.data", "-M", "6"}, &o) 43 | 44 | if o.MaxThreads != 10 { 45 | t.Errorf("maxThreads got %d want 10, %p\n", o.MaxThreads, &o.MaxThreads) 46 | } 47 | 48 | if len(o.Packet) != 1 || o.Packet[0] != "@./test.data" { 49 | t.Errorf("Packet got %s want @./test.data\n", o.Packet) 50 | } 51 | 52 | if len(o.Header) != 3 { 53 | t.Errorf("Header got %s want appkey:value1, user:username passwd:passwd\n", o.Header) 54 | } else { 55 | need := []string{"appkey:value1", "user:username", "passwd:passwd"} 56 | sort.Strings(o.Header) 57 | sort.Strings(need) 58 | 59 | if o.Header[0] != need[0] { 60 | t.Errorf("Header got %s want %s\n", o.Header[0], need[0]) 61 | } 62 | if o.Header[1] != need[1] { 63 | t.Errorf("Header got %s want %s\n", o.Header[1], need[1]) 64 | } 65 | if o.Header[2] != need[2] { 66 | t.Errorf("Header got %s want %s\n", o.Header[2], need[2]) 67 | } 68 | } 69 | 70 | if !o.Debug { 71 | t.Errorf("debug got %t want true\n", o.Debug) 72 | } 73 | 74 | if o.Int != 31 { 75 | t.Errorf("int got %d want 31\n", o.Int) 76 | } 77 | 78 | if o.Int64 != 33 { 79 | t.Errorf("Int64 got %d want 33\n", o.Int64) 80 | } 81 | 82 | if o.Float64 != 1.1 { 83 | t.Errorf("float64 got %f want 1.1\n", o.Float64) 84 | } 85 | 86 | if o.String != "test string" { 87 | t.Errorf("string got %s want test string\n", o.String) 88 | } 89 | 90 | if o.Uint != 17 { 91 | t.Errorf("uint got %d want 17\n", o.Uint) 92 | } 93 | 94 | if o.Uint64 != 13 { 95 | t.Errorf("uint64 got %d want 13\n", o.Uint64) 96 | } 97 | 98 | if len(o.StringSlice) != 2 { 99 | t.Errorf("len(%d) uint64 got %v want [\"aa\", \"bb\"]\n", len(o.StringSlice), o.StringSlice) 100 | } else { 101 | if o.StringSlice[0] != "aa" { 102 | t.Errorf("stringSlice got %s want aa\n", o.StringSlice[0]) 103 | } 104 | 105 | if o.StringSlice[1] != "bb" { 106 | t.Errorf("stringSlice got %s want bb\n", o.StringSlice[1]) 107 | } 108 | } 109 | 110 | if len(o.StringSlice2) != 2 { 111 | t.Errorf("len(%d) uint64 got %v want [\"aa,bb\", \"cc,dd\"]\n", len(o.StringSlice2), o.StringSlice2) 112 | } else { 113 | if o.StringSlice2[0] != "aa,bb" { 114 | t.Errorf("stringSlice got %s want aa,bb\n", o.StringSlice2[0]) 115 | } 116 | 117 | if o.StringSlice2[1] != "cc,dd" { 118 | t.Errorf("stringSlice got %s want cc,dd\n", o.StringSlice2[1]) 119 | } 120 | } 121 | 122 | if len(o.Int64Slice) != 2 { 123 | t.Errorf(`len(%d) uint64 got %v want ["1", "2"]`+"\n", len(o.Int64Slice), o.Int64Slice) 124 | } else { 125 | if o.Int64Slice[0] != 1 { 126 | t.Errorf("intSlice got %d want 1\n", o.Int64Slice[0]) 127 | } 128 | 129 | if o.Int64Slice[1] != 2 { 130 | t.Errorf("intSlice got %d want 2\n", o.Int64Slice[1]) 131 | } 132 | } 133 | 134 | if int64(o.T) != int64(time.Second) { 135 | t.Errorf("got %v want 1s\n", o.T) 136 | } 137 | 138 | if o.Y != 2019 { 139 | t.Errorf("got %v want 2019\n", o.Y) 140 | } 141 | 142 | if o.M != 6 { 143 | t.Errorf("got %v want 6\n", o.M) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /subcommand.go: -------------------------------------------------------------------------------- 1 | package flag 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "sort" 8 | "strings" 9 | ) 10 | 11 | type ParentCommand struct { 12 | Usage func() 13 | name string 14 | output io.Writer 15 | subCommand map[string]*subCommand 16 | subCommand2 map[string]*subCommand 17 | args []string 18 | maxName int 19 | } 20 | 21 | type subCommand struct { 22 | Name string 23 | Usage string 24 | SubProcess func() 25 | } 26 | 27 | func NewParentCommand(name string) *ParentCommand { 28 | p := &ParentCommand{ 29 | name: name, 30 | } 31 | 32 | p.Usage = p.defaultUsage 33 | p.subCommand = make(map[string]*subCommand, 3) 34 | p.subCommand2 = make(map[string]*subCommand, 3) 35 | 36 | return p 37 | } 38 | 39 | func (p *ParentCommand) sortSubUsage() []*subCommand { 40 | list := make([]string, len(p.subCommand)) 41 | i := 0 42 | for _, sub := range p.subCommand { 43 | list[i] = sub.Name 44 | i++ 45 | } 46 | sort.Strings(list) 47 | 48 | result := make([]*subCommand, len(list)) 49 | 50 | for i, name := range list { 51 | result[i] = p.subCommand[name] 52 | } 53 | 54 | return result 55 | } 56 | 57 | func (p *ParentCommand) PrintDefaults() { 58 | subCommand := p.sortSubUsage() 59 | 60 | for _, sub := range subCommand { 61 | 62 | name := sub.Name 63 | if len(name) > 0 { 64 | name = " " + name + " " + strings.Repeat(" ", p.maxName-len(name)) + sub.Usage 65 | } 66 | 67 | fmt.Fprint(p.Output(), name, "\n") 68 | } 69 | } 70 | 71 | func (p *ParentCommand) defaultUsage() { 72 | 73 | if p.name == "" { 74 | fmt.Fprintf(p.Output(), "Usage:\n") 75 | } else { 76 | fmt.Fprintf(p.Output(), "Usage of %s:\n", p.name) 77 | } 78 | 79 | p.PrintDefaults() 80 | } 81 | 82 | func (p *ParentCommand) Output() io.Writer { 83 | if p.output == nil { 84 | return os.Stderr 85 | } 86 | 87 | return p.output 88 | } 89 | 90 | func (p *ParentCommand) SetOutput(output io.Writer) { 91 | p.output = output 92 | } 93 | 94 | func (p *ParentCommand) saveSubCommand(sub map[string]*subCommand, name string, usage string, subProcess func()) { 95 | _, alreadythere := sub[name] 96 | if alreadythere { 97 | msg := "" 98 | if p.name == "" { 99 | msg = fmt.Sprintf("subcommand redefined: %s", name) 100 | } else { 101 | msg = fmt.Sprintf("%s subcommand redefined: %s", p.name, name) 102 | } 103 | fmt.Fprintln(p.Output(), msg) 104 | 105 | panic(msg) 106 | } 107 | 108 | if p.maxName < len(name) { 109 | p.maxName = len(name) 110 | } 111 | 112 | sub[name] = &subCommand{Name: name, Usage: usage, SubProcess: subProcess} 113 | } 114 | 115 | func (p *ParentCommand) SubCommand(name string, usage string, subProcess func()) { 116 | 117 | names := strings.Split(name, ",") 118 | 119 | if len(names) > 1 { 120 | for k, _ := range names { 121 | names[k] = strings.TrimSpace(names[k]) 122 | } 123 | sort.Slice(names, func(i, j int) bool { 124 | return len(names[i]) < len(names[j]) 125 | }) 126 | name = strings.Join(names, ", ") 127 | } 128 | 129 | p.saveSubCommand(p.subCommand, name, usage, subProcess) 130 | 131 | for _, name := range names { 132 | p.saveSubCommand(p.subCommand2, name, usage, subProcess) 133 | } 134 | } 135 | 136 | func (p *ParentCommand) Args() []string { return p.args } 137 | 138 | func (p *ParentCommand) usage() { 139 | if p.Usage == nil { 140 | p.defaultUsage() 141 | } else { 142 | p.Usage() 143 | } 144 | } 145 | 146 | func (p *ParentCommand) parseOne() (bool, error) { 147 | if len(p.args) == 0 { 148 | return false, nil 149 | } 150 | 151 | s := p.args[0] 152 | numMinuses := 0 153 | if s[0] == '-' { 154 | numMinuses++ 155 | if len(s) >= 2 && s[1] == '-' { 156 | numMinuses++ 157 | } 158 | } 159 | 160 | name := s[numMinuses:] 161 | 162 | m := p.subCommand 163 | sub, alreadythere := m[name] 164 | 165 | if !alreadythere { 166 | if name == "h" || name == "help" { 167 | p.usage() 168 | return false, ErrHelp 169 | } 170 | 171 | sub, alreadythere = p.subCommand2[name] 172 | if !alreadythere { 173 | return false, p.failf("subcommand provided but not defined: -%s", name) 174 | } 175 | } 176 | 177 | p.args = p.args[1:] 178 | 179 | sub.SubProcess() 180 | 181 | return true, nil 182 | } 183 | 184 | func (p *ParentCommand) failf(format string, a ...interface{}) error { 185 | err := fmt.Errorf(format, a...) 186 | fmt.Fprintln(p.Output(), err) 187 | p.usage() 188 | return err 189 | } 190 | 191 | func (p *ParentCommand) Parse(arguments []string) error { 192 | 193 | p.args = arguments 194 | 195 | for { 196 | _, err := p.parseOne() 197 | return err 198 | } 199 | return nil 200 | } 201 | -------------------------------------------------------------------------------- /subcommand_test.go: -------------------------------------------------------------------------------- 1 | package flag 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestSubCommand(t *testing.T) { 8 | parent := NewParentCommand("test-subcommand") 9 | 10 | clone, init, add, mv, reset, rm := false, false, false, false, false, false 11 | parent.SubCommand("clone", "Clone a repository into a new directory", func() { 12 | t.Logf("call clone subcommand") 13 | clone = true 14 | }) 15 | 16 | parent.SubCommand("init", "Create an empty Git repository or reinitialize an existing one", func() { 17 | t.Logf("call init subcommand") 18 | init = true 19 | }) 20 | 21 | parent.SubCommand("add", "Add file contents to the index", func() { 22 | t.Logf("call add subcommand") 23 | add = true 24 | }) 25 | 26 | parent.SubCommand("mv", "Move or rename a file, a directory, or a symlink", func() { 27 | t.Logf("call mv subcommand") 28 | mv = true 29 | }) 30 | 31 | parent.SubCommand("reset", "Reset current HEAD to the specified state", func() { 32 | t.Logf("call reset subcommand") 33 | reset = true 34 | }) 35 | 36 | parent.SubCommand("rm", "Remove files from the working tree and from the index", func() { 37 | t.Logf("call rm subcommand") 38 | rm = true 39 | }) 40 | 41 | parent.Parse([]string{"clone"}) 42 | parent.Parse([]string{"init"}) 43 | parent.Parse([]string{"add"}) 44 | parent.Parse([]string{"mv"}) 45 | parent.Parse([]string{"reset"}) 46 | parent.Parse([]string{"rm"}) 47 | 48 | if !clone { 49 | t.Error("clone should be true") 50 | } 51 | 52 | if !init { 53 | t.Error("init should be true") 54 | } 55 | 56 | if !add { 57 | t.Error("add should be true") 58 | } 59 | if !mv { 60 | t.Error("mv should be true") 61 | } 62 | if !reset { 63 | t.Error("reset should be true") 64 | } 65 | if !rm { 66 | t.Error("rm should be true") 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /type.go: -------------------------------------------------------------------------------- 1 | package flag 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "strconv" 7 | "time" 8 | ) 9 | 10 | // -- bool Value 11 | type boolValue bool 12 | 13 | func newBoolValue(val bool, p *bool) *boolValue { 14 | *p = val 15 | return (*boolValue)(p) 16 | } 17 | 18 | func (b *boolValue) Set(s string) error { 19 | v, err := strconv.ParseBool(s) 20 | *b = boolValue(v) 21 | return err 22 | } 23 | 24 | func (b *boolValue) Get() interface{} { return bool(*b) } 25 | 26 | func (b *boolValue) String() string { return strconv.FormatBool(bool(*b)) } 27 | 28 | func (b *boolValue) IsBoolFlag() bool { return true } 29 | 30 | // optional interface to indicate boolean flags that can be 31 | // supplied without "=value" text 32 | type boolFlag interface { 33 | Value 34 | IsBoolFlag() bool 35 | } 36 | 37 | // -- byte value 38 | type byteValue byte 39 | 40 | func newByteValue(val byte, p *byte) *byteValue { 41 | *p = val 42 | return (*byteValue)(p) 43 | } 44 | 45 | func (b *byteValue) Set(s string) error { 46 | v, err := strconv.ParseUint(s, 10, 8) 47 | *b = byteValue(byte(v)) 48 | return err 49 | } 50 | 51 | func (b *byteValue) Get() interface{} { return byte(*b) } 52 | 53 | func (b *byteValue) String() string { return strconv.Itoa(int(*b)) } 54 | 55 | // -- int Value 56 | type intValue int 57 | 58 | func newIntValue(val int, p *int) *intValue { 59 | *p = val 60 | return (*intValue)(p) 61 | } 62 | 63 | func (i *intValue) Set(s string) error { 64 | v, err := strconv.ParseInt(s, 10, 32) 65 | *i = intValue(v) 66 | return err 67 | } 68 | 69 | func (i *intValue) Get() interface{} { return int(*i) } 70 | 71 | func (i *intValue) String() string { return strconv.Itoa(int(*i)) } 72 | 73 | // -- int64 Value 74 | type int64Value int64 75 | 76 | func newInt64Value(val int64, p *int64) *int64Value { 77 | *p = val 78 | return (*int64Value)(p) 79 | } 80 | 81 | func (i *int64Value) Set(s string) error { 82 | v, err := strconv.ParseInt(s, 0, 64) 83 | *i = int64Value(v) 84 | return err 85 | } 86 | 87 | func (i *int64Value) Get() interface{} { return int64(*i) } 88 | 89 | func (i *int64Value) String() string { return strconv.FormatInt(int64(*i), 10) } 90 | 91 | // -- uint Value 92 | type uintValue uint 93 | 94 | func newUintValue(val uint, p *uint) *uintValue { 95 | *p = val 96 | return (*uintValue)(p) 97 | } 98 | 99 | func (i *uintValue) Set(s string) error { 100 | v, err := strconv.ParseUint(s, 0, strconv.IntSize) 101 | *i = uintValue(v) 102 | return err 103 | } 104 | 105 | func (i *uintValue) Get() interface{} { return uint(*i) } 106 | 107 | func (i *uintValue) String() string { return strconv.FormatUint(uint64(*i), 10) } 108 | 109 | // -- uint64 Value 110 | type uint64Value uint64 111 | 112 | func newUint64Value(val uint64, p *uint64) *uint64Value { 113 | *p = val 114 | return (*uint64Value)(p) 115 | } 116 | 117 | func (i *uint64Value) Set(s string) error { 118 | v, err := strconv.ParseUint(s, 0, 64) 119 | *i = uint64Value(v) 120 | return err 121 | } 122 | 123 | func (i *uint64Value) Get() interface{} { return uint64(*i) } 124 | 125 | func (i *uint64Value) String() string { return strconv.FormatUint(uint64(*i), 10) } 126 | 127 | // -- string Value 128 | type stringValue string 129 | 130 | func newStringValue(val string, p *string) *stringValue { 131 | *p = val 132 | return (*stringValue)(p) 133 | } 134 | 135 | func (s *stringValue) Set(val string) error { 136 | *s = stringValue(val) 137 | return nil 138 | } 139 | 140 | func (s *stringValue) Get() interface{} { return string(*s) } 141 | 142 | func (s *stringValue) String() string { return string(*s) } 143 | 144 | // -- float64 Value 145 | type float64Value float64 146 | 147 | func newFloat64Value(val float64, p *float64) *float64Value { 148 | *p = val 149 | return (*float64Value)(p) 150 | } 151 | 152 | func (f *float64Value) Set(s string) error { 153 | v, err := strconv.ParseFloat(s, 64) 154 | *f = float64Value(v) 155 | return err 156 | } 157 | 158 | func (f *float64Value) Get() interface{} { return float64(*f) } 159 | 160 | func (f *float64Value) String() string { return strconv.FormatFloat(float64(*f), 'g', -1, 64) } 161 | 162 | // -- time.Duration Value 163 | type durationValue time.Duration 164 | 165 | func newDurationValue(val time.Duration, p *time.Duration) *durationValue { 166 | *p = val 167 | return (*durationValue)(p) 168 | } 169 | 170 | func (d *durationValue) Set(s string) error { 171 | v, err := time.ParseDuration(s) 172 | *d = durationValue(v) 173 | return err 174 | } 175 | 176 | func (d *durationValue) Get() interface{} { return time.Duration(*d) } 177 | 178 | func (d *durationValue) String() string { return (*time.Duration)(d).String() } 179 | 180 | // -- duration slice value 181 | type durationSliceValue []time.Duration 182 | 183 | func newDurationSliceValue(val []time.Duration, p *[]time.Duration) *durationSliceValue { 184 | *p = val 185 | return (*durationSliceValue)(p) 186 | } 187 | 188 | func (d *durationSliceValue) Set(val string) error { 189 | var dv durationValue 190 | 191 | err := dv.Set(val) 192 | if err != nil { 193 | return err 194 | } 195 | 196 | *d = append(*d, time.Duration(dv)) 197 | return nil 198 | } 199 | 200 | func (d *durationSliceValue) Get() interface{} { 201 | return []time.Duration(*d) 202 | } 203 | 204 | func (d *durationSliceValue) String() string { 205 | switch len(*d) { 206 | case 0: 207 | return "[]" 208 | case 1: 209 | return fmt.Sprintf("[%s]", (*d)[0]) 210 | case 2: 211 | return fmt.Sprintf("[%s, %s]", (*d)[0], (*d)[1]) 212 | case 3: 213 | return fmt.Sprintf("[%s, %s, %s]", (*d)[0], (*d)[1], (*d)[2]) 214 | } 215 | 216 | var buf bytes.Buffer 217 | 218 | buf.WriteString("[") 219 | 220 | for k, v := range *d { 221 | buf.WriteString((*durationValue)(&v).String()) 222 | if k != len(*d)-1 { 223 | buf.WriteString(",") 224 | } 225 | } 226 | 227 | buf.WriteString("]") 228 | 229 | return buf.String() 230 | } 231 | --------------------------------------------------------------------------------