@cryptotaxi247 / kubo / commits / e202c6592

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 [![Build Status](https://travis-ci.org/alecthomas/kingpin.png)](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 + "&lt;script&gt;alert(&#34;XSS&#34;);&lt;/script&gt;", nil, true},
405 + {"html pipeline", `{{printf "<script>alert(\"XSS\");</script>" | html}}`,
406 + "&lt;script&gt;alert(&#34;XSS&#34;);&lt;/script&gt;", 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("&#34;") // shorter than "&quot;"
436 + htmlApos = []byte("&#39;") // shorter than "&apos;" and apos was not in HTML until HTML5
437 + htmlAmp = []byte("&amp;")
438 + htmlLt = []byte("&lt;")
439 + htmlGt = []byte("&gt;")
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 }