├── .gitignore ├── .goxc.json ├── .goxc.ok ├── .travis.yml ├── Godeps ├── Godeps.json ├── Readme └── _workspace │ ├── .gitignore │ └── src │ ├── github.com │ ├── Sirupsen │ │ └── logrus │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── CHANGELOG.md │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── entry.go │ │ │ ├── entry_test.go │ │ │ ├── examples │ │ │ ├── basic │ │ │ │ └── basic.go │ │ │ └── hook │ │ │ │ └── hook.go │ │ │ ├── exported.go │ │ │ ├── formatter.go │ │ │ ├── formatter_bench_test.go │ │ │ ├── formatters │ │ │ └── logstash │ │ │ │ ├── logstash.go │ │ │ │ └── logstash_test.go │ │ │ ├── hook_test.go │ │ │ ├── hooks.go │ │ │ ├── hooks │ │ │ ├── airbrake │ │ │ │ ├── airbrake.go │ │ │ │ └── airbrake_test.go │ │ │ ├── bugsnag │ │ │ │ ├── bugsnag.go │ │ │ │ └── bugsnag_test.go │ │ │ ├── papertrail │ │ │ │ ├── README.md │ │ │ │ ├── papertrail.go │ │ │ │ └── papertrail_test.go │ │ │ ├── sentry │ │ │ │ ├── README.md │ │ │ │ ├── sentry.go │ │ │ │ └── sentry_test.go │ │ │ └── syslog │ │ │ │ ├── README.md │ │ │ │ ├── syslog.go │ │ │ │ └── syslog_test.go │ │ │ ├── json_formatter.go │ │ │ ├── json_formatter_test.go │ │ │ ├── logger.go │ │ │ ├── logrus.go │ │ │ ├── logrus_test.go │ │ │ ├── terminal_bsd.go │ │ │ ├── terminal_linux.go │ │ │ ├── terminal_notwindows.go │ │ │ ├── terminal_windows.go │ │ │ ├── text_formatter.go │ │ │ ├── text_formatter_test.go │ │ │ └── writer.go │ ├── alecthomas │ │ ├── template │ │ │ ├── README.md │ │ │ ├── doc.go │ │ │ ├── example_test.go │ │ │ ├── examplefiles_test.go │ │ │ ├── examplefunc_test.go │ │ │ ├── exec.go │ │ │ ├── exec_test.go │ │ │ ├── funcs.go │ │ │ ├── helper.go │ │ │ ├── multi_test.go │ │ │ ├── parse │ │ │ │ ├── lex.go │ │ │ │ ├── node.go │ │ │ │ └── parse.go │ │ │ ├── template.go │ │ │ └── testdata │ │ │ │ ├── file1.tmpl │ │ │ │ ├── file2.tmpl │ │ │ │ ├── tmpl1.tmpl │ │ │ │ └── tmpl2.tmpl │ │ └── units │ │ │ ├── COPYING │ │ │ ├── README.md │ │ │ ├── bytes.go │ │ │ ├── bytes_test.go │ │ │ ├── doc.go │ │ │ ├── si.go │ │ │ └── util.go │ ├── nfnt │ │ └── resize │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── converter.go │ │ │ ├── converter_test.go │ │ │ ├── filters.go │ │ │ ├── nearest.go │ │ │ ├── nearest_test.go │ │ │ ├── resize.go │ │ │ ├── resize_test.go │ │ │ ├── thumbnail.go │ │ │ ├── thumbnail_test.go │ │ │ ├── ycc.go │ │ │ └── ycc_test.go │ └── stretchr │ │ ├── objx │ │ ├── .gitignore │ │ ├── LICENSE.md │ │ ├── README.md │ │ ├── accessors.go │ │ ├── accessors_test.go │ │ ├── codegen │ │ │ ├── array-access.txt │ │ │ ├── index.html │ │ │ ├── template.txt │ │ │ └── types_list.txt │ │ ├── constants.go │ │ ├── conversions.go │ │ ├── conversions_test.go │ │ ├── doc.go │ │ ├── fixture_test.go │ │ ├── map.go │ │ ├── map_for_test.go │ │ ├── map_test.go │ │ ├── mutations.go │ │ ├── mutations_test.go │ │ ├── security.go │ │ ├── security_test.go │ │ ├── simple_example_test.go │ │ ├── tests.go │ │ ├── tests_test.go │ │ ├── type_specific_codegen.go │ │ ├── type_specific_codegen_test.go │ │ ├── value.go │ │ └── value_test.go │ │ └── testify │ │ ├── assert │ │ ├── assertions.go │ │ ├── assertions_test.go │ │ ├── doc.go │ │ ├── errors.go │ │ ├── forward_assertions.go │ │ ├── forward_assertions_test.go │ │ ├── http_assertions.go │ │ └── http_assertions_test.go │ │ └── mock │ │ ├── doc.go │ │ ├── mock.go │ │ └── mock_test.go │ └── gopkg.in │ ├── alecthomas │ └── kingpin.v2 │ │ ├── .travis.yml │ │ ├── COPYING │ │ ├── README.md │ │ ├── app.go │ │ ├── app_test.go │ │ ├── args.go │ │ ├── args_test.go │ │ ├── cmd.go │ │ ├── cmd_test.go │ │ ├── doc.go │ │ ├── examples │ │ ├── chat1 │ │ │ └── main.go │ │ ├── chat2 │ │ │ └── main.go │ │ ├── curl │ │ │ └── main.go │ │ ├── modular │ │ │ └── main.go │ │ └── ping │ │ │ └── main.go │ │ ├── examples_test.go │ │ ├── flags.go │ │ ├── flags_test.go │ │ ├── genrepeated │ │ └── main.go │ │ ├── global.go │ │ ├── guesswidth.go │ │ ├── guesswidth_unix.go │ │ ├── model.go │ │ ├── parser.go │ │ ├── parser_test.go │ │ ├── parsers.go │ │ ├── parsers_test.go │ │ ├── repeated.go │ │ ├── repeated.json │ │ ├── templates.go │ │ ├── usage.go │ │ ├── usage_test.go │ │ └── values.go │ ├── check.v1 │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── TODO │ ├── benchmark.go │ ├── benchmark_test.go │ ├── bootstrap_test.go │ ├── check.go │ ├── check_test.go │ ├── checkers.go │ ├── checkers_test.go │ ├── export_test.go │ ├── fixture_test.go │ ├── foundation_test.go │ ├── helpers.go │ ├── helpers_test.go │ ├── printer.go │ ├── printer_test.go │ ├── run.go │ └── run_test.go │ └── yaml.v2 │ ├── LICENSE │ ├── LICENSE.libyaml │ ├── README.md │ ├── apic.go │ ├── decode.go │ ├── decode_test.go │ ├── emitterc.go │ ├── encode.go │ ├── encode_test.go │ ├── parserc.go │ ├── readerc.go │ ├── resolve.go │ ├── scannerc.go │ ├── sorter.go │ ├── suite_test.go │ ├── writerc.go │ ├── yaml.go │ ├── yamlh.go │ └── yamlprivateh.go ├── Makefile ├── README.md ├── blade.go ├── bladefile.go ├── contents.go ├── contents_test.go ├── converter.go ├── dimensions.go ├── dimensions_test.go ├── docs ├── blade-s.png ├── blade-walkthrough.gif └── blade.gif ├── dryrun_converter.go ├── fixtures ├── contents-appicon.json ├── contents-no-sizes-no-files.json ├── contents-no-sizes.json ├── full_integration │ └── interpolation │ │ ├── Bladefile │ │ ├── after │ │ └── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── source-iphone-29@2x.png │ │ │ ├── source-iphone-29@3x.png │ │ │ ├── source-iphone-40@2x.png │ │ │ ├── source-iphone-40@3x.png │ │ │ ├── source-iphone-60@2x.png │ │ │ └── source-iphone-60@3x.png │ │ │ ├── Image.imageset │ │ │ ├── Contents.json │ │ │ ├── source-universal-333@1x.png │ │ │ ├── source-universal-333@2x.png │ │ │ └── source-universal-333@3x.png │ │ │ └── Watch.imageset │ │ │ ├── Contents.json │ │ │ ├── source-universal-333@1x.png │ │ │ ├── source-universal-333@2x.png │ │ │ ├── source-universal-333@3x.png │ │ │ └── source-watch-500@2x.png │ │ ├── before │ │ └── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ │ ├── Image.imageset │ │ │ └── Contents.json │ │ │ └── Watch.imageset │ │ │ └── Contents.json │ │ └── source.png ├── single-file.json └── with-files.json ├── integration.rb ├── main.go ├── resize_converter.go ├── runner.go ├── runner_test.go ├── templates ├── iphone-appicon.json ├── universal.json └── watch.json └── test_helper_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | blade 2 | vendor/ 3 | dist/ 4 | .goxc.ok 5 | iTunesArtwork@2x.png 6 | out 7 | c.out 8 | cover.out 9 | .DS_Store 10 | 0.*/ 11 | -------------------------------------------------------------------------------- /.goxc.json: -------------------------------------------------------------------------------- 1 | { 2 | "ArtifactsDest": ".", 3 | "Tasks": [ 4 | "go-vet", 5 | "go-test", 6 | "xc", 7 | "tag", 8 | "archive", 9 | "rmbin" 10 | ], 11 | "BuildConstraints": "linux,darwin", 12 | "PackageVersion": "0.0.9", 13 | "ConfigVersion": "0.9", 14 | "Env": [ 15 | "GOPATH={{.Env.GOPATH}}/src/github.com/jondot/blade/Godeps{{.PLS}}{{.Env.GOPATH}}" 16 | ] 17 | } -------------------------------------------------------------------------------- /.goxc.ok: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/.goxc.ok -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | before_install: make setup 3 | script: make test && make && ruby integration.rb 4 | go: 1.4 5 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/jondot/blade", 3 | "GoVersion": "go1.8", 4 | "GodepVersion": "v79", 5 | "Deps": [ 6 | { 7 | "ImportPath": "github.com/Sirupsen/logrus", 8 | "Comment": "v0.8.5-1-g11538ee", 9 | "Rev": "11538ee6888f72d4ab44a1aeba06b9bc4cb134a1" 10 | }, 11 | { 12 | "ImportPath": "github.com/alecthomas/template", 13 | "Rev": "b867cc6ab45cece8143cfcc6fc9c77cf3f2c23c0" 14 | }, 15 | { 16 | "ImportPath": "github.com/alecthomas/template/parse", 17 | "Rev": "b867cc6ab45cece8143cfcc6fc9c77cf3f2c23c0" 18 | }, 19 | { 20 | "ImportPath": "github.com/alecthomas/units", 21 | "Rev": "6b4e7dc5e3143b85ea77909c72caf89416fc2915" 22 | }, 23 | { 24 | "ImportPath": "github.com/nfnt/resize", 25 | "Rev": "dc93e1b98c579d90ee2fa15c1fd6dac34f6e7899" 26 | }, 27 | { 28 | "ImportPath": "github.com/stretchr/objx", 29 | "Rev": "cbeaeb16a013161a98496fad62933b1d21786672" 30 | }, 31 | { 32 | "ImportPath": "github.com/stretchr/testify/assert", 33 | "Comment": "v1.0", 34 | "Rev": "232e8563676cd15c3a36ba5e675ad4312ac4cb11" 35 | }, 36 | { 37 | "ImportPath": "github.com/stretchr/testify/mock", 38 | "Comment": "v1.0", 39 | "Rev": "232e8563676cd15c3a36ba5e675ad4312ac4cb11" 40 | }, 41 | { 42 | "ImportPath": "gopkg.in/alecthomas/kingpin.v2", 43 | "Comment": "v2.0.11", 44 | "Rev": "3eb8ffbc54a2f5e806181081e23098b67fe06d06" 45 | }, 46 | { 47 | "ImportPath": "gopkg.in/check.v1", 48 | "Rev": "11d3bc7aa68e238947792f30573146a3231fc0f1" 49 | }, 50 | { 51 | "ImportPath": "gopkg.in/yaml.v2", 52 | "Rev": "c1cd2254a6dd314c9d73c338c12688c9325d85c6" 53 | } 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /Godeps/Readme: -------------------------------------------------------------------------------- 1 | This directory tree is generated automatically by godep. 2 | 3 | Please do not edit. 4 | 5 | See https://github.com/tools/godep for more information. 6 | -------------------------------------------------------------------------------- /Godeps/_workspace/.gitignore: -------------------------------------------------------------------------------- 1 | /pkg 2 | /bin 3 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/.gitignore: -------------------------------------------------------------------------------- 1 | logrus 2 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.2 4 | - 1.3 5 | - 1.4 6 | - tip 7 | install: 8 | - go get -t ./... 9 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.8.5 2 | 3 | * logrus/core: revert #208 4 | 5 | # 0.8.4 6 | 7 | * formatter/text: fix data race (#218) 8 | 9 | # 0.8.3 10 | 11 | * logrus/core: fix entry log level (#208) 12 | * logrus/core: improve performance of text formatter by 40% 13 | * logrus/core: expose `LevelHooks` type 14 | * logrus/core: add support for DragonflyBSD and NetBSD 15 | * formatter/text: print structs more verbosely 16 | 17 | # 0.8.2 18 | 19 | * logrus: fix more Fatal family functions 20 | 21 | # 0.8.1 22 | 23 | * logrus: fix not exiting on `Fatalf` and `Fatalln` 24 | 25 | # 0.8.0 26 | 27 | * logrus: defaults to stderr instead of stdout 28 | * hooks/sentry: add special field for `*http.Request` 29 | * formatter/text: ignore Windows for colors 30 | 31 | # 0.7.3 32 | 33 | * formatter/\*: allow configuration of timestamp layout 34 | 35 | # 0.7.2 36 | 37 | * formatter/text: Add configuration option for time format (#158) 38 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Simon Eskildsen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/entry_test.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestEntryPanicln(t *testing.T) { 12 | errBoom := fmt.Errorf("boom time") 13 | 14 | defer func() { 15 | p := recover() 16 | assert.NotNil(t, p) 17 | 18 | switch pVal := p.(type) { 19 | case *Entry: 20 | assert.Equal(t, "kaboom", pVal.Message) 21 | assert.Equal(t, errBoom, pVal.Data["err"]) 22 | default: 23 | t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal) 24 | } 25 | }() 26 | 27 | logger := New() 28 | logger.Out = &bytes.Buffer{} 29 | entry := NewEntry(logger) 30 | entry.WithField("err", errBoom).Panicln("kaboom") 31 | } 32 | 33 | func TestEntryPanicf(t *testing.T) { 34 | errBoom := fmt.Errorf("boom again") 35 | 36 | defer func() { 37 | p := recover() 38 | assert.NotNil(t, p) 39 | 40 | switch pVal := p.(type) { 41 | case *Entry: 42 | assert.Equal(t, "kaboom true", pVal.Message) 43 | assert.Equal(t, errBoom, pVal.Data["err"]) 44 | default: 45 | t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal) 46 | } 47 | }() 48 | 49 | logger := New() 50 | logger.Out = &bytes.Buffer{} 51 | entry := NewEntry(logger) 52 | entry.WithField("err", errBoom).Panicf("kaboom %v", true) 53 | } 54 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/basic/basic.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/Sirupsen/logrus" 5 | ) 6 | 7 | var log = logrus.New() 8 | 9 | func init() { 10 | log.Formatter = new(logrus.JSONFormatter) 11 | log.Formatter = new(logrus.TextFormatter) // default 12 | log.Level = logrus.DebugLevel 13 | } 14 | 15 | func main() { 16 | defer func() { 17 | err := recover() 18 | if err != nil { 19 | log.WithFields(logrus.Fields{ 20 | "omg": true, 21 | "err": err, 22 | "number": 100, 23 | }).Fatal("The ice breaks!") 24 | } 25 | }() 26 | 27 | log.WithFields(logrus.Fields{ 28 | "animal": "walrus", 29 | "number": 8, 30 | }).Debug("Started observing beach") 31 | 32 | log.WithFields(logrus.Fields{ 33 | "animal": "walrus", 34 | "size": 10, 35 | }).Info("A group of walrus emerges from the ocean") 36 | 37 | log.WithFields(logrus.Fields{ 38 | "omg": true, 39 | "number": 122, 40 | }).Warn("The group's number increased tremendously!") 41 | 42 | log.WithFields(logrus.Fields{ 43 | "temperature": -4, 44 | }).Debug("Temperature changes") 45 | 46 | log.WithFields(logrus.Fields{ 47 | "animal": "orca", 48 | "size": 9009, 49 | }).Panic("It's over 9000!") 50 | } 51 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/hook/hook.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/Sirupsen/logrus" 5 | "github.com/Sirupsen/logrus/hooks/airbrake" 6 | ) 7 | 8 | var log = logrus.New() 9 | 10 | func init() { 11 | log.Formatter = new(logrus.TextFormatter) // default 12 | log.Hooks.Add(airbrake.NewHook("https://example.com", "xyz", "development")) 13 | } 14 | 15 | func main() { 16 | log.WithFields(logrus.Fields{ 17 | "animal": "walrus", 18 | "size": 10, 19 | }).Info("A group of walrus emerges from the ocean") 20 | 21 | log.WithFields(logrus.Fields{ 22 | "omg": true, 23 | "number": 122, 24 | }).Warn("The group's number increased tremendously!") 25 | 26 | log.WithFields(logrus.Fields{ 27 | "omg": true, 28 | "number": 100, 29 | }).Fatal("The ice breaks!") 30 | } 31 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import "time" 4 | 5 | const DefaultTimestampFormat = time.RFC3339 6 | 7 | // The Formatter interface is used to implement a custom Formatter. It takes an 8 | // `Entry`. It exposes all the fields, including the default ones: 9 | // 10 | // * `entry.Data["msg"]`. The message passed from Info, Warn, Error .. 11 | // * `entry.Data["time"]`. The timestamp. 12 | // * `entry.Data["level"]. The level the entry was logged at. 13 | // 14 | // Any additional fields added with `WithField` or `WithFields` are also in 15 | // `entry.Data`. Format is expected to return an array of bytes which are then 16 | // logged to `logger.Out`. 17 | type Formatter interface { 18 | Format(*Entry) ([]byte, error) 19 | } 20 | 21 | // This is to not silently overwrite `time`, `msg` and `level` fields when 22 | // dumping it. If this code wasn't there doing: 23 | // 24 | // logrus.WithField("level", 1).Info("hello") 25 | // 26 | // Would just silently drop the user provided level. Instead with this code 27 | // it'll logged as: 28 | // 29 | // {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."} 30 | // 31 | // It's not exported because it's still using Data in an opinionated way. It's to 32 | // avoid code duplication between the two default formatters. 33 | func prefixFieldClashes(data Fields) { 34 | _, ok := data["time"] 35 | if ok { 36 | data["fields.time"] = data["time"] 37 | } 38 | 39 | _, ok = data["msg"] 40 | if ok { 41 | data["fields.msg"] = data["msg"] 42 | } 43 | 44 | _, ok = data["level"] 45 | if ok { 46 | data["fields.level"] = data["level"] 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter_bench_test.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | // smallFields is a small size data set for benchmarking 10 | var smallFields = Fields{ 11 | "foo": "bar", 12 | "baz": "qux", 13 | "one": "two", 14 | "three": "four", 15 | } 16 | 17 | // largeFields is a large size data set for benchmarking 18 | var largeFields = Fields{ 19 | "foo": "bar", 20 | "baz": "qux", 21 | "one": "two", 22 | "three": "four", 23 | "five": "six", 24 | "seven": "eight", 25 | "nine": "ten", 26 | "eleven": "twelve", 27 | "thirteen": "fourteen", 28 | "fifteen": "sixteen", 29 | "seventeen": "eighteen", 30 | "nineteen": "twenty", 31 | "a": "b", 32 | "c": "d", 33 | "e": "f", 34 | "g": "h", 35 | "i": "j", 36 | "k": "l", 37 | "m": "n", 38 | "o": "p", 39 | "q": "r", 40 | "s": "t", 41 | "u": "v", 42 | "w": "x", 43 | "y": "z", 44 | "this": "will", 45 | "make": "thirty", 46 | "entries": "yeah", 47 | } 48 | 49 | var errorFields = Fields{ 50 | "foo": fmt.Errorf("bar"), 51 | "baz": fmt.Errorf("qux"), 52 | } 53 | 54 | func BenchmarkErrorTextFormatter(b *testing.B) { 55 | doBenchmark(b, &TextFormatter{DisableColors: true}, errorFields) 56 | } 57 | 58 | func BenchmarkSmallTextFormatter(b *testing.B) { 59 | doBenchmark(b, &TextFormatter{DisableColors: true}, smallFields) 60 | } 61 | 62 | func BenchmarkLargeTextFormatter(b *testing.B) { 63 | doBenchmark(b, &TextFormatter{DisableColors: true}, largeFields) 64 | } 65 | 66 | func BenchmarkSmallColoredTextFormatter(b *testing.B) { 67 | doBenchmark(b, &TextFormatter{ForceColors: true}, smallFields) 68 | } 69 | 70 | func BenchmarkLargeColoredTextFormatter(b *testing.B) { 71 | doBenchmark(b, &TextFormatter{ForceColors: true}, largeFields) 72 | } 73 | 74 | func BenchmarkSmallJSONFormatter(b *testing.B) { 75 | doBenchmark(b, &JSONFormatter{}, smallFields) 76 | } 77 | 78 | func BenchmarkLargeJSONFormatter(b *testing.B) { 79 | doBenchmark(b, &JSONFormatter{}, largeFields) 80 | } 81 | 82 | func doBenchmark(b *testing.B, formatter Formatter, fields Fields) { 83 | entry := &Entry{ 84 | Time: time.Time{}, 85 | Level: InfoLevel, 86 | Message: "message", 87 | Data: fields, 88 | } 89 | var d []byte 90 | var err error 91 | for i := 0; i < b.N; i++ { 92 | d, err = formatter.Format(entry) 93 | if err != nil { 94 | b.Fatal(err) 95 | } 96 | b.SetBytes(int64(len(d))) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/formatters/logstash/logstash.go: -------------------------------------------------------------------------------- 1 | package logstash 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/Sirupsen/logrus" 8 | ) 9 | 10 | // Formatter generates json in logstash format. 11 | // Logstash site: http://logstash.net/ 12 | type LogstashFormatter struct { 13 | Type string // if not empty use for logstash type field. 14 | 15 | // TimestampFormat sets the format used for timestamps. 16 | TimestampFormat string 17 | } 18 | 19 | func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) { 20 | entry.Data["@version"] = 1 21 | 22 | if f.TimestampFormat == "" { 23 | f.TimestampFormat = logrus.DefaultTimestampFormat 24 | } 25 | 26 | entry.Data["@timestamp"] = entry.Time.Format(f.TimestampFormat) 27 | 28 | // set message field 29 | v, ok := entry.Data["message"] 30 | if ok { 31 | entry.Data["fields.message"] = v 32 | } 33 | entry.Data["message"] = entry.Message 34 | 35 | // set level field 36 | v, ok = entry.Data["level"] 37 | if ok { 38 | entry.Data["fields.level"] = v 39 | } 40 | entry.Data["level"] = entry.Level.String() 41 | 42 | // set type field 43 | if f.Type != "" { 44 | v, ok = entry.Data["type"] 45 | if ok { 46 | entry.Data["fields.type"] = v 47 | } 48 | entry.Data["type"] = f.Type 49 | } 50 | 51 | serialized, err := json.Marshal(entry.Data) 52 | if err != nil { 53 | return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) 54 | } 55 | return append(serialized, '\n'), nil 56 | } 57 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go: -------------------------------------------------------------------------------- 1 | package logstash 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "github.com/Sirupsen/logrus" 7 | "github.com/stretchr/testify/assert" 8 | "testing" 9 | ) 10 | 11 | func TestLogstashFormatter(t *testing.T) { 12 | assert := assert.New(t) 13 | 14 | lf := LogstashFormatter{Type: "abc"} 15 | 16 | fields := logrus.Fields{ 17 | "message": "def", 18 | "level": "ijk", 19 | "type": "lmn", 20 | "one": 1, 21 | "pi": 3.14, 22 | "bool": true, 23 | } 24 | 25 | entry := logrus.WithFields(fields) 26 | entry.Message = "msg" 27 | entry.Level = logrus.InfoLevel 28 | 29 | b, _ := lf.Format(entry) 30 | 31 | var data map[string]interface{} 32 | dec := json.NewDecoder(bytes.NewReader(b)) 33 | dec.UseNumber() 34 | dec.Decode(&data) 35 | 36 | // base fields 37 | assert.Equal(json.Number("1"), data["@version"]) 38 | assert.NotEmpty(data["@timestamp"]) 39 | assert.Equal("abc", data["type"]) 40 | assert.Equal("msg", data["message"]) 41 | assert.Equal("info", data["level"]) 42 | 43 | // substituted fields 44 | assert.Equal("def", data["fields.message"]) 45 | assert.Equal("ijk", data["fields.level"]) 46 | assert.Equal("lmn", data["fields.type"]) 47 | 48 | // formats 49 | assert.Equal(json.Number("1"), data["one"]) 50 | assert.Equal(json.Number("3.14"), data["pi"]) 51 | assert.Equal(true, data["bool"]) 52 | } 53 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hook_test.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | type TestHook struct { 10 | Fired bool 11 | } 12 | 13 | func (hook *TestHook) Fire(entry *Entry) error { 14 | hook.Fired = true 15 | return nil 16 | } 17 | 18 | func (hook *TestHook) Levels() []Level { 19 | return []Level{ 20 | DebugLevel, 21 | InfoLevel, 22 | WarnLevel, 23 | ErrorLevel, 24 | FatalLevel, 25 | PanicLevel, 26 | } 27 | } 28 | 29 | func TestHookFires(t *testing.T) { 30 | hook := new(TestHook) 31 | 32 | LogAndAssertJSON(t, func(log *Logger) { 33 | log.Hooks.Add(hook) 34 | assert.Equal(t, hook.Fired, false) 35 | 36 | log.Print("test") 37 | }, func(fields Fields) { 38 | assert.Equal(t, hook.Fired, true) 39 | }) 40 | } 41 | 42 | type ModifyHook struct { 43 | } 44 | 45 | func (hook *ModifyHook) Fire(entry *Entry) error { 46 | entry.Data["wow"] = "whale" 47 | return nil 48 | } 49 | 50 | func (hook *ModifyHook) Levels() []Level { 51 | return []Level{ 52 | DebugLevel, 53 | InfoLevel, 54 | WarnLevel, 55 | ErrorLevel, 56 | FatalLevel, 57 | PanicLevel, 58 | } 59 | } 60 | 61 | func TestHookCanModifyEntry(t *testing.T) { 62 | hook := new(ModifyHook) 63 | 64 | LogAndAssertJSON(t, func(log *Logger) { 65 | log.Hooks.Add(hook) 66 | log.WithField("wow", "elephant").Print("test") 67 | }, func(fields Fields) { 68 | assert.Equal(t, fields["wow"], "whale") 69 | }) 70 | } 71 | 72 | func TestCanFireMultipleHooks(t *testing.T) { 73 | hook1 := new(ModifyHook) 74 | hook2 := new(TestHook) 75 | 76 | LogAndAssertJSON(t, func(log *Logger) { 77 | log.Hooks.Add(hook1) 78 | log.Hooks.Add(hook2) 79 | 80 | log.WithField("wow", "elephant").Print("test") 81 | }, func(fields Fields) { 82 | assert.Equal(t, fields["wow"], "whale") 83 | assert.Equal(t, hook2.Fired, true) 84 | }) 85 | } 86 | 87 | type ErrorHook struct { 88 | Fired bool 89 | } 90 | 91 | func (hook *ErrorHook) Fire(entry *Entry) error { 92 | hook.Fired = true 93 | return nil 94 | } 95 | 96 | func (hook *ErrorHook) Levels() []Level { 97 | return []Level{ 98 | ErrorLevel, 99 | } 100 | } 101 | 102 | func TestErrorHookShouldntFireOnInfo(t *testing.T) { 103 | hook := new(ErrorHook) 104 | 105 | LogAndAssertJSON(t, func(log *Logger) { 106 | log.Hooks.Add(hook) 107 | log.Info("test") 108 | }, func(fields Fields) { 109 | assert.Equal(t, hook.Fired, false) 110 | }) 111 | } 112 | 113 | func TestErrorHookShouldFireOnError(t *testing.T) { 114 | hook := new(ErrorHook) 115 | 116 | LogAndAssertJSON(t, func(log *Logger) { 117 | log.Hooks.Add(hook) 118 | log.Error("test") 119 | }, func(fields Fields) { 120 | assert.Equal(t, hook.Fired, true) 121 | }) 122 | } 123 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | // A hook to be fired when logging on the logging levels returned from 4 | // `Levels()` on your implementation of the interface. Note that this is not 5 | // fired in a goroutine or a channel with workers, you should handle such 6 | // functionality yourself if your call is non-blocking and you don't wish for 7 | // the logging calls for levels returned from `Levels()` to block. 8 | type Hook interface { 9 | Levels() []Level 10 | Fire(*Entry) error 11 | } 12 | 13 | // Internal type for storing the hooks on a logger instance. 14 | type LevelHooks map[Level][]Hook 15 | 16 | // Add a hook to an instance of logger. This is called with 17 | // `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface. 18 | func (hooks LevelHooks) Add(hook Hook) { 19 | for _, level := range hook.Levels() { 20 | hooks[level] = append(hooks[level], hook) 21 | } 22 | } 23 | 24 | // Fire all the hooks for the passed level. Used by `entry.log` to fire 25 | // appropriate hooks for a log entry. 26 | func (hooks LevelHooks) Fire(level Level, entry *Entry) error { 27 | for _, hook := range hooks[level] { 28 | if err := hook.Fire(entry); err != nil { 29 | return err 30 | } 31 | } 32 | 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake/airbrake.go: -------------------------------------------------------------------------------- 1 | package airbrake 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/Sirupsen/logrus" 8 | "github.com/tobi/airbrake-go" 9 | ) 10 | 11 | // AirbrakeHook to send exceptions to an exception-tracking service compatible 12 | // with the Airbrake API. 13 | type airbrakeHook struct { 14 | APIKey string 15 | Endpoint string 16 | Environment string 17 | } 18 | 19 | func NewHook(endpoint, apiKey, env string) *airbrakeHook { 20 | return &airbrakeHook{ 21 | APIKey: apiKey, 22 | Endpoint: endpoint, 23 | Environment: env, 24 | } 25 | } 26 | 27 | func (hook *airbrakeHook) Fire(entry *logrus.Entry) error { 28 | airbrake.ApiKey = hook.APIKey 29 | airbrake.Endpoint = hook.Endpoint 30 | airbrake.Environment = hook.Environment 31 | 32 | var notifyErr error 33 | err, ok := entry.Data["error"].(error) 34 | if ok { 35 | notifyErr = err 36 | } else { 37 | notifyErr = errors.New(entry.Message) 38 | } 39 | 40 | airErr := airbrake.Notify(notifyErr) 41 | if airErr != nil { 42 | return fmt.Errorf("Failed to send error to Airbrake: %s", airErr) 43 | } 44 | 45 | return nil 46 | } 47 | 48 | func (hook *airbrakeHook) Levels() []logrus.Level { 49 | return []logrus.Level{ 50 | logrus.ErrorLevel, 51 | logrus.FatalLevel, 52 | logrus.PanicLevel, 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag.go: -------------------------------------------------------------------------------- 1 | package logrus_bugsnag 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/Sirupsen/logrus" 7 | "github.com/bugsnag/bugsnag-go" 8 | ) 9 | 10 | type bugsnagHook struct{} 11 | 12 | // ErrBugsnagUnconfigured is returned if NewBugsnagHook is called before 13 | // bugsnag.Configure. Bugsnag must be configured before the hook. 14 | var ErrBugsnagUnconfigured = errors.New("bugsnag must be configured before installing this logrus hook") 15 | 16 | // ErrBugsnagSendFailed indicates that the hook failed to submit an error to 17 | // bugsnag. The error was successfully generated, but `bugsnag.Notify()` 18 | // failed. 19 | type ErrBugsnagSendFailed struct { 20 | err error 21 | } 22 | 23 | func (e ErrBugsnagSendFailed) Error() string { 24 | return "failed to send error to Bugsnag: " + e.err.Error() 25 | } 26 | 27 | // NewBugsnagHook initializes a logrus hook which sends exceptions to an 28 | // exception-tracking service compatible with the Bugsnag API. Before using 29 | // this hook, you must call bugsnag.Configure(). The returned object should be 30 | // registered with a log via `AddHook()` 31 | // 32 | // Entries that trigger an Error, Fatal or Panic should now include an "error" 33 | // field to send to Bugsnag. 34 | func NewBugsnagHook() (*bugsnagHook, error) { 35 | if bugsnag.Config.APIKey == "" { 36 | return nil, ErrBugsnagUnconfigured 37 | } 38 | return &bugsnagHook{}, nil 39 | } 40 | 41 | // Fire forwards an error to Bugsnag. Given a logrus.Entry, it extracts the 42 | // "error" field (or the Message if the error isn't present) and sends it off. 43 | func (hook *bugsnagHook) Fire(entry *logrus.Entry) error { 44 | var notifyErr error 45 | err, ok := entry.Data["error"].(error) 46 | if ok { 47 | notifyErr = err 48 | } else { 49 | notifyErr = errors.New(entry.Message) 50 | } 51 | 52 | bugsnagErr := bugsnag.Notify(notifyErr) 53 | if bugsnagErr != nil { 54 | return ErrBugsnagSendFailed{bugsnagErr} 55 | } 56 | 57 | return nil 58 | } 59 | 60 | // Levels enumerates the log levels on which the error should be forwarded to 61 | // bugsnag: everything at or above the "Error" level. 62 | func (hook *bugsnagHook) Levels() []logrus.Level { 63 | return []logrus.Level{ 64 | logrus.ErrorLevel, 65 | logrus.FatalLevel, 66 | logrus.PanicLevel, 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag_test.go: -------------------------------------------------------------------------------- 1 | package logrus_bugsnag 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "testing" 10 | "time" 11 | 12 | "github.com/Sirupsen/logrus" 13 | "github.com/bugsnag/bugsnag-go" 14 | ) 15 | 16 | type notice struct { 17 | Events []struct { 18 | Exceptions []struct { 19 | Message string `json:"message"` 20 | } `json:"exceptions"` 21 | } `json:"events"` 22 | } 23 | 24 | func TestNoticeReceived(t *testing.T) { 25 | msg := make(chan string, 1) 26 | expectedMsg := "foo" 27 | 28 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 29 | var notice notice 30 | data, _ := ioutil.ReadAll(r.Body) 31 | if err := json.Unmarshal(data, ¬ice); err != nil { 32 | t.Error(err) 33 | } 34 | _ = r.Body.Close() 35 | 36 | msg <- notice.Events[0].Exceptions[0].Message 37 | })) 38 | defer ts.Close() 39 | 40 | hook := &bugsnagHook{} 41 | 42 | bugsnag.Configure(bugsnag.Configuration{ 43 | Endpoint: ts.URL, 44 | ReleaseStage: "production", 45 | APIKey: "12345678901234567890123456789012", 46 | Synchronous: true, 47 | }) 48 | 49 | log := logrus.New() 50 | log.Hooks.Add(hook) 51 | 52 | log.WithFields(logrus.Fields{ 53 | "error": errors.New(expectedMsg), 54 | }).Error("Bugsnag will not see this string") 55 | 56 | select { 57 | case received := <-msg: 58 | if received != expectedMsg { 59 | t.Errorf("Unexpected message received: %s", received) 60 | } 61 | case <-time.After(time.Second): 62 | t.Error("Timed out; no notice received by Bugsnag API") 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/README.md: -------------------------------------------------------------------------------- 1 | # Papertrail Hook for Logrus :walrus: 2 | 3 | [Papertrail](https://papertrailapp.com) provides hosted log management. Once stored in Papertrail, you can [group](http://help.papertrailapp.com/kb/how-it-works/groups/) your logs on various dimensions, [search](http://help.papertrailapp.com/kb/how-it-works/search-syntax) them, and trigger [alerts](http://help.papertrailapp.com/kb/how-it-works/alerts). 4 | 5 | In most deployments, you'll want to send logs to Papertrail via their [remote_syslog](http://help.papertrailapp.com/kb/configuration/configuring-centralized-logging-from-text-log-files-in-unix/) daemon, which requires no application-specific configuration. This hook is intended for relatively low-volume logging, likely in managed cloud hosting deployments where installing `remote_syslog` is not possible. 6 | 7 | ## Usage 8 | 9 | You can find your Papertrail UDP port on your [Papertrail account page](https://papertrailapp.com/account/destinations). Substitute it below for `YOUR_PAPERTRAIL_UDP_PORT`. 10 | 11 | For `YOUR_APP_NAME`, substitute a short string that will readily identify your application or service in the logs. 12 | 13 | ```go 14 | import ( 15 | "log/syslog" 16 | "github.com/Sirupsen/logrus" 17 | "github.com/Sirupsen/logrus/hooks/papertrail" 18 | ) 19 | 20 | func main() { 21 | log := logrus.New() 22 | hook, err := logrus_papertrail.NewPapertrailHook("logs.papertrailapp.com", YOUR_PAPERTRAIL_UDP_PORT, YOUR_APP_NAME) 23 | 24 | if err == nil { 25 | log.Hooks.Add(hook) 26 | } 27 | } 28 | ``` 29 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail.go: -------------------------------------------------------------------------------- 1 | package logrus_papertrail 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os" 7 | "time" 8 | 9 | "github.com/Sirupsen/logrus" 10 | ) 11 | 12 | const ( 13 | format = "Jan 2 15:04:05" 14 | ) 15 | 16 | // PapertrailHook to send logs to a logging service compatible with the Papertrail API. 17 | type PapertrailHook struct { 18 | Host string 19 | Port int 20 | AppName string 21 | UDPConn net.Conn 22 | } 23 | 24 | // NewPapertrailHook creates a hook to be added to an instance of logger. 25 | func NewPapertrailHook(host string, port int, appName string) (*PapertrailHook, error) { 26 | conn, err := net.Dial("udp", fmt.Sprintf("%s:%d", host, port)) 27 | return &PapertrailHook{host, port, appName, conn}, err 28 | } 29 | 30 | // Fire is called when a log event is fired. 31 | func (hook *PapertrailHook) Fire(entry *logrus.Entry) error { 32 | date := time.Now().Format(format) 33 | msg, _ := entry.String() 34 | payload := fmt.Sprintf("<22> %s %s: %s", date, hook.AppName, msg) 35 | 36 | bytesWritten, err := hook.UDPConn.Write([]byte(payload)) 37 | if err != nil { 38 | fmt.Fprintf(os.Stderr, "Unable to send log line to Papertrail via UDP. Wrote %d bytes before error: %v", bytesWritten, err) 39 | return err 40 | } 41 | 42 | return nil 43 | } 44 | 45 | // Levels returns the available logging levels. 46 | func (hook *PapertrailHook) Levels() []logrus.Level { 47 | return []logrus.Level{ 48 | logrus.PanicLevel, 49 | logrus.FatalLevel, 50 | logrus.ErrorLevel, 51 | logrus.WarnLevel, 52 | logrus.InfoLevel, 53 | logrus.DebugLevel, 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail_test.go: -------------------------------------------------------------------------------- 1 | package logrus_papertrail 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/Sirupsen/logrus" 8 | "github.com/stvp/go-udp-testing" 9 | ) 10 | 11 | func TestWritingToUDP(t *testing.T) { 12 | port := 16661 13 | udp.SetAddr(fmt.Sprintf(":%d", port)) 14 | 15 | hook, err := NewPapertrailHook("localhost", port, "test") 16 | if err != nil { 17 | t.Errorf("Unable to connect to local UDP server.") 18 | } 19 | 20 | log := logrus.New() 21 | log.Hooks.Add(hook) 22 | 23 | udp.ShouldReceive(t, "foo", func() { 24 | log.Info("foo") 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/sentry/README.md: -------------------------------------------------------------------------------- 1 | # Sentry Hook for Logrus :walrus: 2 | 3 | [Sentry](https://getsentry.com) provides both self-hosted and hosted 4 | solutions for exception tracking. 5 | Both client and server are 6 | [open source](https://github.com/getsentry/sentry). 7 | 8 | ## Usage 9 | 10 | Every sentry application defined on the server gets a different 11 | [DSN](https://www.getsentry.com/docs/). In the example below replace 12 | `YOUR_DSN` with the one created for your application. 13 | 14 | ```go 15 | import ( 16 | "github.com/Sirupsen/logrus" 17 | "github.com/Sirupsen/logrus/hooks/sentry" 18 | ) 19 | 20 | func main() { 21 | log := logrus.New() 22 | hook, err := logrus_sentry.NewSentryHook(YOUR_DSN, []logrus.Level{ 23 | logrus.PanicLevel, 24 | logrus.FatalLevel, 25 | logrus.ErrorLevel, 26 | }) 27 | 28 | if err == nil { 29 | log.Hooks.Add(hook) 30 | } 31 | } 32 | ``` 33 | 34 | If you wish to initialize a SentryHook with tags, you can use the `NewWithTagsSentryHook` constructor to provide default tags: 35 | 36 | ```go 37 | tags := map[string]string{ 38 | "site": "example.com", 39 | } 40 | levels := []logrus.Level{ 41 | logrus.PanicLevel, 42 | logrus.FatalLevel, 43 | logrus.ErrorLevel, 44 | } 45 | hook, err := logrus_sentry.NewWithTagsSentryHook(YOUR_DSN, tags, levels) 46 | 47 | ``` 48 | 49 | 50 | ## Special fields 51 | 52 | Some logrus fields have a special meaning in this hook, 53 | these are `server_name`, `logger` and `http_request`. 54 | When logs are sent to sentry these fields are treated differently. 55 | - `server_name` (also known as hostname) is the name of the server which 56 | is logging the event (hostname.example.com) 57 | - `logger` is the part of the application which is logging the event. 58 | In go this usually means setting it to the name of the package. 59 | - `http_request` is the in-coming request(*http.Request). The detailed request data are sent to Sentry. 60 | 61 | ## Timeout 62 | 63 | `Timeout` is the time the sentry hook will wait for a response 64 | from the sentry server. 65 | 66 | If this time elapses with no response from 67 | the server an error will be returned. 68 | 69 | If `Timeout` is set to 0 the SentryHook will not wait for a reply 70 | and will assume a correct delivery. 71 | 72 | The SentryHook has a default timeout of `100 milliseconds` when created 73 | with a call to `NewSentryHook`. This can be changed by assigning a value to the `Timeout` field: 74 | 75 | ```go 76 | hook, _ := logrus_sentry.NewSentryHook(...) 77 | hook.Timeout = 20*time.Second 78 | ``` 79 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/sentry/sentry.go: -------------------------------------------------------------------------------- 1 | package logrus_sentry 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "time" 7 | 8 | "github.com/Sirupsen/logrus" 9 | "github.com/getsentry/raven-go" 10 | ) 11 | 12 | var ( 13 | severityMap = map[logrus.Level]raven.Severity{ 14 | logrus.DebugLevel: raven.DEBUG, 15 | logrus.InfoLevel: raven.INFO, 16 | logrus.WarnLevel: raven.WARNING, 17 | logrus.ErrorLevel: raven.ERROR, 18 | logrus.FatalLevel: raven.FATAL, 19 | logrus.PanicLevel: raven.FATAL, 20 | } 21 | ) 22 | 23 | func getAndDel(d logrus.Fields, key string) (string, bool) { 24 | var ( 25 | ok bool 26 | v interface{} 27 | val string 28 | ) 29 | if v, ok = d[key]; !ok { 30 | return "", false 31 | } 32 | 33 | if val, ok = v.(string); !ok { 34 | return "", false 35 | } 36 | delete(d, key) 37 | return val, true 38 | } 39 | 40 | func getAndDelRequest(d logrus.Fields, key string) (*http.Request, bool) { 41 | var ( 42 | ok bool 43 | v interface{} 44 | req *http.Request 45 | ) 46 | if v, ok = d[key]; !ok { 47 | return nil, false 48 | } 49 | if req, ok = v.(*http.Request); !ok || req == nil { 50 | return nil, false 51 | } 52 | delete(d, key) 53 | return req, true 54 | } 55 | 56 | // SentryHook delivers logs to a sentry server. 57 | type SentryHook struct { 58 | // Timeout sets the time to wait for a delivery error from the sentry server. 59 | // If this is set to zero the server will not wait for any response and will 60 | // consider the message correctly sent 61 | Timeout time.Duration 62 | 63 | client *raven.Client 64 | levels []logrus.Level 65 | } 66 | 67 | // NewSentryHook creates a hook to be added to an instance of logger 68 | // and initializes the raven client. 69 | // This method sets the timeout to 100 milliseconds. 70 | func NewSentryHook(DSN string, levels []logrus.Level) (*SentryHook, error) { 71 | client, err := raven.New(DSN) 72 | if err != nil { 73 | return nil, err 74 | } 75 | return &SentryHook{100 * time.Millisecond, client, levels}, nil 76 | } 77 | 78 | // NewWithTagsSentryHook creates a hook with tags to be added to an instance 79 | // of logger and initializes the raven client. This method sets the timeout to 80 | // 100 milliseconds. 81 | func NewWithTagsSentryHook(DSN string, tags map[string]string, levels []logrus.Level) (*SentryHook, error) { 82 | client, err := raven.NewWithTags(DSN, tags) 83 | if err != nil { 84 | return nil, err 85 | } 86 | return &SentryHook{100 * time.Millisecond, client, levels}, nil 87 | } 88 | 89 | // Called when an event should be sent to sentry 90 | // Special fields that sentry uses to give more information to the server 91 | // are extracted from entry.Data (if they are found) 92 | // These fields are: logger, server_name and http_request 93 | func (hook *SentryHook) Fire(entry *logrus.Entry) error { 94 | packet := &raven.Packet{ 95 | Message: entry.Message, 96 | Timestamp: raven.Timestamp(entry.Time), 97 | Level: severityMap[entry.Level], 98 | Platform: "go", 99 | } 100 | 101 | d := entry.Data 102 | 103 | if logger, ok := getAndDel(d, "logger"); ok { 104 | packet.Logger = logger 105 | } 106 | if serverName, ok := getAndDel(d, "server_name"); ok { 107 | packet.ServerName = serverName 108 | } 109 | if req, ok := getAndDelRequest(d, "http_request"); ok { 110 | packet.Interfaces = append(packet.Interfaces, raven.NewHttp(req)) 111 | } 112 | packet.Extra = map[string]interface{}(d) 113 | 114 | _, errCh := hook.client.Capture(packet, nil) 115 | timeout := hook.Timeout 116 | if timeout != 0 { 117 | timeoutCh := time.After(timeout) 118 | select { 119 | case err := <-errCh: 120 | return err 121 | case <-timeoutCh: 122 | return fmt.Errorf("no response from sentry server in %s", timeout) 123 | } 124 | } 125 | return nil 126 | } 127 | 128 | // Levels returns the available logging levels. 129 | func (hook *SentryHook) Levels() []logrus.Level { 130 | return hook.levels 131 | } 132 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/sentry/sentry_test.go: -------------------------------------------------------------------------------- 1 | package logrus_sentry 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "reflect" 10 | "strings" 11 | "testing" 12 | 13 | "github.com/Sirupsen/logrus" 14 | "github.com/getsentry/raven-go" 15 | ) 16 | 17 | const ( 18 | message = "error message" 19 | server_name = "testserver.internal" 20 | logger_name = "test.logger" 21 | ) 22 | 23 | func getTestLogger() *logrus.Logger { 24 | l := logrus.New() 25 | l.Out = ioutil.Discard 26 | return l 27 | } 28 | 29 | func WithTestDSN(t *testing.T, tf func(string, <-chan *raven.Packet)) { 30 | pch := make(chan *raven.Packet, 1) 31 | s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { 32 | defer req.Body.Close() 33 | d := json.NewDecoder(req.Body) 34 | p := &raven.Packet{} 35 | err := d.Decode(p) 36 | if err != nil { 37 | t.Fatal(err.Error()) 38 | } 39 | 40 | pch <- p 41 | })) 42 | defer s.Close() 43 | 44 | fragments := strings.SplitN(s.URL, "://", 2) 45 | dsn := fmt.Sprintf( 46 | "%s://public:secret@%s/sentry/project-id", 47 | fragments[0], 48 | fragments[1], 49 | ) 50 | tf(dsn, pch) 51 | } 52 | 53 | func TestSpecialFields(t *testing.T) { 54 | WithTestDSN(t, func(dsn string, pch <-chan *raven.Packet) { 55 | logger := getTestLogger() 56 | 57 | hook, err := NewSentryHook(dsn, []logrus.Level{ 58 | logrus.ErrorLevel, 59 | }) 60 | 61 | if err != nil { 62 | t.Fatal(err.Error()) 63 | } 64 | logger.Hooks.Add(hook) 65 | 66 | req, _ := http.NewRequest("GET", "url", nil) 67 | logger.WithFields(logrus.Fields{ 68 | "server_name": server_name, 69 | "logger": logger_name, 70 | "http_request": req, 71 | }).Error(message) 72 | 73 | packet := <-pch 74 | if packet.Logger != logger_name { 75 | t.Errorf("logger should have been %s, was %s", logger_name, packet.Logger) 76 | } 77 | 78 | if packet.ServerName != server_name { 79 | t.Errorf("server_name should have been %s, was %s", server_name, packet.ServerName) 80 | } 81 | }) 82 | } 83 | 84 | func TestSentryHandler(t *testing.T) { 85 | WithTestDSN(t, func(dsn string, pch <-chan *raven.Packet) { 86 | logger := getTestLogger() 87 | hook, err := NewSentryHook(dsn, []logrus.Level{ 88 | logrus.ErrorLevel, 89 | }) 90 | if err != nil { 91 | t.Fatal(err.Error()) 92 | } 93 | logger.Hooks.Add(hook) 94 | 95 | logger.Error(message) 96 | packet := <-pch 97 | if packet.Message != message { 98 | t.Errorf("message should have been %s, was %s", message, packet.Message) 99 | } 100 | }) 101 | } 102 | 103 | func TestSentryTags(t *testing.T) { 104 | WithTestDSN(t, func(dsn string, pch <-chan *raven.Packet) { 105 | logger := getTestLogger() 106 | tags := map[string]string{ 107 | "site": "test", 108 | } 109 | levels := []logrus.Level{ 110 | logrus.ErrorLevel, 111 | } 112 | 113 | hook, err := NewWithTagsSentryHook(dsn, tags, levels) 114 | if err != nil { 115 | t.Fatal(err.Error()) 116 | } 117 | 118 | logger.Hooks.Add(hook) 119 | 120 | logger.Error(message) 121 | packet := <-pch 122 | expected := raven.Tags{ 123 | raven.Tag{ 124 | Key: "site", 125 | Value: "test", 126 | }, 127 | } 128 | if !reflect.DeepEqual(packet.Tags, expected) { 129 | t.Errorf("message should have been %s, was %s", message, packet.Message) 130 | } 131 | }) 132 | } 133 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/README.md: -------------------------------------------------------------------------------- 1 | # Syslog Hooks for Logrus :walrus: 2 | 3 | ## Usage 4 | 5 | ```go 6 | import ( 7 | "log/syslog" 8 | "github.com/Sirupsen/logrus" 9 | logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog" 10 | ) 11 | 12 | func main() { 13 | log := logrus.New() 14 | hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "") 15 | 16 | if err == nil { 17 | log.Hooks.Add(hook) 18 | } 19 | } 20 | ``` 21 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog.go: -------------------------------------------------------------------------------- 1 | package logrus_syslog 2 | 3 | import ( 4 | "fmt" 5 | "github.com/Sirupsen/logrus" 6 | "log/syslog" 7 | "os" 8 | ) 9 | 10 | // SyslogHook to send logs via syslog. 11 | type SyslogHook struct { 12 | Writer *syslog.Writer 13 | SyslogNetwork string 14 | SyslogRaddr string 15 | } 16 | 17 | // Creates a hook to be added to an instance of logger. This is called with 18 | // `hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_DEBUG, "")` 19 | // `if err == nil { log.Hooks.Add(hook) }` 20 | func NewSyslogHook(network, raddr string, priority syslog.Priority, tag string) (*SyslogHook, error) { 21 | w, err := syslog.Dial(network, raddr, priority, tag) 22 | return &SyslogHook{w, network, raddr}, err 23 | } 24 | 25 | func (hook *SyslogHook) Fire(entry *logrus.Entry) error { 26 | line, err := entry.String() 27 | if err != nil { 28 | fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err) 29 | return err 30 | } 31 | 32 | switch entry.Level { 33 | case logrus.PanicLevel: 34 | return hook.Writer.Crit(line) 35 | case logrus.FatalLevel: 36 | return hook.Writer.Crit(line) 37 | case logrus.ErrorLevel: 38 | return hook.Writer.Err(line) 39 | case logrus.WarnLevel: 40 | return hook.Writer.Warning(line) 41 | case logrus.InfoLevel: 42 | return hook.Writer.Info(line) 43 | case logrus.DebugLevel: 44 | return hook.Writer.Debug(line) 45 | default: 46 | return nil 47 | } 48 | } 49 | 50 | func (hook *SyslogHook) Levels() []logrus.Level { 51 | return []logrus.Level{ 52 | logrus.PanicLevel, 53 | logrus.FatalLevel, 54 | logrus.ErrorLevel, 55 | logrus.WarnLevel, 56 | logrus.InfoLevel, 57 | logrus.DebugLevel, 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go: -------------------------------------------------------------------------------- 1 | package logrus_syslog 2 | 3 | import ( 4 | "github.com/Sirupsen/logrus" 5 | "log/syslog" 6 | "testing" 7 | ) 8 | 9 | func TestLocalhostAddAndPrint(t *testing.T) { 10 | log := logrus.New() 11 | hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "") 12 | 13 | if err != nil { 14 | t.Errorf("Unable to connect to local syslog.") 15 | } 16 | 17 | log.Hooks.Add(hook) 18 | 19 | for _, level := range hook.Levels() { 20 | if len(log.Hooks[level]) != 1 { 21 | t.Errorf("SyslogHook was not added. The length of log.Hooks[%v]: %v", level, len(log.Hooks[level])) 22 | } 23 | } 24 | 25 | log.Info("Congratulations!") 26 | } 27 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | type JSONFormatter struct { 9 | // TimestampFormat sets the format used for marshaling timestamps. 10 | TimestampFormat string 11 | } 12 | 13 | func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { 14 | data := make(Fields, len(entry.Data)+3) 15 | for k, v := range entry.Data { 16 | switch v := v.(type) { 17 | case error: 18 | // Otherwise errors are ignored by `encoding/json` 19 | // https://github.com/Sirupsen/logrus/issues/137 20 | data[k] = v.Error() 21 | default: 22 | data[k] = v 23 | } 24 | } 25 | prefixFieldClashes(data) 26 | 27 | timestampFormat := f.TimestampFormat 28 | if timestampFormat == "" { 29 | timestampFormat = DefaultTimestampFormat 30 | } 31 | 32 | data["time"] = entry.Time.Format(timestampFormat) 33 | data["msg"] = entry.Message 34 | data["level"] = entry.Level.String() 35 | 36 | serialized, err := json.Marshal(data) 37 | if err != nil { 38 | return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) 39 | } 40 | return append(serialized, '\n'), nil 41 | } 42 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter_test.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | 7 | "testing" 8 | ) 9 | 10 | func TestErrorNotLost(t *testing.T) { 11 | formatter := &JSONFormatter{} 12 | 13 | b, err := formatter.Format(WithField("error", errors.New("wild walrus"))) 14 | if err != nil { 15 | t.Fatal("Unable to format entry: ", err) 16 | } 17 | 18 | entry := make(map[string]interface{}) 19 | err = json.Unmarshal(b, &entry) 20 | if err != nil { 21 | t.Fatal("Unable to unmarshal formatted entry: ", err) 22 | } 23 | 24 | if entry["error"] != "wild walrus" { 25 | t.Fatal("Error field not set") 26 | } 27 | } 28 | 29 | func TestErrorNotLostOnFieldNotNamedError(t *testing.T) { 30 | formatter := &JSONFormatter{} 31 | 32 | b, err := formatter.Format(WithField("omg", errors.New("wild walrus"))) 33 | if err != nil { 34 | t.Fatal("Unable to format entry: ", err) 35 | } 36 | 37 | entry := make(map[string]interface{}) 38 | err = json.Unmarshal(b, &entry) 39 | if err != nil { 40 | t.Fatal("Unable to unmarshal formatted entry: ", err) 41 | } 42 | 43 | if entry["omg"] != "wild walrus" { 44 | t.Fatal("Error field not set") 45 | } 46 | } 47 | 48 | func TestFieldClashWithTime(t *testing.T) { 49 | formatter := &JSONFormatter{} 50 | 51 | b, err := formatter.Format(WithField("time", "right now!")) 52 | if err != nil { 53 | t.Fatal("Unable to format entry: ", err) 54 | } 55 | 56 | entry := make(map[string]interface{}) 57 | err = json.Unmarshal(b, &entry) 58 | if err != nil { 59 | t.Fatal("Unable to unmarshal formatted entry: ", err) 60 | } 61 | 62 | if entry["fields.time"] != "right now!" { 63 | t.Fatal("fields.time not set to original time field") 64 | } 65 | 66 | if entry["time"] != "0001-01-01T00:00:00Z" { 67 | t.Fatal("time field not set to current time, was: ", entry["time"]) 68 | } 69 | } 70 | 71 | func TestFieldClashWithMsg(t *testing.T) { 72 | formatter := &JSONFormatter{} 73 | 74 | b, err := formatter.Format(WithField("msg", "something")) 75 | if err != nil { 76 | t.Fatal("Unable to format entry: ", err) 77 | } 78 | 79 | entry := make(map[string]interface{}) 80 | err = json.Unmarshal(b, &entry) 81 | if err != nil { 82 | t.Fatal("Unable to unmarshal formatted entry: ", err) 83 | } 84 | 85 | if entry["fields.msg"] != "something" { 86 | t.Fatal("fields.msg not set to original msg field") 87 | } 88 | } 89 | 90 | func TestFieldClashWithLevel(t *testing.T) { 91 | formatter := &JSONFormatter{} 92 | 93 | b, err := formatter.Format(WithField("level", "something")) 94 | if err != nil { 95 | t.Fatal("Unable to format entry: ", err) 96 | } 97 | 98 | entry := make(map[string]interface{}) 99 | err = json.Unmarshal(b, &entry) 100 | if err != nil { 101 | t.Fatal("Unable to unmarshal formatted entry: ", err) 102 | } 103 | 104 | if entry["fields.level"] != "something" { 105 | t.Fatal("fields.level not set to original level field") 106 | } 107 | } 108 | 109 | func TestJSONEntryEndsWithNewline(t *testing.T) { 110 | formatter := &JSONFormatter{} 111 | 112 | b, err := formatter.Format(WithField("level", "something")) 113 | if err != nil { 114 | t.Fatal("Unable to format entry: ", err) 115 | } 116 | 117 | if b[len(b)-1] != '\n' { 118 | t.Fatal("Expected JSON log entry to end with a newline") 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | // Fields type, used to pass to `WithFields`. 9 | type Fields map[string]interface{} 10 | 11 | // Level type 12 | type Level uint8 13 | 14 | // Convert the Level to a string. E.g. PanicLevel becomes "panic". 15 | func (level Level) String() string { 16 | switch level { 17 | case DebugLevel: 18 | return "debug" 19 | case InfoLevel: 20 | return "info" 21 | case WarnLevel: 22 | return "warning" 23 | case ErrorLevel: 24 | return "error" 25 | case FatalLevel: 26 | return "fatal" 27 | case PanicLevel: 28 | return "panic" 29 | } 30 | 31 | return "unknown" 32 | } 33 | 34 | // ParseLevel takes a string level and returns the Logrus log level constant. 35 | func ParseLevel(lvl string) (Level, error) { 36 | switch lvl { 37 | case "panic": 38 | return PanicLevel, nil 39 | case "fatal": 40 | return FatalLevel, nil 41 | case "error": 42 | return ErrorLevel, nil 43 | case "warn", "warning": 44 | return WarnLevel, nil 45 | case "info": 46 | return InfoLevel, nil 47 | case "debug": 48 | return DebugLevel, nil 49 | } 50 | 51 | var l Level 52 | return l, fmt.Errorf("not a valid logrus Level: %q", lvl) 53 | } 54 | 55 | // These are the different logging levels. You can set the logging level to log 56 | // on your instance of logger, obtained with `logrus.New()`. 57 | const ( 58 | // PanicLevel level, highest level of severity. Logs and then calls panic with the 59 | // message passed to Debug, Info, ... 60 | PanicLevel Level = iota 61 | // FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the 62 | // logging level is set to Panic. 63 | FatalLevel 64 | // ErrorLevel level. Logs. Used for errors that should definitely be noted. 65 | // Commonly used for hooks to send errors to an error tracking service. 66 | ErrorLevel 67 | // WarnLevel level. Non-critical entries that deserve eyes. 68 | WarnLevel 69 | // InfoLevel level. General operational entries about what's going on inside the 70 | // application. 71 | InfoLevel 72 | // DebugLevel level. Usually only enabled when debugging. Very verbose logging. 73 | DebugLevel 74 | ) 75 | 76 | // Won't compile if StdLogger can't be realized by a log.Logger 77 | var _ StdLogger = &log.Logger{} 78 | 79 | // StdLogger is what your logrus-enabled library should take, that way 80 | // it'll accept a stdlib logger and a logrus logger. There's no standard 81 | // interface, this is the closest we get, unfortunately. 82 | type StdLogger interface { 83 | Print(...interface{}) 84 | Printf(string, ...interface{}) 85 | Println(...interface{}) 86 | 87 | Fatal(...interface{}) 88 | Fatalf(string, ...interface{}) 89 | Fatalln(...interface{}) 90 | 91 | Panic(...interface{}) 92 | Panicf(string, ...interface{}) 93 | Panicln(...interface{}) 94 | } 95 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_bsd.go: -------------------------------------------------------------------------------- 1 | // +build darwin freebsd openbsd netbsd dragonfly 2 | 3 | package logrus 4 | 5 | import "syscall" 6 | 7 | const ioctlReadTermios = syscall.TIOCGETA 8 | 9 | type Termios syscall.Termios 10 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_linux.go: -------------------------------------------------------------------------------- 1 | // Based on ssh/terminal: 2 | // Copyright 2013 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package logrus 7 | 8 | import "syscall" 9 | 10 | const ioctlReadTermios = syscall.TCGETS 11 | 12 | type Termios syscall.Termios 13 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_notwindows.go: -------------------------------------------------------------------------------- 1 | // Based on ssh/terminal: 2 | // Copyright 2011 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build linux darwin freebsd openbsd netbsd dragonfly 7 | 8 | package logrus 9 | 10 | import ( 11 | "syscall" 12 | "unsafe" 13 | ) 14 | 15 | // IsTerminal returns true if the given file descriptor is a terminal. 16 | func IsTerminal() bool { 17 | fd := syscall.Stdout 18 | var termios Termios 19 | _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) 20 | return err == 0 21 | } 22 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_windows.go: -------------------------------------------------------------------------------- 1 | // Based on ssh/terminal: 2 | // Copyright 2011 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build windows 7 | 8 | package logrus 9 | 10 | import ( 11 | "syscall" 12 | "unsafe" 13 | ) 14 | 15 | var kernel32 = syscall.NewLazyDLL("kernel32.dll") 16 | 17 | var ( 18 | procGetConsoleMode = kernel32.NewProc("GetConsoleMode") 19 | ) 20 | 21 | // IsTerminal returns true if the given file descriptor is a terminal. 22 | func IsTerminal() bool { 23 | fd := syscall.Stdout 24 | var st uint32 25 | r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) 26 | return r != 0 && e == 0 27 | } 28 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter_test.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestQuoting(t *testing.T) { 11 | tf := &TextFormatter{DisableColors: true} 12 | 13 | checkQuoting := func(q bool, value interface{}) { 14 | b, _ := tf.Format(WithField("test", value)) 15 | idx := bytes.Index(b, ([]byte)("test=")) 16 | cont := bytes.Contains(b[idx+5:], []byte{'"'}) 17 | if cont != q { 18 | if q { 19 | t.Errorf("quoting expected for: %#v", value) 20 | } else { 21 | t.Errorf("quoting not expected for: %#v", value) 22 | } 23 | } 24 | } 25 | 26 | checkQuoting(false, "abcd") 27 | checkQuoting(false, "v1.0") 28 | checkQuoting(false, "1234567890") 29 | checkQuoting(true, "/foobar") 30 | checkQuoting(true, "x y") 31 | checkQuoting(true, "x,y") 32 | checkQuoting(false, errors.New("invalid")) 33 | checkQuoting(true, errors.New("invalid argument")) 34 | } 35 | 36 | func TestTimestampFormat(t *testing.T) { 37 | checkTimeStr := func(format string) { 38 | customFormatter := &TextFormatter{DisableColors: true, TimestampFormat: format} 39 | customStr, _ := customFormatter.Format(WithField("test", "test")) 40 | timeStart := bytes.Index(customStr, ([]byte)("time=")) 41 | timeEnd := bytes.Index(customStr, ([]byte)("level=")) 42 | timeStr := customStr[timeStart+5 : timeEnd-1] 43 | if timeStr[0] == '"' && timeStr[len(timeStr)-1] == '"' { 44 | timeStr = timeStr[1 : len(timeStr)-1] 45 | } 46 | if format == "" { 47 | format = time.RFC3339 48 | } 49 | _, e := time.Parse(format, (string)(timeStr)) 50 | if e != nil { 51 | t.Errorf("time string \"%s\" did not match provided time format \"%s\": %s", timeStr, format, e) 52 | } 53 | } 54 | 55 | checkTimeStr("2006-01-02T15:04:05.000000000Z07:00") 56 | checkTimeStr("Mon Jan _2 15:04:05 2006") 57 | checkTimeStr("") 58 | } 59 | 60 | // TODO add tests for sorting etc., this requires a parser for the text 61 | // formatter output. 62 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/Sirupsen/logrus/writer.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | "runtime" 7 | ) 8 | 9 | func (logger *Logger) Writer() *io.PipeWriter { 10 | reader, writer := io.Pipe() 11 | 12 | go logger.writerScanner(reader) 13 | runtime.SetFinalizer(writer, writerFinalizer) 14 | 15 | return writer 16 | } 17 | 18 | func (logger *Logger) writerScanner(reader *io.PipeReader) { 19 | scanner := bufio.NewScanner(reader) 20 | for scanner.Scan() { 21 | logger.Print(scanner.Text()) 22 | } 23 | if err := scanner.Err(); err != nil { 24 | logger.Errorf("Error while reading from Writer: %s", err) 25 | } 26 | reader.Close() 27 | } 28 | 29 | func writerFinalizer(writer *io.PipeWriter) { 30 | writer.Close() 31 | } 32 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/template/README.md: -------------------------------------------------------------------------------- 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 | ``` 26 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/template/example_test.go: -------------------------------------------------------------------------------- 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 | "log" 9 | "os" 10 | "github.com/alecthomas/template" 11 | ) 12 | 13 | func ExampleTemplate() { 14 | // Define a template. 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 | } 72 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/template/examplefunc_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package template_test 6 | 7 | import ( 8 | "log" 9 | "os" 10 | "strings" 11 | "github.com/alecthomas/template" 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 | } 55 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/template/testdata/file1.tmpl: -------------------------------------------------------------------------------- 1 | {{define "x"}}TEXT{{end}} 2 | {{define "dotV"}}{{.V}}{{end}} 3 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/template/testdata/file2.tmpl: -------------------------------------------------------------------------------- 1 | {{define "dot"}}{{.}}{{end}} 2 | {{define "nested"}}{{template "dot" .}}{{end}} 3 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/template/testdata/tmpl1.tmpl: -------------------------------------------------------------------------------- 1 | template1 2 | {{define "x"}}x{{end}} 3 | {{template "y"}} 4 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/template/testdata/tmpl2.tmpl: -------------------------------------------------------------------------------- 1 | template2 2 | {{define "y"}}y{{end}} 3 | {{template "x"}} 4 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/units/COPYING: -------------------------------------------------------------------------------- 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. 20 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/units/README.md: -------------------------------------------------------------------------------- 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 | ``` 12 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/units/bytes.go: -------------------------------------------------------------------------------- 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 | } 84 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/units/bytes_test.go: -------------------------------------------------------------------------------- 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 | } 50 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/units/doc.go: -------------------------------------------------------------------------------- 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 14 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/units/si.go: -------------------------------------------------------------------------------- 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 | } 27 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/alecthomas/units/util.go: -------------------------------------------------------------------------------- 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 | } 139 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/nfnt/resize/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.1 5 | - 1.2 6 | - 1.3 7 | - tip 8 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/nfnt/resize/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, Jan Schlicht 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose 4 | with or without fee is hereby granted, provided that the above copyright notice 5 | and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 9 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 11 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 12 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 13 | THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/nfnt/resize/converter_test.go: -------------------------------------------------------------------------------- 1 | package resize 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_ClampUint8(t *testing.T) { 8 | var testData = []struct { 9 | in int32 10 | expected uint8 11 | }{ 12 | {0, 0}, 13 | {255, 255}, 14 | {128, 128}, 15 | {-2, 0}, 16 | {256, 255}, 17 | } 18 | for _, test := range testData { 19 | actual := clampUint8(test.in) 20 | if actual != test.expected { 21 | t.Fail() 22 | } 23 | } 24 | } 25 | 26 | func Test_ClampUint16(t *testing.T) { 27 | var testData = []struct { 28 | in int64 29 | expected uint16 30 | }{ 31 | {0, 0}, 32 | {65535, 65535}, 33 | {128, 128}, 34 | {-2, 0}, 35 | {65536, 65535}, 36 | } 37 | for _, test := range testData { 38 | actual := clampUint16(test.in) 39 | if actual != test.expected { 40 | t.Fail() 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/nfnt/resize/nearest_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014, Charlie Vieth 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any purpose 5 | with or without fee is hereby granted, provided that the above copyright notice 6 | and this permission notice appear in all copies. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 10 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 12 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 13 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 14 | THIS SOFTWARE. 15 | */ 16 | 17 | package resize 18 | 19 | import "testing" 20 | 21 | func Test_FloatToUint8(t *testing.T) { 22 | var testData = []struct { 23 | in float32 24 | expected uint8 25 | }{ 26 | {0, 0}, 27 | {255, 255}, 28 | {128, 128}, 29 | {1, 1}, 30 | {256, 255}, 31 | } 32 | for _, test := range testData { 33 | actual := floatToUint8(test.in) 34 | if actual != test.expected { 35 | t.Fail() 36 | } 37 | } 38 | } 39 | 40 | func Test_FloatToUint16(t *testing.T) { 41 | var testData = []struct { 42 | in float32 43 | expected uint16 44 | }{ 45 | {0, 0}, 46 | {65535, 65535}, 47 | {128, 128}, 48 | {1, 1}, 49 | {65536, 65535}, 50 | } 51 | for _, test := range testData { 52 | actual := floatToUint16(test.in) 53 | if actual != test.expected { 54 | t.Fail() 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/nfnt/resize/thumbnail.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Jan Schlicht 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any purpose 5 | with or without fee is hereby granted, provided that the above copyright notice 6 | and this permission notice appear in all copies. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 10 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 12 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 13 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 14 | THIS SOFTWARE. 15 | */ 16 | 17 | package resize 18 | 19 | import ( 20 | "image" 21 | ) 22 | 23 | // Thumbnail will downscale provided image to max width and height preserving 24 | // original aspect ratio and using the interpolation function interp. 25 | // It will return original image, without processing it, if original sizes 26 | // are already smaller than provided constraints. 27 | func Thumbnail(maxWidth, maxHeight uint, img image.Image, interp InterpolationFunction) image.Image { 28 | origBounds := img.Bounds() 29 | origWidth := uint(origBounds.Dx()) 30 | origHeight := uint(origBounds.Dy()) 31 | newWidth, newHeight := origWidth, origHeight 32 | 33 | // Return original image if it have same or smaller size as constraints 34 | if maxWidth >= origWidth && maxHeight >= origHeight { 35 | return img 36 | } 37 | 38 | // Preserve aspect ratio 39 | if origWidth > maxWidth { 40 | newHeight = uint(origHeight * maxWidth / origWidth) 41 | if newHeight < 1 { 42 | newHeight = 1 43 | } 44 | newWidth = maxWidth 45 | } 46 | 47 | if newHeight > maxHeight { 48 | newWidth = uint(newWidth * maxHeight / newHeight) 49 | if newWidth < 1 { 50 | newWidth = 1 51 | } 52 | newHeight = maxHeight 53 | } 54 | return Resize(newWidth, newHeight, img, interp) 55 | } 56 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/nfnt/resize/thumbnail_test.go: -------------------------------------------------------------------------------- 1 | package resize 2 | 3 | import ( 4 | "image" 5 | "runtime" 6 | "testing" 7 | ) 8 | 9 | func init() { 10 | runtime.GOMAXPROCS(runtime.NumCPU()) 11 | } 12 | 13 | var thumbnailTests = []struct { 14 | origWidth int 15 | origHeight int 16 | maxWidth uint 17 | maxHeight uint 18 | expectedWidth uint 19 | expectedHeight uint 20 | }{ 21 | {5, 5, 10, 10, 5, 5}, 22 | {10, 10, 5, 5, 5, 5}, 23 | {10, 50, 10, 10, 2, 10}, 24 | {50, 10, 10, 10, 10, 2}, 25 | {50, 100, 60, 90, 45, 90}, 26 | {120, 100, 60, 90, 60, 50}, 27 | {200, 250, 200, 150, 120, 150}, 28 | } 29 | 30 | func TestThumbnail(t *testing.T) { 31 | for i, tt := range thumbnailTests { 32 | img := image.NewGray16(image.Rect(0, 0, tt.origWidth, tt.origHeight)) 33 | 34 | outImg := Thumbnail(tt.maxWidth, tt.maxHeight, img, NearestNeighbor) 35 | 36 | newWidth := uint(outImg.Bounds().Dx()) 37 | newHeight := uint(outImg.Bounds().Dy()) 38 | if newWidth != tt.expectedWidth || 39 | newHeight != tt.expectedHeight { 40 | t.Errorf("%d. Thumbnail(%v, %v, img, NearestNeighbor) => "+ 41 | "width: %v, height: %v, want width: %v, height: %v", 42 | i, tt.maxWidth, tt.maxHeight, 43 | newWidth, newHeight, tt.expectedWidth, tt.expectedHeight, 44 | ) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/objx/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/objx/LICENSE.md: -------------------------------------------------------------------------------- 1 | objx - by Mat Ryer and Tyler Bunnell 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2014 Stretchr, Inc. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/objx/README.md: -------------------------------------------------------------------------------- 1 | # objx 2 | 3 | * Jump into the [API Documentation](http://godoc.org/github.com/stretchr/objx) 4 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/objx/codegen/array-access.txt: -------------------------------------------------------------------------------- 1 | case []{1}: 2 | a := object.([]{1}) 3 | if isSet { 4 | a[index] = value.({1}) 5 | } else { 6 | if index >= len(a) { 7 | if panics { 8 | panic(fmt.Sprintf("objx: Index %d is out of range because the []{1} only contains %d items.", index, len(a))) 9 | } 10 | return nil 11 | } else { 12 | return a[index] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/objx/codegen/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Codegen 5 | 6 | 17 | 18 | 19 | 20 |

21 | Template 22 |

23 |

24 | Use {x} as a placeholder for each argument. 25 |

26 | 27 | 28 |

29 | Arguments (comma separated) 30 |

31 |

32 | One block per line 33 |

34 | 35 | 36 |

37 | Output 38 |

39 | 40 | 41 | 42 | 43 | 44 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/objx/codegen/types_list.txt: -------------------------------------------------------------------------------- 1 | Interface,interface{},"something",nil,Inter 2 | Map,map[string]interface{},map[string]interface{}{"name":"Tyler"},nil,MSI 3 | ObjxMap,(Map),New(1),New(nil),ObjxMap 4 | Bool,bool,true,false,Bool 5 | String,string,"hello","",Str 6 | Int,int,1,0,Int 7 | Int8,int8,1,0,Int8 8 | Int16,int16,1,0,Int16 9 | Int32,int32,1,0,Int32 10 | Int64,int64,1,0,Int64 11 | Uint,uint,1,0,Uint 12 | Uint8,uint8,1,0,Uint8 13 | Uint16,uint16,1,0,Uint16 14 | Uint32,uint32,1,0,Uint32 15 | Uint64,uint64,1,0,Uint64 16 | Uintptr,uintptr,1,0,Uintptr 17 | Float32,float32,1,0,Float32 18 | Float64,float64,1,0,Float64 19 | Complex64,complex64,1,0,Complex64 20 | Complex128,complex128,1,0,Complex128 21 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/objx/constants.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | const ( 4 | // PathSeparator is the character used to separate the elements 5 | // of the keypath. 6 | // 7 | // For example, `location.address.city` 8 | PathSeparator string = "." 9 | 10 | // SignatureSeparator is the character that is used to 11 | // separate the Base64 string from the security signature. 12 | SignatureSeparator = "_" 13 | ) 14 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/objx/conversions.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | import ( 4 | "bytes" 5 | "encoding/base64" 6 | "encoding/json" 7 | "errors" 8 | "fmt" 9 | "net/url" 10 | ) 11 | 12 | // JSON converts the contained object to a JSON string 13 | // representation 14 | func (m Map) JSON() (string, error) { 15 | 16 | result, err := json.Marshal(m) 17 | 18 | if err != nil { 19 | err = errors.New("objx: JSON encode failed with: " + err.Error()) 20 | } 21 | 22 | return string(result), err 23 | 24 | } 25 | 26 | // MustJSON converts the contained object to a JSON string 27 | // representation and panics if there is an error 28 | func (m Map) MustJSON() string { 29 | result, err := m.JSON() 30 | if err != nil { 31 | panic(err.Error()) 32 | } 33 | return result 34 | } 35 | 36 | // Base64 converts the contained object to a Base64 string 37 | // representation of the JSON string representation 38 | func (m Map) Base64() (string, error) { 39 | 40 | var buf bytes.Buffer 41 | 42 | jsonData, err := m.JSON() 43 | if err != nil { 44 | return "", err 45 | } 46 | 47 | encoder := base64.NewEncoder(base64.StdEncoding, &buf) 48 | encoder.Write([]byte(jsonData)) 49 | encoder.Close() 50 | 51 | return buf.String(), nil 52 | 53 | } 54 | 55 | // MustBase64 converts the contained object to a Base64 string 56 | // representation of the JSON string representation and panics 57 | // if there is an error 58 | func (m Map) MustBase64() string { 59 | result, err := m.Base64() 60 | if err != nil { 61 | panic(err.Error()) 62 | } 63 | return result 64 | } 65 | 66 | // SignedBase64 converts the contained object to a Base64 string 67 | // representation of the JSON string representation and signs it 68 | // using the provided key. 69 | func (m Map) SignedBase64(key string) (string, error) { 70 | 71 | base64, err := m.Base64() 72 | if err != nil { 73 | return "", err 74 | } 75 | 76 | sig := HashWithKey(base64, key) 77 | 78 | return base64 + SignatureSeparator + sig, nil 79 | 80 | } 81 | 82 | // MustSignedBase64 converts the contained object to a Base64 string 83 | // representation of the JSON string representation and signs it 84 | // using the provided key and panics if there is an error 85 | func (m Map) MustSignedBase64(key string) string { 86 | result, err := m.SignedBase64(key) 87 | if err != nil { 88 | panic(err.Error()) 89 | } 90 | return result 91 | } 92 | 93 | /* 94 | URL Query 95 | ------------------------------------------------ 96 | */ 97 | 98 | // URLValues creates a url.Values object from an Obj. This 99 | // function requires that the wrapped object be a map[string]interface{} 100 | func (m Map) URLValues() url.Values { 101 | 102 | vals := make(url.Values) 103 | 104 | for k, v := range m { 105 | //TODO: can this be done without sprintf? 106 | vals.Set(k, fmt.Sprintf("%v", v)) 107 | } 108 | 109 | return vals 110 | } 111 | 112 | // URLQuery gets an encoded URL query representing the given 113 | // Obj. This function requires that the wrapped object be a 114 | // map[string]interface{} 115 | func (m Map) URLQuery() (string, error) { 116 | return m.URLValues().Encode(), nil 117 | } 118 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/objx/conversions_test.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestConversionJSON(t *testing.T) { 9 | 10 | jsonString := `{"name":"Mat"}` 11 | o := MustFromJSON(jsonString) 12 | 13 | result, err := o.JSON() 14 | 15 | if assert.NoError(t, err) { 16 | assert.Equal(t, jsonString, result) 17 | } 18 | 19 | assert.Equal(t, jsonString, o.MustJSON()) 20 | 21 | } 22 | 23 | func TestConversionJSONWithError(t *testing.T) { 24 | 25 | o := MSI() 26 | o["test"] = func() {} 27 | 28 | assert.Panics(t, func() { 29 | o.MustJSON() 30 | }) 31 | 32 | _, err := o.JSON() 33 | 34 | assert.Error(t, err) 35 | 36 | } 37 | 38 | func TestConversionBase64(t *testing.T) { 39 | 40 | o := New(map[string]interface{}{"name": "Mat"}) 41 | 42 | result, err := o.Base64() 43 | 44 | if assert.NoError(t, err) { 45 | assert.Equal(t, "eyJuYW1lIjoiTWF0In0=", result) 46 | } 47 | 48 | assert.Equal(t, "eyJuYW1lIjoiTWF0In0=", o.MustBase64()) 49 | 50 | } 51 | 52 | func TestConversionBase64WithError(t *testing.T) { 53 | 54 | o := MSI() 55 | o["test"] = func() {} 56 | 57 | assert.Panics(t, func() { 58 | o.MustBase64() 59 | }) 60 | 61 | _, err := o.Base64() 62 | 63 | assert.Error(t, err) 64 | 65 | } 66 | 67 | func TestConversionSignedBase64(t *testing.T) { 68 | 69 | o := New(map[string]interface{}{"name": "Mat"}) 70 | 71 | result, err := o.SignedBase64("key") 72 | 73 | if assert.NoError(t, err) { 74 | assert.Equal(t, "eyJuYW1lIjoiTWF0In0=_67ee82916f90b2c0d68c903266e8998c9ef0c3d6", result) 75 | } 76 | 77 | assert.Equal(t, "eyJuYW1lIjoiTWF0In0=_67ee82916f90b2c0d68c903266e8998c9ef0c3d6", o.MustSignedBase64("key")) 78 | 79 | } 80 | 81 | func TestConversionSignedBase64WithError(t *testing.T) { 82 | 83 | o := MSI() 84 | o["test"] = func() {} 85 | 86 | assert.Panics(t, func() { 87 | o.MustSignedBase64("key") 88 | }) 89 | 90 | _, err := o.SignedBase64("key") 91 | 92 | assert.Error(t, err) 93 | 94 | } 95 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/objx/doc.go: -------------------------------------------------------------------------------- 1 | // objx - Go package for dealing with maps, slices, JSON and other data. 2 | // 3 | // Overview 4 | // 5 | // Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes 6 | // a powerful `Get` method (among others) that allows you to easily and quickly get 7 | // access to data within the map, without having to worry too much about type assertions, 8 | // missing data, default values etc. 9 | // 10 | // Pattern 11 | // 12 | // Objx uses a preditable pattern to make access data from within `map[string]interface{}'s 13 | // easy. 14 | // 15 | // Call one of the `objx.` functions to create your `objx.Map` to get going: 16 | // 17 | // m, err := objx.FromJSON(json) 18 | // 19 | // NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong, 20 | // the rest will be optimistic and try to figure things out without panicking. 21 | // 22 | // Use `Get` to access the value you're interested in. You can use dot and array 23 | // notation too: 24 | // 25 | // m.Get("places[0].latlng") 26 | // 27 | // Once you have saught the `Value` you're interested in, you can use the `Is*` methods 28 | // to determine its type. 29 | // 30 | // if m.Get("code").IsStr() { /* ... */ } 31 | // 32 | // Or you can just assume the type, and use one of the strong type methods to 33 | // extract the real value: 34 | // 35 | // m.Get("code").Int() 36 | // 37 | // If there's no value there (or if it's the wrong type) then a default value 38 | // will be returned, or you can be explicit about the default value. 39 | // 40 | // Get("code").Int(-1) 41 | // 42 | // If you're dealing with a slice of data as a value, Objx provides many useful 43 | // methods for iterating, manipulating and selecting that data. You can find out more 44 | // by exploring the index below. 45 | // 46 | // Reading data 47 | // 48 | // A simple example of how to use Objx: 49 | // 50 | // // use MustFromJSON to make an objx.Map from some JSON 51 | // m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`) 52 | // 53 | // // get the details 54 | // name := m.Get("name").Str() 55 | // age := m.Get("age").Int() 56 | // 57 | // // get their nickname (or use their name if they 58 | // // don't have one) 59 | // nickname := m.Get("nickname").Str(name) 60 | // 61 | // Ranging 62 | // 63 | // Since `objx.Map` is a `map[string]interface{}` you can treat it as such. For 64 | // example, to `range` the data, do what you would expect: 65 | // 66 | // m := objx.MustFromJSON(json) 67 | // for key, value := range m { 68 | // 69 | // /* ... do your magic ... */ 70 | // 71 | // } 72 | package objx 73 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/objx/fixture_test.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | var fixtures = []struct { 9 | // name is the name of the fixture (used for reporting 10 | // failures) 11 | name string 12 | // data is the JSON data to be worked on 13 | data string 14 | // get is the argument(s) to pass to Get 15 | get interface{} 16 | // output is the expected output 17 | output interface{} 18 | }{ 19 | { 20 | name: "Simple get", 21 | data: `{"name": "Mat"}`, 22 | get: "name", 23 | output: "Mat", 24 | }, 25 | { 26 | name: "Get with dot notation", 27 | data: `{"address": {"city": "Boulder"}}`, 28 | get: "address.city", 29 | output: "Boulder", 30 | }, 31 | { 32 | name: "Deep get with dot notation", 33 | data: `{"one": {"two": {"three": {"four": "hello"}}}}`, 34 | get: "one.two.three.four", 35 | output: "hello", 36 | }, 37 | { 38 | name: "Get missing with dot notation", 39 | data: `{"one": {"two": {"three": {"four": "hello"}}}}`, 40 | get: "one.ten", 41 | output: nil, 42 | }, 43 | { 44 | name: "Get with array notation", 45 | data: `{"tags": ["one", "two", "three"]}`, 46 | get: "tags[1]", 47 | output: "two", 48 | }, 49 | { 50 | name: "Get with array and dot notation", 51 | data: `{"types": { "tags": ["one", "two", "three"]}}`, 52 | get: "types.tags[1]", 53 | output: "two", 54 | }, 55 | { 56 | name: "Get with array and dot notation - field after array", 57 | data: `{"tags": [{"name":"one"}, {"name":"two"}, {"name":"three"}]}`, 58 | get: "tags[1].name", 59 | output: "two", 60 | }, 61 | { 62 | name: "Complex get with array and dot notation", 63 | data: `{"tags": [{"list": [{"one":"pizza"}]}]}`, 64 | get: "tags[0].list[0].one", 65 | output: "pizza", 66 | }, 67 | { 68 | name: "Get field from within string should be nil", 69 | data: `{"name":"Tyler"}`, 70 | get: "name.something", 71 | output: nil, 72 | }, 73 | { 74 | name: "Get field from within string (using array accessor) should be nil", 75 | data: `{"numbers":["one", "two", "three"]}`, 76 | get: "numbers[0].nope", 77 | output: nil, 78 | }, 79 | } 80 | 81 | func TestFixtures(t *testing.T) { 82 | 83 | for _, fixture := range fixtures { 84 | 85 | m := MustFromJSON(fixture.data) 86 | 87 | // get the value 88 | t.Logf("Running get fixture: \"%s\" (%v)", fixture.name, fixture) 89 | value := m.Get(fixture.get.(string)) 90 | 91 | // make sure it matches 92 | assert.Equal(t, fixture.output, value.data, 93 | "Get fixture \"%s\" failed: %v", fixture.name, fixture, 94 | ) 95 | 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/objx/map_for_test.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | var TestMap map[string]interface{} = map[string]interface{}{ 4 | "name": "Tyler", 5 | "address": map[string]interface{}{ 6 | "city": "Salt Lake City", 7 | "state": "UT", 8 | }, 9 | "numbers": []interface{}{"one", "two", "three", "four", "five"}, 10 | } 11 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/objx/map_test.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | type Convertable struct { 9 | name string 10 | } 11 | 12 | func (c *Convertable) MSI() map[string]interface{} { 13 | return map[string]interface{}{"name": c.name} 14 | } 15 | 16 | type Unconvertable struct { 17 | name string 18 | } 19 | 20 | func TestMapCreation(t *testing.T) { 21 | 22 | o := New(nil) 23 | assert.Nil(t, o) 24 | 25 | o = New("Tyler") 26 | assert.Nil(t, o) 27 | 28 | unconvertable := &Unconvertable{name: "Tyler"} 29 | o = New(unconvertable) 30 | assert.Nil(t, o) 31 | 32 | convertable := &Convertable{name: "Tyler"} 33 | o = New(convertable) 34 | if assert.NotNil(t, convertable) { 35 | assert.Equal(t, "Tyler", o["name"], "Tyler") 36 | } 37 | 38 | o = MSI() 39 | if assert.NotNil(t, o) { 40 | assert.NotNil(t, o) 41 | } 42 | 43 | o = MSI("name", "Tyler") 44 | if assert.NotNil(t, o) { 45 | if assert.NotNil(t, o) { 46 | assert.Equal(t, o["name"], "Tyler") 47 | } 48 | } 49 | 50 | } 51 | 52 | func TestMapMustFromJSONWithError(t *testing.T) { 53 | 54 | _, err := FromJSON(`"name":"Mat"}`) 55 | assert.Error(t, err) 56 | 57 | } 58 | 59 | func TestMapFromJSON(t *testing.T) { 60 | 61 | o := MustFromJSON(`{"name":"Mat"}`) 62 | 63 | if assert.NotNil(t, o) { 64 | if assert.NotNil(t, o) { 65 | assert.Equal(t, "Mat", o["name"]) 66 | } 67 | } 68 | 69 | } 70 | 71 | func TestMapFromJSONWithError(t *testing.T) { 72 | 73 | var m Map 74 | 75 | assert.Panics(t, func() { 76 | m = MustFromJSON(`"name":"Mat"}`) 77 | }) 78 | 79 | assert.Nil(t, m) 80 | 81 | } 82 | 83 | func TestMapFromBase64String(t *testing.T) { 84 | 85 | base64String := "eyJuYW1lIjoiTWF0In0=" 86 | 87 | o, err := FromBase64(base64String) 88 | 89 | if assert.NoError(t, err) { 90 | assert.Equal(t, o.Get("name").Str(), "Mat") 91 | } 92 | 93 | assert.Equal(t, MustFromBase64(base64String).Get("name").Str(), "Mat") 94 | 95 | } 96 | 97 | func TestMapFromBase64StringWithError(t *testing.T) { 98 | 99 | base64String := "eyJuYW1lIjoiTWFasd0In0=" 100 | 101 | _, err := FromBase64(base64String) 102 | 103 | assert.Error(t, err) 104 | 105 | assert.Panics(t, func() { 106 | MustFromBase64(base64String) 107 | }) 108 | 109 | } 110 | 111 | func TestMapFromSignedBase64String(t *testing.T) { 112 | 113 | base64String := "eyJuYW1lIjoiTWF0In0=_67ee82916f90b2c0d68c903266e8998c9ef0c3d6" 114 | 115 | o, err := FromSignedBase64(base64String, "key") 116 | 117 | if assert.NoError(t, err) { 118 | assert.Equal(t, o.Get("name").Str(), "Mat") 119 | } 120 | 121 | assert.Equal(t, MustFromSignedBase64(base64String, "key").Get("name").Str(), "Mat") 122 | 123 | } 124 | 125 | func TestMapFromSignedBase64StringWithError(t *testing.T) { 126 | 127 | base64String := "eyJuYW1lasdIjoiTWF0In0=_67ee82916f90b2c0d68c903266e8998c9ef0c3d6" 128 | 129 | _, err := FromSignedBase64(base64String, "key") 130 | 131 | assert.Error(t, err) 132 | 133 | assert.Panics(t, func() { 134 | MustFromSignedBase64(base64String, "key") 135 | }) 136 | 137 | } 138 | 139 | func TestMapFromURLQuery(t *testing.T) { 140 | 141 | m, err := FromURLQuery("name=tyler&state=UT") 142 | if assert.NoError(t, err) && assert.NotNil(t, m) { 143 | assert.Equal(t, "tyler", m.Get("name").Str()) 144 | assert.Equal(t, "UT", m.Get("state").Str()) 145 | } 146 | 147 | } 148 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/objx/mutations.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | // Exclude returns a new Map with the keys in the specified []string 4 | // excluded. 5 | func (d Map) Exclude(exclude []string) Map { 6 | 7 | excluded := make(Map) 8 | for k, v := range d { 9 | var shouldInclude bool = true 10 | for _, toExclude := range exclude { 11 | if k == toExclude { 12 | shouldInclude = false 13 | break 14 | } 15 | } 16 | if shouldInclude { 17 | excluded[k] = v 18 | } 19 | } 20 | 21 | return excluded 22 | } 23 | 24 | // Copy creates a shallow copy of the Obj. 25 | func (m Map) Copy() Map { 26 | copied := make(map[string]interface{}) 27 | for k, v := range m { 28 | copied[k] = v 29 | } 30 | return New(copied) 31 | } 32 | 33 | // Merge blends the specified map with a copy of this map and returns the result. 34 | // 35 | // Keys that appear in both will be selected from the specified map. 36 | // This method requires that the wrapped object be a map[string]interface{} 37 | func (m Map) Merge(merge Map) Map { 38 | return m.Copy().MergeHere(merge) 39 | } 40 | 41 | // Merge blends the specified map with this map and returns the current map. 42 | // 43 | // Keys that appear in both will be selected from the specified map. The original map 44 | // will be modified. This method requires that 45 | // the wrapped object be a map[string]interface{} 46 | func (m Map) MergeHere(merge Map) Map { 47 | 48 | for k, v := range merge { 49 | m[k] = v 50 | } 51 | 52 | return m 53 | 54 | } 55 | 56 | // Transform builds a new Obj giving the transformer a chance 57 | // to change the keys and values as it goes. This method requires that 58 | // the wrapped object be a map[string]interface{} 59 | func (m Map) Transform(transformer func(key string, value interface{}) (string, interface{})) Map { 60 | newMap := make(map[string]interface{}) 61 | for k, v := range m { 62 | modifiedKey, modifiedVal := transformer(k, v) 63 | newMap[modifiedKey] = modifiedVal 64 | } 65 | return New(newMap) 66 | } 67 | 68 | // TransformKeys builds a new map using the specified key mapping. 69 | // 70 | // Unspecified keys will be unaltered. 71 | // This method requires that the wrapped object be a map[string]interface{} 72 | func (m Map) TransformKeys(mapping map[string]string) Map { 73 | return m.Transform(func(key string, value interface{}) (string, interface{}) { 74 | 75 | if newKey, ok := mapping[key]; ok { 76 | return newKey, value 77 | } 78 | 79 | return key, value 80 | }) 81 | } 82 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/objx/mutations_test.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestExclude(t *testing.T) { 9 | 10 | d := make(Map) 11 | d["name"] = "Mat" 12 | d["age"] = 29 13 | d["secret"] = "ABC" 14 | 15 | excluded := d.Exclude([]string{"secret"}) 16 | 17 | assert.Equal(t, d["name"], excluded["name"]) 18 | assert.Equal(t, d["age"], excluded["age"]) 19 | assert.False(t, excluded.Has("secret"), "secret should be excluded") 20 | 21 | } 22 | 23 | func TestCopy(t *testing.T) { 24 | 25 | d1 := make(map[string]interface{}) 26 | d1["name"] = "Tyler" 27 | d1["location"] = "UT" 28 | 29 | d1Obj := New(d1) 30 | d2Obj := d1Obj.Copy() 31 | 32 | d2Obj["name"] = "Mat" 33 | 34 | assert.Equal(t, d1Obj.Get("name").Str(), "Tyler") 35 | assert.Equal(t, d2Obj.Get("name").Str(), "Mat") 36 | 37 | } 38 | 39 | func TestMerge(t *testing.T) { 40 | 41 | d := make(map[string]interface{}) 42 | d["name"] = "Mat" 43 | 44 | d1 := make(map[string]interface{}) 45 | d1["name"] = "Tyler" 46 | d1["location"] = "UT" 47 | 48 | dObj := New(d) 49 | d1Obj := New(d1) 50 | 51 | merged := dObj.Merge(d1Obj) 52 | 53 | assert.Equal(t, merged.Get("name").Str(), d1Obj.Get("name").Str()) 54 | assert.Equal(t, merged.Get("location").Str(), d1Obj.Get("location").Str()) 55 | assert.Empty(t, dObj.Get("location").Str()) 56 | 57 | } 58 | 59 | func TestMergeHere(t *testing.T) { 60 | 61 | d := make(map[string]interface{}) 62 | d["name"] = "Mat" 63 | 64 | d1 := make(map[string]interface{}) 65 | d1["name"] = "Tyler" 66 | d1["location"] = "UT" 67 | 68 | dObj := New(d) 69 | d1Obj := New(d1) 70 | 71 | merged := dObj.MergeHere(d1Obj) 72 | 73 | assert.Equal(t, dObj, merged, "With MergeHere, it should return the first modified map") 74 | assert.Equal(t, merged.Get("name").Str(), d1Obj.Get("name").Str()) 75 | assert.Equal(t, merged.Get("location").Str(), d1Obj.Get("location").Str()) 76 | assert.Equal(t, merged.Get("location").Str(), dObj.Get("location").Str()) 77 | } 78 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/objx/security.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | import ( 4 | "crypto/sha1" 5 | "encoding/hex" 6 | ) 7 | 8 | // HashWithKey hashes the specified string using the security 9 | // key. 10 | func HashWithKey(data, key string) string { 11 | hash := sha1.New() 12 | hash.Write([]byte(data + ":" + key)) 13 | return hex.EncodeToString(hash.Sum(nil)) 14 | } 15 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/objx/security_test.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestHashWithKey(t *testing.T) { 9 | 10 | assert.Equal(t, "0ce84d8d01f2c7b6e0882b784429c54d280ea2d9", HashWithKey("abc", "def")) 11 | 12 | } 13 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/objx/simple_example_test.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestSimpleExample(t *testing.T) { 9 | 10 | // build a map from a JSON object 11 | o := MustFromJSON(`{"name":"Mat","foods":["indian","chinese"], "location":{"county":"hobbiton","city":"the shire"}}`) 12 | 13 | // Map can be used as a straight map[string]interface{} 14 | assert.Equal(t, o["name"], "Mat") 15 | 16 | // Get an Value object 17 | v := o.Get("name") 18 | assert.Equal(t, v, &Value{data: "Mat"}) 19 | 20 | // Test the contained value 21 | assert.False(t, v.IsInt()) 22 | assert.False(t, v.IsBool()) 23 | assert.True(t, v.IsStr()) 24 | 25 | // Get the contained value 26 | assert.Equal(t, v.Str(), "Mat") 27 | 28 | // Get a default value if the contained value is not of the expected type or does not exist 29 | assert.Equal(t, 1, v.Int(1)) 30 | 31 | // Get a value by using array notation 32 | assert.Equal(t, "indian", o.Get("foods[0]").Data()) 33 | 34 | // Set a value by using array notation 35 | o.Set("foods[0]", "italian") 36 | assert.Equal(t, "italian", o.Get("foods[0]").Str()) 37 | 38 | // Get a value by using dot notation 39 | assert.Equal(t, "hobbiton", o.Get("location.county").Str()) 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/objx/tests.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | // Has gets whether there is something at the specified selector 4 | // or not. 5 | // 6 | // If m is nil, Has will always return false. 7 | func (m Map) Has(selector string) bool { 8 | if m == nil { 9 | return false 10 | } 11 | return !m.Get(selector).IsNil() 12 | } 13 | 14 | // IsNil gets whether the data is nil or not. 15 | func (v *Value) IsNil() bool { 16 | return v == nil || v.data == nil 17 | } 18 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/objx/tests_test.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestHas(t *testing.T) { 9 | 10 | m := New(TestMap) 11 | 12 | assert.True(t, m.Has("name")) 13 | assert.True(t, m.Has("address.state")) 14 | assert.True(t, m.Has("numbers[4]")) 15 | 16 | assert.False(t, m.Has("address.state.nope")) 17 | assert.False(t, m.Has("address.nope")) 18 | assert.False(t, m.Has("nope")) 19 | assert.False(t, m.Has("numbers[5]")) 20 | 21 | m = nil 22 | assert.False(t, m.Has("nothing")) 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/objx/value.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | // Value provides methods for extracting interface{} data in various 4 | // types. 5 | type Value struct { 6 | // data contains the raw data being managed by this Value 7 | data interface{} 8 | } 9 | 10 | // Data returns the raw data contained by this Value 11 | func (v *Value) Data() interface{} { 12 | return v.data 13 | } 14 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/objx/value_test.go: -------------------------------------------------------------------------------- 1 | package objx 2 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/testify/assert/errors.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // AnError is an error instance useful for testing. If the code does not care 8 | // about error specifics, and only needs to return the error for example, this 9 | // error should be used to make the test code more readable. 10 | var AnError = errors.New("assert.AnError general error for testing") 11 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/testify/assert/http_assertions_test.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/url" 7 | "testing" 8 | ) 9 | 10 | func httpOK(w http.ResponseWriter, r *http.Request) { 11 | w.WriteHeader(http.StatusOK) 12 | } 13 | 14 | func httpRedirect(w http.ResponseWriter, r *http.Request) { 15 | w.WriteHeader(http.StatusTemporaryRedirect) 16 | } 17 | 18 | func httpError(w http.ResponseWriter, r *http.Request) { 19 | w.WriteHeader(http.StatusInternalServerError) 20 | } 21 | 22 | func TestHTTPStatuses(t *testing.T) { 23 | assert := New(t) 24 | mockT := new(testing.T) 25 | 26 | assert.Equal(HTTPSuccess(mockT, httpOK, "GET", "/", nil), true) 27 | assert.Equal(HTTPSuccess(mockT, httpRedirect, "GET", "/", nil), false) 28 | assert.Equal(HTTPSuccess(mockT, httpError, "GET", "/", nil), false) 29 | 30 | assert.Equal(HTTPRedirect(mockT, httpOK, "GET", "/", nil), false) 31 | assert.Equal(HTTPRedirect(mockT, httpRedirect, "GET", "/", nil), true) 32 | assert.Equal(HTTPRedirect(mockT, httpError, "GET", "/", nil), false) 33 | 34 | assert.Equal(HTTPError(mockT, httpOK, "GET", "/", nil), false) 35 | assert.Equal(HTTPError(mockT, httpRedirect, "GET", "/", nil), false) 36 | assert.Equal(HTTPError(mockT, httpError, "GET", "/", nil), true) 37 | } 38 | 39 | func TestHTTPStatusesWrapper(t *testing.T) { 40 | assert := New(t) 41 | mockAssert := New(new(testing.T)) 42 | 43 | assert.Equal(mockAssert.HTTPSuccess(httpOK, "GET", "/", nil), true) 44 | assert.Equal(mockAssert.HTTPSuccess(httpRedirect, "GET", "/", nil), false) 45 | assert.Equal(mockAssert.HTTPSuccess(httpError, "GET", "/", nil), false) 46 | 47 | assert.Equal(mockAssert.HTTPRedirect(httpOK, "GET", "/", nil), false) 48 | assert.Equal(mockAssert.HTTPRedirect(httpRedirect, "GET", "/", nil), true) 49 | assert.Equal(mockAssert.HTTPRedirect(httpError, "GET", "/", nil), false) 50 | 51 | assert.Equal(mockAssert.HTTPError(httpOK, "GET", "/", nil), false) 52 | assert.Equal(mockAssert.HTTPError(httpRedirect, "GET", "/", nil), false) 53 | assert.Equal(mockAssert.HTTPError(httpError, "GET", "/", nil), true) 54 | } 55 | 56 | func httpHelloName(w http.ResponseWriter, r *http.Request) { 57 | name := r.FormValue("name") 58 | w.Write([]byte(fmt.Sprintf("Hello, %s!", name))) 59 | } 60 | 61 | func TestHttpBody(t *testing.T) { 62 | assert := New(t) 63 | mockT := new(testing.T) 64 | 65 | assert.True(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) 66 | assert.True(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World")) 67 | assert.False(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) 68 | 69 | assert.False(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) 70 | assert.False(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World")) 71 | assert.True(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) 72 | } 73 | 74 | func TestHttpBodyWrappers(t *testing.T) { 75 | assert := New(t) 76 | mockAssert := New(new(testing.T)) 77 | 78 | assert.True(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) 79 | assert.True(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World")) 80 | assert.False(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) 81 | 82 | assert.False(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) 83 | assert.False(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World")) 84 | assert.True(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) 85 | 86 | } 87 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/testify/mock/doc.go: -------------------------------------------------------------------------------- 1 | // Provides a system by which it is possible to mock your objects and verify calls are happening as expected. 2 | // 3 | // Example Usage 4 | // 5 | // The mock package provides an object, Mock, that tracks activity on another object. It is usually 6 | // embedded into a test object as shown below: 7 | // 8 | // type MyTestObject struct { 9 | // // add a Mock object instance 10 | // mock.Mock 11 | // 12 | // // other fields go here as normal 13 | // } 14 | // 15 | // When implementing the methods of an interface, you wire your functions up 16 | // to call the Mock.Called(args...) method, and return the appropriate values. 17 | // 18 | // For example, to mock a method that saves the name and age of a person and returns 19 | // the year of their birth or an error, you might write this: 20 | // 21 | // func (o *MyTestObject) SavePersonDetails(firstname, lastname string, age int) (int, error) { 22 | // args := o.Called(firstname, lastname, age) 23 | // return args.Int(0), args.Error(1) 24 | // } 25 | // 26 | // The Int, Error and Bool methods are examples of strongly typed getters that take the argument 27 | // index position. Given this argument list: 28 | // 29 | // (12, true, "Something") 30 | // 31 | // You could read them out strongly typed like this: 32 | // 33 | // args.Int(0) 34 | // args.Bool(1) 35 | // args.String(2) 36 | // 37 | // For objects of your own type, use the generic Arguments.Get(index) method and make a type assertion: 38 | // 39 | // return args.Get(0).(*MyObject), args.Get(1).(*AnotherObjectOfMine) 40 | // 41 | // This may cause a panic if the object you are getting is nil (the type assertion will fail), in those 42 | // cases you should check for nil first. 43 | package mock 44 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | install: go get -t -v ./... 3 | go: 1.2 4 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/COPYING: -------------------------------------------------------------------------------- 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. 20 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/args.go: -------------------------------------------------------------------------------- 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 | parserMixin 53 | name string 54 | help string 55 | defaultValue string 56 | required bool 57 | action Action 58 | preAction Action 59 | } 60 | 61 | func newArg(name, help string) *ArgClause { 62 | a := &ArgClause{ 63 | name: name, 64 | help: help, 65 | } 66 | return a 67 | } 68 | 69 | func (a *ArgClause) consumesRemainder() bool { 70 | if r, ok := a.value.(remainderArg); ok { 71 | return r.IsCumulative() 72 | } 73 | return false 74 | } 75 | 76 | // Required arguments must be input by the user. They can not have a Default() value provided. 77 | func (a *ArgClause) Required() *ArgClause { 78 | a.required = true 79 | return a 80 | } 81 | 82 | // Default value for this argument. It *must* be parseable by the value of the argument. 83 | func (a *ArgClause) Default(value string) *ArgClause { 84 | a.defaultValue = value 85 | return a 86 | } 87 | 88 | func (a *ArgClause) Action(action Action) *ArgClause { 89 | a.action = action 90 | return a 91 | } 92 | 93 | func (a *ArgClause) PreAction(action Action) *ArgClause { 94 | a.preAction = action 95 | return a 96 | } 97 | 98 | func (a *ArgClause) init() error { 99 | if a.required && a.defaultValue != "" { 100 | return fmt.Errorf("required argument '%s' with unusable default value", a.name) 101 | } 102 | if a.value == nil { 103 | return fmt.Errorf("no parser defined for arg '%s'", a.name) 104 | } 105 | return nil 106 | } 107 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/args_test.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestArgRemainder(t *testing.T) { 10 | app := New("test", "") 11 | v := app.Arg("test", "").Strings() 12 | args := []string{"hello", "world"} 13 | _, err := app.Parse(args) 14 | assert.NoError(t, err) 15 | assert.Equal(t, args, *v) 16 | } 17 | 18 | func TestArgRemainderErrorsWhenNotLast(t *testing.T) { 19 | a := newArgGroup() 20 | a.Arg("test", "").Strings() 21 | a.Arg("test2", "").String() 22 | assert.Error(t, a.init()) 23 | } 24 | 25 | func TestArgMultipleRequired(t *testing.T) { 26 | terminated := false 27 | app := New("test", "") 28 | app.Version("0.0.0") 29 | app.Arg("a", "").Required().String() 30 | app.Arg("b", "").Required().String() 31 | app.Terminate(func(int) { terminated = true }) 32 | 33 | _, err := app.Parse([]string{}) 34 | assert.Error(t, err) 35 | _, err = app.Parse([]string{"A"}) 36 | assert.Error(t, err) 37 | _, err = app.Parse([]string{"A", "B"}) 38 | assert.NoError(t, err) 39 | _, err = app.Parse([]string{"--version"}) 40 | assert.True(t, terminated) 41 | } 42 | 43 | func TestInvalidArgsDefaultCanBeOverridden(t *testing.T) { 44 | app := New("test", "") 45 | app.Arg("a", "").Default("invalid").Bool() 46 | _, err := app.Parse([]string{}) 47 | assert.Error(t, err) 48 | } 49 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/cmd.go: -------------------------------------------------------------------------------- 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 newCmdGroup(app *Application) *cmdGroup { 16 | return &cmdGroup{ 17 | app: app, 18 | commands: make(map[string]*CmdClause), 19 | } 20 | } 21 | 22 | func (c *cmdGroup) flattenedCommands() (out []*CmdClause) { 23 | for _, cmd := range c.commandOrder { 24 | if len(cmd.commands) == 0 { 25 | out = append(out, cmd) 26 | } 27 | out = append(out, cmd.flattenedCommands()...) 28 | } 29 | return 30 | } 31 | 32 | func (c *cmdGroup) addCommand(name, help string) *CmdClause { 33 | cmd := newCommand(c.app, name, help) 34 | c.commands[name] = cmd 35 | c.commandOrder = append(c.commandOrder, cmd) 36 | return cmd 37 | } 38 | 39 | func (c *cmdGroup) init() error { 40 | seen := map[string]bool{} 41 | for _, cmd := range c.commandOrder { 42 | if seen[cmd.name] { 43 | return fmt.Errorf("duplicate command '%s'", cmd.name) 44 | } 45 | seen[cmd.name] = true 46 | if err := cmd.init(); err != nil { 47 | return err 48 | } 49 | } 50 | return nil 51 | } 52 | func (c *cmdGroup) have() bool { 53 | return len(c.commands) > 0 54 | } 55 | 56 | type CmdClauseValidator func(*CmdClause) error 57 | 58 | // A CmdClause is a single top-level command. It encapsulates a set of flags 59 | // and either subcommands or positional arguments. 60 | type CmdClause struct { 61 | *flagGroup 62 | *argGroup 63 | *cmdGroup 64 | app *Application 65 | name string 66 | help string 67 | action Action 68 | preAction Action 69 | validator CmdClauseValidator 70 | hidden bool 71 | } 72 | 73 | func newCommand(app *Application, name, help string) *CmdClause { 74 | c := &CmdClause{ 75 | flagGroup: newFlagGroup(), 76 | argGroup: newArgGroup(), 77 | cmdGroup: newCmdGroup(app), 78 | app: app, 79 | name: name, 80 | help: help, 81 | } 82 | return c 83 | } 84 | 85 | // Validate sets a validation function to run when parsing. 86 | func (c *CmdClause) Validate(validator CmdClauseValidator) *CmdClause { 87 | c.validator = validator 88 | return c 89 | } 90 | 91 | func (c *CmdClause) FullCommand() string { 92 | out := []string{c.name} 93 | for p := c.parent; p != nil; p = p.parent { 94 | out = append([]string{p.name}, out...) 95 | } 96 | return strings.Join(out, " ") 97 | } 98 | 99 | // Command adds a new sub-command. 100 | func (c *CmdClause) Command(name, help string) *CmdClause { 101 | cmd := c.addCommand(name, help) 102 | cmd.parent = c 103 | return cmd 104 | } 105 | 106 | func (c *CmdClause) Action(action Action) *CmdClause { 107 | c.action = action 108 | return c 109 | } 110 | 111 | func (c *CmdClause) PreAction(action Action) *CmdClause { 112 | c.preAction = action 113 | return c 114 | } 115 | 116 | func (c *CmdClause) init() error { 117 | if err := c.flagGroup.init(); err != nil { 118 | return err 119 | } 120 | if c.argGroup.have() && c.cmdGroup.have() { 121 | return fmt.Errorf("can't mix Arg()s with Command()s") 122 | } 123 | if err := c.argGroup.init(); err != nil { 124 | return err 125 | } 126 | if err := c.cmdGroup.init(); err != nil { 127 | return err 128 | } 129 | return nil 130 | } 131 | 132 | func (c *CmdClause) Hidden() *CmdClause { 133 | c.hidden = true 134 | return c 135 | } 136 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/cmd_test.go: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/doc.go: -------------------------------------------------------------------------------- 1 | // Package kingpin provides command line interfaces like this: 2 | // 3 | // $ chat 4 | // usage: chat [] [] [ ...] 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 13 | // Show help for a command. 14 | // 15 | // post [] 16 | // Post a message to a channel. 17 | // 18 | // register 19 | // Register a new user. 20 | // 21 | // $ chat help post 22 | // usage: chat [] post [] [] 23 | // 24 | // Post a message to a channel. 25 | // 26 | // Flags: 27 | // --image=IMAGE image to post 28 | // 29 | // Args: 30 | // channel to post to 31 | // [] 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 69 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/examples/chat1/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "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 | } 21 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/examples/chat2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | 7 | "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 | } 39 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/examples/curl/main.go: -------------------------------------------------------------------------------- 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/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.") 20 | getFlag = get.Flag("test", "Test flag").Bool() 21 | getURL = get.Command("url", "Retrieve a URL.") 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 | } 106 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/examples/modular/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "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 | } 31 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/examples/ping/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "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 | } 21 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/examples_test.go: -------------------------------------------------------------------------------- 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 | } 43 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/flags_test.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/stretchr/testify/assert" 7 | 8 | "testing" 9 | ) 10 | 11 | func TestBool(t *testing.T) { 12 | app := New("test", "") 13 | b := app.Flag("b", "").Bool() 14 | _, err := app.Parse([]string{"--b"}) 15 | assert.NoError(t, err) 16 | assert.True(t, *b) 17 | } 18 | 19 | func TestNoBool(t *testing.T) { 20 | fg := newFlagGroup() 21 | f := fg.Flag("b", "").Default("true") 22 | b := f.Bool() 23 | fg.init() 24 | tokens := tokenize([]string{"--no-b"}) 25 | err := fg.parse(tokens) 26 | assert.NoError(t, err) 27 | assert.False(t, *b) 28 | } 29 | 30 | func TestNegateNonBool(t *testing.T) { 31 | fg := newFlagGroup() 32 | f := fg.Flag("b", "") 33 | f.Int() 34 | fg.init() 35 | tokens := tokenize([]string{"--no-b"}) 36 | err := fg.parse(tokens) 37 | assert.Error(t, err) 38 | } 39 | 40 | func TestInvalidFlagDefaultCanBeOverridden(t *testing.T) { 41 | app := New("test", "") 42 | app.Flag("a", "").Default("invalid").Bool() 43 | _, err := app.Parse([]string{}) 44 | assert.Error(t, err) 45 | } 46 | 47 | func TestRequiredFlag(t *testing.T) { 48 | app := New("test", "") 49 | app.Version("0.0.0") 50 | app.Terminate(nil) 51 | app.Flag("a", "").Required().Bool() 52 | _, err := app.Parse([]string{"--a"}) 53 | assert.NoError(t, err) 54 | _, err = app.Parse([]string{}) 55 | assert.Error(t, err) 56 | _, err = app.Parse([]string{"--version"}) 57 | } 58 | 59 | func TestShortFlag(t *testing.T) { 60 | app := New("test", "") 61 | f := app.Flag("long", "").Short('s').Bool() 62 | _, err := app.Parse([]string{"-s"}) 63 | assert.NoError(t, err) 64 | assert.True(t, *f) 65 | } 66 | 67 | func TestCombinedShortFlags(t *testing.T) { 68 | app := New("test", "") 69 | a := app.Flag("short0", "").Short('0').Bool() 70 | b := app.Flag("short1", "").Short('1').Bool() 71 | c := app.Flag("short2", "").Short('2').Bool() 72 | _, err := app.Parse([]string{"-01"}) 73 | assert.NoError(t, err) 74 | assert.True(t, *a) 75 | assert.True(t, *b) 76 | assert.False(t, *c) 77 | } 78 | 79 | func TestCombinedShortFlagArg(t *testing.T) { 80 | a := New("test", "") 81 | n := a.Flag("short", "").Short('s').Int() 82 | _, err := a.Parse([]string{"-s10"}) 83 | assert.NoError(t, err) 84 | assert.Equal(t, 10, *n) 85 | } 86 | 87 | func TestEmptyShortFlagIsAnError(t *testing.T) { 88 | _, err := New("test", "").Parse([]string{"-"}) 89 | assert.Error(t, err) 90 | } 91 | 92 | func TestRequiredWithEnvarMissingErrors(t *testing.T) { 93 | app := New("test", "") 94 | app.Flag("t", "").OverrideDefaultFromEnvar("TEST_ENVAR").Required().Int() 95 | _, err := app.Parse([]string{}) 96 | assert.Error(t, err) 97 | } 98 | 99 | func TestRequiredWithEnvar(t *testing.T) { 100 | os.Setenv("TEST_ENVAR", "123") 101 | app := New("test", "") 102 | flag := app.Flag("t", "").OverrideDefaultFromEnvar("TEST_ENVAR").Required().Int() 103 | _, err := app.Parse([]string{}) 104 | assert.NoError(t, err) 105 | assert.Equal(t, 123, *flag) 106 | } 107 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/genrepeated/main.go: -------------------------------------------------------------------------------- 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 .Comment}}{{.Comment}}{{else}}{{.|Name}} accumulates {{.Type}} values into a slice.{{end}} 19 | func (p *parserMixin) {{.|Name}}() (target *[]{{.Type}}) { 20 | target = new([]{{.Type}}) 21 | p.{{.|Name}}Var(target) 22 | return 23 | } 24 | 25 | func (p *parserMixin) {{.|Name}}Var(target *[]{{.Type}}) { 26 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return new{{.Name}}Value(v.(*{{.Type}})) })) 27 | } 28 | 29 | {{end}} 30 | ` 31 | ) 32 | 33 | type Repeated struct { 34 | Name string `json:"name"` 35 | Comment string `json:"comment"` 36 | Plural string `json:"plural"` 37 | Type string `json:"type"` 38 | } 39 | 40 | func fatalIfError(err error) { 41 | if err != nil { 42 | panic(err) 43 | } 44 | } 45 | 46 | func main() { 47 | r, err := os.Open("repeated.json") 48 | fatalIfError(err) 49 | defer r.Close() 50 | 51 | v := []Repeated{} 52 | err = json.NewDecoder(r).Decode(&v) 53 | fatalIfError(err) 54 | 55 | t, err := template.New("genrepeated").Funcs(template.FuncMap{ 56 | "Lower": strings.ToLower, 57 | "Name": func(v *Repeated) string { 58 | if v.Plural != "" { 59 | return v.Plural 60 | } 61 | return v.Name + "List" 62 | }, 63 | }).Parse(tmpl) 64 | fatalIfError(err) 65 | 66 | w, err := os.Create("repeated.go") 67 | fatalIfError(err) 68 | defer w.Close() 69 | 70 | err = t.Execute(w, v) 71 | fatalIfError(err) 72 | 73 | err = exec.Command("goimports", "-w", "repeated.go").Run() 74 | fatalIfError(err) 75 | } 76 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/global.go: -------------------------------------------------------------------------------- 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 | } 89 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/guesswidth.go: -------------------------------------------------------------------------------- 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 | } 10 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/guesswidth_unix.go: -------------------------------------------------------------------------------- 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 | } 39 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/parser_test.go: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/parsers_test.go: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/repeated.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "net" 5 | "time" 6 | ) 7 | 8 | // This file is autogenerated by "go generate .". Do not modify. 9 | 10 | // Strings accumulates string values into a slice. 11 | func (p *parserMixin) Strings() (target *[]string) { 12 | target = new([]string) 13 | p.StringsVar(target) 14 | return 15 | } 16 | 17 | func (p *parserMixin) StringsVar(target *[]string) { 18 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newStringValue(v.(*string)) })) 19 | } 20 | 21 | // Uint64List accumulates uint64 values into a slice. 22 | func (p *parserMixin) Uint64List() (target *[]uint64) { 23 | target = new([]uint64) 24 | p.Uint64ListVar(target) 25 | return 26 | } 27 | 28 | func (p *parserMixin) Uint64ListVar(target *[]uint64) { 29 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newUint64Value(v.(*uint64)) })) 30 | } 31 | 32 | // Int64List accumulates int64 values into a slice. 33 | func (p *parserMixin) Int64List() (target *[]int64) { 34 | target = new([]int64) 35 | p.Int64ListVar(target) 36 | return 37 | } 38 | 39 | func (p *parserMixin) Int64ListVar(target *[]int64) { 40 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newInt64Value(v.(*int64)) })) 41 | } 42 | 43 | // DurationList accumulates time.Duration values into a slice. 44 | func (p *parserMixin) DurationList() (target *[]time.Duration) { 45 | target = new([]time.Duration) 46 | p.DurationListVar(target) 47 | return 48 | } 49 | 50 | func (p *parserMixin) DurationListVar(target *[]time.Duration) { 51 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newDurationValue(v.(*time.Duration)) })) 52 | } 53 | 54 | // IPList accumulates net.IP values into a slice. 55 | func (p *parserMixin) IPList() (target *[]net.IP) { 56 | target = new([]net.IP) 57 | p.IPListVar(target) 58 | return 59 | } 60 | 61 | func (p *parserMixin) IPListVar(target *[]net.IP) { 62 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newIPValue(v.(*net.IP)) })) 63 | } 64 | 65 | // TCPList accumulates *net.TCPAddr values into a slice. 66 | func (p *parserMixin) TCPList() (target *[]*net.TCPAddr) { 67 | target = new([]*net.TCPAddr) 68 | p.TCPListVar(target) 69 | return 70 | } 71 | 72 | func (p *parserMixin) TCPListVar(target *[]*net.TCPAddr) { 73 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newTCPAddrValue(v.(**net.TCPAddr)) })) 74 | } 75 | 76 | // ExistingFiles accumulates string values into a slice. 77 | func (p *parserMixin) ExistingFiles() (target *[]string) { 78 | target = new([]string) 79 | p.ExistingFilesVar(target) 80 | return 81 | } 82 | 83 | func (p *parserMixin) ExistingFilesVar(target *[]string) { 84 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newExistingFileValue(v.(*string)) })) 85 | } 86 | 87 | // ExistingDirs accumulates string values into a slice. 88 | func (p *parserMixin) ExistingDirs() (target *[]string) { 89 | target = new([]string) 90 | p.ExistingDirsVar(target) 91 | return 92 | } 93 | 94 | func (p *parserMixin) ExistingDirsVar(target *[]string) { 95 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newExistingDirValue(v.(*string)) })) 96 | } 97 | 98 | // ExistingFilesOrDirs accumulates string values into a slice. 99 | func (p *parserMixin) ExistingFilesOrDirs() (target *[]string) { 100 | target = new([]string) 101 | p.ExistingFilesOrDirsVar(target) 102 | return 103 | } 104 | 105 | func (p *parserMixin) ExistingFilesOrDirsVar(target *[]string) { 106 | p.SetValue(newAccumulator(target, func(v interface{}) Value { return newExistingFileOrDirValue(v.(*string)) })) 107 | } 108 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/repeated.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"name": "String", "type": "string", "plural": "Strings"}, 3 | {"name": "Uint64", "type": "uint64"}, 4 | {"name": "Int64", "type": "int64"}, 5 | {"name": "Duration", "type": "time.Duration"}, 6 | {"name": "IP", "type": "net.IP"}, 7 | {"name": "TCPAddr", "Type": "*net.TCPAddr", "plural": "TCPList"}, 8 | {"name": "ExistingFile", "Type": "string", "plural": "ExistingFiles"}, 9 | {"name": "ExistingDir", "Type": "string", "plural": "ExistingDirs"}, 10 | {"name": "ExistingFileOrDir", "Type": "string", "plural": "ExistingFilesOrDirs"} 11 | ] 12 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/alecthomas/kingpin.v2/usage_test.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "strings" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestFormatTwoColumns(t *testing.T) { 13 | buf := bytes.NewBuffer(nil) 14 | formatTwoColumns(buf, 2, 2, 20, [][2]string{ 15 | {"--hello", "Hello world help with something that is cool."}, 16 | }) 17 | expected := ` --hello Hello 18 | world 19 | help with 20 | something 21 | that is 22 | cool. 23 | ` 24 | assert.Equal(t, expected, buf.String()) 25 | } 26 | 27 | func TestFormatTwoColumnsWide(t *testing.T) { 28 | samples := [][2]string{ 29 | {strings.Repeat("x", 19), "19 chars"}, 30 | {strings.Repeat("x", 20), "20 chars"}} 31 | buf := bytes.NewBuffer(nil) 32 | formatTwoColumns(buf, 0, 0, 200, samples) 33 | fmt.Println(buf.String()) 34 | expected := `xxxxxxxxxxxxxxxxxxx19 chars 35 | xxxxxxxxxxxxxxxxxxxx 36 | 20 chars 37 | ` 38 | assert.Equal(t, expected, buf.String()) 39 | } 40 | 41 | func TestHiddenCommand(t *testing.T) { 42 | templates := []struct{ name, template string }{ 43 | {"default", DefaultUsageTemplate}, 44 | {"Compact", CompactUsageTemplate}, 45 | {"Long", LongHelpTemplate}, 46 | {"Man", ManPageTemplate}, 47 | } 48 | 49 | var buf bytes.Buffer 50 | t.Log("1") 51 | 52 | a := New("test", "Test").Writer(&buf).Terminate(nil) 53 | a.Command("visible", "visible") 54 | a.Command("hidden", "hidden").Hidden() 55 | 56 | for _, tp := range templates { 57 | buf.Reset() 58 | a.UsageTemplate(tp.template) 59 | a.Parse(nil) 60 | // a.Parse([]string{"--help"}) 61 | usage := buf.String() 62 | t.Logf("Usage for %s is:\n%s\n", tp.name, usage) 63 | 64 | assert.NotContains(t, usage, "hidden") 65 | assert.Contains(t, usage, "visible") 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/check.v1/.gitignore: -------------------------------------------------------------------------------- 1 | _* 2 | *.swp 3 | *.[568] 4 | [568].out 5 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/check.v1/LICENSE: -------------------------------------------------------------------------------- 1 | Gocheck - A rich testing framework for Go 2 | 3 | Copyright (c) 2010-2013 Gustavo Niemeyer 4 | 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/check.v1/README.md: -------------------------------------------------------------------------------- 1 | Instructions 2 | ============ 3 | 4 | Install the package with: 5 | 6 | go get gopkg.in/check.v1 7 | 8 | Import it with: 9 | 10 | import "gopkg.in/check.v1" 11 | 12 | and use _check_ as the package name inside the code. 13 | 14 | For more details, visit the project page: 15 | 16 | * http://labix.org/gocheck 17 | 18 | and the API documentation: 19 | 20 | * https://gopkg.in/check.v1 21 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/check.v1/TODO: -------------------------------------------------------------------------------- 1 | - Assert(slice, Contains, item) 2 | - Parallel test support 3 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/check.v1/benchmark_test.go: -------------------------------------------------------------------------------- 1 | // These tests verify the test running logic. 2 | 3 | package check_test 4 | 5 | import ( 6 | "time" 7 | . "gopkg.in/check.v1" 8 | ) 9 | 10 | var benchmarkS = Suite(&BenchmarkS{}) 11 | 12 | type BenchmarkS struct{} 13 | 14 | func (s *BenchmarkS) TestCountSuite(c *C) { 15 | suitesRun += 1 16 | } 17 | 18 | func (s *BenchmarkS) TestBasicTestTiming(c *C) { 19 | helper := FixtureHelper{sleepOn: "Test1", sleep: 1000000 * time.Nanosecond} 20 | output := String{} 21 | runConf := RunConf{Output: &output, Verbose: true} 22 | Run(&helper, &runConf) 23 | 24 | expected := "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Test1\t0\\.001s\n" + 25 | "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Test2\t0\\.000s\n" 26 | c.Assert(output.value, Matches, expected) 27 | } 28 | 29 | func (s *BenchmarkS) TestStreamTestTiming(c *C) { 30 | helper := FixtureHelper{sleepOn: "SetUpSuite", sleep: 1000000 * time.Nanosecond} 31 | output := String{} 32 | runConf := RunConf{Output: &output, Stream: true} 33 | Run(&helper, &runConf) 34 | 35 | expected := "(?s).*\nPASS: check_test\\.go:[0-9]+: FixtureHelper\\.SetUpSuite\t *0\\.001s\n.*" 36 | c.Assert(output.value, Matches, expected) 37 | } 38 | 39 | func (s *BenchmarkS) TestBenchmark(c *C) { 40 | helper := FixtureHelper{sleep: 100000} 41 | output := String{} 42 | runConf := RunConf{ 43 | Output: &output, 44 | Benchmark: true, 45 | BenchmarkTime: 10000000, 46 | Filter: "Benchmark1", 47 | } 48 | Run(&helper, &runConf) 49 | c.Check(helper.calls[0], Equals, "SetUpSuite") 50 | c.Check(helper.calls[1], Equals, "SetUpTest") 51 | c.Check(helper.calls[2], Equals, "Benchmark1") 52 | c.Check(helper.calls[3], Equals, "TearDownTest") 53 | c.Check(helper.calls[4], Equals, "SetUpTest") 54 | c.Check(helper.calls[5], Equals, "Benchmark1") 55 | c.Check(helper.calls[6], Equals, "TearDownTest") 56 | // ... and more. 57 | 58 | expected := "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Benchmark1\t *100\t *[12][0-9]{5} ns/op\n" 59 | c.Assert(output.value, Matches, expected) 60 | } 61 | 62 | func (s *BenchmarkS) TestBenchmarkBytes(c *C) { 63 | helper := FixtureHelper{sleep: 100000} 64 | output := String{} 65 | runConf := RunConf{ 66 | Output: &output, 67 | Benchmark: true, 68 | BenchmarkTime: 10000000, 69 | Filter: "Benchmark2", 70 | } 71 | Run(&helper, &runConf) 72 | 73 | expected := "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Benchmark2\t *100\t *[12][0-9]{5} ns/op\t *[4-9]\\.[0-9]{2} MB/s\n" 74 | c.Assert(output.value, Matches, expected) 75 | } 76 | 77 | func (s *BenchmarkS) TestBenchmarkMem(c *C) { 78 | helper := FixtureHelper{sleep: 100000} 79 | output := String{} 80 | runConf := RunConf{ 81 | Output: &output, 82 | Benchmark: true, 83 | BenchmarkMem: true, 84 | BenchmarkTime: 10000000, 85 | Filter: "Benchmark3", 86 | } 87 | Run(&helper, &runConf) 88 | 89 | expected := "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Benchmark3\t *100\t *[12][0-9]{5} ns/op\t *[0-9]+ B/op\t *[1-9] allocs/op\n" 90 | c.Assert(output.value, Matches, expected) 91 | } 92 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/check.v1/bootstrap_test.go: -------------------------------------------------------------------------------- 1 | // These initial tests are for bootstrapping. They verify that we can 2 | // basically use the testing infrastructure itself to check if the test 3 | // system is working. 4 | // 5 | // These tests use will break down the test runner badly in case of 6 | // errors because if they simply fail, we can't be sure the developer 7 | // will ever see anything (because failing means the failing system 8 | // somehow isn't working! :-) 9 | // 10 | // Do not assume *any* internal functionality works as expected besides 11 | // what's actually tested here. 12 | 13 | package check_test 14 | 15 | import ( 16 | "fmt" 17 | "gopkg.in/check.v1" 18 | "strings" 19 | ) 20 | 21 | type BootstrapS struct{} 22 | 23 | var boostrapS = check.Suite(&BootstrapS{}) 24 | 25 | func (s *BootstrapS) TestCountSuite(c *check.C) { 26 | suitesRun += 1 27 | } 28 | 29 | func (s *BootstrapS) TestFailedAndFail(c *check.C) { 30 | if c.Failed() { 31 | critical("c.Failed() must be false first!") 32 | } 33 | c.Fail() 34 | if !c.Failed() { 35 | critical("c.Fail() didn't put the test in a failed state!") 36 | } 37 | c.Succeed() 38 | } 39 | 40 | func (s *BootstrapS) TestFailedAndSucceed(c *check.C) { 41 | c.Fail() 42 | c.Succeed() 43 | if c.Failed() { 44 | critical("c.Succeed() didn't put the test back in a non-failed state") 45 | } 46 | } 47 | 48 | func (s *BootstrapS) TestLogAndGetTestLog(c *check.C) { 49 | c.Log("Hello there!") 50 | log := c.GetTestLog() 51 | if log != "Hello there!\n" { 52 | critical(fmt.Sprintf("Log() or GetTestLog() is not working! Got: %#v", log)) 53 | } 54 | } 55 | 56 | func (s *BootstrapS) TestLogfAndGetTestLog(c *check.C) { 57 | c.Logf("Hello %v", "there!") 58 | log := c.GetTestLog() 59 | if log != "Hello there!\n" { 60 | critical(fmt.Sprintf("Logf() or GetTestLog() is not working! Got: %#v", log)) 61 | } 62 | } 63 | 64 | func (s *BootstrapS) TestRunShowsErrors(c *check.C) { 65 | output := String{} 66 | check.Run(&FailHelper{}, &check.RunConf{Output: &output}) 67 | if strings.Index(output.value, "Expected failure!") == -1 { 68 | critical(fmt.Sprintf("RunWithWriter() output did not contain the "+ 69 | "expected failure! Got: %#v", 70 | output.value)) 71 | } 72 | } 73 | 74 | func (s *BootstrapS) TestRunDoesntShowSuccesses(c *check.C) { 75 | output := String{} 76 | check.Run(&SuccessHelper{}, &check.RunConf{Output: &output}) 77 | if strings.Index(output.value, "Expected success!") != -1 { 78 | critical(fmt.Sprintf("RunWithWriter() output contained a successful "+ 79 | "test! Got: %#v", 80 | output.value)) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/check.v1/export_test.go: -------------------------------------------------------------------------------- 1 | package check 2 | 3 | func PrintLine(filename string, line int) (string, error) { 4 | return printLine(filename, line) 5 | } 6 | 7 | func Indent(s, with string) string { 8 | return indent(s, with) 9 | } 10 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/check.v1/printer_test.go: -------------------------------------------------------------------------------- 1 | package check_test 2 | 3 | import ( 4 | . "gopkg.in/check.v1" 5 | ) 6 | 7 | var _ = Suite(&PrinterS{}) 8 | 9 | type PrinterS struct{} 10 | 11 | func (s *PrinterS) TestCountSuite(c *C) { 12 | suitesRun += 1 13 | } 14 | 15 | var printTestFuncLine int 16 | 17 | func init() { 18 | printTestFuncLine = getMyLine() + 3 19 | } 20 | 21 | func printTestFunc() { 22 | println(1) // Comment1 23 | if 2 == 2 { // Comment2 24 | println(3) // Comment3 25 | } 26 | switch 5 { 27 | case 6: println(6) // Comment6 28 | println(7) 29 | } 30 | switch interface{}(9).(type) {// Comment9 31 | case int: println(10) 32 | println(11) 33 | } 34 | select { 35 | case <-(chan bool)(nil): println(14) 36 | println(15) 37 | default: println(16) 38 | println(17) 39 | } 40 | println(19, 41 | 20) 42 | _ = func() { println(21) 43 | println(22) 44 | } 45 | println(24, func() { 46 | println(25) 47 | }) 48 | // Leading comment 49 | // with multiple lines. 50 | println(29) // Comment29 51 | } 52 | 53 | var printLineTests = []struct { 54 | line int 55 | output string 56 | }{ 57 | {1, "println(1) // Comment1"}, 58 | {2, "if 2 == 2 { // Comment2\n ...\n}"}, 59 | {3, "println(3) // Comment3"}, 60 | {5, "switch 5 {\n...\n}"}, 61 | {6, "case 6:\n println(6) // Comment6\n ..."}, 62 | {7, "println(7)"}, 63 | {9, "switch interface{}(9).(type) { // Comment9\n...\n}"}, 64 | {10, "case int:\n println(10)\n ..."}, 65 | {14, "case <-(chan bool)(nil):\n println(14)\n ..."}, 66 | {15, "println(15)"}, 67 | {16, "default:\n println(16)\n ..."}, 68 | {17, "println(17)"}, 69 | {19, "println(19,\n 20)"}, 70 | {20, "println(19,\n 20)"}, 71 | {21, "_ = func() {\n println(21)\n println(22)\n}"}, 72 | {22, "println(22)"}, 73 | {24, "println(24, func() {\n println(25)\n})"}, 74 | {25, "println(25)"}, 75 | {26, "println(24, func() {\n println(25)\n})"}, 76 | {29, "// Leading comment\n// with multiple lines.\nprintln(29) // Comment29"}, 77 | } 78 | 79 | func (s *PrinterS) TestPrintLine(c *C) { 80 | for _, test := range printLineTests { 81 | output, err := PrintLine("printer_test.go", printTestFuncLine+test.line) 82 | c.Assert(err, IsNil) 83 | c.Assert(output, Equals, test.output) 84 | } 85 | } 86 | 87 | var indentTests = []struct { 88 | in, out string 89 | }{ 90 | {"", ""}, 91 | {"\n", "\n"}, 92 | {"a", ">>>a"}, 93 | {"a\n", ">>>a\n"}, 94 | {"a\nb", ">>>a\n>>>b"}, 95 | {" ", ">>> "}, 96 | } 97 | 98 | func (s *PrinterS) TestIndent(c *C) { 99 | for _, test := range indentTests { 100 | out := Indent(test.in, ">>>") 101 | c.Assert(out, Equals, test.out) 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/yaml.v2/LICENSE.libyaml: -------------------------------------------------------------------------------- 1 | The following files were ported to Go from C files of libyaml, and thus 2 | are still covered by their original copyright and license: 3 | 4 | apic.go 5 | emitterc.go 6 | parserc.go 7 | readerc.go 8 | scannerc.go 9 | writerc.go 10 | yamlh.go 11 | yamlprivateh.go 12 | 13 | Copyright (c) 2006 Kirill Simonov 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy of 16 | this software and associated documentation files (the "Software"), to deal in 17 | the Software without restriction, including without limitation the rights to 18 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 19 | of the Software, and to permit persons to whom the Software is furnished to do 20 | so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. 32 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/yaml.v2/README.md: -------------------------------------------------------------------------------- 1 | # YAML support for the Go language 2 | 3 | Introduction 4 | ------------ 5 | 6 | The yaml package enables Go programs to comfortably encode and decode YAML 7 | values. It was developed within [Canonical](https://www.canonical.com) as 8 | part of the [juju](https://juju.ubuntu.com) project, and is based on a 9 | pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) 10 | C library to parse and generate YAML data quickly and reliably. 11 | 12 | Compatibility 13 | ------------- 14 | 15 | The yaml package supports most of YAML 1.1 and 1.2, including support for 16 | anchors, tags, map merging, etc. Multi-document unmarshalling is not yet 17 | implemented, and base-60 floats from YAML 1.1 are purposefully not 18 | supported since they're a poor design and are gone in YAML 1.2. 19 | 20 | Installation and usage 21 | ---------------------- 22 | 23 | The import path for the package is *gopkg.in/yaml.v2*. 24 | 25 | To install it, run: 26 | 27 | go get gopkg.in/yaml.v2 28 | 29 | API documentation 30 | ----------------- 31 | 32 | If opened in a browser, the import path itself leads to the API documentation: 33 | 34 | * [https://gopkg.in/yaml.v2](https://gopkg.in/yaml.v2) 35 | 36 | API stability 37 | ------------- 38 | 39 | The package API for yaml v2 will remain stable as described in [gopkg.in](https://gopkg.in). 40 | 41 | 42 | License 43 | ------- 44 | 45 | The yaml package is licensed under the LGPL with an exception that allows it to be linked statically. Please see the LICENSE file for details. 46 | 47 | 48 | Example 49 | ------- 50 | 51 | ```Go 52 | package main 53 | 54 | import ( 55 | "fmt" 56 | "log" 57 | 58 | "gopkg.in/yaml.v2" 59 | ) 60 | 61 | var data = ` 62 | a: Easy! 63 | b: 64 | c: 2 65 | d: [3, 4] 66 | ` 67 | 68 | type T struct { 69 | A string 70 | B struct{C int; D []int ",flow"} 71 | } 72 | 73 | func main() { 74 | t := T{} 75 | 76 | err := yaml.Unmarshal([]byte(data), &t) 77 | if err != nil { 78 | log.Fatalf("error: %v", err) 79 | } 80 | fmt.Printf("--- t:\n%v\n\n", t) 81 | 82 | d, err := yaml.Marshal(&t) 83 | if err != nil { 84 | log.Fatalf("error: %v", err) 85 | } 86 | fmt.Printf("--- t dump:\n%s\n\n", string(d)) 87 | 88 | m := make(map[interface{}]interface{}) 89 | 90 | err = yaml.Unmarshal([]byte(data), &m) 91 | if err != nil { 92 | log.Fatalf("error: %v", err) 93 | } 94 | fmt.Printf("--- m:\n%v\n\n", m) 95 | 96 | d, err = yaml.Marshal(&m) 97 | if err != nil { 98 | log.Fatalf("error: %v", err) 99 | } 100 | fmt.Printf("--- m dump:\n%s\n\n", string(d)) 101 | } 102 | ``` 103 | 104 | This example will generate the following output: 105 | 106 | ``` 107 | --- t: 108 | {Easy! {2 [3 4]}} 109 | 110 | --- t dump: 111 | a: Easy! 112 | b: 113 | c: 2 114 | d: [3, 4] 115 | 116 | 117 | --- m: 118 | map[a:Easy! b:map[c:2 d:[3 4]]] 119 | 120 | --- m dump: 121 | a: Easy! 122 | b: 123 | c: 2 124 | d: 125 | - 3 126 | - 4 127 | ``` 128 | 129 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/yaml.v2/sorter.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | import ( 4 | "reflect" 5 | "unicode" 6 | ) 7 | 8 | type keyList []reflect.Value 9 | 10 | func (l keyList) Len() int { return len(l) } 11 | func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 12 | func (l keyList) Less(i, j int) bool { 13 | a := l[i] 14 | b := l[j] 15 | ak := a.Kind() 16 | bk := b.Kind() 17 | for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() { 18 | a = a.Elem() 19 | ak = a.Kind() 20 | } 21 | for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() { 22 | b = b.Elem() 23 | bk = b.Kind() 24 | } 25 | af, aok := keyFloat(a) 26 | bf, bok := keyFloat(b) 27 | if aok && bok { 28 | if af != bf { 29 | return af < bf 30 | } 31 | if ak != bk { 32 | return ak < bk 33 | } 34 | return numLess(a, b) 35 | } 36 | if ak != reflect.String || bk != reflect.String { 37 | return ak < bk 38 | } 39 | ar, br := []rune(a.String()), []rune(b.String()) 40 | for i := 0; i < len(ar) && i < len(br); i++ { 41 | if ar[i] == br[i] { 42 | continue 43 | } 44 | al := unicode.IsLetter(ar[i]) 45 | bl := unicode.IsLetter(br[i]) 46 | if al && bl { 47 | return ar[i] < br[i] 48 | } 49 | if al || bl { 50 | return bl 51 | } 52 | var ai, bi int 53 | var an, bn int64 54 | for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ { 55 | an = an*10 + int64(ar[ai]-'0') 56 | } 57 | for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ { 58 | bn = bn*10 + int64(br[bi]-'0') 59 | } 60 | if an != bn { 61 | return an < bn 62 | } 63 | if ai != bi { 64 | return ai < bi 65 | } 66 | return ar[i] < br[i] 67 | } 68 | return len(ar) < len(br) 69 | } 70 | 71 | // keyFloat returns a float value for v if it is a number/bool 72 | // and whether it is a number/bool or not. 73 | func keyFloat(v reflect.Value) (f float64, ok bool) { 74 | switch v.Kind() { 75 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 76 | return float64(v.Int()), true 77 | case reflect.Float32, reflect.Float64: 78 | return v.Float(), true 79 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 80 | return float64(v.Uint()), true 81 | case reflect.Bool: 82 | if v.Bool() { 83 | return 1, true 84 | } 85 | return 0, true 86 | } 87 | return 0, false 88 | } 89 | 90 | // numLess returns whether a < b. 91 | // a and b must necessarily have the same kind. 92 | func numLess(a, b reflect.Value) bool { 93 | switch a.Kind() { 94 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 95 | return a.Int() < b.Int() 96 | case reflect.Float32, reflect.Float64: 97 | return a.Float() < b.Float() 98 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 99 | return a.Uint() < b.Uint() 100 | case reflect.Bool: 101 | return !a.Bool() && b.Bool() 102 | } 103 | panic("not a number") 104 | } 105 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/yaml.v2/suite_test.go: -------------------------------------------------------------------------------- 1 | package yaml_test 2 | 3 | import ( 4 | . "gopkg.in/check.v1" 5 | "testing" 6 | ) 7 | 8 | func Test(t *testing.T) { TestingT(t) } 9 | 10 | type S struct{} 11 | 12 | var _ = Suite(&S{}) 13 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/yaml.v2/writerc.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | // Set the writer error and return false. 4 | func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { 5 | emitter.error = yaml_WRITER_ERROR 6 | emitter.problem = problem 7 | return false 8 | } 9 | 10 | // Flush the output buffer. 11 | func yaml_emitter_flush(emitter *yaml_emitter_t) bool { 12 | if emitter.write_handler == nil { 13 | panic("write handler not set") 14 | } 15 | 16 | // Check if the buffer is empty. 17 | if emitter.buffer_pos == 0 { 18 | return true 19 | } 20 | 21 | // If the output encoding is UTF-8, we don't need to recode the buffer. 22 | if emitter.encoding == yaml_UTF8_ENCODING { 23 | if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { 24 | return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) 25 | } 26 | emitter.buffer_pos = 0 27 | return true 28 | } 29 | 30 | // Recode the buffer into the raw buffer. 31 | var low, high int 32 | if emitter.encoding == yaml_UTF16LE_ENCODING { 33 | low, high = 0, 1 34 | } else { 35 | high, low = 1, 0 36 | } 37 | 38 | pos := 0 39 | for pos < emitter.buffer_pos { 40 | // See the "reader.c" code for more details on UTF-8 encoding. Note 41 | // that we assume that the buffer contains a valid UTF-8 sequence. 42 | 43 | // Read the next UTF-8 character. 44 | octet := emitter.buffer[pos] 45 | 46 | var w int 47 | var value rune 48 | switch { 49 | case octet&0x80 == 0x00: 50 | w, value = 1, rune(octet&0x7F) 51 | case octet&0xE0 == 0xC0: 52 | w, value = 2, rune(octet&0x1F) 53 | case octet&0xF0 == 0xE0: 54 | w, value = 3, rune(octet&0x0F) 55 | case octet&0xF8 == 0xF0: 56 | w, value = 4, rune(octet&0x07) 57 | } 58 | for k := 1; k < w; k++ { 59 | octet = emitter.buffer[pos+k] 60 | value = (value << 6) + (rune(octet) & 0x3F) 61 | } 62 | pos += w 63 | 64 | // Write the character. 65 | if value < 0x10000 { 66 | var b [2]byte 67 | b[high] = byte(value >> 8) 68 | b[low] = byte(value & 0xFF) 69 | emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1]) 70 | } else { 71 | // Write the character using a surrogate pair (check "reader.c"). 72 | var b [4]byte 73 | value -= 0x10000 74 | b[high] = byte(0xD8 + (value >> 18)) 75 | b[low] = byte((value >> 10) & 0xFF) 76 | b[high+2] = byte(0xDC + ((value >> 8) & 0xFF)) 77 | b[low+2] = byte(value & 0xFF) 78 | emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1], b[2], b[3]) 79 | } 80 | } 81 | 82 | // Write the raw buffer. 83 | if err := emitter.write_handler(emitter, emitter.raw_buffer); err != nil { 84 | return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) 85 | } 86 | emitter.buffer_pos = 0 87 | emitter.raw_buffer = emitter.raw_buffer[:0] 88 | return true 89 | } 90 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | default: 3 | @godep go build 4 | @ls -ltrh 5 | 6 | setup: 7 | @echo Installing developer tooling, godep and reflex 8 | go get github.com/tools/godep 9 | go get github.com/cespare/reflex/... 10 | go get golang.org/x/tools/cmd/cover 11 | go get github.com/vektra/mockery/... 12 | 13 | .goxc.ok: 14 | @echo Installing crossbuild tooling. This will take a while... 15 | go get github.com/laher/goxc 16 | goxc -t 17 | touch .goxc.ok 18 | 19 | watch: 20 | @reflex -g '*.go' make test 21 | 22 | test: 23 | @godep go test -coverprofile=c.out 24 | 25 | coverage: test 26 | @godep go tool cover -html=c.out 27 | 28 | bump: 29 | @goxc bump 30 | 31 | release: .goxc.ok 32 | godep save 33 | goxc 34 | 35 | mocks: 36 | @mockery -name Converter 37 | 38 | brew_sha: 39 | @shasum -a 256 $(ver)/blade_$(ver)_darwin_amd64.zip 40 | .PHONY: default test setup release watch coverage mocks bump brew_sha 41 | 42 | -------------------------------------------------------------------------------- /blade.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | log "github.com/Sirupsen/logrus" 5 | "os" 6 | "path" 7 | ) 8 | 9 | type Blade struct { 10 | Template string `yaml:"template"` 11 | Interpolation string `yaml:"interpolation"` 12 | Source string `yaml:"source"` 13 | Out string `yaml:"out"` 14 | IncludeContents bool `yaml:"contents"` 15 | DryRun bool 16 | Mount string `yaml:"mount"` 17 | } 18 | 19 | func (b *Blade) Run() { 20 | if b.Mount != "" { 21 | template := path.Join(b.Mount, "Contents.json") 22 | if _, err := os.Stat(template); os.IsNotExist(err) { 23 | log.Fatalf("A mount must point to an image catalog (Contents.json missing)") 24 | } 25 | b.Out = b.Mount 26 | b.Template = template 27 | } 28 | 29 | c := NewContentsFromFile(b.Template) 30 | 31 | var converter Converter 32 | 33 | if b.DryRun { 34 | converter = NewDryrunConverter() 35 | } else { 36 | cv := NewResizeConverter() 37 | cv.Interpolation = b.Interpolation 38 | converter = cv 39 | } 40 | 41 | r := NewRunner(c, converter, b.Source) 42 | if b.Out != "" { 43 | err := os.MkdirAll(b.Out, 0755) 44 | if err != nil { 45 | log.Fatalf("Cannot create output directory '%s' (%s)", b.Out, err) 46 | } 47 | r.OutDir = b.Out 48 | } 49 | 50 | if b.IncludeContents { 51 | r.GenerateContents = true 52 | } 53 | 54 | r.run() 55 | 56 | } 57 | -------------------------------------------------------------------------------- /bladefile.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "gopkg.in/yaml.v2" 5 | "io/ioutil" 6 | "os" 7 | ) 8 | 9 | type Bladefile struct { 10 | Blades []Blade `yaml:"blades"` 11 | } 12 | 13 | func (bf *Bladefile) Exists() bool { 14 | if _, err := os.Stat("Bladefile"); os.IsNotExist(err) { 15 | return false 16 | } 17 | return true 18 | } 19 | 20 | func (bf *Bladefile) Load() error { 21 | data, err := ioutil.ReadFile("Bladefile") 22 | if err != nil { 23 | return err 24 | } 25 | 26 | err = yaml.Unmarshal([]byte(data), bf) 27 | if err != nil { 28 | return err 29 | } 30 | return nil 31 | } 32 | -------------------------------------------------------------------------------- /contents.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | log "github.com/Sirupsen/logrus" 7 | "io/ioutil" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | type Contents struct { 13 | Images []ContentsImage `json:"images"` 14 | Info struct { 15 | Author string `json:"author"` 16 | Version int `json:"version"` 17 | } `json:"info"` 18 | } 19 | 20 | type ContentsImage struct { 21 | Filename string `json:"filename"` 22 | Idiom string `json:"idiom"` 23 | Scale string `json:"scale"` 24 | Size string `json:"size"` 25 | ScreenWidth string `json:"screenWidth,omitempty"` 26 | } 27 | 28 | func (ci *ContentsImage) GetScale() int { 29 | if ci.Scale == "" { 30 | return 1 31 | } 32 | 33 | factor, err := strconv.Atoi(ci.Scale[0:1]) 34 | if err != nil { 35 | log.Fatalf("Converter(Resize): cannot parse scale %s (%s).", ci.Size, err) 36 | } 37 | return factor 38 | } 39 | 40 | func (ci *ContentsImage) GetSize() (float64, float64) { 41 | a := strings.Split(ci.Size, "x") 42 | w, err := strconv.ParseFloat(a[0], 64) 43 | if err != nil { 44 | log.Fatalf("Converter(Resize): cannot parse width %s (%s).", a[0], err) 45 | } 46 | h, err := strconv.ParseFloat(a[1], 64) 47 | if err != nil { 48 | log.Fatalf("Converter(Resize): cannot parse height %s (%s).", a[0], err) 49 | } 50 | 51 | return w, h 52 | } 53 | 54 | func (ci *ContentsImage) BuildFilename(base string, rect Rect) string { 55 | scale := ci.GetScale() 56 | return fmt.Sprintf("%s-%s-%d@%dx.png", base, ci.Idiom, int(float64(rect.Width)/float64(scale)), scale) 57 | } 58 | 59 | func NewContentsFromFile(path string) *Contents { 60 | data, err := ioutil.ReadFile(path) 61 | if err != nil { 62 | log.Fatalf("Contents: cannot read from %s (%s).", path, err) 63 | } 64 | return NewContentsFromString(data) 65 | } 66 | 67 | func NewContentsFromString(data []byte) *Contents { 68 | var contents Contents 69 | json.Unmarshal(data, &contents) 70 | return &contents 71 | } 72 | 73 | func (c *Contents) WriteToFile(file string) error { 74 | b, err := json.MarshalIndent(c, "", " ") 75 | if err != nil { 76 | return err 77 | } 78 | 79 | err = ioutil.WriteFile(file, b, 0644) 80 | if err != nil { 81 | return err 82 | } 83 | return nil 84 | } 85 | -------------------------------------------------------------------------------- /contents_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | . "gopkg.in/check.v1" 5 | ) 6 | 7 | type ContentsSuite struct { 8 | } 9 | 10 | var _ = Suite(&ContentsSuite{}) 11 | 12 | func (cs *ContentsSuite) Test_should_build_filename(c *C) { 13 | ci := ContentsImage{} 14 | fname := ci.BuildFilename("base", Rect{42, 24}) 15 | c.Check(fname, Equals, "base--42@1x.png") 16 | 17 | ci.Idiom = "iphone" 18 | fname = ci.BuildFilename("base", Rect{42, 24}) 19 | c.Check(fname, Equals, "base-iphone-42@1x.png") 20 | 21 | ci.Scale = "1x" 22 | fname = ci.BuildFilename("base", Rect{42, 24}) 23 | c.Check(fname, Equals, "base-iphone-42@1x.png") 24 | 25 | ci.Scale = "3x" 26 | fname = ci.BuildFilename("base", Rect{42, 24}) 27 | c.Check(fname, Equals, "base-iphone-14@3x.png") 28 | } 29 | 30 | func (cs *ContentsSuite) Test_should_get_size(c *C) { 31 | ci := ContentsImage{} 32 | ci.Size = "42x24" 33 | w, h := ci.GetSize() 34 | c.Check(w, Equals, 42.0) 35 | c.Check(h, Equals, 24.0) 36 | 37 | ci.Size = "27.5x24.5" 38 | w, h = ci.GetSize() 39 | c.Check(w, Equals, 27.5) 40 | c.Check(h, Equals, 24.5) 41 | 42 | ci.Size = "27x24.5" 43 | w, h = ci.GetSize() 44 | c.Check(w, Equals, 27.0) 45 | c.Check(h, Equals, 24.5) 46 | 47 | ci.Size = "27.5x24" 48 | w, h = ci.GetSize() 49 | c.Check(w, Equals, 27.5) 50 | c.Check(h, Equals, 24.0) 51 | } 52 | 53 | func (cs *ContentsSuite) Test_should_get_scale(c *C) { 54 | ci := ContentsImage{} 55 | ci.Scale = "3x" 56 | s := ci.GetScale() 57 | c.Check(s, Equals, 3) 58 | 59 | ci.Scale = "" 60 | s = ci.GetScale() 61 | c.Check(s, Equals, 1) 62 | } 63 | -------------------------------------------------------------------------------- /converter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Converter interface { 4 | Convert(string, string, Rect) error 5 | Size(string) (Rect, error) 6 | } 7 | -------------------------------------------------------------------------------- /dimensions.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "math" 5 | ) 6 | 7 | type Dimensions struct { 8 | } 9 | 10 | type Rect struct { 11 | Width float64 12 | Height float64 13 | } 14 | 15 | func NewDimensions() *Dimensions { 16 | return &Dimensions{} 17 | } 18 | 19 | func (d *Dimensions) Compute(contents *Contents, meta *ContentsImage, sourceRect Rect) Rect { 20 | if meta.Size != "" { 21 | return dimensionFromSize(meta) 22 | } 23 | 24 | // 25 | // If we don't get an explicit size we have to compute it. 26 | // 1. Find the biggest scale factor in the requested idiom group 27 | // 2. Assuming the source image is the largest possible image we have, 28 | // compute a reduction scale factor (current / highest), and scale down the source. 29 | // e.g if highest scale factor for iPad is x3, and the current image request is x2, 30 | // we need to scale down the source image by 2/3. 31 | // 32 | 33 | // an idiom is just meta.Idiom, however with Apple Watch we have the same idiom with different 34 | // screen sizes. Feels like Apple patched this abstraction out with 'screenWidth'. So let's 35 | // compose a new idiom built from the original idiom and this screenWidth concept. 36 | idiom := meta.Idiom + meta.ScreenWidth 37 | highestFactor := 1.0 38 | 39 | for _, m := range contents.Images { 40 | if m.Idiom+m.ScreenWidth == idiom { 41 | highestFactor = math.Max(float64(highestFactor), float64(m.GetScale())) 42 | } 43 | } 44 | 45 | scaleDownFactor := float64(meta.GetScale()) / highestFactor 46 | //log.Printf("%v scale factor: %1.2f computed from %d and highest %2.0f", meta, scaleDownFactor, meta.GetScale(), highestFactor) 47 | return Rect{ 48 | float64(sourceRect.Width) * scaleDownFactor, 49 | float64(sourceRect.Height) * scaleDownFactor, 50 | } 51 | 52 | } 53 | 54 | func dimensionFromSize(c *ContentsImage) Rect { 55 | w, h := c.GetSize() 56 | factor := float64(c.GetScale()) 57 | 58 | return Rect{Width: factor * w, Height: factor * h} 59 | } 60 | -------------------------------------------------------------------------------- /dimensions_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | . "gopkg.in/check.v1" 5 | ) 6 | 7 | type DimensionsSuite struct { 8 | dim *Dimensions 9 | } 10 | 11 | var _ = Suite(&DimensionsSuite{ 12 | dim: NewDimensions(), 13 | }) 14 | 15 | func (s *DimensionsSuite) Test_should_compute_size_based_on_existing_scales_given_no_size(c *C) { 16 | contents := NewContentsFromFile("fixtures/contents-no-sizes.json") 17 | // iphone 18 | r := s.dim.Compute( 19 | contents, 20 | &contents.Images[0], 21 | Rect{60, 60}, 22 | ) 23 | c.Check(r.Width, Equals, 40.0) 24 | c.Check(r.Height, Equals, 40.0) 25 | 26 | // ipad 27 | r = s.dim.Compute( 28 | contents, 29 | &contents.Images[2], 30 | Rect{60, 60}, 31 | ) 32 | c.Check(r.Width, Equals, 60.0) 33 | c.Check(r.Height, Equals, 60.0) 34 | 35 | } 36 | 37 | func (s *DimensionsSuite) Test_should_use_size_and_scale_when_given_size_explicitely(c *C) { 38 | contents := NewContentsFromFile("fixtures/contents-appicon.json") 39 | r := s.dim.Compute( 40 | contents, 41 | &contents.Images[0], 42 | Rect{42, 42}, 43 | ) 44 | c.Check(r.Width, Equals, 29*2.0) 45 | c.Check(r.Height, Equals, 29*2.0) 46 | } 47 | -------------------------------------------------------------------------------- /docs/blade-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/docs/blade-s.png -------------------------------------------------------------------------------- /docs/blade-walkthrough.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/docs/blade-walkthrough.gif -------------------------------------------------------------------------------- /docs/blade.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/docs/blade.gif -------------------------------------------------------------------------------- /dryrun_converter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | log "github.com/Sirupsen/logrus" 5 | ) 6 | 7 | type DryrunConverter struct { 8 | } 9 | 10 | func NewDryrunConverter() *DryrunConverter { 11 | return &DryrunConverter{} 12 | } 13 | func (d *DryrunConverter) Size(in string) (Rect, error) { 14 | return Rect{42, 42}, nil 15 | } 16 | 17 | func (d *DryrunConverter) Convert(in string, out string, toRect Rect) error { 18 | log.Infof("(dryrun) Converting %s to %s using %v\n", in, out, toRect) 19 | return nil 20 | } 21 | -------------------------------------------------------------------------------- /fixtures/contents-appicon.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "29x29", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-Small@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "29x29", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-Small@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "40x40", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-40@2x.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "40x40", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-40@3x.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "60x60", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-60@2x.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-60@3x.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "29x29", 41 | "idiom" : "ipad", 42 | "filename" : "Icon-Small.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "29x29", 47 | "idiom" : "ipad", 48 | "filename" : "Icon-Small@2x-1.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "40x40", 53 | "idiom" : "ipad", 54 | "filename" : "Icon-40.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "40x40", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-40@2x-1.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "76x76", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-76.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "76x76", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-76@2x.png", 73 | "scale" : "2x" 74 | } 75 | ], 76 | "info" : { 77 | "version" : 1, 78 | "author" : "xcode" 79 | } 80 | } -------------------------------------------------------------------------------- /fixtures/contents-no-sizes-no-files.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x" 6 | }, 7 | { 8 | "idiom" : "iphone", 9 | "scale" : "3x" 10 | }, 11 | { 12 | "idiom" : "iphone", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "idiom" : "iphone", 17 | "scale" : "3x" 18 | }, 19 | { 20 | "idiom" : "iphone", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "idiom" : "ipad", 29 | "scale" : "1x" 30 | }, 31 | { 32 | "idiom" : "ipad", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "idiom" : "ipad", 37 | "scale" : "1x" 38 | }, 39 | { 40 | "idiom" : "ipad", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x" 46 | }, 47 | { 48 | "idiom" : "ipad", 49 | "scale" : "2x" 50 | } 51 | ], 52 | "info" : { 53 | "version" : 1, 54 | "author" : "xcode" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /fixtures/contents-no-sizes.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "filename" : "Icon-Small@2x.png", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "filename" : "Icon-Small@3x.png", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "ipad", 15 | "filename" : "Icon-40@2x.png", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /fixtures/full_integration/interpolation/Bladefile: -------------------------------------------------------------------------------- 1 | blades: 2 | - source: source.png 3 | mount: after/Assets.xcassets/AppIcon.appiconset 4 | contents: true 5 | - source: source.png 6 | mount: after/Assets.xcassets/Watch.imageset 7 | contents: true 8 | - source: source.png 9 | mount: after/Assets.xcassets/Image.imageset 10 | contents: true 11 | 12 | 13 | -------------------------------------------------------------------------------- /fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "source-iphone-29@2x.png", 5 | "idiom": "iphone", 6 | "scale": "2x", 7 | "size": "29x29" 8 | }, 9 | { 10 | "filename": "source-iphone-29@3x.png", 11 | "idiom": "iphone", 12 | "scale": "3x", 13 | "size": "29x29" 14 | }, 15 | { 16 | "filename": "source-iphone-40@2x.png", 17 | "idiom": "iphone", 18 | "scale": "2x", 19 | "size": "40x40" 20 | }, 21 | { 22 | "filename": "source-iphone-40@3x.png", 23 | "idiom": "iphone", 24 | "scale": "3x", 25 | "size": "40x40" 26 | }, 27 | { 28 | "filename": "source-iphone-60@2x.png", 29 | "idiom": "iphone", 30 | "scale": "2x", 31 | "size": "60x60" 32 | }, 33 | { 34 | "filename": "source-iphone-60@3x.png", 35 | "idiom": "iphone", 36 | "scale": "3x", 37 | "size": "60x60" 38 | } 39 | ], 40 | "info": { 41 | "author": "xcode", 42 | "version": 1 43 | } 44 | } -------------------------------------------------------------------------------- /fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-29@2x.png -------------------------------------------------------------------------------- /fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-29@3x.png -------------------------------------------------------------------------------- /fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-40@2x.png -------------------------------------------------------------------------------- /fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-40@3x.png -------------------------------------------------------------------------------- /fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-60@2x.png -------------------------------------------------------------------------------- /fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/AppIcon.appiconset/source-iphone-60@3x.png -------------------------------------------------------------------------------- /fixtures/full_integration/interpolation/after/Assets.xcassets/Image.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "source-universal-333@1x.png", 5 | "idiom": "universal", 6 | "scale": "1x", 7 | "size": "333.3333333333333x333.3333333333333" 8 | }, 9 | { 10 | "filename": "source-universal-333@2x.png", 11 | "idiom": "universal", 12 | "scale": "2x", 13 | "size": "333.3333333333333x333.3333333333333" 14 | }, 15 | { 16 | "filename": "source-universal-333@3x.png", 17 | "idiom": "universal", 18 | "scale": "3x", 19 | "size": "333.3333333333333x333.3333333333333" 20 | } 21 | ], 22 | "info": { 23 | "author": "xcode", 24 | "version": 1 25 | } 26 | } -------------------------------------------------------------------------------- /fixtures/full_integration/interpolation/after/Assets.xcassets/Image.imageset/source-universal-333@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/Image.imageset/source-universal-333@1x.png -------------------------------------------------------------------------------- /fixtures/full_integration/interpolation/after/Assets.xcassets/Image.imageset/source-universal-333@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/Image.imageset/source-universal-333@2x.png -------------------------------------------------------------------------------- /fixtures/full_integration/interpolation/after/Assets.xcassets/Image.imageset/source-universal-333@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/Image.imageset/source-universal-333@3x.png -------------------------------------------------------------------------------- /fixtures/full_integration/interpolation/after/Assets.xcassets/Watch.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "source-universal-333@1x.png", 5 | "idiom": "universal", 6 | "scale": "1x", 7 | "size": "333.3333333333333x333.3333333333333" 8 | }, 9 | { 10 | "filename": "source-universal-333@2x.png", 11 | "idiom": "universal", 12 | "scale": "2x", 13 | "size": "333.3333333333333x333.3333333333333" 14 | }, 15 | { 16 | "filename": "source-universal-333@3x.png", 17 | "idiom": "universal", 18 | "scale": "3x", 19 | "size": "333.3333333333333x333.3333333333333" 20 | }, 21 | { 22 | "filename": "source-watch-500@2x.png", 23 | "idiom": "watch", 24 | "scale": "2x", 25 | "size": "500x500" 26 | }, 27 | { 28 | "filename": "source-watch-500@2x.png", 29 | "idiom": "watch", 30 | "scale": "2x", 31 | "size": "500x500" 32 | }, 33 | { 34 | "filename": "source-watch-500@2x.png", 35 | "idiom": "watch", 36 | "scale": "2x", 37 | "size": "500x500" 38 | } 39 | ], 40 | "info": { 41 | "author": "xcode", 42 | "version": 1 43 | } 44 | } -------------------------------------------------------------------------------- /fixtures/full_integration/interpolation/after/Assets.xcassets/Watch.imageset/source-universal-333@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/Watch.imageset/source-universal-333@1x.png -------------------------------------------------------------------------------- /fixtures/full_integration/interpolation/after/Assets.xcassets/Watch.imageset/source-universal-333@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/Watch.imageset/source-universal-333@2x.png -------------------------------------------------------------------------------- /fixtures/full_integration/interpolation/after/Assets.xcassets/Watch.imageset/source-universal-333@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/Watch.imageset/source-universal-333@3x.png -------------------------------------------------------------------------------- /fixtures/full_integration/interpolation/after/Assets.xcassets/Watch.imageset/source-watch-500@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/after/Assets.xcassets/Watch.imageset/source-watch-500@2x.png -------------------------------------------------------------------------------- /fixtures/full_integration/interpolation/before/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /fixtures/full_integration/interpolation/before/Assets.xcassets/Image.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "scale" : "3x" 14 | } 15 | ], 16 | "info" : { 17 | "version" : 1, 18 | "author" : "xcode" 19 | } 20 | } -------------------------------------------------------------------------------- /fixtures/full_integration/interpolation/before/Assets.xcassets/Watch.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "idiom" : "watch", 17 | "scale" : "2x" 18 | }, 19 | { 20 | "idiom" : "watch", 21 | "scale" : "2x", 22 | "screen-width" : "<=145" 23 | }, 24 | { 25 | "idiom" : "watch", 26 | "scale" : "2x", 27 | "screen-width" : ">145" 28 | } 29 | ], 30 | "info" : { 31 | "version" : 1, 32 | "author" : "xcode" 33 | } 34 | } -------------------------------------------------------------------------------- /fixtures/full_integration/interpolation/source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jondot/blade/33b3a88b257b5a019b041823033f0e296c94027e/fixtures/full_integration/interpolation/source.png -------------------------------------------------------------------------------- /fixtures/single-file.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "filename" : "Icon-Small@2x.png", 6 | "scale" : "2x" 7 | } 8 | ], 9 | "info" : { 10 | "version" : 1, 11 | "author" : "xcode" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /fixtures/with-files.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "filename" : "Icon-Small@2x.png", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /integration.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | include FileUtils 3 | 4 | def error_if(condition, msg) 5 | return unless condition 6 | puts msg 7 | exit(1) 8 | end 9 | 10 | puts "Running integration tests..." 11 | 12 | blade = File.join(File.expand_path(File.dirname(__FILE__)), 'blade') 13 | tests = %w{ interpolation } 14 | 15 | tests.each do |test| 16 | test_dir = File.expand_path("fixtures/full_integration/#{test}", File.dirname(__FILE__)) 17 | cd test_dir 18 | rm_rf "after" 19 | cp_r "before", "after" 20 | 21 | 2.times do 22 | blade_out = `#{blade}` 23 | error_if(!blade_out.empty?, blade_out) 24 | exit(1) unless blade_out.empty? 25 | end 26 | 27 | diff_out = `git diff #{test_dir}` 28 | error_if(!diff_out.empty?, diff_out) 29 | putc "." 30 | end 31 | 32 | puts "\nOK" 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | log "github.com/Sirupsen/logrus" 6 | "gopkg.in/alecthomas/kingpin.v2" 7 | "io/ioutil" 8 | "os" 9 | ) 10 | 11 | var VERSION string 12 | 13 | var shouldInit = kingpin.Flag("init", "Initialize a Bladefile").Bool() 14 | var contentsTemplate = kingpin.Flag("template", "Contents json template to generate for.").Short('t').Default("Contents.json").String() 15 | var interpolation = kingpin.Flag("interpolation", "Interpolation: l2, l3 (Lanczos 2 and 3), n (nearest neighbor), bc (bicubic), bl (bilinear), mn (Mitchell-Netravali)").Short('i').Default("l3").String() 16 | var source = kingpin.Flag("source", "Source image to use. For optimal results supply a highest size PNG.").Short('s').String() 17 | var out = kingpin.Flag("out", "Out folder. Use current folder if none given.").Short('o').String() 18 | var includeContents = kingpin.Flag("catalog", "Include generation of a new image catalog Contents.json").Short('c').Bool() 19 | var dryRun = kingpin.Flag("dryrun", "Do a dry run, don't write out anything").Short('d').Bool() 20 | var mount = kingpin.Flag("mount", "Mount on an existing image catalog (take its Contents.json and output images to it)").Short('m').String() 21 | var verbose = kingpin.Flag("verbose", "Verbose output").Bool() 22 | var version = kingpin.Flag("version", "Current version").Short('v').Bool() 23 | 24 | var BLADEFILE = `# 25 | # Uncomment below to specify your own resources. 26 | # See https://github.com/jondot/blade for more information. 27 | # 28 | 29 | blades: 30 | - source: assets/icon-1024x1024.png # this image should be the only image, and the biggest image you can use (typically for icons, 1024x1024) 31 | mount: project_name/Assets.xcassets/AppIcon.appiconset 32 | contents: true # use 'false' if you want to only update existing catalog, 'true' if you want to generate a full catalog every time 33 | # - source: spaceship.png 34 | # mount: project_name/Images.xcassets/Spaceship.imageset # you can also generate regular image sets, not just app icons 35 | # - source: iTunesArtwork@2x.png 36 | # template: templates/iphone-appicon.json # use a template if you want to batch build catalogs regardless of an xcode project (e.g. as part of a CI process) 37 | # out: out/newiphone 38 | # contents: true 39 | ` 40 | 41 | func init() { 42 | log.SetLevel(log.FatalLevel) 43 | } 44 | func main() { 45 | kingpin.Parse() 46 | 47 | if *version { 48 | println(VERSION) 49 | os.Exit(0) 50 | } 51 | 52 | if *shouldInit { 53 | ioutil.WriteFile("Bladefile", []byte(BLADEFILE), 0644) 54 | fmt.Println("Wrote Bladefile.\nYou have 1 blade set up with recommended settings, please review that it matches your project.") 55 | os.Exit(0) 56 | } 57 | 58 | if *verbose { 59 | log.SetLevel(log.InfoLevel) 60 | } 61 | bladefile := Bladefile{} 62 | if bladefile.Exists() { 63 | log.Infof("Found a local Bladefile.") 64 | err := bladefile.Load() 65 | if err != nil { 66 | log.Fatalf("Cannot load bladefile (%s)", err) 67 | } 68 | 69 | log.Infof("Bladefile contains %d blade defs.", len(bladefile.Blades)) 70 | for _, blade := range bladefile.Blades { 71 | blade.Run() 72 | } 73 | } else { 74 | blade := Blade{ 75 | Template: *contentsTemplate, 76 | Interpolation: *interpolation, 77 | Source: *source, 78 | Out: *out, 79 | IncludeContents: *includeContents, 80 | DryRun: *dryRun, 81 | Mount: *mount, 82 | } 83 | blade.Run() 84 | 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /resize_converter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | log "github.com/Sirupsen/logrus" 5 | "github.com/nfnt/resize" 6 | "image" 7 | _ "image/jpeg" 8 | "image/png" 9 | "os" 10 | ) 11 | 12 | var interpolations = map[string]resize.InterpolationFunction{ 13 | "l3": resize.Lanczos3, 14 | "l2": resize.Lanczos2, 15 | "n": resize.NearestNeighbor, 16 | "bc": resize.Bicubic, 17 | "bl": resize.Bilinear, 18 | "mn": resize.MitchellNetravali, 19 | } 20 | 21 | type ResizeConverter struct { 22 | Interpolation string 23 | } 24 | 25 | func NewResizeConverter() *ResizeConverter { 26 | return &ResizeConverter{} 27 | } 28 | 29 | func (r *ResizeConverter) Size(inFile string) (Rect, error) { 30 | in, err := os.Open(inFile) 31 | if err != nil { 32 | log.Fatalf("Resize: cannot open file '%s' (%s).", inFile, err) 33 | } 34 | 35 | source, _, err := image.Decode(in) 36 | if err != nil { 37 | return Rect{}, err 38 | } 39 | 40 | return Rect{ 41 | float64(source.Bounds().Dx()), 42 | float64(source.Bounds().Dy()), 43 | }, nil 44 | } 45 | 46 | func (r *ResizeConverter) Convert(inFile string, outFile string, rect Rect) error { 47 | in, err := os.Open(inFile) 48 | if err != nil { 49 | log.Fatalf("Runner(File): cannot open source file '%s' for reading (%s).", inFile, err) 50 | } 51 | defer in.Close() 52 | 53 | out, err := os.Create(outFile) 54 | if err != nil { 55 | log.Printf("ERROR: cannot create new file %s (%s)", outFile, err) 56 | } 57 | defer out.Close() 58 | 59 | source, _, err := image.Decode(in) 60 | if err != nil { 61 | return err 62 | } 63 | 64 | interp, ok := interpolations[r.Interpolation] 65 | if !ok { 66 | interp = interpolations["l3"] 67 | } 68 | 69 | resized := resize.Resize(uint(rect.Width), uint(rect.Height), source, interp) 70 | png.Encode(out, resized) 71 | return nil 72 | } 73 | -------------------------------------------------------------------------------- /runner.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | log "github.com/Sirupsen/logrus" 6 | 7 | "path" 8 | "strings" 9 | ) 10 | 11 | type Runner struct { 12 | converter Converter 13 | contents *Contents 14 | SourceFile string 15 | OutDir string 16 | GenerateContents bool 17 | } 18 | 19 | func NewRunner(contents *Contents, converter Converter, sourceFile string) *Runner { 20 | return &Runner{contents: contents, converter: converter, SourceFile: sourceFile} 21 | } 22 | 23 | func (f *Runner) run() { 24 | contents := f.contents 25 | 26 | sourceRect, err := f.converter.Size(f.SourceFile) 27 | if err != nil { 28 | log.Fatalf("Runner(File): cannot decode source file '%s' (%s).", f.SourceFile, err) 29 | } 30 | 31 | dim := NewDimensions() 32 | for i, _ := range contents.Images { 33 | meta := &contents.Images[i] 34 | rect := dim.Compute(contents, meta, sourceRect) 35 | 36 | // sync meta structure with the new data we've generated 37 | if meta.Size == "" { 38 | meta.Size = fmt.Sprintf("%vx%v", float64(rect.Width)/float64(meta.GetScale()), float64(rect.Height)/float64(meta.GetScale())) 39 | } 40 | if meta.Filename == "" { 41 | baseName := strings.TrimSuffix(path.Base(f.SourceFile), path.Ext(f.SourceFile)) 42 | meta.Filename = meta.BuildFilename(baseName, rect) 43 | } 44 | 45 | outpath := path.Join(f.OutDir, meta.Filename) 46 | err = f.converter.Convert(f.SourceFile, outpath, rect) 47 | if err != nil { 48 | log.Printf("ERROR: cannot convert %s (%s)", outpath, err) 49 | } 50 | 51 | log.Infof("[%s] -> %v", f.OutDir, contents.Images[i]) 52 | } 53 | log.Infof("[%s] %d images generated.", f.OutDir, len(contents.Images)) 54 | 55 | if f.GenerateContents { 56 | err := contents.WriteToFile(path.Join(f.OutDir, "Contents.json")) 57 | if err != nil { 58 | log.Fatalf("Could not write Contents.json to %s (%s).", f.OutDir, err) 59 | } 60 | log.Infof("[%s] Wrote Contents.json.", f.OutDir) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /runner_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/stretchr/testify/mock" 5 | . "gopkg.in/check.v1" 6 | ) 7 | 8 | type MockConverter struct { 9 | mock.Mock 10 | } 11 | 12 | func (_m *MockConverter) Convert(_a0 string, _a1 string, _a2 Rect) error { 13 | ret := _m.Called(_a0, _a1, _a2) 14 | 15 | var r0 error 16 | if rf, ok := ret.Get(0).(func(string, string, Rect) error); ok { 17 | r0 = rf(_a0, _a1, _a2) 18 | } else { 19 | r0 = ret.Error(0) 20 | } 21 | 22 | return r0 23 | } 24 | func (_m *MockConverter) Size(_a0 string) (Rect, error) { 25 | ret := _m.Called(_a0) 26 | 27 | var r0 Rect 28 | if rf, ok := ret.Get(0).(func(string) Rect); ok { 29 | r0 = rf(_a0) 30 | } else { 31 | r0 = ret.Get(0).(Rect) 32 | } 33 | 34 | var r1 error 35 | if rf, ok := ret.Get(1).(func(string) error); ok { 36 | r1 = rf(_a0) 37 | } else { 38 | r1 = ret.Error(1) 39 | } 40 | 41 | return r0, r1 42 | } 43 | 44 | type RunnerSuite struct { 45 | conv Converter 46 | } 47 | 48 | var _ = Suite(&RunnerSuite{ 49 | conv: &RunnerSuiteConverter{}, 50 | }) 51 | 52 | type RunnerSuiteConverter struct { 53 | } 54 | 55 | func (r *RunnerSuiteConverter) Convert(infile string, outfile string, sourceRect Rect) error { 56 | return nil 57 | } 58 | 59 | func (r *RunnerSuiteConverter) Size(infile string) (Rect, error) { 60 | return Rect{60, 60}, nil 61 | } 62 | 63 | func (s *RunnerSuite) Testshould_auto_complete_filename_given_missing_in_template(c *C) { 64 | contents := NewContentsFromFile("fixtures/contents-no-sizes-no-files.json") 65 | r := NewRunner(contents, s.conv, "sourcefile.png") 66 | 67 | for _, meta := range contents.Images { 68 | c.Check(meta.Filename, Equals, "") 69 | } 70 | r.run() 71 | 72 | for _, meta := range contents.Images { 73 | c.Check(meta.Filename, Not(Equals), "") 74 | } 75 | c.Check(contents.Images[0].Filename, Equals, "sourcefile-iphone-20@2x.png") 76 | c.Check(contents.Images[6].Filename, Equals, "sourcefile-ipad-30@1x.png") 77 | } 78 | 79 | func (s *RunnerSuite) Test_should_leave_file_as_is_given_in_template(c *C) { 80 | contents := NewContentsFromFile("fixtures/with-files.json") 81 | r := NewRunner(contents, s.conv, "sourcefile.png") 82 | 83 | r.run() 84 | 85 | for _, meta := range contents.Images { 86 | c.Check(meta.Filename, Not(Equals), "") 87 | } 88 | c.Check(contents.Images[0].Filename, Equals, "Icon-Small@2x.png") 89 | c.Check(contents.Images[1].Filename, Equals, "sourcefile-iphone-20@3x.png") 90 | } 91 | 92 | func (s *RunnerSuite) Test_should_use_outdir_given_nonempty(c *C) { 93 | contents := NewContentsFromFile("fixtures/single-file.json") 94 | mk := &MockConverter{} 95 | mk.On("Size", "sourcefile.png").Return(Rect{42, 42}, nil) 96 | // 28 because it's 2/3 97 | mk.On("Convert", "sourcefile.png", "mydir/Icon-Small@2x.png", Rect{42, 42}).Return(nil) 98 | 99 | r := NewRunner(contents, mk, "sourcefile.png") 100 | r.OutDir = "mydir" 101 | 102 | r.run() 103 | 104 | mk.AssertExpectations(c) 105 | } 106 | 107 | func (s *RunnerSuite) Test_should_fill_in_computed_size_given_empty_size(c *C) { 108 | contents := NewContentsFromFile("fixtures/contents-no-sizes.json") 109 | r := NewRunner(contents, s.conv, "sourcefile.png") 110 | 111 | r.run() 112 | 113 | c.Check(contents.Images[0].Size, Equals, "20x20") 114 | } 115 | 116 | func (s *RunnerSuite) Test_should_convert_given_list_of_images(c *C) { 117 | contents := NewContentsFromFile("fixtures/contents-appicon.json") 118 | mk := &MockConverter{} 119 | mk.On("Size", "sourcefile.png").Return(Rect{42, 42}, nil) 120 | // 28 because it's 2/3 121 | mk.On("Convert", mock.Anything, mock.Anything, mock.Anything).Return(nil).Times(12) 122 | 123 | r := NewRunner(contents, mk, "sourcefile.png") 124 | r.OutDir = "mydir" 125 | 126 | r.run() 127 | 128 | mk.AssertExpectations(c) 129 | } 130 | -------------------------------------------------------------------------------- /templates/iphone-appicon.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "29x29", 5 | "idiom" : "iphone", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "size" : "29x29", 10 | "idiom" : "iphone", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "size" : "40x40", 15 | "idiom" : "iphone", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "size" : "40x40", 20 | "idiom" : "iphone", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "size" : "60x60", 25 | "idiom" : "iphone", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "size" : "60x60", 30 | "idiom" : "iphone", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "29x29", 35 | "idiom" : "ipad", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "size" : "29x29", 40 | "idiom" : "ipad", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "size" : "40x40", 45 | "idiom" : "ipad", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "size" : "40x40", 50 | "idiom" : "ipad", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "size" : "76x76", 55 | "idiom" : "ipad", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "size" : "76x76", 60 | "idiom" : "ipad", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /templates/universal.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "minus-86@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /templates/watch.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x" 6 | }, 7 | { 8 | "idiom" : "watch", 9 | "screenWidth" : "{130,145}", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "watch", 14 | "screenWidth" : "{146,165}", 15 | "scale" : "2x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test_helper_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | . "gopkg.in/check.v1" 5 | "testing" 6 | ) 7 | 8 | func Test(t *testing.T) { TestingT(t) } 9 | --------------------------------------------------------------------------------