vendor in iptb and kingpin
License: MIT Signed-off-by: Jeromy <jeromyj@gmail.com>
Jeromy committed
Sep 30, 2015 at 11:03 UTC
e202c6592f4a54353222a834acf72c0b28720d14
65 files changed
+12676
-110
Godeps/Godeps.json
+13
@@ -19,6 +19,19 @@
19
"Comment": "v0.7.3-2-g26709e2",
20
"Rev": "26709e2714106fb8ad40b773b711ebce25b78914"
21
},
22
+ {
23
+ "ImportPath": "github.com/alecthomas/kingpin",
24
+ "Comment": "v2.1.0-2-gaedd543",
25
+ "Rev": "aedd5430ecd39ba1396fee0c00308b494c552b1e"
26
+ },
27
+ {
28
+ "ImportPath": "github.com/alecthomas/template",
29
+ "Rev": "b867cc6ab45cece8143cfcc6fc9c77cf3f2c23c0"
30
+ },
31
+ {
32
+ "ImportPath": "github.com/alecthomas/units",
33
+ "Rev": "6b4e7dc5e3143b85ea77909c72caf89416fc2915"
34
+ },
35
{
36
"ImportPath": "github.com/beorn7/perks/quantile",
37
"Rev": "b965b613227fddccbfffe13eae360ed3fa822f8d"
Godeps/_workspace/src/github.com/alecthomas/kingpin/.travis.yml
new
+4
@@ -0,0 +1,4 @@
1
+sudo: false
2
+language: go
3
+install: go get -t -v ./...
4
+go: 1.2
Godeps/_workspace/src/github.com/alecthomas/kingpin/COPYING
new
+19
@@ -0,0 +1,19 @@
1
+Copyright (C) 2014 Alec Thomas
2
+
3
+Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+this software and associated documentation files (the "Software"), to deal in
5
+the Software without restriction, including without limitation the rights to
6
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+of the Software, and to permit persons to whom the Software is furnished to do
8
+so, subject to the following conditions:
9
+
10
+The above copyright notice and this permission notice shall be included in all
11
+copies or substantial portions of the Software.
12
+
13
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+SOFTWARE.
Godeps/_workspace/src/github.com/alecthomas/kingpin/README.md
new
+555
@@ -0,0 +1,555 @@
1
+# Kingpin - A Go (golang) command line and flag parser [](https://travis-ci.org/alecthomas/kingpin)
2
+
3
+<!-- MarkdownTOC -->
4
+
5
+- [Overview](#overview)
6
+- [Features](#features)
7
+- [User-visible changes between v1 and v2](#user-visible-changes-between-v1-and-v2)
8
+ - [Flags can be used at any point after their definition.](#flags-can-be-used-at-any-point-after-their-definition)
9
+ - [Short flags can be combined with their parameters](#short-flags-can-be-combined-with-their-parameters)
10
+- [API changes between v1 and v2](#api-changes-between-v1-and-v2)
11
+- [Versions](#versions)
12
+ - [V2 is the current stable version](#v2-is-the-current-stable-version)
13
+ - [V1 is the OLD stable version](#v1-is-the-old-stable-version)
14
+- [Change History](#change-history)
15
+- [Examples](#examples)
16
+ - [Simple Example](#simple-example)
17
+ - [Complex Example](#complex-example)
18
+- [Reference Documentation](#reference-documentation)
19
+ - [Displaying errors and usage information](#displaying-errors-and-usage-information)
20
+ - [Sub-commands](#sub-commands)
21
+ - [Custom Parsers](#custom-parsers)
22
+ - [Default Values](#default-values)
23
+ - [Place-holders in Help](#place-holders-in-help)
24
+ - [Consuming all remaining arguments](#consuming-all-remaining-arguments)
25
+ - [Custom help](#custom-help)
26
+
27
+<!-- /MarkdownTOC -->
28
+
29
+## Overview
30
+
31
+Kingpin is a [fluent-style](http://en.wikipedia.org/wiki/Fluent_interface),
32
+type-safe command-line parser. It supports flags, nested commands, and
33
+positional arguments.
34
+
35
+Install it with:
36
+
37
+ $ go get gopkg.in/alecthomas/kingpin.v2
38
+
39
+It looks like this:
40
+
41
+```go
42
+var (
43
+ verbose = kingpin.Flag("verbose", "Verbose mode.").Short('v').Bool()
44
+ name = kingpin.Arg("name", "Name of user.").Required().String()
45
+)
46
+
47
+func main() {
48
+ kingpin.Parse()
49
+ fmt.Printf("%v, %s\n", *verbose, *name)
50
+}
51
+```
52
+
53
+More [examples](https://github.com/alecthomas/kingpin/tree/master/examples) are available.
54
+
55
+Second to parsing, providing the user with useful help is probably the most
56
+important thing a command-line parser does. Kingpin tries to provide detailed
57
+contextual help if `--help` is encountered at any point in the command line
58
+(excluding after `--`).
59
+
60
+## Features
61
+
62
+- Help output that isn't as ugly as sin.
63
+- Fully [customisable help](#custom-help), via Go templates.
64
+- Parsed, type-safe flags (`kingpin.Flag("f", "help").Int()`)
65
+- Parsed, type-safe positional arguments (`kingpin.Arg("a", "help").Int()`).
66
+- Parsed, type-safe, arbitrarily deep commands (`kingpin.Command("c", "help")`).
67
+- Support for required flags and required positional arguments (`kingpin.Flag("f", "").Required().Int()`).
68
+- Support for arbitrarily nested default commands (`command.Default()`).
69
+- Callbacks per command, flag and argument (`kingpin.Command("c", "").Action(myAction)`).
70
+- POSIX-style short flag combining (`-a -b` -> `-ab`).
71
+- Short-flag+parameter combining (`-a parm` -> `-aparm`).
72
+- Read command-line from files (`@<file>`).
73
+- Automatically generate man pages (`--man-page`).
74
+
75
+## User-visible changes between v1 and v2
76
+
77
+### Flags can be used at any point after their definition.
78
+
79
+Flags can be specified at any point after their definition, not just
80
+*immediately after their associated command*. From the chat example below, the
81
+following used to be required:
82
+
83
+```
84
+$ chat --server=chat.server.com:8080 post --image=~/Downloads/owls.jpg pics
85
+```
86
+
87
+But the following will now work:
88
+
89
+```
90
+$ chat post --server=chat.server.com:8080 --image=~/Downloads/owls.jpg pics
91
+```
92
+
93
+### Short flags can be combined with their parameters
94
+
95
+Previously, if a short flag was used, any argument to that flag would have to
96
+be separated by a space. That is no longer the case.
97
+
98
+## API changes between v1 and v2
99
+
100
+- `ParseWithFileExpansion()` is gone. The new parser directly supports expanding `@<file>`.
101
+- Added `FatalUsage()` and `FatalUsageContext()` for displaying an error + usage and terminating.
102
+- `Dispatch()` renamed to `Action()`.
103
+- Added `ParseContext()` for parsing a command line into its intermediate context form without executing.
104
+- Added `Terminate()` function to override the termination function.
105
+- Added `UsageForContextWithTemplate()` for printing usage via a custom template.
106
+- Added `UsageTemplate()` for overriding the default template to use. Two templates are included:
107
+ 1. `DefaultUsageTemplate` - default template.
108
+ 2. `CompactUsageTemplate` - compact command template for larger applications.
109
+
110
+## Versions
111
+
112
+Kingpin uses [gopkg.in](https://gopkg.in/alecthomas/kingpin) for versioning.
113
+
114
+The current stable version is [gopkg.in/alecthomas/kingpin.v2](https://gopkg.in/alecthomas/kingpin.v2). The previous version, [gopkg.in/alecthomas/kingpin.v1](https://gopkg.in/alecthomas/kingpin.v1), is deprecated and in maintenance mode.
115
+
116
+### [V2](https://gopkg.in/alecthomas/kingpin.v2) is the current stable version
117
+
118
+Installation:
119
+
120
+```sh
121
+$ go get gopkg.in/alecthomas/kingpin.v2
122
+```
123
+
124
+### [V1](https://gopkg.in/alecthomas/kingpin.v1) is the OLD stable version
125
+
126
+Installation:
127
+
128
+```sh
129
+$ go get gopkg.in/alecthomas/kingpin.v1
130
+```
131
+
132
+## Change History
133
+
134
+- *2015-09-19* -- Stable v2.1.0 release.
135
+ - Added `command.Default()` to specify a default command to use if no other
136
+ command matches. This allows for convenient user shortcuts.
137
+ - Exposed `HelpFlag` and `VersionFlag` for further cusomisation.
138
+ - `Action()` and `PreAction()` added and both now support an arbitrary
139
+ number of callbacks.
140
+ - `kingpin.SeparateOptionalFlagsUsageTemplate`.
141
+ - `--help-long` and `--help-man` (hidden by default) flags.
142
+ - Flags are "interspersed" by default, but can be disabled with `app.Interspersed(false)`.
143
+ - Added flags for all simple builtin types (int8, uint16, etc.) and slice variants.
144
+ - Use `app.Writer(os.Writer)` to specify the default writer for all output functions.
145
+ - Dropped `os.Writer` prefix from all printf-like functions.
146
+
147
+- *2015-05-22* -- Stable v2.0.0 release.
148
+ - Initial stable release of v2.0.0.
149
+ - Fully supports interspersed flags, commands and arguments.
150
+ - Flags can be present at any point after their logical definition.
151
+ - Application.Parse() terminates if commands are present and a command is not parsed.
152
+ - Dispatch() -> Action().
153
+ - Actions are dispatched after all values are populated.
154
+ - Override termination function (defaults to os.Exit).
155
+ - Override output stream (defaults to os.Stderr).
156
+ - Templatised usage help, with default and compact templates.
157
+ - Make error/usage functions more consistent.
158
+ - Support argument expansion from files by default (with @<file>).
159
+ - Fully public data model is available via .Model().
160
+ - Parser has been completely refactored.
161
+ - Parsing and execution has been split into distinct stages.
162
+ - Use `go generate` to generate repeated flags.
163
+ - Support combined short-flag+argument: -fARG.
164
+
165
+- *2015-01-23* -- Stable v1.3.4 release.
166
+ - Support "--" for separating flags from positional arguments.
167
+ - Support loading flags from files (ParseWithFileExpansion()). Use @FILE as an argument.
168
+ - Add post-app and post-cmd validation hooks. This allows arbitrary validation to be added.
169
+ - A bunch of improvements to help usage and formatting.
170
+ - Support arbitrarily nested sub-commands.
171
+
172
+- *2014-07-08* -- Stable v1.2.0 release.
173
+ - Pass any value through to `Strings()` when final argument.
174
+ Allows for values that look like flags to be processed.
175
+ - Allow `--help` to be used with commands.
176
+ - Support `Hidden()` flags.
177
+ - Parser for [units.Base2Bytes](https://github.com/alecthomas/units)
178
+ type. Allows for flags like `--ram=512MB` or `--ram=1GB`.
179
+ - Add an `Enum()` value, allowing only one of a set of values
180
+ to be selected. eg. `Flag(...).Enum("debug", "info", "warning")`.
181
+
182
+- *2014-06-27* -- Stable v1.1.0 release.
183
+ - Bug fixes.
184
+ - Always return an error (rather than panicing) when misconfigured.
185
+ - `OpenFile(flag, perm)` value type added, for finer control over opening files.
186
+ - Significantly improved usage formatting.
187
+
188
+- *2014-06-19* -- Stable v1.0.0 release.
189
+ - Support [cumulative positional](#consuming-all-remaining-arguments) arguments.
190
+ - Return error rather than panic when there are fatal errors not caught by
191
+ the type system. eg. when a default value is invalid.
192
+ - Use gokpg.in.
193
+
194
+- *2014-06-10* -- Place-holder streamlining.
195
+ - Renamed `MetaVar` to `PlaceHolder`.
196
+ - Removed `MetaVarFromDefault`. Kingpin now uses [heuristics](#place-holders-in-help)
197
+ to determine what to display.
198
+
199
+## Examples
200
+
201
+### Simple Example
202
+
203
+Kingpin can be used for simple flag+arg applications like so:
204
+
205
+```
206
+$ ping --help
207
+usage: ping [<flags>] <ip> [<count>]
208
+
209
+Flags:
210
+ --debug Enable debug mode.
211
+ --help Show help.
212
+ -t, --timeout=5s Timeout waiting for ping.
213
+
214
+Args:
215
+ <ip> IP address to ping.
216
+ [<count>] Number of packets to send
217
+$ ping 1.2.3.4 5
218
+Would ping: 1.2.3.4 with timeout 5s and count 0
219
+```
220
+
221
+From the following source:
222
+
223
+```go
224
+package main
225
+
226
+import (
227
+ "fmt"
228
+
229
+ "gopkg.in/alecthomas/kingpin.v2"
230
+)
231
+
232
+var (
233
+ debug = kingpin.Flag("debug", "Enable debug mode.").Bool()
234
+ timeout = kingpin.Flag("timeout", "Timeout waiting for ping.").Default("5s").OverrideDefaultFromEnvar("PING_TIMEOUT").Short('t').Duration()
235
+ ip = kingpin.Arg("ip", "IP address to ping.").Required().IP()
236
+ count = kingpin.Arg("count", "Number of packets to send").Int()
237
+)
238
+
239
+func main() {
240
+ kingpin.Version("0.0.1")
241
+ kingpin.Parse()
242
+ fmt.Printf("Would ping: %s with timeout %s and count %d", *ip, *timeout, *count)
243
+}
244
+```
245
+
246
+### Complex Example
247
+
248
+Kingpin can also produce complex command-line applications with global flags,
249
+subcommands, and per-subcommand flags, like this:
250
+
251
+```
252
+$ chat --help
253
+usage: chat [<flags>] <command> [<flags>] [<args> ...]
254
+
255
+A command-line chat application.
256
+
257
+Flags:
258
+ --help Show help.
259
+ --debug Enable debug mode.
260
+ --server=127.0.0.1 Server address.
261
+
262
+Commands:
263
+ help [<command>]
264
+ Show help for a command.
265
+
266
+ register <nick> <name>
267
+ Register a new user.
268
+
269
+ post [<flags>] <channel> [<text>]
270
+ Post a message to a channel.
271
+
272
+$ chat help post
273
+usage: chat [<flags>] post [<flags>] <channel> [<text>]
274
+
275
+Post a message to a channel.
276
+
277
+Flags:
278
+ --image=IMAGE Image to post.
279
+
280
+Args:
281
+ <channel> Channel to post to.
282
+ [<text>] Text to post.
283
+
284
+$ chat post --image=~/Downloads/owls.jpg pics
285
+...
286
+```
287
+
288
+From this code:
289
+
290
+```go
291
+package main
292
+
293
+import (
294
+ "os"
295
+ "strings"
296
+ "gopkg.in/alecthomas/kingpin.v2"
297
+)
298
+
299
+var (
300
+ app = kingpin.New("chat", "A command-line chat application.")
301
+ debug = app.Flag("debug", "Enable debug mode.").Bool()
302
+ serverIP = app.Flag("server", "Server address.").Default("127.0.0.1").IP()
303
+
304
+ register = app.Command("register", "Register a new user.")
305
+ registerNick = register.Arg("nick", "Nickname for user.").Required().String()
306
+ registerName = register.Arg("name", "Name of user.").Required().String()
307
+
308
+ post = app.Command("post", "Post a message to a channel.")
309
+ postImage = post.Flag("image", "Image to post.").File()
310
+ postChannel = post.Arg("channel", "Channel to post to.").Required().String()
311
+ postText = post.Arg("text", "Text to post.").Strings()
312
+)
313
+
314
+func main() {
315
+ switch kingpin.MustParse(app.Parse(os.Args[1:])) {
316
+ // Register user
317
+ case register.FullCommand():
318
+ println(*registerNick)
319
+
320
+ // Post message
321
+ case post.FullCommand():
322
+ if *postImage != nil {
323
+ }
324
+ text := strings.Join(*postText, " ")
325
+ println("Post:", text)
326
+ }
327
+}
328
+```
329
+
330
+## Reference Documentation
331
+
332
+### Displaying errors and usage information
333
+
334
+Kingpin exports a set of functions to provide consistent errors and usage
335
+information to the user.
336
+
337
+Error messages look something like this:
338
+
339
+ <app>: error: <message>
340
+
341
+The functions on `Application` are:
342
+
343
+Function | Purpose
344
+---------|--------------
345
+`Errorf(format, args)` | Display a printf formatted error to the user.
346
+`Fatalf(format, args)` | As with Errorf, but also call the termination handler.
347
+`FatalUsage(format, args)` | As with Fatalf, but also print contextual usage information.
348
+`FatalUsageContext(context, format, args)` | As with Fatalf, but also print contextual usage information from a `ParseContext`.
349
+`FatalIfError(err, format, args)` | Conditionally print an error prefixed with format+args, then call the termination handler
350
+
351
+There are equivalent global functions in the kingpin namespace for the default
352
+`kingpin.CommandLine` instance.
353
+
354
+### Sub-commands
355
+
356
+Kingpin supports nested sub-commands, with separate flag and positional
357
+arguments per sub-command. Note that positional arguments may only occur after
358
+sub-commands.
359
+
360
+For example:
361
+
362
+```go
363
+var (
364
+ deleteCommand = kingpin.Command("delete", "Delete an object.")
365
+ deleteUserCommand = deleteCommand.Command("user", "Delete a user.")
366
+ deleteUserUIDFlag = deleteUserCommand.Flag("uid", "Delete user by UID rather than username.")
367
+ deleteUserUsername = deleteUserCommand.Arg("username", "Username to delete.")
368
+ deletePostCommand = deleteCommand.Command("post", "Delete a post.")
369
+)
370
+
371
+func main() {
372
+ switch kingpin.Parse() {
373
+ case "delete user":
374
+ case "delete post":
375
+ }
376
+}
377
+```
378
+
379
+### Custom Parsers
380
+
381
+Kingpin supports both flag and positional argument parsers for converting to
382
+Go types. For example, some included parsers are `Int()`, `Float()`,
383
+`Duration()` and `ExistingFile()`.
384
+
385
+Parsers conform to Go's [`flag.Value`](http://godoc.org/flag#Value)
386
+interface, so any existing implementations will work.
387
+
388
+For example, a parser for accumulating HTTP header values might look like this:
389
+
390
+```go
391
+type HTTPHeaderValue http.Header
392
+
393
+func (h *HTTPHeaderValue) Set(value string) error {
394
+ parts := strings.SplitN(value, ":", 2)
395
+ if len(parts) != 2 {
396
+ return fmt.Errorf("expected HEADER:VALUE got '%s'", value)
397
+ }
398
+ (*http.Header)(h).Add(parts[0], parts[1])
399
+ return nil
400
+}
401
+
402
+func (h *HTTPHeaderValue) String() string {
403
+ return ""
404
+}
405
+```
406
+
407
+As a convenience, I would recommend something like this:
408
+
409
+```go
410
+func HTTPHeader(s Settings) (target *http.Header) {
411
+ target = new(http.Header)
412
+ s.SetValue((*HTTPHeaderValue)(target))
413
+ return
414
+}
415
+```
416
+
417
+You would use it like so:
418
+
419
+```go
420
+headers = HTTPHeader(kingpin.Flag("header", "Add a HTTP header to the request.").Short('H'))
421
+```
422
+
423
+### Default Values
424
+
425
+The default value is the zero value for a type. This can be overridden with
426
+the `Default(value)` function on flags and arguments. This function accepts a
427
+string, which is parsed by the value itself, so it *must* be compliant with
428
+the format expected.
429
+
430
+### Place-holders in Help
431
+
432
+The place-holder value for a flag is the value used in the help to describe
433
+the value of a non-boolean flag.
434
+
435
+The value provided to PlaceHolder() is used if provided, then the value
436
+provided by Default() if provided, then finally the capitalised flag name is
437
+used.
438
+
439
+Here are some examples of flags with various permutations:
440
+
441
+ --name=NAME // Flag(...).String()
442
+ --name="Harry" // Flag(...).Default("Harry").String()
443
+ --name=FULL-NAME // flag(...).PlaceHolder("FULL-NAME").Default("Harry").String()
444
+
445
+### Consuming all remaining arguments
446
+
447
+A common command-line idiom is to use all remaining arguments for some
448
+purpose. eg. The following command accepts an arbitrary number of
449
+IP addresses as positional arguments:
450
+
451
+ ./cmd ping 10.1.1.1 192.168.1.1
452
+
453
+Kingpin supports this by having `Value` provide a `IsCumulative() bool`
454
+function. If this function exists and returns true, the value parser will be
455
+called repeatedly for every remaining argument.
456
+
457
+Examples of this are the `Strings()` and `StringMap()` values.
458
+
459
+To implement the above example we might do something like this:
460
+
461
+```go
462
+type ipList []net.IP
463
+
464
+func (i *ipList) Set(value string) error {
465
+ if ip := net.ParseIP(value); ip == nil {
466
+ return fmt.Errorf("'%s' is not an IP address", value)
467
+ } else {
468
+ *i = append(*i, ip)
469
+ return nil
470
+ }
471
+}
472
+
473
+func (i *ipList) String() string {
474
+ return ""
475
+}
476
+
477
+func (i *ipList) IsCumulative() bool {
478
+ return true
479
+}
480
+
481
+func IPList(s Settings) (target *[]net.IP) {
482
+ target = new([]net.IP)
483
+ s.SetValue((*ipList)(target))
484
+ return
485
+}
486
+```
487
+
488
+And use it like so:
489
+
490
+```go
491
+ips := IPList(kingpin.Arg("ips", "IP addresses to ping."))
492
+```
493
+
494
+### Custom help
495
+
496
+Kingpin v2 supports templatised help using the text/template library (actually, [a fork](https://github.com/alecthomas/template)).
497
+
498
+You can specify the template to use with the [Application.UsageTemplate()](http://godoc.org/gopkg.in/alecthomas/kingpin.v2#Application.UsageTemplate) function.
499
+
500
+There are four included templates: `kingpin.DefaultUsageTemplate` is the default,
501
+`kingpin.CompactUsageTemplate` provides a more compact representation for more complex command-line structures,
502
+`kingpin.SeparateOptionalFlagsUsageTemplate` looks like the default template, but splits required
503
+and optional command flags into separate lists, and `kingpin.ManPageTemplate` is used to generate man pages.
504
+
505
+See the above templates for examples of usage, and the the function [UsageForContextWithTemplate()](https://github.com/alecthomas/kingpin/blob/master/usage.go#L198) method for details on the context.
506
+
507
+#### Default help template
508
+
509
+```
510
+$ go run ./examples/curl/curl.go --help
511
+usage: curl [<flags>] <command> [<args> ...]
512
+
513
+An example implementation of curl.
514
+
515
+Flags:
516
+ --help Show help.
517
+ -t, --timeout=5s Set connection timeout.
518
+ -H, --headers=HEADER=VALUE
519
+ Add HTTP headers to the request.
520
+
521
+Commands:
522
+ help [<command>...]
523
+ Show help.
524
+
525
+ get url <url>
526
+ Retrieve a URL.
527
+
528
+ get file <file>
529
+ Retrieve a file.
530
+
531
+ post [<flags>] <url>
532
+ POST a resource.
533
+```
534
+
535
+#### Compact help template
536
+
537
+```
538
+$ go run ./examples/curl/curl.go --help
539
+usage: curl [<flags>] <command> [<args> ...]
540
+
541
+An example implementation of curl.
542
+
543
+Flags:
544
+ --help Show help.
545
+ -t, --timeout=5s Set connection timeout.
546
+ -H, --headers=HEADER=VALUE
547
+ Add HTTP headers to the request.
548
+
549
+Commands:
550
+ help [<command>...]
551
+ get [<flags>]
552
+ url <url>
553
+ file <file>
554
+ post [<flags>] <url>
555
+```
Godeps/_workspace/src/github.com/alecthomas/kingpin/actions.go
new
+42
@@ -0,0 +1,42 @@
1
+package kingpin
2
+
3
+// Action callback executed at various stages after all values are populated.
4
+// The application, commands, arguments and flags all have corresponding
5
+// actions.
6
+type Action func(*ParseContext) error
7
+
8
+type actionMixin struct {
9
+ actions []Action
10
+ preActions []Action
11
+}
12
+
13
+type actionApplier interface {
14
+ applyActions(*ParseContext) error
15
+ applyPreActions(*ParseContext) error
16
+}
17
+
18
+func (a *actionMixin) addAction(action Action) {
19
+ a.actions = append(a.actions, action)
20
+}
21
+
22
+func (a *actionMixin) addPreAction(action Action) {
23
+ a.preActions = append(a.preActions, action)
24
+}
25
+
26
+func (a *actionMixin) applyActions(context *ParseContext) error {
27
+ for _, action := range a.actions {
28
+ if err := action(context); err != nil {
29
+ return err
30
+ }
31
+ }
32
+ return nil
33
+}
34
+
35
+func (a *actionMixin) applyPreActions(context *ParseContext) error {
36
+ for _, preAction := range a.preActions {
37
+ if err := preAction(context); err != nil {
38
+ return err
39
+ }
40
+ }
41
+ return nil
42
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/app.go
new
+544
@@ -0,0 +1,544 @@
1
+package kingpin
2
+
3
+import (
4
+ "fmt"
5
+ "io"
6
+ "os"
7
+ "strings"
8
+)
9
+
10
+var (
11
+ ErrCommandNotSpecified = fmt.Errorf("command not specified")
12
+)
13
+
14
+type ApplicationValidator func(*Application) error
15
+
16
+// An Application contains the definitions of flags, arguments and commands
17
+// for an application.
18
+type Application struct {
19
+ *flagGroup
20
+ *argGroup
21
+ *cmdGroup
22
+ actionMixin
23
+ initialized bool
24
+ Name string
25
+ Help string
26
+ author string
27
+ version string
28
+ writer io.Writer // Destination for usage and errors.
29
+ usageTemplate string
30
+ validator ApplicationValidator
31
+ terminate func(status int) // See Terminate()
32
+ noInterspersed bool // can flags be interspersed with args (or must they come first)
33
+}
34
+
35
+var (
36
+ // Global help flag. Exposed for user customisation.
37
+ HelpFlag *FlagClause
38
+ // Top-level help command. Exposed for user customisation. May be nil.
39
+ HelpCommand *CmdClause
40
+ // Global version flag. Exposed for user customisation. May be nil.
41
+ VersionFlag *FlagClause
42
+)
43
+
44
+// New creates a new Kingpin application instance.
45
+func New(name, help string) *Application {
46
+ a := &Application{
47
+ flagGroup: newFlagGroup(),
48
+ argGroup: newArgGroup(),
49
+ Name: name,
50
+ Help: help,
51
+ writer: os.Stderr,
52
+ usageTemplate: DefaultUsageTemplate,
53
+ terminate: os.Exit,
54
+ }
55
+ a.cmdGroup = newCmdGroup(a)
56
+ HelpFlag = a.Flag("help", "Show context-sensitive help (also try --help-long and --help-man).")
57
+ HelpFlag.Bool()
58
+ a.Flag("help-long", "Generate long help.").Hidden().PreAction(a.generateLongHelp).Bool()
59
+ a.Flag("help-man", "Generate a man page.").Hidden().PreAction(a.generateManPage).Bool()
60
+ return a
61
+}
62
+
63
+func (a *Application) generateLongHelp(c *ParseContext) error {
64
+ a.Writer(os.Stdout)
65
+ if err := a.UsageForContextWithTemplate(c, 2, LongHelpTemplate); err != nil {
66
+ return err
67
+ }
68
+ a.terminate(0)
69
+ return nil
70
+}
71
+
72
+func (a *Application) generateManPage(c *ParseContext) error {
73
+ a.Writer(os.Stdout)
74
+ if err := a.UsageForContextWithTemplate(c, 2, ManPageTemplate); err != nil {
75
+ return err
76
+ }
77
+ a.terminate(0)
78
+ return nil
79
+}
80
+
81
+// Terminate specifies the termination handler. Defaults to os.Exit(status).
82
+// If nil is passed, a no-op function will be used.
83
+func (a *Application) Terminate(terminate func(int)) *Application {
84
+ if terminate == nil {
85
+ terminate = func(int) {}
86
+ }
87
+ a.terminate = terminate
88
+ return a
89
+}
90
+
91
+// Specify the writer to use for usage and errors. Defaults to os.Stderr.
92
+func (a *Application) Writer(w io.Writer) *Application {
93
+ a.writer = w
94
+ return a
95
+}
96
+
97
+// UsageTemplate specifies the text template to use when displaying usage
98
+// information. The default is UsageTemplate.
99
+func (a *Application) UsageTemplate(template string) *Application {
100
+ a.usageTemplate = template
101
+ return a
102
+}
103
+
104
+// Validate sets a validation function to run when parsing.
105
+func (a *Application) Validate(validator ApplicationValidator) *Application {
106
+ a.validator = validator
107
+ return a
108
+}
109
+
110
+// ParseContext parses the given command line and returns the fully populated
111
+// ParseContext.
112
+func (a *Application) ParseContext(args []string) (*ParseContext, error) {
113
+ if err := a.init(); err != nil {
114
+ return nil, err
115
+ }
116
+ context := tokenize(args)
117
+ err := parse(context, a)
118
+ return context, err
119
+}
120
+
121
+// Parse parses command-line arguments. It returns the selected command and an
122
+// error. The selected command will be a space separated subcommand, if
123
+// subcommands have been configured.
124
+//
125
+// This will populate all flag and argument values, call all callbacks, and so
126
+// on.
127
+func (a *Application) Parse(args []string) (command string, err error) {
128
+ context, err := a.ParseContext(args)
129
+ if err != nil {
130
+ if a.hasHelp(args) {
131
+ a.writeUsage(context, err)
132
+ }
133
+ return "", err
134
+ }
135
+ a.maybeHelp(context)
136
+ if !context.EOL() {
137
+ return "", fmt.Errorf("unexpected argument '%s'", context.Peek())
138
+ }
139
+ command, err = a.execute(context)
140
+ if err == ErrCommandNotSpecified {
141
+ a.writeUsage(context, nil)
142
+ }
143
+ return command, err
144
+}
145
+
146
+func (a *Application) writeUsage(context *ParseContext, err error) {
147
+ if err != nil {
148
+ a.Errorf("%s", err)
149
+ }
150
+ if err := a.UsageForContext(context); err != nil {
151
+ panic(err)
152
+ }
153
+ a.terminate(1)
154
+}
155
+
156
+func (a *Application) hasHelp(args []string) bool {
157
+ for _, arg := range args {
158
+ if arg == "--help" {
159
+ return true
160
+ }
161
+ }
162
+ return false
163
+}
164
+
165
+func (a *Application) maybeHelp(context *ParseContext) {
166
+ for _, element := range context.Elements {
167
+ if flag, ok := element.Clause.(*FlagClause); ok && flag == HelpFlag {
168
+ a.writeUsage(context, nil)
169
+ }
170
+ }
171
+}
172
+
173
+// findCommandFromArgs finds a command (if any) from the given command line arguments.
174
+func (a *Application) findCommandFromArgs(args []string) (command string, err error) {
175
+ if err := a.init(); err != nil {
176
+ return "", err
177
+ }
178
+ context := tokenize(args)
179
+ if _, err := a.parse(context); err != nil {
180
+ return "", err
181
+ }
182
+ return a.findCommandFromContext(context), nil
183
+}
184
+
185
+// findCommandFromContext finds a command (if any) from a parsed context.
186
+func (a *Application) findCommandFromContext(context *ParseContext) string {
187
+ commands := []string{}
188
+ for _, element := range context.Elements {
189
+ if c, ok := element.Clause.(*CmdClause); ok {
190
+ commands = append(commands, c.name)
191
+ }
192
+ }
193
+ return strings.Join(commands, " ")
194
+}
195
+
196
+// Version adds a --version flag for displaying the application version.
197
+func (a *Application) Version(version string) *Application {
198
+ a.version = version
199
+ VersionFlag = a.Flag("version", "Show application version.").PreAction(func(*ParseContext) error {
200
+ fmt.Fprintln(a.writer, version)
201
+ a.terminate(0)
202
+ return nil
203
+ })
204
+ VersionFlag.Bool()
205
+ return a
206
+}
207
+
208
+func (a *Application) Author(author string) *Application {
209
+ a.author = author
210
+ return a
211
+}
212
+
213
+// Action callback to call when all values are populated and parsing is
214
+// complete, but before any command, flag or argument actions.
215
+//
216
+// All Action() callbacks are called in the order they are encountered on the
217
+// command line.
218
+func (a *Application) Action(action Action) *Application {
219
+ a.addAction(action)
220
+ return a
221
+}
222
+
223
+// Action called after parsing completes but before validation and execution.
224
+func (a *Application) PreAction(action Action) *Application {
225
+ a.addPreAction(action)
226
+ return a
227
+}
228
+
229
+// Command adds a new top-level command.
230
+func (a *Application) Command(name, help string) *CmdClause {
231
+ return a.addCommand(name, help)
232
+}
233
+
234
+// Interspersed control if flags can be interspersed with positional arguments
235
+//
236
+// true (the default) means that they can, false means that all the flags must appear before the first positional arguments.
237
+func (a *Application) Interspersed(interspersed bool) *Application {
238
+ a.noInterspersed = !interspersed
239
+ return a
240
+}
241
+
242
+func (a *Application) init() error {
243
+ if a.initialized {
244
+ return nil
245
+ }
246
+ if a.cmdGroup.have() && a.argGroup.have() {
247
+ return fmt.Errorf("can't mix top-level Arg()s with Command()s")
248
+ }
249
+
250
+ // If we have subcommands, add a help command at the top-level.
251
+ if a.cmdGroup.have() {
252
+ var command []string
253
+ HelpCommand = a.Command("help", "Show help.").Action(func(c *ParseContext) error {
254
+ a.UsageForContext(c)
255
+ a.terminate(0)
256
+ return nil
257
+ })
258
+ HelpCommand.Arg("command", "Show help on command.").StringsVar(&command)
259
+ // Make help first command.
260
+ l := len(a.commandOrder)
261
+ a.commandOrder = append(a.commandOrder[l-1:l], a.commandOrder[:l-1]...)
262
+ }
263
+
264
+ if err := a.flagGroup.init(); err != nil {
265
+ return err
266
+ }
267
+ if err := a.cmdGroup.init(); err != nil {
268
+ return err
269
+ }
270
+ if err := a.argGroup.init(); err != nil {
271
+ return err
272
+ }
273
+ for _, cmd := range a.commands {
274
+ if err := cmd.init(); err != nil {
275
+ return err
276
+ }
277
+ }
278
+ flagGroups := []*flagGroup{a.flagGroup}
279
+ for _, cmd := range a.commandOrder {
280
+ if err := checkDuplicateFlags(cmd, flagGroups); err != nil {
281
+ return err
282
+ }
283
+ }
284
+ a.initialized = true
285
+ return nil
286
+}
287
+
288
+// Recursively check commands for duplicate flags.
289
+func checkDuplicateFlags(current *CmdClause, flagGroups []*flagGroup) error {
290
+ // Check for duplicates.
291
+ for _, flags := range flagGroups {
292
+ for _, flag := range current.flagOrder {
293
+ if flag.shorthand != 0 {
294
+ if _, ok := flags.short[string(flag.shorthand)]; ok {
295
+ return fmt.Errorf("duplicate short flag -%c", flag.shorthand)
296
+ }
297
+ }
298
+ if _, ok := flags.long[flag.name]; ok {
299
+ return fmt.Errorf("duplicate long flag --%s", flag.name)
300
+ }
301
+ }
302
+ }
303
+ flagGroups = append(flagGroups, current.flagGroup)
304
+ // Check subcommands.
305
+ for _, subcmd := range current.commandOrder {
306
+ if err := checkDuplicateFlags(subcmd, flagGroups); err != nil {
307
+ return err
308
+ }
309
+ }
310
+ return nil
311
+}
312
+
313
+func (a *Application) execute(context *ParseContext) (string, error) {
314
+ var err error
315
+ selected := []string{}
316
+
317
+ if err = a.setDefaults(context); err != nil {
318
+ return "", err
319
+ }
320
+
321
+ selected, err = a.setValues(context)
322
+ if err != nil {
323
+ return "", err
324
+ }
325
+
326
+ if err = a.applyPreActions(context); err != nil {
327
+ return "", err
328
+ }
329
+
330
+ if err = a.validateRequired(context); err != nil {
331
+ return "", err
332
+ }
333
+
334
+ if err = a.applyValidators(context); err != nil {
335
+ return "", err
336
+ }
337
+
338
+ if err = a.applyActions(context); err != nil {
339
+ return "", err
340
+ }
341
+
342
+ command := strings.Join(selected, " ")
343
+ if command == "" && a.cmdGroup.have() {
344
+ return "", ErrCommandNotSpecified
345
+ }
346
+ return command, err
347
+}
348
+
349
+func (a *Application) setDefaults(context *ParseContext) error {
350
+ flagElements := map[string]*ParseElement{}
351
+ for _, element := range context.Elements {
352
+ if flag, ok := element.Clause.(*FlagClause); ok {
353
+ flagElements[flag.name] = element
354
+ }
355
+ }
356
+
357
+ argElements := map[string]*ParseElement{}
358
+ for _, element := range context.Elements {
359
+ if arg, ok := element.Clause.(*ArgClause); ok {
360
+ argElements[arg.name] = element
361
+ }
362
+ }
363
+
364
+ // Check required flags and set defaults.
365
+ for _, flag := range context.flags.long {
366
+ if flagElements[flag.name] == nil {
367
+ // Set defaults, if any.
368
+ if flag.defaultValue != "" {
369
+ if err := flag.value.Set(flag.defaultValue); err != nil {
370
+ return err
371
+ }
372
+ }
373
+ }
374
+ }
375
+
376
+ for _, arg := range context.arguments.args {
377
+ if argElements[arg.name] == nil {
378
+ // Set defaults, if any.
379
+ if arg.defaultValue != "" {
380
+ if err := arg.value.Set(arg.defaultValue); err != nil {
381
+ return err
382
+ }
383
+ }
384
+ }
385
+ }
386
+
387
+ return nil
388
+}
389
+
390
+func (a *Application) validateRequired(context *ParseContext) error {
391
+ flagElements := map[string]*ParseElement{}
392
+ for _, element := range context.Elements {
393
+ if flag, ok := element.Clause.(*FlagClause); ok {
394
+ flagElements[flag.name] = element
395
+ }
396
+ }
397
+
398
+ argElements := map[string]*ParseElement{}
399
+ for _, element := range context.Elements {
400
+ if arg, ok := element.Clause.(*ArgClause); ok {
401
+ argElements[arg.name] = element
402
+ }
403
+ }
404
+
405
+ // Check required flags and set defaults.
406
+ for _, flag := range context.flags.long {
407
+ if flagElements[flag.name] == nil {
408
+ // Check required flags were provided.
409
+ if flag.needsValue() {
410
+ return fmt.Errorf("required flag --%s not provided", flag.name)
411
+ }
412
+ }
413
+ }
414
+
415
+ for _, arg := range context.arguments.args {
416
+ if argElements[arg.name] == nil {
417
+ if arg.required {
418
+ return fmt.Errorf("required argument '%s' not provided", arg.name)
419
+ }
420
+ }
421
+ }
422
+ return nil
423
+}
424
+
425
+func (a *Application) setValues(context *ParseContext) (selected []string, err error) {
426
+ // Set all arg and flag values.
427
+ var lastCmd *CmdClause
428
+ for _, element := range context.Elements {
429
+ switch clause := element.Clause.(type) {
430
+ case *FlagClause:
431
+ if err = clause.value.Set(*element.Value); err != nil {
432
+ return
433
+ }
434
+
435
+ case *ArgClause:
436
+ if err = clause.value.Set(*element.Value); err != nil {
437
+ return
438
+ }
439
+
440
+ case *CmdClause:
441
+ if clause.validator != nil {
442
+ if err = clause.validator(clause); err != nil {
443
+ return
444
+ }
445
+ }
446
+ selected = append(selected, clause.name)
447
+ lastCmd = clause
448
+ }
449
+ }
450
+
451
+ if lastCmd != nil && len(lastCmd.commands) > 0 {
452
+ return nil, fmt.Errorf("must select a subcommand of '%s'", lastCmd.FullCommand())
453
+ }
454
+
455
+ return
456
+}
457
+
458
+func (a *Application) applyValidators(context *ParseContext) (err error) {
459
+ // Call command validation functions.
460
+ for _, element := range context.Elements {
461
+ if cmd, ok := element.Clause.(*CmdClause); ok && cmd.validator != nil {
462
+ if err = cmd.validator(cmd); err != nil {
463
+ return err
464
+ }
465
+ }
466
+ }
467
+
468
+ if a.validator != nil {
469
+ err = a.validator(a)
470
+ }
471
+ return err
472
+}
473
+
474
+func (a *Application) applyPreActions(context *ParseContext) error {
475
+ if err := a.actionMixin.applyPreActions(context); err != nil {
476
+ return err
477
+ }
478
+ // Dispatch to actions.
479
+ for _, element := range context.Elements {
480
+ if applier, ok := element.Clause.(actionApplier); ok {
481
+ if err := applier.applyPreActions(context); err != nil {
482
+ return err
483
+ }
484
+ }
485
+ }
486
+ return nil
487
+}
488
+
489
+func (a *Application) applyActions(context *ParseContext) error {
490
+ if err := a.actionMixin.applyActions(context); err != nil {
491
+ return err
492
+ }
493
+ // Dispatch to actions.
494
+ for _, element := range context.Elements {
495
+ if applier, ok := element.Clause.(actionApplier); ok {
496
+ if err := applier.applyActions(context); err != nil {
497
+ return err
498
+ }
499
+ }
500
+ }
501
+ return nil
502
+}
503
+
504
+// Errorf prints an error message to w in the format "<appname>: error: <message>".
505
+func (a *Application) Errorf(format string, args ...interface{}) {
506
+ fmt.Fprintf(a.writer, a.Name+": error: "+format+"\n", args...)
507
+}
508
+
509
+// Fatalf writes a formatted error to w then terminates with exit status 1.
510
+func (a *Application) Fatalf(format string, args ...interface{}) {
511
+ a.Errorf(format, args...)
512
+ a.terminate(1)
513
+}
514
+
515
+// FatalUsage prints an error message followed by usage information, then
516
+// exits with a non-zero status.
517
+func (a *Application) FatalUsage(format string, args ...interface{}) {
518
+ a.Errorf(format, args...)
519
+ a.Usage([]string{})
520
+ a.terminate(1)
521
+}
522
+
523
+// FatalUsageContext writes a printf formatted error message to w, then usage
524
+// information for the given ParseContext, before exiting.
525
+func (a *Application) FatalUsageContext(context *ParseContext, format string, args ...interface{}) {
526
+ a.Errorf(format, args...)
527
+ if err := a.UsageForContext(context); err != nil {
528
+ panic(err)
529
+ }
530
+ a.terminate(1)
531
+}
532
+
533
+// FatalIfError prints an error and exits if err is not nil. The error is printed
534
+// with the given formatted string, if any.
535
+func (a *Application) FatalIfError(err error, format string, args ...interface{}) {
536
+ if err != nil {
537
+ prefix := ""
538
+ if format != "" {
539
+ prefix = fmt.Sprintf(format, args...) + ": "
540
+ }
541
+ a.Errorf(prefix+"%s", err)
542
+ a.terminate(1)
543
+ }
544
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/app_test.go
new
+197
@@ -0,0 +1,197 @@
1
+package kingpin
2
+
3
+import (
4
+ "io/ioutil"
5
+
6
+ "github.com/stretchr/testify/assert"
7
+
8
+ "testing"
9
+ "time"
10
+)
11
+
12
+func TestCommander(t *testing.T) {
13
+ c := New("test", "test")
14
+ ping := c.Command("ping", "Ping an IP address.")
15
+ pingTTL := ping.Flag("ttl", "TTL for ICMP packets").Short('t').Default("5s").Duration()
16
+
17
+ selected, err := c.Parse([]string{"ping"})
18
+ assert.NoError(t, err)
19
+ assert.Equal(t, "ping", selected)
20
+ assert.Equal(t, 5*time.Second, *pingTTL)
21
+
22
+ selected, err = c.Parse([]string{"ping", "--ttl=10s"})
23
+ assert.NoError(t, err)
24
+ assert.Equal(t, "ping", selected)
25
+ assert.Equal(t, 10*time.Second, *pingTTL)
26
+}
27
+
28
+func TestRequiredFlags(t *testing.T) {
29
+ c := New("test", "test")
30
+ c.Flag("a", "a").String()
31
+ c.Flag("b", "b").Required().String()
32
+
33
+ _, err := c.Parse([]string{"--a=foo"})
34
+ assert.Error(t, err)
35
+ _, err = c.Parse([]string{"--b=foo"})
36
+ assert.NoError(t, err)
37
+}
38
+
39
+func TestInvalidDefaultFlagValueErrors(t *testing.T) {
40
+ c := New("test", "test")
41
+ c.Flag("foo", "foo").Default("a").Int()
42
+ _, err := c.Parse([]string{})
43
+ assert.Error(t, err)
44
+}
45
+
46
+func TestInvalidDefaultArgValueErrors(t *testing.T) {
47
+ c := New("test", "test")
48
+ cmd := c.Command("cmd", "cmd")
49
+ cmd.Arg("arg", "arg").Default("one").Int()
50
+ _, err := c.Parse([]string{"cmd"})
51
+ assert.Error(t, err)
52
+}
53
+
54
+func TestArgsRequiredAfterNonRequiredErrors(t *testing.T) {
55
+ c := New("test", "test")
56
+ cmd := c.Command("cmd", "")
57
+ cmd.Arg("a", "a").String()
58
+ cmd.Arg("b", "b").Required().String()
59
+ _, err := c.Parse([]string{"cmd"})
60
+ assert.Error(t, err)
61
+}
62
+
63
+func TestArgsMultipleRequiredThenNonRequired(t *testing.T) {
64
+ c := New("test", "test").Terminate(nil).Writer(ioutil.Discard)
65
+ cmd := c.Command("cmd", "")
66
+ cmd.Arg("a", "a").Required().String()
67
+ cmd.Arg("b", "b").Required().String()
68
+ cmd.Arg("c", "c").String()
69
+ cmd.Arg("d", "d").String()
70
+ _, err := c.Parse([]string{"cmd", "a", "b"})
71
+ assert.NoError(t, err)
72
+ _, err = c.Parse([]string{})
73
+ assert.Error(t, err)
74
+}
75
+
76
+func TestDispatchCallbackIsCalled(t *testing.T) {
77
+ dispatched := false
78
+ c := New("test", "")
79
+ c.Command("cmd", "").Action(func(*ParseContext) error {
80
+ dispatched = true
81
+ return nil
82
+ })
83
+
84
+ _, err := c.Parse([]string{"cmd"})
85
+ assert.NoError(t, err)
86
+ assert.True(t, dispatched)
87
+}
88
+
89
+func TestTopLevelArgWorks(t *testing.T) {
90
+ c := New("test", "test")
91
+ s := c.Arg("arg", "help").String()
92
+ _, err := c.Parse([]string{"foo"})
93
+ assert.NoError(t, err)
94
+ assert.Equal(t, "foo", *s)
95
+}
96
+
97
+func TestTopLevelArgCantBeUsedWithCommands(t *testing.T) {
98
+ c := New("test", "test")
99
+ c.Arg("arg", "help").String()
100
+ c.Command("cmd", "help")
101
+ _, err := c.Parse([]string{})
102
+ assert.Error(t, err)
103
+}
104
+
105
+func TestTooManyArgs(t *testing.T) {
106
+ a := New("test", "test")
107
+ a.Arg("a", "").String()
108
+ _, err := a.Parse([]string{"a", "b"})
109
+ assert.Error(t, err)
110
+}
111
+
112
+func TestTooManyArgsAfterCommand(t *testing.T) {
113
+ a := New("test", "test")
114
+ a.Command("a", "")
115
+ assert.NoError(t, a.init())
116
+ _, err := a.Parse([]string{"a", "b"})
117
+ assert.Error(t, err)
118
+}
119
+
120
+func TestArgsLooksLikeFlagsWithConsumeRemainder(t *testing.T) {
121
+ a := New("test", "")
122
+ a.Arg("opts", "").Required().Strings()
123
+ _, err := a.Parse([]string{"hello", "-world"})
124
+ assert.Error(t, err)
125
+}
126
+
127
+func TestCommandParseDoesNotResetFlagsToDefault(t *testing.T) {
128
+ app := New("test", "")
129
+ flag := app.Flag("flag", "").Default("default").String()
130
+ app.Command("cmd", "")
131
+
132
+ _, err := app.Parse([]string{"--flag=123", "cmd"})
133
+ assert.NoError(t, err)
134
+ assert.Equal(t, "123", *flag)
135
+}
136
+
137
+func TestCommandParseDoesNotFailRequired(t *testing.T) {
138
+ app := New("test", "")
139
+ flag := app.Flag("flag", "").Required().String()
140
+ app.Command("cmd", "")
141
+
142
+ _, err := app.Parse([]string{"cmd", "--flag=123"})
143
+ assert.NoError(t, err)
144
+ assert.Equal(t, "123", *flag)
145
+}
146
+
147
+func TestSelectedCommand(t *testing.T) {
148
+ app := New("test", "help")
149
+ c0 := app.Command("c0", "")
150
+ c0.Command("c1", "")
151
+ s, err := app.Parse([]string{"c0", "c1"})
152
+ assert.NoError(t, err)
153
+ assert.Equal(t, "c0 c1", s)
154
+}
155
+
156
+func TestSubCommandRequired(t *testing.T) {
157
+ app := New("test", "help")
158
+ c0 := app.Command("c0", "")
159
+ c0.Command("c1", "")
160
+ _, err := app.Parse([]string{"c0"})
161
+ assert.Error(t, err)
162
+}
163
+
164
+func TestInterspersedFalse(t *testing.T) {
165
+ app := New("test", "help").Interspersed(false)
166
+ a1 := app.Arg("a1", "").String()
167
+ a2 := app.Arg("a2", "").String()
168
+ f1 := app.Flag("flag", "").String()
169
+
170
+ _, err := app.Parse([]string{"a1", "--flag=flag"})
171
+ assert.NoError(t, err)
172
+ assert.Equal(t, "a1", *a1)
173
+ assert.Equal(t, "--flag=flag", *a2)
174
+ assert.Equal(t, "", *f1)
175
+}
176
+
177
+func TestInterspersedTrue(t *testing.T) {
178
+ // test once with the default value and once with explicit true
179
+ for i := 0; i < 2; i++ {
180
+ app := New("test", "help")
181
+ if i != 0 {
182
+ t.Log("Setting explicit")
183
+ app.Interspersed(true)
184
+ } else {
185
+ t.Log("Using default")
186
+ }
187
+ a1 := app.Arg("a1", "").String()
188
+ a2 := app.Arg("a2", "").String()
189
+ f1 := app.Flag("flag", "").String()
190
+
191
+ _, err := app.Parse([]string{"a1", "--flag=flag"})
192
+ assert.NoError(t, err)
193
+ assert.Equal(t, "a1", *a1)
194
+ assert.Equal(t, "", *a2)
195
+ assert.Equal(t, "flag", *f1)
196
+ }
197
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/args.go
new
+105
@@ -0,0 +1,105 @@
1
+package kingpin
2
+
3
+import "fmt"
4
+
5
+type argGroup struct {
6
+ args []*ArgClause
7
+}
8
+
9
+func newArgGroup() *argGroup {
10
+ return &argGroup{}
11
+}
12
+
13
+func (a *argGroup) have() bool {
14
+ return len(a.args) > 0
15
+}
16
+
17
+func (a *argGroup) Arg(name, help string) *ArgClause {
18
+ arg := newArg(name, help)
19
+ a.args = append(a.args, arg)
20
+ return arg
21
+}
22
+
23
+func (a *argGroup) init() error {
24
+ required := 0
25
+ seen := map[string]struct{}{}
26
+ previousArgMustBeLast := false
27
+ for i, arg := range a.args {
28
+ if previousArgMustBeLast {
29
+ return fmt.Errorf("Args() can't be followed by another argument '%s'", arg.name)
30
+ }
31
+ if arg.consumesRemainder() {
32
+ previousArgMustBeLast = true
33
+ }
34
+ if _, ok := seen[arg.name]; ok {
35
+ return fmt.Errorf("duplicate argument '%s'", arg.name)
36
+ }
37
+ seen[arg.name] = struct{}{}
38
+ if arg.required && required != i {
39
+ return fmt.Errorf("required arguments found after non-required")
40
+ }
41
+ if arg.required {
42
+ required++
43
+ }
44
+ if err := arg.init(); err != nil {
45
+ return err
46
+ }
47
+ }
48
+ return nil
49
+}
50
+
51
+type ArgClause struct {
52
+ actionMixin
53
+ parserMixin
54
+ name string
55
+ help string
56
+ defaultValue string
57
+ required bool
58
+}
59
+
60
+func newArg(name, help string) *ArgClause {
61
+ a := &ArgClause{
62
+ name: name,
63
+ help: help,
64
+ }
65
+ return a
66
+}
67
+
68
+func (a *ArgClause) consumesRemainder() bool {
69
+ if r, ok := a.value.(remainderArg); ok {
70
+ return r.IsCumulative()
71
+ }
72
+ return false
73
+}
74
+
75
+// Required arguments must be input by the user. They can not have a Default() value provided.
76
+func (a *ArgClause) Required() *ArgClause {
77
+ a.required = true
78
+ return a
79
+}
80
+
81
+// Default value for this argument. It *must* be parseable by the value of the argument.
82
+func (a *ArgClause) Default(value string) *ArgClause {
83
+ a.defaultValue = value
84
+ return a
85
+}
86
+
87
+func (a *ArgClause) Action(action Action) *ArgClause {
88
+ a.addAction(action)
89
+ return a
90
+}
91
+
92
+func (a *ArgClause) PreAction(action Action) *ArgClause {
93
+ a.addPreAction(action)
94
+ return a
95
+}
96
+
97
+func (a *ArgClause) init() error {
98
+ if a.required && a.defaultValue != "" {
99
+ return fmt.Errorf("required argument '%s' with unusable default value", a.name)
100
+ }
101
+ if a.value == nil {
102
+ return fmt.Errorf("no parser defined for arg '%s'", a.name)
103
+ }
104
+ return nil
105
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/args_test.go
new
+49
@@ -0,0 +1,49 @@
1
+package kingpin
2
+
3
+import (
4
+ "io/ioutil"
5
+ "testing"
6
+
7
+ "github.com/stretchr/testify/assert"
8
+)
9
+
10
+func TestArgRemainder(t *testing.T) {
11
+ app := New("test", "")
12
+ v := app.Arg("test", "").Strings()
13
+ args := []string{"hello", "world"}
14
+ _, err := app.Parse(args)
15
+ assert.NoError(t, err)
16
+ assert.Equal(t, args, *v)
17
+}
18
+
19
+func TestArgRemainderErrorsWhenNotLast(t *testing.T) {
20
+ a := newArgGroup()
21
+ a.Arg("test", "").Strings()
22
+ a.Arg("test2", "").String()
23
+ assert.Error(t, a.init())
24
+}
25
+
26
+func TestArgMultipleRequired(t *testing.T) {
27
+ terminated := false
28
+ app := New("test", "")
29
+ app.Version("0.0.0").Writer(ioutil.Discard)
30
+ app.Arg("a", "").Required().String()
31
+ app.Arg("b", "").Required().String()
32
+ app.Terminate(func(int) { terminated = true })
33
+
34
+ _, err := app.Parse([]string{})
35
+ assert.Error(t, err)
36
+ _, err = app.Parse([]string{"A"})
37
+ assert.Error(t, err)
38
+ _, err = app.Parse([]string{"A", "B"})
39
+ assert.NoError(t, err)
40
+ _, err = app.Parse([]string{"--version"})
41
+ assert.True(t, terminated)
42
+}
43
+
44
+func TestInvalidArgsDefaultCanBeOverridden(t *testing.T) {
45
+ app := New("test", "")
46
+ app.Arg("a", "").Default("invalid").Bool()
47
+ _, err := app.Parse([]string{})
48
+ assert.Error(t, err)
49
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/cmd.go
new
+161
@@ -0,0 +1,161 @@
1
+package kingpin
2
+
3
+import (
4
+ "fmt"
5
+ "strings"
6
+)
7
+
8
+type cmdGroup struct {
9
+ app *Application
10
+ parent *CmdClause
11
+ commands map[string]*CmdClause
12
+ commandOrder []*CmdClause
13
+}
14
+
15
+func (c *cmdGroup) defaultSubcommand() *CmdClause {
16
+ for _, cmd := range c.commandOrder {
17
+ if cmd.isDefault {
18
+ return cmd
19
+ }
20
+ }
21
+ return nil
22
+}
23
+
24
+func newCmdGroup(app *Application) *cmdGroup {
25
+ return &cmdGroup{
26
+ app: app,
27
+ commands: make(map[string]*CmdClause),
28
+ }
29
+}
30
+
31
+func (c *cmdGroup) flattenedCommands() (out []*CmdClause) {
32
+ for _, cmd := range c.commandOrder {
33
+ if len(cmd.commands) == 0 {
34
+ out = append(out, cmd)
35
+ }
36
+ out = append(out, cmd.flattenedCommands()...)
37
+ }
38
+ return
39
+}
40
+
41
+func (c *cmdGroup) addCommand(name, help string) *CmdClause {
42
+ cmd := newCommand(c.app, name, help)
43
+ c.commands[name] = cmd
44
+ c.commandOrder = append(c.commandOrder, cmd)
45
+ return cmd
46
+}
47
+
48
+func (c *cmdGroup) init() error {
49
+ seen := map[string]bool{}
50
+ if c.defaultSubcommand() != nil && !c.have() {
51
+ return fmt.Errorf("default subcommand %q provided but no subcommands defined", c.defaultSubcommand().name)
52
+ }
53
+ defaults := []string{}
54
+ for _, cmd := range c.commandOrder {
55
+ if cmd.isDefault {
56
+ defaults = append(defaults, cmd.name)
57
+ }
58
+ if seen[cmd.name] {
59
+ return fmt.Errorf("duplicate command %q", cmd.name)
60
+ }
61
+ seen[cmd.name] = true
62
+ if err := cmd.init(); err != nil {
63
+ return err
64
+ }
65
+ }
66
+ if len(defaults) > 1 {
67
+ return fmt.Errorf("more than one default subcommand exists: %s", strings.Join(defaults, ", "))
68
+ }
69
+ return nil
70
+}
71
+
72
+func (c *cmdGroup) have() bool {
73
+ return len(c.commands) > 0
74
+}
75
+
76
+type CmdClauseValidator func(*CmdClause) error
77
+
78
+// A CmdClause is a single top-level command. It encapsulates a set of flags
79
+// and either subcommands or positional arguments.
80
+type CmdClause struct {
81
+ actionMixin
82
+ *flagGroup
83
+ *argGroup
84
+ *cmdGroup
85
+ app *Application
86
+ name string
87
+ help string
88
+ isDefault bool
89
+ validator CmdClauseValidator
90
+ hidden bool
91
+}
92
+
93
+func newCommand(app *Application, name, help string) *CmdClause {
94
+ c := &CmdClause{
95
+ flagGroup: newFlagGroup(),
96
+ argGroup: newArgGroup(),
97
+ cmdGroup: newCmdGroup(app),
98
+ app: app,
99
+ name: name,
100
+ help: help,
101
+ }
102
+ return c
103
+}
104
+
105
+// Validate sets a validation function to run when parsing.
106
+func (c *CmdClause) Validate(validator CmdClauseValidator) *CmdClause {
107
+ c.validator = validator
108
+ return c
109
+}
110
+
111
+func (c *CmdClause) FullCommand() string {
112
+ out := []string{c.name}
113
+ for p := c.parent; p != nil; p = p.parent {
114
+ out = append([]string{p.name}, out...)
115
+ }
116
+ return strings.Join(out, " ")
117
+}
118
+
119
+// Command adds a new sub-command.
120
+func (c *CmdClause) Command(name, help string) *CmdClause {
121
+ cmd := c.addCommand(name, help)
122
+ cmd.parent = c
123
+ return cmd
124
+}
125
+
126
+// Default makes this command the default if commands don't match.
127
+func (c *CmdClause) Default() *CmdClause {
128
+ c.isDefault = true
129
+ return c
130
+}
131
+
132
+func (c *CmdClause) Action(action Action) *CmdClause {
133
+ c.addAction(action)
134
+ return c
135
+}
136
+
137
+func (c *CmdClause) PreAction(action Action) *CmdClause {
138
+ c.addPreAction(action)
139
+ return c
140
+}
141
+
142
+func (c *CmdClause) init() error {
143
+ if err := c.flagGroup.init(); err != nil {
144
+ return err
145
+ }
146
+ if c.argGroup.have() && c.cmdGroup.have() {
147
+ return fmt.Errorf("can't mix Arg()s with Command()s")
148
+ }
149
+ if err := c.argGroup.init(); err != nil {
150
+ return err
151
+ }
152
+ if err := c.cmdGroup.init(); err != nil {
153
+ return err
154
+ }
155
+ return nil
156
+}
157
+
158
+func (c *CmdClause) Hidden() *CmdClause {
159
+ c.hidden = true
160
+ return c
161
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/cmd/genvalues/main.go
new
+121
@@ -0,0 +1,121 @@
1
+package main
2
+
3
+import (
4
+ "encoding/json"
5
+ "os/exec"
6
+ "strings"
7
+ "text/template"
8
+
9
+ "os"
10
+)
11
+
12
+const (
13
+ tmpl = `package kingpin
14
+
15
+// This file is autogenerated by "go generate .". Do not modify.
16
+
17
+{{range .}}
18
+{{if not .NoValueParser}}
19
+// -- {{.Type}} Value
20
+type {{.Type}}Value {{.Type}}
21
+
22
+func new{{.|Name}}Value(p *{{.Type}}) *{{.Type}}Value {
23
+ return (*{{.Type}}Value)(p)
24
+}
25
+
26
+func (f *{{.Type}}Value) Set(s string) error {
27
+ v, err := {{.Parser}}
28
+ *f = {{.Type}}Value(v)
29
+ return err
30
+}
31
+
32
+func (f *{{.Type}}Value) Get() interface{} { return {{.Type}}(*f) }
33
+
34
+func (f *{{.Type}}Value) String() string { return {{.|Format}} }
35
+
36
+// {{.|Name}} parses the next command-line value as {{.Type}}.
37
+func (p *parserMixin) {{.|Name}}() (target *{{.Type}}) {
38
+ target = new({{.Type}})
39
+ p.{{.|Name}}Var(target)
40
+ return
41
+}
42
+
43
+func (p *parserMixin) {{.|Name}}Var(target *{{.Type}}) {
44
+ p.SetValue(new{{.|Name}}Value(target))
45
+}
46
+
47
+{{end}}
48
+// {{.|Plural}} accumulates {{.Type}} values into a slice.
49
+func (p *parserMixin) {{.|Plural}}() (target *[]{{.Type}}) {
50
+ target = new([]{{.Type}})
51
+ p.{{.|Plural}}Var(target)
52
+ return
53
+}
54
+
55
+func (p *parserMixin) {{.|Plural}}Var(target *[]{{.Type}}) {
56
+ p.SetValue(newAccumulator(target, func(v interface{}) Value { return new{{.|Name}}Value(v.(*{{.Type}})) }))
57
+}
58
+
59
+{{end}}
60
+`
61
+)
62
+
63
+type Value struct {
64
+ Name string `json:"name"`
65
+ NoValueParser bool `json:"no_value_parser"`
66
+ Type string `json:"type"`
67
+ Parser string `json:"parser"`
68
+ Format string `json:"format"`
69
+ Plural string `json:"plural"`
70
+}
71
+
72
+func fatalIfError(err error) {
73
+ if err != nil {
74
+ panic(err)
75
+ }
76
+}
77
+
78
+func main() {
79
+ r, err := os.Open("values.json")
80
+ fatalIfError(err)
81
+ defer r.Close()
82
+
83
+ v := []Value{}
84
+ err = json.NewDecoder(r).Decode(&v)
85
+ fatalIfError(err)
86
+
87
+ valueName := func(v *Value) string {
88
+ if v.Name != "" {
89
+ return v.Name
90
+ }
91
+ return strings.Title(v.Type)
92
+ }
93
+
94
+ t, err := template.New("genvalues").Funcs(template.FuncMap{
95
+ "Lower": strings.ToLower,
96
+ "Format": func(v *Value) string {
97
+ if v.Format != "" {
98
+ return v.Format
99
+ }
100
+ return "fmt.Sprintf(\"%v\", *f)"
101
+ },
102
+ "Name": valueName,
103
+ "Plural": func(v *Value) string {
104
+ if v.Plural != "" {
105
+ return v.Plural
106
+ }
107
+ return valueName(v) + "List"
108
+ },
109
+ }).Parse(tmpl)
110
+ fatalIfError(err)
111
+
112
+ w, err := os.Create("values_generated.go")
113
+ fatalIfError(err)
114
+ defer w.Close()
115
+
116
+ err = t.Execute(w, v)
117
+ fatalIfError(err)
118
+
119
+ err = exec.Command("goimports", "-w", "values_generated.go").Run()
120
+ fatalIfError(err)
121
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/cmd_test.go
new
+157
@@ -0,0 +1,157 @@
1
+package kingpin
2
+
3
+import (
4
+ "strings"
5
+
6
+ "github.com/stretchr/testify/assert"
7
+
8
+ "testing"
9
+)
10
+
11
+func parseAndExecute(app *Application, context *ParseContext) (string, error) {
12
+ if err := parse(context, app); err != nil {
13
+ return "", err
14
+ }
15
+ return app.execute(context)
16
+}
17
+
18
+func TestNestedCommands(t *testing.T) {
19
+ app := New("app", "")
20
+ sub1 := app.Command("sub1", "")
21
+ sub1.Flag("sub1", "")
22
+ subsub1 := sub1.Command("sub1sub1", "")
23
+ subsub1.Command("sub1sub1end", "")
24
+
25
+ sub2 := app.Command("sub2", "")
26
+ sub2.Flag("sub2", "")
27
+ sub2.Command("sub2sub1", "")
28
+
29
+ context := tokenize([]string{"sub1", "sub1sub1", "sub1sub1end"})
30
+ selected, err := parseAndExecute(app, context)
31
+ assert.NoError(t, err)
32
+ assert.True(t, context.EOL())
33
+ assert.Equal(t, "sub1 sub1sub1 sub1sub1end", selected)
34
+}
35
+
36
+func TestNestedCommandsWithArgs(t *testing.T) {
37
+ app := New("app", "")
38
+ cmd := app.Command("a", "").Command("b", "")
39
+ a := cmd.Arg("a", "").String()
40
+ b := cmd.Arg("b", "").String()
41
+ context := tokenize([]string{"a", "b", "c", "d"})
42
+ selected, err := parseAndExecute(app, context)
43
+ assert.NoError(t, err)
44
+ assert.True(t, context.EOL())
45
+ assert.Equal(t, "a b", selected)
46
+ assert.Equal(t, "c", *a)
47
+ assert.Equal(t, "d", *b)
48
+}
49
+
50
+func TestNestedCommandsWithFlags(t *testing.T) {
51
+ app := New("app", "")
52
+ cmd := app.Command("a", "").Command("b", "")
53
+ a := cmd.Flag("aaa", "").Short('a').String()
54
+ b := cmd.Flag("bbb", "").Short('b').String()
55
+ err := app.init()
56
+ assert.NoError(t, err)
57
+ context := tokenize(strings.Split("a b --aaa x -b x", " "))
58
+ selected, err := parseAndExecute(app, context)
59
+ assert.NoError(t, err)
60
+ assert.True(t, context.EOL())
61
+ assert.Equal(t, "a b", selected)
62
+ assert.Equal(t, "x", *a)
63
+ assert.Equal(t, "x", *b)
64
+}
65
+
66
+func TestNestedCommandWithMergedFlags(t *testing.T) {
67
+ app := New("app", "")
68
+ cmd0 := app.Command("a", "")
69
+ cmd0f0 := cmd0.Flag("aflag", "").Bool()
70
+ // cmd1 := app.Command("b", "")
71
+ // cmd1f0 := cmd0.Flag("bflag", "").Bool()
72
+ cmd00 := cmd0.Command("aa", "")
73
+ cmd00f0 := cmd00.Flag("aaflag", "").Bool()
74
+ err := app.init()
75
+ assert.NoError(t, err)
76
+ context := tokenize(strings.Split("a aa --aflag --aaflag", " "))
77
+ selected, err := parseAndExecute(app, context)
78
+ assert.NoError(t, err)
79
+ assert.True(t, *cmd0f0)
80
+ assert.True(t, *cmd00f0)
81
+ assert.Equal(t, "a aa", selected)
82
+}
83
+
84
+func TestNestedCommandWithDuplicateFlagErrors(t *testing.T) {
85
+ app := New("app", "")
86
+ app.Flag("test", "").Bool()
87
+ app.Command("cmd0", "").Flag("test", "").Bool()
88
+ err := app.init()
89
+ assert.Error(t, err)
90
+}
91
+
92
+func TestNestedCommandWithArgAndMergedFlags(t *testing.T) {
93
+ app := New("app", "")
94
+ cmd0 := app.Command("a", "")
95
+ cmd0f0 := cmd0.Flag("aflag", "").Bool()
96
+ // cmd1 := app.Command("b", "")
97
+ // cmd1f0 := cmd0.Flag("bflag", "").Bool()
98
+ cmd00 := cmd0.Command("aa", "")
99
+ cmd00a0 := cmd00.Arg("arg", "").String()
100
+ cmd00f0 := cmd00.Flag("aaflag", "").Bool()
101
+ err := app.init()
102
+ assert.NoError(t, err)
103
+ context := tokenize(strings.Split("a aa hello --aflag --aaflag", " "))
104
+ selected, err := parseAndExecute(app, context)
105
+ assert.NoError(t, err)
106
+ assert.True(t, *cmd0f0)
107
+ assert.True(t, *cmd00f0)
108
+ assert.Equal(t, "a aa", selected)
109
+ assert.Equal(t, "hello", *cmd00a0)
110
+}
111
+
112
+func TestDefaultSubcommandEOL(t *testing.T) {
113
+ app := New("app", "").Terminate(nil)
114
+ c0 := app.Command("c0", "").Default()
115
+ c0.Command("c01", "").Default()
116
+ c0.Command("c02", "")
117
+
118
+ cmd, err := app.Parse([]string{"c0"})
119
+ assert.NoError(t, err)
120
+ assert.Equal(t, "c0 c01", cmd)
121
+}
122
+
123
+func TestDefaultSubcommandWithArg(t *testing.T) {
124
+ app := New("app", "").Terminate(nil)
125
+ c0 := app.Command("c0", "").Default()
126
+ c01 := c0.Command("c01", "").Default()
127
+ c012 := c01.Command("c012", "").Default()
128
+ a0 := c012.Arg("a0", "").String()
129
+ c0.Command("c02", "")
130
+
131
+ cmd, err := app.Parse([]string{"c0", "hello"})
132
+ assert.NoError(t, err)
133
+ assert.Equal(t, "c0 c01 c012", cmd)
134
+ assert.Equal(t, "hello", *a0)
135
+}
136
+
137
+func TestDefaultSubcommandWithFlags(t *testing.T) {
138
+ app := New("app", "").Terminate(nil)
139
+ c0 := app.Command("c0", "").Default()
140
+ _ = c0.Flag("f0", "").Int()
141
+ c0c1 := c0.Command("c1", "").Default()
142
+ c0c1f1 := c0c1.Flag("f1", "").Int()
143
+ selected, err := app.Parse([]string{"--f1=2"})
144
+ assert.NoError(t, err)
145
+ assert.Equal(t, "c0 c1", selected)
146
+ assert.Equal(t, 2, *c0c1f1)
147
+ _, err = app.Parse([]string{"--f2"})
148
+ assert.Error(t, err)
149
+}
150
+
151
+func TestMultipleDefaultCommands(t *testing.T) {
152
+ app := New("app", "").Terminate(nil)
153
+ app.Command("c0", "").Default()
154
+ app.Command("c1", "").Default()
155
+ _, err := app.Parse([]string{})
156
+ assert.Error(t, err)
157
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/doc.go
new
+68
@@ -0,0 +1,68 @@
1
+// Package kingpin provides command line interfaces like this:
2
+//
3
+// $ chat
4
+// usage: chat [<flags>] <command> [<flags>] [<args> ...]
5
+//
6
+// Flags:
7
+// --debug enable debug mode
8
+// --help Show help.
9
+// --server=127.0.0.1 server address
10
+//
11
+// Commands:
12
+// help <command>
13
+// Show help for a command.
14
+//
15
+// post [<flags>] <channel>
16
+// Post a message to a channel.
17
+//
18
+// register <nick> <name>
19
+// Register a new user.
20
+//
21
+// $ chat help post
22
+// usage: chat [<flags>] post [<flags>] <channel> [<text>]
23
+//
24
+// Post a message to a channel.
25
+//
26
+// Flags:
27
+// --image=IMAGE image to post
28
+//
29
+// Args:
30
+// <channel> channel to post to
31
+// [<text>] text to post
32
+// $ chat post --image=~/Downloads/owls.jpg pics
33
+//
34
+// From code like this:
35
+//
36
+// package main
37
+//
38
+// import "gopkg.in/alecthomas/kingpin.v1"
39
+//
40
+// var (
41
+// debug = kingpin.Flag("debug", "enable debug mode").Default("false").Bool()
42
+// serverIP = kingpin.Flag("server", "server address").Default("127.0.0.1").IP()
43
+//
44
+// register = kingpin.Command("register", "Register a new user.")
45
+// registerNick = register.Arg("nick", "nickname for user").Required().String()
46
+// registerName = register.Arg("name", "name of user").Required().String()
47
+//
48
+// post = kingpin.Command("post", "Post a message to a channel.")
49
+// postImage = post.Flag("image", "image to post").ExistingFile()
50
+// postChannel = post.Arg("channel", "channel to post to").Required().String()
51
+// postText = post.Arg("text", "text to post").String()
52
+// )
53
+//
54
+// func main() {
55
+// switch kingpin.Parse() {
56
+// // Register user
57
+// case "register":
58
+// println(*registerNick)
59
+//
60
+// // Post message
61
+// case "post":
62
+// if *postImage != nil {
63
+// }
64
+// if *postText != "" {
65
+// }
66
+// }
67
+// }
68
+package kingpin
Godeps/_workspace/src/github.com/alecthomas/kingpin/examples/chat1/main.go
new
+20
@@ -0,0 +1,20 @@
1
+package main
2
+
3
+import (
4
+ "fmt"
5
+
6
+ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/alecthomas/kingpin"
7
+)
8
+
9
+var (
10
+ debug = kingpin.Flag("debug", "Enable debug mode.").Bool()
11
+ timeout = kingpin.Flag("timeout", "Timeout waiting for ping.").Default("5s").OverrideDefaultFromEnvar("PING_TIMEOUT").Short('t').Duration()
12
+ ip = kingpin.Arg("ip", "IP address to ping.").Required().IP()
13
+ count = kingpin.Arg("count", "Number of packets to send").Int()
14
+)
15
+
16
+func main() {
17
+ kingpin.Version("0.0.1")
18
+ kingpin.Parse()
19
+ fmt.Printf("Would ping: %s with timeout %s and count %d", *ip, *timeout, *count)
20
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/examples/chat2/main.go
new
+38
@@ -0,0 +1,38 @@
1
+package main
2
+
3
+import (
4
+ "os"
5
+ "strings"
6
+
7
+ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/alecthomas/kingpin"
8
+)
9
+
10
+var (
11
+ app = kingpin.New("chat", "A command-line chat application.")
12
+ debug = app.Flag("debug", "Enable debug mode.").Bool()
13
+ serverIP = app.Flag("server", "Server address.").Default("127.0.0.1").IP()
14
+
15
+ register = app.Command("register", "Register a new user.")
16
+ registerNick = register.Arg("nick", "Nickname for user.").Required().String()
17
+ registerName = register.Arg("name", "Name of user.").Required().String()
18
+
19
+ post = app.Command("post", "Post a message to a channel.")
20
+ postImage = post.Flag("image", "Image to post.").File()
21
+ postChannel = post.Arg("channel", "Channel to post to.").Required().String()
22
+ postText = post.Arg("text", "Text to post.").Strings()
23
+)
24
+
25
+func main() {
26
+ switch kingpin.MustParse(app.Parse(os.Args[1:])) {
27
+ // Register user
28
+ case register.FullCommand():
29
+ println(*registerNick)
30
+
31
+ // Post message
32
+ case post.FullCommand():
33
+ if *postImage != nil {
34
+ }
35
+ text := strings.Join(*postText, " ")
36
+ println("Post:", text)
37
+ }
38
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/examples/curl/main.go
new
+105
@@ -0,0 +1,105 @@
1
+// A curl-like HTTP command-line client.
2
+package main
3
+
4
+import (
5
+ "errors"
6
+ "fmt"
7
+ "io"
8
+ "net/http"
9
+ "os"
10
+ "strings"
11
+
12
+ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/alecthomas/kingpin"
13
+)
14
+
15
+var (
16
+ timeout = kingpin.Flag("timeout", "Set connection timeout.").Short('t').Default("5s").Duration()
17
+ headers = HTTPHeader(kingpin.Flag("headers", "Add HTTP headers to the request.").Short('H').PlaceHolder("HEADER=VALUE"))
18
+
19
+ get = kingpin.Command("get", "GET a resource.").Default()
20
+ getFlag = get.Flag("test", "Test flag").Bool()
21
+ getURL = get.Command("url", "Retrieve a URL.").Default()
22
+ getURLURL = getURL.Arg("url", "URL to GET.").Required().URL()
23
+ getFile = get.Command("file", "Retrieve a file.")
24
+ getFileFile = getFile.Arg("file", "File to retrieve.").Required().ExistingFile()
25
+
26
+ post = kingpin.Command("post", "POST a resource.")
27
+ postData = post.Flag("data", "Key-value data to POST").Short('d').PlaceHolder("KEY:VALUE").StringMap()
28
+ postBinaryFile = post.Flag("data-binary", "File with binary data to POST.").File()
29
+ postURL = post.Arg("url", "URL to POST to.").Required().URL()
30
+)
31
+
32
+type HTTPHeaderValue http.Header
33
+
34
+func (h HTTPHeaderValue) Set(value string) error {
35
+ parts := strings.SplitN(value, "=", 2)
36
+ if len(parts) != 2 {
37
+ return fmt.Errorf("expected HEADER=VALUE got '%s'", value)
38
+ }
39
+ (http.Header)(h).Add(parts[0], parts[1])
40
+ return nil
41
+}
42
+
43
+func (h HTTPHeaderValue) String() string {
44
+ return ""
45
+}
46
+
47
+func HTTPHeader(s kingpin.Settings) (target *http.Header) {
48
+ target = &http.Header{}
49
+ s.SetValue((*HTTPHeaderValue)(target))
50
+ return
51
+}
52
+
53
+func applyRequest(req *http.Request) error {
54
+ req.Header = *headers
55
+ resp, err := http.DefaultClient.Do(req)
56
+ if err != nil {
57
+ return err
58
+ }
59
+ defer resp.Body.Close()
60
+ if resp.StatusCode < 200 || resp.StatusCode > 299 {
61
+ return fmt.Errorf("HTTP request failed: %s", resp.Status)
62
+ }
63
+ _, err = io.Copy(os.Stdout, resp.Body)
64
+ return err
65
+}
66
+
67
+func apply(method string, url string) error {
68
+ req, err := http.NewRequest(method, url, nil)
69
+ if err != nil {
70
+ return err
71
+ }
72
+ return applyRequest(req)
73
+}
74
+
75
+func applyPOST() error {
76
+ req, err := http.NewRequest("POST", (*postURL).String(), nil)
77
+ if err != nil {
78
+ return err
79
+ }
80
+ if len(*postData) > 0 {
81
+ for key, value := range *postData {
82
+ req.Form.Set(key, value)
83
+ }
84
+ } else if postBinaryFile != nil {
85
+ if headers.Get("Content-Type") != "" {
86
+ headers.Set("Content-Type", "application/octet-stream")
87
+ }
88
+ req.Body = *postBinaryFile
89
+ } else {
90
+ return errors.New("--data or --data-binary must be provided to POST")
91
+ }
92
+ return applyRequest(req)
93
+}
94
+
95
+func main() {
96
+ kingpin.UsageTemplate(kingpin.CompactUsageTemplate).Version("1.0").Author("Alec Thomas")
97
+ kingpin.CommandLine.Help = "An example implementation of curl."
98
+ switch kingpin.Parse() {
99
+ case "get url":
100
+ kingpin.FatalIfError(apply("GET", (*getURLURL).String()), "GET failed")
101
+
102
+ case "post":
103
+ kingpin.FatalIfError(applyPOST(), "POST failed")
104
+ }
105
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/examples/modular/main.go
new
+30
@@ -0,0 +1,30 @@
1
+package main
2
+
3
+import (
4
+ "fmt"
5
+ "os"
6
+
7
+ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/alecthomas/kingpin"
8
+)
9
+
10
+// Context for "ls" command
11
+type LsCommand struct {
12
+ All bool
13
+}
14
+
15
+func (l *LsCommand) run(c *kingpin.ParseContext) error {
16
+ fmt.Printf("all=%v\n", l.All)
17
+ return nil
18
+}
19
+
20
+func configureLsCommand(app *kingpin.Application) {
21
+ c := &LsCommand{}
22
+ ls := app.Command("ls", "List files.").Action(c.run)
23
+ ls.Flag("all", "List all files.").Short('a').BoolVar(&c.All)
24
+}
25
+
26
+func main() {
27
+ app := kingpin.New("modular", "My modular application.")
28
+ configureLsCommand(app)
29
+ kingpin.MustParse(app.Parse(os.Args[1:]))
30
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/examples/ping/main.go
new
+20
@@ -0,0 +1,20 @@
1
+package main
2
+
3
+import (
4
+ "fmt"
5
+
6
+ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/alecthomas/kingpin"
7
+)
8
+
9
+var (
10
+ debug = kingpin.Flag("debug", "Enable debug mode.").Bool()
11
+ timeout = kingpin.Flag("timeout", "Timeout waiting for ping.").OverrideDefaultFromEnvar("PING_TIMEOUT").Required().Short('t').Duration()
12
+ ip = kingpin.Arg("ip", "IP address to ping.").Required().IP()
13
+ count = kingpin.Arg("count", "Number of packets to send").Int()
14
+)
15
+
16
+func main() {
17
+ kingpin.Version("0.0.1")
18
+ kingpin.Parse()
19
+ fmt.Printf("Would ping: %s with timeout %s and count %d", *ip, *timeout, *count)
20
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/examples_test.go
new
+42
@@ -0,0 +1,42 @@
1
+package kingpin
2
+
3
+import (
4
+ "fmt"
5
+ "net/http"
6
+ "strings"
7
+)
8
+
9
+type HTTPHeaderValue http.Header
10
+
11
+func (h *HTTPHeaderValue) Set(value string) error {
12
+ parts := strings.SplitN(value, ":", 2)
13
+ if len(parts) != 2 {
14
+ return fmt.Errorf("expected HEADER:VALUE got '%s'", value)
15
+ }
16
+ (*http.Header)(h).Add(parts[0], parts[1])
17
+ return nil
18
+}
19
+
20
+func (h *HTTPHeaderValue) String() string {
21
+ return ""
22
+}
23
+
24
+func HTTPHeader(s Settings) (target *http.Header) {
25
+ target = new(http.Header)
26
+ s.SetValue((*HTTPHeaderValue)(target))
27
+ return
28
+}
29
+
30
+// This example ilustrates how to define custom parsers. HTTPHeader
31
+// cumulatively parses each encountered --header flag into a http.Header struct.
32
+func ExampleValue() {
33
+ var (
34
+ curl = New("curl", "transfer a URL")
35
+ headers = HTTPHeader(curl.Flag("headers", "Add HTTP headers to the request.").Short('H').PlaceHolder("HEADER:VALUE"))
36
+ )
37
+
38
+ curl.Parse([]string{"-H Content-Type:application/octet-stream"})
39
+ for key, value := range *headers {
40
+ fmt.Printf("%s = %s\n", key, value)
41
+ }
42
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/flags.go
new
+237
@@ -0,0 +1,237 @@
1
+package kingpin
2
+
3
+import (
4
+ "fmt"
5
+ "os"
6
+ "strings"
7
+)
8
+
9
+type flagGroup struct {
10
+ short map[string]*FlagClause
11
+ long map[string]*FlagClause
12
+ flagOrder []*FlagClause
13
+}
14
+
15
+func newFlagGroup() *flagGroup {
16
+ return &flagGroup{
17
+ short: make(map[string]*FlagClause),
18
+ long: make(map[string]*FlagClause),
19
+ }
20
+}
21
+
22
+func (f *flagGroup) merge(o *flagGroup) {
23
+ for _, flag := range o.flagOrder {
24
+ if flag.shorthand != 0 {
25
+ f.short[string(flag.shorthand)] = flag
26
+ }
27
+ f.long[flag.name] = flag
28
+ f.flagOrder = append(f.flagOrder, flag)
29
+ }
30
+}
31
+
32
+// Flag defines a new flag with the given long name and help.
33
+func (f *flagGroup) Flag(name, help string) *FlagClause {
34
+ flag := newFlag(name, help)
35
+ f.long[name] = flag
36
+ f.flagOrder = append(f.flagOrder, flag)
37
+ return flag
38
+}
39
+
40
+func (f *flagGroup) init() error {
41
+ for _, flag := range f.long {
42
+ if err := flag.init(); err != nil {
43
+ return err
44
+ }
45
+ if flag.shorthand != 0 {
46
+ f.short[string(flag.shorthand)] = flag
47
+ }
48
+ }
49
+ return nil
50
+}
51
+
52
+func (f *flagGroup) parse(context *ParseContext) (*FlagClause, error) {
53
+ var token *Token
54
+
55
+loop:
56
+ for {
57
+ token = context.Peek()
58
+ switch token.Type {
59
+ case TokenEOL:
60
+ break loop
61
+
62
+ case TokenLong, TokenShort:
63
+ flagToken := token
64
+ defaultValue := ""
65
+ var flag *FlagClause
66
+ var ok bool
67
+ invert := false
68
+
69
+ name := token.Value
70
+ if token.Type == TokenLong {
71
+ if strings.HasPrefix(name, "no-") {
72
+ name = name[3:]
73
+ invert = true
74
+ }
75
+ flag, ok = f.long[name]
76
+ if !ok {
77
+ return nil, fmt.Errorf("unknown long flag '%s'", flagToken)
78
+ }
79
+ } else {
80
+ flag, ok = f.short[name]
81
+ if !ok {
82
+ return nil, fmt.Errorf("unknown short flag '%s'", flagToken)
83
+ }
84
+ }
85
+
86
+ context.Next()
87
+
88
+ fb, ok := flag.value.(boolFlag)
89
+ if ok && fb.IsBoolFlag() {
90
+ if invert {
91
+ defaultValue = "false"
92
+ } else {
93
+ defaultValue = "true"
94
+ }
95
+ } else {
96
+ if invert {
97
+ context.Push(token)
98
+ return nil, fmt.Errorf("unknown long flag '%s'", flagToken)
99
+ }
100
+ token = context.Peek()
101
+ if token.Type != TokenArg {
102
+ context.Push(token)
103
+ return nil, fmt.Errorf("expected argument for flag '%s'", flagToken)
104
+ }
105
+ context.Next()
106
+ defaultValue = token.Value
107
+ }
108
+
109
+ context.matchedFlag(flag, defaultValue)
110
+ return flag, nil
111
+
112
+ default:
113
+ break loop
114
+ }
115
+ }
116
+ return nil, nil
117
+}
118
+
119
+func (f *flagGroup) visibleFlags() int {
120
+ count := 0
121
+ for _, flag := range f.long {
122
+ if !flag.hidden {
123
+ count++
124
+ }
125
+ }
126
+ return count
127
+}
128
+
129
+// FlagClause is a fluid interface used to build flags.
130
+type FlagClause struct {
131
+ parserMixin
132
+ actionMixin
133
+ name string
134
+ shorthand byte
135
+ help string
136
+ envar string
137
+ defaultValue string
138
+ placeholder string
139
+ hidden bool
140
+}
141
+
142
+func newFlag(name, help string) *FlagClause {
143
+ f := &FlagClause{
144
+ name: name,
145
+ help: help,
146
+ }
147
+ return f
148
+}
149
+
150
+func (f *FlagClause) needsValue() bool {
151
+ return f.required && f.defaultValue == ""
152
+}
153
+
154
+func (f *FlagClause) formatPlaceHolder() string {
155
+ if f.placeholder != "" {
156
+ return f.placeholder
157
+ }
158
+ if f.defaultValue != "" {
159
+ if _, ok := f.value.(*stringValue); ok {
160
+ return fmt.Sprintf("%q", f.defaultValue)
161
+ }
162
+ return f.defaultValue
163
+ }
164
+ return strings.ToUpper(f.name)
165
+}
166
+
167
+func (f *FlagClause) init() error {
168
+ if f.required && f.defaultValue != "" {
169
+ return fmt.Errorf("required flag '--%s' with default value that will never be used", f.name)
170
+ }
171
+ if f.value == nil {
172
+ return fmt.Errorf("no type defined for --%s (eg. .String())", f.name)
173
+ }
174
+ if f.envar != "" {
175
+ if v := os.Getenv(f.envar); v != "" {
176
+ f.defaultValue = v
177
+ }
178
+ }
179
+ return nil
180
+}
181
+
182
+// Dispatch to the given function after the flag is parsed and validated.
183
+func (f *FlagClause) Action(action Action) *FlagClause {
184
+ f.addAction(action)
185
+ return f
186
+}
187
+
188
+func (f *FlagClause) PreAction(action Action) *FlagClause {
189
+ f.addPreAction(action)
190
+ return f
191
+}
192
+
193
+// Default value for this flag. It *must* be parseable by the value of the flag.
194
+func (f *FlagClause) Default(value string) *FlagClause {
195
+ f.defaultValue = value
196
+ return f
197
+}
198
+
199
+// OverrideDefaultFromEnvar overrides the default value for a flag from an
200
+// environment variable, if available.
201
+func (f *FlagClause) OverrideDefaultFromEnvar(envar string) *FlagClause {
202
+ f.envar = envar
203
+ return f
204
+}
205
+
206
+// PlaceHolder sets the place-holder string used for flag values in the help. The
207
+// default behaviour is to use the value provided by Default() if provided,
208
+// then fall back on the capitalized flag name.
209
+func (f *FlagClause) PlaceHolder(placeholder string) *FlagClause {
210
+ f.placeholder = placeholder
211
+ return f
212
+}
213
+
214
+// Hidden hides a flag from usage but still allows it to be used.
215
+func (f *FlagClause) Hidden() *FlagClause {
216
+ f.hidden = true
217
+ return f
218
+}
219
+
220
+// Required makes the flag required. You can not provide a Default() value to a Required() flag.
221
+func (f *FlagClause) Required() *FlagClause {
222
+ f.required = true
223
+ return f
224
+}
225
+
226
+// Short sets the short flag name.
227
+func (f *FlagClause) Short(name byte) *FlagClause {
228
+ f.shorthand = name
229
+ return f
230
+}
231
+
232
+// Bool makes this flag a boolean flag.
233
+func (f *FlagClause) Bool() (target *bool) {
234
+ target = new(bool)
235
+ f.SetValue(newBoolValue(target))
236
+ return
237
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/flags_test.go
new
+109
@@ -0,0 +1,109 @@
1
+package kingpin
2
+
3
+import (
4
+ "io/ioutil"
5
+ "os"
6
+
7
+ "github.com/stretchr/testify/assert"
8
+
9
+ "testing"
10
+)
11
+
12
+func TestBool(t *testing.T) {
13
+ app := New("test", "")
14
+ b := app.Flag("b", "").Bool()
15
+ _, err := app.Parse([]string{"--b"})
16
+ assert.NoError(t, err)
17
+ assert.True(t, *b)
18
+}
19
+
20
+func TestNoBool(t *testing.T) {
21
+ fg := newFlagGroup()
22
+ f := fg.Flag("b", "").Default("true")
23
+ b := f.Bool()
24
+ fg.init()
25
+ tokens := tokenize([]string{"--no-b"})
26
+ _, err := fg.parse(tokens)
27
+ assert.NoError(t, err)
28
+ assert.False(t, *b)
29
+}
30
+
31
+func TestNegateNonBool(t *testing.T) {
32
+ fg := newFlagGroup()
33
+ f := fg.Flag("b", "")
34
+ f.Int()
35
+ fg.init()
36
+ tokens := tokenize([]string{"--no-b"})
37
+ _, err := fg.parse(tokens)
38
+ assert.Error(t, err)
39
+}
40
+
41
+func TestInvalidFlagDefaultCanBeOverridden(t *testing.T) {
42
+ app := New("test", "")
43
+ app.Flag("a", "").Default("invalid").Bool()
44
+ _, err := app.Parse([]string{})
45
+ assert.Error(t, err)
46
+}
47
+
48
+func TestRequiredFlag(t *testing.T) {
49
+ app := New("test", "")
50
+ app.Version("0.0.0").Writer(ioutil.Discard)
51
+ exits := 0
52
+ app.Terminate(func(int) { exits++ })
53
+ app.Flag("a", "").Required().Bool()
54
+ _, err := app.Parse([]string{"--a"})
55
+ assert.NoError(t, err)
56
+ _, err = app.Parse([]string{})
57
+ assert.Error(t, err)
58
+ _, err = app.Parse([]string{"--version"})
59
+ assert.Equal(t, 1, exits)
60
+}
61
+
62
+func TestShortFlag(t *testing.T) {
63
+ app := New("test", "")
64
+ f := app.Flag("long", "").Short('s').Bool()
65
+ _, err := app.Parse([]string{"-s"})
66
+ assert.NoError(t, err)
67
+ assert.True(t, *f)
68
+}
69
+
70
+func TestCombinedShortFlags(t *testing.T) {
71
+ app := New("test", "")
72
+ a := app.Flag("short0", "").Short('0').Bool()
73
+ b := app.Flag("short1", "").Short('1').Bool()
74
+ c := app.Flag("short2", "").Short('2').Bool()
75
+ _, err := app.Parse([]string{"-01"})
76
+ assert.NoError(t, err)
77
+ assert.True(t, *a)
78
+ assert.True(t, *b)
79
+ assert.False(t, *c)
80
+}
81
+
82
+func TestCombinedShortFlagArg(t *testing.T) {
83
+ a := New("test", "")
84
+ n := a.Flag("short", "").Short('s').Int()
85
+ _, err := a.Parse([]string{"-s10"})
86
+ assert.NoError(t, err)
87
+ assert.Equal(t, 10, *n)
88
+}
89
+
90
+func TestEmptyShortFlagIsAnError(t *testing.T) {
91
+ _, err := New("test", "").Parse([]string{"-"})
92
+ assert.Error(t, err)
93
+}
94
+
95
+func TestRequiredWithEnvarMissingErrors(t *testing.T) {
96
+ app := New("test", "")
97
+ app.Flag("t", "").OverrideDefaultFromEnvar("TEST_ENVAR").Required().Int()
98
+ _, err := app.Parse([]string{})
99
+ assert.Error(t, err)
100
+}
101
+
102
+func TestRequiredWithEnvar(t *testing.T) {
103
+ os.Setenv("TEST_ENVAR", "123")
104
+ app := New("test", "")
105
+ flag := app.Flag("t", "").OverrideDefaultFromEnvar("TEST_ENVAR").Required().Int()
106
+ _, err := app.Parse([]string{})
107
+ assert.NoError(t, err)
108
+ assert.Equal(t, 123, *flag)
109
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/global.go
new
+88
@@ -0,0 +1,88 @@
1
+package kingpin
2
+
3
+import (
4
+ "os"
5
+ "path/filepath"
6
+)
7
+
8
+var (
9
+ // CommandLine is the default Kingpin parser.
10
+ CommandLine = New(filepath.Base(os.Args[0]), "")
11
+)
12
+
13
+// Command adds a new command to the default parser.
14
+func Command(name, help string) *CmdClause {
15
+ return CommandLine.Command(name, help)
16
+}
17
+
18
+// Flag adds a new flag to the default parser.
19
+func Flag(name, help string) *FlagClause {
20
+ return CommandLine.Flag(name, help)
21
+}
22
+
23
+// Arg adds a new argument to the top-level of the default parser.
24
+func Arg(name, help string) *ArgClause {
25
+ return CommandLine.Arg(name, help)
26
+}
27
+
28
+// Parse and return the selected command. Will call the termination handler if
29
+// an error is encountered.
30
+func Parse() string {
31
+ selected := MustParse(CommandLine.Parse(os.Args[1:]))
32
+ if selected == "" && CommandLine.cmdGroup.have() {
33
+ Usage()
34
+ CommandLine.terminate(0)
35
+ }
36
+ return selected
37
+}
38
+
39
+// Errorf prints an error message to stderr.
40
+func Errorf(format string, args ...interface{}) {
41
+ CommandLine.Errorf(format, args...)
42
+}
43
+
44
+// Fatalf prints an error message to stderr and exits.
45
+func Fatalf(format string, args ...interface{}) {
46
+ CommandLine.Fatalf(format, args...)
47
+}
48
+
49
+// FatalIfError prints an error and exits if err is not nil. The error is printed
50
+// with the given prefix.
51
+func FatalIfError(err error, format string, args ...interface{}) {
52
+ CommandLine.FatalIfError(err, format, args...)
53
+}
54
+
55
+// FatalUsage prints an error message followed by usage information, then
56
+// exits with a non-zero status.
57
+func FatalUsage(format string, args ...interface{}) {
58
+ CommandLine.FatalUsage(format, args...)
59
+}
60
+
61
+// FatalUsageContext writes a printf formatted error message to stderr, then
62
+// usage information for the given ParseContext, before exiting.
63
+func FatalUsageContext(context *ParseContext, format string, args ...interface{}) {
64
+ CommandLine.FatalUsageContext(context, format, args...)
65
+}
66
+
67
+// Usage prints usage to stderr.
68
+func Usage() {
69
+ CommandLine.Usage(os.Args[1:])
70
+}
71
+
72
+// Set global usage template to use (defaults to DefaultUsageTemplate).
73
+func UsageTemplate(template string) *Application {
74
+ return CommandLine.UsageTemplate(template)
75
+}
76
+
77
+// MustParse can be used with app.Parse(args) to exit with an error if parsing fails.
78
+func MustParse(command string, err error) string {
79
+ if err != nil {
80
+ Fatalf("%s, try --help", err)
81
+ }
82
+ return command
83
+}
84
+
85
+// Version adds a flag for displaying the application version number.
86
+func Version(version string) *Application {
87
+ return CommandLine.Version(version)
88
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/guesswidth.go
new
+9
@@ -0,0 +1,9 @@
1
+// +build !linux,!freebsd,!darwin,!dragonfly,!netbsd,!openbsd
2
+
3
+package kingpin
4
+
5
+import "io"
6
+
7
+func guessWidth(w io.Writer) int {
8
+ return 80
9
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/guesswidth_unix.go
new
+38
@@ -0,0 +1,38 @@
1
+// +build linux freebsd darwin dragonfly netbsd openbsd
2
+
3
+package kingpin
4
+
5
+import (
6
+ "io"
7
+ "os"
8
+ "strconv"
9
+ "syscall"
10
+ "unsafe"
11
+)
12
+
13
+func guessWidth(w io.Writer) int {
14
+ // check if COLUMNS env is set to comply with
15
+ // http://pubs.opengroup.org/onlinepubs/009604499/basedefs/xbd_chap08.html
16
+ colsStr := os.Getenv("COLUMNS")
17
+ if colsStr != "" {
18
+ if cols, err := strconv.Atoi(colsStr); err == nil {
19
+ return cols
20
+ }
21
+ }
22
+
23
+ if t, ok := w.(*os.File); ok {
24
+ fd := t.Fd()
25
+ var dimensions [4]uint16
26
+
27
+ if _, _, err := syscall.Syscall6(
28
+ syscall.SYS_IOCTL,
29
+ uintptr(fd),
30
+ uintptr(syscall.TIOCGWINSZ),
31
+ uintptr(unsafe.Pointer(&dimensions)),
32
+ 0, 0, 0,
33
+ ); err == 0 {
34
+ return int(dimensions[1])
35
+ }
36
+ }
37
+ return 80
38
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/model.go
new
+219
@@ -0,0 +1,219 @@
1
+package kingpin
2
+
3
+import (
4
+ "fmt"
5
+ "strconv"
6
+ "strings"
7
+)
8
+
9
+// Data model for Kingpin command-line structure.
10
+
11
+type FlagGroupModel struct {
12
+ Flags []*FlagModel
13
+}
14
+
15
+func (f *FlagGroupModel) FlagSummary() string {
16
+ out := []string{}
17
+ count := 0
18
+ for _, flag := range f.Flags {
19
+ if flag.Name != "help" {
20
+ count++
21
+ }
22
+ if flag.Required {
23
+ if flag.IsBoolFlag() {
24
+ out = append(out, fmt.Sprintf("--[no-]%s", flag.Name))
25
+ } else {
26
+ out = append(out, fmt.Sprintf("--%s=%s", flag.Name, flag.FormatPlaceHolder()))
27
+ }
28
+ }
29
+ }
30
+ if count != len(out) {
31
+ out = append(out, "[<flags>]")
32
+ }
33
+ return strings.Join(out, " ")
34
+}
35
+
36
+type FlagModel struct {
37
+ Name string
38
+ Help string
39
+ Short rune
40
+ Default string
41
+ Envar string
42
+ PlaceHolder string
43
+ Required bool
44
+ Hidden bool
45
+ Value Value
46
+}
47
+
48
+func (f *FlagModel) String() string {
49
+ return f.Value.String()
50
+}
51
+
52
+func (f *FlagModel) IsBoolFlag() bool {
53
+ if fl, ok := f.Value.(boolFlag); ok {
54
+ return fl.IsBoolFlag()
55
+ }
56
+ return false
57
+}
58
+
59
+func (f *FlagModel) FormatPlaceHolder() string {
60
+ if f.PlaceHolder != "" {
61
+ return f.PlaceHolder
62
+ }
63
+ if f.Default != "" {
64
+ if _, ok := f.Value.(*stringValue); ok {
65
+ return strconv.Quote(f.Default)
66
+ }
67
+ return f.Default
68
+ }
69
+ return strings.ToUpper(f.Name)
70
+}
71
+
72
+type ArgGroupModel struct {
73
+ Args []*ArgModel
74
+}
75
+
76
+func (a *ArgGroupModel) ArgSummary() string {
77
+ depth := 0
78
+ out := []string{}
79
+ for _, arg := range a.Args {
80
+ h := "<" + arg.Name + ">"
81
+ if !arg.Required {
82
+ h = "[" + h
83
+ depth++
84
+ }
85
+ out = append(out, h)
86
+ }
87
+ out[len(out)-1] = out[len(out)-1] + strings.Repeat("]", depth)
88
+ return strings.Join(out, " ")
89
+}
90
+
91
+type ArgModel struct {
92
+ Name string
93
+ Help string
94
+ Default string
95
+ Required bool
96
+ Value Value
97
+}
98
+
99
+func (a *ArgModel) String() string {
100
+ return a.Value.String()
101
+}
102
+
103
+type CmdGroupModel struct {
104
+ Commands []*CmdModel
105
+}
106
+
107
+func (c *CmdGroupModel) FlattenedCommands() (out []*CmdModel) {
108
+ for _, cmd := range c.Commands {
109
+ if len(cmd.Commands) == 0 {
110
+ out = append(out, cmd)
111
+ }
112
+ out = append(out, cmd.FlattenedCommands()...)
113
+ }
114
+ return
115
+}
116
+
117
+type CmdModel struct {
118
+ Name string
119
+ Help string
120
+ FullCommand string
121
+ Depth int
122
+ Hidden bool
123
+ Default bool
124
+ *FlagGroupModel
125
+ *ArgGroupModel
126
+ *CmdGroupModel
127
+}
128
+
129
+func (c *CmdModel) String() string {
130
+ return c.FullCommand
131
+}
132
+
133
+type ApplicationModel struct {
134
+ Name string
135
+ Help string
136
+ Version string
137
+ Author string
138
+ *ArgGroupModel
139
+ *CmdGroupModel
140
+ *FlagGroupModel
141
+}
142
+
143
+func (a *Application) Model() *ApplicationModel {
144
+ return &ApplicationModel{
145
+ Name: a.Name,
146
+ Help: a.Help,
147
+ Version: a.version,
148
+ Author: a.author,
149
+ FlagGroupModel: a.flagGroup.Model(),
150
+ ArgGroupModel: a.argGroup.Model(),
151
+ CmdGroupModel: a.cmdGroup.Model(),
152
+ }
153
+}
154
+
155
+func (a *argGroup) Model() *ArgGroupModel {
156
+ m := &ArgGroupModel{}
157
+ for _, arg := range a.args {
158
+ m.Args = append(m.Args, arg.Model())
159
+ }
160
+ return m
161
+}
162
+
163
+func (a *ArgClause) Model() *ArgModel {
164
+ return &ArgModel{
165
+ Name: a.name,
166
+ Help: a.help,
167
+ Default: a.defaultValue,
168
+ Required: a.required,
169
+ Value: a.value,
170
+ }
171
+}
172
+
173
+func (f *flagGroup) Model() *FlagGroupModel {
174
+ m := &FlagGroupModel{}
175
+ for _, fl := range f.flagOrder {
176
+ m.Flags = append(m.Flags, fl.Model())
177
+ }
178
+ return m
179
+}
180
+
181
+func (f *FlagClause) Model() *FlagModel {
182
+ return &FlagModel{
183
+ Name: f.name,
184
+ Help: f.help,
185
+ Short: rune(f.shorthand),
186
+ Default: f.defaultValue,
187
+ Envar: f.envar,
188
+ PlaceHolder: f.placeholder,
189
+ Required: f.required,
190
+ Hidden: f.hidden,
191
+ Value: f.value,
192
+ }
193
+}
194
+
195
+func (c *cmdGroup) Model() *CmdGroupModel {
196
+ m := &CmdGroupModel{}
197
+ for _, cm := range c.commandOrder {
198
+ m.Commands = append(m.Commands, cm.Model())
199
+ }
200
+ return m
201
+}
202
+
203
+func (c *CmdClause) Model() *CmdModel {
204
+ depth := 0
205
+ for i := c; i != nil; i = i.parent {
206
+ depth++
207
+ }
208
+ return &CmdModel{
209
+ Name: c.name,
210
+ Help: c.help,
211
+ Depth: depth,
212
+ Hidden: c.hidden,
213
+ Default: c.isDefault,
214
+ FullCommand: c.FullCommand(),
215
+ FlagGroupModel: c.flagGroup.Model(),
216
+ ArgGroupModel: c.argGroup.Model(),
217
+ CmdGroupModel: c.cmdGroup.Model(),
218
+ }
219
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/parser.go
new
+372
@@ -0,0 +1,372 @@
1
+package kingpin
2
+
3
+import (
4
+ "bufio"
5
+ "fmt"
6
+ "os"
7
+ "strings"
8
+)
9
+
10
+type TokenType int
11
+
12
+// Token types.
13
+const (
14
+ TokenShort TokenType = iota
15
+ TokenLong
16
+ TokenArg
17
+ TokenError
18
+ TokenEOL
19
+)
20
+
21
+func (t TokenType) String() string {
22
+ switch t {
23
+ case TokenShort:
24
+ return "short flag"
25
+ case TokenLong:
26
+ return "long flag"
27
+ case TokenArg:
28
+ return "argument"
29
+ case TokenError:
30
+ return "error"
31
+ case TokenEOL:
32
+ return "<EOL>"
33
+ }
34
+ return "?"
35
+}
36
+
37
+var (
38
+ TokenEOLMarker = Token{-1, TokenEOL, ""}
39
+)
40
+
41
+type Token struct {
42
+ Index int
43
+ Type TokenType
44
+ Value string
45
+}
46
+
47
+func (t *Token) Equal(o *Token) bool {
48
+ return t.Index == o.Index
49
+}
50
+
51
+func (t *Token) IsFlag() bool {
52
+ return t.Type == TokenShort || t.Type == TokenLong
53
+}
54
+
55
+func (t *Token) IsEOF() bool {
56
+ return t.Type == TokenEOL
57
+}
58
+
59
+func (t *Token) String() string {
60
+ switch t.Type {
61
+ case TokenShort:
62
+ return "-" + t.Value
63
+ case TokenLong:
64
+ return "--" + t.Value
65
+ case TokenArg:
66
+ return t.Value
67
+ case TokenError:
68
+ return "error: " + t.Value
69
+ case TokenEOL:
70
+ return "<EOL>"
71
+ default:
72
+ panic("unhandled type")
73
+ }
74
+}
75
+
76
+// A union of possible elements in a parse stack.
77
+type ParseElement struct {
78
+ // Clause is either *CmdClause, *ArgClause or *FlagClause.
79
+ Clause interface{}
80
+ // Value is corresponding value for an ArgClause or FlagClause (if any).
81
+ Value *string
82
+}
83
+
84
+// ParseContext holds the current context of the parser. When passed to
85
+// Action() callbacks Elements will be fully populated with *FlagClause,
86
+// *ArgClause and *CmdClause values and their corresponding arguments (if
87
+// any).
88
+type ParseContext struct {
89
+ SelectedCommand *CmdClause
90
+ argsOnly bool
91
+ peek []*Token
92
+ argi int // Index of current command-line arg we're processing.
93
+ args []string
94
+ flags *flagGroup
95
+ arguments *argGroup
96
+ argumenti int // Cursor into arguments
97
+ // Flags, arguments and commands encountered and collected during parse.
98
+ Elements []*ParseElement
99
+}
100
+
101
+func (p *ParseContext) nextArg() *ArgClause {
102
+ if p.argumenti >= len(p.arguments.args) {
103
+ return nil
104
+ }
105
+ arg := p.arguments.args[p.argumenti]
106
+ if !arg.consumesRemainder() {
107
+ p.argumenti++
108
+ }
109
+ return arg
110
+}
111
+
112
+func (p *ParseContext) next() {
113
+ p.argi++
114
+ p.args = p.args[1:]
115
+}
116
+
117
+// HasTrailingArgs returns true if there are unparsed command-line arguments.
118
+// This can occur if the parser can not match remaining arguments.
119
+func (p *ParseContext) HasTrailingArgs() bool {
120
+ return len(p.args) > 0
121
+}
122
+
123
+func tokenize(args []string) *ParseContext {
124
+ return &ParseContext{
125
+ args: args,
126
+ flags: newFlagGroup(),
127
+ arguments: newArgGroup(),
128
+ }
129
+}
130
+
131
+func (p *ParseContext) mergeFlags(flags *flagGroup) {
132
+ for _, flag := range flags.flagOrder {
133
+ if flag.shorthand != 0 {
134
+ p.flags.short[string(flag.shorthand)] = flag
135
+ }
136
+ p.flags.long[flag.name] = flag
137
+ p.flags.flagOrder = append(p.flags.flagOrder, flag)
138
+ }
139
+}
140
+
141
+func (p *ParseContext) mergeArgs(args *argGroup) {
142
+ for _, arg := range args.args {
143
+ p.arguments.args = append(p.arguments.args, arg)
144
+ }
145
+}
146
+
147
+func (p *ParseContext) EOL() bool {
148
+ return p.Peek().Type == TokenEOL
149
+}
150
+
151
+// Next token in the parse context.
152
+func (p *ParseContext) Next() *Token {
153
+ if len(p.peek) > 0 {
154
+ return p.pop()
155
+ }
156
+
157
+ // End of tokens.
158
+ if len(p.args) == 0 {
159
+ return &Token{Index: p.argi, Type: TokenEOL}
160
+ }
161
+
162
+ arg := p.args[0]
163
+ p.next()
164
+
165
+ if p.argsOnly {
166
+ return &Token{p.argi, TokenArg, arg}
167
+ }
168
+
169
+ // All remaining args are passed directly.
170
+ if arg == "--" {
171
+ p.argsOnly = true
172
+ return p.Next()
173
+ }
174
+
175
+ if strings.HasPrefix(arg, "--") {
176
+ parts := strings.SplitN(arg[2:], "=", 2)
177
+ token := &Token{p.argi, TokenLong, parts[0]}
178
+ if len(parts) == 2 {
179
+ p.Push(&Token{p.argi, TokenArg, parts[1]})
180
+ }
181
+ return token
182
+ }
183
+
184
+ if strings.HasPrefix(arg, "-") {
185
+ if len(arg) == 1 {
186
+ return &Token{Index: p.argi, Type: TokenShort}
187
+ }
188
+ short := arg[1:2]
189
+ flag, ok := p.flags.short[short]
190
+ // Not a known short flag, we'll just return it anyway.
191
+ if !ok {
192
+ } else if fb, ok := flag.value.(boolFlag); ok && fb.IsBoolFlag() {
193
+ // Bool short flag.
194
+ } else {
195
+ // Short flag with combined argument: -fARG
196
+ token := &Token{p.argi, TokenShort, short}
197
+ if len(arg) > 2 {
198
+ p.Push(&Token{p.argi, TokenArg, arg[2:]})
199
+ }
200
+ return token
201
+ }
202
+
203
+ if len(arg) > 2 {
204
+ p.args = append([]string{"-" + arg[2:]}, p.args...)
205
+ }
206
+ return &Token{p.argi, TokenShort, short}
207
+ } else if strings.HasPrefix(arg, "@") {
208
+ expanded, err := ExpandArgsFromFile(arg[1:])
209
+ if err != nil {
210
+ return &Token{p.argi, TokenError, err.Error()}
211
+ }
212
+ if p.argi >= len(p.args) {
213
+ p.args = append(p.args[:p.argi-1], expanded...)
214
+ } else {
215
+ p.args = append(p.args[:p.argi-1], append(expanded, p.args[p.argi+1:]...)...)
216
+ }
217
+ return p.Next()
218
+ }
219
+
220
+ return &Token{p.argi, TokenArg, arg}
221
+}
222
+
223
+func (p *ParseContext) Peek() *Token {
224
+ if len(p.peek) == 0 {
225
+ return p.Push(p.Next())
226
+ }
227
+ return p.peek[len(p.peek)-1]
228
+}
229
+
230
+func (p *ParseContext) Push(token *Token) *Token {
231
+ p.peek = append(p.peek, token)
232
+ return token
233
+}
234
+
235
+func (p *ParseContext) pop() *Token {
236
+ end := len(p.peek) - 1
237
+ token := p.peek[end]
238
+ p.peek = p.peek[0:end]
239
+ return token
240
+}
241
+
242
+func (p *ParseContext) String() string {
243
+ return p.SelectedCommand.FullCommand()
244
+}
245
+
246
+func (p *ParseContext) matchedFlag(flag *FlagClause, value string) {
247
+ p.Elements = append(p.Elements, &ParseElement{Clause: flag, Value: &value})
248
+}
249
+
250
+func (p *ParseContext) matchedArg(arg *ArgClause, value string) {
251
+ p.Elements = append(p.Elements, &ParseElement{Clause: arg, Value: &value})
252
+}
253
+
254
+func (p *ParseContext) matchedCmd(cmd *CmdClause) {
255
+ p.Elements = append(p.Elements, &ParseElement{Clause: cmd})
256
+ p.mergeFlags(cmd.flagGroup)
257
+ p.mergeArgs(cmd.argGroup)
258
+ p.SelectedCommand = cmd
259
+}
260
+
261
+// Expand arguments from a file. Lines starting with # will be treated as comments.
262
+func ExpandArgsFromFile(filename string) (out []string, err error) {
263
+ r, err := os.Open(filename)
264
+ if err != nil {
265
+ return nil, err
266
+ }
267
+ defer r.Close()
268
+ scanner := bufio.NewScanner(r)
269
+ for scanner.Scan() {
270
+ line := scanner.Text()
271
+ if strings.HasPrefix(line, "#") {
272
+ continue
273
+ }
274
+ out = append(out, line)
275
+ }
276
+ err = scanner.Err()
277
+ return
278
+}
279
+
280
+func parse(context *ParseContext, app *Application) (err error) {
281
+ context.mergeFlags(app.flagGroup)
282
+ context.mergeArgs(app.argGroup)
283
+
284
+ cmds := app.cmdGroup
285
+ help := false
286
+
287
+loop:
288
+ for !context.EOL() {
289
+ token := context.Peek()
290
+
291
+ switch token.Type {
292
+ case TokenLong, TokenShort:
293
+ if flag, err := context.flags.parse(context); err != nil {
294
+ if !help {
295
+ if cmd := cmds.defaultSubcommand(); cmd != nil {
296
+ context.matchedCmd(cmd)
297
+ cmds = cmd.cmdGroup
298
+ break
299
+ }
300
+ }
301
+ return err
302
+ } else if flag == HelpFlag {
303
+ help = true
304
+ }
305
+
306
+ case TokenArg:
307
+ if cmds.have() {
308
+ selectedDefault := false
309
+ cmd, ok := cmds.commands[token.String()]
310
+ if !ok {
311
+ if !help {
312
+ if cmd = cmds.defaultSubcommand(); cmd != nil {
313
+ selectedDefault = true
314
+ }
315
+ }
316
+ if cmd == nil {
317
+ return fmt.Errorf("expected command but got %q", token)
318
+ }
319
+ }
320
+ if cmd == HelpCommand {
321
+ help = true
322
+ }
323
+ context.matchedCmd(cmd)
324
+ cmds = cmd.cmdGroup
325
+ if !selectedDefault {
326
+ context.Next()
327
+ }
328
+ } else if context.arguments.have() {
329
+ if app.noInterspersed {
330
+ // no more flags
331
+ context.argsOnly = true
332
+ }
333
+ arg := context.nextArg()
334
+ if arg == nil {
335
+ break loop
336
+ }
337
+ context.matchedArg(arg, token.String())
338
+ context.Next()
339
+ } else {
340
+ break loop
341
+ }
342
+
343
+ case TokenEOL:
344
+ break loop
345
+ }
346
+ }
347
+
348
+ // Move to innermost default command.
349
+ for !help {
350
+ if cmd := cmds.defaultSubcommand(); cmd != nil {
351
+ context.matchedCmd(cmd)
352
+ cmds = cmd.cmdGroup
353
+ } else {
354
+ break
355
+ }
356
+ }
357
+
358
+ if !context.EOL() {
359
+ return fmt.Errorf("unexpected %s", context.Peek())
360
+ }
361
+
362
+ // Set defaults for all remaining args.
363
+ for arg := context.nextArg(); arg != nil && !arg.consumesRemainder(); arg = context.nextArg() {
364
+ if arg.defaultValue != "" {
365
+ if err := arg.value.Set(arg.defaultValue); err != nil {
366
+ return fmt.Errorf("invalid default value '%s' for argument '%s'", arg.defaultValue, arg.name)
367
+ }
368
+ }
369
+ }
370
+
371
+ return
372
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/parser_test.go
new
+42
@@ -0,0 +1,42 @@
1
+package kingpin
2
+
3
+import (
4
+ "io/ioutil"
5
+ "os"
6
+ "testing"
7
+
8
+ "github.com/stretchr/testify/assert"
9
+)
10
+
11
+func TestParserExpandFromFile(t *testing.T) {
12
+ f, err := ioutil.TempFile("", "")
13
+ assert.NoError(t, err)
14
+ defer os.Remove(f.Name())
15
+ f.WriteString("hello\nworld\n")
16
+ f.Close()
17
+
18
+ app := New("test", "")
19
+ arg0 := app.Arg("arg0", "").String()
20
+ arg1 := app.Arg("arg1", "").String()
21
+
22
+ _, err = app.Parse([]string{"@" + f.Name()})
23
+ assert.NoError(t, err)
24
+ assert.Equal(t, "hello", *arg0)
25
+ assert.Equal(t, "world", *arg1)
26
+}
27
+
28
+func TestParseContextPush(t *testing.T) {
29
+ app := New("test", "")
30
+ app.Command("foo", "").Command("bar", "")
31
+ c := tokenize([]string{"foo", "bar"})
32
+ a := c.Next()
33
+ assert.Equal(t, TokenArg, a.Type)
34
+ b := c.Next()
35
+ assert.Equal(t, TokenArg, b.Type)
36
+ c.Push(b)
37
+ c.Push(a)
38
+ a = c.Next()
39
+ assert.Equal(t, "foo", a.Value)
40
+ b = c.Next()
41
+ assert.Equal(t, "bar", b.Value)
42
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/parsers.go
new
+201
@@ -0,0 +1,201 @@
1
+package kingpin
2
+
3
+import (
4
+ "net"
5
+ "net/url"
6
+ "os"
7
+ "time"
8
+
9
+ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/alecthomas/units"
10
+)
11
+
12
+type Settings interface {
13
+ SetValue(value Value)
14
+}
15
+
16
+type parserMixin struct {
17
+ value Value
18
+ required bool
19
+}
20
+
21
+func (p *parserMixin) SetValue(value Value) {
22
+ p.value = value
23
+}
24
+
25
+// StringMap provides key=value parsing into a map.
26
+func (p *parserMixin) StringMap() (target *map[string]string) {
27
+ target = &(map[string]string{})
28
+ p.StringMapVar(target)
29
+ return
30
+}
31
+
32
+// Duration sets the parser to a time.Duration parser.
33
+func (p *parserMixin) Duration() (target *time.Duration) {
34
+ target = new(time.Duration)
35
+ p.DurationVar(target)
36
+ return
37
+}
38
+
39
+// Bytes parses numeric byte units. eg. 1.5KB
40
+func (p *parserMixin) Bytes() (target *units.Base2Bytes) {
41
+ target = new(units.Base2Bytes)
42
+ p.BytesVar(target)
43
+ return
44
+}
45
+
46
+// IP sets the parser to a net.IP parser.
47
+func (p *parserMixin) IP() (target *net.IP) {
48
+ target = new(net.IP)
49
+ p.IPVar(target)
50
+ return
51
+}
52
+
53
+// TCP (host:port) address.
54
+func (p *parserMixin) TCP() (target **net.TCPAddr) {
55
+ target = new(*net.TCPAddr)
56
+ p.TCPVar(target)
57
+ return
58
+}
59
+
60
+// TCPVar (host:port) address.
61
+func (p *parserMixin) TCPVar(target **net.TCPAddr) {
62
+ p.SetValue(newTCPAddrValue(target))
63
+}
64
+
65
+// ExistingFile sets the parser to one that requires and returns an existing file.
66
+func (p *parserMixin) ExistingFile() (target *string) {
67
+ target = new(string)
68
+ p.ExistingFileVar(target)
69
+ return
70
+}
71
+
72
+// ExistingDir sets the parser to one that requires and returns an existing directory.
73
+func (p *parserMixin) ExistingDir() (target *string) {
74
+ target = new(string)
75
+ p.ExistingDirVar(target)
76
+ return
77
+}
78
+
79
+// ExistingFileOrDir sets the parser to one that requires and returns an existing file OR directory.
80
+func (p *parserMixin) ExistingFileOrDir() (target *string) {
81
+ target = new(string)
82
+ p.ExistingFileOrDirVar(target)
83
+ return
84
+}
85
+
86
+// File returns an os.File against an existing file.
87
+func (p *parserMixin) File() (target **os.File) {
88
+ target = new(*os.File)
89
+ p.FileVar(target)
90
+ return
91
+}
92
+
93
+// File attempts to open a File with os.OpenFile(flag, perm).
94
+func (p *parserMixin) OpenFile(flag int, perm os.FileMode) (target **os.File) {
95
+ target = new(*os.File)
96
+ p.OpenFileVar(target, flag, perm)
97
+ return
98
+}
99
+
100
+// URL provides a valid, parsed url.URL.
101
+func (p *parserMixin) URL() (target **url.URL) {
102
+ target = new(*url.URL)
103
+ p.URLVar(target)
104
+ return
105
+}
106
+
107
+// StringMap provides key=value parsing into a map.
108
+func (p *parserMixin) StringMapVar(target *map[string]string) {
109
+ p.SetValue(newStringMapValue(target))
110
+}
111
+
112
+// Float sets the parser to a float64 parser.
113
+func (p *parserMixin) Float() (target *float64) {
114
+ return p.Float64()
115
+}
116
+
117
+// Float sets the parser to a float64 parser.
118
+func (p *parserMixin) FloatVar(target *float64) {
119
+ p.Float64Var(target)
120
+}
121
+
122
+// Duration sets the parser to a time.Duration parser.
123
+func (p *parserMixin) DurationVar(target *time.Duration) {
124
+ p.SetValue(newDurationValue(target))
125
+}
126
+
127
+// BytesVar parses numeric byte units. eg. 1.5KB
128
+func (p *parserMixin) BytesVar(target *units.Base2Bytes) {
129
+ p.SetValue(newBytesValue(target))
130
+}
131
+
132
+// IP sets the parser to a net.IP parser.
133
+func (p *parserMixin) IPVar(target *net.IP) {
134
+ p.SetValue(newIPValue(target))
135
+}
136
+
137
+// ExistingFile sets the parser to one that requires and returns an existing file.
138
+func (p *parserMixin) ExistingFileVar(target *string) {
139
+ p.SetValue(newExistingFileValue(target))
140
+}
141
+
142
+// ExistingDir sets the parser to one that requires and returns an existing directory.
143
+func (p *parserMixin) ExistingDirVar(target *string) {
144
+ p.SetValue(newExistingDirValue(target))
145
+}
146
+
147
+// ExistingDir sets the parser to one that requires and returns an existing directory.
148
+func (p *parserMixin) ExistingFileOrDirVar(target *string) {
149
+ p.SetValue(newExistingFileOrDirValue(target))
150
+}
151
+
152
+// FileVar opens an existing file.
153
+func (p *parserMixin) FileVar(target **os.File) {
154
+ p.SetValue(newFileValue(target, os.O_RDONLY, 0))
155
+}
156
+
157
+// OpenFileVar calls os.OpenFile(flag, perm)
158
+func (p *parserMixin) OpenFileVar(target **os.File, flag int, perm os.FileMode) {
159
+ p.SetValue(newFileValue(target, flag, perm))
160
+}
161
+
162
+// URL provides a valid, parsed url.URL.
163
+func (p *parserMixin) URLVar(target **url.URL) {
164
+ p.SetValue(newURLValue(target))
165
+}
166
+
167
+// URLList provides a parsed list of url.URL values.
168
+func (p *parserMixin) URLList() (target *[]*url.URL) {
169
+ target = new([]*url.URL)
170
+ p.URLListVar(target)
171
+ return
172
+}
173
+
174
+// URLListVar provides a parsed list of url.URL values.
175
+func (p *parserMixin) URLListVar(target *[]*url.URL) {
176
+ p.SetValue(newURLListValue(target))
177
+}
178
+
179
+// Enum allows a value from a set of options.
180
+func (p *parserMixin) Enum(options ...string) (target *string) {
181
+ target = new(string)
182
+ p.EnumVar(target, options...)
183
+ return
184
+}
185
+
186
+// EnumVar allows a value from a set of options.
187
+func (p *parserMixin) EnumVar(target *string, options ...string) {
188
+ p.SetValue(newEnumFlag(target, options...))
189
+}
190
+
191
+// Enums allows a set of values from a set of options.
192
+func (p *parserMixin) Enums(options ...string) (target *[]string) {
193
+ target = new([]string)
194
+ p.EnumsVar(target, options...)
195
+ return
196
+}
197
+
198
+// EnumVar allows a value from a set of options.
199
+func (p *parserMixin) EnumsVar(target *[]string, options ...string) {
200
+ p.SetValue(newEnumsFlag(target, options...))
201
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/parsers_test.go
new
+98
@@ -0,0 +1,98 @@
1
+package kingpin
2
+
3
+import (
4
+ "io/ioutil"
5
+ "net"
6
+ "net/url"
7
+ "os"
8
+
9
+ "github.com/stretchr/testify/assert"
10
+
11
+ "testing"
12
+)
13
+
14
+func TestParseStrings(t *testing.T) {
15
+ p := parserMixin{}
16
+ v := p.Strings()
17
+ p.value.Set("a")
18
+ p.value.Set("b")
19
+ assert.Equal(t, []string{"a", "b"}, *v)
20
+}
21
+
22
+func TestStringsStringer(t *testing.T) {
23
+ target := []string{}
24
+ v := newAccumulator(&target, func(v interface{}) Value { return newStringValue(v.(*string)) })
25
+ v.Set("hello")
26
+ v.Set("world")
27
+ assert.Equal(t, "hello,world", v.String())
28
+}
29
+
30
+func TestParseStringMap(t *testing.T) {
31
+ p := parserMixin{}
32
+ v := p.StringMap()
33
+ p.value.Set("a:b")
34
+ p.value.Set("b:c")
35
+ assert.Equal(t, map[string]string{"a": "b", "b": "c"}, *v)
36
+}
37
+
38
+func TestParseIP(t *testing.T) {
39
+ p := parserMixin{}
40
+ v := p.IP()
41
+ p.value.Set("10.1.1.2")
42
+ ip := net.ParseIP("10.1.1.2")
43
+ assert.Equal(t, ip, *v)
44
+}
45
+
46
+func TestParseURL(t *testing.T) {
47
+ p := parserMixin{}
48
+ v := p.URL()
49
+ p.value.Set("http://w3.org")
50
+ u, err := url.Parse("http://w3.org")
51
+ assert.NoError(t, err)
52
+ assert.Equal(t, *u, **v)
53
+}
54
+
55
+func TestParseExistingFile(t *testing.T) {
56
+ f, err := ioutil.TempFile("", "")
57
+ if err != nil {
58
+ t.Fatal(err)
59
+ }
60
+ defer f.Close()
61
+ defer os.Remove(f.Name())
62
+
63
+ p := parserMixin{}
64
+ v := p.ExistingFile()
65
+ err = p.value.Set(f.Name())
66
+ assert.NoError(t, err)
67
+ assert.Equal(t, f.Name(), *v)
68
+ err = p.value.Set("/etc/hostsDEFINITELYMISSING")
69
+ assert.Error(t, err)
70
+}
71
+
72
+func TestParseTCPAddr(t *testing.T) {
73
+ p := parserMixin{}
74
+ v := p.TCP()
75
+ err := p.value.Set("127.0.0.1:1234")
76
+ assert.NoError(t, err)
77
+ expected, err := net.ResolveTCPAddr("tcp", "127.0.0.1:1234")
78
+ assert.NoError(t, err)
79
+ assert.Equal(t, *expected, **v)
80
+}
81
+
82
+func TestParseTCPAddrList(t *testing.T) {
83
+ p := parserMixin{}
84
+ _ = p.TCPList()
85
+ err := p.value.Set("127.0.0.1:1234")
86
+ assert.NoError(t, err)
87
+ err = p.value.Set("127.0.0.1:1235")
88
+ assert.NoError(t, err)
89
+ assert.Equal(t, "127.0.0.1:1234,127.0.0.1:1235", p.value.String())
90
+}
91
+
92
+func TestFloat32(t *testing.T) {
93
+ p := parserMixin{}
94
+ v := p.Float32()
95
+ err := p.value.Set("123.45")
96
+ assert.NoError(t, err)
97
+ assert.InEpsilon(t, 123.45, *v, 0.001)
98
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/templates.go
new
+233
@@ -0,0 +1,233 @@
1
+package kingpin
2
+
3
+// Default usage template.
4
+var DefaultUsageTemplate = `{{define "FormatCommand"}}\
5
+{{if .FlagSummary}} {{.FlagSummary}}{{end}}\
6
+{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\
7
+{{end}}\
8
+
9
+{{define "FormatCommands"}}\
10
+{{range .FlattenedCommands}}\
11
+{{if not .Hidden}}\
12
+ {{.FullCommand}}{{if .Default}}*{{end}}{{template "FormatCommand" .}}
13
+{{.Help|Wrap 4}}
14
+{{end}}\
15
+{{end}}\
16
+{{end}}\
17
+
18
+{{define "FormatUsage"}}\
19
+{{template "FormatCommand" .}}{{if .Commands}} <command> [<args> ...]{{end}}
20
+{{if .Help}}
21
+{{.Help|Wrap 0}}\
22
+{{end}}\
23
+
24
+{{end}}\
25
+
26
+{{if .Context.SelectedCommand}}\
27
+usage: {{.App.Name}} {{.Context.SelectedCommand}}{{template "FormatUsage" .Context.SelectedCommand}}
28
+{{else}}\
29
+usage: {{.App.Name}}{{template "FormatUsage" .App}}
30
+{{end}}\
31
+{{if .Context.Flags}}\
32
+Flags:
33
+{{.Context.Flags|FlagsToTwoColumns|FormatTwoColumns}}
34
+{{end}}\
35
+{{if .Context.Args}}\
36
+Args:
37
+{{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}}
38
+{{end}}\
39
+{{if .Context.SelectedCommand}}\
40
+Subcommands:
41
+{{if .Context.SelectedCommand.Commands}}\
42
+{{template "FormatCommands" .Context.SelectedCommand}}
43
+{{end}}\
44
+{{else if .App.Commands}}\
45
+Commands:
46
+{{template "FormatCommands" .App}}
47
+{{end}}\
48
+`
49
+
50
+// Usage template where command's optional flags are listed separately
51
+var SeparateOptionalFlagsUsageTemplate = `{{define "FormatCommand"}}\
52
+{{if .FlagSummary}} {{.FlagSummary}}{{end}}\
53
+{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\
54
+{{end}}\
55
+
56
+{{define "FormatCommands"}}\
57
+{{range .FlattenedCommands}}\
58
+{{if not .Hidden}}\
59
+ {{.FullCommand}}{{if .Default}}*{{end}}{{template "FormatCommand" .}}
60
+{{.Help|Wrap 4}}
61
+{{end}}\
62
+{{end}}\
63
+{{end}}\
64
+
65
+{{define "FormatUsage"}}\
66
+{{template "FormatCommand" .}}{{if .Commands}} <command> [<args> ...]{{end}}
67
+{{if .Help}}
68
+{{.Help|Wrap 0}}\
69
+{{end}}\
70
+
71
+{{end}}\
72
+{{if .Context.SelectedCommand}}\
73
+usage: {{.App.Name}} {{.Context.SelectedCommand}}{{template "FormatUsage" .Context.SelectedCommand}}
74
+{{else}}\
75
+usage: {{.App.Name}}{{template "FormatUsage" .App}}
76
+{{end}}\
77
+
78
+{{if .Context.Flags|RequiredFlags}}\
79
+Required flags:
80
+{{.Context.Flags|RequiredFlags|FlagsToTwoColumns|FormatTwoColumns}}
81
+{{end}}\
82
+{{if .Context.Flags|OptionalFlags}}\
83
+Optional flags:
84
+{{.Context.Flags|OptionalFlags|FlagsToTwoColumns|FormatTwoColumns}}
85
+{{end}}\
86
+{{if .Context.Args}}\
87
+Args:
88
+{{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}}
89
+{{end}}\
90
+{{if .Context.SelectedCommand}}\
91
+Subcommands:
92
+{{if .Context.SelectedCommand.Commands}}\
93
+{{template "FormatCommands" .Context.SelectedCommand}}
94
+{{end}}\
95
+{{else if .App.Commands}}\
96
+Commands:
97
+{{template "FormatCommands" .App}}
98
+{{end}}\
99
+`
100
+
101
+// Usage template with compactly formatted commands.
102
+var CompactUsageTemplate = `{{define "FormatCommand"}}\
103
+{{if .FlagSummary}} {{.FlagSummary}}{{end}}\
104
+{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\
105
+{{end}}\
106
+
107
+{{define "FormatCommandList"}}\
108
+{{range .}}\
109
+{{if not .Hidden}}\
110
+{{.Depth|Indent}}{{.Name}}{{if .Default}}*{{end}}{{template "FormatCommand" .}}
111
+{{end}}\
112
+{{template "FormatCommandList" .Commands}}\
113
+{{end}}\
114
+{{end}}\
115
+
116
+{{define "FormatUsage"}}\
117
+{{template "FormatCommand" .}}{{if .Commands}} <command> [<args> ...]{{end}}
118
+{{if .Help}}
119
+{{.Help|Wrap 0}}\
120
+{{end}}\
121
+
122
+{{end}}\
123
+
124
+{{if .Context.SelectedCommand}}\
125
+usage: {{.App.Name}} {{.Context.SelectedCommand}}{{template "FormatUsage" .Context.SelectedCommand}}
126
+{{else}}\
127
+usage: {{.App.Name}}{{template "FormatUsage" .App}}
128
+{{end}}\
129
+{{if .Context.Flags}}\
130
+Flags:
131
+{{.Context.Flags|FlagsToTwoColumns|FormatTwoColumns}}
132
+{{end}}\
133
+{{if .Context.Args}}\
134
+Args:
135
+{{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}}
136
+{{end}}\
137
+{{if .Context.SelectedCommand}}\
138
+{{if .Context.SelectedCommand.Commands}}\
139
+Commands:
140
+ {{.Context.SelectedCommand}}
141
+{{template "FormatCommandList" .Context.SelectedCommand.Commands}}
142
+{{end}}\
143
+{{else if .App.Commands}}\
144
+Commands:
145
+{{template "FormatCommandList" .App.Commands}}
146
+{{end}}\
147
+`
148
+
149
+var ManPageTemplate = `{{define "FormatFlags"}}\
150
+{{range .Flags}}\
151
+{{if not .Hidden}}\
152
+.TP
153
+\fB{{if .Short}}-{{.Short|Char}}, {{end}}--{{.Name}}{{if not .IsBoolFlag}}={{.FormatPlaceHolder}}{{end}}\\fR
154
+{{.Help}}
155
+{{end}}\
156
+{{end}}\
157
+{{end}}\
158
+
159
+{{define "FormatCommand"}}\
160
+{{if .FlagSummary}} {{.FlagSummary}}{{end}}\
161
+{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}{{if .Default}}*{{end}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\
162
+{{end}}\
163
+
164
+{{define "FormatCommands"}}\
165
+{{range .FlattenedCommands}}\
166
+{{if not .Hidden}}\
167
+.SS
168
+\fB{{.FullCommand}}{{template "FormatCommand" .}}\\fR
169
+.PP
170
+{{.Help}}
171
+{{template "FormatFlags" .}}\
172
+{{end}}\
173
+{{end}}\
174
+{{end}}\
175
+
176
+{{define "FormatUsage"}}\
177
+{{template "FormatCommand" .}}{{if .Commands}} <command> [<args> ...]{{end}}\\fR
178
+{{end}}\
179
+
180
+.TH {{.App.Name}} 1 {{.App.Version}} "{{.App.Author}}"
181
+.SH "NAME"
182
+{{.App.Name}}
183
+.SH "SYNOPSIS"
184
+.TP
185
+\fB{{.App.Name}}{{template "FormatUsage" .App}}
186
+.SH "DESCRIPTION"
187
+{{.App.Help}}
188
+.SH "OPTIONS"
189
+{{template "FormatFlags" .App}}\
190
+{{if .App.Commands}}\
191
+.SH "COMMANDS"
192
+{{template "FormatCommands" .App}}\
193
+{{end}}\
194
+`
195
+
196
+// Default usage template.
197
+var LongHelpTemplate = `{{define "FormatCommand"}}\
198
+{{if .FlagSummary}} {{.FlagSummary}}{{end}}\
199
+{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\
200
+{{end}}\
201
+
202
+{{define "FormatCommands"}}\
203
+{{range .FlattenedCommands}}\
204
+{{if not .Hidden}}\
205
+ {{.FullCommand}}{{template "FormatCommand" .}}
206
+{{.Help|Wrap 4}}
207
+{{with .Flags|FlagsToTwoColumns}}{{FormatTwoColumnsWithIndent . 4 2}}{{end}}
208
+{{end}}\
209
+{{end}}\
210
+{{end}}\
211
+
212
+{{define "FormatUsage"}}\
213
+{{template "FormatCommand" .}}{{if .Commands}} <command> [<args> ...]{{end}}
214
+{{if .Help}}
215
+{{.Help|Wrap 0}}\
216
+{{end}}\
217
+
218
+{{end}}\
219
+
220
+usage: {{.App.Name}}{{template "FormatUsage" .App}}
221
+{{if .Context.Flags}}\
222
+Flags:
223
+{{.Context.Flags|FlagsToTwoColumns|FormatTwoColumns}}
224
+{{end}}\
225
+{{if .Context.Args}}\
226
+Args:
227
+{{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}}
228
+{{end}}\
229
+{{if .App.Commands}}\
230
+Commands:
231
+{{template "FormatCommands" .App}}
232
+{{end}}\
233
+`
Godeps/_workspace/src/github.com/alecthomas/kingpin/usage.go
new
+208
@@ -0,0 +1,208 @@
1
+package kingpin
2
+
3
+import (
4
+ "bytes"
5
+ "fmt"
6
+ "go/doc"
7
+ "io"
8
+ "strings"
9
+
10
+ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/alecthomas/template"
11
+)
12
+
13
+var (
14
+ preIndent = " "
15
+)
16
+
17
+func formatTwoColumns(w io.Writer, indent, padding, width int, rows [][2]string) {
18
+ // Find size of first column.
19
+ s := 0
20
+ for _, row := range rows {
21
+ if c := len(row[0]); c > s && c < 30 {
22
+ s = c
23
+ }
24
+ }
25
+
26
+ indentStr := strings.Repeat(" ", indent)
27
+ offsetStr := strings.Repeat(" ", s+padding)
28
+
29
+ for _, row := range rows {
30
+ buf := bytes.NewBuffer(nil)
31
+ doc.ToText(buf, row[1], "", preIndent, width-s-padding-indent)
32
+ lines := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n")
33
+ fmt.Fprintf(w, "%s%-*s%*s", indentStr, s, row[0], padding, "")
34
+ if len(row[0]) >= 30 {
35
+ fmt.Fprintf(w, "\n%s%s", indentStr, offsetStr)
36
+ }
37
+ fmt.Fprintf(w, "%s\n", lines[0])
38
+ for _, line := range lines[1:] {
39
+ fmt.Fprintf(w, "%s%s%s\n", indentStr, offsetStr, line)
40
+ }
41
+ }
42
+}
43
+
44
+// Usage writes application usage to w. It parses args to determine
45
+// appropriate help context, such as which command to show help for.
46
+func (a *Application) Usage(args []string) {
47
+ context, err := a.ParseContext(args)
48
+ a.FatalIfError(err, "")
49
+ if err := a.UsageForContextWithTemplate(context, 2, a.usageTemplate); err != nil {
50
+ panic(err)
51
+ }
52
+}
53
+
54
+func formatAppUsage(app *ApplicationModel) string {
55
+ s := []string{app.Name}
56
+ if len(app.Flags) > 0 {
57
+ s = append(s, app.FlagSummary())
58
+ }
59
+ if len(app.Args) > 0 {
60
+ s = append(s, app.ArgSummary())
61
+ }
62
+ return strings.Join(s, " ")
63
+}
64
+
65
+func formatCmdUsage(app *ApplicationModel, cmd *CmdModel) string {
66
+ s := []string{app.Name, cmd.String()}
67
+ if len(app.Flags) > 0 {
68
+ s = append(s, app.FlagSummary())
69
+ }
70
+ if len(app.Args) > 0 {
71
+ s = append(s, app.ArgSummary())
72
+ }
73
+ return strings.Join(s, " ")
74
+}
75
+
76
+func formatFlag(haveShort bool, flag *FlagModel) string {
77
+ flagString := ""
78
+ if flag.Short != 0 {
79
+ flagString += fmt.Sprintf("-%c, --%s", flag.Short, flag.Name)
80
+ } else {
81
+ if haveShort {
82
+ flagString += fmt.Sprintf(" --%s", flag.Name)
83
+ } else {
84
+ flagString += fmt.Sprintf("--%s", flag.Name)
85
+ }
86
+ }
87
+ if !flag.IsBoolFlag() {
88
+ flagString += fmt.Sprintf("=%s", flag.FormatPlaceHolder())
89
+ }
90
+ return flagString
91
+}
92
+
93
+type templateParseContext struct {
94
+ SelectedCommand *CmdModel
95
+ *FlagGroupModel
96
+ *ArgGroupModel
97
+}
98
+
99
+type templateContext struct {
100
+ App *ApplicationModel
101
+ Width int
102
+ Context *templateParseContext
103
+}
104
+
105
+// UsageForContext displays usage information from a ParseContext (obtained from
106
+// Application.ParseContext() or Action(f) callbacks).
107
+func (a *Application) UsageForContext(context *ParseContext) error {
108
+ return a.UsageForContextWithTemplate(context, 2, a.usageTemplate)
109
+}
110
+
111
+// UsageForContextWithTemplate is the base usage function. You generally don't need to use this.
112
+func (a *Application) UsageForContextWithTemplate(context *ParseContext, indent int, tmpl string) error {
113
+ width := guessWidth(a.writer)
114
+ funcs := template.FuncMap{
115
+ "Indent": func(level int) string {
116
+ return strings.Repeat(" ", level*indent)
117
+ },
118
+ "Wrap": func(indent int, s string) string {
119
+ buf := bytes.NewBuffer(nil)
120
+ indentText := strings.Repeat(" ", indent)
121
+ doc.ToText(buf, s, indentText, indentText, width)
122
+ return buf.String()
123
+ },
124
+ "FormatFlag": formatFlag,
125
+ "FlagsToTwoColumns": func(f []*FlagModel) [][2]string {
126
+ rows := [][2]string{}
127
+ haveShort := false
128
+ for _, flag := range f {
129
+ if flag.Short != 0 {
130
+ haveShort = true
131
+ break
132
+ }
133
+ }
134
+ for _, flag := range f {
135
+ if !flag.Hidden {
136
+ rows = append(rows, [2]string{formatFlag(haveShort, flag), flag.Help})
137
+ }
138
+ }
139
+ return rows
140
+ },
141
+ "RequiredFlags": func(f []*FlagModel) []*FlagModel {
142
+ requiredFlags := []*FlagModel{}
143
+ for _, flag := range f {
144
+ if flag.Required == true {
145
+ requiredFlags = append(requiredFlags, flag)
146
+ }
147
+ }
148
+ return requiredFlags
149
+ },
150
+ "OptionalFlags": func(f []*FlagModel) []*FlagModel {
151
+ optionalFlags := []*FlagModel{}
152
+ for _, flag := range f {
153
+ if flag.Required == false {
154
+ optionalFlags = append(optionalFlags, flag)
155
+ }
156
+ }
157
+ return optionalFlags
158
+ },
159
+ "ArgsToTwoColumns": func(a []*ArgModel) [][2]string {
160
+ rows := [][2]string{}
161
+ for _, arg := range a {
162
+ s := "<" + arg.Name + ">"
163
+ if !arg.Required {
164
+ s = "[" + s + "]"
165
+ }
166
+ rows = append(rows, [2]string{s, arg.Help})
167
+ }
168
+ return rows
169
+ },
170
+ "FormatTwoColumns": func(rows [][2]string) string {
171
+ buf := bytes.NewBuffer(nil)
172
+ formatTwoColumns(buf, indent, indent, width, rows)
173
+ return buf.String()
174
+ },
175
+ "FormatTwoColumnsWithIndent": func(rows [][2]string, indent, padding int) string {
176
+ buf := bytes.NewBuffer(nil)
177
+ formatTwoColumns(buf, indent, padding, width, rows)
178
+ return buf.String()
179
+ },
180
+ "FormatAppUsage": formatAppUsage,
181
+ "FormatCommandUsage": formatCmdUsage,
182
+ "IsCumulative": func(value Value) bool {
183
+ _, ok := value.(remainderArg)
184
+ return ok
185
+ },
186
+ "Char": func(c rune) string {
187
+ return string(c)
188
+ },
189
+ }
190
+ t, err := template.New("usage").Funcs(funcs).Parse(tmpl)
191
+ if err != nil {
192
+ return err
193
+ }
194
+ var selectedCommand *CmdModel
195
+ if context.SelectedCommand != nil {
196
+ selectedCommand = context.SelectedCommand.Model()
197
+ }
198
+ ctx := templateContext{
199
+ App: a.Model(),
200
+ Width: width,
201
+ Context: &templateParseContext{
202
+ SelectedCommand: selectedCommand,
203
+ FlagGroupModel: context.flags.Model(),
204
+ ArgGroupModel: context.arguments.Model(),
205
+ },
206
+ }
207
+ return t.Execute(a.writer, ctx)
208
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/usage_test.go
new
+65
@@ -0,0 +1,65 @@
1
+package kingpin
2
+
3
+import (
4
+ "bytes"
5
+ "strings"
6
+ "testing"
7
+
8
+ "github.com/stretchr/testify/assert"
9
+)
10
+
11
+func TestFormatTwoColumns(t *testing.T) {
12
+ buf := bytes.NewBuffer(nil)
13
+ formatTwoColumns(buf, 2, 2, 20, [][2]string{
14
+ {"--hello", "Hello world help with something that is cool."},
15
+ })
16
+ expected := ` --hello Hello
17
+ world
18
+ help with
19
+ something
20
+ that is
21
+ cool.
22
+`
23
+ assert.Equal(t, expected, buf.String())
24
+}
25
+
26
+func TestFormatTwoColumnsWide(t *testing.T) {
27
+ samples := [][2]string{
28
+ {strings.Repeat("x", 29), "29 chars"},
29
+ {strings.Repeat("x", 30), "30 chars"}}
30
+ buf := bytes.NewBuffer(nil)
31
+ formatTwoColumns(buf, 0, 0, 200, samples)
32
+ expected := `xxxxxxxxxxxxxxxxxxxxxxxxxxxxx29 chars
33
+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
34
+ 30 chars
35
+`
36
+ assert.Equal(t, expected, buf.String())
37
+}
38
+
39
+func TestHiddenCommand(t *testing.T) {
40
+ templates := []struct{ name, template string }{
41
+ {"default", DefaultUsageTemplate},
42
+ {"Compact", CompactUsageTemplate},
43
+ {"Long", LongHelpTemplate},
44
+ {"Man", ManPageTemplate},
45
+ }
46
+
47
+ var buf bytes.Buffer
48
+ t.Log("1")
49
+
50
+ a := New("test", "Test").Writer(&buf).Terminate(nil)
51
+ a.Command("visible", "visible")
52
+ a.Command("hidden", "hidden").Hidden()
53
+
54
+ for _, tp := range templates {
55
+ buf.Reset()
56
+ a.UsageTemplate(tp.template)
57
+ a.Parse(nil)
58
+ // a.Parse([]string{"--help"})
59
+ usage := buf.String()
60
+ t.Logf("Usage for %s is:\n%s\n", tp.name, usage)
61
+
62
+ assert.NotContains(t, usage, "hidden")
63
+ assert.Contains(t, usage, "visible")
64
+ }
65
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/values.go
new
+391
@@ -0,0 +1,391 @@
1
+package kingpin
2
+
3
+//go:generate go run ./cmd/genvalues/main.go
4
+
5
+import (
6
+ "fmt"
7
+ "net"
8
+ "net/url"
9
+ "os"
10
+ "reflect"
11
+ "regexp"
12
+ "strings"
13
+ "time"
14
+
15
+ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/alecthomas/units"
16
+)
17
+
18
+// NOTE: Most of the base type values were lifted from:
19
+// http://golang.org/src/pkg/flag/flag.go?s=20146:20222
20
+
21
+// Value is the interface to the dynamic value stored in a flag.
22
+// (The default value is represented as a string.)
23
+//
24
+// If a Value has an IsBoolFlag() bool method returning true, the command-line
25
+// parser makes --name equivalent to -name=true rather than using the next
26
+// command-line argument, and adds a --no-name counterpart for negating the
27
+// flag.
28
+type Value interface {
29
+ String() string
30
+ Set(string) error
31
+}
32
+
33
+// Getter is an interface that allows the contents of a Value to be retrieved.
34
+// It wraps the Value interface, rather than being part of it, because it
35
+// appeared after Go 1 and its compatibility rules. All Value types provided
36
+// by this package satisfy the Getter interface.
37
+type Getter interface {
38
+ Value
39
+ Get() interface{}
40
+}
41
+
42
+// Optional interface to indicate boolean flags that don't accept a value, and
43
+// implicitly have a --no-<x> negation counterpart.
44
+type boolFlag interface {
45
+ Value
46
+ IsBoolFlag() bool
47
+}
48
+
49
+// Optional interface for arguments that cumulatively consume all remaining
50
+// input.
51
+type remainderArg interface {
52
+ Value
53
+ IsCumulative() bool
54
+}
55
+
56
+type accumulator struct {
57
+ element func(value interface{}) Value
58
+ typ reflect.Type
59
+ slice reflect.Value
60
+}
61
+
62
+// Use reflection to accumulate values into a slice.
63
+//
64
+// target := []string{}
65
+// newAccumulator(&target, func (value interface{}) Value {
66
+// return newStringValue(value.(*string))
67
+// })
68
+func newAccumulator(slice interface{}, element func(value interface{}) Value) *accumulator {
69
+ typ := reflect.TypeOf(slice)
70
+ if typ.Kind() != reflect.Ptr || typ.Elem().Kind() != reflect.Slice {
71
+ panic("expected a pointer to a slice")
72
+ }
73
+ return &accumulator{
74
+ element: element,
75
+ typ: typ.Elem().Elem(),
76
+ slice: reflect.ValueOf(slice),
77
+ }
78
+}
79
+
80
+func (a *accumulator) String() string {
81
+ out := []string{}
82
+ s := a.slice.Elem()
83
+ for i := 0; i < s.Len(); i++ {
84
+ out = append(out, a.element(s.Index(i).Addr().Interface()).String())
85
+ }
86
+ return strings.Join(out, ",")
87
+}
88
+
89
+func (a *accumulator) Set(value string) error {
90
+ e := reflect.New(a.typ)
91
+ if err := a.element(e.Interface()).Set(value); err != nil {
92
+ return err
93
+ }
94
+ slice := reflect.Append(a.slice.Elem(), e.Elem())
95
+ a.slice.Elem().Set(slice)
96
+ return nil
97
+}
98
+
99
+func (a *accumulator) IsCumulative() bool {
100
+ return true
101
+}
102
+
103
+func (b *boolValue) IsBoolFlag() bool { return true }
104
+
105
+// -- time.Duration Value
106
+type durationValue time.Duration
107
+
108
+func newDurationValue(p *time.Duration) *durationValue {
109
+ return (*durationValue)(p)
110
+}
111
+
112
+func (d *durationValue) Set(s string) error {
113
+ v, err := time.ParseDuration(s)
114
+ *d = durationValue(v)
115
+ return err
116
+}
117
+
118
+func (d *durationValue) Get() interface{} { return time.Duration(*d) }
119
+
120
+func (d *durationValue) String() string { return (*time.Duration)(d).String() }
121
+
122
+// -- map[string]string Value
123
+type stringMapValue map[string]string
124
+
125
+func newStringMapValue(p *map[string]string) *stringMapValue {
126
+ return (*stringMapValue)(p)
127
+}
128
+
129
+var stringMapRegex = regexp.MustCompile("[:=]")
130
+
131
+func (s *stringMapValue) Set(value string) error {
132
+ parts := stringMapRegex.Split(value, 2)
133
+ if len(parts) != 2 {
134
+ return fmt.Errorf("expected KEY=VALUE got '%s'", value)
135
+ }
136
+ (*s)[parts[0]] = parts[1]
137
+ return nil
138
+}
139
+func (s *stringMapValue) String() string {
140
+ return fmt.Sprintf("%s", map[string]string(*s))
141
+}
142
+
143
+func (s *stringMapValue) IsCumulative() bool {
144
+ return true
145
+}
146
+
147
+// -- net.IP Value
148
+type ipValue net.IP
149
+
150
+func newIPValue(p *net.IP) *ipValue {
151
+ return (*ipValue)(p)
152
+}
153
+
154
+func (i *ipValue) Set(value string) error {
155
+ if ip := net.ParseIP(value); ip == nil {
156
+ return fmt.Errorf("'%s' is not an IP address", value)
157
+ } else {
158
+ *i = *(*ipValue)(&ip)
159
+ return nil
160
+ }
161
+}
162
+
163
+func (i *ipValue) String() string {
164
+ return (*net.IP)(i).String()
165
+}
166
+
167
+// -- *net.TCPAddr Value
168
+type tcpAddrValue struct {
169
+ addr **net.TCPAddr
170
+}
171
+
172
+func newTCPAddrValue(p **net.TCPAddr) *tcpAddrValue {
173
+ return &tcpAddrValue{p}
174
+}
175
+
176
+func (i *tcpAddrValue) Set(value string) error {
177
+ if addr, err := net.ResolveTCPAddr("tcp", value); err != nil {
178
+ return fmt.Errorf("'%s' is not a valid TCP address: %s", value, err)
179
+ } else {
180
+ *i.addr = addr
181
+ return nil
182
+ }
183
+}
184
+
185
+func (i *tcpAddrValue) String() string {
186
+ return (*i.addr).String()
187
+}
188
+
189
+// -- existingFile Value
190
+
191
+type fileStatValue struct {
192
+ path *string
193
+ predicate func(os.FileInfo) error
194
+}
195
+
196
+func newFileStatValue(p *string, predicate func(os.FileInfo) error) *fileStatValue {
197
+ return &fileStatValue{
198
+ path: p,
199
+ predicate: predicate,
200
+ }
201
+}
202
+
203
+func (e *fileStatValue) Set(value string) error {
204
+ if s, err := os.Stat(value); os.IsNotExist(err) {
205
+ return fmt.Errorf("path '%s' does not exist", value)
206
+ } else if err != nil {
207
+ return err
208
+ } else if err := e.predicate(s); err != nil {
209
+ return err
210
+ }
211
+ *e.path = value
212
+ return nil
213
+}
214
+
215
+func (e *fileStatValue) String() string {
216
+ return *e.path
217
+}
218
+
219
+// -- os.File value
220
+
221
+type fileValue struct {
222
+ f **os.File
223
+ flag int
224
+ perm os.FileMode
225
+}
226
+
227
+func newFileValue(p **os.File, flag int, perm os.FileMode) *fileValue {
228
+ return &fileValue{p, flag, perm}
229
+}
230
+
231
+func (f *fileValue) Set(value string) error {
232
+ if fd, err := os.OpenFile(value, f.flag, f.perm); err != nil {
233
+ return err
234
+ } else {
235
+ *f.f = fd
236
+ return nil
237
+ }
238
+}
239
+
240
+func (f *fileValue) String() string {
241
+ if *f.f == nil {
242
+ return "<nil>"
243
+ }
244
+ return (*f.f).Name()
245
+}
246
+
247
+// -- url.URL Value
248
+type urlValue struct {
249
+ u **url.URL
250
+}
251
+
252
+func newURLValue(p **url.URL) *urlValue {
253
+ return &urlValue{p}
254
+}
255
+
256
+func (u *urlValue) Set(value string) error {
257
+ if url, err := url.Parse(value); err != nil {
258
+ return fmt.Errorf("invalid URL: %s", err)
259
+ } else {
260
+ *u.u = url
261
+ return nil
262
+ }
263
+}
264
+
265
+func (u *urlValue) String() string {
266
+ if *u.u == nil {
267
+ return "<nil>"
268
+ }
269
+ return (*u.u).String()
270
+}
271
+
272
+// -- []*url.URL Value
273
+type urlListValue []*url.URL
274
+
275
+func newURLListValue(p *[]*url.URL) *urlListValue {
276
+ return (*urlListValue)(p)
277
+}
278
+
279
+func (u *urlListValue) Set(value string) error {
280
+ if url, err := url.Parse(value); err != nil {
281
+ return fmt.Errorf("invalid URL: %s", err)
282
+ } else {
283
+ *u = append(*u, url)
284
+ return nil
285
+ }
286
+}
287
+
288
+func (u *urlListValue) String() string {
289
+ out := []string{}
290
+ for _, url := range *u {
291
+ out = append(out, url.String())
292
+ }
293
+ return strings.Join(out, ",")
294
+}
295
+
296
+// A flag whose value must be in a set of options.
297
+type enumValue struct {
298
+ value *string
299
+ options []string
300
+}
301
+
302
+func newEnumFlag(target *string, options ...string) *enumValue {
303
+ return &enumValue{
304
+ value: target,
305
+ options: options,
306
+ }
307
+}
308
+
309
+func (a *enumValue) String() string {
310
+ return *a.value
311
+}
312
+
313
+func (a *enumValue) Set(value string) error {
314
+ for _, v := range a.options {
315
+ if v == value {
316
+ *a.value = value
317
+ return nil
318
+ }
319
+ }
320
+ return fmt.Errorf("enum value must be one of %s, got '%s'", strings.Join(a.options, ","), value)
321
+}
322
+
323
+// -- []string Enum Value
324
+type enumsValue struct {
325
+ value *[]string
326
+ options []string
327
+}
328
+
329
+func newEnumsFlag(target *[]string, options ...string) *enumsValue {
330
+ return &enumsValue{
331
+ value: target,
332
+ options: options,
333
+ }
334
+}
335
+
336
+func (s *enumsValue) Set(value string) error {
337
+ for _, v := range s.options {
338
+ if v == value {
339
+ *s.value = append(*s.value, value)
340
+ return nil
341
+ }
342
+ }
343
+ return fmt.Errorf("enum value must be one of %s, got '%s'", strings.Join(s.options, ","), value)
344
+}
345
+
346
+func (s *enumsValue) String() string {
347
+ return strings.Join(*s.value, ",")
348
+}
349
+
350
+func (s *enumsValue) IsCumulative() bool {
351
+ return true
352
+}
353
+
354
+// -- units.Base2Bytes Value
355
+type bytesValue units.Base2Bytes
356
+
357
+func newBytesValue(p *units.Base2Bytes) *bytesValue {
358
+ return (*bytesValue)(p)
359
+}
360
+
361
+func (d *bytesValue) Set(s string) error {
362
+ v, err := units.ParseBase2Bytes(s)
363
+ *d = bytesValue(v)
364
+ return err
365
+}
366
+
367
+func (d *bytesValue) Get() interface{} { return units.Base2Bytes(*d) }
368
+
369
+func (d *bytesValue) String() string { return (*units.Base2Bytes)(d).String() }
370
+
371
+func newExistingFileValue(target *string) *fileStatValue {
372
+ return newFileStatValue(target, func(s os.FileInfo) error {
373
+ if s.IsDir() {
374
+ return fmt.Errorf("'%s' is a directory", s.Name())
375
+ }
376
+ return nil
377
+ })
378
+}
379
+
380
+func newExistingDirValue(target *string) *fileStatValue {
381
+ return newFileStatValue(target, func(s os.FileInfo) error {
382
+ if !s.IsDir() {
383
+ return fmt.Errorf("'%s' is a file", s.Name())
384
+ }
385
+ return nil
386
+ })
387
+}
388
+
389
+func newExistingFileOrDirValue(target *string) *fileStatValue {
390
+ return newFileStatValue(target, func(s os.FileInfo) error { return nil })
391
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/values.json
new
+22
@@ -0,0 +1,22 @@
1
+[
2
+ {"type": "bool", "parser": "strconv.ParseBool(s)"},
3
+ {"type": "string", "parser": "s, error(nil)", "format": "string(*f)", "plural": "Strings"},
4
+ {"type": "uint", "parser": "strconv.ParseUint(s, 0, 64)", "plural": "Uints"},
5
+ {"type": "uint8", "parser": "strconv.ParseUint(s, 0, 8)"},
6
+ {"type": "uint16", "parser": "strconv.ParseUint(s, 0, 16)"},
7
+ {"type": "uint32", "parser": "strconv.ParseUint(s, 0, 32)"},
8
+ {"type": "uint64", "parser": "strconv.ParseUint(s, 0, 64)"},
9
+ {"type": "int", "parser": "strconv.ParseFloat(s, 64)", "plural": "Ints"},
10
+ {"type": "int8", "parser": "strconv.ParseInt(s, 0, 8)"},
11
+ {"type": "int16", "parser": "strconv.ParseInt(s, 0, 16)"},
12
+ {"type": "int32", "parser": "strconv.ParseInt(s, 0, 32)"},
13
+ {"type": "int64", "parser": "strconv.ParseInt(s, 0, 64)"},
14
+ {"type": "float64", "parser": "strconv.ParseFloat(s, 64)"},
15
+ {"type": "float32", "parser": "strconv.ParseFloat(s, 32)"},
16
+ {"name": "Duration", "type": "time.Duration", "no_value_parser": true},
17
+ {"name": "IP", "type": "net.IP", "no_value_parser": true},
18
+ {"name": "TCPAddr", "Type": "*net.TCPAddr", "plural": "TCPList", "no_value_parser": true},
19
+ {"name": "ExistingFile", "Type": "string", "plural": "ExistingFiles", "no_value_parser": true},
20
+ {"name": "ExistingDir", "Type": "string", "plural": "ExistingDirs", "no_value_parser": true},
21
+ {"name": "ExistingFileOrDir", "Type": "string", "plural": "ExistingFilesOrDirs", "no_value_parser": true}
22
+]
Godeps/_workspace/src/github.com/alecthomas/kingpin/values_generated.go
new
+622
@@ -0,0 +1,622 @@
1
+package kingpin
2
+
3
+import (
4
+ "fmt"
5
+ "net"
6
+ "strconv"
7
+ "time"
8
+)
9
+
10
+// This file is autogenerated by "go generate .". Do not modify.
11
+
12
+// -- bool Value
13
+type boolValue bool
14
+
15
+func newBoolValue(p *bool) *boolValue {
16
+ return (*boolValue)(p)
17
+}
18
+
19
+func (f *boolValue) Set(s string) error {
20
+ v, err := strconv.ParseBool(s)
21
+ *f = boolValue(v)
22
+ return err
23
+}
24
+
25
+func (f *boolValue) Get() interface{} { return bool(*f) }
26
+
27
+func (f *boolValue) String() string { return fmt.Sprintf("%v", *f) }
28
+
29
+// Bool parses the next command-line value as bool.
30
+func (p *parserMixin) Bool() (target *bool) {
31
+ target = new(bool)
32
+ p.BoolVar(target)
33
+ return
34
+}
35
+
36
+func (p *parserMixin) BoolVar(target *bool) {
37
+ p.SetValue(newBoolValue(target))
38
+}
39
+
40
+// BoolList accumulates bool values into a slice.
41
+func (p *parserMixin) BoolList() (target *[]bool) {
42
+ target = new([]bool)
43
+ p.BoolListVar(target)
44
+ return
45
+}
46
+
47
+func (p *parserMixin) BoolListVar(target *[]bool) {
48
+ p.SetValue(newAccumulator(target, func(v interface{}) Value { return newBoolValue(v.(*bool)) }))
49
+}
50
+
51
+// -- string Value
52
+type stringValue string
53
+
54
+func newStringValue(p *string) *stringValue {
55
+ return (*stringValue)(p)
56
+}
57
+
58
+func (f *stringValue) Set(s string) error {
59
+ v, err := s, error(nil)
60
+ *f = stringValue(v)
61
+ return err
62
+}
63
+
64
+func (f *stringValue) Get() interface{} { return string(*f) }
65
+
66
+func (f *stringValue) String() string { return string(*f) }
67
+
68
+// String parses the next command-line value as string.
69
+func (p *parserMixin) String() (target *string) {
70
+ target = new(string)
71
+ p.StringVar(target)
72
+ return
73
+}
74
+
75
+func (p *parserMixin) StringVar(target *string) {
76
+ p.SetValue(newStringValue(target))
77
+}
78
+
79
+// Strings accumulates string values into a slice.
80
+func (p *parserMixin) Strings() (target *[]string) {
81
+ target = new([]string)
82
+ p.StringsVar(target)
83
+ return
84
+}
85
+
86
+func (p *parserMixin) StringsVar(target *[]string) {
87
+ p.SetValue(newAccumulator(target, func(v interface{}) Value { return newStringValue(v.(*string)) }))
88
+}
89
+
90
+// -- uint Value
91
+type uintValue uint
92
+
93
+func newUintValue(p *uint) *uintValue {
94
+ return (*uintValue)(p)
95
+}
96
+
97
+func (f *uintValue) Set(s string) error {
98
+ v, err := strconv.ParseUint(s, 0, 64)
99
+ *f = uintValue(v)
100
+ return err
101
+}
102
+
103
+func (f *uintValue) Get() interface{} { return uint(*f) }
104
+
105
+func (f *uintValue) String() string { return fmt.Sprintf("%v", *f) }
106
+
107
+// Uint parses the next command-line value as uint.
108
+func (p *parserMixin) Uint() (target *uint) {
109
+ target = new(uint)
110
+ p.UintVar(target)
111
+ return
112
+}
113
+
114
+func (p *parserMixin) UintVar(target *uint) {
115
+ p.SetValue(newUintValue(target))
116
+}
117
+
118
+// Uints accumulates uint values into a slice.
119
+func (p *parserMixin) Uints() (target *[]uint) {
120
+ target = new([]uint)
121
+ p.UintsVar(target)
122
+ return
123
+}
124
+
125
+func (p *parserMixin) UintsVar(target *[]uint) {
126
+ p.SetValue(newAccumulator(target, func(v interface{}) Value { return newUintValue(v.(*uint)) }))
127
+}
128
+
129
+// -- uint8 Value
130
+type uint8Value uint8
131
+
132
+func newUint8Value(p *uint8) *uint8Value {
133
+ return (*uint8Value)(p)
134
+}
135
+
136
+func (f *uint8Value) Set(s string) error {
137
+ v, err := strconv.ParseUint(s, 0, 8)
138
+ *f = uint8Value(v)
139
+ return err
140
+}
141
+
142
+func (f *uint8Value) Get() interface{} { return uint8(*f) }
143
+
144
+func (f *uint8Value) String() string { return fmt.Sprintf("%v", *f) }
145
+
146
+// Uint8 parses the next command-line value as uint8.
147
+func (p *parserMixin) Uint8() (target *uint8) {
148
+ target = new(uint8)
149
+ p.Uint8Var(target)
150
+ return
151
+}
152
+
153
+func (p *parserMixin) Uint8Var(target *uint8) {
154
+ p.SetValue(newUint8Value(target))
155
+}
156
+
157
+// Uint8List accumulates uint8 values into a slice.
158
+func (p *parserMixin) Uint8List() (target *[]uint8) {
159
+ target = new([]uint8)
160
+ p.Uint8ListVar(target)
161
+ return
162
+}
163
+
164
+func (p *parserMixin) Uint8ListVar(target *[]uint8) {
165
+ p.SetValue(newAccumulator(target, func(v interface{}) Value { return newUint8Value(v.(*uint8)) }))
166
+}
167
+
168
+// -- uint16 Value
169
+type uint16Value uint16
170
+
171
+func newUint16Value(p *uint16) *uint16Value {
172
+ return (*uint16Value)(p)
173
+}
174
+
175
+func (f *uint16Value) Set(s string) error {
176
+ v, err := strconv.ParseUint(s, 0, 16)
177
+ *f = uint16Value(v)
178
+ return err
179
+}
180
+
181
+func (f *uint16Value) Get() interface{} { return uint16(*f) }
182
+
183
+func (f *uint16Value) String() string { return fmt.Sprintf("%v", *f) }
184
+
185
+// Uint16 parses the next command-line value as uint16.
186
+func (p *parserMixin) Uint16() (target *uint16) {
187
+ target = new(uint16)
188
+ p.Uint16Var(target)
189
+ return
190
+}
191
+
192
+func (p *parserMixin) Uint16Var(target *uint16) {
193
+ p.SetValue(newUint16Value(target))
194
+}
195
+
196
+// Uint16List accumulates uint16 values into a slice.
197
+func (p *parserMixin) Uint16List() (target *[]uint16) {
198
+ target = new([]uint16)
199
+ p.Uint16ListVar(target)
200
+ return
201
+}
202
+
203
+func (p *parserMixin) Uint16ListVar(target *[]uint16) {
204
+ p.SetValue(newAccumulator(target, func(v interface{}) Value { return newUint16Value(v.(*uint16)) }))
205
+}
206
+
207
+// -- uint32 Value
208
+type uint32Value uint32
209
+
210
+func newUint32Value(p *uint32) *uint32Value {
211
+ return (*uint32Value)(p)
212
+}
213
+
214
+func (f *uint32Value) Set(s string) error {
215
+ v, err := strconv.ParseUint(s, 0, 32)
216
+ *f = uint32Value(v)
217
+ return err
218
+}
219
+
220
+func (f *uint32Value) Get() interface{} { return uint32(*f) }
221
+
222
+func (f *uint32Value) String() string { return fmt.Sprintf("%v", *f) }
223
+
224
+// Uint32 parses the next command-line value as uint32.
225
+func (p *parserMixin) Uint32() (target *uint32) {
226
+ target = new(uint32)
227
+ p.Uint32Var(target)
228
+ return
229
+}
230
+
231
+func (p *parserMixin) Uint32Var(target *uint32) {
232
+ p.SetValue(newUint32Value(target))
233
+}
234
+
235
+// Uint32List accumulates uint32 values into a slice.
236
+func (p *parserMixin) Uint32List() (target *[]uint32) {
237
+ target = new([]uint32)
238
+ p.Uint32ListVar(target)
239
+ return
240
+}
241
+
242
+func (p *parserMixin) Uint32ListVar(target *[]uint32) {
243
+ p.SetValue(newAccumulator(target, func(v interface{}) Value { return newUint32Value(v.(*uint32)) }))
244
+}
245
+
246
+// -- uint64 Value
247
+type uint64Value uint64
248
+
249
+func newUint64Value(p *uint64) *uint64Value {
250
+ return (*uint64Value)(p)
251
+}
252
+
253
+func (f *uint64Value) Set(s string) error {
254
+ v, err := strconv.ParseUint(s, 0, 64)
255
+ *f = uint64Value(v)
256
+ return err
257
+}
258
+
259
+func (f *uint64Value) Get() interface{} { return uint64(*f) }
260
+
261
+func (f *uint64Value) String() string { return fmt.Sprintf("%v", *f) }
262
+
263
+// Uint64 parses the next command-line value as uint64.
264
+func (p *parserMixin) Uint64() (target *uint64) {
265
+ target = new(uint64)
266
+ p.Uint64Var(target)
267
+ return
268
+}
269
+
270
+func (p *parserMixin) Uint64Var(target *uint64) {
271
+ p.SetValue(newUint64Value(target))
272
+}
273
+
274
+// Uint64List accumulates uint64 values into a slice.
275
+func (p *parserMixin) Uint64List() (target *[]uint64) {
276
+ target = new([]uint64)
277
+ p.Uint64ListVar(target)
278
+ return
279
+}
280
+
281
+func (p *parserMixin) Uint64ListVar(target *[]uint64) {
282
+ p.SetValue(newAccumulator(target, func(v interface{}) Value { return newUint64Value(v.(*uint64)) }))
283
+}
284
+
285
+// -- int Value
286
+type intValue int
287
+
288
+func newIntValue(p *int) *intValue {
289
+ return (*intValue)(p)
290
+}
291
+
292
+func (f *intValue) Set(s string) error {
293
+ v, err := strconv.ParseFloat(s, 64)
294
+ *f = intValue(v)
295
+ return err
296
+}
297
+
298
+func (f *intValue) Get() interface{} { return int(*f) }
299
+
300
+func (f *intValue) String() string { return fmt.Sprintf("%v", *f) }
301
+
302
+// Int parses the next command-line value as int.
303
+func (p *parserMixin) Int() (target *int) {
304
+ target = new(int)
305
+ p.IntVar(target)
306
+ return
307
+}
308
+
309
+func (p *parserMixin) IntVar(target *int) {
310
+ p.SetValue(newIntValue(target))
311
+}
312
+
313
+// Ints accumulates int values into a slice.
314
+func (p *parserMixin) Ints() (target *[]int) {
315
+ target = new([]int)
316
+ p.IntsVar(target)
317
+ return
318
+}
319
+
320
+func (p *parserMixin) IntsVar(target *[]int) {
321
+ p.SetValue(newAccumulator(target, func(v interface{}) Value { return newIntValue(v.(*int)) }))
322
+}
323
+
324
+// -- int8 Value
325
+type int8Value int8
326
+
327
+func newInt8Value(p *int8) *int8Value {
328
+ return (*int8Value)(p)
329
+}
330
+
331
+func (f *int8Value) Set(s string) error {
332
+ v, err := strconv.ParseInt(s, 0, 8)
333
+ *f = int8Value(v)
334
+ return err
335
+}
336
+
337
+func (f *int8Value) Get() interface{} { return int8(*f) }
338
+
339
+func (f *int8Value) String() string { return fmt.Sprintf("%v", *f) }
340
+
341
+// Int8 parses the next command-line value as int8.
342
+func (p *parserMixin) Int8() (target *int8) {
343
+ target = new(int8)
344
+ p.Int8Var(target)
345
+ return
346
+}
347
+
348
+func (p *parserMixin) Int8Var(target *int8) {
349
+ p.SetValue(newInt8Value(target))
350
+}
351
+
352
+// Int8List accumulates int8 values into a slice.
353
+func (p *parserMixin) Int8List() (target *[]int8) {
354
+ target = new([]int8)
355
+ p.Int8ListVar(target)
356
+ return
357
+}
358
+
359
+func (p *parserMixin) Int8ListVar(target *[]int8) {
360
+ p.SetValue(newAccumulator(target, func(v interface{}) Value { return newInt8Value(v.(*int8)) }))
361
+}
362
+
363
+// -- int16 Value
364
+type int16Value int16
365
+
366
+func newInt16Value(p *int16) *int16Value {
367
+ return (*int16Value)(p)
368
+}
369
+
370
+func (f *int16Value) Set(s string) error {
371
+ v, err := strconv.ParseInt(s, 0, 16)
372
+ *f = int16Value(v)
373
+ return err
374
+}
375
+
376
+func (f *int16Value) Get() interface{} { return int16(*f) }
377
+
378
+func (f *int16Value) String() string { return fmt.Sprintf("%v", *f) }
379
+
380
+// Int16 parses the next command-line value as int16.
381
+func (p *parserMixin) Int16() (target *int16) {
382
+ target = new(int16)
383
+ p.Int16Var(target)
384
+ return
385
+}
386
+
387
+func (p *parserMixin) Int16Var(target *int16) {
388
+ p.SetValue(newInt16Value(target))
389
+}
390
+
391
+// Int16List accumulates int16 values into a slice.
392
+func (p *parserMixin) Int16List() (target *[]int16) {
393
+ target = new([]int16)
394
+ p.Int16ListVar(target)
395
+ return
396
+}
397
+
398
+func (p *parserMixin) Int16ListVar(target *[]int16) {
399
+ p.SetValue(newAccumulator(target, func(v interface{}) Value { return newInt16Value(v.(*int16)) }))
400
+}
401
+
402
+// -- int32 Value
403
+type int32Value int32
404
+
405
+func newInt32Value(p *int32) *int32Value {
406
+ return (*int32Value)(p)
407
+}
408
+
409
+func (f *int32Value) Set(s string) error {
410
+ v, err := strconv.ParseInt(s, 0, 32)
411
+ *f = int32Value(v)
412
+ return err
413
+}
414
+
415
+func (f *int32Value) Get() interface{} { return int32(*f) }
416
+
417
+func (f *int32Value) String() string { return fmt.Sprintf("%v", *f) }
418
+
419
+// Int32 parses the next command-line value as int32.
420
+func (p *parserMixin) Int32() (target *int32) {
421
+ target = new(int32)
422
+ p.Int32Var(target)
423
+ return
424
+}
425
+
426
+func (p *parserMixin) Int32Var(target *int32) {
427
+ p.SetValue(newInt32Value(target))
428
+}
429
+
430
+// Int32List accumulates int32 values into a slice.
431
+func (p *parserMixin) Int32List() (target *[]int32) {
432
+ target = new([]int32)
433
+ p.Int32ListVar(target)
434
+ return
435
+}
436
+
437
+func (p *parserMixin) Int32ListVar(target *[]int32) {
438
+ p.SetValue(newAccumulator(target, func(v interface{}) Value { return newInt32Value(v.(*int32)) }))
439
+}
440
+
441
+// -- int64 Value
442
+type int64Value int64
443
+
444
+func newInt64Value(p *int64) *int64Value {
445
+ return (*int64Value)(p)
446
+}
447
+
448
+func (f *int64Value) Set(s string) error {
449
+ v, err := strconv.ParseInt(s, 0, 64)
450
+ *f = int64Value(v)
451
+ return err
452
+}
453
+
454
+func (f *int64Value) Get() interface{} { return int64(*f) }
455
+
456
+func (f *int64Value) String() string { return fmt.Sprintf("%v", *f) }
457
+
458
+// Int64 parses the next command-line value as int64.
459
+func (p *parserMixin) Int64() (target *int64) {
460
+ target = new(int64)
461
+ p.Int64Var(target)
462
+ return
463
+}
464
+
465
+func (p *parserMixin) Int64Var(target *int64) {
466
+ p.SetValue(newInt64Value(target))
467
+}
468
+
469
+// Int64List accumulates int64 values into a slice.
470
+func (p *parserMixin) Int64List() (target *[]int64) {
471
+ target = new([]int64)
472
+ p.Int64ListVar(target)
473
+ return
474
+}
475
+
476
+func (p *parserMixin) Int64ListVar(target *[]int64) {
477
+ p.SetValue(newAccumulator(target, func(v interface{}) Value { return newInt64Value(v.(*int64)) }))
478
+}
479
+
480
+// -- float64 Value
481
+type float64Value float64
482
+
483
+func newFloat64Value(p *float64) *float64Value {
484
+ return (*float64Value)(p)
485
+}
486
+
487
+func (f *float64Value) Set(s string) error {
488
+ v, err := strconv.ParseFloat(s, 64)
489
+ *f = float64Value(v)
490
+ return err
491
+}
492
+
493
+func (f *float64Value) Get() interface{} { return float64(*f) }
494
+
495
+func (f *float64Value) String() string { return fmt.Sprintf("%v", *f) }
496
+
497
+// Float64 parses the next command-line value as float64.
498
+func (p *parserMixin) Float64() (target *float64) {
499
+ target = new(float64)
500
+ p.Float64Var(target)
501
+ return
502
+}
503
+
504
+func (p *parserMixin) Float64Var(target *float64) {
505
+ p.SetValue(newFloat64Value(target))
506
+}
507
+
508
+// Float64List accumulates float64 values into a slice.
509
+func (p *parserMixin) Float64List() (target *[]float64) {
510
+ target = new([]float64)
511
+ p.Float64ListVar(target)
512
+ return
513
+}
514
+
515
+func (p *parserMixin) Float64ListVar(target *[]float64) {
516
+ p.SetValue(newAccumulator(target, func(v interface{}) Value { return newFloat64Value(v.(*float64)) }))
517
+}
518
+
519
+// -- float32 Value
520
+type float32Value float32
521
+
522
+func newFloat32Value(p *float32) *float32Value {
523
+ return (*float32Value)(p)
524
+}
525
+
526
+func (f *float32Value) Set(s string) error {
527
+ v, err := strconv.ParseFloat(s, 32)
528
+ *f = float32Value(v)
529
+ return err
530
+}
531
+
532
+func (f *float32Value) Get() interface{} { return float32(*f) }
533
+
534
+func (f *float32Value) String() string { return fmt.Sprintf("%v", *f) }
535
+
536
+// Float32 parses the next command-line value as float32.
537
+func (p *parserMixin) Float32() (target *float32) {
538
+ target = new(float32)
539
+ p.Float32Var(target)
540
+ return
541
+}
542
+
543
+func (p *parserMixin) Float32Var(target *float32) {
544
+ p.SetValue(newFloat32Value(target))
545
+}
546
+
547
+// Float32List accumulates float32 values into a slice.
548
+func (p *parserMixin) Float32List() (target *[]float32) {
549
+ target = new([]float32)
550
+ p.Float32ListVar(target)
551
+ return
552
+}
553
+
554
+func (p *parserMixin) Float32ListVar(target *[]float32) {
555
+ p.SetValue(newAccumulator(target, func(v interface{}) Value { return newFloat32Value(v.(*float32)) }))
556
+}
557
+
558
+// DurationList accumulates time.Duration values into a slice.
559
+func (p *parserMixin) DurationList() (target *[]time.Duration) {
560
+ target = new([]time.Duration)
561
+ p.DurationListVar(target)
562
+ return
563
+}
564
+
565
+func (p *parserMixin) DurationListVar(target *[]time.Duration) {
566
+ p.SetValue(newAccumulator(target, func(v interface{}) Value { return newDurationValue(v.(*time.Duration)) }))
567
+}
568
+
569
+// IPList accumulates net.IP values into a slice.
570
+func (p *parserMixin) IPList() (target *[]net.IP) {
571
+ target = new([]net.IP)
572
+ p.IPListVar(target)
573
+ return
574
+}
575
+
576
+func (p *parserMixin) IPListVar(target *[]net.IP) {
577
+ p.SetValue(newAccumulator(target, func(v interface{}) Value { return newIPValue(v.(*net.IP)) }))
578
+}
579
+
580
+// TCPList accumulates *net.TCPAddr values into a slice.
581
+func (p *parserMixin) TCPList() (target *[]*net.TCPAddr) {
582
+ target = new([]*net.TCPAddr)
583
+ p.TCPListVar(target)
584
+ return
585
+}
586
+
587
+func (p *parserMixin) TCPListVar(target *[]*net.TCPAddr) {
588
+ p.SetValue(newAccumulator(target, func(v interface{}) Value { return newTCPAddrValue(v.(**net.TCPAddr)) }))
589
+}
590
+
591
+// ExistingFiles accumulates string values into a slice.
592
+func (p *parserMixin) ExistingFiles() (target *[]string) {
593
+ target = new([]string)
594
+ p.ExistingFilesVar(target)
595
+ return
596
+}
597
+
598
+func (p *parserMixin) ExistingFilesVar(target *[]string) {
599
+ p.SetValue(newAccumulator(target, func(v interface{}) Value { return newExistingFileValue(v.(*string)) }))
600
+}
601
+
602
+// ExistingDirs accumulates string values into a slice.
603
+func (p *parserMixin) ExistingDirs() (target *[]string) {
604
+ target = new([]string)
605
+ p.ExistingDirsVar(target)
606
+ return
607
+}
608
+
609
+func (p *parserMixin) ExistingDirsVar(target *[]string) {
610
+ p.SetValue(newAccumulator(target, func(v interface{}) Value { return newExistingDirValue(v.(*string)) }))
611
+}
612
+
613
+// ExistingFilesOrDirs accumulates string values into a slice.
614
+func (p *parserMixin) ExistingFilesOrDirs() (target *[]string) {
615
+ target = new([]string)
616
+ p.ExistingFilesOrDirsVar(target)
617
+ return
618
+}
619
+
620
+func (p *parserMixin) ExistingFilesOrDirsVar(target *[]string) {
621
+ p.SetValue(newAccumulator(target, func(v interface{}) Value { return newExistingFileOrDirValue(v.(*string)) }))
622
+}
Godeps/_workspace/src/github.com/alecthomas/kingpin/values_test.go
new
+46
@@ -0,0 +1,46 @@
1
+package kingpin
2
+
3
+import (
4
+ "github.com/stretchr/testify/assert"
5
+
6
+ "testing"
7
+)
8
+
9
+func TestAccumulatorStrings(t *testing.T) {
10
+ target := []string{}
11
+ acc := newAccumulator(&target, func(v interface{}) Value { return newStringValue(v.(*string)) })
12
+ acc.Set("a")
13
+ assert.Equal(t, []string{"a"}, target)
14
+ acc.Set("b")
15
+ assert.Equal(t, []string{"a", "b"}, target)
16
+}
17
+
18
+func TestStrings(t *testing.T) {
19
+ app := New("", "")
20
+ app.Arg("a", "").Required().String()
21
+ app.Arg("b", "").Required().String()
22
+ c := app.Arg("c", "").Required().Strings()
23
+ app.Parse([]string{"a", "b", "a", "b"})
24
+ assert.Equal(t, []string{"a", "b"}, *c)
25
+}
26
+
27
+func TestEnum(t *testing.T) {
28
+ app := New("", "")
29
+ a := app.Arg("a", "").Enum("one", "two", "three")
30
+ _, err := app.Parse([]string{"moo"})
31
+ assert.Error(t, err)
32
+ _, err = app.Parse([]string{"one"})
33
+ assert.NoError(t, err)
34
+ assert.Equal(t, "one", *a)
35
+}
36
+
37
+func TestEnumVar(t *testing.T) {
38
+ app := New("", "")
39
+ var a string
40
+ app.Arg("a", "").EnumVar(&a, "one", "two", "three")
41
+ _, err := app.Parse([]string{"moo"})
42
+ assert.Error(t, err)
43
+ _, err = app.Parse([]string{"one"})
44
+ assert.NoError(t, err)
45
+ assert.Equal(t, "one", a)
46
+}
Godeps/_workspace/src/github.com/alecthomas/template/README.md
new
+25
@@ -0,0 +1,25 @@
1
+# Go's `text/template` package with newline elision
2
+
3
+This is a fork of Go 1.4's [text/template](http://golang.org/pkg/text/template/) package with one addition: a backslash immediately after a closing delimiter will delete all subsequent newlines until a non-newline.
4
+
5
+eg.
6
+
7
+```
8
+{{if true}}\
9
+hello
10
+{{end}}\
11
+```
12
+
13
+Will result in:
14
+
15
+```
16
+hello\n
17
+```
18
+
19
+Rather than:
20
+
21
+```
22
+\n
23
+hello\n
24
+\n
25
+```
Godeps/_workspace/src/github.com/alecthomas/template/doc.go
new
+406
@@ -0,0 +1,406 @@
1
+// Copyright 2011 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 template implements data-driven templates for generating textual output.
7
+
8
+To generate HTML output, see package html/template, which has the same interface
9
+as this package but automatically secures HTML output against certain attacks.
10
+
11
+Templates are executed by applying them to a data structure. Annotations in the
12
+template refer to elements of the data structure (typically a field of a struct
13
+or a key in a map) to control execution and derive values to be displayed.
14
+Execution of the template walks the structure and sets the cursor, represented
15
+by a period '.' and called "dot", to the value at the current location in the
16
+structure as execution proceeds.
17
+
18
+The input text for a template is UTF-8-encoded text in any format.
19
+"Actions"--data evaluations or control structures--are delimited by
20
+"{{" and "}}"; all text outside actions is copied to the output unchanged.
21
+Actions may not span newlines, although comments can.
22
+
23
+Once parsed, a template may be executed safely in parallel.
24
+
25
+Here is a trivial example that prints "17 items are made of wool".
26
+
27
+ type Inventory struct {
28
+ Material string
29
+ Count uint
30
+ }
31
+ sweaters := Inventory{"wool", 17}
32
+ tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}")
33
+ if err != nil { panic(err) }
34
+ err = tmpl.Execute(os.Stdout, sweaters)
35
+ if err != nil { panic(err) }
36
+
37
+More intricate examples appear below.
38
+
39
+Actions
40
+
41
+Here is the list of actions. "Arguments" and "pipelines" are evaluations of
42
+data, defined in detail below.
43
+
44
+*/
45
+// {{/* a comment */}}
46
+// A comment; discarded. May contain newlines.
47
+// Comments do not nest and must start and end at the
48
+// delimiters, as shown here.
49
+/*
50
+
51
+ {{pipeline}}
52
+ The default textual representation of the value of the pipeline
53
+ is copied to the output.
54
+
55
+ {{if pipeline}} T1 {{end}}
56
+ If the value of the pipeline is empty, no output is generated;
57
+ otherwise, T1 is executed. The empty values are false, 0, any
58
+ nil pointer or interface value, and any array, slice, map, or
59
+ string of length zero.
60
+ Dot is unaffected.
61
+
62
+ {{if pipeline}} T1 {{else}} T0 {{end}}
63
+ If the value of the pipeline is empty, T0 is executed;
64
+ otherwise, T1 is executed. Dot is unaffected.
65
+
66
+ {{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
67
+ To simplify the appearance of if-else chains, the else action
68
+ of an if may include another if directly; the effect is exactly
69
+ the same as writing
70
+ {{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}}
71
+
72
+ {{range pipeline}} T1 {{end}}
73
+ The value of the pipeline must be an array, slice, map, or channel.
74
+ If the value of the pipeline has length zero, nothing is output;
75
+ otherwise, dot is set to the successive elements of the array,
76
+ slice, or map and T1 is executed. If the value is a map and the
77
+ keys are of basic type with a defined order ("comparable"), the
78
+ elements will be visited in sorted key order.
79
+
80
+ {{range pipeline}} T1 {{else}} T0 {{end}}
81
+ The value of the pipeline must be an array, slice, map, or channel.
82
+ If the value of the pipeline has length zero, dot is unaffected and
83
+ T0 is executed; otherwise, dot is set to the successive elements
84
+ of the array, slice, or map and T1 is executed.
85
+
86
+ {{template "name"}}
87
+ The template with the specified name is executed with nil data.
88
+
89
+ {{template "name" pipeline}}
90
+ The template with the specified name is executed with dot set
91
+ to the value of the pipeline.
92
+
93
+ {{with pipeline}} T1 {{end}}
94
+ If the value of the pipeline is empty, no output is generated;
95
+ otherwise, dot is set to the value of the pipeline and T1 is
96
+ executed.
97
+
98
+ {{with pipeline}} T1 {{else}} T0 {{end}}
99
+ If the value of the pipeline is empty, dot is unaffected and T0
100
+ is executed; otherwise, dot is set to the value of the pipeline
101
+ and T1 is executed.
102
+
103
+Arguments
104
+
105
+An argument is a simple value, denoted by one of the following.
106
+
107
+ - A boolean, string, character, integer, floating-point, imaginary
108
+ or complex constant in Go syntax. These behave like Go's untyped
109
+ constants, although raw strings may not span newlines.
110
+ - The keyword nil, representing an untyped Go nil.
111
+ - The character '.' (period):
112
+ .
113
+ The result is the value of dot.
114
+ - A variable name, which is a (possibly empty) alphanumeric string
115
+ preceded by a dollar sign, such as
116
+ $piOver2
117
+ or
118
+ $
119
+ The result is the value of the variable.
120
+ Variables are described below.
121
+ - The name of a field of the data, which must be a struct, preceded
122
+ by a period, such as
123
+ .Field
124
+ The result is the value of the field. Field invocations may be
125
+ chained:
126
+ .Field1.Field2
127
+ Fields can also be evaluated on variables, including chaining:
128
+ $x.Field1.Field2
129
+ - The name of a key of the data, which must be a map, preceded
130
+ by a period, such as
131
+ .Key
132
+ The result is the map element value indexed by the key.
133
+ Key invocations may be chained and combined with fields to any
134
+ depth:
135
+ .Field1.Key1.Field2.Key2
136
+ Although the key must be an alphanumeric identifier, unlike with
137
+ field names they do not need to start with an upper case letter.
138
+ Keys can also be evaluated on variables, including chaining:
139
+ $x.key1.key2
140
+ - The name of a niladic method of the data, preceded by a period,
141
+ such as
142
+ .Method
143
+ The result is the value of invoking the method with dot as the
144
+ receiver, dot.Method(). Such a method must have one return value (of
145
+ any type) or two return values, the second of which is an error.
146
+ If it has two and the returned error is non-nil, execution terminates
147
+ and an error is returned to the caller as the value of Execute.
148
+ Method invocations may be chained and combined with fields and keys
149
+ to any depth:
150
+ .Field1.Key1.Method1.Field2.Key2.Method2
151
+ Methods can also be evaluated on variables, including chaining:
152
+ $x.Method1.Field
153
+ - The name of a niladic function, such as
154
+ fun
155
+ The result is the value of invoking the function, fun(). The return
156
+ types and values behave as in methods. Functions and function
157
+ names are described below.
158
+ - A parenthesized instance of one the above, for grouping. The result
159
+ may be accessed by a field or map key invocation.
160
+ print (.F1 arg1) (.F2 arg2)
161
+ (.StructValuedMethod "arg").Field
162
+
163
+Arguments may evaluate to any type; if they are pointers the implementation
164
+automatically indirects to the base type when required.
165
+If an evaluation yields a function value, such as a function-valued
166
+field of a struct, the function is not invoked automatically, but it
167
+can be used as a truth value for an if action and the like. To invoke
168
+it, use the call function, defined below.
169
+
170
+A pipeline is a possibly chained sequence of "commands". A command is a simple
171
+value (argument) or a function or method call, possibly with multiple arguments:
172
+
173
+ Argument
174
+ The result is the value of evaluating the argument.
175
+ .Method [Argument...]
176
+ The method can be alone or the last element of a chain but,
177
+ unlike methods in the middle of a chain, it can take arguments.
178
+ The result is the value of calling the method with the
179
+ arguments:
180
+ dot.Method(Argument1, etc.)
181
+ functionName [Argument...]
182
+ The result is the value of calling the function associated
183
+ with the name:
184
+ function(Argument1, etc.)
185
+ Functions and function names are described below.
186
+
187
+Pipelines
188
+
189
+A pipeline may be "chained" by separating a sequence of commands with pipeline
190
+characters '|'. In a chained pipeline, the result of the each command is
191
+passed as the last argument of the following command. The output of the final
192
+command in the pipeline is the value of the pipeline.
193
+
194
+The output of a command will be either one value or two values, the second of
195
+which has type error. If that second value is present and evaluates to
196
+non-nil, execution terminates and the error is returned to the caller of
197
+Execute.
198
+
199
+Variables
200
+
201
+A pipeline inside an action may initialize a variable to capture the result.
202
+The initialization has syntax
203
+
204
+ $variable := pipeline
205
+
206
+where $variable is the name of the variable. An action that declares a
207
+variable produces no output.
208
+
209
+If a "range" action initializes a variable, the variable is set to the
210
+successive elements of the iteration. Also, a "range" may declare two
211
+variables, separated by a comma:
212
+
213
+ range $index, $element := pipeline
214
+
215
+in which case $index and $element are set to the successive values of the
216
+array/slice index or map key and element, respectively. Note that if there is
217
+only one variable, it is assigned the element; this is opposite to the
218
+convention in Go range clauses.
219
+
220
+A variable's scope extends to the "end" action of the control structure ("if",
221
+"with", or "range") in which it is declared, or to the end of the template if
222
+there is no such control structure. A template invocation does not inherit
223
+variables from the point of its invocation.
224
+
225
+When execution begins, $ is set to the data argument passed to Execute, that is,
226
+to the starting value of dot.
227
+
228
+Examples
229
+
230
+Here are some example one-line templates demonstrating pipelines and variables.
231
+All produce the quoted word "output":
232
+
233
+ {{"\"output\""}}
234
+ A string constant.
235
+ {{`"output"`}}
236
+ A raw string constant.
237
+ {{printf "%q" "output"}}
238
+ A function call.
239
+ {{"output" | printf "%q"}}
240
+ A function call whose final argument comes from the previous
241
+ command.
242
+ {{printf "%q" (print "out" "put")}}
243
+ A parenthesized argument.
244
+ {{"put" | printf "%s%s" "out" | printf "%q"}}
245
+ A more elaborate call.
246
+ {{"output" | printf "%s" | printf "%q"}}
247
+ A longer chain.
248
+ {{with "output"}}{{printf "%q" .}}{{end}}
249
+ A with action using dot.
250
+ {{with $x := "output" | printf "%q"}}{{$x}}{{end}}
251
+ A with action that creates and uses a variable.
252
+ {{with $x := "output"}}{{printf "%q" $x}}{{end}}
253
+ A with action that uses the variable in another action.
254
+ {{with $x := "output"}}{{$x | printf "%q"}}{{end}}
255
+ The same, but pipelined.
256
+
257
+Functions
258
+
259
+During execution functions are found in two function maps: first in the
260
+template, then in the global function map. By default, no functions are defined
261
+in the template but the Funcs method can be used to add them.
262
+
263
+Predefined global functions are named as follows.
264
+
265
+ and
266
+ Returns the boolean AND of its arguments by returning the
267
+ first empty argument or the last argument, that is,
268
+ "and x y" behaves as "if x then y else x". All the
269
+ arguments are evaluated.
270
+ call
271
+ Returns the result of calling the first argument, which
272
+ must be a function, with the remaining arguments as parameters.
273
+ Thus "call .X.Y 1 2" is, in Go notation, dot.X.Y(1, 2) where
274
+ Y is a func-valued field, map entry, or the like.
275
+ The first argument must be the result of an evaluation
276
+ that yields a value of function type (as distinct from
277
+ a predefined function such as print). The function must
278
+ return either one or two result values, the second of which
279
+ is of type error. If the arguments don't match the function
280
+ or the returned error value is non-nil, execution stops.
281
+ html
282
+ Returns the escaped HTML equivalent of the textual
283
+ representation of its arguments.
284
+ index
285
+ Returns the result of indexing its first argument by the
286
+ following arguments. Thus "index x 1 2 3" is, in Go syntax,
287
+ x[1][2][3]. Each indexed item must be a map, slice, or array.
288
+ js
289
+ Returns the escaped JavaScript equivalent of the textual
290
+ representation of its arguments.
291
+ len
292
+ Returns the integer length of its argument.
293
+ not
294
+ Returns the boolean negation of its single argument.
295
+ or
296
+ Returns the boolean OR of its arguments by returning the
297
+ first non-empty argument or the last argument, that is,
298
+ "or x y" behaves as "if x then x else y". All the
299
+ arguments are evaluated.
300
+ print
301
+ An alias for fmt.Sprint
302
+ printf
303
+ An alias for fmt.Sprintf
304
+ println
305
+ An alias for fmt.Sprintln
306
+ urlquery
307
+ Returns the escaped value of the textual representation of
308
+ its arguments in a form suitable for embedding in a URL query.
309
+
310
+The boolean functions take any zero value to be false and a non-zero
311
+value to be true.
312
+
313
+There is also a set of binary comparison operators defined as
314
+functions:
315
+
316
+ eq
317
+ Returns the boolean truth of arg1 == arg2
318
+ ne
319
+ Returns the boolean truth of arg1 != arg2
320
+ lt
321
+ Returns the boolean truth of arg1 < arg2
322
+ le
323
+ Returns the boolean truth of arg1 <= arg2
324
+ gt
325
+ Returns the boolean truth of arg1 > arg2
326
+ ge
327
+ Returns the boolean truth of arg1 >= arg2
328
+
329
+For simpler multi-way equality tests, eq (only) accepts two or more
330
+arguments and compares the second and subsequent to the first,
331
+returning in effect
332
+
333
+ arg1==arg2 || arg1==arg3 || arg1==arg4 ...
334
+
335
+(Unlike with || in Go, however, eq is a function call and all the
336
+arguments will be evaluated.)
337
+
338
+The comparison functions work on basic types only (or named basic
339
+types, such as "type Celsius float32"). They implement the Go rules
340
+for comparison of values, except that size and exact type are
341
+ignored, so any integer value, signed or unsigned, may be compared
342
+with any other integer value. (The arithmetic value is compared,
343
+not the bit pattern, so all negative integers are less than all
344
+unsigned integers.) However, as usual, one may not compare an int
345
+with a float32 and so on.
346
+
347
+Associated templates
348
+
349
+Each template is named by a string specified when it is created. Also, each
350
+template is associated with zero or more other templates that it may invoke by
351
+name; such associations are transitive and form a name space of templates.
352
+
353
+A template may use a template invocation to instantiate another associated
354
+template; see the explanation of the "template" action above. The name must be
355
+that of a template associated with the template that contains the invocation.
356
+
357
+Nested template definitions
358
+
359
+When parsing a template, another template may be defined and associated with the
360
+template being parsed. Template definitions must appear at the top level of the
361
+template, much like global variables in a Go program.
362
+
363
+The syntax of such definitions is to surround each template declaration with a
364
+"define" and "end" action.
365
+
366
+The define action names the template being created by providing a string
367
+constant. Here is a simple example:
368
+
369
+ `{{define "T1"}}ONE{{end}}
370
+ {{define "T2"}}TWO{{end}}
371
+ {{define "T3"}}{{template "T1"}} {{template "T2"}}{{end}}
372
+ {{template "T3"}}`
373
+
374
+This defines two templates, T1 and T2, and a third T3 that invokes the other two
375
+when it is executed. Finally it invokes T3. If executed this template will
376
+produce the text
377
+
378
+ ONE TWO
379
+
380
+By construction, a template may reside in only one association. If it's
381
+necessary to have a template addressable from multiple associations, the
382
+template definition must be parsed multiple times to create distinct *Template
383
+values, or must be copied with the Clone or AddParseTree method.
384
+
385
+Parse may be called multiple times to assemble the various associated templates;
386
+see the ParseFiles and ParseGlob functions and methods for simple ways to parse
387
+related templates stored in files.
388
+
389
+A template may be executed directly or through ExecuteTemplate, which executes
390
+an associated template identified by name. To invoke our example above, we
391
+might write,
392
+
393
+ err := tmpl.Execute(os.Stdout, "no data needed")
394
+ if err != nil {
395
+ log.Fatalf("execution failed: %s", err)
396
+ }
397
+
398
+or to invoke a particular template explicitly by name,
399
+
400
+ err := tmpl.ExecuteTemplate(os.Stdout, "T2", "no data needed")
401
+ if err != nil {
402
+ log.Fatalf("execution failed: %s", err)
403
+ }
404
+
405
+*/
406
+package template
Godeps/_workspace/src/github.com/alecthomas/template/example_test.go
new
+71
@@ -0,0 +1,71 @@
1
+// Copyright 2011 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 template_test
6
+
7
+import (
8
+ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/alecthomas/template" // Define a template.
9
+ "log"
10
+ "os"
11
+)
12
+
13
+func ExampleTemplate() {
14
+
15
+ const letter = `
16
+Dear {{.Name}},
17
+{{if .Attended}}
18
+It was a pleasure to see you at the wedding.{{else}}
19
+It is a shame you couldn't make it to the wedding.{{end}}
20
+{{with .Gift}}Thank you for the lovely {{.}}.
21
+{{end}}
22
+Best wishes,
23
+Josie
24
+`
25
+
26
+ // Prepare some data to insert into the template.
27
+ type Recipient struct {
28
+ Name, Gift string
29
+ Attended bool
30
+ }
31
+ var recipients = []Recipient{
32
+ {"Aunt Mildred", "bone china tea set", true},
33
+ {"Uncle John", "moleskin pants", false},
34
+ {"Cousin Rodney", "", false},
35
+ }
36
+
37
+ // Create a new template and parse the letter into it.
38
+ t := template.Must(template.New("letter").Parse(letter))
39
+
40
+ // Execute the template for each recipient.
41
+ for _, r := range recipients {
42
+ err := t.Execute(os.Stdout, r)
43
+ if err != nil {
44
+ log.Println("executing template:", err)
45
+ }
46
+ }
47
+
48
+ // Output:
49
+ // Dear Aunt Mildred,
50
+ //
51
+ // It was a pleasure to see you at the wedding.
52
+ // Thank you for the lovely bone china tea set.
53
+ //
54
+ // Best wishes,
55
+ // Josie
56
+ //
57
+ // Dear Uncle John,
58
+ //
59
+ // It is a shame you couldn't make it to the wedding.
60
+ // Thank you for the lovely moleskin pants.
61
+ //
62
+ // Best wishes,
63
+ // Josie
64
+ //
65
+ // Dear Cousin Rodney,
66
+ //
67
+ // It is a shame you couldn't make it to the wedding.
68
+ //
69
+ // Best wishes,
70
+ // Josie
71
+}
Godeps/_workspace/src/github.com/alecthomas/template/examplefiles_test.go
new
+181
@@ -0,0 +1,181 @@
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
+package template_test
6
+
7
+import (
8
+ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/alecthomas/template" // templateFile defines the contents of a template to be stored in a file, for testing.
9
+ "io"
10
+ "io/ioutil"
11
+ "log"
12
+ "os"
13
+ "path/filepath"
14
+)
15
+
16
+type templateFile struct {
17
+ name string
18
+ contents string
19
+}
20
+
21
+func createTestDir(files []templateFile) string {
22
+ dir, err := ioutil.TempDir("", "template")
23
+ if err != nil {
24
+ log.Fatal(err)
25
+ }
26
+ for _, file := range files {
27
+ f, err := os.Create(filepath.Join(dir, file.name))
28
+ if err != nil {
29
+ log.Fatal(err)
30
+ }
31
+ defer f.Close()
32
+ _, err = io.WriteString(f, file.contents)
33
+ if err != nil {
34
+ log.Fatal(err)
35
+ }
36
+ }
37
+ return dir
38
+}
39
+
40
+// Here we demonstrate loading a set of templates from a directory.
41
+func ExampleTemplate_glob() {
42
+ // Here we create a temporary directory and populate it with our sample
43
+ // template definition files; usually the template files would already
44
+ // exist in some location known to the program.
45
+ dir := createTestDir([]templateFile{
46
+ // T0.tmpl is a plain template file that just invokes T1.
47
+ {"T0.tmpl", `T0 invokes T1: ({{template "T1"}})`},
48
+ // T1.tmpl defines a template, T1 that invokes T2.
49
+ {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
50
+ // T2.tmpl defines a template T2.
51
+ {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
52
+ })
53
+ // Clean up after the test; another quirk of running as an example.
54
+ defer os.RemoveAll(dir)
55
+
56
+ // pattern is the glob pattern used to find all the template files.
57
+ pattern := filepath.Join(dir, "*.tmpl")
58
+
59
+ // Here starts the example proper.
60
+ // T0.tmpl is the first name matched, so it becomes the starting template,
61
+ // the value returned by ParseGlob.
62
+ tmpl := template.Must(template.ParseGlob(pattern))
63
+
64
+ err := tmpl.Execute(os.Stdout, nil)
65
+ if err != nil {
66
+ log.Fatalf("template execution: %s", err)
67
+ }
68
+ // Output:
69
+ // T0 invokes T1: (T1 invokes T2: (This is T2))
70
+}
71
+
72
+// This example demonstrates one way to share some templates
73
+// and use them in different contexts. In this variant we add multiple driver
74
+// templates by hand to an existing bundle of templates.
75
+func ExampleTemplate_helpers() {
76
+ // Here we create a temporary directory and populate it with our sample
77
+ // template definition files; usually the template files would already
78
+ // exist in some location known to the program.
79
+ dir := createTestDir([]templateFile{
80
+ // T1.tmpl defines a template, T1 that invokes T2.
81
+ {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
82
+ // T2.tmpl defines a template T2.
83
+ {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
84
+ })
85
+ // Clean up after the test; another quirk of running as an example.
86
+ defer os.RemoveAll(dir)
87
+
88
+ // pattern is the glob pattern used to find all the template files.
89
+ pattern := filepath.Join(dir, "*.tmpl")
90
+
91
+ // Here starts the example proper.
92
+ // Load the helpers.
93
+ templates := template.Must(template.ParseGlob(pattern))
94
+ // Add one driver template to the bunch; we do this with an explicit template definition.
95
+ _, err := templates.Parse("{{define `driver1`}}Driver 1 calls T1: ({{template `T1`}})\n{{end}}")
96
+ if err != nil {
97
+ log.Fatal("parsing driver1: ", err)
98
+ }
99
+ // Add another driver template.
100
+ _, err = templates.Parse("{{define `driver2`}}Driver 2 calls T2: ({{template `T2`}})\n{{end}}")
101
+ if err != nil {
102
+ log.Fatal("parsing driver2: ", err)
103
+ }
104
+ // We load all the templates before execution. This package does not require
105
+ // that behavior but html/template's escaping does, so it's a good habit.
106
+ err = templates.ExecuteTemplate(os.Stdout, "driver1", nil)
107
+ if err != nil {
108
+ log.Fatalf("driver1 execution: %s", err)
109
+ }
110
+ err = templates.ExecuteTemplate(os.Stdout, "driver2", nil)
111
+ if err != nil {
112
+ log.Fatalf("driver2 execution: %s", err)
113
+ }
114
+ // Output:
115
+ // Driver 1 calls T1: (T1 invokes T2: (This is T2))
116
+ // Driver 2 calls T2: (This is T2)
117
+}
118
+
119
+// This example demonstrates how to use one group of driver
120
+// templates with distinct sets of helper templates.
121
+func ExampleTemplate_share() {
122
+ // Here we create a temporary directory and populate it with our sample
123
+ // template definition files; usually the template files would already
124
+ // exist in some location known to the program.
125
+ dir := createTestDir([]templateFile{
126
+ // T0.tmpl is a plain template file that just invokes T1.
127
+ {"T0.tmpl", "T0 ({{.}} version) invokes T1: ({{template `T1`}})\n"},
128
+ // T1.tmpl defines a template, T1 that invokes T2. Note T2 is not defined
129
+ {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
130
+ })
131
+ // Clean up after the test; another quirk of running as an example.
132
+ defer os.RemoveAll(dir)
133
+
134
+ // pattern is the glob pattern used to find all the template files.
135
+ pattern := filepath.Join(dir, "*.tmpl")
136
+
137
+ // Here starts the example proper.
138
+ // Load the drivers.
139
+ drivers := template.Must(template.ParseGlob(pattern))
140
+
141
+ // We must define an implementation of the T2 template. First we clone
142
+ // the drivers, then add a definition of T2 to the template name space.
143
+
144
+ // 1. Clone the helper set to create a new name space from which to run them.
145
+ first, err := drivers.Clone()
146
+ if err != nil {
147
+ log.Fatal("cloning helpers: ", err)
148
+ }
149
+ // 2. Define T2, version A, and parse it.
150
+ _, err = first.Parse("{{define `T2`}}T2, version A{{end}}")
151
+ if err != nil {
152
+ log.Fatal("parsing T2: ", err)
153
+ }
154
+
155
+ // Now repeat the whole thing, using a different version of T2.
156
+ // 1. Clone the drivers.
157
+ second, err := drivers.Clone()
158
+ if err != nil {
159
+ log.Fatal("cloning drivers: ", err)
160
+ }
161
+ // 2. Define T2, version B, and parse it.
162
+ _, err = second.Parse("{{define `T2`}}T2, version B{{end}}")
163
+ if err != nil {
164
+ log.Fatal("parsing T2: ", err)
165
+ }
166
+
167
+ // Execute the templates in the reverse order to verify the
168
+ // first is unaffected by the second.
169
+ err = second.ExecuteTemplate(os.Stdout, "T0.tmpl", "second")
170
+ if err != nil {
171
+ log.Fatalf("second execution: %s", err)
172
+ }
173
+ err = first.ExecuteTemplate(os.Stdout, "T0.tmpl", "first")
174
+ if err != nil {
175
+ log.Fatalf("first: execution: %s", err)
176
+ }
177
+
178
+ // Output:
179
+ // T0 (second version) invokes T1: (T1 invokes T2: (T2, version B))
180
+ // T0 (first version) invokes T1: (T1 invokes T2: (T2, version A))
181
+}
Godeps/_workspace/src/github.com/alecthomas/template/examplefunc_test.go
new
+54
@@ -0,0 +1,54 @@
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
+package template_test
6
+
7
+import (
8
+ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/alecthomas/template"
9
+ "log"
10
+ "os"
11
+ "strings"
12
+)
13
+
14
+// This example demonstrates a custom function to process template text.
15
+// It installs the strings.Title function and uses it to
16
+// Make Title Text Look Good In Our Template's Output.
17
+func ExampleTemplate_func() {
18
+ // First we create a FuncMap with which to register the function.
19
+ funcMap := template.FuncMap{
20
+ // The name "title" is what the function will be called in the template text.
21
+ "title": strings.Title,
22
+ }
23
+
24
+ // A simple template definition to test our function.
25
+ // We print the input text several ways:
26
+ // - the original
27
+ // - title-cased
28
+ // - title-cased and then printed with %q
29
+ // - printed with %q and then title-cased.
30
+ const templateText = `
31
+Input: {{printf "%q" .}}
32
+Output 0: {{title .}}
33
+Output 1: {{title . | printf "%q"}}
34
+Output 2: {{printf "%q" . | title}}
35
+`
36
+
37
+ // Create a template, add the function map, and parse the text.
38
+ tmpl, err := template.New("titleTest").Funcs(funcMap).Parse(templateText)
39
+ if err != nil {
40
+ log.Fatalf("parsing: %s", err)
41
+ }
42
+
43
+ // Run the template to verify the output.
44
+ err = tmpl.Execute(os.Stdout, "the go programming language")
45
+ if err != nil {
46
+ log.Fatalf("execution: %s", err)
47
+ }
48
+
49
+ // Output:
50
+ // Input: "the go programming language"
51
+ // Output 0: The Go Programming Language
52
+ // Output 1: "The Go Programming Language"
53
+ // Output 2: "The Go Programming Language"
54
+}
Godeps/_workspace/src/github.com/alecthomas/template/exec.go
new
+844
@@ -0,0 +1,844 @@
1
+// Copyright 2011 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 template
6
+
7
+import (
8
+ "bytes"
9
+ "fmt"
10
+ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/alecthomas/template/parse"
11
+ "io"
12
+ "reflect"
13
+ "runtime"
14
+ "sort"
15
+ "strings"
16
+)
17
+
18
+// state represents the state of an execution. It's not part of the
19
+// template so that multiple executions of the same template
20
+// can execute in parallel.
21
+type state struct {
22
+ tmpl *Template
23
+ wr io.Writer
24
+ node parse.Node // current node, for errors
25
+ vars []variable // push-down stack of variable values.
26
+}
27
+
28
+// variable holds the dynamic value of a variable such as $, $x etc.
29
+type variable struct {
30
+ name string
31
+ value reflect.Value
32
+}
33
+
34
+// push pushes a new variable on the stack.
35
+func (s *state) push(name string, value reflect.Value) {
36
+ s.vars = append(s.vars, variable{name, value})
37
+}
38
+
39
+// mark returns the length of the variable stack.
40
+func (s *state) mark() int {
41
+ return len(s.vars)
42
+}
43
+
44
+// pop pops the variable stack up to the mark.
45
+func (s *state) pop(mark int) {
46
+ s.vars = s.vars[0:mark]
47
+}
48
+
49
+// setVar overwrites the top-nth variable on the stack. Used by range iterations.
50
+func (s *state) setVar(n int, value reflect.Value) {
51
+ s.vars[len(s.vars)-n].value = value
52
+}
53
+
54
+// varValue returns the value of the named variable.
55
+func (s *state) varValue(name string) reflect.Value {
56
+ for i := s.mark() - 1; i >= 0; i-- {
57
+ if s.vars[i].name == name {
58
+ return s.vars[i].value
59
+ }
60
+ }
61
+ s.errorf("undefined variable: %s", name)
62
+ return zero
63
+}
64
+
65
+var zero reflect.Value
66
+
67
+// at marks the state to be on node n, for error reporting.
68
+func (s *state) at(node parse.Node) {
69
+ s.node = node
70
+}
71
+
72
+// doublePercent returns the string with %'s replaced by %%, if necessary,
73
+// so it can be used safely inside a Printf format string.
74
+func doublePercent(str string) string {
75
+ if strings.Contains(str, "%") {
76
+ str = strings.Replace(str, "%", "%%", -1)
77
+ }
78
+ return str
79
+}
80
+
81
+// errorf formats the error and terminates processing.
82
+func (s *state) errorf(format string, args ...interface{}) {
83
+ name := doublePercent(s.tmpl.Name())
84
+ if s.node == nil {
85
+ format = fmt.Sprintf("template: %s: %s", name, format)
86
+ } else {
87
+ location, context := s.tmpl.ErrorContext(s.node)
88
+ format = fmt.Sprintf("template: %s: executing %q at <%s>: %s", location, name, doublePercent(context), format)
89
+ }
90
+ panic(fmt.Errorf(format, args...))
91
+}
92
+
93
+// errRecover is the handler that turns panics into returns from the top
94
+// level of Parse.
95
+func errRecover(errp *error) {
96
+ e := recover()
97
+ if e != nil {
98
+ switch err := e.(type) {
99
+ case runtime.Error:
100
+ panic(e)
101
+ case error:
102
+ *errp = err
103
+ default:
104
+ panic(e)
105
+ }
106
+ }
107
+}
108
+
109
+// ExecuteTemplate applies the template associated with t that has the given name
110
+// to the specified data object and writes the output to wr.
111
+// If an error occurs executing the template or writing its output,
112
+// execution stops, but partial results may already have been written to
113
+// the output writer.
114
+// A template may be executed safely in parallel.
115
+func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
116
+ tmpl := t.tmpl[name]
117
+ if tmpl == nil {
118
+ return fmt.Errorf("template: no template %q associated with template %q", name, t.name)
119
+ }
120
+ return tmpl.Execute(wr, data)
121
+}
122
+
123
+// Execute applies a parsed template to the specified data object,
124
+// and writes the output to wr.
125
+// If an error occurs executing the template or writing its output,
126
+// execution stops, but partial results may already have been written to
127
+// the output writer.
128
+// A template may be executed safely in parallel.
129
+func (t *Template) Execute(wr io.Writer, data interface{}) (err error) {
130
+ defer errRecover(&err)
131
+ value := reflect.ValueOf(data)
132
+ state := &state{
133
+ tmpl: t,
134
+ wr: wr,
135
+ vars: []variable{{"$", value}},
136
+ }
137
+ t.init()
138
+ if t.Tree == nil || t.Root == nil {
139
+ var b bytes.Buffer
140
+ for name, tmpl := range t.tmpl {
141
+ if tmpl.Tree == nil || tmpl.Root == nil {
142
+ continue
143
+ }
144
+ if b.Len() > 0 {
145
+ b.WriteString(", ")
146
+ }
147
+ fmt.Fprintf(&b, "%q", name)
148
+ }
149
+ var s string
150
+ if b.Len() > 0 {
151
+ s = "; defined templates are: " + b.String()
152
+ }
153
+ state.errorf("%q is an incomplete or empty template%s", t.Name(), s)
154
+ }
155
+ state.walk(value, t.Root)
156
+ return
157
+}
158
+
159
+// Walk functions step through the major pieces of the template structure,
160
+// generating output as they go.
161
+func (s *state) walk(dot reflect.Value, node parse.Node) {
162
+ s.at(node)
163
+ switch node := node.(type) {
164
+ case *parse.ActionNode:
165
+ // Do not pop variables so they persist until next end.
166
+ // Also, if the action declares variables, don't print the result.
167
+ val := s.evalPipeline(dot, node.Pipe)
168
+ if len(node.Pipe.Decl) == 0 {
169
+ s.printValue(node, val)
170
+ }
171
+ case *parse.IfNode:
172
+ s.walkIfOrWith(parse.NodeIf, dot, node.Pipe, node.List, node.ElseList)
173
+ case *parse.ListNode:
174
+ for _, node := range node.Nodes {
175
+ s.walk(dot, node)
176
+ }
177
+ case *parse.RangeNode:
178
+ s.walkRange(dot, node)
179
+ case *parse.TemplateNode:
180
+ s.walkTemplate(dot, node)
181
+ case *parse.TextNode:
182
+ if _, err := s.wr.Write(node.Text); err != nil {
183
+ s.errorf("%s", err)
184
+ }
185
+ case *parse.WithNode:
186
+ s.walkIfOrWith(parse.NodeWith, dot, node.Pipe, node.List, node.ElseList)
187
+ default:
188
+ s.errorf("unknown node: %s", node)
189
+ }
190
+}
191
+
192
+// walkIfOrWith walks an 'if' or 'with' node. The two control structures
193
+// are identical in behavior except that 'with' sets dot.
194
+func (s *state) walkIfOrWith(typ parse.NodeType, dot reflect.Value, pipe *parse.PipeNode, list, elseList *parse.ListNode) {
195
+ defer s.pop(s.mark())
196
+ val := s.evalPipeline(dot, pipe)
197
+ truth, ok := isTrue(val)
198
+ if !ok {
199
+ s.errorf("if/with can't use %v", val)
200
+ }
201
+ if truth {
202
+ if typ == parse.NodeWith {
203
+ s.walk(val, list)
204
+ } else {
205
+ s.walk(dot, list)
206
+ }
207
+ } else if elseList != nil {
208
+ s.walk(dot, elseList)
209
+ }
210
+}
211
+
212
+// isTrue reports whether the value is 'true', in the sense of not the zero of its type,
213
+// and whether the value has a meaningful truth value.
214
+func isTrue(val reflect.Value) (truth, ok bool) {
215
+ if !val.IsValid() {
216
+ // Something like var x interface{}, never set. It's a form of nil.
217
+ return false, true
218
+ }
219
+ switch val.Kind() {
220
+ case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
221
+ truth = val.Len() > 0
222
+ case reflect.Bool:
223
+ truth = val.Bool()
224
+ case reflect.Complex64, reflect.Complex128:
225
+ truth = val.Complex() != 0
226
+ case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Interface:
227
+ truth = !val.IsNil()
228
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
229
+ truth = val.Int() != 0
230
+ case reflect.Float32, reflect.Float64:
231
+ truth = val.Float() != 0
232
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
233
+ truth = val.Uint() != 0
234
+ case reflect.Struct:
235
+ truth = true // Struct values are always true.
236
+ default:
237
+ return
238
+ }
239
+ return truth, true
240
+}
241
+
242
+func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
243
+ s.at(r)
244
+ defer s.pop(s.mark())
245
+ val, _ := indirect(s.evalPipeline(dot, r.Pipe))
246
+ // mark top of stack before any variables in the body are pushed.
247
+ mark := s.mark()
248
+ oneIteration := func(index, elem reflect.Value) {
249
+ // Set top var (lexically the second if there are two) to the element.
250
+ if len(r.Pipe.Decl) > 0 {
251
+ s.setVar(1, elem)
252
+ }
253
+ // Set next var (lexically the first if there are two) to the index.
254
+ if len(r.Pipe.Decl) > 1 {
255
+ s.setVar(2, index)
256
+ }
257
+ s.walk(elem, r.List)
258
+ s.pop(mark)
259
+ }
260
+ switch val.Kind() {
261
+ case reflect.Array, reflect.Slice:
262
+ if val.Len() == 0 {
263
+ break
264
+ }
265
+ for i := 0; i < val.Len(); i++ {
266
+ oneIteration(reflect.ValueOf(i), val.Index(i))
267
+ }
268
+ return
269
+ case reflect.Map:
270
+ if val.Len() == 0 {
271
+ break
272
+ }
273
+ for _, key := range sortKeys(val.MapKeys()) {
274
+ oneIteration(key, val.MapIndex(key))
275
+ }
276
+ return
277
+ case reflect.Chan:
278
+ if val.IsNil() {
279
+ break
280
+ }
281
+ i := 0
282
+ for ; ; i++ {
283
+ elem, ok := val.Recv()
284
+ if !ok {
285
+ break
286
+ }
287
+ oneIteration(reflect.ValueOf(i), elem)
288
+ }
289
+ if i == 0 {
290
+ break
291
+ }
292
+ return
293
+ case reflect.Invalid:
294
+ break // An invalid value is likely a nil map, etc. and acts like an empty map.
295
+ default:
296
+ s.errorf("range can't iterate over %v", val)
297
+ }
298
+ if r.ElseList != nil {
299
+ s.walk(dot, r.ElseList)
300
+ }
301
+}
302
+
303
+func (s *state) walkTemplate(dot reflect.Value, t *parse.TemplateNode) {
304
+ s.at(t)
305
+ tmpl := s.tmpl.tmpl[t.Name]
306
+ if tmpl == nil {
307
+ s.errorf("template %q not defined", t.Name)
308
+ }
309
+ // Variables declared by the pipeline persist.
310
+ dot = s.evalPipeline(dot, t.Pipe)
311
+ newState := *s
312
+ newState.tmpl = tmpl
313
+ // No dynamic scoping: template invocations inherit no variables.
314
+ newState.vars = []variable{{"$", dot}}
315
+ newState.walk(dot, tmpl.Root)
316
+}
317
+
318
+// Eval functions evaluate pipelines, commands, and their elements and extract
319
+// values from the data structure by examining fields, calling methods, and so on.
320
+// The printing of those values happens only through walk functions.
321
+
322
+// evalPipeline returns the value acquired by evaluating a pipeline. If the
323
+// pipeline has a variable declaration, the variable will be pushed on the
324
+// stack. Callers should therefore pop the stack after they are finished
325
+// executing commands depending on the pipeline value.
326
+func (s *state) evalPipeline(dot reflect.Value, pipe *parse.PipeNode) (value reflect.Value) {
327
+ if pipe == nil {
328
+ return
329
+ }
330
+ s.at(pipe)
331
+ for _, cmd := range pipe.Cmds {
332
+ value = s.evalCommand(dot, cmd, value) // previous value is this one's final arg.
333
+ // If the object has type interface{}, dig down one level to the thing inside.
334
+ if value.Kind() == reflect.Interface && value.Type().NumMethod() == 0 {
335
+ value = reflect.ValueOf(value.Interface()) // lovely!
336
+ }
337
+ }
338
+ for _, variable := range pipe.Decl {
339
+ s.push(variable.Ident[0], value)
340
+ }
341
+ return value
342
+}
343
+
344
+func (s *state) notAFunction(args []parse.Node, final reflect.Value) {
345
+ if len(args) > 1 || final.IsValid() {
346
+ s.errorf("can't give argument to non-function %s", args[0])
347
+ }
348
+}
349
+
350
+func (s *state) evalCommand(dot reflect.Value, cmd *parse.CommandNode, final reflect.Value) reflect.Value {
351
+ firstWord := cmd.Args[0]
352
+ switch n := firstWord.(type) {
353
+ case *parse.FieldNode:
354
+ return s.evalFieldNode(dot, n, cmd.Args, final)
355
+ case *parse.ChainNode:
356
+ return s.evalChainNode(dot, n, cmd.Args, final)
357
+ case *parse.IdentifierNode:
358
+ // Must be a function.
359
+ return s.evalFunction(dot, n, cmd, cmd.Args, final)
360
+ case *parse.PipeNode:
361
+ // Parenthesized pipeline. The arguments are all inside the pipeline; final is ignored.
362
+ return s.evalPipeline(dot, n)
363
+ case *parse.VariableNode:
364
+ return s.evalVariableNode(dot, n, cmd.Args, final)
365
+ }
366
+ s.at(firstWord)
367
+ s.notAFunction(cmd.Args, final)
368
+ switch word := firstWord.(type) {
369
+ case *parse.BoolNode:
370
+ return reflect.ValueOf(word.True)
371
+ case *parse.DotNode:
372
+ return dot
373
+ case *parse.NilNode:
374
+ s.errorf("nil is not a command")
375
+ case *parse.NumberNode:
376
+ return s.idealConstant(word)
377
+ case *parse.StringNode:
378
+ return reflect.ValueOf(word.Text)
379
+ }
380
+ s.errorf("can't evaluate command %q", firstWord)
381
+ panic("not reached")
382
+}
383
+
384
+// idealConstant is called to return the value of a number in a context where
385
+// we don't know the type. In that case, the syntax of the number tells us
386
+// its type, and we use Go rules to resolve. Note there is no such thing as
387
+// a uint ideal constant in this situation - the value must be of int type.
388
+func (s *state) idealConstant(constant *parse.NumberNode) reflect.Value {
389
+ // These are ideal constants but we don't know the type
390
+ // and we have no context. (If it was a method argument,
391
+ // we'd know what we need.) The syntax guides us to some extent.
392
+ s.at(constant)
393
+ switch {
394
+ case constant.IsComplex:
395
+ return reflect.ValueOf(constant.Complex128) // incontrovertible.
396
+ case constant.IsFloat && !isHexConstant(constant.Text) && strings.IndexAny(constant.Text, ".eE") >= 0:
397
+ return reflect.ValueOf(constant.Float64)
398
+ case constant.IsInt:
399
+ n := int(constant.Int64)
400
+ if int64(n) != constant.Int64 {
401
+ s.errorf("%s overflows int", constant.Text)
402
+ }
403
+ return reflect.ValueOf(n)
404
+ case constant.IsUint:
405
+ s.errorf("%s overflows int", constant.Text)
406
+ }
407
+ return zero
408
+}
409
+
410
+func isHexConstant(s string) bool {
411
+ return len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')
412
+}
413
+
414
+func (s *state) evalFieldNode(dot reflect.Value, field *parse.FieldNode, args []parse.Node, final reflect.Value) reflect.Value {
415
+ s.at(field)
416
+ return s.evalFieldChain(dot, dot, field, field.Ident, args, final)
417
+}
418
+
419
+func (s *state) evalChainNode(dot reflect.Value, chain *parse.ChainNode, args []parse.Node, final reflect.Value) reflect.Value {
420
+ s.at(chain)
421
+ // (pipe).Field1.Field2 has pipe as .Node, fields as .Field. Eval the pipeline, then the fields.
422
+ pipe := s.evalArg(dot, nil, chain.Node)
423
+ if len(chain.Field) == 0 {
424
+ s.errorf("internal error: no fields in evalChainNode")
425
+ }
426
+ return s.evalFieldChain(dot, pipe, chain, chain.Field, args, final)
427
+}
428
+
429
+func (s *state) evalVariableNode(dot reflect.Value, variable *parse.VariableNode, args []parse.Node, final reflect.Value) reflect.Value {
430
+ // $x.Field has $x as the first ident, Field as the second. Eval the var, then the fields.
431
+ s.at(variable)
432
+ value := s.varValue(variable.Ident[0])
433
+ if len(variable.Ident) == 1 {
434
+ s.notAFunction(args, final)
435
+ return value
436
+ }
437
+ return s.evalFieldChain(dot, value, variable, variable.Ident[1:], args, final)
438
+}
439
+
440
+// evalFieldChain evaluates .X.Y.Z possibly followed by arguments.
441
+// dot is the environment in which to evaluate arguments, while
442
+// receiver is the value being walked along the chain.
443
+func (s *state) evalFieldChain(dot, receiver reflect.Value, node parse.Node, ident []string, args []parse.Node, final reflect.Value) reflect.Value {
444
+ n := len(ident)
445
+ for i := 0; i < n-1; i++ {
446
+ receiver = s.evalField(dot, ident[i], node, nil, zero, receiver)
447
+ }
448
+ // Now if it's a method, it gets the arguments.
449
+ return s.evalField(dot, ident[n-1], node, args, final, receiver)
450
+}
451
+
452
+func (s *state) evalFunction(dot reflect.Value, node *parse.IdentifierNode, cmd parse.Node, args []parse.Node, final reflect.Value) reflect.Value {
453
+ s.at(node)
454
+ name := node.Ident
455
+ function, ok := findFunction(name, s.tmpl)
456
+ if !ok {
457
+ s.errorf("%q is not a defined function", name)
458
+ }
459
+ return s.evalCall(dot, function, cmd, name, args, final)
460
+}
461
+
462
+// evalField evaluates an expression like (.Field) or (.Field arg1 arg2).
463
+// The 'final' argument represents the return value from the preceding
464
+// value of the pipeline, if any.
465
+func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, args []parse.Node, final, receiver reflect.Value) reflect.Value {
466
+ if !receiver.IsValid() {
467
+ return zero
468
+ }
469
+ typ := receiver.Type()
470
+ receiver, _ = indirect(receiver)
471
+ // Unless it's an interface, need to get to a value of type *T to guarantee
472
+ // we see all methods of T and *T.
473
+ ptr := receiver
474
+ if ptr.Kind() != reflect.Interface && ptr.CanAddr() {
475
+ ptr = ptr.Addr()
476
+ }
477
+ if method := ptr.MethodByName(fieldName); method.IsValid() {
478
+ return s.evalCall(dot, method, node, fieldName, args, final)
479
+ }
480
+ hasArgs := len(args) > 1 || final.IsValid()
481
+ // It's not a method; must be a field of a struct or an element of a map. The receiver must not be nil.
482
+ receiver, isNil := indirect(receiver)
483
+ if isNil {
484
+ s.errorf("nil pointer evaluating %s.%s", typ, fieldName)
485
+ }
486
+ switch receiver.Kind() {
487
+ case reflect.Struct:
488
+ tField, ok := receiver.Type().FieldByName(fieldName)
489
+ if ok {
490
+ field := receiver.FieldByIndex(tField.Index)
491
+ if tField.PkgPath != "" { // field is unexported
492
+ s.errorf("%s is an unexported field of struct type %s", fieldName, typ)
493
+ }
494
+ // If it's a function, we must call it.
495
+ if hasArgs {
496
+ s.errorf("%s has arguments but cannot be invoked as function", fieldName)
497
+ }
498
+ return field
499
+ }
500
+ s.errorf("%s is not a field of struct type %s", fieldName, typ)
501
+ case reflect.Map:
502
+ // If it's a map, attempt to use the field name as a key.
503
+ nameVal := reflect.ValueOf(fieldName)
504
+ if nameVal.Type().AssignableTo(receiver.Type().Key()) {
505
+ if hasArgs {
506
+ s.errorf("%s is not a method but has arguments", fieldName)
507
+ }
508
+ return receiver.MapIndex(nameVal)
509
+ }
510
+ }
511
+ s.errorf("can't evaluate field %s in type %s", fieldName, typ)
512
+ panic("not reached")
513
+}
514
+
515
+var (
516
+ errorType = reflect.TypeOf((*error)(nil)).Elem()
517
+ fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
518
+)
519
+
520
+// evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so
521
+// it looks just like a function call. The arg list, if non-nil, includes (in the manner of the shell), arg[0]
522
+// as the function itself.
523
+func (s *state) evalCall(dot, fun reflect.Value, node parse.Node, name string, args []parse.Node, final reflect.Value) reflect.Value {
524
+ if args != nil {
525
+ args = args[1:] // Zeroth arg is function name/node; not passed to function.
526
+ }
527
+ typ := fun.Type()
528
+ numIn := len(args)
529
+ if final.IsValid() {
530
+ numIn++
531
+ }
532
+ numFixed := len(args)
533
+ if typ.IsVariadic() {
534
+ numFixed = typ.NumIn() - 1 // last arg is the variadic one.
535
+ if numIn < numFixed {
536
+ s.errorf("wrong number of args for %s: want at least %d got %d", name, typ.NumIn()-1, len(args))
537
+ }
538
+ } else if numIn < typ.NumIn()-1 || !typ.IsVariadic() && numIn != typ.NumIn() {
539
+ s.errorf("wrong number of args for %s: want %d got %d", name, typ.NumIn(), len(args))
540
+ }
541
+ if !goodFunc(typ) {
542
+ // TODO: This could still be a confusing error; maybe goodFunc should provide info.
543
+ s.errorf("can't call method/function %q with %d results", name, typ.NumOut())
544
+ }
545
+ // Build the arg list.
546
+ argv := make([]reflect.Value, numIn)
547
+ // Args must be evaluated. Fixed args first.
548
+ i := 0
549
+ for ; i < numFixed && i < len(args); i++ {
550
+ argv[i] = s.evalArg(dot, typ.In(i), args[i])
551
+ }
552
+ // Now the ... args.
553
+ if typ.IsVariadic() {
554
+ argType := typ.In(typ.NumIn() - 1).Elem() // Argument is a slice.
555
+ for ; i < len(args); i++ {
556
+ argv[i] = s.evalArg(dot, argType, args[i])
557
+ }
558
+ }
559
+ // Add final value if necessary.
560
+ if final.IsValid() {
561
+ t := typ.In(typ.NumIn() - 1)
562
+ if typ.IsVariadic() {
563
+ t = t.Elem()
564
+ }
565
+ argv[i] = s.validateType(final, t)
566
+ }
567
+ result := fun.Call(argv)
568
+ // If we have an error that is not nil, stop execution and return that error to the caller.
569
+ if len(result) == 2 && !result[1].IsNil() {
570
+ s.at(node)
571
+ s.errorf("error calling %s: %s", name, result[1].Interface().(error))
572
+ }
573
+ return result[0]
574
+}
575
+
576
+// canBeNil reports whether an untyped nil can be assigned to the type. See reflect.Zero.
577
+func canBeNil(typ reflect.Type) bool {
578
+ switch typ.Kind() {
579
+ case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
580
+ return true
581
+ }
582
+ return false
583
+}
584
+
585
+// validateType guarantees that the value is valid and assignable to the type.
586
+func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Value {
587
+ if !value.IsValid() {
588
+ if typ == nil || canBeNil(typ) {
589
+ // An untyped nil interface{}. Accept as a proper nil value.
590
+ return reflect.Zero(typ)
591
+ }
592
+ s.errorf("invalid value; expected %s", typ)
593
+ }
594
+ if typ != nil && !value.Type().AssignableTo(typ) {
595
+ if value.Kind() == reflect.Interface && !value.IsNil() {
596
+ value = value.Elem()
597
+ if value.Type().AssignableTo(typ) {
598
+ return value
599
+ }
600
+ // fallthrough
601
+ }
602
+ // Does one dereference or indirection work? We could do more, as we
603
+ // do with method receivers, but that gets messy and method receivers
604
+ // are much more constrained, so it makes more sense there than here.
605
+ // Besides, one is almost always all you need.
606
+ switch {
607
+ case value.Kind() == reflect.Ptr && value.Type().Elem().AssignableTo(typ):
608
+ value = value.Elem()
609
+ if !value.IsValid() {
610
+ s.errorf("dereference of nil pointer of type %s", typ)
611
+ }
612
+ case reflect.PtrTo(value.Type()).AssignableTo(typ) && value.CanAddr():
613
+ value = value.Addr()
614
+ default:
615
+ s.errorf("wrong type for value; expected %s; got %s", typ, value.Type())
616
+ }
617
+ }
618
+ return value
619
+}
620
+
621
+func (s *state) evalArg(dot reflect.Value, typ reflect.Type, n parse.Node) reflect.Value {
622
+ s.at(n)
623
+ switch arg := n.(type) {
624
+ case *parse.DotNode:
625
+ return s.validateType(dot, typ)
626
+ case *parse.NilNode:
627
+ if canBeNil(typ) {
628
+ return reflect.Zero(typ)
629
+ }
630
+ s.errorf("cannot assign nil to %s", typ)
631
+ case *parse.FieldNode:
632
+ return s.validateType(s.evalFieldNode(dot, arg, []parse.Node{n}, zero), typ)
633
+ case *parse.VariableNode:
634
+ return s.validateType(s.evalVariableNode(dot, arg, nil, zero), typ)
635
+ case *parse.PipeNode:
636
+ return s.validateType(s.evalPipeline(dot, arg), typ)
637
+ case *parse.IdentifierNode:
638
+ return s.evalFunction(dot, arg, arg, nil, zero)
639
+ case *parse.ChainNode:
640
+ return s.validateType(s.evalChainNode(dot, arg, nil, zero), typ)
641
+ }
642
+ switch typ.Kind() {
643
+ case reflect.Bool:
644
+ return s.evalBool(typ, n)
645
+ case reflect.Complex64, reflect.Complex128:
646
+ return s.evalComplex(typ, n)
647
+ case reflect.Float32, reflect.Float64:
648
+ return s.evalFloat(typ, n)
649
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
650
+ return s.evalInteger(typ, n)
651
+ case reflect.Interface:
652
+ if typ.NumMethod() == 0 {
653
+ return s.evalEmptyInterface(dot, n)
654
+ }
655
+ case reflect.String:
656
+ return s.evalString(typ, n)
657
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
658
+ return s.evalUnsignedInteger(typ, n)
659
+ }
660
+ s.errorf("can't handle %s for arg of type %s", n, typ)
661
+ panic("not reached")
662
+}
663
+
664
+func (s *state) evalBool(typ reflect.Type, n parse.Node) reflect.Value {
665
+ s.at(n)
666
+ if n, ok := n.(*parse.BoolNode); ok {
667
+ value := reflect.New(typ).Elem()
668
+ value.SetBool(n.True)
669
+ return value
670
+ }
671
+ s.errorf("expected bool; found %s", n)
672
+ panic("not reached")
673
+}
674
+
675
+func (s *state) evalString(typ reflect.Type, n parse.Node) reflect.Value {
676
+ s.at(n)
677
+ if n, ok := n.(*parse.StringNode); ok {
678
+ value := reflect.New(typ).Elem()
679
+ value.SetString(n.Text)
680
+ return value
681
+ }
682
+ s.errorf("expected string; found %s", n)
683
+ panic("not reached")
684
+}
685
+
686
+func (s *state) evalInteger(typ reflect.Type, n parse.Node) reflect.Value {
687
+ s.at(n)
688
+ if n, ok := n.(*parse.NumberNode); ok && n.IsInt {
689
+ value := reflect.New(typ).Elem()
690
+ value.SetInt(n.Int64)
691
+ return value
692
+ }
693
+ s.errorf("expected integer; found %s", n)
694
+ panic("not reached")
695
+}
696
+
697
+func (s *state) evalUnsignedInteger(typ reflect.Type, n parse.Node) reflect.Value {
698
+ s.at(n)
699
+ if n, ok := n.(*parse.NumberNode); ok && n.IsUint {
700
+ value := reflect.New(typ).Elem()
701
+ value.SetUint(n.Uint64)
702
+ return value
703
+ }
704
+ s.errorf("expected unsigned integer; found %s", n)
705
+ panic("not reached")
706
+}
707
+
708
+func (s *state) evalFloat(typ reflect.Type, n parse.Node) reflect.Value {
709
+ s.at(n)
710
+ if n, ok := n.(*parse.NumberNode); ok && n.IsFloat {
711
+ value := reflect.New(typ).Elem()
712
+ value.SetFloat(n.Float64)
713
+ return value
714
+ }
715
+ s.errorf("expected float; found %s", n)
716
+ panic("not reached")
717
+}
718
+
719
+func (s *state) evalComplex(typ reflect.Type, n parse.Node) reflect.Value {
720
+ if n, ok := n.(*parse.NumberNode); ok && n.IsComplex {
721
+ value := reflect.New(typ).Elem()
722
+ value.SetComplex(n.Complex128)
723
+ return value
724
+ }
725
+ s.errorf("expected complex; found %s", n)
726
+ panic("not reached")
727
+}
728
+
729
+func (s *state) evalEmptyInterface(dot reflect.Value, n parse.Node) reflect.Value {
730
+ s.at(n)
731
+ switch n := n.(type) {
732
+ case *parse.BoolNode:
733
+ return reflect.ValueOf(n.True)
734
+ case *parse.DotNode:
735
+ return dot
736
+ case *parse.FieldNode:
737
+ return s.evalFieldNode(dot, n, nil, zero)
738
+ case *parse.IdentifierNode:
739
+ return s.evalFunction(dot, n, n, nil, zero)
740
+ case *parse.NilNode:
741
+ // NilNode is handled in evalArg, the only place that calls here.
742
+ s.errorf("evalEmptyInterface: nil (can't happen)")
743
+ case *parse.NumberNode:
744
+ return s.idealConstant(n)
745
+ case *parse.StringNode:
746
+ return reflect.ValueOf(n.Text)
747
+ case *parse.VariableNode:
748
+ return s.evalVariableNode(dot, n, nil, zero)
749
+ case *parse.PipeNode:
750
+ return s.evalPipeline(dot, n)
751
+ }
752
+ s.errorf("can't handle assignment of %s to empty interface argument", n)
753
+ panic("not reached")
754
+}
755
+
756
+// indirect returns the item at the end of indirection, and a bool to indicate if it's nil.
757
+// We indirect through pointers and empty interfaces (only) because
758
+// non-empty interfaces have methods we might need.
759
+func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
760
+ for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() {
761
+ if v.IsNil() {
762
+ return v, true
763
+ }
764
+ if v.Kind() == reflect.Interface && v.NumMethod() > 0 {
765
+ break
766
+ }
767
+ }
768
+ return v, false
769
+}
770
+
771
+// printValue writes the textual representation of the value to the output of
772
+// the template.
773
+func (s *state) printValue(n parse.Node, v reflect.Value) {
774
+ s.at(n)
775
+ iface, ok := printableValue(v)
776
+ if !ok {
777
+ s.errorf("can't print %s of type %s", n, v.Type())
778
+ }
779
+ fmt.Fprint(s.wr, iface)
780
+}
781
+
782
+// printableValue returns the, possibly indirected, interface value inside v that
783
+// is best for a call to formatted printer.
784
+func printableValue(v reflect.Value) (interface{}, bool) {
785
+ if v.Kind() == reflect.Ptr {
786
+ v, _ = indirect(v) // fmt.Fprint handles nil.
787
+ }
788
+ if !v.IsValid() {
789
+ return "<no value>", true
790
+ }
791
+
792
+ if !v.Type().Implements(errorType) && !v.Type().Implements(fmtStringerType) {
793
+ if v.CanAddr() && (reflect.PtrTo(v.Type()).Implements(errorType) || reflect.PtrTo(v.Type()).Implements(fmtStringerType)) {
794
+ v = v.Addr()
795
+ } else {
796
+ switch v.Kind() {
797
+ case reflect.Chan, reflect.Func:
798
+ return nil, false
799
+ }
800
+ }
801
+ }
802
+ return v.Interface(), true
803
+}
804
+
805
+// Types to help sort the keys in a map for reproducible output.
806
+
807
+type rvs []reflect.Value
808
+
809
+func (x rvs) Len() int { return len(x) }
810
+func (x rvs) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
811
+
812
+type rvInts struct{ rvs }
813
+
814
+func (x rvInts) Less(i, j int) bool { return x.rvs[i].Int() < x.rvs[j].Int() }
815
+
816
+type rvUints struct{ rvs }
817
+
818
+func (x rvUints) Less(i, j int) bool { return x.rvs[i].Uint() < x.rvs[j].Uint() }
819
+
820
+type rvFloats struct{ rvs }
821
+
822
+func (x rvFloats) Less(i, j int) bool { return x.rvs[i].Float() < x.rvs[j].Float() }
823
+
824
+type rvStrings struct{ rvs }
825
+
826
+func (x rvStrings) Less(i, j int) bool { return x.rvs[i].String() < x.rvs[j].String() }
827
+
828
+// sortKeys sorts (if it can) the slice of reflect.Values, which is a slice of map keys.
829
+func sortKeys(v []reflect.Value) []reflect.Value {
830
+ if len(v) <= 1 {
831
+ return v
832
+ }
833
+ switch v[0].Kind() {
834
+ case reflect.Float32, reflect.Float64:
835
+ sort.Sort(rvFloats{v})
836
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
837
+ sort.Sort(rvInts{v})
838
+ case reflect.String:
839
+ sort.Sort(rvStrings{v})
840
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
841
+ sort.Sort(rvUints{v})
842
+ }
843
+ return v
844
+}
Godeps/_workspace/src/github.com/alecthomas/template/exec_test.go
new
+1044
@@ -0,0 +1,1044 @@
1
+// Copyright 2011 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 template
6
+
7
+import (
8
+ "bytes"
9
+ "errors"
10
+ "flag"
11
+ "fmt"
12
+ "reflect"
13
+ "strings"
14
+ "testing"
15
+)
16
+
17
+var debug = flag.Bool("debug", false, "show the errors produced by the tests")
18
+
19
+// T has lots of interesting pieces to use to test execution.
20
+type T struct {
21
+ // Basics
22
+ True bool
23
+ I int
24
+ U16 uint16
25
+ X string
26
+ FloatZero float64
27
+ ComplexZero complex128
28
+ // Nested structs.
29
+ U *U
30
+ // Struct with String method.
31
+ V0 V
32
+ V1, V2 *V
33
+ // Struct with Error method.
34
+ W0 W
35
+ W1, W2 *W
36
+ // Slices
37
+ SI []int
38
+ SIEmpty []int
39
+ SB []bool
40
+ // Maps
41
+ MSI map[string]int
42
+ MSIone map[string]int // one element, for deterministic output
43
+ MSIEmpty map[string]int
44
+ MXI map[interface{}]int
45
+ MII map[int]int
46
+ SMSI []map[string]int
47
+ // Empty interfaces; used to see if we can dig inside one.
48
+ Empty0 interface{} // nil
49
+ Empty1 interface{}
50
+ Empty2 interface{}
51
+ Empty3 interface{}
52
+ Empty4 interface{}
53
+ // Non-empty interface.
54
+ NonEmptyInterface I
55
+ // Stringer.
56
+ Str fmt.Stringer
57
+ Err error
58
+ // Pointers
59
+ PI *int
60
+ PS *string
61
+ PSI *[]int
62
+ NIL *int
63
+ // Function (not method)
64
+ BinaryFunc func(string, string) string
65
+ VariadicFunc func(...string) string
66
+ VariadicFuncInt func(int, ...string) string
67
+ NilOKFunc func(*int) bool
68
+ ErrFunc func() (string, error)
69
+ // Template to test evaluation of templates.
70
+ Tmpl *Template
71
+ // Unexported field; cannot be accessed by template.
72
+ unexported int
73
+}
74
+
75
+type U struct {
76
+ V string
77
+}
78
+
79
+type V struct {
80
+ j int
81
+}
82
+
83
+func (v *V) String() string {
84
+ if v == nil {
85
+ return "nilV"
86
+ }
87
+ return fmt.Sprintf("<%d>", v.j)
88
+}
89
+
90
+type W struct {
91
+ k int
92
+}
93
+
94
+func (w *W) Error() string {
95
+ if w == nil {
96
+ return "nilW"
97
+ }
98
+ return fmt.Sprintf("[%d]", w.k)
99
+}
100
+
101
+var tVal = &T{
102
+ True: true,
103
+ I: 17,
104
+ U16: 16,
105
+ X: "x",
106
+ U: &U{"v"},
107
+ V0: V{6666},
108
+ V1: &V{7777}, // leave V2 as nil
109
+ W0: W{888},
110
+ W1: &W{999}, // leave W2 as nil
111
+ SI: []int{3, 4, 5},
112
+ SB: []bool{true, false},
113
+ MSI: map[string]int{"one": 1, "two": 2, "three": 3},
114
+ MSIone: map[string]int{"one": 1},
115
+ MXI: map[interface{}]int{"one": 1},
116
+ MII: map[int]int{1: 1},
117
+ SMSI: []map[string]int{
118
+ {"one": 1, "two": 2},
119
+ {"eleven": 11, "twelve": 12},
120
+ },
121
+ Empty1: 3,
122
+ Empty2: "empty2",
123
+ Empty3: []int{7, 8},
124
+ Empty4: &U{"UinEmpty"},
125
+ NonEmptyInterface: new(T),
126
+ Str: bytes.NewBuffer([]byte("foozle")),
127
+ Err: errors.New("erroozle"),
128
+ PI: newInt(23),
129
+ PS: newString("a string"),
130
+ PSI: newIntSlice(21, 22, 23),
131
+ BinaryFunc: func(a, b string) string { return fmt.Sprintf("[%s=%s]", a, b) },
132
+ VariadicFunc: func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") },
133
+ VariadicFuncInt: func(a int, s ...string) string { return fmt.Sprint(a, "=<", strings.Join(s, "+"), ">") },
134
+ NilOKFunc: func(s *int) bool { return s == nil },
135
+ ErrFunc: func() (string, error) { return "bla", nil },
136
+ Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X
137
+}
138
+
139
+// A non-empty interface.
140
+type I interface {
141
+ Method0() string
142
+}
143
+
144
+var iVal I = tVal
145
+
146
+// Helpers for creation.
147
+func newInt(n int) *int {
148
+ return &n
149
+}
150
+
151
+func newString(s string) *string {
152
+ return &s
153
+}
154
+
155
+func newIntSlice(n ...int) *[]int {
156
+ p := new([]int)
157
+ *p = make([]int, len(n))
158
+ copy(*p, n)
159
+ return p
160
+}
161
+
162
+// Simple methods with and without arguments.
163
+func (t *T) Method0() string {
164
+ return "M0"
165
+}
166
+
167
+func (t *T) Method1(a int) int {
168
+ return a
169
+}
170
+
171
+func (t *T) Method2(a uint16, b string) string {
172
+ return fmt.Sprintf("Method2: %d %s", a, b)
173
+}
174
+
175
+func (t *T) Method3(v interface{}) string {
176
+ return fmt.Sprintf("Method3: %v", v)
177
+}
178
+
179
+func (t *T) Copy() *T {
180
+ n := new(T)
181
+ *n = *t
182
+ return n
183
+}
184
+
185
+func (t *T) MAdd(a int, b []int) []int {
186
+ v := make([]int, len(b))
187
+ for i, x := range b {
188
+ v[i] = x + a
189
+ }
190
+ return v
191
+}
192
+
193
+var myError = errors.New("my error")
194
+
195
+// MyError returns a value and an error according to its argument.
196
+func (t *T) MyError(error bool) (bool, error) {
197
+ if error {
198
+ return true, myError
199
+ }
200
+ return false, nil
201
+}
202
+
203
+// A few methods to test chaining.
204
+func (t *T) GetU() *U {
205
+ return t.U
206
+}
207
+
208
+func (u *U) TrueFalse(b bool) string {
209
+ if b {
210
+ return "true"
211
+ }
212
+ return ""
213
+}
214
+
215
+func typeOf(arg interface{}) string {
216
+ return fmt.Sprintf("%T", arg)
217
+}
218
+
219
+type execTest struct {
220
+ name string
221
+ input string
222
+ output string
223
+ data interface{}
224
+ ok bool
225
+}
226
+
227
+// bigInt and bigUint are hex string representing numbers either side
228
+// of the max int boundary.
229
+// We do it this way so the test doesn't depend on ints being 32 bits.
230
+var (
231
+ bigInt = fmt.Sprintf("0x%x", int(1<<uint(reflect.TypeOf(0).Bits()-1)-1))
232
+ bigUint = fmt.Sprintf("0x%x", uint(1<<uint(reflect.TypeOf(0).Bits()-1)))
233
+)
234
+
235
+var execTests = []execTest{
236
+ // Trivial cases.
237
+ {"empty", "", "", nil, true},
238
+ {"text", "some text", "some text", nil, true},
239
+ {"nil action", "{{nil}}", "", nil, false},
240
+
241
+ // Ideal constants.
242
+ {"ideal int", "{{typeOf 3}}", "int", 0, true},
243
+ {"ideal float", "{{typeOf 1.0}}", "float64", 0, true},
244
+ {"ideal exp float", "{{typeOf 1e1}}", "float64", 0, true},
245
+ {"ideal complex", "{{typeOf 1i}}", "complex128", 0, true},
246
+ {"ideal int", "{{typeOf " + bigInt + "}}", "int", 0, true},
247
+ {"ideal too big", "{{typeOf " + bigUint + "}}", "", 0, false},
248
+ {"ideal nil without type", "{{nil}}", "", 0, false},
249
+
250
+ // Fields of structs.
251
+ {".X", "-{{.X}}-", "-x-", tVal, true},
252
+ {".U.V", "-{{.U.V}}-", "-v-", tVal, true},
253
+ {".unexported", "{{.unexported}}", "", tVal, false},
254
+
255
+ // Fields on maps.
256
+ {"map .one", "{{.MSI.one}}", "1", tVal, true},
257
+ {"map .two", "{{.MSI.two}}", "2", tVal, true},
258
+ {"map .NO", "{{.MSI.NO}}", "<no value>", tVal, true},
259
+ {"map .one interface", "{{.MXI.one}}", "1", tVal, true},
260
+ {"map .WRONG args", "{{.MSI.one 1}}", "", tVal, false},
261
+ {"map .WRONG type", "{{.MII.one}}", "", tVal, false},
262
+
263
+ // Dots of all kinds to test basic evaluation.
264
+ {"dot int", "<{{.}}>", "<13>", 13, true},
265
+ {"dot uint", "<{{.}}>", "<14>", uint(14), true},
266
+ {"dot float", "<{{.}}>", "<15.1>", 15.1, true},
267
+ {"dot bool", "<{{.}}>", "<true>", true, true},
268
+ {"dot complex", "<{{.}}>", "<(16.2-17i)>", 16.2 - 17i, true},
269
+ {"dot string", "<{{.}}>", "<hello>", "hello", true},
270
+ {"dot slice", "<{{.}}>", "<[-1 -2 -3]>", []int{-1, -2, -3}, true},
271
+ {"dot map", "<{{.}}>", "<map[two:22]>", map[string]int{"two": 22}, true},
272
+ {"dot struct", "<{{.}}>", "<{7 seven}>", struct {
273
+ a int
274
+ b string
275
+ }{7, "seven"}, true},
276
+
277
+ // Variables.
278
+ {"$ int", "{{$}}", "123", 123, true},
279
+ {"$.I", "{{$.I}}", "17", tVal, true},
280
+ {"$.U.V", "{{$.U.V}}", "v", tVal, true},
281
+ {"declare in action", "{{$x := $.U.V}}{{$x}}", "v", tVal, true},
282
+
283
+ // Type with String method.
284
+ {"V{6666}.String()", "-{{.V0}}-", "-<6666>-", tVal, true},
285
+ {"&V{7777}.String()", "-{{.V1}}-", "-<7777>-", tVal, true},
286
+ {"(*V)(nil).String()", "-{{.V2}}-", "-nilV-", tVal, true},
287
+
288
+ // Type with Error method.
289
+ {"W{888}.Error()", "-{{.W0}}-", "-[888]-", tVal, true},
290
+ {"&W{999}.Error()", "-{{.W1}}-", "-[999]-", tVal, true},
291
+ {"(*W)(nil).Error()", "-{{.W2}}-", "-nilW-", tVal, true},
292
+
293
+ // Pointers.
294
+ {"*int", "{{.PI}}", "23", tVal, true},
295
+ {"*string", "{{.PS}}", "a string", tVal, true},
296
+ {"*[]int", "{{.PSI}}", "[21 22 23]", tVal, true},
297
+ {"*[]int[1]", "{{index .PSI 1}}", "22", tVal, true},
298
+ {"NIL", "{{.NIL}}", "<nil>", tVal, true},
299
+
300
+ // Empty interfaces holding values.
301
+ {"empty nil", "{{.Empty0}}", "<no value>", tVal, true},
302
+ {"empty with int", "{{.Empty1}}", "3", tVal, true},
303
+ {"empty with string", "{{.Empty2}}", "empty2", tVal, true},
304
+ {"empty with slice", "{{.Empty3}}", "[7 8]", tVal, true},
305
+ {"empty with struct", "{{.Empty4}}", "{UinEmpty}", tVal, true},
306
+ {"empty with struct, field", "{{.Empty4.V}}", "UinEmpty", tVal, true},
307
+
308
+ // Method calls.
309
+ {".Method0", "-{{.Method0}}-", "-M0-", tVal, true},
310
+ {".Method1(1234)", "-{{.Method1 1234}}-", "-1234-", tVal, true},
311
+ {".Method1(.I)", "-{{.Method1 .I}}-", "-17-", tVal, true},
312
+ {".Method2(3, .X)", "-{{.Method2 3 .X}}-", "-Method2: 3 x-", tVal, true},
313
+ {".Method2(.U16, `str`)", "-{{.Method2 .U16 `str`}}-", "-Method2: 16 str-", tVal, true},
314
+ {".Method2(.U16, $x)", "{{if $x := .X}}-{{.Method2 .U16 $x}}{{end}}-", "-Method2: 16 x-", tVal, true},
315
+ {".Method3(nil constant)", "-{{.Method3 nil}}-", "-Method3: <nil>-", tVal, true},
316
+ {".Method3(nil value)", "-{{.Method3 .MXI.unset}}-", "-Method3: <nil>-", tVal, true},
317
+ {"method on var", "{{if $x := .}}-{{$x.Method2 .U16 $x.X}}{{end}}-", "-Method2: 16 x-", tVal, true},
318
+ {"method on chained var",
319
+ "{{range .MSIone}}{{if $.U.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}",
320
+ "true", tVal, true},
321
+ {"chained method",
322
+ "{{range .MSIone}}{{if $.GetU.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}",
323
+ "true", tVal, true},
324
+ {"chained method on variable",
325
+ "{{with $x := .}}{{with .SI}}{{$.GetU.TrueFalse $.True}}{{end}}{{end}}",
326
+ "true", tVal, true},
327
+ {".NilOKFunc not nil", "{{call .NilOKFunc .PI}}", "false", tVal, true},
328
+ {".NilOKFunc nil", "{{call .NilOKFunc nil}}", "true", tVal, true},
329
+
330
+ // Function call builtin.
331
+ {".BinaryFunc", "{{call .BinaryFunc `1` `2`}}", "[1=2]", tVal, true},
332
+ {".VariadicFunc0", "{{call .VariadicFunc}}", "<>", tVal, true},
333
+ {".VariadicFunc2", "{{call .VariadicFunc `he` `llo`}}", "<he+llo>", tVal, true},
334
+ {".VariadicFuncInt", "{{call .VariadicFuncInt 33 `he` `llo`}}", "33=<he+llo>", tVal, true},
335
+ {"if .BinaryFunc call", "{{ if .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{end}}", "[1=2]", tVal, true},
336
+ {"if not .BinaryFunc call", "{{ if not .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{else}}No{{end}}", "No", tVal, true},
337
+ {"Interface Call", `{{stringer .S}}`, "foozle", map[string]interface{}{"S": bytes.NewBufferString("foozle")}, true},
338
+ {".ErrFunc", "{{call .ErrFunc}}", "bla", tVal, true},
339
+
340
+ // Erroneous function calls (check args).
341
+ {".BinaryFuncTooFew", "{{call .BinaryFunc `1`}}", "", tVal, false},
342
+ {".BinaryFuncTooMany", "{{call .BinaryFunc `1` `2` `3`}}", "", tVal, false},
343
+ {".BinaryFuncBad0", "{{call .BinaryFunc 1 3}}", "", tVal, false},
344
+ {".BinaryFuncBad1", "{{call .BinaryFunc `1` 3}}", "", tVal, false},
345
+ {".VariadicFuncBad0", "{{call .VariadicFunc 3}}", "", tVal, false},
346
+ {".VariadicFuncIntBad0", "{{call .VariadicFuncInt}}", "", tVal, false},
347
+ {".VariadicFuncIntBad`", "{{call .VariadicFuncInt `x`}}", "", tVal, false},
348
+ {".VariadicFuncNilBad", "{{call .VariadicFunc nil}}", "", tVal, false},
349
+
350
+ // Pipelines.
351
+ {"pipeline", "-{{.Method0 | .Method2 .U16}}-", "-Method2: 16 M0-", tVal, true},
352
+ {"pipeline func", "-{{call .VariadicFunc `llo` | call .VariadicFunc `he` }}-", "-<he+<llo>>-", tVal, true},
353
+
354
+ // Parenthesized expressions
355
+ {"parens in pipeline", "{{printf `%d %d %d` (1) (2 | add 3) (add 4 (add 5 6))}}", "1 5 15", tVal, true},
356
+
357
+ // Parenthesized expressions with field accesses
358
+ {"parens: $ in paren", "{{($).X}}", "x", tVal, true},
359
+ {"parens: $.GetU in paren", "{{($.GetU).V}}", "v", tVal, true},
360
+ {"parens: $ in paren in pipe", "{{($ | echo).X}}", "x", tVal, true},
361
+ {"parens: spaces and args", `{{(makemap "up" "down" "left" "right").left}}`, "right", tVal, true},
362
+
363
+ // If.
364
+ {"if true", "{{if true}}TRUE{{end}}", "TRUE", tVal, true},
365
+ {"if false", "{{if false}}TRUE{{else}}FALSE{{end}}", "FALSE", tVal, true},
366
+ {"if nil", "{{if nil}}TRUE{{end}}", "", tVal, false},
367
+ {"if 1", "{{if 1}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true},
368
+ {"if 0", "{{if 0}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true},
369
+ {"if 1.5", "{{if 1.5}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true},
370
+ {"if 0.0", "{{if .FloatZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true},
371
+ {"if 1.5i", "{{if 1.5i}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true},
372
+ {"if 0.0i", "{{if .ComplexZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true},
373
+ {"if emptystring", "{{if ``}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
374
+ {"if string", "{{if `notempty`}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true},
375
+ {"if emptyslice", "{{if .SIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
376
+ {"if slice", "{{if .SI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true},
377
+ {"if emptymap", "{{if .MSIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
378
+ {"if map", "{{if .MSI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true},
379
+ {"if map unset", "{{if .MXI.none}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true},
380
+ {"if map not unset", "{{if not .MXI.none}}ZERO{{else}}NON-ZERO{{end}}", "ZERO", tVal, true},
381
+ {"if $x with $y int", "{{if $x := true}}{{with $y := .I}}{{$x}},{{$y}}{{end}}{{end}}", "true,17", tVal, true},
382
+ {"if $x with $x int", "{{if $x := true}}{{with $x := .I}}{{$x}},{{end}}{{$x}}{{end}}", "17,true", tVal, true},
383
+ {"if else if", "{{if false}}FALSE{{else if true}}TRUE{{end}}", "TRUE", tVal, true},
384
+ {"if else chain", "{{if eq 1 3}}1{{else if eq 2 3}}2{{else if eq 3 3}}3{{end}}", "3", tVal, true},
385
+
386
+ // Print etc.
387
+ {"print", `{{print "hello, print"}}`, "hello, print", tVal, true},
388
+ {"print 123", `{{print 1 2 3}}`, "1 2 3", tVal, true},
389
+ {"print nil", `{{print nil}}`, "<nil>", tVal, true},
390
+ {"println", `{{println 1 2 3}}`, "1 2 3\n", tVal, true},
391
+ {"printf int", `{{printf "%04x" 127}}`, "007f", tVal, true},
392
+ {"printf float", `{{printf "%g" 3.5}}`, "3.5", tVal, true},
393
+ {"printf complex", `{{printf "%g" 1+7i}}`, "(1+7i)", tVal, true},
394
+ {"printf string", `{{printf "%s" "hello"}}`, "hello", tVal, true},
395
+ {"printf function", `{{printf "%#q" zeroArgs}}`, "`zeroArgs`", tVal, true},
396
+ {"printf field", `{{printf "%s" .U.V}}`, "v", tVal, true},
397
+ {"printf method", `{{printf "%s" .Method0}}`, "M0", tVal, true},
398
+ {"printf dot", `{{with .I}}{{printf "%d" .}}{{end}}`, "17", tVal, true},
399
+ {"printf var", `{{with $x := .I}}{{printf "%d" $x}}{{end}}`, "17", tVal, true},
400
+ {"printf lots", `{{printf "%d %s %g %s" 127 "hello" 7-3i .Method0}}`, "127 hello (7-3i) M0", tVal, true},
401
+
402
+ // HTML.
403
+ {"html", `{{html "<script>alert(\"XSS\");</script>"}}`,
404
+ "<script>alert("XSS");</script>", nil, true},
405
+ {"html pipeline", `{{printf "<script>alert(\"XSS\");</script>" | html}}`,
406
+ "<script>alert("XSS");</script>", nil, true},
407
+ {"html", `{{html .PS}}`, "a string", tVal, true},
408
+
409
+ // JavaScript.
410
+ {"js", `{{js .}}`, `It\'d be nice.`, `It'd be nice.`, true},
411
+
412
+ // URL query.
413
+ {"urlquery", `{{"http://www.example.org/"|urlquery}}`, "http%3A%2F%2Fwww.example.org%2F", nil, true},
414
+
415
+ // Booleans
416
+ {"not", "{{not true}} {{not false}}", "false true", nil, true},
417
+ {"and", "{{and false 0}} {{and 1 0}} {{and 0 true}} {{and 1 1}}", "false 0 0 1", nil, true},
418
+ {"or", "{{or 0 0}} {{or 1 0}} {{or 0 true}} {{or 1 1}}", "0 1 true 1", nil, true},
419
+ {"boolean if", "{{if and true 1 `hi`}}TRUE{{else}}FALSE{{end}}", "TRUE", tVal, true},
420
+ {"boolean if not", "{{if and true 1 `hi` | not}}TRUE{{else}}FALSE{{end}}", "FALSE", nil, true},
421
+
422
+ // Indexing.
423
+ {"slice[0]", "{{index .SI 0}}", "3", tVal, true},
424
+ {"slice[1]", "{{index .SI 1}}", "4", tVal, true},
425
+ {"slice[HUGE]", "{{index .SI 10}}", "", tVal, false},
426
+ {"slice[WRONG]", "{{index .SI `hello`}}", "", tVal, false},
427
+ {"map[one]", "{{index .MSI `one`}}", "1", tVal, true},
428
+ {"map[two]", "{{index .MSI `two`}}", "2", tVal, true},
429
+ {"map[NO]", "{{index .MSI `XXX`}}", "0", tVal, true},
430
+ {"map[nil]", "{{index .MSI nil}}", "0", tVal, true},
431
+ {"map[WRONG]", "{{index .MSI 10}}", "", tVal, false},
432
+ {"double index", "{{index .SMSI 1 `eleven`}}", "11", tVal, true},
433
+
434
+ // Len.
435
+ {"slice", "{{len .SI}}", "3", tVal, true},
436
+ {"map", "{{len .MSI }}", "3", tVal, true},
437
+ {"len of int", "{{len 3}}", "", tVal, false},
438
+ {"len of nothing", "{{len .Empty0}}", "", tVal, false},
439
+
440
+ // With.
441
+ {"with true", "{{with true}}{{.}}{{end}}", "true", tVal, true},
442
+ {"with false", "{{with false}}{{.}}{{else}}FALSE{{end}}", "FALSE", tVal, true},
443
+ {"with 1", "{{with 1}}{{.}}{{else}}ZERO{{end}}", "1", tVal, true},
444
+ {"with 0", "{{with 0}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true},
445
+ {"with 1.5", "{{with 1.5}}{{.}}{{else}}ZERO{{end}}", "1.5", tVal, true},
446
+ {"with 0.0", "{{with .FloatZero}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true},
447
+ {"with 1.5i", "{{with 1.5i}}{{.}}{{else}}ZERO{{end}}", "(0+1.5i)", tVal, true},
448
+ {"with 0.0i", "{{with .ComplexZero}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true},
449
+ {"with emptystring", "{{with ``}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
450
+ {"with string", "{{with `notempty`}}{{.}}{{else}}EMPTY{{end}}", "notempty", tVal, true},
451
+ {"with emptyslice", "{{with .SIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
452
+ {"with slice", "{{with .SI}}{{.}}{{else}}EMPTY{{end}}", "[3 4 5]", tVal, true},
453
+ {"with emptymap", "{{with .MSIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
454
+ {"with map", "{{with .MSIone}}{{.}}{{else}}EMPTY{{end}}", "map[one:1]", tVal, true},
455
+ {"with empty interface, struct field", "{{with .Empty4}}{{.V}}{{end}}", "UinEmpty", tVal, true},
456
+ {"with $x int", "{{with $x := .I}}{{$x}}{{end}}", "17", tVal, true},
457
+ {"with $x struct.U.V", "{{with $x := $}}{{$x.U.V}}{{end}}", "v", tVal, true},
458
+ {"with variable and action", "{{with $x := $}}{{$y := $.U.V}}{{$y}}{{end}}", "v", tVal, true},
459
+
460
+ // Range.
461
+ {"range []int", "{{range .SI}}-{{.}}-{{end}}", "-3--4--5-", tVal, true},
462
+ {"range empty no else", "{{range .SIEmpty}}-{{.}}-{{end}}", "", tVal, true},
463
+ {"range []int else", "{{range .SI}}-{{.}}-{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true},
464
+ {"range empty else", "{{range .SIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
465
+ {"range []bool", "{{range .SB}}-{{.}}-{{end}}", "-true--false-", tVal, true},
466
+ {"range []int method", "{{range .SI | .MAdd .I}}-{{.}}-{{end}}", "-20--21--22-", tVal, true},
467
+ {"range map", "{{range .MSI}}-{{.}}-{{end}}", "-1--3--2-", tVal, true},
468
+ {"range empty map no else", "{{range .MSIEmpty}}-{{.}}-{{end}}", "", tVal, true},
469
+ {"range map else", "{{range .MSI}}-{{.}}-{{else}}EMPTY{{end}}", "-1--3--2-", tVal, true},
470
+ {"range empty map else", "{{range .MSIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
471
+ {"range empty interface", "{{range .Empty3}}-{{.}}-{{else}}EMPTY{{end}}", "-7--8-", tVal, true},
472
+ {"range empty nil", "{{range .Empty0}}-{{.}}-{{end}}", "", tVal, true},
473
+ {"range $x SI", "{{range $x := .SI}}<{{$x}}>{{end}}", "<3><4><5>", tVal, true},
474
+ {"range $x $y SI", "{{range $x, $y := .SI}}<{{$x}}={{$y}}>{{end}}", "<0=3><1=4><2=5>", tVal, true},
475
+ {"range $x MSIone", "{{range $x := .MSIone}}<{{$x}}>{{end}}", "<1>", tVal, true},
476
+ {"range $x $y MSIone", "{{range $x, $y := .MSIone}}<{{$x}}={{$y}}>{{end}}", "<one=1>", tVal, true},
477
+ {"range $x PSI", "{{range $x := .PSI}}<{{$x}}>{{end}}", "<21><22><23>", tVal, true},
478
+ {"declare in range", "{{range $x := .PSI}}<{{$foo:=$x}}{{$x}}>{{end}}", "<21><22><23>", tVal, true},
479
+ {"range count", `{{range $i, $x := count 5}}[{{$i}}]{{$x}}{{end}}`, "[0]a[1]b[2]c[3]d[4]e", tVal, true},
480
+ {"range nil count", `{{range $i, $x := count 0}}{{else}}empty{{end}}`, "empty", tVal, true},
481
+
482
+ // Cute examples.
483
+ {"or as if true", `{{or .SI "slice is empty"}}`, "[3 4 5]", tVal, true},
484
+ {"or as if false", `{{or .SIEmpty "slice is empty"}}`, "slice is empty", tVal, true},
485
+
486
+ // Error handling.
487
+ {"error method, error", "{{.MyError true}}", "", tVal, false},
488
+ {"error method, no error", "{{.MyError false}}", "false", tVal, true},
489
+
490
+ // Fixed bugs.
491
+ // Must separate dot and receiver; otherwise args are evaluated with dot set to variable.
492
+ {"bug0", "{{range .MSIone}}{{if $.Method1 .}}X{{end}}{{end}}", "X", tVal, true},
493
+ // Do not loop endlessly in indirect for non-empty interfaces.
494
+ // The bug appears with *interface only; looped forever.
495
+ {"bug1", "{{.Method0}}", "M0", &iVal, true},
496
+ // Was taking address of interface field, so method set was empty.
497
+ {"bug2", "{{$.NonEmptyInterface.Method0}}", "M0", tVal, true},
498
+ // Struct values were not legal in with - mere oversight.
499
+ {"bug3", "{{with $}}{{.Method0}}{{end}}", "M0", tVal, true},
500
+ // Nil interface values in if.
501
+ {"bug4", "{{if .Empty0}}non-nil{{else}}nil{{end}}", "nil", tVal, true},
502
+ // Stringer.
503
+ {"bug5", "{{.Str}}", "foozle", tVal, true},
504
+ {"bug5a", "{{.Err}}", "erroozle", tVal, true},
505
+ // Args need to be indirected and dereferenced sometimes.
506
+ {"bug6a", "{{vfunc .V0 .V1}}", "vfunc", tVal, true},
507
+ {"bug6b", "{{vfunc .V0 .V0}}", "vfunc", tVal, true},
508
+ {"bug6c", "{{vfunc .V1 .V0}}", "vfunc", tVal, true},
509
+ {"bug6d", "{{vfunc .V1 .V1}}", "vfunc", tVal, true},
510
+ // Legal parse but illegal execution: non-function should have no arguments.
511
+ {"bug7a", "{{3 2}}", "", tVal, false},
512
+ {"bug7b", "{{$x := 1}}{{$x 2}}", "", tVal, false},
513
+ {"bug7c", "{{$x := 1}}{{3 | $x}}", "", tVal, false},
514
+ // Pipelined arg was not being type-checked.
515
+ {"bug8a", "{{3|oneArg}}", "", tVal, false},
516
+ {"bug8b", "{{4|dddArg 3}}", "", tVal, false},
517
+ // A bug was introduced that broke map lookups for lower-case names.
518
+ {"bug9", "{{.cause}}", "neglect", map[string]string{"cause": "neglect"}, true},
519
+ // Field chain starting with function did not work.
520
+ {"bug10", "{{mapOfThree.three}}-{{(mapOfThree).three}}", "3-3", 0, true},
521
+ // Dereferencing nil pointer while evaluating function arguments should not panic. Issue 7333.
522
+ {"bug11", "{{valueString .PS}}", "", T{}, false},
523
+ // 0xef gave constant type float64. Issue 8622.
524
+ {"bug12xe", "{{printf `%T` 0xef}}", "int", T{}, true},
525
+ {"bug12xE", "{{printf `%T` 0xEE}}", "int", T{}, true},
526
+ {"bug12Xe", "{{printf `%T` 0Xef}}", "int", T{}, true},
527
+ {"bug12XE", "{{printf `%T` 0XEE}}", "int", T{}, true},
528
+ // Chained nodes did not work as arguments. Issue 8473.
529
+ {"bug13", "{{print (.Copy).I}}", "17", tVal, true},
530
+}
531
+
532
+func zeroArgs() string {
533
+ return "zeroArgs"
534
+}
535
+
536
+func oneArg(a string) string {
537
+ return "oneArg=" + a
538
+}
539
+
540
+func dddArg(a int, b ...string) string {
541
+ return fmt.Sprintln(a, b)
542
+}
543
+
544
+// count returns a channel that will deliver n sequential 1-letter strings starting at "a"
545
+func count(n int) chan string {
546
+ if n == 0 {
547
+ return nil
548
+ }
549
+ c := make(chan string)
550
+ go func() {
551
+ for i := 0; i < n; i++ {
552
+ c <- "abcdefghijklmnop"[i : i+1]
553
+ }
554
+ close(c)
555
+ }()
556
+ return c
557
+}
558
+
559
+// vfunc takes a *V and a V
560
+func vfunc(V, *V) string {
561
+ return "vfunc"
562
+}
563
+
564
+// valueString takes a string, not a pointer.
565
+func valueString(v string) string {
566
+ return "value is ignored"
567
+}
568
+
569
+func add(args ...int) int {
570
+ sum := 0
571
+ for _, x := range args {
572
+ sum += x
573
+ }
574
+ return sum
575
+}
576
+
577
+func echo(arg interface{}) interface{} {
578
+ return arg
579
+}
580
+
581
+func makemap(arg ...string) map[string]string {
582
+ if len(arg)%2 != 0 {
583
+ panic("bad makemap")
584
+ }
585
+ m := make(map[string]string)
586
+ for i := 0; i < len(arg); i += 2 {
587
+ m[arg[i]] = arg[i+1]
588
+ }
589
+ return m
590
+}
591
+
592
+func stringer(s fmt.Stringer) string {
593
+ return s.String()
594
+}
595
+
596
+func mapOfThree() interface{} {
597
+ return map[string]int{"three": 3}
598
+}
599
+
600
+func testExecute(execTests []execTest, template *Template, t *testing.T) {
601
+ b := new(bytes.Buffer)
602
+ funcs := FuncMap{
603
+ "add": add,
604
+ "count": count,
605
+ "dddArg": dddArg,
606
+ "echo": echo,
607
+ "makemap": makemap,
608
+ "mapOfThree": mapOfThree,
609
+ "oneArg": oneArg,
610
+ "stringer": stringer,
611
+ "typeOf": typeOf,
612
+ "valueString": valueString,
613
+ "vfunc": vfunc,
614
+ "zeroArgs": zeroArgs,
615
+ }
616
+ for _, test := range execTests {
617
+ var tmpl *Template
618
+ var err error
619
+ if template == nil {
620
+ tmpl, err = New(test.name).Funcs(funcs).Parse(test.input)
621
+ } else {
622
+ tmpl, err = template.New(test.name).Funcs(funcs).Parse(test.input)
623
+ }
624
+ if err != nil {
625
+ t.Errorf("%s: parse error: %s", test.name, err)
626
+ continue
627
+ }
628
+ b.Reset()
629
+ err = tmpl.Execute(b, test.data)
630
+ switch {
631
+ case !test.ok && err == nil:
632
+ t.Errorf("%s: expected error; got none", test.name)
633
+ continue
634
+ case test.ok && err != nil:
635
+ t.Errorf("%s: unexpected execute error: %s", test.name, err)
636
+ continue
637
+ case !test.ok && err != nil:
638
+ // expected error, got one
639
+ if *debug {
640
+ fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err)
641
+ }
642
+ }
643
+ result := b.String()
644
+ if result != test.output {
645
+ t.Errorf("%s: expected\n\t%q\ngot\n\t%q", test.name, test.output, result)
646
+ }
647
+ }
648
+}
649
+
650
+func TestExecute(t *testing.T) {
651
+ testExecute(execTests, nil, t)
652
+}
653
+
654
+var delimPairs = []string{
655
+ "", "", // default
656
+ "{{", "}}", // same as default
657
+ "<<", ">>", // distinct
658
+ "|", "|", // same
659
+ "(日)", "(本)", // peculiar
660
+}
661
+
662
+func TestDelims(t *testing.T) {
663
+ const hello = "Hello, world"
664
+ var value = struct{ Str string }{hello}
665
+ for i := 0; i < len(delimPairs); i += 2 {
666
+ text := ".Str"
667
+ left := delimPairs[i+0]
668
+ trueLeft := left
669
+ right := delimPairs[i+1]
670
+ trueRight := right
671
+ if left == "" { // default case
672
+ trueLeft = "{{"
673
+ }
674
+ if right == "" { // default case
675
+ trueRight = "}}"
676
+ }
677
+ text = trueLeft + text + trueRight
678
+ // Now add a comment
679
+ text += trueLeft + "/*comment*/" + trueRight
680
+ // Now add an action containing a string.
681
+ text += trueLeft + `"` + trueLeft + `"` + trueRight
682
+ // At this point text looks like `{{.Str}}{{/*comment*/}}{{"{{"}}`.
683
+ tmpl, err := New("delims").Delims(left, right).Parse(text)
684
+ if err != nil {
685
+ t.Fatalf("delim %q text %q parse err %s", left, text, err)
686
+ }
687
+ var b = new(bytes.Buffer)
688
+ err = tmpl.Execute(b, value)
689
+ if err != nil {
690
+ t.Fatalf("delim %q exec err %s", left, err)
691
+ }
692
+ if b.String() != hello+trueLeft {
693
+ t.Errorf("expected %q got %q", hello+trueLeft, b.String())
694
+ }
695
+ }
696
+}
697
+
698
+// Check that an error from a method flows back to the top.
699
+func TestExecuteError(t *testing.T) {
700
+ b := new(bytes.Buffer)
701
+ tmpl := New("error")
702
+ _, err := tmpl.Parse("{{.MyError true}}")
703
+ if err != nil {
704
+ t.Fatalf("parse error: %s", err)
705
+ }
706
+ err = tmpl.Execute(b, tVal)
707
+ if err == nil {
708
+ t.Errorf("expected error; got none")
709
+ } else if !strings.Contains(err.Error(), myError.Error()) {
710
+ if *debug {
711
+ fmt.Printf("test execute error: %s\n", err)
712
+ }
713
+ t.Errorf("expected myError; got %s", err)
714
+ }
715
+}
716
+
717
+const execErrorText = `line 1
718
+line 2
719
+line 3
720
+{{template "one" .}}
721
+{{define "one"}}{{template "two" .}}{{end}}
722
+{{define "two"}}{{template "three" .}}{{end}}
723
+{{define "three"}}{{index "hi" $}}{{end}}`
724
+
725
+// Check that an error from a nested template contains all the relevant information.
726
+func TestExecError(t *testing.T) {
727
+ tmpl, err := New("top").Parse(execErrorText)
728
+ if err != nil {
729
+ t.Fatal("parse error:", err)
730
+ }
731
+ var b bytes.Buffer
732
+ err = tmpl.Execute(&b, 5) // 5 is out of range indexing "hi"
733
+ if err == nil {
734
+ t.Fatal("expected error")
735
+ }
736
+ const want = `template: top:7:20: executing "three" at <index "hi" $>: error calling index: index out of range: 5`
737
+ got := err.Error()
738
+ if got != want {
739
+ t.Errorf("expected\n%q\ngot\n%q", want, got)
740
+ }
741
+}
742
+
743
+func TestJSEscaping(t *testing.T) {
744
+ testCases := []struct {
745
+ in, exp string
746
+ }{
747
+ {`a`, `a`},
748
+ {`'foo`, `\'foo`},
749
+ {`Go "jump" \`, `Go \"jump\" \\`},
750
+ {`Yukihiro says "今日は世界"`, `Yukihiro says \"今日は世界\"`},
751
+ {"unprintable \uFDFF", `unprintable \uFDFF`},
752
+ {`<html>`, `\x3Chtml\x3E`},
753
+ }
754
+ for _, tc := range testCases {
755
+ s := JSEscapeString(tc.in)
756
+ if s != tc.exp {
757
+ t.Errorf("JS escaping [%s] got [%s] want [%s]", tc.in, s, tc.exp)
758
+ }
759
+ }
760
+}
761
+
762
+// A nice example: walk a binary tree.
763
+
764
+type Tree struct {
765
+ Val int
766
+ Left, Right *Tree
767
+}
768
+
769
+// Use different delimiters to test Set.Delims.
770
+const treeTemplate = `
771
+ (define "tree")
772
+ [
773
+ (.Val)
774
+ (with .Left)
775
+ (template "tree" .)
776
+ (end)
777
+ (with .Right)
778
+ (template "tree" .)
779
+ (end)
780
+ ]
781
+ (end)
782
+`
783
+
784
+func TestTree(t *testing.T) {
785
+ var tree = &Tree{
786
+ 1,
787
+ &Tree{
788
+ 2, &Tree{
789
+ 3,
790
+ &Tree{
791
+ 4, nil, nil,
792
+ },
793
+ nil,
794
+ },
795
+ &Tree{
796
+ 5,
797
+ &Tree{
798
+ 6, nil, nil,
799
+ },
800
+ nil,
801
+ },
802
+ },
803
+ &Tree{
804
+ 7,
805
+ &Tree{
806
+ 8,
807
+ &Tree{
808
+ 9, nil, nil,
809
+ },
810
+ nil,
811
+ },
812
+ &Tree{
813
+ 10,
814
+ &Tree{
815
+ 11, nil, nil,
816
+ },
817
+ nil,
818
+ },
819
+ },
820
+ }
821
+ tmpl, err := New("root").Delims("(", ")").Parse(treeTemplate)
822
+ if err != nil {
823
+ t.Fatal("parse error:", err)
824
+ }
825
+ var b bytes.Buffer
826
+ stripSpace := func(r rune) rune {
827
+ if r == '\t' || r == '\n' {
828
+ return -1
829
+ }
830
+ return r
831
+ }
832
+ const expect = "[1[2[3[4]][5[6]]][7[8[9]][10[11]]]]"
833
+ // First by looking up the template.
834
+ err = tmpl.Lookup("tree").Execute(&b, tree)
835
+ if err != nil {
836
+ t.Fatal("exec error:", err)
837
+ }
838
+ result := strings.Map(stripSpace, b.String())
839
+ if result != expect {
840
+ t.Errorf("expected %q got %q", expect, result)
841
+ }
842
+ // Then direct to execution.
843
+ b.Reset()
844
+ err = tmpl.ExecuteTemplate(&b, "tree", tree)
845
+ if err != nil {
846
+ t.Fatal("exec error:", err)
847
+ }
848
+ result = strings.Map(stripSpace, b.String())
849
+ if result != expect {
850
+ t.Errorf("expected %q got %q", expect, result)
851
+ }
852
+}
853
+
854
+func TestExecuteOnNewTemplate(t *testing.T) {
855
+ // This is issue 3872.
856
+ _ = New("Name").Templates()
857
+}
858
+
859
+const testTemplates = `{{define "one"}}one{{end}}{{define "two"}}two{{end}}`
860
+
861
+func TestMessageForExecuteEmpty(t *testing.T) {
862
+ // Test a truly empty template.
863
+ tmpl := New("empty")
864
+ var b bytes.Buffer
865
+ err := tmpl.Execute(&b, 0)
866
+ if err == nil {
867
+ t.Fatal("expected initial error")
868
+ }
869
+ got := err.Error()
870
+ want := `template: empty: "empty" is an incomplete or empty template`
871
+ if got != want {
872
+ t.Errorf("expected error %s got %s", want, got)
873
+ }
874
+ // Add a non-empty template to check that the error is helpful.
875
+ tests, err := New("").Parse(testTemplates)
876
+ if err != nil {
877
+ t.Fatal(err)
878
+ }
879
+ tmpl.AddParseTree("secondary", tests.Tree)
880
+ err = tmpl.Execute(&b, 0)
881
+ if err == nil {
882
+ t.Fatal("expected second error")
883
+ }
884
+ got = err.Error()
885
+ want = `template: empty: "empty" is an incomplete or empty template; defined templates are: "secondary"`
886
+ if got != want {
887
+ t.Errorf("expected error %s got %s", want, got)
888
+ }
889
+ // Make sure we can execute the secondary.
890
+ err = tmpl.ExecuteTemplate(&b, "secondary", 0)
891
+ if err != nil {
892
+ t.Fatal(err)
893
+ }
894
+}
895
+
896
+func TestFinalForPrintf(t *testing.T) {
897
+ tmpl, err := New("").Parse(`{{"x" | printf}}`)
898
+ if err != nil {
899
+ t.Fatal(err)
900
+ }
901
+ var b bytes.Buffer
902
+ err = tmpl.Execute(&b, 0)
903
+ if err != nil {
904
+ t.Fatal(err)
905
+ }
906
+}
907
+
908
+type cmpTest struct {
909
+ expr string
910
+ truth string
911
+ ok bool
912
+}
913
+
914
+var cmpTests = []cmpTest{
915
+ {"eq true true", "true", true},
916
+ {"eq true false", "false", true},
917
+ {"eq 1+2i 1+2i", "true", true},
918
+ {"eq 1+2i 1+3i", "false", true},
919
+ {"eq 1.5 1.5", "true", true},
920
+ {"eq 1.5 2.5", "false", true},
921
+ {"eq 1 1", "true", true},
922
+ {"eq 1 2", "false", true},
923
+ {"eq `xy` `xy`", "true", true},
924
+ {"eq `xy` `xyz`", "false", true},
925
+ {"eq .Uthree .Uthree", "true", true},
926
+ {"eq .Uthree .Ufour", "false", true},
927
+ {"eq 3 4 5 6 3", "true", true},
928
+ {"eq 3 4 5 6 7", "false", true},
929
+ {"ne true true", "false", true},
930
+ {"ne true false", "true", true},
931
+ {"ne 1+2i 1+2i", "false", true},
932
+ {"ne 1+2i 1+3i", "true", true},
933
+ {"ne 1.5 1.5", "false", true},
934
+ {"ne 1.5 2.5", "true", true},
935
+ {"ne 1 1", "false", true},
936
+ {"ne 1 2", "true", true},
937
+ {"ne `xy` `xy`", "false", true},
938
+ {"ne `xy` `xyz`", "true", true},
939
+ {"ne .Uthree .Uthree", "false", true},
940
+ {"ne .Uthree .Ufour", "true", true},
941
+ {"lt 1.5 1.5", "false", true},
942
+ {"lt 1.5 2.5", "true", true},
943
+ {"lt 1 1", "false", true},
944
+ {"lt 1 2", "true", true},
945
+ {"lt `xy` `xy`", "false", true},
946
+ {"lt `xy` `xyz`", "true", true},
947
+ {"lt .Uthree .Uthree", "false", true},
948
+ {"lt .Uthree .Ufour", "true", true},
949
+ {"le 1.5 1.5", "true", true},
950
+ {"le 1.5 2.5", "true", true},
951
+ {"le 2.5 1.5", "false", true},
952
+ {"le 1 1", "true", true},
953
+ {"le 1 2", "true", true},
954
+ {"le 2 1", "false", true},
955
+ {"le `xy` `xy`", "true", true},
956
+ {"le `xy` `xyz`", "true", true},
957
+ {"le `xyz` `xy`", "false", true},
958
+ {"le .Uthree .Uthree", "true", true},
959
+ {"le .Uthree .Ufour", "true", true},
960
+ {"le .Ufour .Uthree", "false", true},
961
+ {"gt 1.5 1.5", "false", true},
962
+ {"gt 1.5 2.5", "false", true},
963
+ {"gt 1 1", "false", true},
964
+ {"gt 2 1", "true", true},
965
+ {"gt 1 2", "false", true},
966
+ {"gt `xy` `xy`", "false", true},
967
+ {"gt `xy` `xyz`", "false", true},
968
+ {"gt .Uthree .Uthree", "false", true},
969
+ {"gt .Uthree .Ufour", "false", true},
970
+ {"gt .Ufour .Uthree", "true", true},
971
+ {"ge 1.5 1.5", "true", true},
972
+ {"ge 1.5 2.5", "false", true},
973
+ {"ge 2.5 1.5", "true", true},
974
+ {"ge 1 1", "true", true},
975
+ {"ge 1 2", "false", true},
976
+ {"ge 2 1", "true", true},
977
+ {"ge `xy` `xy`", "true", true},
978
+ {"ge `xy` `xyz`", "false", true},
979
+ {"ge `xyz` `xy`", "true", true},
980
+ {"ge .Uthree .Uthree", "true", true},
981
+ {"ge .Uthree .Ufour", "false", true},
982
+ {"ge .Ufour .Uthree", "true", true},
983
+ // Mixing signed and unsigned integers.
984
+ {"eq .Uthree .Three", "true", true},
985
+ {"eq .Three .Uthree", "true", true},
986
+ {"le .Uthree .Three", "true", true},
987
+ {"le .Three .Uthree", "true", true},
988
+ {"ge .Uthree .Three", "true", true},
989
+ {"ge .Three .Uthree", "true", true},
990
+ {"lt .Uthree .Three", "false", true},
991
+ {"lt .Three .Uthree", "false", true},
992
+ {"gt .Uthree .Three", "false", true},
993
+ {"gt .Three .Uthree", "false", true},
994
+ {"eq .Ufour .Three", "false", true},
995
+ {"lt .Ufour .Three", "false", true},
996
+ {"gt .Ufour .Three", "true", true},
997
+ {"eq .NegOne .Uthree", "false", true},
998
+ {"eq .Uthree .NegOne", "false", true},
999
+ {"ne .NegOne .Uthree", "true", true},
1000
+ {"ne .Uthree .NegOne", "true", true},
1001
+ {"lt .NegOne .Uthree", "true", true},
1002
+ {"lt .Uthree .NegOne", "false", true},
1003
+ {"le .NegOne .Uthree", "true", true},
1004
+ {"le .Uthree .NegOne", "false", true},
1005
+ {"gt .NegOne .Uthree", "false", true},
1006
+ {"gt .Uthree .NegOne", "true", true},
1007
+ {"ge .NegOne .Uthree", "false", true},
1008
+ {"ge .Uthree .NegOne", "true", true},
1009
+ {"eq (index `x` 0) 'x'", "true", true}, // The example that triggered this rule.
1010
+ {"eq (index `x` 0) 'y'", "false", true},
1011
+ // Errors
1012
+ {"eq `xy` 1", "", false}, // Different types.
1013
+ {"eq 2 2.0", "", false}, // Different types.
1014
+ {"lt true true", "", false}, // Unordered types.
1015
+ {"lt 1+0i 1+0i", "", false}, // Unordered types.
1016
+}
1017
+
1018
+func TestComparison(t *testing.T) {
1019
+ b := new(bytes.Buffer)
1020
+ var cmpStruct = struct {
1021
+ Uthree, Ufour uint
1022
+ NegOne, Three int
1023
+ }{3, 4, -1, 3}
1024
+ for _, test := range cmpTests {
1025
+ text := fmt.Sprintf("{{if %s}}true{{else}}false{{end}}", test.expr)
1026
+ tmpl, err := New("empty").Parse(text)
1027
+ if err != nil {
1028
+ t.Fatalf("%q: %s", test.expr, err)
1029
+ }
1030
+ b.Reset()
1031
+ err = tmpl.Execute(b, &cmpStruct)
1032
+ if test.ok && err != nil {
1033
+ t.Errorf("%s errored incorrectly: %s", test.expr, err)
1034
+ continue
1035
+ }
1036
+ if !test.ok && err == nil {
1037
+ t.Errorf("%s did not error", test.expr)
1038
+ continue
1039
+ }
1040
+ if b.String() != test.truth {
1041
+ t.Errorf("%s: want %s; got %s", test.expr, test.truth, b.String())
1042
+ }
1043
+ }
1044
+}
Godeps/_workspace/src/github.com/alecthomas/template/funcs.go
new
+598
@@ -0,0 +1,598 @@
1
+// Copyright 2011 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 template
6
+
7
+import (
8
+ "bytes"
9
+ "errors"
10
+ "fmt"
11
+ "io"
12
+ "net/url"
13
+ "reflect"
14
+ "strings"
15
+ "unicode"
16
+ "unicode/utf8"
17
+)
18
+
19
+// FuncMap is the type of the map defining the mapping from names to functions.
20
+// Each function must have either a single return value, or two return values of
21
+// which the second has type error. In that case, if the second (error)
22
+// return value evaluates to non-nil during execution, execution terminates and
23
+// Execute returns that error.
24
+type FuncMap map[string]interface{}
25
+
26
+var builtins = FuncMap{
27
+ "and": and,
28
+ "call": call,
29
+ "html": HTMLEscaper,
30
+ "index": index,
31
+ "js": JSEscaper,
32
+ "len": length,
33
+ "not": not,
34
+ "or": or,
35
+ "print": fmt.Sprint,
36
+ "printf": fmt.Sprintf,
37
+ "println": fmt.Sprintln,
38
+ "urlquery": URLQueryEscaper,
39
+
40
+ // Comparisons
41
+ "eq": eq, // ==
42
+ "ge": ge, // >=
43
+ "gt": gt, // >
44
+ "le": le, // <=
45
+ "lt": lt, // <
46
+ "ne": ne, // !=
47
+}
48
+
49
+var builtinFuncs = createValueFuncs(builtins)
50
+
51
+// createValueFuncs turns a FuncMap into a map[string]reflect.Value
52
+func createValueFuncs(funcMap FuncMap) map[string]reflect.Value {
53
+ m := make(map[string]reflect.Value)
54
+ addValueFuncs(m, funcMap)
55
+ return m
56
+}
57
+
58
+// addValueFuncs adds to values the functions in funcs, converting them to reflect.Values.
59
+func addValueFuncs(out map[string]reflect.Value, in FuncMap) {
60
+ for name, fn := range in {
61
+ v := reflect.ValueOf(fn)
62
+ if v.Kind() != reflect.Func {
63
+ panic("value for " + name + " not a function")
64
+ }
65
+ if !goodFunc(v.Type()) {
66
+ panic(fmt.Errorf("can't install method/function %q with %d results", name, v.Type().NumOut()))
67
+ }
68
+ out[name] = v
69
+ }
70
+}
71
+
72
+// addFuncs adds to values the functions in funcs. It does no checking of the input -
73
+// call addValueFuncs first.
74
+func addFuncs(out, in FuncMap) {
75
+ for name, fn := range in {
76
+ out[name] = fn
77
+ }
78
+}
79
+
80
+// goodFunc checks that the function or method has the right result signature.
81
+func goodFunc(typ reflect.Type) bool {
82
+ // We allow functions with 1 result or 2 results where the second is an error.
83
+ switch {
84
+ case typ.NumOut() == 1:
85
+ return true
86
+ case typ.NumOut() == 2 && typ.Out(1) == errorType:
87
+ return true
88
+ }
89
+ return false
90
+}
91
+
92
+// findFunction looks for a function in the template, and global map.
93
+func findFunction(name string, tmpl *Template) (reflect.Value, bool) {
94
+ if tmpl != nil && tmpl.common != nil {
95
+ if fn := tmpl.execFuncs[name]; fn.IsValid() {
96
+ return fn, true
97
+ }
98
+ }
99
+ if fn := builtinFuncs[name]; fn.IsValid() {
100
+ return fn, true
101
+ }
102
+ return reflect.Value{}, false
103
+}
104
+
105
+// Indexing.
106
+
107
+// index returns the result of indexing its first argument by the following
108
+// arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each
109
+// indexed item must be a map, slice, or array.
110
+func index(item interface{}, indices ...interface{}) (interface{}, error) {
111
+ v := reflect.ValueOf(item)
112
+ for _, i := range indices {
113
+ index := reflect.ValueOf(i)
114
+ var isNil bool
115
+ if v, isNil = indirect(v); isNil {
116
+ return nil, fmt.Errorf("index of nil pointer")
117
+ }
118
+ switch v.Kind() {
119
+ case reflect.Array, reflect.Slice, reflect.String:
120
+ var x int64
121
+ switch index.Kind() {
122
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
123
+ x = index.Int()
124
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
125
+ x = int64(index.Uint())
126
+ default:
127
+ return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type())
128
+ }
129
+ if x < 0 || x >= int64(v.Len()) {
130
+ return nil, fmt.Errorf("index out of range: %d", x)
131
+ }
132
+ v = v.Index(int(x))
133
+ case reflect.Map:
134
+ if !index.IsValid() {
135
+ index = reflect.Zero(v.Type().Key())
136
+ }
137
+ if !index.Type().AssignableTo(v.Type().Key()) {
138
+ return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type())
139
+ }
140
+ if x := v.MapIndex(index); x.IsValid() {
141
+ v = x
142
+ } else {
143
+ v = reflect.Zero(v.Type().Elem())
144
+ }
145
+ default:
146
+ return nil, fmt.Errorf("can't index item of type %s", v.Type())
147
+ }
148
+ }
149
+ return v.Interface(), nil
150
+}
151
+
152
+// Length
153
+
154
+// length returns the length of the item, with an error if it has no defined length.
155
+func length(item interface{}) (int, error) {
156
+ v, isNil := indirect(reflect.ValueOf(item))
157
+ if isNil {
158
+ return 0, fmt.Errorf("len of nil pointer")
159
+ }
160
+ switch v.Kind() {
161
+ case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
162
+ return v.Len(), nil
163
+ }
164
+ return 0, fmt.Errorf("len of type %s", v.Type())
165
+}
166
+
167
+// Function invocation
168
+
169
+// call returns the result of evaluating the first argument as a function.
170
+// The function must return 1 result, or 2 results, the second of which is an error.
171
+func call(fn interface{}, args ...interface{}) (interface{}, error) {
172
+ v := reflect.ValueOf(fn)
173
+ typ := v.Type()
174
+ if typ.Kind() != reflect.Func {
175
+ return nil, fmt.Errorf("non-function of type %s", typ)
176
+ }
177
+ if !goodFunc(typ) {
178
+ return nil, fmt.Errorf("function called with %d args; should be 1 or 2", typ.NumOut())
179
+ }
180
+ numIn := typ.NumIn()
181
+ var dddType reflect.Type
182
+ if typ.IsVariadic() {
183
+ if len(args) < numIn-1 {
184
+ return nil, fmt.Errorf("wrong number of args: got %d want at least %d", len(args), numIn-1)
185
+ }
186
+ dddType = typ.In(numIn - 1).Elem()
187
+ } else {
188
+ if len(args) != numIn {
189
+ return nil, fmt.Errorf("wrong number of args: got %d want %d", len(args), numIn)
190
+ }
191
+ }
192
+ argv := make([]reflect.Value, len(args))
193
+ for i, arg := range args {
194
+ value := reflect.ValueOf(arg)
195
+ // Compute the expected type. Clumsy because of variadics.
196
+ var argType reflect.Type
197
+ if !typ.IsVariadic() || i < numIn-1 {
198
+ argType = typ.In(i)
199
+ } else {
200
+ argType = dddType
201
+ }
202
+ if !value.IsValid() && canBeNil(argType) {
203
+ value = reflect.Zero(argType)
204
+ }
205
+ if !value.Type().AssignableTo(argType) {
206
+ return nil, fmt.Errorf("arg %d has type %s; should be %s", i, value.Type(), argType)
207
+ }
208
+ argv[i] = value
209
+ }
210
+ result := v.Call(argv)
211
+ if len(result) == 2 && !result[1].IsNil() {
212
+ return result[0].Interface(), result[1].Interface().(error)
213
+ }
214
+ return result[0].Interface(), nil
215
+}
216
+
217
+// Boolean logic.
218
+
219
+func truth(a interface{}) bool {
220
+ t, _ := isTrue(reflect.ValueOf(a))
221
+ return t
222
+}
223
+
224
+// and computes the Boolean AND of its arguments, returning
225
+// the first false argument it encounters, or the last argument.
226
+func and(arg0 interface{}, args ...interface{}) interface{} {
227
+ if !truth(arg0) {
228
+ return arg0
229
+ }
230
+ for i := range args {
231
+ arg0 = args[i]
232
+ if !truth(arg0) {
233
+ break
234
+ }
235
+ }
236
+ return arg0
237
+}
238
+
239
+// or computes the Boolean OR of its arguments, returning
240
+// the first true argument it encounters, or the last argument.
241
+func or(arg0 interface{}, args ...interface{}) interface{} {
242
+ if truth(arg0) {
243
+ return arg0
244
+ }
245
+ for i := range args {
246
+ arg0 = args[i]
247
+ if truth(arg0) {
248
+ break
249
+ }
250
+ }
251
+ return arg0
252
+}
253
+
254
+// not returns the Boolean negation of its argument.
255
+func not(arg interface{}) (truth bool) {
256
+ truth, _ = isTrue(reflect.ValueOf(arg))
257
+ return !truth
258
+}
259
+
260
+// Comparison.
261
+
262
+// TODO: Perhaps allow comparison between signed and unsigned integers.
263
+
264
+var (
265
+ errBadComparisonType = errors.New("invalid type for comparison")
266
+ errBadComparison = errors.New("incompatible types for comparison")
267
+ errNoComparison = errors.New("missing argument for comparison")
268
+)
269
+
270
+type kind int
271
+
272
+const (
273
+ invalidKind kind = iota
274
+ boolKind
275
+ complexKind
276
+ intKind
277
+ floatKind
278
+ integerKind
279
+ stringKind
280
+ uintKind
281
+)
282
+
283
+func basicKind(v reflect.Value) (kind, error) {
284
+ switch v.Kind() {
285
+ case reflect.Bool:
286
+ return boolKind, nil
287
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
288
+ return intKind, nil
289
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
290
+ return uintKind, nil
291
+ case reflect.Float32, reflect.Float64:
292
+ return floatKind, nil
293
+ case reflect.Complex64, reflect.Complex128:
294
+ return complexKind, nil
295
+ case reflect.String:
296
+ return stringKind, nil
297
+ }
298
+ return invalidKind, errBadComparisonType
299
+}
300
+
301
+// eq evaluates the comparison a == b || a == c || ...
302
+func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) {
303
+ v1 := reflect.ValueOf(arg1)
304
+ k1, err := basicKind(v1)
305
+ if err != nil {
306
+ return false, err
307
+ }
308
+ if len(arg2) == 0 {
309
+ return false, errNoComparison
310
+ }
311
+ for _, arg := range arg2 {
312
+ v2 := reflect.ValueOf(arg)
313
+ k2, err := basicKind(v2)
314
+ if err != nil {
315
+ return false, err
316
+ }
317
+ truth := false
318
+ if k1 != k2 {
319
+ // Special case: Can compare integer values regardless of type's sign.
320
+ switch {
321
+ case k1 == intKind && k2 == uintKind:
322
+ truth = v1.Int() >= 0 && uint64(v1.Int()) == v2.Uint()
323
+ case k1 == uintKind && k2 == intKind:
324
+ truth = v2.Int() >= 0 && v1.Uint() == uint64(v2.Int())
325
+ default:
326
+ return false, errBadComparison
327
+ }
328
+ } else {
329
+ switch k1 {
330
+ case boolKind:
331
+ truth = v1.Bool() == v2.Bool()
332
+ case complexKind:
333
+ truth = v1.Complex() == v2.Complex()
334
+ case floatKind:
335
+ truth = v1.Float() == v2.Float()
336
+ case intKind:
337
+ truth = v1.Int() == v2.Int()
338
+ case stringKind:
339
+ truth = v1.String() == v2.String()
340
+ case uintKind:
341
+ truth = v1.Uint() == v2.Uint()
342
+ default:
343
+ panic("invalid kind")
344
+ }
345
+ }
346
+ if truth {
347
+ return true, nil
348
+ }
349
+ }
350
+ return false, nil
351
+}
352
+
353
+// ne evaluates the comparison a != b.
354
+func ne(arg1, arg2 interface{}) (bool, error) {
355
+ // != is the inverse of ==.
356
+ equal, err := eq(arg1, arg2)
357
+ return !equal, err
358
+}
359
+
360
+// lt evaluates the comparison a < b.
361
+func lt(arg1, arg2 interface{}) (bool, error) {
362
+ v1 := reflect.ValueOf(arg1)
363
+ k1, err := basicKind(v1)
364
+ if err != nil {
365
+ return false, err
366
+ }
367
+ v2 := reflect.ValueOf(arg2)
368
+ k2, err := basicKind(v2)
369
+ if err != nil {
370
+ return false, err
371
+ }
372
+ truth := false
373
+ if k1 != k2 {
374
+ // Special case: Can compare integer values regardless of type's sign.
375
+ switch {
376
+ case k1 == intKind && k2 == uintKind:
377
+ truth = v1.Int() < 0 || uint64(v1.Int()) < v2.Uint()
378
+ case k1 == uintKind && k2 == intKind:
379
+ truth = v2.Int() >= 0 && v1.Uint() < uint64(v2.Int())
380
+ default:
381
+ return false, errBadComparison
382
+ }
383
+ } else {
384
+ switch k1 {
385
+ case boolKind, complexKind:
386
+ return false, errBadComparisonType
387
+ case floatKind:
388
+ truth = v1.Float() < v2.Float()
389
+ case intKind:
390
+ truth = v1.Int() < v2.Int()
391
+ case stringKind:
392
+ truth = v1.String() < v2.String()
393
+ case uintKind:
394
+ truth = v1.Uint() < v2.Uint()
395
+ default:
396
+ panic("invalid kind")
397
+ }
398
+ }
399
+ return truth, nil
400
+}
401
+
402
+// le evaluates the comparison <= b.
403
+func le(arg1, arg2 interface{}) (bool, error) {
404
+ // <= is < or ==.
405
+ lessThan, err := lt(arg1, arg2)
406
+ if lessThan || err != nil {
407
+ return lessThan, err
408
+ }
409
+ return eq(arg1, arg2)
410
+}
411
+
412
+// gt evaluates the comparison a > b.
413
+func gt(arg1, arg2 interface{}) (bool, error) {
414
+ // > is the inverse of <=.
415
+ lessOrEqual, err := le(arg1, arg2)
416
+ if err != nil {
417
+ return false, err
418
+ }
419
+ return !lessOrEqual, nil
420
+}
421
+
422
+// ge evaluates the comparison a >= b.
423
+func ge(arg1, arg2 interface{}) (bool, error) {
424
+ // >= is the inverse of <.
425
+ lessThan, err := lt(arg1, arg2)
426
+ if err != nil {
427
+ return false, err
428
+ }
429
+ return !lessThan, nil
430
+}
431
+
432
+// HTML escaping.
433
+
434
+var (
435
+ htmlQuot = []byte(""") // shorter than """
436
+ htmlApos = []byte("'") // shorter than "'" and apos was not in HTML until HTML5
437
+ htmlAmp = []byte("&")
438
+ htmlLt = []byte("<")
439
+ htmlGt = []byte(">")
440
+)
441
+
442
+// HTMLEscape writes to w the escaped HTML equivalent of the plain text data b.
443
+func HTMLEscape(w io.Writer, b []byte) {
444
+ last := 0
445
+ for i, c := range b {
446
+ var html []byte
447
+ switch c {
448
+ case '"':
449
+ html = htmlQuot
450
+ case '\'':
451
+ html = htmlApos
452
+ case '&':
453
+ html = htmlAmp
454
+ case '<':
455
+ html = htmlLt
456
+ case '>':
457
+ html = htmlGt
458
+ default:
459
+ continue
460
+ }
461
+ w.Write(b[last:i])
462
+ w.Write(html)
463
+ last = i + 1
464
+ }
465
+ w.Write(b[last:])
466
+}
467
+
468
+// HTMLEscapeString returns the escaped HTML equivalent of the plain text data s.
469
+func HTMLEscapeString(s string) string {
470
+ // Avoid allocation if we can.
471
+ if strings.IndexAny(s, `'"&<>`) < 0 {
472
+ return s
473
+ }
474
+ var b bytes.Buffer
475
+ HTMLEscape(&b, []byte(s))
476
+ return b.String()
477
+}
478
+
479
+// HTMLEscaper returns the escaped HTML equivalent of the textual
480
+// representation of its arguments.
481
+func HTMLEscaper(args ...interface{}) string {
482
+ return HTMLEscapeString(evalArgs(args))
483
+}
484
+
485
+// JavaScript escaping.
486
+
487
+var (
488
+ jsLowUni = []byte(`\u00`)
489
+ hex = []byte("0123456789ABCDEF")
490
+
491
+ jsBackslash = []byte(`\\`)
492
+ jsApos = []byte(`\'`)
493
+ jsQuot = []byte(`\"`)
494
+ jsLt = []byte(`\x3C`)
495
+ jsGt = []byte(`\x3E`)
496
+)
497
+
498
+// JSEscape writes to w the escaped JavaScript equivalent of the plain text data b.
499
+func JSEscape(w io.Writer, b []byte) {
500
+ last := 0
501
+ for i := 0; i < len(b); i++ {
502
+ c := b[i]
503
+
504
+ if !jsIsSpecial(rune(c)) {
505
+ // fast path: nothing to do
506
+ continue
507
+ }
508
+ w.Write(b[last:i])
509
+
510
+ if c < utf8.RuneSelf {
511
+ // Quotes, slashes and angle brackets get quoted.
512
+ // Control characters get written as \u00XX.
513
+ switch c {
514
+ case '\\':
515
+ w.Write(jsBackslash)
516
+ case '\'':
517
+ w.Write(jsApos)
518
+ case '"':
519
+ w.Write(jsQuot)
520
+ case '<':
521
+ w.Write(jsLt)
522
+ case '>':
523
+ w.Write(jsGt)
524
+ default:
525
+ w.Write(jsLowUni)
526
+ t, b := c>>4, c&0x0f
527
+ w.Write(hex[t : t+1])
528
+ w.Write(hex[b : b+1])
529
+ }
530
+ } else {
531
+ // Unicode rune.
532
+ r, size := utf8.DecodeRune(b[i:])
533
+ if unicode.IsPrint(r) {
534
+ w.Write(b[i : i+size])
535
+ } else {
536
+ fmt.Fprintf(w, "\\u%04X", r)
537
+ }
538
+ i += size - 1
539
+ }
540
+ last = i + 1
541
+ }
542
+ w.Write(b[last:])
543
+}
544
+
545
+// JSEscapeString returns the escaped JavaScript equivalent of the plain text data s.
546
+func JSEscapeString(s string) string {
547
+ // Avoid allocation if we can.
548
+ if strings.IndexFunc(s, jsIsSpecial) < 0 {
549
+ return s
550
+ }
551
+ var b bytes.Buffer
552
+ JSEscape(&b, []byte(s))
553
+ return b.String()
554
+}
555
+
556
+func jsIsSpecial(r rune) bool {
557
+ switch r {
558
+ case '\\', '\'', '"', '<', '>':
559
+ return true
560
+ }
561
+ return r < ' ' || utf8.RuneSelf <= r
562
+}
563
+
564
+// JSEscaper returns the escaped JavaScript equivalent of the textual
565
+// representation of its arguments.
566
+func JSEscaper(args ...interface{}) string {
567
+ return JSEscapeString(evalArgs(args))
568
+}
569
+
570
+// URLQueryEscaper returns the escaped value of the textual representation of
571
+// its arguments in a form suitable for embedding in a URL query.
572
+func URLQueryEscaper(args ...interface{}) string {
573
+ return url.QueryEscape(evalArgs(args))
574
+}
575
+
576
+// evalArgs formats the list of arguments into a string. It is therefore equivalent to
577
+// fmt.Sprint(args...)
578
+// except that each argument is indirected (if a pointer), as required,
579
+// using the same rules as the default string evaluation during template
580
+// execution.
581
+func evalArgs(args []interface{}) string {
582
+ ok := false
583
+ var s string
584
+ // Fast path for simple common case.
585
+ if len(args) == 1 {
586
+ s, ok = args[0].(string)
587
+ }
588
+ if !ok {
589
+ for i, arg := range args {
590
+ a, ok := printableValue(reflect.ValueOf(arg))
591
+ if ok {
592
+ args[i] = a
593
+ } // else left fmt do its thing
594
+ }
595
+ s = fmt.Sprint(args...)
596
+ }
597
+ return s
598
+}
Godeps/_workspace/src/github.com/alecthomas/template/helper.go
new
+108
@@ -0,0 +1,108 @@
1
+// Copyright 2011 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
+// Helper functions to make constructing templates easier.
6
+
7
+package template
8
+
9
+import (
10
+ "fmt"
11
+ "io/ioutil"
12
+ "path/filepath"
13
+)
14
+
15
+// Functions and methods to parse templates.
16
+
17
+// Must is a helper that wraps a call to a function returning (*Template, error)
18
+// and panics if the error is non-nil. It is intended for use in variable
19
+// initializations such as
20
+// var t = template.Must(template.New("name").Parse("text"))
21
+func Must(t *Template, err error) *Template {
22
+ if err != nil {
23
+ panic(err)
24
+ }
25
+ return t
26
+}
27
+
28
+// ParseFiles creates a new Template and parses the template definitions from
29
+// the named files. The returned template's name will have the (base) name and
30
+// (parsed) contents of the first file. There must be at least one file.
31
+// If an error occurs, parsing stops and the returned *Template is nil.
32
+func ParseFiles(filenames ...string) (*Template, error) {
33
+ return parseFiles(nil, filenames...)
34
+}
35
+
36
+// ParseFiles parses the named files and associates the resulting templates with
37
+// t. If an error occurs, parsing stops and the returned template is nil;
38
+// otherwise it is t. There must be at least one file.
39
+func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
40
+ return parseFiles(t, filenames...)
41
+}
42
+
43
+// parseFiles is the helper for the method and function. If the argument
44
+// template is nil, it is created from the first file.
45
+func parseFiles(t *Template, filenames ...string) (*Template, error) {
46
+ if len(filenames) == 0 {
47
+ // Not really a problem, but be consistent.
48
+ return nil, fmt.Errorf("template: no files named in call to ParseFiles")
49
+ }
50
+ for _, filename := range filenames {
51
+ b, err := ioutil.ReadFile(filename)
52
+ if err != nil {
53
+ return nil, err
54
+ }
55
+ s := string(b)
56
+ name := filepath.Base(filename)
57
+ // First template becomes return value if not already defined,
58
+ // and we use that one for subsequent New calls to associate
59
+ // all the templates together. Also, if this file has the same name
60
+ // as t, this file becomes the contents of t, so
61
+ // t, err := New(name).Funcs(xxx).ParseFiles(name)
62
+ // works. Otherwise we create a new template associated with t.
63
+ var tmpl *Template
64
+ if t == nil {
65
+ t = New(name)
66
+ }
67
+ if name == t.Name() {
68
+ tmpl = t
69
+ } else {
70
+ tmpl = t.New(name)
71
+ }
72
+ _, err = tmpl.Parse(s)
73
+ if err != nil {
74
+ return nil, err
75
+ }
76
+ }
77
+ return t, nil
78
+}
79
+
80
+// ParseGlob creates a new Template and parses the template definitions from the
81
+// files identified by the pattern, which must match at least one file. The
82
+// returned template will have the (base) name and (parsed) contents of the
83
+// first file matched by the pattern. ParseGlob is equivalent to calling
84
+// ParseFiles with the list of files matched by the pattern.
85
+func ParseGlob(pattern string) (*Template, error) {
86
+ return parseGlob(nil, pattern)
87
+}
88
+
89
+// ParseGlob parses the template definitions in the files identified by the
90
+// pattern and associates the resulting templates with t. The pattern is
91
+// processed by filepath.Glob and must match at least one file. ParseGlob is
92
+// equivalent to calling t.ParseFiles with the list of files matched by the
93
+// pattern.
94
+func (t *Template) ParseGlob(pattern string) (*Template, error) {
95
+ return parseGlob(t, pattern)
96
+}
97
+
98
+// parseGlob is the implementation of the function and method ParseGlob.
99
+func parseGlob(t *Template, pattern string) (*Template, error) {
100
+ filenames, err := filepath.Glob(pattern)
101
+ if err != nil {
102
+ return nil, err
103
+ }
104
+ if len(filenames) == 0 {
105
+ return nil, fmt.Errorf("template: pattern matches no files: %#q", pattern)
106
+ }
107
+ return parseFiles(t, filenames...)
108
+}
Godeps/_workspace/src/github.com/alecthomas/template/multi_test.go
new
+292
@@ -0,0 +1,292 @@
1
+// Copyright 2011 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 template
6
+
7
+// Tests for mulitple-template parsing and execution.
8
+
9
+import (
10
+ "bytes"
11
+ "fmt"
12
+ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/alecthomas/template/parse"
13
+ "strings"
14
+ "testing"
15
+)
16
+
17
+const (
18
+ noError = true
19
+ hasError = false
20
+)
21
+
22
+type multiParseTest struct {
23
+ name string
24
+ input string
25
+ ok bool
26
+ names []string
27
+ results []string
28
+}
29
+
30
+var multiParseTests = []multiParseTest{
31
+ {"empty", "", noError,
32
+ nil,
33
+ nil},
34
+ {"one", `{{define "foo"}} FOO {{end}}`, noError,
35
+ []string{"foo"},
36
+ []string{" FOO "}},
37
+ {"two", `{{define "foo"}} FOO {{end}}{{define "bar"}} BAR {{end}}`, noError,
38
+ []string{"foo", "bar"},
39
+ []string{" FOO ", " BAR "}},
40
+ // errors
41
+ {"missing end", `{{define "foo"}} FOO `, hasError,
42
+ nil,
43
+ nil},
44
+ {"malformed name", `{{define "foo}} FOO `, hasError,
45
+ nil,
46
+ nil},
47
+}
48
+
49
+func TestMultiParse(t *testing.T) {
50
+ for _, test := range multiParseTests {
51
+ template, err := New("root").Parse(test.input)
52
+ switch {
53
+ case err == nil && !test.ok:
54
+ t.Errorf("%q: expected error; got none", test.name)
55
+ continue
56
+ case err != nil && test.ok:
57
+ t.Errorf("%q: unexpected error: %v", test.name, err)
58
+ continue
59
+ case err != nil && !test.ok:
60
+ // expected error, got one
61
+ if *debug {
62
+ fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err)
63
+ }
64
+ continue
65
+ }
66
+ if template == nil {
67
+ continue
68
+ }
69
+ if len(template.tmpl) != len(test.names)+1 { // +1 for root
70
+ t.Errorf("%s: wrong number of templates; wanted %d got %d", test.name, len(test.names), len(template.tmpl))
71
+ continue
72
+ }
73
+ for i, name := range test.names {
74
+ tmpl, ok := template.tmpl[name]
75
+ if !ok {
76
+ t.Errorf("%s: can't find template %q", test.name, name)
77
+ continue
78
+ }
79
+ result := tmpl.Root.String()
80
+ if result != test.results[i] {
81
+ t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.results[i])
82
+ }
83
+ }
84
+ }
85
+}
86
+
87
+var multiExecTests = []execTest{
88
+ {"empty", "", "", nil, true},
89
+ {"text", "some text", "some text", nil, true},
90
+ {"invoke x", `{{template "x" .SI}}`, "TEXT", tVal, true},
91
+ {"invoke x no args", `{{template "x"}}`, "TEXT", tVal, true},
92
+ {"invoke dot int", `{{template "dot" .I}}`, "17", tVal, true},
93
+ {"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true},
94
+ {"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true},
95
+ {"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true},
96
+ {"variable declared by template", `{{template "nested" $x:=.SI}},{{index $x 1}}`, "[3 4 5],4", tVal, true},
97
+
98
+ // User-defined function: test argument evaluator.
99
+ {"testFunc literal", `{{oneArg "joe"}}`, "oneArg=joe", tVal, true},
100
+ {"testFunc .", `{{oneArg .}}`, "oneArg=joe", "joe", true},
101
+}
102
+
103
+// These strings are also in testdata/*.
104
+const multiText1 = `
105
+ {{define "x"}}TEXT{{end}}
106
+ {{define "dotV"}}{{.V}}{{end}}
107
+`
108
+
109
+const multiText2 = `
110
+ {{define "dot"}}{{.}}{{end}}
111
+ {{define "nested"}}{{template "dot" .}}{{end}}
112
+`
113
+
114
+func TestMultiExecute(t *testing.T) {
115
+ // Declare a couple of templates first.
116
+ template, err := New("root").Parse(multiText1)
117
+ if err != nil {
118
+ t.Fatalf("parse error for 1: %s", err)
119
+ }
120
+ _, err = template.Parse(multiText2)
121
+ if err != nil {
122
+ t.Fatalf("parse error for 2: %s", err)
123
+ }
124
+ testExecute(multiExecTests, template, t)
125
+}
126
+
127
+func TestParseFiles(t *testing.T) {
128
+ _, err := ParseFiles("DOES NOT EXIST")
129
+ if err == nil {
130
+ t.Error("expected error for non-existent file; got none")
131
+ }
132
+ template := New("root")
133
+ _, err = template.ParseFiles("testdata/file1.tmpl", "testdata/file2.tmpl")
134
+ if err != nil {
135
+ t.Fatalf("error parsing files: %v", err)
136
+ }
137
+ testExecute(multiExecTests, template, t)
138
+}
139
+
140
+func TestParseGlob(t *testing.T) {
141
+ _, err := ParseGlob("DOES NOT EXIST")
142
+ if err == nil {
143
+ t.Error("expected error for non-existent file; got none")
144
+ }
145
+ _, err = New("error").ParseGlob("[x")
146
+ if err == nil {
147
+ t.Error("expected error for bad pattern; got none")
148
+ }
149
+ template := New("root")
150
+ _, err = template.ParseGlob("testdata/file*.tmpl")
151
+ if err != nil {
152
+ t.Fatalf("error parsing files: %v", err)
153
+ }
154
+ testExecute(multiExecTests, template, t)
155
+}
156
+
157
+// In these tests, actual content (not just template definitions) comes from the parsed files.
158
+
159
+var templateFileExecTests = []execTest{
160
+ {"test", `{{template "tmpl1.tmpl"}}{{template "tmpl2.tmpl"}}`, "template1\n\ny\ntemplate2\n\nx\n", 0, true},
161
+}
162
+
163
+func TestParseFilesWithData(t *testing.T) {
164
+ template, err := New("root").ParseFiles("testdata/tmpl1.tmpl", "testdata/tmpl2.tmpl")
165
+ if err != nil {
166
+ t.Fatalf("error parsing files: %v", err)
167
+ }
168
+ testExecute(templateFileExecTests, template, t)
169
+}
170
+
171
+func TestParseGlobWithData(t *testing.T) {
172
+ template, err := New("root").ParseGlob("testdata/tmpl*.tmpl")
173
+ if err != nil {
174
+ t.Fatalf("error parsing files: %v", err)
175
+ }
176
+ testExecute(templateFileExecTests, template, t)
177
+}
178
+
179
+const (
180
+ cloneText1 = `{{define "a"}}{{template "b"}}{{template "c"}}{{end}}`
181
+ cloneText2 = `{{define "b"}}b{{end}}`
182
+ cloneText3 = `{{define "c"}}root{{end}}`
183
+ cloneText4 = `{{define "c"}}clone{{end}}`
184
+)
185
+
186
+func TestClone(t *testing.T) {
187
+ // Create some templates and clone the root.
188
+ root, err := New("root").Parse(cloneText1)
189
+ if err != nil {
190
+ t.Fatal(err)
191
+ }
192
+ _, err = root.Parse(cloneText2)
193
+ if err != nil {
194
+ t.Fatal(err)
195
+ }
196
+ clone := Must(root.Clone())
197
+ // Add variants to both.
198
+ _, err = root.Parse(cloneText3)
199
+ if err != nil {
200
+ t.Fatal(err)
201
+ }
202
+ _, err = clone.Parse(cloneText4)
203
+ if err != nil {
204
+ t.Fatal(err)
205
+ }
206
+ // Verify that the clone is self-consistent.
207
+ for k, v := range clone.tmpl {
208
+ if k == clone.name && v.tmpl[k] != clone {
209
+ t.Error("clone does not contain root")
210
+ }
211
+ if v != v.tmpl[v.name] {
212
+ t.Errorf("clone does not contain self for %q", k)
213
+ }
214
+ }
215
+ // Execute root.
216
+ var b bytes.Buffer
217
+ err = root.ExecuteTemplate(&b, "a", 0)
218
+ if err != nil {
219
+ t.Fatal(err)
220
+ }
221
+ if b.String() != "broot" {
222
+ t.Errorf("expected %q got %q", "broot", b.String())
223
+ }
224
+ // Execute copy.
225
+ b.Reset()
226
+ err = clone.ExecuteTemplate(&b, "a", 0)
227
+ if err != nil {
228
+ t.Fatal(err)
229
+ }
230
+ if b.String() != "bclone" {
231
+ t.Errorf("expected %q got %q", "bclone", b.String())
232
+ }
233
+}
234
+
235
+func TestAddParseTree(t *testing.T) {
236
+ // Create some templates.
237
+ root, err := New("root").Parse(cloneText1)
238
+ if err != nil {
239
+ t.Fatal(err)
240
+ }
241
+ _, err = root.Parse(cloneText2)
242
+ if err != nil {
243
+ t.Fatal(err)
244
+ }
245
+ // Add a new parse tree.
246
+ tree, err := parse.Parse("cloneText3", cloneText3, "", "", nil, builtins)
247
+ if err != nil {
248
+ t.Fatal(err)
249
+ }
250
+ added, err := root.AddParseTree("c", tree["c"])
251
+ // Execute.
252
+ var b bytes.Buffer
253
+ err = added.ExecuteTemplate(&b, "a", 0)
254
+ if err != nil {
255
+ t.Fatal(err)
256
+ }
257
+ if b.String() != "broot" {
258
+ t.Errorf("expected %q got %q", "broot", b.String())
259
+ }
260
+}
261
+
262
+// Issue 7032
263
+func TestAddParseTreeToUnparsedTemplate(t *testing.T) {
264
+ master := "{{define \"master\"}}{{end}}"
265
+ tmpl := New("master")
266
+ tree, err := parse.Parse("master", master, "", "", nil)
267
+ if err != nil {
268
+ t.Fatalf("unexpected parse err: %v", err)
269
+ }
270
+ masterTree := tree["master"]
271
+ tmpl.AddParseTree("master", masterTree) // used to panic
272
+}
273
+
274
+func TestRedefinition(t *testing.T) {
275
+ var tmpl *Template
276
+ var err error
277
+ if tmpl, err = New("tmpl1").Parse(`{{define "test"}}foo{{end}}`); err != nil {
278
+ t.Fatalf("parse 1: %v", err)
279
+ }
280
+ if _, err = tmpl.Parse(`{{define "test"}}bar{{end}}`); err == nil {
281
+ t.Fatal("expected error")
282
+ }
283
+ if !strings.Contains(err.Error(), "redefinition") {
284
+ t.Fatalf("expected redefinition error; got %v", err)
285
+ }
286
+ if _, err = tmpl.New("tmpl2").Parse(`{{define "test"}}bar{{end}}`); err == nil {
287
+ t.Fatal("expected error")
288
+ }
289
+ if !strings.Contains(err.Error(), "redefinition") {
290
+ t.Fatalf("expected redefinition error; got %v", err)
291
+ }
292
+}
Godeps/_workspace/src/github.com/alecthomas/template/parse/lex.go
new
+556
@@ -0,0 +1,556 @@
1
+// Copyright 2011 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 parse
6
+
7
+import (
8
+ "fmt"
9
+ "strings"
10
+ "unicode"
11
+ "unicode/utf8"
12
+)
13
+
14
+// item represents a token or text string returned from the scanner.
15
+type item struct {
16
+ typ itemType // The type of this item.
17
+ pos Pos // The starting position, in bytes, of this item in the input string.
18
+ val string // The value of this item.
19
+}
20
+
21
+func (i item) String() string {
22
+ switch {
23
+ case i.typ == itemEOF:
24
+ return "EOF"
25
+ case i.typ == itemError:
26
+ return i.val
27
+ case i.typ > itemKeyword:
28
+ return fmt.Sprintf("<%s>", i.val)
29
+ case len(i.val) > 10:
30
+ return fmt.Sprintf("%.10q...", i.val)
31
+ }
32
+ return fmt.Sprintf("%q", i.val)
33
+}
34
+
35
+// itemType identifies the type of lex items.
36
+type itemType int
37
+
38
+const (
39
+ itemError itemType = iota // error occurred; value is text of error
40
+ itemBool // boolean constant
41
+ itemChar // printable ASCII character; grab bag for comma etc.
42
+ itemCharConstant // character constant
43
+ itemComplex // complex constant (1+2i); imaginary is just a number
44
+ itemColonEquals // colon-equals (':=') introducing a declaration
45
+ itemEOF
46
+ itemField // alphanumeric identifier starting with '.'
47
+ itemIdentifier // alphanumeric identifier not starting with '.'
48
+ itemLeftDelim // left action delimiter
49
+ itemLeftParen // '(' inside action
50
+ itemNumber // simple number, including imaginary
51
+ itemPipe // pipe symbol
52
+ itemRawString // raw quoted string (includes quotes)
53
+ itemRightDelim // right action delimiter
54
+ itemElideNewline // elide newline after right delim
55
+ itemRightParen // ')' inside action
56
+ itemSpace // run of spaces separating arguments
57
+ itemString // quoted string (includes quotes)
58
+ itemText // plain text
59
+ itemVariable // variable starting with '$', such as '$' or '$1' or '$hello'
60
+ // Keywords appear after all the rest.
61
+ itemKeyword // used only to delimit the keywords
62
+ itemDot // the cursor, spelled '.'
63
+ itemDefine // define keyword
64
+ itemElse // else keyword
65
+ itemEnd // end keyword
66
+ itemIf // if keyword
67
+ itemNil // the untyped nil constant, easiest to treat as a keyword
68
+ itemRange // range keyword
69
+ itemTemplate // template keyword
70
+ itemWith // with keyword
71
+)
72
+
73
+var key = map[string]itemType{
74
+ ".": itemDot,
75
+ "define": itemDefine,
76
+ "else": itemElse,
77
+ "end": itemEnd,
78
+ "if": itemIf,
79
+ "range": itemRange,
80
+ "nil": itemNil,
81
+ "template": itemTemplate,
82
+ "with": itemWith,
83
+}
84
+
85
+const eof = -1
86
+
87
+// stateFn represents the state of the scanner as a function that returns the next state.
88
+type stateFn func(*lexer) stateFn
89
+
90
+// lexer holds the state of the scanner.
91
+type lexer struct {
92
+ name string // the name of the input; used only for error reports
93
+ input string // the string being scanned
94
+ leftDelim string // start of action
95
+ rightDelim string // end of action
96
+ state stateFn // the next lexing function to enter
97
+ pos Pos // current position in the input
98
+ start Pos // start position of this item
99
+ width Pos // width of last rune read from input
100
+ lastPos Pos // position of most recent item returned by nextItem
101
+ items chan item // channel of scanned items
102
+ parenDepth int // nesting depth of ( ) exprs
103
+}
104
+
105
+// next returns the next rune in the input.
106
+func (l *lexer) next() rune {
107
+ if int(l.pos) >= len(l.input) {
108
+ l.width = 0
109
+ return eof
110
+ }
111
+ r, w := utf8.DecodeRuneInString(l.input[l.pos:])
112
+ l.width = Pos(w)
113
+ l.pos += l.width
114
+ return r
115
+}
116
+
117
+// peek returns but does not consume the next rune in the input.
118
+func (l *lexer) peek() rune {
119
+ r := l.next()
120
+ l.backup()
121
+ return r
122
+}
123
+
124
+// backup steps back one rune. Can only be called once per call of next.
125
+func (l *lexer) backup() {
126
+ l.pos -= l.width
127
+}
128
+
129
+// emit passes an item back to the client.
130
+func (l *lexer) emit(t itemType) {
131
+ l.items <- item{t, l.start, l.input[l.start:l.pos]}
132
+ l.start = l.pos
133
+}
134
+
135
+// ignore skips over the pending input before this point.
136
+func (l *lexer) ignore() {
137
+ l.start = l.pos
138
+}
139
+
140
+// accept consumes the next rune if it's from the valid set.
141
+func (l *lexer) accept(valid string) bool {
142
+ if strings.IndexRune(valid, l.next()) >= 0 {
143
+ return true
144
+ }
145
+ l.backup()
146
+ return false
147
+}
148
+
149
+// acceptRun consumes a run of runes from the valid set.
150
+func (l *lexer) acceptRun(valid string) {
151
+ for strings.IndexRune(valid, l.next()) >= 0 {
152
+ }
153
+ l.backup()
154
+}
155
+
156
+// lineNumber reports which line we're on, based on the position of
157
+// the previous item returned by nextItem. Doing it this way
158
+// means we don't have to worry about peek double counting.
159
+func (l *lexer) lineNumber() int {
160
+ return 1 + strings.Count(l.input[:l.lastPos], "\n")
161
+}
162
+
163
+// errorf returns an error token and terminates the scan by passing
164
+// back a nil pointer that will be the next state, terminating l.nextItem.
165
+func (l *lexer) errorf(format string, args ...interface{}) stateFn {
166
+ l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}
167
+ return nil
168
+}
169
+
170
+// nextItem returns the next item from the input.
171
+func (l *lexer) nextItem() item {
172
+ item := <-l.items
173
+ l.lastPos = item.pos
174
+ return item
175
+}
176
+
177
+// lex creates a new scanner for the input string.
178
+func lex(name, input, left, right string) *lexer {
179
+ if left == "" {
180
+ left = leftDelim
181
+ }
182
+ if right == "" {
183
+ right = rightDelim
184
+ }
185
+ l := &lexer{
186
+ name: name,
187
+ input: input,
188
+ leftDelim: left,
189
+ rightDelim: right,
190
+ items: make(chan item),
191
+ }
192
+ go l.run()
193
+ return l
194
+}
195
+
196
+// run runs the state machine for the lexer.
197
+func (l *lexer) run() {
198
+ for l.state = lexText; l.state != nil; {
199
+ l.state = l.state(l)
200
+ }
201
+}
202
+
203
+// state functions
204
+
205
+const (
206
+ leftDelim = "{{"
207
+ rightDelim = "}}"
208
+ leftComment = "/*"
209
+ rightComment = "*/"
210
+)
211
+
212
+// lexText scans until an opening action delimiter, "{{".
213
+func lexText(l *lexer) stateFn {
214
+ for {
215
+ if strings.HasPrefix(l.input[l.pos:], l.leftDelim) {
216
+ if l.pos > l.start {
217
+ l.emit(itemText)
218
+ }
219
+ return lexLeftDelim
220
+ }
221
+ if l.next() == eof {
222
+ break
223
+ }
224
+ }
225
+ // Correctly reached EOF.
226
+ if l.pos > l.start {
227
+ l.emit(itemText)
228
+ }
229
+ l.emit(itemEOF)
230
+ return nil
231
+}
232
+
233
+// lexLeftDelim scans the left delimiter, which is known to be present.
234
+func lexLeftDelim(l *lexer) stateFn {
235
+ l.pos += Pos(len(l.leftDelim))
236
+ if strings.HasPrefix(l.input[l.pos:], leftComment) {
237
+ return lexComment
238
+ }
239
+ l.emit(itemLeftDelim)
240
+ l.parenDepth = 0
241
+ return lexInsideAction
242
+}
243
+
244
+// lexComment scans a comment. The left comment marker is known to be present.
245
+func lexComment(l *lexer) stateFn {
246
+ l.pos += Pos(len(leftComment))
247
+ i := strings.Index(l.input[l.pos:], rightComment)
248
+ if i < 0 {
249
+ return l.errorf("unclosed comment")
250
+ }
251
+ l.pos += Pos(i + len(rightComment))
252
+ if !strings.HasPrefix(l.input[l.pos:], l.rightDelim) {
253
+ return l.errorf("comment ends before closing delimiter")
254
+
255
+ }
256
+ l.pos += Pos(len(l.rightDelim))
257
+ l.ignore()
258
+ return lexText
259
+}
260
+
261
+// lexRightDelim scans the right delimiter, which is known to be present.
262
+func lexRightDelim(l *lexer) stateFn {
263
+ l.pos += Pos(len(l.rightDelim))
264
+ l.emit(itemRightDelim)
265
+ if l.peek() == '\\' {
266
+ l.pos++
267
+ l.emit(itemElideNewline)
268
+ }
269
+ return lexText
270
+}
271
+
272
+// lexInsideAction scans the elements inside action delimiters.
273
+func lexInsideAction(l *lexer) stateFn {
274
+ // Either number, quoted string, or identifier.
275
+ // Spaces separate arguments; runs of spaces turn into itemSpace.
276
+ // Pipe symbols separate and are emitted.
277
+ if strings.HasPrefix(l.input[l.pos:], l.rightDelim+"\\") || strings.HasPrefix(l.input[l.pos:], l.rightDelim) {
278
+ if l.parenDepth == 0 {
279
+ return lexRightDelim
280
+ }
281
+ return l.errorf("unclosed left paren")
282
+ }
283
+ switch r := l.next(); {
284
+ case r == eof || isEndOfLine(r):
285
+ return l.errorf("unclosed action")
286
+ case isSpace(r):
287
+ return lexSpace
288
+ case r == ':':
289
+ if l.next() != '=' {
290
+ return l.errorf("expected :=")
291
+ }
292
+ l.emit(itemColonEquals)
293
+ case r == '|':
294
+ l.emit(itemPipe)
295
+ case r == '"':
296
+ return lexQuote
297
+ case r == '`':
298
+ return lexRawQuote
299
+ case r == '$':
300
+ return lexVariable
301
+ case r == '\'':
302
+ return lexChar
303
+ case r == '.':
304
+ // special look-ahead for ".field" so we don't break l.backup().
305
+ if l.pos < Pos(len(l.input)) {
306
+ r := l.input[l.pos]
307
+ if r < '0' || '9' < r {
308
+ return lexField
309
+ }
310
+ }
311
+ fallthrough // '.' can start a number.
312
+ case r == '+' || r == '-' || ('0' <= r && r <= '9'):
313
+ l.backup()
314
+ return lexNumber
315
+ case isAlphaNumeric(r):
316
+ l.backup()
317
+ return lexIdentifier
318
+ case r == '(':
319
+ l.emit(itemLeftParen)
320
+ l.parenDepth++
321
+ return lexInsideAction
322
+ case r == ')':
323
+ l.emit(itemRightParen)
324
+ l.parenDepth--
325
+ if l.parenDepth < 0 {
326
+ return l.errorf("unexpected right paren %#U", r)
327
+ }
328
+ return lexInsideAction
329
+ case r <= unicode.MaxASCII && unicode.IsPrint(r):
330
+ l.emit(itemChar)
331
+ return lexInsideAction
332
+ default:
333
+ return l.errorf("unrecognized character in action: %#U", r)
334
+ }
335
+ return lexInsideAction
336
+}
337
+
338
+// lexSpace scans a run of space characters.
339
+// One space has already been seen.
340
+func lexSpace(l *lexer) stateFn {
341
+ for isSpace(l.peek()) {
342
+ l.next()
343
+ }
344
+ l.emit(itemSpace)
345
+ return lexInsideAction
346
+}
347
+
348
+// lexIdentifier scans an alphanumeric.
349
+func lexIdentifier(l *lexer) stateFn {
350
+Loop:
351
+ for {
352
+ switch r := l.next(); {
353
+ case isAlphaNumeric(r):
354
+ // absorb.
355
+ default:
356
+ l.backup()
357
+ word := l.input[l.start:l.pos]
358
+ if !l.atTerminator() {
359
+ return l.errorf("bad character %#U", r)
360
+ }
361
+ switch {
362
+ case key[word] > itemKeyword:
363
+ l.emit(key[word])
364
+ case word[0] == '.':
365
+ l.emit(itemField)
366
+ case word == "true", word == "false":
367
+ l.emit(itemBool)
368
+ default:
369
+ l.emit(itemIdentifier)
370
+ }
371
+ break Loop
372
+ }
373
+ }
374
+ return lexInsideAction
375
+}
376
+
377
+// lexField scans a field: .Alphanumeric.
378
+// The . has been scanned.
379
+func lexField(l *lexer) stateFn {
380
+ return lexFieldOrVariable(l, itemField)
381
+}
382
+
383
+// lexVariable scans a Variable: $Alphanumeric.
384
+// The $ has been scanned.
385
+func lexVariable(l *lexer) stateFn {
386
+ if l.atTerminator() { // Nothing interesting follows -> "$".
387
+ l.emit(itemVariable)
388
+ return lexInsideAction
389
+ }
390
+ return lexFieldOrVariable(l, itemVariable)
391
+}
392
+
393
+// lexVariable scans a field or variable: [.$]Alphanumeric.
394
+// The . or $ has been scanned.
395
+func lexFieldOrVariable(l *lexer, typ itemType) stateFn {
396
+ if l.atTerminator() { // Nothing interesting follows -> "." or "$".
397
+ if typ == itemVariable {
398
+ l.emit(itemVariable)
399
+ } else {
400
+ l.emit(itemDot)
401
+ }
402
+ return lexInsideAction
403
+ }
404
+ var r rune
405
+ for {
406
+ r = l.next()
407
+ if !isAlphaNumeric(r) {
408
+ l.backup()
409
+ break
410
+ }
411
+ }
412
+ if !l.atTerminator() {
413
+ return l.errorf("bad character %#U", r)
414
+ }
415
+ l.emit(typ)
416
+ return lexInsideAction
417
+}
418
+
419
+// atTerminator reports whether the input is at valid termination character to
420
+// appear after an identifier. Breaks .X.Y into two pieces. Also catches cases
421
+// like "$x+2" not being acceptable without a space, in case we decide one
422
+// day to implement arithmetic.
423
+func (l *lexer) atTerminator() bool {
424
+ r := l.peek()
425
+ if isSpace(r) || isEndOfLine(r) {
426
+ return true
427
+ }
428
+ switch r {
429
+ case eof, '.', ',', '|', ':', ')', '(':
430
+ return true
431
+ }
432
+ // Does r start the delimiter? This can be ambiguous (with delim=="//", $x/2 will
433
+ // succeed but should fail) but only in extremely rare cases caused by willfully
434
+ // bad choice of delimiter.
435
+ if rd, _ := utf8.DecodeRuneInString(l.rightDelim); rd == r {
436
+ return true
437
+ }
438
+ return false
439
+}
440
+
441
+// lexChar scans a character constant. The initial quote is already
442
+// scanned. Syntax checking is done by the parser.
443
+func lexChar(l *lexer) stateFn {
444
+Loop:
445
+ for {
446
+ switch l.next() {
447
+ case '\\':
448
+ if r := l.next(); r != eof && r != '\n' {
449
+ break
450
+ }
451
+ fallthrough
452
+ case eof, '\n':
453
+ return l.errorf("unterminated character constant")
454
+ case '\'':
455
+ break Loop
456
+ }
457
+ }
458
+ l.emit(itemCharConstant)
459
+ return lexInsideAction
460
+}
461
+
462
+// lexNumber scans a number: decimal, octal, hex, float, or imaginary. This
463
+// isn't a perfect number scanner - for instance it accepts "." and "0x0.2"
464
+// and "089" - but when it's wrong the input is invalid and the parser (via
465
+// strconv) will notice.
466
+func lexNumber(l *lexer) stateFn {
467
+ if !l.scanNumber() {
468
+ return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
469
+ }
470
+ if sign := l.peek(); sign == '+' || sign == '-' {
471
+ // Complex: 1+2i. No spaces, must end in 'i'.
472
+ if !l.scanNumber() || l.input[l.pos-1] != 'i' {
473
+ return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
474
+ }
475
+ l.emit(itemComplex)
476
+ } else {
477
+ l.emit(itemNumber)
478
+ }
479
+ return lexInsideAction
480
+}
481
+
482
+func (l *lexer) scanNumber() bool {
483
+ // Optional leading sign.
484
+ l.accept("+-")
485
+ // Is it hex?
486
+ digits := "0123456789"
487
+ if l.accept("0") && l.accept("xX") {
488
+ digits = "0123456789abcdefABCDEF"
489
+ }
490
+ l.acceptRun(digits)
491
+ if l.accept(".") {
492
+ l.acceptRun(digits)
493
+ }
494
+ if l.accept("eE") {
495
+ l.accept("+-")
496
+ l.acceptRun("0123456789")
497
+ }
498
+ // Is it imaginary?
499
+ l.accept("i")
500
+ // Next thing mustn't be alphanumeric.
501
+ if isAlphaNumeric(l.peek()) {
502
+ l.next()
503
+ return false
504
+ }
505
+ return true
506
+}
507
+
508
+// lexQuote scans a quoted string.
509
+func lexQuote(l *lexer) stateFn {
510
+Loop:
511
+ for {
512
+ switch l.next() {
513
+ case '\\':
514
+ if r := l.next(); r != eof && r != '\n' {
515
+ break
516
+ }
517
+ fallthrough
518
+ case eof, '\n':
519
+ return l.errorf("unterminated quoted string")
520
+ case '"':
521
+ break Loop
522
+ }
523
+ }
524
+ l.emit(itemString)
525
+ return lexInsideAction
526
+}
527
+
528
+// lexRawQuote scans a raw quoted string.
529
+func lexRawQuote(l *lexer) stateFn {
530
+Loop:
531
+ for {
532
+ switch l.next() {
533
+ case eof, '\n':
534
+ return l.errorf("unterminated raw quoted string")
535
+ case '`':
536
+ break Loop
537
+ }
538
+ }
539
+ l.emit(itemRawString)
540
+ return lexInsideAction
541
+}
542
+
543
+// isSpace reports whether r is a space character.
544
+func isSpace(r rune) bool {
545
+ return r == ' ' || r == '\t'
546
+}
547
+
548
+// isEndOfLine reports whether r is an end-of-line character.
549
+func isEndOfLine(r rune) bool {
550
+ return r == '\r' || r == '\n'
551
+}
552
+
553
+// isAlphaNumeric reports whether r is an alphabetic, digit, or underscore.
554
+func isAlphaNumeric(r rune) bool {
555
+ return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r)
556
+}
Godeps/_workspace/src/github.com/alecthomas/template/parse/lex_test.go
new
+468
@@ -0,0 +1,468 @@
1
+// Copyright 2011 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 parse
6
+
7
+import (
8
+ "fmt"
9
+ "testing"
10
+)
11
+
12
+// Make the types prettyprint.
13
+var itemName = map[itemType]string{
14
+ itemError: "error",
15
+ itemBool: "bool",
16
+ itemChar: "char",
17
+ itemCharConstant: "charconst",
18
+ itemComplex: "complex",
19
+ itemColonEquals: ":=",
20
+ itemEOF: "EOF",
21
+ itemField: "field",
22
+ itemIdentifier: "identifier",
23
+ itemLeftDelim: "left delim",
24
+ itemLeftParen: "(",
25
+ itemNumber: "number",
26
+ itemPipe: "pipe",
27
+ itemRawString: "raw string",
28
+ itemRightDelim: "right delim",
29
+ itemElideNewline: "elide newline",
30
+ itemRightParen: ")",
31
+ itemSpace: "space",
32
+ itemString: "string",
33
+ itemVariable: "variable",
34
+
35
+ // keywords
36
+ itemDot: ".",
37
+ itemDefine: "define",
38
+ itemElse: "else",
39
+ itemIf: "if",
40
+ itemEnd: "end",
41
+ itemNil: "nil",
42
+ itemRange: "range",
43
+ itemTemplate: "template",
44
+ itemWith: "with",
45
+}
46
+
47
+func (i itemType) String() string {
48
+ s := itemName[i]
49
+ if s == "" {
50
+ return fmt.Sprintf("item%d", int(i))
51
+ }
52
+ return s
53
+}
54
+
55
+type lexTest struct {
56
+ name string
57
+ input string
58
+ items []item
59
+}
60
+
61
+var (
62
+ tEOF = item{itemEOF, 0, ""}
63
+ tFor = item{itemIdentifier, 0, "for"}
64
+ tLeft = item{itemLeftDelim, 0, "{{"}
65
+ tLpar = item{itemLeftParen, 0, "("}
66
+ tPipe = item{itemPipe, 0, "|"}
67
+ tQuote = item{itemString, 0, `"abc \n\t\" "`}
68
+ tRange = item{itemRange, 0, "range"}
69
+ tRight = item{itemRightDelim, 0, "}}"}
70
+ tElideNewline = item{itemElideNewline, 0, "\\"}
71
+ tRpar = item{itemRightParen, 0, ")"}
72
+ tSpace = item{itemSpace, 0, " "}
73
+ raw = "`" + `abc\n\t\" ` + "`"
74
+ tRawQuote = item{itemRawString, 0, raw}
75
+)
76
+
77
+var lexTests = []lexTest{
78
+ {"empty", "", []item{tEOF}},
79
+ {"spaces", " \t\n", []item{{itemText, 0, " \t\n"}, tEOF}},
80
+ {"text", `now is the time`, []item{{itemText, 0, "now is the time"}, tEOF}},
81
+ {"elide newline", "{{}}\\", []item{tLeft, tRight, tElideNewline, tEOF}},
82
+ {"text with comment", "hello-{{/* this is a comment */}}-world", []item{
83
+ {itemText, 0, "hello-"},
84
+ {itemText, 0, "-world"},
85
+ tEOF,
86
+ }},
87
+ {"punctuation", "{{,@% }}", []item{
88
+ tLeft,
89
+ {itemChar, 0, ","},
90
+ {itemChar, 0, "@"},
91
+ {itemChar, 0, "%"},
92
+ tSpace,
93
+ tRight,
94
+ tEOF,
95
+ }},
96
+ {"parens", "{{((3))}}", []item{
97
+ tLeft,
98
+ tLpar,
99
+ tLpar,
100
+ {itemNumber, 0, "3"},
101
+ tRpar,
102
+ tRpar,
103
+ tRight,
104
+ tEOF,
105
+ }},
106
+ {"empty action", `{{}}`, []item{tLeft, tRight, tEOF}},
107
+ {"for", `{{for}}`, []item{tLeft, tFor, tRight, tEOF}},
108
+ {"quote", `{{"abc \n\t\" "}}`, []item{tLeft, tQuote, tRight, tEOF}},
109
+ {"raw quote", "{{" + raw + "}}", []item{tLeft, tRawQuote, tRight, tEOF}},
110
+ {"numbers", "{{1 02 0x14 -7.2i 1e3 +1.2e-4 4.2i 1+2i}}", []item{
111
+ tLeft,
112
+ {itemNumber, 0, "1"},
113
+ tSpace,
114
+ {itemNumber, 0, "02"},
115
+ tSpace,
116
+ {itemNumber, 0, "0x14"},
117
+ tSpace,
118
+ {itemNumber, 0, "-7.2i"},
119
+ tSpace,
120
+ {itemNumber, 0, "1e3"},
121
+ tSpace,
122
+ {itemNumber, 0, "+1.2e-4"},
123
+ tSpace,
124
+ {itemNumber, 0, "4.2i"},
125
+ tSpace,
126
+ {itemComplex, 0, "1+2i"},
127
+ tRight,
128
+ tEOF,
129
+ }},
130
+ {"characters", `{{'a' '\n' '\'' '\\' '\u00FF' '\xFF' '本'}}`, []item{
131
+ tLeft,
132
+ {itemCharConstant, 0, `'a'`},
133
+ tSpace,
134
+ {itemCharConstant, 0, `'\n'`},
135
+ tSpace,
136
+ {itemCharConstant, 0, `'\''`},
137
+ tSpace,
138
+ {itemCharConstant, 0, `'\\'`},
139
+ tSpace,
140
+ {itemCharConstant, 0, `'\u00FF'`},
141
+ tSpace,
142
+ {itemCharConstant, 0, `'\xFF'`},
143
+ tSpace,
144
+ {itemCharConstant, 0, `'本'`},
145
+ tRight,
146
+ tEOF,
147
+ }},
148
+ {"bools", "{{true false}}", []item{
149
+ tLeft,
150
+ {itemBool, 0, "true"},
151
+ tSpace,
152
+ {itemBool, 0, "false"},
153
+ tRight,
154
+ tEOF,
155
+ }},
156
+ {"dot", "{{.}}", []item{
157
+ tLeft,
158
+ {itemDot, 0, "."},
159
+ tRight,
160
+ tEOF,
161
+ }},
162
+ {"nil", "{{nil}}", []item{
163
+ tLeft,
164
+ {itemNil, 0, "nil"},
165
+ tRight,
166
+ tEOF,
167
+ }},
168
+ {"dots", "{{.x . .2 .x.y.z}}", []item{
169
+ tLeft,
170
+ {itemField, 0, ".x"},
171
+ tSpace,
172
+ {itemDot, 0, "."},
173
+ tSpace,
174
+ {itemNumber, 0, ".2"},
175
+ tSpace,
176
+ {itemField, 0, ".x"},
177
+ {itemField, 0, ".y"},
178
+ {itemField, 0, ".z"},
179
+ tRight,
180
+ tEOF,
181
+ }},
182
+ {"keywords", "{{range if else end with}}", []item{
183
+ tLeft,
184
+ {itemRange, 0, "range"},
185
+ tSpace,
186
+ {itemIf, 0, "if"},
187
+ tSpace,
188
+ {itemElse, 0, "else"},
189
+ tSpace,
190
+ {itemEnd, 0, "end"},
191
+ tSpace,
192
+ {itemWith, 0, "with"},
193
+ tRight,
194
+ tEOF,
195
+ }},
196
+ {"variables", "{{$c := printf $ $hello $23 $ $var.Field .Method}}", []item{
197
+ tLeft,
198
+ {itemVariable, 0, "$c"},
199
+ tSpace,
200
+ {itemColonEquals, 0, ":="},
201
+ tSpace,
202
+ {itemIdentifier, 0, "printf"},
203
+ tSpace,
204
+ {itemVariable, 0, "$"},
205
+ tSpace,
206
+ {itemVariable, 0, "$hello"},
207
+ tSpace,
208
+ {itemVariable, 0, "$23"},
209
+ tSpace,
210
+ {itemVariable, 0, "$"},
211
+ tSpace,
212
+ {itemVariable, 0, "$var"},
213
+ {itemField, 0, ".Field"},
214
+ tSpace,
215
+ {itemField, 0, ".Method"},
216
+ tRight,
217
+ tEOF,
218
+ }},
219
+ {"variable invocation", "{{$x 23}}", []item{
220
+ tLeft,
221
+ {itemVariable, 0, "$x"},
222
+ tSpace,
223
+ {itemNumber, 0, "23"},
224
+ tRight,
225
+ tEOF,
226
+ }},
227
+ {"pipeline", `intro {{echo hi 1.2 |noargs|args 1 "hi"}} outro`, []item{
228
+ {itemText, 0, "intro "},
229
+ tLeft,
230
+ {itemIdentifier, 0, "echo"},
231
+ tSpace,
232
+ {itemIdentifier, 0, "hi"},
233
+ tSpace,
234
+ {itemNumber, 0, "1.2"},
235
+ tSpace,
236
+ tPipe,
237
+ {itemIdentifier, 0, "noargs"},
238
+ tPipe,
239
+ {itemIdentifier, 0, "args"},
240
+ tSpace,
241
+ {itemNumber, 0, "1"},
242
+ tSpace,
243
+ {itemString, 0, `"hi"`},
244
+ tRight,
245
+ {itemText, 0, " outro"},
246
+ tEOF,
247
+ }},
248
+ {"declaration", "{{$v := 3}}", []item{
249
+ tLeft,
250
+ {itemVariable, 0, "$v"},
251
+ tSpace,
252
+ {itemColonEquals, 0, ":="},
253
+ tSpace,
254
+ {itemNumber, 0, "3"},
255
+ tRight,
256
+ tEOF,
257
+ }},
258
+ {"2 declarations", "{{$v , $w := 3}}", []item{
259
+ tLeft,
260
+ {itemVariable, 0, "$v"},
261
+ tSpace,
262
+ {itemChar, 0, ","},
263
+ tSpace,
264
+ {itemVariable, 0, "$w"},
265
+ tSpace,
266
+ {itemColonEquals, 0, ":="},
267
+ tSpace,
268
+ {itemNumber, 0, "3"},
269
+ tRight,
270
+ tEOF,
271
+ }},
272
+ {"field of parenthesized expression", "{{(.X).Y}}", []item{
273
+ tLeft,
274
+ tLpar,
275
+ {itemField, 0, ".X"},
276
+ tRpar,
277
+ {itemField, 0, ".Y"},
278
+ tRight,
279
+ tEOF,
280
+ }},
281
+ // errors
282
+ {"badchar", "#{{\x01}}", []item{
283
+ {itemText, 0, "#"},
284
+ tLeft,
285
+ {itemError, 0, "unrecognized character in action: U+0001"},
286
+ }},
287
+ {"unclosed action", "{{\n}}", []item{
288
+ tLeft,
289
+ {itemError, 0, "unclosed action"},
290
+ }},
291
+ {"EOF in action", "{{range", []item{
292
+ tLeft,
293
+ tRange,
294
+ {itemError, 0, "unclosed action"},
295
+ }},
296
+ {"unclosed quote", "{{\"\n\"}}", []item{
297
+ tLeft,
298
+ {itemError, 0, "unterminated quoted string"},
299
+ }},
300
+ {"unclosed raw quote", "{{`xx\n`}}", []item{
301
+ tLeft,
302
+ {itemError, 0, "unterminated raw quoted string"},
303
+ }},
304
+ {"unclosed char constant", "{{'\n}}", []item{
305
+ tLeft,
306
+ {itemError, 0, "unterminated character constant"},
307
+ }},
308
+ {"bad number", "{{3k}}", []item{
309
+ tLeft,
310
+ {itemError, 0, `bad number syntax: "3k"`},
311
+ }},
312
+ {"unclosed paren", "{{(3}}", []item{
313
+ tLeft,
314
+ tLpar,
315
+ {itemNumber, 0, "3"},
316
+ {itemError, 0, `unclosed left paren`},
317
+ }},
318
+ {"extra right paren", "{{3)}}", []item{
319
+ tLeft,
320
+ {itemNumber, 0, "3"},
321
+ tRpar,
322
+ {itemError, 0, `unexpected right paren U+0029 ')'`},
323
+ }},
324
+
325
+ // Fixed bugs
326
+ // Many elements in an action blew the lookahead until
327
+ // we made lexInsideAction not loop.
328
+ {"long pipeline deadlock", "{{|||||}}", []item{
329
+ tLeft,
330
+ tPipe,
331
+ tPipe,
332
+ tPipe,
333
+ tPipe,
334
+ tPipe,
335
+ tRight,
336
+ tEOF,
337
+ }},
338
+ {"text with bad comment", "hello-{{/*/}}-world", []item{
339
+ {itemText, 0, "hello-"},
340
+ {itemError, 0, `unclosed comment`},
341
+ }},
342
+ {"text with comment close separted from delim", "hello-{{/* */ }}-world", []item{
343
+ {itemText, 0, "hello-"},
344
+ {itemError, 0, `comment ends before closing delimiter`},
345
+ }},
346
+ // This one is an error that we can't catch because it breaks templates with
347
+ // minimized JavaScript. Should have fixed it before Go 1.1.
348
+ {"unmatched right delimiter", "hello-{.}}-world", []item{
349
+ {itemText, 0, "hello-{.}}-world"},
350
+ tEOF,
351
+ }},
352
+}
353
+
354
+// collect gathers the emitted items into a slice.
355
+func collect(t *lexTest, left, right string) (items []item) {
356
+ l := lex(t.name, t.input, left, right)
357
+ for {
358
+ item := l.nextItem()
359
+ items = append(items, item)
360
+ if item.typ == itemEOF || item.typ == itemError {
361
+ break
362
+ }
363
+ }
364
+ return
365
+}
366
+
367
+func equal(i1, i2 []item, checkPos bool) bool {
368
+ if len(i1) != len(i2) {
369
+ return false
370
+ }
371
+ for k := range i1 {
372
+ if i1[k].typ != i2[k].typ {
373
+ return false
374
+ }
375
+ if i1[k].val != i2[k].val {
376
+ return false
377
+ }
378
+ if checkPos && i1[k].pos != i2[k].pos {
379
+ return false
380
+ }
381
+ }
382
+ return true
383
+}
384
+
385
+func TestLex(t *testing.T) {
386
+ for _, test := range lexTests {
387
+ items := collect(&test, "", "")
388
+ if !equal(items, test.items, false) {
389
+ t.Errorf("%s: got\n\t%+v\nexpected\n\t%v", test.name, items, test.items)
390
+ }
391
+ }
392
+}
393
+
394
+// Some easy cases from above, but with delimiters $$ and @@
395
+var lexDelimTests = []lexTest{
396
+ {"punctuation", "$$,@%{{}}@@", []item{
397
+ tLeftDelim,
398
+ {itemChar, 0, ","},
399
+ {itemChar, 0, "@"},
400
+ {itemChar, 0, "%"},
401
+ {itemChar, 0, "{"},
402
+ {itemChar, 0, "{"},
403
+ {itemChar, 0, "}"},
404
+ {itemChar, 0, "}"},
405
+ tRightDelim,
406
+ tEOF,
407
+ }},
408
+ {"empty action", `$$@@`, []item{tLeftDelim, tRightDelim, tEOF}},
409
+ {"for", `$$for@@`, []item{tLeftDelim, tFor, tRightDelim, tEOF}},
410
+ {"quote", `$$"abc \n\t\" "@@`, []item{tLeftDelim, tQuote, tRightDelim, tEOF}},
411
+ {"raw quote", "$$" + raw + "@@", []item{tLeftDelim, tRawQuote, tRightDelim, tEOF}},
412
+}
413
+
414
+var (
415
+ tLeftDelim = item{itemLeftDelim, 0, "$$"}
416
+ tRightDelim = item{itemRightDelim, 0, "@@"}
417
+)
418
+
419
+func TestDelims(t *testing.T) {
420
+ for _, test := range lexDelimTests {
421
+ items := collect(&test, "$$", "@@")
422
+ if !equal(items, test.items, false) {
423
+ t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items)
424
+ }
425
+ }
426
+}
427
+
428
+var lexPosTests = []lexTest{
429
+ {"empty", "", []item{tEOF}},
430
+ {"punctuation", "{{,@%#}}", []item{
431
+ {itemLeftDelim, 0, "{{"},
432
+ {itemChar, 2, ","},
433
+ {itemChar, 3, "@"},
434
+ {itemChar, 4, "%"},
435
+ {itemChar, 5, "#"},
436
+ {itemRightDelim, 6, "}}"},
437
+ {itemEOF, 8, ""},
438
+ }},
439
+ {"sample", "0123{{hello}}xyz", []item{
440
+ {itemText, 0, "0123"},
441
+ {itemLeftDelim, 4, "{{"},
442
+ {itemIdentifier, 6, "hello"},
443
+ {itemRightDelim, 11, "}}"},
444
+ {itemText, 13, "xyz"},
445
+ {itemEOF, 16, ""},
446
+ }},
447
+}
448
+
449
+// The other tests don't check position, to make the test cases easier to construct.
450
+// This one does.
451
+func TestPos(t *testing.T) {
452
+ for _, test := range lexPosTests {
453
+ items := collect(&test, "", "")
454
+ if !equal(items, test.items, true) {
455
+ t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items)
456
+ if len(items) == len(test.items) {
457
+ // Detailed print; avoid item.String() to expose the position value.
458
+ for i := range items {
459
+ if !equal(items[i:i+1], test.items[i:i+1], true) {
460
+ i1 := items[i]
461
+ i2 := test.items[i]
462
+ t.Errorf("\t#%d: got {%v %d %q} expected {%v %d %q}", i, i1.typ, i1.pos, i1.val, i2.typ, i2.pos, i2.val)
463
+ }
464
+ }
465
+ }
466
+ }
467
+ }
468
+}
Godeps/_workspace/src/github.com/alecthomas/template/parse/node.go
new
+834
@@ -0,0 +1,834 @@
1
+// Copyright 2011 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
+// Parse nodes.
6
+
7
+package parse
8
+
9
+import (
10
+ "bytes"
11
+ "fmt"
12
+ "strconv"
13
+ "strings"
14
+)
15
+
16
+var textFormat = "%s" // Changed to "%q" in tests for better error messages.
17
+
18
+// A Node is an element in the parse tree. The interface is trivial.
19
+// The interface contains an unexported method so that only
20
+// types local to this package can satisfy it.
21
+type Node interface {
22
+ Type() NodeType
23
+ String() string
24
+ // Copy does a deep copy of the Node and all its components.
25
+ // To avoid type assertions, some XxxNodes also have specialized
26
+ // CopyXxx methods that return *XxxNode.
27
+ Copy() Node
28
+ Position() Pos // byte position of start of node in full original input string
29
+ // tree returns the containing *Tree.
30
+ // It is unexported so all implementations of Node are in this package.
31
+ tree() *Tree
32
+}
33
+
34
+// NodeType identifies the type of a parse tree node.
35
+type NodeType int
36
+
37
+// Pos represents a byte position in the original input text from which
38
+// this template was parsed.
39
+type Pos int
40
+
41
+func (p Pos) Position() Pos {
42
+ return p
43
+}
44
+
45
+// Type returns itself and provides an easy default implementation
46
+// for embedding in a Node. Embedded in all non-trivial Nodes.
47
+func (t NodeType) Type() NodeType {
48
+ return t
49
+}
50
+
51
+const (
52
+ NodeText NodeType = iota // Plain text.
53
+ NodeAction // A non-control action such as a field evaluation.
54
+ NodeBool // A boolean constant.
55
+ NodeChain // A sequence of field accesses.
56
+ NodeCommand // An element of a pipeline.
57
+ NodeDot // The cursor, dot.
58
+ nodeElse // An else action. Not added to tree.
59
+ nodeEnd // An end action. Not added to tree.
60
+ NodeField // A field or method name.
61
+ NodeIdentifier // An identifier; always a function name.
62
+ NodeIf // An if action.
63
+ NodeList // A list of Nodes.
64
+ NodeNil // An untyped nil constant.
65
+ NodeNumber // A numerical constant.
66
+ NodePipe // A pipeline of commands.
67
+ NodeRange // A range action.
68
+ NodeString // A string constant.
69
+ NodeTemplate // A template invocation action.
70
+ NodeVariable // A $ variable.
71
+ NodeWith // A with action.
72
+)
73
+
74
+// Nodes.
75
+
76
+// ListNode holds a sequence of nodes.
77
+type ListNode struct {
78
+ NodeType
79
+ Pos
80
+ tr *Tree
81
+ Nodes []Node // The element nodes in lexical order.
82
+}
83
+
84
+func (t *Tree) newList(pos Pos) *ListNode {
85
+ return &ListNode{tr: t, NodeType: NodeList, Pos: pos}
86
+}
87
+
88
+func (l *ListNode) append(n Node) {
89
+ l.Nodes = append(l.Nodes, n)
90
+}
91
+
92
+func (l *ListNode) tree() *Tree {
93
+ return l.tr
94
+}
95
+
96
+func (l *ListNode) String() string {
97
+ b := new(bytes.Buffer)
98
+ for _, n := range l.Nodes {
99
+ fmt.Fprint(b, n)
100
+ }
101
+ return b.String()
102
+}
103
+
104
+func (l *ListNode) CopyList() *ListNode {
105
+ if l == nil {
106
+ return l
107
+ }
108
+ n := l.tr.newList(l.Pos)
109
+ for _, elem := range l.Nodes {
110
+ n.append(elem.Copy())
111
+ }
112
+ return n
113
+}
114
+
115
+func (l *ListNode) Copy() Node {
116
+ return l.CopyList()
117
+}
118
+
119
+// TextNode holds plain text.
120
+type TextNode struct {
121
+ NodeType
122
+ Pos
123
+ tr *Tree
124
+ Text []byte // The text; may span newlines.
125
+}
126
+
127
+func (t *Tree) newText(pos Pos, text string) *TextNode {
128
+ return &TextNode{tr: t, NodeType: NodeText, Pos: pos, Text: []byte(text)}
129
+}
130
+
131
+func (t *TextNode) String() string {
132
+ return fmt.Sprintf(textFormat, t.Text)
133
+}
134
+
135
+func (t *TextNode) tree() *Tree {
136
+ return t.tr
137
+}
138
+
139
+func (t *TextNode) Copy() Node {
140
+ return &TextNode{tr: t.tr, NodeType: NodeText, Pos: t.Pos, Text: append([]byte{}, t.Text...)}
141
+}
142
+
143
+// PipeNode holds a pipeline with optional declaration
144
+type PipeNode struct {
145
+ NodeType
146
+ Pos
147
+ tr *Tree
148
+ Line int // The line number in the input (deprecated; kept for compatibility)
149
+ Decl []*VariableNode // Variable declarations in lexical order.
150
+ Cmds []*CommandNode // The commands in lexical order.
151
+}
152
+
153
+func (t *Tree) newPipeline(pos Pos, line int, decl []*VariableNode) *PipeNode {
154
+ return &PipeNode{tr: t, NodeType: NodePipe, Pos: pos, Line: line, Decl: decl}
155
+}
156
+
157
+func (p *PipeNode) append(command *CommandNode) {
158
+ p.Cmds = append(p.Cmds, command)
159
+}
160
+
161
+func (p *PipeNode) String() string {
162
+ s := ""
163
+ if len(p.Decl) > 0 {
164
+ for i, v := range p.Decl {
165
+ if i > 0 {
166
+ s += ", "
167
+ }
168
+ s += v.String()
169
+ }
170
+ s += " := "
171
+ }
172
+ for i, c := range p.Cmds {
173
+ if i > 0 {
174
+ s += " | "
175
+ }
176
+ s += c.String()
177
+ }
178
+ return s
179
+}
180
+
181
+func (p *PipeNode) tree() *Tree {
182
+ return p.tr
183
+}
184
+
185
+func (p *PipeNode) CopyPipe() *PipeNode {
186
+ if p == nil {
187
+ return p
188
+ }
189
+ var decl []*VariableNode
190
+ for _, d := range p.Decl {
191
+ decl = append(decl, d.Copy().(*VariableNode))
192
+ }
193
+ n := p.tr.newPipeline(p.Pos, p.Line, decl)
194
+ for _, c := range p.Cmds {
195
+ n.append(c.Copy().(*CommandNode))
196
+ }
197
+ return n
198
+}
199
+
200
+func (p *PipeNode) Copy() Node {
201
+ return p.CopyPipe()
202
+}
203
+
204
+// ActionNode holds an action (something bounded by delimiters).
205
+// Control actions have their own nodes; ActionNode represents simple
206
+// ones such as field evaluations and parenthesized pipelines.
207
+type ActionNode struct {
208
+ NodeType
209
+ Pos
210
+ tr *Tree
211
+ Line int // The line number in the input (deprecated; kept for compatibility)
212
+ Pipe *PipeNode // The pipeline in the action.
213
+}
214
+
215
+func (t *Tree) newAction(pos Pos, line int, pipe *PipeNode) *ActionNode {
216
+ return &ActionNode{tr: t, NodeType: NodeAction, Pos: pos, Line: line, Pipe: pipe}
217
+}
218
+
219
+func (a *ActionNode) String() string {
220
+ return fmt.Sprintf("{{%s}}", a.Pipe)
221
+
222
+}
223
+
224
+func (a *ActionNode) tree() *Tree {
225
+ return a.tr
226
+}
227
+
228
+func (a *ActionNode) Copy() Node {
229
+ return a.tr.newAction(a.Pos, a.Line, a.Pipe.CopyPipe())
230
+
231
+}
232
+
233
+// CommandNode holds a command (a pipeline inside an evaluating action).
234
+type CommandNode struct {
235
+ NodeType
236
+ Pos
237
+ tr *Tree
238
+ Args []Node // Arguments in lexical order: Identifier, field, or constant.
239
+}
240
+
241
+func (t *Tree) newCommand(pos Pos) *CommandNode {
242
+ return &CommandNode{tr: t, NodeType: NodeCommand, Pos: pos}
243
+}
244
+
245
+func (c *CommandNode) append(arg Node) {
246
+ c.Args = append(c.Args, arg)
247
+}
248
+
249
+func (c *CommandNode) String() string {
250
+ s := ""
251
+ for i, arg := range c.Args {
252
+ if i > 0 {
253
+ s += " "
254
+ }
255
+ if arg, ok := arg.(*PipeNode); ok {
256
+ s += "(" + arg.String() + ")"
257
+ continue
258
+ }
259
+ s += arg.String()
260
+ }
261
+ return s
262
+}
263
+
264
+func (c *CommandNode) tree() *Tree {
265
+ return c.tr
266
+}
267
+
268
+func (c *CommandNode) Copy() Node {
269
+ if c == nil {
270
+ return c
271
+ }
272
+ n := c.tr.newCommand(c.Pos)
273
+ for _, c := range c.Args {
274
+ n.append(c.Copy())
275
+ }
276
+ return n
277
+}
278
+
279
+// IdentifierNode holds an identifier.
280
+type IdentifierNode struct {
281
+ NodeType
282
+ Pos
283
+ tr *Tree
284
+ Ident string // The identifier's name.
285
+}
286
+
287
+// NewIdentifier returns a new IdentifierNode with the given identifier name.
288
+func NewIdentifier(ident string) *IdentifierNode {
289
+ return &IdentifierNode{NodeType: NodeIdentifier, Ident: ident}
290
+}
291
+
292
+// SetPos sets the position. NewIdentifier is a public method so we can't modify its signature.
293
+// Chained for convenience.
294
+// TODO: fix one day?
295
+func (i *IdentifierNode) SetPos(pos Pos) *IdentifierNode {
296
+ i.Pos = pos
297
+ return i
298
+}
299
+
300
+// SetTree sets the parent tree for the node. NewIdentifier is a public method so we can't modify its signature.
301
+// Chained for convenience.
302
+// TODO: fix one day?
303
+func (i *IdentifierNode) SetTree(t *Tree) *IdentifierNode {
304
+ i.tr = t
305
+ return i
306
+}
307
+
308
+func (i *IdentifierNode) String() string {
309
+ return i.Ident
310
+}
311
+
312
+func (i *IdentifierNode) tree() *Tree {
313
+ return i.tr
314
+}
315
+
316
+func (i *IdentifierNode) Copy() Node {
317
+ return NewIdentifier(i.Ident).SetTree(i.tr).SetPos(i.Pos)
318
+}
319
+
320
+// VariableNode holds a list of variable names, possibly with chained field
321
+// accesses. The dollar sign is part of the (first) name.
322
+type VariableNode struct {
323
+ NodeType
324
+ Pos
325
+ tr *Tree
326
+ Ident []string // Variable name and fields in lexical order.
327
+}
328
+
329
+func (t *Tree) newVariable(pos Pos, ident string) *VariableNode {
330
+ return &VariableNode{tr: t, NodeType: NodeVariable, Pos: pos, Ident: strings.Split(ident, ".")}
331
+}
332
+
333
+func (v *VariableNode) String() string {
334
+ s := ""
335
+ for i, id := range v.Ident {
336
+ if i > 0 {
337
+ s += "."
338
+ }
339
+ s += id
340
+ }
341
+ return s
342
+}
343
+
344
+func (v *VariableNode) tree() *Tree {
345
+ return v.tr
346
+}
347
+
348
+func (v *VariableNode) Copy() Node {
349
+ return &VariableNode{tr: v.tr, NodeType: NodeVariable, Pos: v.Pos, Ident: append([]string{}, v.Ident...)}
350
+}
351
+
352
+// DotNode holds the special identifier '.'.
353
+type DotNode struct {
354
+ NodeType
355
+ Pos
356
+ tr *Tree
357
+}
358
+
359
+func (t *Tree) newDot(pos Pos) *DotNode {
360
+ return &DotNode{tr: t, NodeType: NodeDot, Pos: pos}
361
+}
362
+
363
+func (d *DotNode) Type() NodeType {
364
+ // Override method on embedded NodeType for API compatibility.
365
+ // TODO: Not really a problem; could change API without effect but
366
+ // api tool complains.
367
+ return NodeDot
368
+}
369
+
370
+func (d *DotNode) String() string {
371
+ return "."
372
+}
373
+
374
+func (d *DotNode) tree() *Tree {
375
+ return d.tr
376
+}
377
+
378
+func (d *DotNode) Copy() Node {
379
+ return d.tr.newDot(d.Pos)
380
+}
381
+
382
+// NilNode holds the special identifier 'nil' representing an untyped nil constant.
383
+type NilNode struct {
384
+ NodeType
385
+ Pos
386
+ tr *Tree
387
+}
388
+
389
+func (t *Tree) newNil(pos Pos) *NilNode {
390
+ return &NilNode{tr: t, NodeType: NodeNil, Pos: pos}
391
+}
392
+
393
+func (n *NilNode) Type() NodeType {
394
+ // Override method on embedded NodeType for API compatibility.
395
+ // TODO: Not really a problem; could change API without effect but
396
+ // api tool complains.
397
+ return NodeNil
398
+}
399
+
400
+func (n *NilNode) String() string {
401
+ return "nil"
402
+}
403
+
404
+func (n *NilNode) tree() *Tree {
405
+ return n.tr
406
+}
407
+
408
+func (n *NilNode) Copy() Node {
409
+ return n.tr.newNil(n.Pos)
410
+}
411
+
412
+// FieldNode holds a field (identifier starting with '.').
413
+// The names may be chained ('.x.y').
414
+// The period is dropped from each ident.
415
+type FieldNode struct {
416
+ NodeType
417
+ Pos
418
+ tr *Tree
419
+ Ident []string // The identifiers in lexical order.
420
+}
421
+
422
+func (t *Tree) newField(pos Pos, ident string) *FieldNode {
423
+ return &FieldNode{tr: t, NodeType: NodeField, Pos: pos, Ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period
424
+}
425
+
426
+func (f *FieldNode) String() string {
427
+ s := ""
428
+ for _, id := range f.Ident {
429
+ s += "." + id
430
+ }
431
+ return s
432
+}
433
+
434
+func (f *FieldNode) tree() *Tree {
435
+ return f.tr
436
+}
437
+
438
+func (f *FieldNode) Copy() Node {
439
+ return &FieldNode{tr: f.tr, NodeType: NodeField, Pos: f.Pos, Ident: append([]string{}, f.Ident...)}
440
+}
441
+
442
+// ChainNode holds a term followed by a chain of field accesses (identifier starting with '.').
443
+// The names may be chained ('.x.y').
444
+// The periods are dropped from each ident.
445
+type ChainNode struct {
446
+ NodeType
447
+ Pos
448
+ tr *Tree
449
+ Node Node
450
+ Field []string // The identifiers in lexical order.
451
+}
452
+
453
+func (t *Tree) newChain(pos Pos, node Node) *ChainNode {
454
+ return &ChainNode{tr: t, NodeType: NodeChain, Pos: pos, Node: node}
455
+}
456
+
457
+// Add adds the named field (which should start with a period) to the end of the chain.
458
+func (c *ChainNode) Add(field string) {
459
+ if len(field) == 0 || field[0] != '.' {
460
+ panic("no dot in field")
461
+ }
462
+ field = field[1:] // Remove leading dot.
463
+ if field == "" {
464
+ panic("empty field")
465
+ }
466
+ c.Field = append(c.Field, field)
467
+}
468
+
469
+func (c *ChainNode) String() string {
470
+ s := c.Node.String()
471
+ if _, ok := c.Node.(*PipeNode); ok {
472
+ s = "(" + s + ")"
473
+ }
474
+ for _, field := range c.Field {
475
+ s += "." + field
476
+ }
477
+ return s
478
+}
479
+
480
+func (c *ChainNode) tree() *Tree {
481
+ return c.tr
482
+}
483
+
484
+func (c *ChainNode) Copy() Node {
485
+ return &ChainNode{tr: c.tr, NodeType: NodeChain, Pos: c.Pos, Node: c.Node, Field: append([]string{}, c.Field...)}
486
+}
487
+
488
+// BoolNode holds a boolean constant.
489
+type BoolNode struct {
490
+ NodeType
491
+ Pos
492
+ tr *Tree
493
+ True bool // The value of the boolean constant.
494
+}
495
+
496
+func (t *Tree) newBool(pos Pos, true bool) *BoolNode {
497
+ return &BoolNode{tr: t, NodeType: NodeBool, Pos: pos, True: true}
498
+}
499
+
500
+func (b *BoolNode) String() string {
501
+ if b.True {
502
+ return "true"
503
+ }
504
+ return "false"
505
+}
506
+
507
+func (b *BoolNode) tree() *Tree {
508
+ return b.tr
509
+}
510
+
511
+func (b *BoolNode) Copy() Node {
512
+ return b.tr.newBool(b.Pos, b.True)
513
+}
514
+
515
+// NumberNode holds a number: signed or unsigned integer, float, or complex.
516
+// The value is parsed and stored under all the types that can represent the value.
517
+// This simulates in a small amount of code the behavior of Go's ideal constants.
518
+type NumberNode struct {
519
+ NodeType
520
+ Pos
521
+ tr *Tree
522
+ IsInt bool // Number has an integral value.
523
+ IsUint bool // Number has an unsigned integral value.
524
+ IsFloat bool // Number has a floating-point value.
525
+ IsComplex bool // Number is complex.
526
+ Int64 int64 // The signed integer value.
527
+ Uint64 uint64 // The unsigned integer value.
528
+ Float64 float64 // The floating-point value.
529
+ Complex128 complex128 // The complex value.
530
+ Text string // The original textual representation from the input.
531
+}
532
+
533
+func (t *Tree) newNumber(pos Pos, text string, typ itemType) (*NumberNode, error) {
534
+ n := &NumberNode{tr: t, NodeType: NodeNumber, Pos: pos, Text: text}
535
+ switch typ {
536
+ case itemCharConstant:
537
+ rune, _, tail, err := strconv.UnquoteChar(text[1:], text[0])
538
+ if err != nil {
539
+ return nil, err
540
+ }
541
+ if tail != "'" {
542
+ return nil, fmt.Errorf("malformed character constant: %s", text)
543
+ }
544
+ n.Int64 = int64(rune)
545
+ n.IsInt = true
546
+ n.Uint64 = uint64(rune)
547
+ n.IsUint = true
548
+ n.Float64 = float64(rune) // odd but those are the rules.
549
+ n.IsFloat = true
550
+ return n, nil
551
+ case itemComplex:
552
+ // fmt.Sscan can parse the pair, so let it do the work.
553
+ if _, err := fmt.Sscan(text, &n.Complex128); err != nil {
554
+ return nil, err
555
+ }
556
+ n.IsComplex = true
557
+ n.simplifyComplex()
558
+ return n, nil
559
+ }
560
+ // Imaginary constants can only be complex unless they are zero.
561
+ if len(text) > 0 && text[len(text)-1] == 'i' {
562
+ f, err := strconv.ParseFloat(text[:len(text)-1], 64)
563
+ if err == nil {
564
+ n.IsComplex = true
565
+ n.Complex128 = complex(0, f)
566
+ n.simplifyComplex()
567
+ return n, nil
568
+ }
569
+ }
570
+ // Do integer test first so we get 0x123 etc.
571
+ u, err := strconv.ParseUint(text, 0, 64) // will fail for -0; fixed below.
572
+ if err == nil {
573
+ n.IsUint = true
574
+ n.Uint64 = u
575
+ }
576
+ i, err := strconv.ParseInt(text, 0, 64)
577
+ if err == nil {
578
+ n.IsInt = true
579
+ n.Int64 = i
580
+ if i == 0 {
581
+ n.IsUint = true // in case of -0.
582
+ n.Uint64 = u
583
+ }
584
+ }
585
+ // If an integer extraction succeeded, promote the float.
586
+ if n.IsInt {
587
+ n.IsFloat = true
588
+ n.Float64 = float64(n.Int64)
589
+ } else if n.IsUint {
590
+ n.IsFloat = true
591
+ n.Float64 = float64(n.Uint64)
592
+ } else {
593
+ f, err := strconv.ParseFloat(text, 64)
594
+ if err == nil {
595
+ n.IsFloat = true
596
+ n.Float64 = f
597
+ // If a floating-point extraction succeeded, extract the int if needed.
598
+ if !n.IsInt && float64(int64(f)) == f {
599
+ n.IsInt = true
600
+ n.Int64 = int64(f)
601
+ }
602
+ if !n.IsUint && float64(uint64(f)) == f {
603
+ n.IsUint = true
604
+ n.Uint64 = uint64(f)
605
+ }
606
+ }
607
+ }
608
+ if !n.IsInt && !n.IsUint && !n.IsFloat {
609
+ return nil, fmt.Errorf("illegal number syntax: %q", text)
610
+ }
611
+ return n, nil
612
+}
613
+
614
+// simplifyComplex pulls out any other types that are represented by the complex number.
615
+// These all require that the imaginary part be zero.
616
+func (n *NumberNode) simplifyComplex() {
617
+ n.IsFloat = imag(n.Complex128) == 0
618
+ if n.IsFloat {
619
+ n.Float64 = real(n.Complex128)
620
+ n.IsInt = float64(int64(n.Float64)) == n.Float64
621
+ if n.IsInt {
622
+ n.Int64 = int64(n.Float64)
623
+ }
624
+ n.IsUint = float64(uint64(n.Float64)) == n.Float64
625
+ if n.IsUint {
626
+ n.Uint64 = uint64(n.Float64)
627
+ }
628
+ }
629
+}
630
+
631
+func (n *NumberNode) String() string {
632
+ return n.Text
633
+}
634
+
635
+func (n *NumberNode) tree() *Tree {
636
+ return n.tr
637
+}
638
+
639
+func (n *NumberNode) Copy() Node {
640
+ nn := new(NumberNode)
641
+ *nn = *n // Easy, fast, correct.
642
+ return nn
643
+}
644
+
645
+// StringNode holds a string constant. The value has been "unquoted".
646
+type StringNode struct {
647
+ NodeType
648
+ Pos
649
+ tr *Tree
650
+ Quoted string // The original text of the string, with quotes.
651
+ Text string // The string, after quote processing.
652
+}
653
+
654
+func (t *Tree) newString(pos Pos, orig, text string) *StringNode {
655
+ return &StringNode{tr: t, NodeType: NodeString, Pos: pos, Quoted: orig, Text: text}
656
+}
657
+
658
+func (s *StringNode) String() string {
659
+ return s.Quoted
660
+}
661
+
662
+func (s *StringNode) tree() *Tree {
663
+ return s.tr
664
+}
665
+
666
+func (s *StringNode) Copy() Node {
667
+ return s.tr.newString(s.Pos, s.Quoted, s.Text)
668
+}
669
+
670
+// endNode represents an {{end}} action.
671
+// It does not appear in the final parse tree.
672
+type endNode struct {
673
+ NodeType
674
+ Pos
675
+ tr *Tree
676
+}
677
+
678
+func (t *Tree) newEnd(pos Pos) *endNode {
679
+ return &endNode{tr: t, NodeType: nodeEnd, Pos: pos}
680
+}
681
+
682
+func (e *endNode) String() string {
683
+ return "{{end}}"
684
+}
685
+
686
+func (e *endNode) tree() *Tree {
687
+ return e.tr
688
+}
689
+
690
+func (e *endNode) Copy() Node {
691
+ return e.tr.newEnd(e.Pos)
692
+}
693
+
694
+// elseNode represents an {{else}} action. Does not appear in the final tree.
695
+type elseNode struct {
696
+ NodeType
697
+ Pos
698
+ tr *Tree
699
+ Line int // The line number in the input (deprecated; kept for compatibility)
700
+}
701
+
702
+func (t *Tree) newElse(pos Pos, line int) *elseNode {
703
+ return &elseNode{tr: t, NodeType: nodeElse, Pos: pos, Line: line}
704
+}
705
+
706
+func (e *elseNode) Type() NodeType {
707
+ return nodeElse
708
+}
709
+
710
+func (e *elseNode) String() string {
711
+ return "{{else}}"
712
+}
713
+
714
+func (e *elseNode) tree() *Tree {
715
+ return e.tr
716
+}
717
+
718
+func (e *elseNode) Copy() Node {
719
+ return e.tr.newElse(e.Pos, e.Line)
720
+}
721
+
722
+// BranchNode is the common representation of if, range, and with.
723
+type BranchNode struct {
724
+ NodeType
725
+ Pos
726
+ tr *Tree
727
+ Line int // The line number in the input (deprecated; kept for compatibility)
728
+ Pipe *PipeNode // The pipeline to be evaluated.
729
+ List *ListNode // What to execute if the value is non-empty.
730
+ ElseList *ListNode // What to execute if the value is empty (nil if absent).
731
+}
732
+
733
+func (b *BranchNode) String() string {
734
+ name := ""
735
+ switch b.NodeType {
736
+ case NodeIf:
737
+ name = "if"
738
+ case NodeRange:
739
+ name = "range"
740
+ case NodeWith:
741
+ name = "with"
742
+ default:
743
+ panic("unknown branch type")
744
+ }
745
+ if b.ElseList != nil {
746
+ return fmt.Sprintf("{{%s %s}}%s{{else}}%s{{end}}", name, b.Pipe, b.List, b.ElseList)
747
+ }
748
+ return fmt.Sprintf("{{%s %s}}%s{{end}}", name, b.Pipe, b.List)
749
+}
750
+
751
+func (b *BranchNode) tree() *Tree {
752
+ return b.tr
753
+}
754
+
755
+func (b *BranchNode) Copy() Node {
756
+ switch b.NodeType {
757
+ case NodeIf:
758
+ return b.tr.newIf(b.Pos, b.Line, b.Pipe, b.List, b.ElseList)
759
+ case NodeRange:
760
+ return b.tr.newRange(b.Pos, b.Line, b.Pipe, b.List, b.ElseList)
761
+ case NodeWith:
762
+ return b.tr.newWith(b.Pos, b.Line, b.Pipe, b.List, b.ElseList)
763
+ default:
764
+ panic("unknown branch type")
765
+ }
766
+}
767
+
768
+// IfNode represents an {{if}} action and its commands.
769
+type IfNode struct {
770
+ BranchNode
771
+}
772
+
773
+func (t *Tree) newIf(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *IfNode {
774
+ return &IfNode{BranchNode{tr: t, NodeType: NodeIf, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
775
+}
776
+
777
+func (i *IfNode) Copy() Node {
778
+ return i.tr.newIf(i.Pos, i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList())
779
+}
780
+
781
+// RangeNode represents a {{range}} action and its commands.
782
+type RangeNode struct {
783
+ BranchNode
784
+}
785
+
786
+func (t *Tree) newRange(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode {
787
+ return &RangeNode{BranchNode{tr: t, NodeType: NodeRange, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
788
+}
789
+
790
+func (r *RangeNode) Copy() Node {
791
+ return r.tr.newRange(r.Pos, r.Line, r.Pipe.CopyPipe(), r.List.CopyList(), r.ElseList.CopyList())
792
+}
793
+
794
+// WithNode represents a {{with}} action and its commands.
795
+type WithNode struct {
796
+ BranchNode
797
+}
798
+
799
+func (t *Tree) newWith(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *WithNode {
800
+ return &WithNode{BranchNode{tr: t, NodeType: NodeWith, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
801
+}
802
+
803
+func (w *WithNode) Copy() Node {
804
+ return w.tr.newWith(w.Pos, w.Line, w.Pipe.CopyPipe(), w.List.CopyList(), w.ElseList.CopyList())
805
+}
806
+
807
+// TemplateNode represents a {{template}} action.
808
+type TemplateNode struct {
809
+ NodeType
810
+ Pos
811
+ tr *Tree
812
+ Line int // The line number in the input (deprecated; kept for compatibility)
813
+ Name string // The name of the template (unquoted).
814
+ Pipe *PipeNode // The command to evaluate as dot for the template.
815
+}
816
+
817
+func (t *Tree) newTemplate(pos Pos, line int, name string, pipe *PipeNode) *TemplateNode {
818
+ return &TemplateNode{tr: t, NodeType: NodeTemplate, Pos: pos, Line: line, Name: name, Pipe: pipe}
819
+}
820
+
821
+func (t *TemplateNode) String() string {
822
+ if t.Pipe == nil {
823
+ return fmt.Sprintf("{{template %q}}", t.Name)
824
+ }
825
+ return fmt.Sprintf("{{template %q %s}}", t.Name, t.Pipe)
826
+}
827
+
828
+func (t *TemplateNode) tree() *Tree {
829
+ return t.tr
830
+}
831
+
832
+func (t *TemplateNode) Copy() Node {
833
+ return t.tr.newTemplate(t.Pos, t.Line, t.Name, t.Pipe.CopyPipe())
834
+}
Godeps/_workspace/src/github.com/alecthomas/template/parse/parse.go
new
+700
@@ -0,0 +1,700 @@
1
+// Copyright 2011 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 parse builds parse trees for templates as defined by text/template
6
+// and html/template. Clients should use those packages to construct templates
7
+// rather than this one, which provides shared internal data structures not
8
+// intended for general use.
9
+package parse
10
+
11
+import (
12
+ "bytes"
13
+ "fmt"
14
+ "runtime"
15
+ "strconv"
16
+ "strings"
17
+)
18
+
19
+// Tree is the representation of a single parsed template.
20
+type Tree struct {
21
+ Name string // name of the template represented by the tree.
22
+ ParseName string // name of the top-level template during parsing, for error messages.
23
+ Root *ListNode // top-level root of the tree.
24
+ text string // text parsed to create the template (or its parent)
25
+ // Parsing only; cleared after parse.
26
+ funcs []map[string]interface{}
27
+ lex *lexer
28
+ token [3]item // three-token lookahead for parser.
29
+ peekCount int
30
+ vars []string // variables defined at the moment.
31
+}
32
+
33
+// Copy returns a copy of the Tree. Any parsing state is discarded.
34
+func (t *Tree) Copy() *Tree {
35
+ if t == nil {
36
+ return nil
37
+ }
38
+ return &Tree{
39
+ Name: t.Name,
40
+ ParseName: t.ParseName,
41
+ Root: t.Root.CopyList(),
42
+ text: t.text,
43
+ }
44
+}
45
+
46
+// Parse returns a map from template name to parse.Tree, created by parsing the
47
+// templates described in the argument string. The top-level template will be
48
+// given the specified name. If an error is encountered, parsing stops and an
49
+// empty map is returned with the error.
50
+func Parse(name, text, leftDelim, rightDelim string, funcs ...map[string]interface{}) (treeSet map[string]*Tree, err error) {
51
+ treeSet = make(map[string]*Tree)
52
+ t := New(name)
53
+ t.text = text
54
+ _, err = t.Parse(text, leftDelim, rightDelim, treeSet, funcs...)
55
+ return
56
+}
57
+
58
+// next returns the next token.
59
+func (t *Tree) next() item {
60
+ if t.peekCount > 0 {
61
+ t.peekCount--
62
+ } else {
63
+ t.token[0] = t.lex.nextItem()
64
+ }
65
+ return t.token[t.peekCount]
66
+}
67
+
68
+// backup backs the input stream up one token.
69
+func (t *Tree) backup() {
70
+ t.peekCount++
71
+}
72
+
73
+// backup2 backs the input stream up two tokens.
74
+// The zeroth token is already there.
75
+func (t *Tree) backup2(t1 item) {
76
+ t.token[1] = t1
77
+ t.peekCount = 2
78
+}
79
+
80
+// backup3 backs the input stream up three tokens
81
+// The zeroth token is already there.
82
+func (t *Tree) backup3(t2, t1 item) { // Reverse order: we're pushing back.
83
+ t.token[1] = t1
84
+ t.token[2] = t2
85
+ t.peekCount = 3
86
+}
87
+
88
+// peek returns but does not consume the next token.
89
+func (t *Tree) peek() item {
90
+ if t.peekCount > 0 {
91
+ return t.token[t.peekCount-1]
92
+ }
93
+ t.peekCount = 1
94
+ t.token[0] = t.lex.nextItem()
95
+ return t.token[0]
96
+}
97
+
98
+// nextNonSpace returns the next non-space token.
99
+func (t *Tree) nextNonSpace() (token item) {
100
+ for {
101
+ token = t.next()
102
+ if token.typ != itemSpace {
103
+ break
104
+ }
105
+ }
106
+ return token
107
+}
108
+
109
+// peekNonSpace returns but does not consume the next non-space token.
110
+func (t *Tree) peekNonSpace() (token item) {
111
+ for {
112
+ token = t.next()
113
+ if token.typ != itemSpace {
114
+ break
115
+ }
116
+ }
117
+ t.backup()
118
+ return token
119
+}
120
+
121
+// Parsing.
122
+
123
+// New allocates a new parse tree with the given name.
124
+func New(name string, funcs ...map[string]interface{}) *Tree {
125
+ return &Tree{
126
+ Name: name,
127
+ funcs: funcs,
128
+ }
129
+}
130
+
131
+// ErrorContext returns a textual representation of the location of the node in the input text.
132
+// The receiver is only used when the node does not have a pointer to the tree inside,
133
+// which can occur in old code.
134
+func (t *Tree) ErrorContext(n Node) (location, context string) {
135
+ pos := int(n.Position())
136
+ tree := n.tree()
137
+ if tree == nil {
138
+ tree = t
139
+ }
140
+ text := tree.text[:pos]
141
+ byteNum := strings.LastIndex(text, "\n")
142
+ if byteNum == -1 {
143
+ byteNum = pos // On first line.
144
+ } else {
145
+ byteNum++ // After the newline.
146
+ byteNum = pos - byteNum
147
+ }
148
+ lineNum := 1 + strings.Count(text, "\n")
149
+ context = n.String()
150
+ if len(context) > 20 {
151
+ context = fmt.Sprintf("%.20s...", context)
152
+ }
153
+ return fmt.Sprintf("%s:%d:%d", tree.ParseName, lineNum, byteNum), context
154
+}
155
+
156
+// errorf formats the error and terminates processing.
157
+func (t *Tree) errorf(format string, args ...interface{}) {
158
+ t.Root = nil
159
+ format = fmt.Sprintf("template: %s:%d: %s", t.ParseName, t.lex.lineNumber(), format)
160
+ panic(fmt.Errorf(format, args...))
161
+}
162
+
163
+// error terminates processing.
164
+func (t *Tree) error(err error) {
165
+ t.errorf("%s", err)
166
+}
167
+
168
+// expect consumes the next token and guarantees it has the required type.
169
+func (t *Tree) expect(expected itemType, context string) item {
170
+ token := t.nextNonSpace()
171
+ if token.typ != expected {
172
+ t.unexpected(token, context)
173
+ }
174
+ return token
175
+}
176
+
177
+// expectOneOf consumes the next token and guarantees it has one of the required types.
178
+func (t *Tree) expectOneOf(expected1, expected2 itemType, context string) item {
179
+ token := t.nextNonSpace()
180
+ if token.typ != expected1 && token.typ != expected2 {
181
+ t.unexpected(token, context)
182
+ }
183
+ return token
184
+}
185
+
186
+// unexpected complains about the token and terminates processing.
187
+func (t *Tree) unexpected(token item, context string) {
188
+ t.errorf("unexpected %s in %s", token, context)
189
+}
190
+
191
+// recover is the handler that turns panics into returns from the top level of Parse.
192
+func (t *Tree) recover(errp *error) {
193
+ e := recover()
194
+ if e != nil {
195
+ if _, ok := e.(runtime.Error); ok {
196
+ panic(e)
197
+ }
198
+ if t != nil {
199
+ t.stopParse()
200
+ }
201
+ *errp = e.(error)
202
+ }
203
+ return
204
+}
205
+
206
+// startParse initializes the parser, using the lexer.
207
+func (t *Tree) startParse(funcs []map[string]interface{}, lex *lexer) {
208
+ t.Root = nil
209
+ t.lex = lex
210
+ t.vars = []string{"$"}
211
+ t.funcs = funcs
212
+}
213
+
214
+// stopParse terminates parsing.
215
+func (t *Tree) stopParse() {
216
+ t.lex = nil
217
+ t.vars = nil
218
+ t.funcs = nil
219
+}
220
+
221
+// Parse parses the template definition string to construct a representation of
222
+// the template for execution. If either action delimiter string is empty, the
223
+// default ("{{" or "}}") is used. Embedded template definitions are added to
224
+// the treeSet map.
225
+func (t *Tree) Parse(text, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]interface{}) (tree *Tree, err error) {
226
+ defer t.recover(&err)
227
+ t.ParseName = t.Name
228
+ t.startParse(funcs, lex(t.Name, text, leftDelim, rightDelim))
229
+ t.text = text
230
+ t.parse(treeSet)
231
+ t.add(treeSet)
232
+ t.stopParse()
233
+ return t, nil
234
+}
235
+
236
+// add adds tree to the treeSet.
237
+func (t *Tree) add(treeSet map[string]*Tree) {
238
+ tree := treeSet[t.Name]
239
+ if tree == nil || IsEmptyTree(tree.Root) {
240
+ treeSet[t.Name] = t
241
+ return
242
+ }
243
+ if !IsEmptyTree(t.Root) {
244
+ t.errorf("template: multiple definition of template %q", t.Name)
245
+ }
246
+}
247
+
248
+// IsEmptyTree reports whether this tree (node) is empty of everything but space.
249
+func IsEmptyTree(n Node) bool {
250
+ switch n := n.(type) {
251
+ case nil:
252
+ return true
253
+ case *ActionNode:
254
+ case *IfNode:
255
+ case *ListNode:
256
+ for _, node := range n.Nodes {
257
+ if !IsEmptyTree(node) {
258
+ return false
259
+ }
260
+ }
261
+ return true
262
+ case *RangeNode:
263
+ case *TemplateNode:
264
+ case *TextNode:
265
+ return len(bytes.TrimSpace(n.Text)) == 0
266
+ case *WithNode:
267
+ default:
268
+ panic("unknown node: " + n.String())
269
+ }
270
+ return false
271
+}
272
+
273
+// parse is the top-level parser for a template, essentially the same
274
+// as itemList except it also parses {{define}} actions.
275
+// It runs to EOF.
276
+func (t *Tree) parse(treeSet map[string]*Tree) (next Node) {
277
+ t.Root = t.newList(t.peek().pos)
278
+ for t.peek().typ != itemEOF {
279
+ if t.peek().typ == itemLeftDelim {
280
+ delim := t.next()
281
+ if t.nextNonSpace().typ == itemDefine {
282
+ newT := New("definition") // name will be updated once we know it.
283
+ newT.text = t.text
284
+ newT.ParseName = t.ParseName
285
+ newT.startParse(t.funcs, t.lex)
286
+ newT.parseDefinition(treeSet)
287
+ continue
288
+ }
289
+ t.backup2(delim)
290
+ }
291
+ n := t.textOrAction()
292
+ if n.Type() == nodeEnd {
293
+ t.errorf("unexpected %s", n)
294
+ }
295
+ t.Root.append(n)
296
+ }
297
+ return nil
298
+}
299
+
300
+// parseDefinition parses a {{define}} ... {{end}} template definition and
301
+// installs the definition in the treeSet map. The "define" keyword has already
302
+// been scanned.
303
+func (t *Tree) parseDefinition(treeSet map[string]*Tree) {
304
+ const context = "define clause"
305
+ name := t.expectOneOf(itemString, itemRawString, context)
306
+ var err error
307
+ t.Name, err = strconv.Unquote(name.val)
308
+ if err != nil {
309
+ t.error(err)
310
+ }
311
+ t.expect(itemRightDelim, context)
312
+ var end Node
313
+ t.Root, end = t.itemList()
314
+ if end.Type() != nodeEnd {
315
+ t.errorf("unexpected %s in %s", end, context)
316
+ }
317
+ t.add(treeSet)
318
+ t.stopParse()
319
+}
320
+
321
+// itemList:
322
+// textOrAction*
323
+// Terminates at {{end}} or {{else}}, returned separately.
324
+func (t *Tree) itemList() (list *ListNode, next Node) {
325
+ list = t.newList(t.peekNonSpace().pos)
326
+ for t.peekNonSpace().typ != itemEOF {
327
+ n := t.textOrAction()
328
+ switch n.Type() {
329
+ case nodeEnd, nodeElse:
330
+ return list, n
331
+ }
332
+ list.append(n)
333
+ }
334
+ t.errorf("unexpected EOF")
335
+ return
336
+}
337
+
338
+// textOrAction:
339
+// text | action
340
+func (t *Tree) textOrAction() Node {
341
+ switch token := t.nextNonSpace(); token.typ {
342
+ case itemElideNewline:
343
+ return t.elideNewline()
344
+ case itemText:
345
+ return t.newText(token.pos, token.val)
346
+ case itemLeftDelim:
347
+ return t.action()
348
+ default:
349
+ t.unexpected(token, "input")
350
+ }
351
+ return nil
352
+}
353
+
354
+// elideNewline:
355
+// Remove newlines trailing rightDelim if \\ is present.
356
+func (t *Tree) elideNewline() Node {
357
+ token := t.peek()
358
+ if token.typ != itemText {
359
+ t.unexpected(token, "input")
360
+ return nil
361
+ }
362
+
363
+ t.next()
364
+ stripped := strings.TrimLeft(token.val, "\n\r")
365
+ diff := len(token.val) - len(stripped)
366
+ if diff > 0 {
367
+ // This is a bit nasty. We mutate the token in-place to remove
368
+ // preceding newlines.
369
+ token.pos += Pos(diff)
370
+ token.val = stripped
371
+ }
372
+ return t.newText(token.pos, token.val)
373
+}
374
+
375
+// Action:
376
+// control
377
+// command ("|" command)*
378
+// Left delim is past. Now get actions.
379
+// First word could be a keyword such as range.
380
+func (t *Tree) action() (n Node) {
381
+ switch token := t.nextNonSpace(); token.typ {
382
+ case itemElse:
383
+ return t.elseControl()
384
+ case itemEnd:
385
+ return t.endControl()
386
+ case itemIf:
387
+ return t.ifControl()
388
+ case itemRange:
389
+ return t.rangeControl()
390
+ case itemTemplate:
391
+ return t.templateControl()
392
+ case itemWith:
393
+ return t.withControl()
394
+ }
395
+ t.backup()
396
+ // Do not pop variables; they persist until "end".
397
+ return t.newAction(t.peek().pos, t.lex.lineNumber(), t.pipeline("command"))
398
+}
399
+
400
+// Pipeline:
401
+// declarations? command ('|' command)*
402
+func (t *Tree) pipeline(context string) (pipe *PipeNode) {
403
+ var decl []*VariableNode
404
+ pos := t.peekNonSpace().pos
405
+ // Are there declarations?
406
+ for {
407
+ if v := t.peekNonSpace(); v.typ == itemVariable {
408
+ t.next()
409
+ // Since space is a token, we need 3-token look-ahead here in the worst case:
410
+ // in "$x foo" we need to read "foo" (as opposed to ":=") to know that $x is an
411
+ // argument variable rather than a declaration. So remember the token
412
+ // adjacent to the variable so we can push it back if necessary.
413
+ tokenAfterVariable := t.peek()
414
+ if next := t.peekNonSpace(); next.typ == itemColonEquals || (next.typ == itemChar && next.val == ",") {
415
+ t.nextNonSpace()
416
+ variable := t.newVariable(v.pos, v.val)
417
+ decl = append(decl, variable)
418
+ t.vars = append(t.vars, v.val)
419
+ if next.typ == itemChar && next.val == "," {
420
+ if context == "range" && len(decl) < 2 {
421
+ continue
422
+ }
423
+ t.errorf("too many declarations in %s", context)
424
+ }
425
+ } else if tokenAfterVariable.typ == itemSpace {
426
+ t.backup3(v, tokenAfterVariable)
427
+ } else {
428
+ t.backup2(v)
429
+ }
430
+ }
431
+ break
432
+ }
433
+ pipe = t.newPipeline(pos, t.lex.lineNumber(), decl)
434
+ for {
435
+ switch token := t.nextNonSpace(); token.typ {
436
+ case itemRightDelim, itemRightParen:
437
+ if len(pipe.Cmds) == 0 {
438
+ t.errorf("missing value for %s", context)
439
+ }
440
+ if token.typ == itemRightParen {
441
+ t.backup()
442
+ }
443
+ return
444
+ case itemBool, itemCharConstant, itemComplex, itemDot, itemField, itemIdentifier,
445
+ itemNumber, itemNil, itemRawString, itemString, itemVariable, itemLeftParen:
446
+ t.backup()
447
+ pipe.append(t.command())
448
+ default:
449
+ t.unexpected(token, context)
450
+ }
451
+ }
452
+}
453
+
454
+func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) {
455
+ defer t.popVars(len(t.vars))
456
+ line = t.lex.lineNumber()
457
+ pipe = t.pipeline(context)
458
+ var next Node
459
+ list, next = t.itemList()
460
+ switch next.Type() {
461
+ case nodeEnd: //done
462
+ case nodeElse:
463
+ if allowElseIf {
464
+ // Special case for "else if". If the "else" is followed immediately by an "if",
465
+ // the elseControl will have left the "if" token pending. Treat
466
+ // {{if a}}_{{else if b}}_{{end}}
467
+ // as
468
+ // {{if a}}_{{else}}{{if b}}_{{end}}{{end}}.
469
+ // To do this, parse the if as usual and stop at it {{end}}; the subsequent{{end}}
470
+ // is assumed. This technique works even for long if-else-if chains.
471
+ // TODO: Should we allow else-if in with and range?
472
+ if t.peek().typ == itemIf {
473
+ t.next() // Consume the "if" token.
474
+ elseList = t.newList(next.Position())
475
+ elseList.append(t.ifControl())
476
+ // Do not consume the next item - only one {{end}} required.
477
+ break
478
+ }
479
+ }
480
+ elseList, next = t.itemList()
481
+ if next.Type() != nodeEnd {
482
+ t.errorf("expected end; found %s", next)
483
+ }
484
+ }
485
+ return pipe.Position(), line, pipe, list, elseList
486
+}
487
+
488
+// If:
489
+// {{if pipeline}} itemList {{end}}
490
+// {{if pipeline}} itemList {{else}} itemList {{end}}
491
+// If keyword is past.
492
+func (t *Tree) ifControl() Node {
493
+ return t.newIf(t.parseControl(true, "if"))
494
+}
495
+
496
+// Range:
497
+// {{range pipeline}} itemList {{end}}
498
+// {{range pipeline}} itemList {{else}} itemList {{end}}
499
+// Range keyword is past.
500
+func (t *Tree) rangeControl() Node {
501
+ return t.newRange(t.parseControl(false, "range"))
502
+}
503
+
504
+// With:
505
+// {{with pipeline}} itemList {{end}}
506
+// {{with pipeline}} itemList {{else}} itemList {{end}}
507
+// If keyword is past.
508
+func (t *Tree) withControl() Node {
509
+ return t.newWith(t.parseControl(false, "with"))
510
+}
511
+
512
+// End:
513
+// {{end}}
514
+// End keyword is past.
515
+func (t *Tree) endControl() Node {
516
+ return t.newEnd(t.expect(itemRightDelim, "end").pos)
517
+}
518
+
519
+// Else:
520
+// {{else}}
521
+// Else keyword is past.
522
+func (t *Tree) elseControl() Node {
523
+ // Special case for "else if".
524
+ peek := t.peekNonSpace()
525
+ if peek.typ == itemIf {
526
+ // We see "{{else if ... " but in effect rewrite it to {{else}}{{if ... ".
527
+ return t.newElse(peek.pos, t.lex.lineNumber())
528
+ }
529
+ return t.newElse(t.expect(itemRightDelim, "else").pos, t.lex.lineNumber())
530
+}
531
+
532
+// Template:
533
+// {{template stringValue pipeline}}
534
+// Template keyword is past. The name must be something that can evaluate
535
+// to a string.
536
+func (t *Tree) templateControl() Node {
537
+ var name string
538
+ token := t.nextNonSpace()
539
+ switch token.typ {
540
+ case itemString, itemRawString:
541
+ s, err := strconv.Unquote(token.val)
542
+ if err != nil {
543
+ t.error(err)
544
+ }
545
+ name = s
546
+ default:
547
+ t.unexpected(token, "template invocation")
548
+ }
549
+ var pipe *PipeNode
550
+ if t.nextNonSpace().typ != itemRightDelim {
551
+ t.backup()
552
+ // Do not pop variables; they persist until "end".
553
+ pipe = t.pipeline("template")
554
+ }
555
+ return t.newTemplate(token.pos, t.lex.lineNumber(), name, pipe)
556
+}
557
+
558
+// command:
559
+// operand (space operand)*
560
+// space-separated arguments up to a pipeline character or right delimiter.
561
+// we consume the pipe character but leave the right delim to terminate the action.
562
+func (t *Tree) command() *CommandNode {
563
+ cmd := t.newCommand(t.peekNonSpace().pos)
564
+ for {
565
+ t.peekNonSpace() // skip leading spaces.
566
+ operand := t.operand()
567
+ if operand != nil {
568
+ cmd.append(operand)
569
+ }
570
+ switch token := t.next(); token.typ {
571
+ case itemSpace:
572
+ continue
573
+ case itemError:
574
+ t.errorf("%s", token.val)
575
+ case itemRightDelim, itemRightParen:
576
+ t.backup()
577
+ case itemPipe:
578
+ default:
579
+ t.errorf("unexpected %s in operand; missing space?", token)
580
+ }
581
+ break
582
+ }
583
+ if len(cmd.Args) == 0 {
584
+ t.errorf("empty command")
585
+ }
586
+ return cmd
587
+}
588
+
589
+// operand:
590
+// term .Field*
591
+// An operand is a space-separated component of a command,
592
+// a term possibly followed by field accesses.
593
+// A nil return means the next item is not an operand.
594
+func (t *Tree) operand() Node {
595
+ node := t.term()
596
+ if node == nil {
597
+ return nil
598
+ }
599
+ if t.peek().typ == itemField {
600
+ chain := t.newChain(t.peek().pos, node)
601
+ for t.peek().typ == itemField {
602
+ chain.Add(t.next().val)
603
+ }
604
+ // Compatibility with original API: If the term is of type NodeField
605
+ // or NodeVariable, just put more fields on the original.
606
+ // Otherwise, keep the Chain node.
607
+ // TODO: Switch to Chains always when we can.
608
+ switch node.Type() {
609
+ case NodeField:
610
+ node = t.newField(chain.Position(), chain.String())
611
+ case NodeVariable:
612
+ node = t.newVariable(chain.Position(), chain.String())
613
+ default:
614
+ node = chain
615
+ }
616
+ }
617
+ return node
618
+}
619
+
620
+// term:
621
+// literal (number, string, nil, boolean)
622
+// function (identifier)
623
+// .
624
+// .Field
625
+// $
626
+// '(' pipeline ')'
627
+// A term is a simple "expression".
628
+// A nil return means the next item is not a term.
629
+func (t *Tree) term() Node {
630
+ switch token := t.nextNonSpace(); token.typ {
631
+ case itemError:
632
+ t.errorf("%s", token.val)
633
+ case itemIdentifier:
634
+ if !t.hasFunction(token.val) {
635
+ t.errorf("function %q not defined", token.val)
636
+ }
637
+ return NewIdentifier(token.val).SetTree(t).SetPos(token.pos)
638
+ case itemDot:
639
+ return t.newDot(token.pos)
640
+ case itemNil:
641
+ return t.newNil(token.pos)
642
+ case itemVariable:
643
+ return t.useVar(token.pos, token.val)
644
+ case itemField:
645
+ return t.newField(token.pos, token.val)
646
+ case itemBool:
647
+ return t.newBool(token.pos, token.val == "true")
648
+ case itemCharConstant, itemComplex, itemNumber:
649
+ number, err := t.newNumber(token.pos, token.val, token.typ)
650
+ if err != nil {
651
+ t.error(err)
652
+ }
653
+ return number
654
+ case itemLeftParen:
655
+ pipe := t.pipeline("parenthesized pipeline")
656
+ if token := t.next(); token.typ != itemRightParen {
657
+ t.errorf("unclosed right paren: unexpected %s", token)
658
+ }
659
+ return pipe
660
+ case itemString, itemRawString:
661
+ s, err := strconv.Unquote(token.val)
662
+ if err != nil {
663
+ t.error(err)
664
+ }
665
+ return t.newString(token.pos, token.val, s)
666
+ }
667
+ t.backup()
668
+ return nil
669
+}
670
+
671
+// hasFunction reports if a function name exists in the Tree's maps.
672
+func (t *Tree) hasFunction(name string) bool {
673
+ for _, funcMap := range t.funcs {
674
+ if funcMap == nil {
675
+ continue
676
+ }
677
+ if funcMap[name] != nil {
678
+ return true
679
+ }
680
+ }
681
+ return false
682
+}
683
+
684
+// popVars trims the variable list to the specified length
685
+func (t *Tree) popVars(n int) {
686
+ t.vars = t.vars[:n]
687
+}
688
+
689
+// useVar returns a node for a variable reference. It errors if the
690
+// variable is not defined.
691
+func (t *Tree) useVar(pos Pos, name string) Node {
692
+ v := t.newVariable(pos, name)
693
+ for _, varName := range t.vars {
694
+ if varName == v.Ident[0] {
695
+ return v
696
+ }
697
+ }
698
+ t.errorf("undefined variable %q", v.Ident[0])
699
+ return nil
700
+}
Godeps/_workspace/src/github.com/alecthomas/template/parse/parse_test.go
new
+426
@@ -0,0 +1,426 @@
1
+// Copyright 2011 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 parse
6
+
7
+import (
8
+ "flag"
9
+ "fmt"
10
+ "strings"
11
+ "testing"
12
+)
13
+
14
+var debug = flag.Bool("debug", false, "show the errors produced by the main tests")
15
+
16
+type numberTest struct {
17
+ text string
18
+ isInt bool
19
+ isUint bool
20
+ isFloat bool
21
+ isComplex bool
22
+ int64
23
+ uint64
24
+ float64
25
+ complex128
26
+}
27
+
28
+var numberTests = []numberTest{
29
+ // basics
30
+ {"0", true, true, true, false, 0, 0, 0, 0},
31
+ {"-0", true, true, true, false, 0, 0, 0, 0}, // check that -0 is a uint.
32
+ {"73", true, true, true, false, 73, 73, 73, 0},
33
+ {"073", true, true, true, false, 073, 073, 073, 0},
34
+ {"0x73", true, true, true, false, 0x73, 0x73, 0x73, 0},
35
+ {"-73", true, false, true, false, -73, 0, -73, 0},
36
+ {"+73", true, false, true, false, 73, 0, 73, 0},
37
+ {"100", true, true, true, false, 100, 100, 100, 0},
38
+ {"1e9", true, true, true, false, 1e9, 1e9, 1e9, 0},
39
+ {"-1e9", true, false, true, false, -1e9, 0, -1e9, 0},
40
+ {"-1.2", false, false, true, false, 0, 0, -1.2, 0},
41
+ {"1e19", false, true, true, false, 0, 1e19, 1e19, 0},
42
+ {"-1e19", false, false, true, false, 0, 0, -1e19, 0},
43
+ {"4i", false, false, false, true, 0, 0, 0, 4i},
44
+ {"-1.2+4.2i", false, false, false, true, 0, 0, 0, -1.2 + 4.2i},
45
+ {"073i", false, false, false, true, 0, 0, 0, 73i}, // not octal!
46
+ // complex with 0 imaginary are float (and maybe integer)
47
+ {"0i", true, true, true, true, 0, 0, 0, 0},
48
+ {"-1.2+0i", false, false, true, true, 0, 0, -1.2, -1.2},
49
+ {"-12+0i", true, false, true, true, -12, 0, -12, -12},
50
+ {"13+0i", true, true, true, true, 13, 13, 13, 13},
51
+ // funny bases
52
+ {"0123", true, true, true, false, 0123, 0123, 0123, 0},
53
+ {"-0x0", true, true, true, false, 0, 0, 0, 0},
54
+ {"0xdeadbeef", true, true, true, false, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0},
55
+ // character constants
56
+ {`'a'`, true, true, true, false, 'a', 'a', 'a', 0},
57
+ {`'\n'`, true, true, true, false, '\n', '\n', '\n', 0},
58
+ {`'\\'`, true, true, true, false, '\\', '\\', '\\', 0},
59
+ {`'\''`, true, true, true, false, '\'', '\'', '\'', 0},
60
+ {`'\xFF'`, true, true, true, false, 0xFF, 0xFF, 0xFF, 0},
61
+ {`'パ'`, true, true, true, false, 0x30d1, 0x30d1, 0x30d1, 0},
62
+ {`'\u30d1'`, true, true, true, false, 0x30d1, 0x30d1, 0x30d1, 0},
63
+ {`'\U000030d1'`, true, true, true, false, 0x30d1, 0x30d1, 0x30d1, 0},
64
+ // some broken syntax
65
+ {text: "+-2"},
66
+ {text: "0x123."},
67
+ {text: "1e."},
68
+ {text: "0xi."},
69
+ {text: "1+2."},
70
+ {text: "'x"},
71
+ {text: "'xx'"},
72
+ // Issue 8622 - 0xe parsed as floating point. Very embarrassing.
73
+ {"0xef", true, true, true, false, 0xef, 0xef, 0xef, 0},
74
+}
75
+
76
+func TestNumberParse(t *testing.T) {
77
+ for _, test := range numberTests {
78
+ // If fmt.Sscan thinks it's complex, it's complex. We can't trust the output
79
+ // because imaginary comes out as a number.
80
+ var c complex128
81
+ typ := itemNumber
82
+ var tree *Tree
83
+ if test.text[0] == '\'' {
84
+ typ = itemCharConstant
85
+ } else {
86
+ _, err := fmt.Sscan(test.text, &c)
87
+ if err == nil {
88
+ typ = itemComplex
89
+ }
90
+ }
91
+ n, err := tree.newNumber(0, test.text, typ)
92
+ ok := test.isInt || test.isUint || test.isFloat || test.isComplex
93
+ if ok && err != nil {
94
+ t.Errorf("unexpected error for %q: %s", test.text, err)
95
+ continue
96
+ }
97
+ if !ok && err == nil {
98
+ t.Errorf("expected error for %q", test.text)
99
+ continue
100
+ }
101
+ if !ok {
102
+ if *debug {
103
+ fmt.Printf("%s\n\t%s\n", test.text, err)
104
+ }
105
+ continue
106
+ }
107
+ if n.IsComplex != test.isComplex {
108
+ t.Errorf("complex incorrect for %q; should be %t", test.text, test.isComplex)
109
+ }
110
+ if test.isInt {
111
+ if !n.IsInt {
112
+ t.Errorf("expected integer for %q", test.text)
113
+ }
114
+ if n.Int64 != test.int64 {
115
+ t.Errorf("int64 for %q should be %d Is %d", test.text, test.int64, n.Int64)
116
+ }
117
+ } else if n.IsInt {
118
+ t.Errorf("did not expect integer for %q", test.text)
119
+ }
120
+ if test.isUint {
121
+ if !n.IsUint {
122
+ t.Errorf("expected unsigned integer for %q", test.text)
123
+ }
124
+ if n.Uint64 != test.uint64 {
125
+ t.Errorf("uint64 for %q should be %d Is %d", test.text, test.uint64, n.Uint64)
126
+ }
127
+ } else if n.IsUint {
128
+ t.Errorf("did not expect unsigned integer for %q", test.text)
129
+ }
130
+ if test.isFloat {
131
+ if !n.IsFloat {
132
+ t.Errorf("expected float for %q", test.text)
133
+ }
134
+ if n.Float64 != test.float64 {
135
+ t.Errorf("float64 for %q should be %g Is %g", test.text, test.float64, n.Float64)
136
+ }
137
+ } else if n.IsFloat {
138
+ t.Errorf("did not expect float for %q", test.text)
139
+ }
140
+ if test.isComplex {
141
+ if !n.IsComplex {
142
+ t.Errorf("expected complex for %q", test.text)
143
+ }
144
+ if n.Complex128 != test.complex128 {
145
+ t.Errorf("complex128 for %q should be %g Is %g", test.text, test.complex128, n.Complex128)
146
+ }
147
+ } else if n.IsComplex {
148
+ t.Errorf("did not expect complex for %q", test.text)
149
+ }
150
+ }
151
+}
152
+
153
+type parseTest struct {
154
+ name string
155
+ input string
156
+ ok bool
157
+ result string // what the user would see in an error message.
158
+}
159
+
160
+const (
161
+ noError = true
162
+ hasError = false
163
+)
164
+
165
+var parseTests = []parseTest{
166
+ {"empty", "", noError,
167
+ ``},
168
+ {"comment", "{{/*\n\n\n*/}}", noError,
169
+ ``},
170
+ {"spaces", " \t\n", noError,
171
+ `" \t\n"`},
172
+ {"text", "some text", noError,
173
+ `"some text"`},
174
+ {"emptyAction", "{{}}", hasError,
175
+ `{{}}`},
176
+ {"field", "{{.X}}", noError,
177
+ `{{.X}}`},
178
+ {"simple command", "{{printf}}", noError,
179
+ `{{printf}}`},
180
+ {"$ invocation", "{{$}}", noError,
181
+ "{{$}}"},
182
+ {"variable invocation", "{{with $x := 3}}{{$x 23}}{{end}}", noError,
183
+ "{{with $x := 3}}{{$x 23}}{{end}}"},
184
+ {"variable with fields", "{{$.I}}", noError,
185
+ "{{$.I}}"},
186
+ {"multi-word command", "{{printf `%d` 23}}", noError,
187
+ "{{printf `%d` 23}}"},
188
+ {"pipeline", "{{.X|.Y}}", noError,
189
+ `{{.X | .Y}}`},
190
+ {"pipeline with decl", "{{$x := .X|.Y}}", noError,
191
+ `{{$x := .X | .Y}}`},
192
+ {"nested pipeline", "{{.X (.Y .Z) (.A | .B .C) (.E)}}", noError,
193
+ `{{.X (.Y .Z) (.A | .B .C) (.E)}}`},
194
+ {"field applied to parentheses", "{{(.Y .Z).Field}}", noError,
195
+ `{{(.Y .Z).Field}}`},
196
+ {"simple if", "{{if .X}}hello{{end}}", noError,
197
+ `{{if .X}}"hello"{{end}}`},
198
+ {"if with else", "{{if .X}}true{{else}}false{{end}}", noError,
199
+ `{{if .X}}"true"{{else}}"false"{{end}}`},
200
+ {"if with else if", "{{if .X}}true{{else if .Y}}false{{end}}", noError,
201
+ `{{if .X}}"true"{{else}}{{if .Y}}"false"{{end}}{{end}}`},
202
+ {"if else chain", "+{{if .X}}X{{else if .Y}}Y{{else if .Z}}Z{{end}}+", noError,
203
+ `"+"{{if .X}}"X"{{else}}{{if .Y}}"Y"{{else}}{{if .Z}}"Z"{{end}}{{end}}{{end}}"+"`},
204
+ {"simple range", "{{range .X}}hello{{end}}", noError,
205
+ `{{range .X}}"hello"{{end}}`},
206
+ {"chained field range", "{{range .X.Y.Z}}hello{{end}}", noError,
207
+ `{{range .X.Y.Z}}"hello"{{end}}`},
208
+ {"nested range", "{{range .X}}hello{{range .Y}}goodbye{{end}}{{end}}", noError,
209
+ `{{range .X}}"hello"{{range .Y}}"goodbye"{{end}}{{end}}`},
210
+ {"range with else", "{{range .X}}true{{else}}false{{end}}", noError,
211
+ `{{range .X}}"true"{{else}}"false"{{end}}`},
212
+ {"range over pipeline", "{{range .X|.M}}true{{else}}false{{end}}", noError,
213
+ `{{range .X | .M}}"true"{{else}}"false"{{end}}`},
214
+ {"range []int", "{{range .SI}}{{.}}{{end}}", noError,
215
+ `{{range .SI}}{{.}}{{end}}`},
216
+ {"range 1 var", "{{range $x := .SI}}{{.}}{{end}}", noError,
217
+ `{{range $x := .SI}}{{.}}{{end}}`},
218
+ {"range 2 vars", "{{range $x, $y := .SI}}{{.}}{{end}}", noError,
219
+ `{{range $x, $y := .SI}}{{.}}{{end}}`},
220
+ {"constants", "{{range .SI 1 -3.2i true false 'a' nil}}{{end}}", noError,
221
+ `{{range .SI 1 -3.2i true false 'a' nil}}{{end}}`},
222
+ {"template", "{{template `x`}}", noError,
223
+ `{{template "x"}}`},
224
+ {"template with arg", "{{template `x` .Y}}", noError,
225
+ `{{template "x" .Y}}`},
226
+ {"with", "{{with .X}}hello{{end}}", noError,
227
+ `{{with .X}}"hello"{{end}}`},
228
+ {"with with else", "{{with .X}}hello{{else}}goodbye{{end}}", noError,
229
+ `{{with .X}}"hello"{{else}}"goodbye"{{end}}`},
230
+ {"elide newline", "{{true}}\\\n ", noError,
231
+ `{{true}}" "`},
232
+ // Errors.
233
+ {"unclosed action", "hello{{range", hasError, ""},
234
+ {"unmatched end", "{{end}}", hasError, ""},
235
+ {"missing end", "hello{{range .x}}", hasError, ""},
236
+ {"missing end after else", "hello{{range .x}}{{else}}", hasError, ""},
237
+ {"undefined function", "hello{{undefined}}", hasError, ""},
238
+ {"undefined variable", "{{$x}}", hasError, ""},
239
+ {"variable undefined after end", "{{with $x := 4}}{{end}}{{$x}}", hasError, ""},
240
+ {"variable undefined in template", "{{template $v}}", hasError, ""},
241
+ {"declare with field", "{{with $x.Y := 4}}{{end}}", hasError, ""},
242
+ {"template with field ref", "{{template .X}}", hasError, ""},
243
+ {"template with var", "{{template $v}}", hasError, ""},
244
+ {"invalid punctuation", "{{printf 3, 4}}", hasError, ""},
245
+ {"multidecl outside range", "{{with $v, $u := 3}}{{end}}", hasError, ""},
246
+ {"too many decls in range", "{{range $u, $v, $w := 3}}{{end}}", hasError, ""},
247
+ {"dot applied to parentheses", "{{printf (printf .).}}", hasError, ""},
248
+ {"adjacent args", "{{printf 3`x`}}", hasError, ""},
249
+ {"adjacent args with .", "{{printf `x`.}}", hasError, ""},
250
+ {"extra end after if", "{{if .X}}a{{else if .Y}}b{{end}}{{end}}", hasError, ""},
251
+ {"invalid newline elision", "{{true}}\\{{true}}", hasError, ""},
252
+ // Equals (and other chars) do not assignments make (yet).
253
+ {"bug0a", "{{$x := 0}}{{$x}}", noError, "{{$x := 0}}{{$x}}"},
254
+ {"bug0b", "{{$x = 1}}{{$x}}", hasError, ""},
255
+ {"bug0c", "{{$x ! 2}}{{$x}}", hasError, ""},
256
+ {"bug0d", "{{$x % 3}}{{$x}}", hasError, ""},
257
+ // Check the parse fails for := rather than comma.
258
+ {"bug0e", "{{range $x := $y := 3}}{{end}}", hasError, ""},
259
+ // Another bug: variable read must ignore following punctuation.
260
+ {"bug1a", "{{$x:=.}}{{$x!2}}", hasError, ""}, // ! is just illegal here.
261
+ {"bug1b", "{{$x:=.}}{{$x+2}}", hasError, ""}, // $x+2 should not parse as ($x) (+2).
262
+ {"bug1c", "{{$x:=.}}{{$x +2}}", noError, "{{$x := .}}{{$x +2}}"}, // It's OK with a space.
263
+}
264
+
265
+var builtins = map[string]interface{}{
266
+ "printf": fmt.Sprintf,
267
+}
268
+
269
+func testParse(doCopy bool, t *testing.T) {
270
+ textFormat = "%q"
271
+ defer func() { textFormat = "%s" }()
272
+ for _, test := range parseTests {
273
+ tmpl, err := New(test.name).Parse(test.input, "", "", make(map[string]*Tree), builtins)
274
+ switch {
275
+ case err == nil && !test.ok:
276
+ t.Errorf("%q: expected error; got none", test.name)
277
+ continue
278
+ case err != nil && test.ok:
279
+ t.Errorf("%q: unexpected error: %v", test.name, err)
280
+ continue
281
+ case err != nil && !test.ok:
282
+ // expected error, got one
283
+ if *debug {
284
+ fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err)
285
+ }
286
+ continue
287
+ }
288
+ var result string
289
+ if doCopy {
290
+ result = tmpl.Root.Copy().String()
291
+ } else {
292
+ result = tmpl.Root.String()
293
+ }
294
+ if result != test.result {
295
+ t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.result)
296
+ }
297
+ }
298
+}
299
+
300
+func TestParse(t *testing.T) {
301
+ testParse(false, t)
302
+}
303
+
304
+// Same as TestParse, but we copy the node first
305
+func TestParseCopy(t *testing.T) {
306
+ testParse(true, t)
307
+}
308
+
309
+type isEmptyTest struct {
310
+ name string
311
+ input string
312
+ empty bool
313
+}
314
+
315
+var isEmptyTests = []isEmptyTest{
316
+ {"empty", ``, true},
317
+ {"nonempty", `hello`, false},
318
+ {"spaces only", " \t\n \t\n", true},
319
+ {"definition", `{{define "x"}}something{{end}}`, true},
320
+ {"definitions and space", "{{define `x`}}something{{end}}\n\n{{define `y`}}something{{end}}\n\n", true},
321
+ {"definitions and text", "{{define `x`}}something{{end}}\nx\n{{define `y`}}something{{end}}\ny\n", false},
322
+ {"definition and action", "{{define `x`}}something{{end}}{{if 3}}foo{{end}}", false},
323
+}
324
+
325
+func TestIsEmpty(t *testing.T) {
326
+ if !IsEmptyTree(nil) {
327
+ t.Errorf("nil tree is not empty")
328
+ }
329
+ for _, test := range isEmptyTests {
330
+ tree, err := New("root").Parse(test.input, "", "", make(map[string]*Tree), nil)
331
+ if err != nil {
332
+ t.Errorf("%q: unexpected error: %v", test.name, err)
333
+ continue
334
+ }
335
+ if empty := IsEmptyTree(tree.Root); empty != test.empty {
336
+ t.Errorf("%q: expected %t got %t", test.name, test.empty, empty)
337
+ }
338
+ }
339
+}
340
+
341
+func TestErrorContextWithTreeCopy(t *testing.T) {
342
+ tree, err := New("root").Parse("{{if true}}{{end}}", "", "", make(map[string]*Tree), nil)
343
+ if err != nil {
344
+ t.Fatalf("unexpected tree parse failure: %v", err)
345
+ }
346
+ treeCopy := tree.Copy()
347
+ wantLocation, wantContext := tree.ErrorContext(tree.Root.Nodes[0])
348
+ gotLocation, gotContext := treeCopy.ErrorContext(treeCopy.Root.Nodes[0])
349
+ if wantLocation != gotLocation {
350
+ t.Errorf("wrong error location want %q got %q", wantLocation, gotLocation)
351
+ }
352
+ if wantContext != gotContext {
353
+ t.Errorf("wrong error location want %q got %q", wantContext, gotContext)
354
+ }
355
+}
356
+
357
+// All failures, and the result is a string that must appear in the error message.
358
+var errorTests = []parseTest{
359
+ // Check line numbers are accurate.
360
+ {"unclosed1",
361
+ "line1\n{{",
362
+ hasError, `unclosed1:2: unexpected unclosed action in command`},
363
+ {"unclosed2",
364
+ "line1\n{{define `x`}}line2\n{{",
365
+ hasError, `unclosed2:3: unexpected unclosed action in command`},
366
+ // Specific errors.
367
+ {"function",
368
+ "{{foo}}",
369
+ hasError, `function "foo" not defined`},
370
+ {"comment",
371
+ "{{/*}}",
372
+ hasError, `unclosed comment`},
373
+ {"lparen",
374
+ "{{.X (1 2 3}}",
375
+ hasError, `unclosed left paren`},
376
+ {"rparen",
377
+ "{{.X 1 2 3)}}",
378
+ hasError, `unexpected ")"`},
379
+ {"space",
380
+ "{{`x`3}}",
381
+ hasError, `missing space?`},
382
+ {"idchar",
383
+ "{{a#}}",
384
+ hasError, `'#'`},
385
+ {"charconst",
386
+ "{{'a}}",
387
+ hasError, `unterminated character constant`},
388
+ {"stringconst",
389
+ `{{"a}}`,
390
+ hasError, `unterminated quoted string`},
391
+ {"rawstringconst",
392
+ "{{`a}}",
393
+ hasError, `unterminated raw quoted string`},
394
+ {"number",
395
+ "{{0xi}}",
396
+ hasError, `number syntax`},
397
+ {"multidefine",
398
+ "{{define `a`}}a{{end}}{{define `a`}}b{{end}}",
399
+ hasError, `multiple definition of template`},
400
+ {"eof",
401
+ "{{range .X}}",
402
+ hasError, `unexpected EOF`},
403
+ {"variable",
404
+ // Declare $x so it's defined, to avoid that error, and then check we don't parse a declaration.
405
+ "{{$x := 23}}{{with $x.y := 3}}{{$x 23}}{{end}}",
406
+ hasError, `unexpected ":="`},
407
+ {"multidecl",
408
+ "{{$a,$b,$c := 23}}",
409
+ hasError, `too many declarations`},
410
+ {"undefvar",
411
+ "{{$a}}",
412
+ hasError, `undefined variable`},
413
+}
414
+
415
+func TestErrors(t *testing.T) {
416
+ for _, test := range errorTests {
417
+ _, err := New(test.name).Parse(test.input, "", "", make(map[string]*Tree))
418
+ if err == nil {
419
+ t.Errorf("%q: expected error", test.name)
420
+ continue
421
+ }
422
+ if !strings.Contains(err.Error(), test.result) {
423
+ t.Errorf("%q: error %q does not contain %q", test.name, err, test.result)
424
+ }
425
+ }
426
+}
Godeps/_workspace/src/github.com/alecthomas/template/template.go
new
+216
@@ -0,0 +1,216 @@
1
+// Copyright 2011 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 template
6
+
7
+import (
8
+ "fmt"
9
+ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/alecthomas/template/parse" // common holds the information shared by related templates.
10
+ "reflect"
11
+)
12
+
13
+type common struct {
14
+ tmpl map[string]*Template
15
+ // We use two maps, one for parsing and one for execution.
16
+ // This separation makes the API cleaner since it doesn't
17
+ // expose reflection to the client.
18
+ parseFuncs FuncMap
19
+ execFuncs map[string]reflect.Value
20
+}
21
+
22
+// Template is the representation of a parsed template. The *parse.Tree
23
+// field is exported only for use by html/template and should be treated
24
+// as unexported by all other clients.
25
+type Template struct {
26
+ name string
27
+ *parse.Tree
28
+ *common
29
+ leftDelim string
30
+ rightDelim string
31
+}
32
+
33
+// New allocates a new template with the given name.
34
+func New(name string) *Template {
35
+ return &Template{
36
+ name: name,
37
+ }
38
+}
39
+
40
+// Name returns the name of the template.
41
+func (t *Template) Name() string {
42
+ return t.name
43
+}
44
+
45
+// New allocates a new template associated with the given one and with the same
46
+// delimiters. The association, which is transitive, allows one template to
47
+// invoke another with a {{template}} action.
48
+func (t *Template) New(name string) *Template {
49
+ t.init()
50
+ return &Template{
51
+ name: name,
52
+ common: t.common,
53
+ leftDelim: t.leftDelim,
54
+ rightDelim: t.rightDelim,
55
+ }
56
+}
57
+
58
+func (t *Template) init() {
59
+ if t.common == nil {
60
+ t.common = new(common)
61
+ t.tmpl = make(map[string]*Template)
62
+ t.parseFuncs = make(FuncMap)
63
+ t.execFuncs = make(map[string]reflect.Value)
64
+ }
65
+}
66
+
67
+// Clone returns a duplicate of the template, including all associated
68
+// templates. The actual representation is not copied, but the name space of
69
+// associated templates is, so further calls to Parse in the copy will add
70
+// templates to the copy but not to the original. Clone can be used to prepare
71
+// common templates and use them with variant definitions for other templates
72
+// by adding the variants after the clone is made.
73
+func (t *Template) Clone() (*Template, error) {
74
+ nt := t.copy(nil)
75
+ nt.init()
76
+ nt.tmpl[t.name] = nt
77
+ for k, v := range t.tmpl {
78
+ if k == t.name { // Already installed.
79
+ continue
80
+ }
81
+ // The associated templates share nt's common structure.
82
+ tmpl := v.copy(nt.common)
83
+ nt.tmpl[k] = tmpl
84
+ }
85
+ for k, v := range t.parseFuncs {
86
+ nt.parseFuncs[k] = v
87
+ }
88
+ for k, v := range t.execFuncs {
89
+ nt.execFuncs[k] = v
90
+ }
91
+ return nt, nil
92
+}
93
+
94
+// copy returns a shallow copy of t, with common set to the argument.
95
+func (t *Template) copy(c *common) *Template {
96
+ nt := New(t.name)
97
+ nt.Tree = t.Tree
98
+ nt.common = c
99
+ nt.leftDelim = t.leftDelim
100
+ nt.rightDelim = t.rightDelim
101
+ return nt
102
+}
103
+
104
+// AddParseTree creates a new template with the name and parse tree
105
+// and associates it with t.
106
+func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) {
107
+ if t.common != nil && t.tmpl[name] != nil {
108
+ return nil, fmt.Errorf("template: redefinition of template %q", name)
109
+ }
110
+ nt := t.New(name)
111
+ nt.Tree = tree
112
+ t.tmpl[name] = nt
113
+ return nt, nil
114
+}
115
+
116
+// Templates returns a slice of the templates associated with t, including t
117
+// itself.
118
+func (t *Template) Templates() []*Template {
119
+ if t.common == nil {
120
+ return nil
121
+ }
122
+ // Return a slice so we don't expose the map.
123
+ m := make([]*Template, 0, len(t.tmpl))
124
+ for _, v := range t.tmpl {
125
+ m = append(m, v)
126
+ }
127
+ return m
128
+}
129
+
130
+// Delims sets the action delimiters to the specified strings, to be used in
131
+// subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template
132
+// definitions will inherit the settings. An empty delimiter stands for the
133
+// corresponding default: {{ or }}.
134
+// The return value is the template, so calls can be chained.
135
+func (t *Template) Delims(left, right string) *Template {
136
+ t.leftDelim = left
137
+ t.rightDelim = right
138
+ return t
139
+}
140
+
141
+// Funcs adds the elements of the argument map to the template's function map.
142
+// It panics if a value in the map is not a function with appropriate return
143
+// type. However, it is legal to overwrite elements of the map. The return
144
+// value is the template, so calls can be chained.
145
+func (t *Template) Funcs(funcMap FuncMap) *Template {
146
+ t.init()
147
+ addValueFuncs(t.execFuncs, funcMap)
148
+ addFuncs(t.parseFuncs, funcMap)
149
+ return t
150
+}
151
+
152
+// Lookup returns the template with the given name that is associated with t,
153
+// or nil if there is no such template.
154
+func (t *Template) Lookup(name string) *Template {
155
+ if t.common == nil {
156
+ return nil
157
+ }
158
+ return t.tmpl[name]
159
+}
160
+
161
+// Parse parses a string into a template. Nested template definitions will be
162
+// associated with the top-level template t. Parse may be called multiple times
163
+// to parse definitions of templates to associate with t. It is an error if a
164
+// resulting template is non-empty (contains content other than template
165
+// definitions) and would replace a non-empty template with the same name.
166
+// (In multiple calls to Parse with the same receiver template, only one call
167
+// can contain text other than space, comments, and template definitions.)
168
+func (t *Template) Parse(text string) (*Template, error) {
169
+ t.init()
170
+ trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins)
171
+ if err != nil {
172
+ return nil, err
173
+ }
174
+ // Add the newly parsed trees, including the one for t, into our common structure.
175
+ for name, tree := range trees {
176
+ // If the name we parsed is the name of this template, overwrite this template.
177
+ // The associate method checks it's not a redefinition.
178
+ tmpl := t
179
+ if name != t.name {
180
+ tmpl = t.New(name)
181
+ }
182
+ // Even if t == tmpl, we need to install it in the common.tmpl map.
183
+ if replace, err := t.associate(tmpl, tree); err != nil {
184
+ return nil, err
185
+ } else if replace {
186
+ tmpl.Tree = tree
187
+ }
188
+ tmpl.leftDelim = t.leftDelim
189
+ tmpl.rightDelim = t.rightDelim
190
+ }
191
+ return t, nil
192
+}
193
+
194
+// associate installs the new template into the group of templates associated
195
+// with t. It is an error to reuse a name except to overwrite an empty
196
+// template. The two are already known to share the common structure.
197
+// The boolean return value reports wither to store this tree as t.Tree.
198
+func (t *Template) associate(new *Template, tree *parse.Tree) (bool, error) {
199
+ if new.common != t.common {
200
+ panic("internal error: associate not common")
201
+ }
202
+ name := new.name
203
+ if old := t.tmpl[name]; old != nil {
204
+ oldIsEmpty := parse.IsEmptyTree(old.Root)
205
+ newIsEmpty := parse.IsEmptyTree(tree.Root)
206
+ if newIsEmpty {
207
+ // Whether old is empty or not, new is empty; no reason to replace old.
208
+ return false, nil
209
+ }
210
+ if !oldIsEmpty {
211
+ return false, fmt.Errorf("template: redefinition of template %q", name)
212
+ }
213
+ }
214
+ t.tmpl[name] = new
215
+ return true, nil
216
+}
Godeps/_workspace/src/github.com/alecthomas/template/testdata/file1.tmpl
new
+2
@@ -0,0 +1,2 @@
1
+{{define "x"}}TEXT{{end}}
2
+{{define "dotV"}}{{.V}}{{end}}
Godeps/_workspace/src/github.com/alecthomas/template/testdata/file2.tmpl
new
+2
@@ -0,0 +1,2 @@
1
+{{define "dot"}}{{.}}{{end}}
2
+{{define "nested"}}{{template "dot" .}}{{end}}
Godeps/_workspace/src/github.com/alecthomas/template/testdata/tmpl1.tmpl
new
+3
@@ -0,0 +1,3 @@
1
+template1
2
+{{define "x"}}x{{end}}
3
+{{template "y"}}
Godeps/_workspace/src/github.com/alecthomas/template/testdata/tmpl2.tmpl
new
+3
@@ -0,0 +1,3 @@
1
+template2
2
+{{define "y"}}y{{end}}
3
+{{template "x"}}
Godeps/_workspace/src/github.com/alecthomas/units/COPYING
new
+19
@@ -0,0 +1,19 @@
1
+Copyright (C) 2014 Alec Thomas
2
+
3
+Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+this software and associated documentation files (the "Software"), to deal in
5
+the Software without restriction, including without limitation the rights to
6
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+of the Software, and to permit persons to whom the Software is furnished to do
8
+so, subject to the following conditions:
9
+
10
+The above copyright notice and this permission notice shall be included in all
11
+copies or substantial portions of the Software.
12
+
13
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+SOFTWARE.
Godeps/_workspace/src/github.com/alecthomas/units/README.md
new
+11
@@ -0,0 +1,11 @@
1
+# Units - Helpful unit multipliers and functions for Go
2
+
3
+The goal of this package is to have functionality similar to the [time](http://golang.org/pkg/time/) package.
4
+
5
+It allows for code like this:
6
+
7
+```go
8
+n, err := ParseBase2Bytes("1KB")
9
+// n == 1024
10
+n = units.Mebibyte * 512
11
+```
Godeps/_workspace/src/github.com/alecthomas/units/bytes.go
new
+83
@@ -0,0 +1,83 @@
1
+package units
2
+
3
+// Base2Bytes is the old non-SI power-of-2 byte scale (1024 bytes in a kilobyte,
4
+// etc.).
5
+type Base2Bytes int64
6
+
7
+// Base-2 byte units.
8
+const (
9
+ Kibibyte Base2Bytes = 1024
10
+ KiB = Kibibyte
11
+ Mebibyte = Kibibyte * 1024
12
+ MiB = Mebibyte
13
+ Gibibyte = Mebibyte * 1024
14
+ GiB = Gibibyte
15
+ Tebibyte = Gibibyte * 1024
16
+ TiB = Tebibyte
17
+ Pebibyte = Tebibyte * 1024
18
+ PiB = Pebibyte
19
+ Exbibyte = Pebibyte * 1024
20
+ EiB = Exbibyte
21
+)
22
+
23
+var (
24
+ bytesUnitMap = MakeUnitMap("iB", "B", 1024)
25
+ oldBytesUnitMap = MakeUnitMap("B", "B", 1024)
26
+)
27
+
28
+// ParseBase2Bytes supports both iB and B in base-2 multipliers. That is, KB
29
+// and KiB are both 1024.
30
+func ParseBase2Bytes(s string) (Base2Bytes, error) {
31
+ n, err := ParseUnit(s, bytesUnitMap)
32
+ if err != nil {
33
+ n, err = ParseUnit(s, oldBytesUnitMap)
34
+ }
35
+ return Base2Bytes(n), err
36
+}
37
+
38
+func (b Base2Bytes) String() string {
39
+ return ToString(int64(b), 1024, "iB", "B")
40
+}
41
+
42
+var (
43
+ metricBytesUnitMap = MakeUnitMap("B", "B", 1000)
44
+)
45
+
46
+// MetricBytes are SI byte units (1000 bytes in a kilobyte).
47
+type MetricBytes SI
48
+
49
+// SI base-10 byte units.
50
+const (
51
+ Kilobyte MetricBytes = 1000
52
+ KB = Kilobyte
53
+ Megabyte = Kilobyte * 1000
54
+ MB = Megabyte
55
+ Gigabyte = Megabyte * 1000
56
+ GB = Gigabyte
57
+ Terabyte = Gigabyte * 1000
58
+ TB = Terabyte
59
+ Petabyte = Terabyte * 1000
60
+ PB = Petabyte
61
+ Exabyte = Petabyte * 1000
62
+ EB = Exabyte
63
+)
64
+
65
+// ParseMetricBytes parses base-10 metric byte units. That is, KB is 1000 bytes.
66
+func ParseMetricBytes(s string) (MetricBytes, error) {
67
+ n, err := ParseUnit(s, metricBytesUnitMap)
68
+ return MetricBytes(n), err
69
+}
70
+
71
+func (m MetricBytes) String() string {
72
+ return ToString(int64(m), 1000, "B", "B")
73
+}
74
+
75
+// ParseStrictBytes supports both iB and B suffixes for base 2 and metric,
76
+// respectively. That is, KiB represents 1024 and KB represents 1000.
77
+func ParseStrictBytes(s string) (int64, error) {
78
+ n, err := ParseUnit(s, bytesUnitMap)
79
+ if err != nil {
80
+ n, err = ParseUnit(s, metricBytesUnitMap)
81
+ }
82
+ return int64(n), err
83
+}
Godeps/_workspace/src/github.com/alecthomas/units/bytes_test.go
new
+49
@@ -0,0 +1,49 @@
1
+package units
2
+
3
+import (
4
+ "testing"
5
+
6
+ "github.com/stretchr/testify/assert"
7
+)
8
+
9
+func TestBase2BytesString(t *testing.T) {
10
+ assert.Equal(t, Base2Bytes(0).String(), "0B")
11
+ assert.Equal(t, Base2Bytes(1025).String(), "1KiB1B")
12
+ assert.Equal(t, Base2Bytes(1048577).String(), "1MiB1B")
13
+}
14
+
15
+func TestParseBase2Bytes(t *testing.T) {
16
+ n, err := ParseBase2Bytes("0B")
17
+ assert.NoError(t, err)
18
+ assert.Equal(t, 0, n)
19
+ n, err = ParseBase2Bytes("1KB")
20
+ assert.NoError(t, err)
21
+ assert.Equal(t, 1024, n)
22
+ n, err = ParseBase2Bytes("1MB1KB25B")
23
+ assert.NoError(t, err)
24
+ assert.Equal(t, 1049625, n)
25
+ n, err = ParseBase2Bytes("1.5MB")
26
+ assert.NoError(t, err)
27
+ assert.Equal(t, 1572864, n)
28
+}
29
+
30
+func TestMetricBytesString(t *testing.T) {
31
+ assert.Equal(t, MetricBytes(0).String(), "0B")
32
+ assert.Equal(t, MetricBytes(1001).String(), "1KB1B")
33
+ assert.Equal(t, MetricBytes(1001025).String(), "1MB1KB25B")
34
+}
35
+
36
+func TestParseMetricBytes(t *testing.T) {
37
+ n, err := ParseMetricBytes("0B")
38
+ assert.NoError(t, err)
39
+ assert.Equal(t, 0, n)
40
+ n, err = ParseMetricBytes("1KB1B")
41
+ assert.NoError(t, err)
42
+ assert.Equal(t, 1001, n)
43
+ n, err = ParseMetricBytes("1MB1KB25B")
44
+ assert.NoError(t, err)
45
+ assert.Equal(t, 1001025, n)
46
+ n, err = ParseMetricBytes("1.5MB")
47
+ assert.NoError(t, err)
48
+ assert.Equal(t, 1500000, n)
49
+}
Godeps/_workspace/src/github.com/alecthomas/units/doc.go
new
+13
@@ -0,0 +1,13 @@
1
+// Package units provides helpful unit multipliers and functions for Go.
2
+//
3
+// The goal of this package is to have functionality similar to the time [1] package.
4
+//
5
+//
6
+// [1] http://golang.org/pkg/time/
7
+//
8
+// It allows for code like this:
9
+//
10
+// n, err := ParseBase2Bytes("1KB")
11
+// // n == 1024
12
+// n = units.Mebibyte * 512
13
+package units
Godeps/_workspace/src/github.com/alecthomas/units/si.go
new
+26
@@ -0,0 +1,26 @@
1
+package units
2
+
3
+// SI units.
4
+type SI int64
5
+
6
+// SI unit multiples.
7
+const (
8
+ Kilo SI = 1000
9
+ Mega = Kilo * 1000
10
+ Giga = Mega * 1000
11
+ Tera = Giga * 1000
12
+ Peta = Tera * 1000
13
+ Exa = Peta * 1000
14
+)
15
+
16
+func MakeUnitMap(suffix, shortSuffix string, scale int64) map[string]float64 {
17
+ return map[string]float64{
18
+ shortSuffix: 1,
19
+ "K" + suffix: float64(scale),
20
+ "M" + suffix: float64(scale * scale),
21
+ "G" + suffix: float64(scale * scale * scale),
22
+ "T" + suffix: float64(scale * scale * scale * scale),
23
+ "P" + suffix: float64(scale * scale * scale * scale * scale),
24
+ "E" + suffix: float64(scale * scale * scale * scale * scale * scale),
25
+ }
26
+}
Godeps/_workspace/src/github.com/alecthomas/units/util.go
new
+138
@@ -0,0 +1,138 @@
1
+package units
2
+
3
+import (
4
+ "errors"
5
+ "fmt"
6
+ "strings"
7
+)
8
+
9
+var (
10
+ siUnits = []string{"", "K", "M", "G", "T", "P", "E"}
11
+)
12
+
13
+func ToString(n int64, scale int64, suffix, baseSuffix string) string {
14
+ mn := len(siUnits)
15
+ out := make([]string, mn)
16
+ for i, m := range siUnits {
17
+ if n%scale != 0 || i == 0 && n == 0 {
18
+ s := suffix
19
+ if i == 0 {
20
+ s = baseSuffix
21
+ }
22
+ out[mn-1-i] = fmt.Sprintf("%d%s%s", n%scale, m, s)
23
+ }
24
+ n /= scale
25
+ if n == 0 {
26
+ break
27
+ }
28
+ }
29
+ return strings.Join(out, "")
30
+}
31
+
32
+// Below code ripped straight from http://golang.org/src/pkg/time/format.go?s=33392:33438#L1123
33
+var errLeadingInt = errors.New("units: bad [0-9]*") // never printed
34
+
35
+// leadingInt consumes the leading [0-9]* from s.
36
+func leadingInt(s string) (x int64, rem string, err error) {
37
+ i := 0
38
+ for ; i < len(s); i++ {
39
+ c := s[i]
40
+ if c < '0' || c > '9' {
41
+ break
42
+ }
43
+ if x >= (1<<63-10)/10 {
44
+ // overflow
45
+ return 0, "", errLeadingInt
46
+ }
47
+ x = x*10 + int64(c) - '0'
48
+ }
49
+ return x, s[i:], nil
50
+}
51
+
52
+func ParseUnit(s string, unitMap map[string]float64) (int64, error) {
53
+ // [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+
54
+ orig := s
55
+ f := float64(0)
56
+ neg := false
57
+
58
+ // Consume [-+]?
59
+ if s != "" {
60
+ c := s[0]
61
+ if c == '-' || c == '+' {
62
+ neg = c == '-'
63
+ s = s[1:]
64
+ }
65
+ }
66
+ // Special case: if all that is left is "0", this is zero.
67
+ if s == "0" {
68
+ return 0, nil
69
+ }
70
+ if s == "" {
71
+ return 0, errors.New("units: invalid " + orig)
72
+ }
73
+ for s != "" {
74
+ g := float64(0) // this element of the sequence
75
+
76
+ var x int64
77
+ var err error
78
+
79
+ // The next character must be [0-9.]
80
+ if !(s[0] == '.' || ('0' <= s[0] && s[0] <= '9')) {
81
+ return 0, errors.New("units: invalid " + orig)
82
+ }
83
+ // Consume [0-9]*
84
+ pl := len(s)
85
+ x, s, err = leadingInt(s)
86
+ if err != nil {
87
+ return 0, errors.New("units: invalid " + orig)
88
+ }
89
+ g = float64(x)
90
+ pre := pl != len(s) // whether we consumed anything before a period
91
+
92
+ // Consume (\.[0-9]*)?
93
+ post := false
94
+ if s != "" && s[0] == '.' {
95
+ s = s[1:]
96
+ pl := len(s)
97
+ x, s, err = leadingInt(s)
98
+ if err != nil {
99
+ return 0, errors.New("units: invalid " + orig)
100
+ }
101
+ scale := 1.0
102
+ for n := pl - len(s); n > 0; n-- {
103
+ scale *= 10
104
+ }
105
+ g += float64(x) / scale
106
+ post = pl != len(s)
107
+ }
108
+ if !pre && !post {
109
+ // no digits (e.g. ".s" or "-.s")
110
+ return 0, errors.New("units: invalid " + orig)
111
+ }
112
+
113
+ // Consume unit.
114
+ i := 0
115
+ for ; i < len(s); i++ {
116
+ c := s[i]
117
+ if c == '.' || ('0' <= c && c <= '9') {
118
+ break
119
+ }
120
+ }
121
+ u := s[:i]
122
+ s = s[i:]
123
+ unit, ok := unitMap[u]
124
+ if !ok {
125
+ return 0, errors.New("units: unknown unit " + u + " in " + orig)
126
+ }
127
+
128
+ f += g * unit
129
+ }
130
+
131
+ if neg {
132
+ f = -f
133
+ }
134
+ if f < float64(-1<<63) || f > float64(1<<63-1) {
135
+ return 0, errors.New("units: overflow parsing unit")
136
+ }
137
+ return int64(f), nil
138
+}
test/dependencies/iptb/lib/todo.go
deleted
-3
@@ -1,3 +0,0 @@
1
-package iptb
2
-
3
-// TODO: move code here to be used as a lib
test/dependencies/iptb/main.go
+214
-107
@@ -3,7 +3,6 @@ package main
3
import (
4
"encoding/json"
5
"errors"
6
- "flag"
6
"fmt"
7
"io/ioutil"
8
"log"
@@ -17,6 +16,7 @@ import (
16
"syscall"
17
"time"
18
19
+ kingpin "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/alecthomas/kingpin"
20
serial "github.com/ipfs/go-ipfs/repo/fsrepo/serialize"
21
22
ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
@@ -76,10 +76,16 @@ type initCfg struct {
76
}
77
78
func (c *initCfg) swarmAddrForPeer(i int) string {
79
+ if c.PortStart == 0 {
80
+ return "/ip4/0.0.0.0/tcp/0"
81
+ }
82
return fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", c.PortStart+i)
83
}
84
85
func (c *initCfg) apiAddrForPeer(i int) string {
86
+ if c.PortStart == 0 {
87
+ return "/ip4/127.0.0.1/tcp/0"
88
+ }
89
return fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", c.PortStart+1000+i)
90
}
91
@@ -208,32 +214,37 @@ func IpfsPidOf(n int) (int, error) {
214
return strconv.Atoi(string(b))
215
}
216
211
-func IpfsKill() error {
212
- n := GetNumNodes()
213
- for i := 0; i < n; i++ {
214
- pid, err := IpfsPidOf(i)
215
- if err != nil {
216
- fmt.Printf("error killing daemon %d: %s\n", i, err)
217
- continue
218
- }
217
+func KillNode(i int) error {
218
+ pid, err := IpfsPidOf(i)
219
+ if err != nil {
220
+ return fmt.Errorf("error killing daemon %d: %s", i, err)
221
+ }
222
220
- p, err := os.FindProcess(pid)
221
- if err != nil {
222
- fmt.Printf("error killing daemon %d: %s\n", i, err)
223
- continue
224
- }
225
- err = p.Kill()
226
- if err != nil {
227
- fmt.Printf("error killing daemon %d: %s\n", i, err)
228
- continue
229
- }
223
+ p, err := os.FindProcess(pid)
224
+ if err != nil {
225
+ return fmt.Errorf("error killing daemon %d: %s", i, err)
226
+ }
227
+ err = p.Kill()
228
+ if err != nil {
229
+ return fmt.Errorf("error killing daemon %d: %s\n", i, err)
230
+ }
231
+
232
+ p.Wait()
233
231
- p.Wait()
234
+ err = os.Remove(path.Join(IpfsDirN(i), "daemon.pid"))
235
+ if err != nil {
236
+ return fmt.Errorf("error removing pid file for daemon %d: %s\n", i, err)
237
+ }
238
+
239
+ return nil
240
+}
241
233
- err = os.Remove(path.Join(IpfsDirN(i), "daemon.pid"))
242
+func IpfsKillAll() error {
243
+ n := GetNumNodes()
244
+ for i := 0; i < n; i++ {
245
+ err := KillNode(i)
246
if err != nil {
235
- fmt.Printf("error removing pid file for daemon %d: %s\n", i, err)
236
- continue
247
+ return err
248
}
249
}
250
return nil
@@ -246,7 +257,7 @@ func IpfsStart(waitall bool) error {
257
dir := IpfsDirN(i)
258
cmd := exec.Command("ipfs", "daemon")
259
cmd.Dir = dir
249
- cmd.Env = []string{"IPFS_PATH=" + dir}
260
+ cmd.Env = append(os.Environ(), "IPFS_PATH="+dir)
261
262
cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
263
@@ -277,29 +288,27 @@ func IpfsStart(waitall bool) error {
288
289
// Make sure node 0 is up before starting the rest so
290
// bootstrapping works properly
280
- if i == 0 || waitall {
281
- cfg, err := serial.Load(path.Join(IpfsDirN(i), "config"))
282
- if err != nil {
283
- return err
284
- }
291
+ cfg, err := serial.Load(path.Join(IpfsDirN(i), "config"))
292
+ if err != nil {
293
+ return err
294
+ }
295
286
- maddr := ma.StringCast(cfg.Addresses.API)
287
- _, addr, err := manet.DialArgs(maddr)
288
- if err != nil {
289
- return err
290
- }
296
+ maddr := ma.StringCast(cfg.Addresses.API)
297
+ _, addr, err := manet.DialArgs(maddr)
298
+ if err != nil {
299
+ return err
300
+ }
301
292
- addrs = append(addrs, addr)
302
+ addrs = append(addrs, addr)
303
294
- err = waitOnAPI(cfg.Identity.PeerID, addr)
295
- if err != nil {
296
- return err
297
- }
304
+ err = waitOnAPI(cfg.Identity.PeerID, i)
305
+ if err != nil {
306
+ return err
307
}
308
}
309
if waitall {
310
for i := 0; i < n; i++ {
302
- err := waitOnSwarmPeers(addrs[i])
311
+ err := waitOnSwarmPeers(i)
312
if err != nil {
313
return err
314
}
@@ -309,32 +318,73 @@ func IpfsStart(waitall bool) error {
318
return nil
319
}
320
312
-func waitOnAPI(peerid, addr string) error {
321
+func waitOnAPI(peerid string, nnum int) error {
322
for i := 0; i < 50; i++ {
314
- resp, err := http.Get("http://" + addr + "/api/v0/id")
323
+ err := tryAPICheck(peerid, nnum)
324
if err == nil {
316
- out := make(map[string]interface{})
317
- err := json.NewDecoder(resp.Body).Decode(&out)
318
- if err != nil {
319
- return fmt.Errorf("liveness check failed: %s", err)
320
- }
321
- id, ok := out["ID"]
322
- if !ok {
323
- return fmt.Errorf("liveness check failed: ID field not present in output")
324
- }
325
- idstr := id.(string)
326
- if idstr != peerid {
327
- return fmt.Errorf("liveness check failed: unexpected peer at endpoint")
328
- }
329
-
325
return nil
326
}
327
time.Sleep(time.Millisecond * 200)
328
}
334
- return fmt.Errorf("node at %s failed to come online in given time period", addr)
329
+ return fmt.Errorf("node %d failed to come online in given time period", nnum)
330
+}
331
+
332
+func getNodesAPIAddr(nnum int) (string, error) {
333
+ addrb, err := ioutil.ReadFile(path.Join(IpfsDirN(nnum), "api"))
334
+ if err != nil {
335
+ return "", err
336
+ }
337
+
338
+ maddr, err := ma.NewMultiaddr(string(addrb))
339
+ if err != nil {
340
+ fmt.Println("error parsing multiaddr: ", err)
341
+ return "", err
342
+ }
343
+
344
+ _, addr, err := manet.DialArgs(maddr)
345
+ if err != nil {
346
+ fmt.Println("error on multiaddr dialargs: ", err)
347
+ return "", err
348
+ }
349
+ return addr, nil
350
}
351
337
-func waitOnSwarmPeers(addr string) error {
352
+func tryAPICheck(peerid string, nnum int) error {
353
+ addr, err := getNodesAPIAddr(nnum)
354
+ if err != nil {
355
+ return err
356
+ }
357
+
358
+ resp, err := http.Get("http://" + addr + "/api/v0/id")
359
+ if err != nil {
360
+ return err
361
+ }
362
+
363
+ out := make(map[string]interface{})
364
+ err = json.NewDecoder(resp.Body).Decode(&out)
365
+ if err != nil {
366
+ return fmt.Errorf("liveness check failed: %s", err)
367
+ }
368
+
369
+ id, ok := out["ID"]
370
+ if !ok {
371
+ return fmt.Errorf("liveness check failed: ID field not present in output")
372
+ }
373
+
374
+ idstr := id.(string)
375
+ if idstr != peerid {
376
+ return fmt.Errorf("liveness check failed: unexpected peer at endpoint")
377
+ }
378
+
379
+ return nil
380
+}
381
+
382
+func waitOnSwarmPeers(nnum int) error {
383
+ addr, err := getNodesAPIAddr(nnum)
384
+ if err != nil {
385
+ return err
386
+ }
387
+
388
for i := 0; i < 50; i++ {
389
resp, err := http.Get("http://" + addr + "/api/v0/swarm/peers")
390
if err == nil {
@@ -390,6 +440,27 @@ func IpfsShell(n int) error {
440
return syscall.Exec(shell, []string{shell}, nenvs)
441
}
442
443
+func ConnectNodes(from, to int) error {
444
+ cmd := exec.Command("ipfs", "id", "-f", "<addrs>")
445
+ cmd.Env = []string{"IPFS_PATH=" + IpfsDirN(to)}
446
+ out, err := cmd.Output()
447
+ if err != nil {
448
+ fmt.Println("ERR: ", string(out))
449
+ return err
450
+ }
451
+ addr := strings.Split(string(out), "\n")[0]
452
+ fmt.Println("ADDR: ", addr)
453
+
454
+ connectcmd := exec.Command("ipfs", "swarm", "connect", addr)
455
+ connectcmd.Env = []string{"IPFS_PATH=" + IpfsDirN(from)}
456
+ out, err = connectcmd.CombinedOutput()
457
+ if err != nil {
458
+ fmt.Println(string(out))
459
+ return err
460
+ }
461
+ return nil
462
+}
463
+
464
func GetAttr(attr string, node int) (string, error) {
465
switch attr {
466
case "id":
@@ -402,38 +473,38 @@ func GetAttr(attr string, node int) (string, error) {
473
var helptext = `Ipfs Testbed
474
475
Commands:
405
- init
406
- creates and initializes 'n' repos
407
-
408
- Options:
409
- -n=[number of nodes]
410
- -f - force overwriting of existing nodes
411
- -bootstrap - select bootstrapping style for cluster
412
- choices: star, none
413
-
414
- start
415
- starts up all testbed nodes
416
-
417
- Options:
418
- -wait - wait until daemons are fully initialized
419
- stop
420
- kills all testbed nodes
421
- restart
422
- kills, then restarts all testbed nodes
423
-
424
- shell [n]
425
- execs your shell with environment variables set as follows:
426
- IPFS_PATH - set to testbed node n's IPFS_PATH
427
- NODE[x] - set to the peer ID of node x
428
-
429
- get [attribute] [node]
430
- get an attribute of the given node
431
- currently supports: "id"
476
+ init
477
+ creates and initializes 'n' repos
478
+
479
+ Options:
480
+ -n=[number of nodes]
481
+ -f - force overwriting of existing nodes
482
+ -bootstrap - select bootstrapping style for cluster
483
+ choices: star, none
484
+
485
+ start
486
+ starts up all testbed nodes
487
+
488
+ Options:
489
+ -wait - wait until daemons are fully initialized
490
+ stop
491
+ kills all testbed nodes
492
+ restart
493
+ kills, then restarts all testbed nodes
494
+
495
+ shell [n]
496
+ execs your shell with environment variables set as follows:
497
+ IPFS_PATH - set to testbed node n's IPFS_PATH
498
+ NODE[x] - set to the peer ID of node x
499
+
500
+ get [attribute] [node]
501
+ get an attribute of the given node
502
+ currently supports: "id"
503
504
Env Vars:
505
506
IPTB_ROOT:
436
- Used to specify the directory that nodes will be created in.
507
+ Used to specify the directory that nodes will be created in.
508
`
509
510
func handleErr(s string, err error) {
@@ -445,23 +516,22 @@ func handleErr(s string, err error) {
516
517
func main() {
518
cfg := new(initCfg)
448
- flag.IntVar(&cfg.Count, "n", 0, "number of ipfs nodes to initialize")
449
- flag.IntVar(&cfg.PortStart, "p", 4002, "port to start allocations from")
450
- flag.BoolVar(&cfg.Force, "f", false, "force initialization (overwrite existing configs)")
451
- flag.BoolVar(&cfg.Mdns, "mdns", false, "turn on mdns for nodes")
452
- flag.StringVar(&cfg.Bootstrap, "bootstrap", "star", "select bootstrapping style for cluster")
453
-
454
- wait := flag.Bool("wait", false, "wait for nodes to come fully online before exiting")
455
- flag.Usage = func() {
456
- fmt.Println(helptext)
457
- }
519
+ kingpin.Flag("n", "number of ipfs nodes to initialize").Short('n').IntVar(&cfg.Count)
520
+ kingpin.Flag("port", "port to start allocations from").Default("4002").Short('p').IntVar(&cfg.PortStart)
521
+ kingpin.Flag("f", "force initialization (overwrite existing configs)").BoolVar(&cfg.Force)
522
+ kingpin.Flag("mdns", "turn on mdns for nodes").BoolVar(&cfg.Mdns)
523
+ kingpin.Flag("bootstrap", "select bootstrapping style for cluster").Default("star").StringVar(&cfg.Bootstrap)
524
459
- flag.Parse()
525
+ wait := kingpin.Flag("wait", "wait for nodes to come fully online before exiting").Bool()
526
461
- switch flag.Arg(0) {
527
+ var args []string
528
+ kingpin.Arg("args", "arguments").StringsVar(&args)
529
+ kingpin.Parse()
530
+
531
+ switch args[0] {
532
case "init":
533
if cfg.Count == 0 {
464
- fmt.Printf("please specify number of nodes: '%s -n=10 init'\n", os.Args[0])
534
+ fmt.Printf("please specify number of nodes: '%s init -n 10'\n", os.Args[0])
535
os.Exit(1)
536
}
537
err := IpfsInit(cfg)
@@ -470,38 +540,75 @@ func main() {
540
err := IpfsStart(*wait)
541
handleErr("ipfs start err: ", err)
542
case "stop", "kill":
473
- err := IpfsKill()
543
+ if len(args) > 1 {
544
+ i, err := strconv.Atoi(args[1])
545
+ if err != nil {
546
+ fmt.Println("failed to parse node number: ", err)
547
+ os.Exit(1)
548
+ }
549
+ err = KillNode(i)
550
+ if err != nil {
551
+ fmt.Println("failed to kill node: ", err)
552
+ }
553
+ return
554
+ }
555
+ err := IpfsKillAll()
556
handleErr("ipfs kill err: ", err)
557
case "restart":
476
- err := IpfsKill()
558
+ err := IpfsKillAll()
559
handleErr("ipfs kill err: ", err)
560
561
err = IpfsStart(*wait)
562
handleErr("ipfs start err: ", err)
563
case "shell":
482
- if len(flag.Args()) < 2 {
564
+ if len(args) < 2 {
565
fmt.Println("please specify which node you want a shell for")
566
os.Exit(1)
567
}
486
- n, err := strconv.Atoi(flag.Arg(1))
568
+ n, err := strconv.Atoi(args[1])
569
handleErr("parse err: ", err)
570
571
err = IpfsShell(n)
572
handleErr("ipfs shell err: ", err)
573
+ case "connect":
574
+ if len(args) < 3 {
575
+ fmt.Println("iptb connect [node] [node]")
576
+ os.Exit(1)
577
+ }
578
+
579
+ from, err := strconv.Atoi(args[1])
580
+ if err != nil {
581
+ fmt.Printf("failed to parse: %s\n", err)
582
+ return
583
+ }
584
+
585
+ to, err := strconv.Atoi(args[2])
586
+ if err != nil {
587
+ fmt.Printf("failed to parse: %s\n", err)
588
+ return
589
+ }
590
+
591
+ err = ConnectNodes(from, to)
592
+ if err != nil {
593
+ fmt.Printf("failed to connect: %s\n", err)
594
+ return
595
+ }
596
+
597
case "get":
492
- if len(flag.Args()) < 3 {
598
+ if len(args) < 3 {
599
fmt.Println("iptb get [attr] [node]")
600
os.Exit(1)
601
}
496
- attr := flag.Arg(1)
497
- num, err := strconv.Atoi(flag.Arg(2))
602
+ attr := args[1]
603
+ num, err := strconv.Atoi(args[2])
604
handleErr("error parsing node number: ", err)
605
606
val, err := GetAttr(attr, num)
607
handleErr("error getting attribute: ", err)
608
fmt.Println(val)
609
default:
504
- flag.Usage()
610
+ kingpin.Usage()
611
+ fmt.Println(helptext)
612
os.Exit(1)
613
}
614
}