├── Procfile ├── image ├── Procfile ├── Godeps │ ├── _workspace │ │ └── .gitignore │ ├── Godeps.json │ └── Readme ├── 410050.jpg ├── deckbrew-image └── image.go ├── .gitignore ├── vendor ├── github.com │ ├── lib │ │ └── pq │ │ │ ├── .gitignore │ │ │ ├── oid │ │ │ ├── doc.go │ │ │ ├── gen.go │ │ │ └── types.go │ │ │ ├── user_posix.go │ │ │ ├── user_windows.go │ │ │ ├── LICENSE.md │ │ │ ├── CONTRIBUTING.md │ │ │ ├── buf.go │ │ │ ├── .travis.yml │ │ │ ├── url.go │ │ │ ├── README.md │ │ │ └── copy.go │ ├── spf13 │ │ ├── cobra │ │ │ ├── .travis.yml │ │ │ ├── .gitignore │ │ │ └── cobra.go │ │ └── pflag │ │ │ ├── LICENSE │ │ │ └── README.md │ ├── opentracing │ │ └── opentracing-go │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── Makefile │ │ │ ├── globaltracer.go │ │ │ ├── LICENSE │ │ │ ├── noop.go │ │ │ ├── gocontext.go │ │ │ ├── README.md │ │ │ ├── tracer.go │ │ │ ├── propagation.go │ │ │ └── span.go │ └── kyleconroy │ │ └── migrator │ │ ├── .gitignore │ │ ├── README.md │ │ ├── LICENSE │ │ └── migrator.go ├── gopkg.in │ └── stack.v1 │ │ ├── .travis.yml │ │ ├── LICENSE.md │ │ └── README.md ├── goji.io │ ├── .travis.yml │ ├── dispatch.go │ ├── pattern.go │ ├── router.go │ ├── internal │ │ ├── http.go │ │ ├── context.go │ │ └── internal.go │ ├── router_simple.go │ ├── LICENSE │ ├── pat │ │ ├── match.go │ │ ├── methods.go │ │ └── url.go │ ├── handle.go │ ├── pattern │ │ └── pattern.go │ ├── middleware │ │ └── middleware.go │ ├── README.md │ ├── mux.go │ ├── goji.go │ ├── middleware.go │ └── router_trie.go ├── stackmachine.com │ ├── vhost │ │ └── handler.go │ ├── logtrace │ │ ├── span.go │ │ └── tracer.go │ └── cql │ │ └── db.go ├── golang.org │ └── x │ │ └── net │ │ ├── PATENTS │ │ └── LICENSE └── code.google.com │ └── p │ └── go.net │ ├── PATENTS │ ├── LICENSE │ └── html │ ├── atom │ └── atom.go │ ├── const.go │ ├── doc.go │ ├── doctype.go │ ├── node.go │ └── escape.go ├── scripts ├── test └── importdb ├── api ├── migrations │ ├── 0004_update_set_id_length.sql │ ├── 0002_add_prices.sql │ └── 0001_create_card_table.sql ├── database.go ├── mtgjson_test.go ├── middleware_test.go ├── mtgjson.go ├── middleware.go ├── soup.go ├── handler_test.go ├── search.go └── handler.go ├── Godeps ├── Readme └── Godeps.json ├── .travis.yml ├── config └── config.go ├── LICENSE ├── brew ├── router.go ├── tcgplayer.go └── service.go ├── main.go └── web ├── static ├── index.html └── css │ ├── normalize.css │ ├── syntax.css │ └── style.css └── handler.go /Procfile: -------------------------------------------------------------------------------- 1 | web: deckbrew serve 2 | -------------------------------------------------------------------------------- /image/Procfile: -------------------------------------------------------------------------------- 1 | web: deckbrew-image 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | cards.json 2 | deckbrew 3 | env.sh 4 | -------------------------------------------------------------------------------- /image/Godeps/_workspace/.gitignore: -------------------------------------------------------------------------------- 1 | /pkg 2 | /bin 3 | -------------------------------------------------------------------------------- /vendor/github.com/lib/pq/.gitignore: -------------------------------------------------------------------------------- 1 | .db 2 | *.test 3 | *~ 4 | *.swp 5 | -------------------------------------------------------------------------------- /image/410050.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyleconroy/deckbrew/HEAD/image/410050.jpg -------------------------------------------------------------------------------- /scripts/test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | go test -v $(go list ./... | grep -v /vendor/) 4 | -------------------------------------------------------------------------------- /image/deckbrew-image: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyleconroy/deckbrew/HEAD/image/deckbrew-image -------------------------------------------------------------------------------- /vendor/github.com/spf13/cobra/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.1 4 | script: 5 | - go test ./... 6 | - go build 7 | -------------------------------------------------------------------------------- /image/Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/kyleconroy/deckbrew-image", 3 | "GoVersion": "go1.3", 4 | "Deps": [] 5 | } 6 | -------------------------------------------------------------------------------- /api/migrations/0004_update_set_id_length.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE sets ALTER COLUMN id TYPE varchar(10); 2 | ALTER TABLE cards ALTER COLUMN sets TYPE varchar(10)[]; 3 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /image/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 | -------------------------------------------------------------------------------- /vendor/github.com/lib/pq/oid/doc.go: -------------------------------------------------------------------------------- 1 | // Package oid contains OID constants 2 | // as defined by the Postgres server. 3 | package oid 4 | 5 | // Oid is a Postgres Object ID. 6 | type Oid uint32 7 | -------------------------------------------------------------------------------- /vendor/gopkg.in/stack.v1/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.0.3 5 | - 1.1.2 6 | - 1.2.2 7 | - 1.3.3 8 | - 1.4.3 9 | - 1.5.3 10 | - release 11 | - tip 12 | -------------------------------------------------------------------------------- /scripts/importdb: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | dropdb --if-exists deckbrew 5 | createdb deckbrew 6 | curl -o latest.dump `heroku pg:backups public-url` 7 | pg_restore --verbose --clean --no-acl --no-owner -h localhost -U $USER -d deckbrew latest.dump 8 | -------------------------------------------------------------------------------- /vendor/github.com/opentracing/opentracing-go/.gitignore: -------------------------------------------------------------------------------- 1 | # IntelliJ project files 2 | .idea/ 3 | opentracing-go.iml 4 | opentracing-go.ipr 5 | opentracing-go.iws 6 | 7 | # Test results 8 | *.cov 9 | *.html 10 | test.log 11 | 12 | # Build dir 13 | build/ 14 | -------------------------------------------------------------------------------- /vendor/goji.io/.travis.yml: -------------------------------------------------------------------------------- 1 | go_import_path: goji.io 2 | language: go 3 | sudo: false 4 | 5 | matrix: 6 | include: 7 | - go: 1.5 8 | - go: 1.5.1 9 | - go: 1.5.2 10 | - go: 1.5.3 11 | - go: 1.6 12 | - go: tip 13 | 14 | script: 15 | - go test -cover -race ./... 16 | -------------------------------------------------------------------------------- /vendor/github.com/opentracing/opentracing-go/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.5 5 | - tip 6 | 7 | install: 8 | - go get -d -t github.com/opentracing/opentracing-go/... 9 | - go get -u github.com/golang/lint/... 10 | script: 11 | - make test lint vet 12 | - go build ./... 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.6 4 | services: 5 | - postgresql 6 | addons: 7 | postgresql: "9.4" 8 | install: 9 | - go build 10 | before_script: 11 | - psql -c 'create database deckbrewtest;' -U postgres 12 | - ./deckbrew migrate 13 | - ./deckbrew sync 14 | script: 15 | - ./scripts/test 16 | -------------------------------------------------------------------------------- /vendor/stackmachine.com/vhost/handler.go: -------------------------------------------------------------------------------- 1 | package vhost 2 | 3 | import "net/http" 4 | 5 | type Handler map[string]http.Handler 6 | 7 | func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 8 | if handler, ok := h[r.Host]; ok { 9 | handler.ServeHTTP(w, r) 10 | } else { 11 | http.NotFound(w, r) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /api/database.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/kyleconroy/deckbrew/config" 5 | "github.com/kyleconroy/migrator" 6 | _ "github.com/lib/pq" 7 | ) 8 | 9 | func MigrateDatabase() error { 10 | cfg, err := config.FromEnv() 11 | if err != nil { 12 | return err 13 | } 14 | return migrator.Run(cfg.DB.DB, "api/migrations") 15 | } 16 | -------------------------------------------------------------------------------- /api/mtgjson_test.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestLoadCardJSON(t *testing.T) { 8 | collection, err := LoadCollection("../cards.json") 9 | 10 | if err != nil { 11 | t.Fatal(err) 12 | } 13 | 14 | _, ok := collection["LEA"] 15 | 16 | if !ok { 17 | t.Fatal("The collection did not load properly") 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vendor/github.com/lib/pq/user_posix.go: -------------------------------------------------------------------------------- 1 | // Package pq is a pure Go Postgres driver for the database/sql package. 2 | 3 | // +build darwin freebsd linux nacl netbsd openbsd solaris 4 | 5 | package pq 6 | 7 | import "os/user" 8 | 9 | func userCurrent() (string, error) { 10 | u, err := user.Current() 11 | if err != nil { 12 | return "", err 13 | } 14 | return u.Username, nil 15 | } 16 | -------------------------------------------------------------------------------- /vendor/github.com/spf13/cobra/.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 | 24 | cobra.test 25 | -------------------------------------------------------------------------------- /vendor/github.com/kyleconroy/migrator/.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 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /vendor/goji.io/dispatch.go: -------------------------------------------------------------------------------- 1 | package goji 2 | 3 | import ( 4 | "net/http" 5 | 6 | "goji.io/internal" 7 | "golang.org/x/net/context" 8 | ) 9 | 10 | type dispatch struct{} 11 | 12 | func (d dispatch) ServeHTTPC(ctx context.Context, w http.ResponseWriter, r *http.Request) { 13 | h := ctx.Value(internal.Handler) 14 | if h == nil { 15 | http.NotFound(w, r) 16 | } else { 17 | h.(Handler).ServeHTTPC(ctx, w, r) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vendor/goji.io/pattern.go: -------------------------------------------------------------------------------- 1 | package goji 2 | 3 | // httpMethods is an internal interface for the HTTPMethods pattern 4 | // optimization. See the documentation on Pattern for more. 5 | type httpMethods interface { 6 | HTTPMethods() map[string]struct{} 7 | } 8 | 9 | // pathPrefix is an internal interface for the PathPrefix pattern optimization. 10 | // See the documentation on Pattern for more. 11 | type pathPrefix interface { 12 | PathPrefix() string 13 | } 14 | -------------------------------------------------------------------------------- /vendor/goji.io/router.go: -------------------------------------------------------------------------------- 1 | package goji 2 | 3 | import ( 4 | "goji.io/internal" 5 | "golang.org/x/net/context" 6 | ) 7 | 8 | type match struct { 9 | context.Context 10 | p Pattern 11 | h Handler 12 | } 13 | 14 | func (m match) Value(key interface{}) interface{} { 15 | switch key { 16 | case internal.Pattern: 17 | return m.p 18 | case internal.Handler: 19 | return m.h 20 | default: 21 | return m.Context.Value(key) 22 | } 23 | } 24 | 25 | var _ context.Context = match{} 26 | -------------------------------------------------------------------------------- /vendor/github.com/opentracing/opentracing-go/Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | .DEFAULT_GOAL := test 4 | 5 | .PHONY: test 6 | test: 7 | go test -v -cover ./... 8 | 9 | .PHONY: lint 10 | lint: 11 | golint ./... 12 | @# Run again with magic to exit non-zero if golint outputs anything. 13 | @! (golint ./... | read dummy) 14 | 15 | .PHONY: vet 16 | vet: 17 | go vet ./... 18 | 19 | .PHONY: example 20 | example: 21 | go build -o build/dapperish-example ./examples/dapperish.go 22 | ./build/dapperish-example 23 | -------------------------------------------------------------------------------- /vendor/goji.io/internal/http.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "net/http" 5 | 6 | "golang.org/x/net/context" 7 | ) 8 | 9 | // ContextWrapper is a standard bridge type from http.Handlers to context-aware 10 | // Handlers. It is included here so that the middleware subpackage can use it to 11 | // unwrap Handlers. 12 | type ContextWrapper struct { 13 | http.Handler 14 | } 15 | 16 | // ServeHTTPC implements goji.Handler. 17 | func (c ContextWrapper) ServeHTTPC(ctx context.Context, w http.ResponseWriter, r *http.Request) { 18 | c.Handler.ServeHTTP(w, r) 19 | } 20 | -------------------------------------------------------------------------------- /vendor/goji.io/internal/context.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | // ContextKey is a type used for Goji's context.Context keys. 4 | type ContextKey int 5 | 6 | const ( 7 | // Path is the context key used to store the path Goji uses for its 8 | // PathPrefix optimization. 9 | Path ContextKey = iota 10 | // Pattern is the context key used to store the Pattern that Goji last 11 | // matched. 12 | Pattern 13 | // Handler is the context key used to store the Handler that Goji last 14 | // mached (and will therefore dispatch to at the end of the middleware 15 | // stack). 16 | Handler 17 | ) 18 | -------------------------------------------------------------------------------- /vendor/gopkg.in/stack.v1/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2014 Chris Hines 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /api/migrations/0002_add_prices.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE prices ( 2 | multiverse_id varchar(20) NOT NULL, 3 | created timestamp DEFAULT now(), 4 | foil boolean DEFAULT false, 5 | low integer DEFAULT 0, 6 | median integer DEFAULT 0, 7 | high integer DEFAULT 0 8 | ); 9 | 10 | CREATE INDEX prices_created_index ON prices(created); 11 | CREATE INDEX prices_id_index ON prices(multiverse_id); 12 | CREATE INDEX prices_low_index ON prices(low); 13 | CREATE INDEX prices_median_index ON prices(median); 14 | CREATE INDEX prices_high_index ON prices(high); 15 | -------------------------------------------------------------------------------- /vendor/goji.io/router_simple.go: -------------------------------------------------------------------------------- 1 | // +build goji_router_simple 2 | 3 | package goji 4 | 5 | import ( 6 | "net/http" 7 | 8 | "golang.org/x/net/context" 9 | ) 10 | 11 | /* 12 | This is the simplest correct router implementation for Goji. 13 | */ 14 | 15 | type router []route 16 | 17 | type route struct { 18 | Pattern 19 | Handler 20 | } 21 | 22 | func (rt *router) add(p Pattern, h Handler) { 23 | *rt = append(*rt, route{p, h}) 24 | } 25 | 26 | func (rt *router) route(ctx context.Context, r *http.Request) context.Context { 27 | for _, route := range *rt { 28 | if ctx := route.Match(ctx, r); ctx != nil { 29 | return &match{ctx, route.Pattern, route.Handler} 30 | } 31 | } 32 | return &match{Context: ctx} 33 | } 34 | -------------------------------------------------------------------------------- /api/middleware_test.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | 8 | "golang.org/x/net/context" 9 | 10 | "goji.io" 11 | "goji.io/middleware" 12 | "goji.io/pat" 13 | ) 14 | 15 | func TestTracing(t *testing.T) { 16 | mux := goji.NewMux() 17 | 18 | var name string 19 | mux.HandleFuncC(pat.Get("/mtg/cards/:id"), 20 | func(ctx context.Context, w http.ResponseWriter, r *http.Request) { 21 | pattern := middleware.Pattern(ctx).(*pat.Pattern) 22 | name = pattern.String() 23 | }) 24 | 25 | req, _ := http.NewRequest("GET", "/mtg/cards/foo", nil) 26 | w := httptest.NewRecorder() 27 | mux.ServeHTTP(w, req) 28 | 29 | if name != "/mtg/cards/:id" { 30 | t.Errorf("name is %s", name) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /vendor/goji.io/internal/internal.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package internal is a private package that allows Goji to expose a less 3 | confusing interface to its users. This package must not be used outside of Goji; 4 | every piece of its functionality has been exposed by one of Goji's subpackages. 5 | 6 | The problem this package solves is to allow Goji to internally agree on types 7 | and secret values between its packages without introducing import cycles. Goji 8 | needs to agree on these types and values in order to organize its public API 9 | into audience-specific subpackages (for instance, a package for pattern authors, 10 | a package for middleware authors, and a main package for routing users) without 11 | exposing implementation details in any of the packages. 12 | */ 13 | package internal 14 | -------------------------------------------------------------------------------- /vendor/github.com/kyleconroy/migrator/README.md: -------------------------------------------------------------------------------- 1 | # Migrator 2 | 3 | Migrator is a simple framework for running and maintaing database migrations in 4 | Go. If you're looking for a full-featured migration framework, look elsewhere, 5 | as Migrator is kept intentionally simple. 6 | 7 | ## Usage 8 | 9 | ```golang 10 | 11 | package main 12 | 13 | import ( 14 | "github.com/kyleconroy/migrator" 15 | ) 16 | 17 | func main() { 18 | err := migrator.Run(db, "migrations") 19 | } 20 | ``` 21 | 22 | ## How it Works 23 | 24 | Migrator creates a `migrations` table in the database, in which it keeps track 25 | of what migrations have been run. Migrations are just SQL files stored in a 26 | directory. The migration order is determined by the putting all the SQL 27 | filenames into a slice and using `sort.String`. 28 | 29 | ## Development 30 | -------------------------------------------------------------------------------- /vendor/github.com/lib/pq/user_windows.go: -------------------------------------------------------------------------------- 1 | // Package pq is a pure Go Postgres driver for the database/sql package. 2 | package pq 3 | 4 | import ( 5 | "path/filepath" 6 | "syscall" 7 | ) 8 | 9 | // Perform Windows user name lookup identically to libpq. 10 | // 11 | // The PostgreSQL code makes use of the legacy Win32 function 12 | // GetUserName, and that function has not been imported into stock Go. 13 | // GetUserNameEx is available though, the difference being that a 14 | // wider range of names are available. To get the output to be the 15 | // same as GetUserName, only the base (or last) component of the 16 | // result is returned. 17 | func userCurrent() (string, error) { 18 | pw_name := make([]uint16, 128) 19 | pwname_size := uint32(len(pw_name)) - 1 20 | err := syscall.GetUserNameEx(syscall.NameSamCompatible, &pw_name[0], &pwname_size) 21 | if err != nil { 22 | return "", err 23 | } 24 | s := syscall.UTF16ToString(pw_name) 25 | u := filepath.Base(s) 26 | return u, nil 27 | } 28 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "stackmachine.com/cql" 8 | ) 9 | 10 | type Config struct { 11 | DB *cql.DB 12 | Port string 13 | 14 | HostImage string 15 | HostAPI string 16 | HostWeb string 17 | } 18 | 19 | func env(key, empty string) string { 20 | value := os.Getenv(key) 21 | if value == "" { 22 | return empty 23 | } 24 | return value 25 | } 26 | 27 | func FromEnv() (*Config, error) { 28 | 29 | // Configure the database 30 | url := env("DATABASE_URL", "") 31 | if url == "" { 32 | return nil, fmt.Errorf("connection requires DATABASE_URL environment variable") 33 | } 34 | 35 | db, err := cql.Open("postgres", url) 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | port := env("PORT", "3000") 41 | return &Config{ 42 | DB: db, 43 | Port: port, 44 | HostImage: env("DECKBREW_IMAGE_HOST", "deckbrew.image:"+port), 45 | HostAPI: env("DECKBREW_API_HOST", "deckbrew.api:"+port), 46 | HostWeb: env("DECKBREW_WEB_HOST", "deckbrew.web:"+port), 47 | }, nil 48 | } 49 | -------------------------------------------------------------------------------- /vendor/github.com/opentracing/opentracing-go/globaltracer.go: -------------------------------------------------------------------------------- 1 | package opentracing 2 | 3 | var ( 4 | globalTracer Tracer = NoopTracer{} 5 | ) 6 | 7 | // InitGlobalTracer sets the [singleton] opentracing.Tracer returned by 8 | // GlobalTracer(). Those who use GlobalTracer (rather than directly manage an 9 | // opentracing.Tracer instance) should call InitGlobalTracer as early as possible in 10 | // main(), prior to calling the `StartSpan` (etc) global funcs below. Prior to 11 | // calling `InitGlobalTracer`, any Spans started via the `StartSpan` (etc) 12 | // globals are noops. 13 | func InitGlobalTracer(tracer Tracer) { 14 | globalTracer = tracer 15 | } 16 | 17 | // GlobalTracer returns the global singleton `Tracer` implementation. 18 | // Before `InitGlobalTracer()` is called, the `GlobalTracer()` is a noop 19 | // implementation that drops all data handed to it. 20 | func GlobalTracer() Tracer { 21 | return globalTracer 22 | } 23 | 24 | // StartSpan defers to `Tracer.StartSpan`. See `GlobalTracer()`. 25 | func StartSpan(operationName string) Span { 26 | return globalTracer.StartSpan(operationName) 27 | } 28 | -------------------------------------------------------------------------------- /vendor/github.com/lib/pq/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2013, 'pq' Contributors 2 | Portions Copyright (C) 2011 Blake Mizerany 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Kyle Conroy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /brew/router.go: -------------------------------------------------------------------------------- 1 | package brew 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/kyleconroy/deckbrew/config" 8 | ) 9 | 10 | type router struct { 11 | cfg *config.Config 12 | } 13 | 14 | func base(host string) string { 15 | if strings.Contains(host, ":") { 16 | return "http://" + host 17 | } else { 18 | return "https://" + host 19 | } 20 | } 21 | 22 | func (r router) CardURL(id string) string { 23 | return fmt.Sprintf("%s/mtg/cards/%s", base(r.cfg.HostAPI), id) 24 | } 25 | 26 | func (r router) EditionURL(id int) string { 27 | return fmt.Sprintf("%s/mtg/cards?multiverseid=%d", base(r.cfg.HostAPI), id) 28 | } 29 | 30 | func (r router) SetURL(id string) string { 31 | return fmt.Sprintf("%s/mtg/sets/%s", base(r.cfg.HostAPI), id) 32 | } 33 | 34 | func (r router) SetCardsURL(id string) string { 35 | return fmt.Sprintf("%s/mtg/cards?set=%s", base(r.cfg.HostAPI), id) 36 | } 37 | 38 | func (r router) EditionImageURL(id int) string { 39 | return fmt.Sprintf("%s/mtg/multiverseid/%d.jpg", base(r.cfg.HostImage), id) 40 | } 41 | 42 | func (r router) EditionHtmlURL(id int) string { 43 | return fmt.Sprintf("%s/mtg/cards/%d", base(r.cfg.HostWeb), id) 44 | } 45 | -------------------------------------------------------------------------------- /vendor/goji.io/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, 2016 Carl Jackson (carl@avtok.com) 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /vendor/github.com/kyleconroy/migrator/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Kyle Conroy 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 all 13 | 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 THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /vendor/github.com/opentracing/opentracing-go/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 The OpenTracing Authors 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /image/image.go: -------------------------------------------------------------------------------- 1 | package image 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httputil" 6 | "regexp" 7 | ) 8 | 9 | var pattern = regexp.MustCompile(`^/mtg/multiverseid/(\d+)\.jpg$`) 10 | 11 | func NewSingleHostReverseProxy() *httputil.ReverseProxy { 12 | 13 | director := func(req *http.Request) { 14 | // Incoming requests: /mtg/multiverseid/\d+.jpg 15 | matches := pattern.FindStringSubmatch(req.URL.Path) 16 | id := "0" 17 | 18 | if matches != nil && len(matches) > 1 { 19 | id = matches[1] 20 | } 21 | 22 | req.URL.Scheme = "http" 23 | req.URL.Host = "gatherer.wizards.com" 24 | req.URL.Path = "/Handlers/Image.ashx" 25 | values := req.URL.Query() 26 | values.Set("type", "card") 27 | values.Set("multiverseid", id) 28 | req.URL.RawQuery = values.Encode() 29 | } 30 | return &httputil.ReverseProxy{Director: director} 31 | } 32 | 33 | func images(w http.ResponseWriter, r *http.Request) { 34 | // change the request host to match the target 35 | r.Host = "gatherer.wizards.com" 36 | proxy := NewSingleHostReverseProxy() 37 | proxy.ServeHTTP(w, r) 38 | } 39 | 40 | func New() http.Handler { 41 | mux := http.NewServeMux() 42 | mux.HandleFunc("/mtg/multiverseid/", images) 43 | return mux 44 | } 45 | -------------------------------------------------------------------------------- /vendor/goji.io/pat/match.go: -------------------------------------------------------------------------------- 1 | package pat 2 | 3 | import ( 4 | "sort" 5 | 6 | "goji.io/internal" 7 | "goji.io/pattern" 8 | "golang.org/x/net/context" 9 | ) 10 | 11 | type match struct { 12 | context.Context 13 | pat *Pattern 14 | matches []string 15 | } 16 | 17 | func (m match) Value(key interface{}) interface{} { 18 | switch key { 19 | case pattern.AllVariables: 20 | var vs map[pattern.Variable]interface{} 21 | if vsi := m.Context.Value(key); vsi == nil { 22 | if len(m.pat.pats) == 0 { 23 | return nil 24 | } 25 | vs = make(map[pattern.Variable]interface{}, len(m.matches)) 26 | } else { 27 | vs = vsi.(map[pattern.Variable]interface{}) 28 | } 29 | 30 | for _, p := range m.pat.pats { 31 | vs[p.name] = m.matches[p.idx] 32 | } 33 | return vs 34 | case internal.Path: 35 | if len(m.matches) == len(m.pat.pats)+1 { 36 | return m.matches[len(m.matches)-1] 37 | } 38 | return "" 39 | } 40 | 41 | if k, ok := key.(pattern.Variable); ok { 42 | i := sort.Search(len(m.pat.pats), func(i int) bool { 43 | return m.pat.pats[i].name >= k 44 | }) 45 | if i < len(m.pat.pats) && m.pat.pats[i].name == k { 46 | return m.matches[m.pat.pats[i].idx] 47 | } 48 | } 49 | 50 | return m.Context.Value(key) 51 | } 52 | -------------------------------------------------------------------------------- /vendor/github.com/lib/pq/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing to pq 2 | 3 | `pq` has a backlog of pull requests, but contributions are still very 4 | much welcome. You can help with patch review, submitting bug reports, 5 | or adding new functionality. There is no formal style guide, but 6 | please conform to the style of existing code and general Go formatting 7 | conventions when submitting patches. 8 | 9 | ### Patch review 10 | 11 | Help review existing open pull requests by commenting on the code or 12 | proposed functionality. 13 | 14 | ### Bug reports 15 | 16 | We appreciate any bug reports, but especially ones with self-contained 17 | (doesn't depend on code outside of pq), minimal (can't be simplified 18 | further) test cases. It's especially helpful if you can submit a pull 19 | request with just the failing test case (you'll probably want to 20 | pattern it after the tests in 21 | [conn_test.go](https://github.com/lib/pq/blob/master/conn_test.go). 22 | 23 | ### New functionality 24 | 25 | There are a number of pending patches for new functionality, so 26 | additional feature patches will take a while to merge. Still, patches 27 | are generally reviewed based on usefulness and complexity in addition 28 | to time-in-queue, so if you have a knockout idea, take a shot. Feel 29 | free to open an issue discussion your proposed patch beforehand. 30 | -------------------------------------------------------------------------------- /vendor/goji.io/pat/methods.go: -------------------------------------------------------------------------------- 1 | package pat 2 | 3 | /* 4 | Delete returns a Pat route that only matches the DELETE HTTP method. 5 | */ 6 | func Delete(pat string) *Pattern { 7 | return newWithMethods(pat, "DELETE") 8 | } 9 | 10 | /* 11 | Get returns a Pat route that only matches the GET and HEAD HTTP method. HEAD 12 | requests are handled transparently by net/http. 13 | */ 14 | func Get(pat string) *Pattern { 15 | return newWithMethods(pat, "GET", "HEAD") 16 | } 17 | 18 | /* 19 | Head returns a Pat route that only matches the HEAD HTTP method. 20 | */ 21 | func Head(pat string) *Pattern { 22 | return newWithMethods(pat, "HEAD") 23 | } 24 | 25 | /* 26 | Options returns a Pat route that only matches the OPTIONS HTTP method. 27 | */ 28 | func Options(pat string) *Pattern { 29 | return newWithMethods(pat, "OPTIONS") 30 | } 31 | 32 | /* 33 | Patch returns a Pat route that only matches the PATCH HTTP method. 34 | */ 35 | func Patch(pat string) *Pattern { 36 | return newWithMethods(pat, "PATCH") 37 | } 38 | 39 | /* 40 | Post returns a Pat route that only matches the POST HTTP method. 41 | */ 42 | func Post(pat string) *Pattern { 43 | return newWithMethods(pat, "POST") 44 | } 45 | 46 | /* 47 | Put returns a Pat route that only matches the PUT HTTP method. 48 | */ 49 | func Put(pat string) *Pattern { 50 | return newWithMethods(pat, "PUT") 51 | } 52 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the Go project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of Go, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of Go. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of Go or any code incorporated within this 19 | implementation of Go constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of Go 22 | shall terminate as of the date such litigation is filed. 23 | -------------------------------------------------------------------------------- /vendor/code.google.com/p/go.net/PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the Go project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of Go, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of Go. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of Go or any code incorporated within this 19 | implementation of Go constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of Go 22 | shall terminate as of the date such litigation is filed. 23 | -------------------------------------------------------------------------------- /vendor/goji.io/pat/url.go: -------------------------------------------------------------------------------- 1 | package pat 2 | 3 | import "net/url" 4 | 5 | // Stolen (with modifications) from net/url in the Go stdlib 6 | 7 | func ishex(c byte) bool { 8 | switch { 9 | case '0' <= c && c <= '9': 10 | return true 11 | case 'a' <= c && c <= 'f': 12 | return true 13 | case 'A' <= c && c <= 'F': 14 | return true 15 | } 16 | return false 17 | } 18 | 19 | func unhex(c byte) byte { 20 | switch { 21 | case '0' <= c && c <= '9': 22 | return c - '0' 23 | case 'a' <= c && c <= 'f': 24 | return c - 'a' + 10 25 | case 'A' <= c && c <= 'F': 26 | return c - 'A' + 10 27 | } 28 | return 0 29 | } 30 | 31 | func unescape(s string) (string, error) { 32 | // Count %, check that they're well-formed. 33 | n := 0 34 | for i := 0; i < len(s); { 35 | switch s[i] { 36 | case '%': 37 | n++ 38 | if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) { 39 | s = s[i:] 40 | if len(s) > 3 { 41 | s = s[:3] 42 | } 43 | return "", url.EscapeError(s) 44 | } 45 | i += 3 46 | default: 47 | i++ 48 | } 49 | } 50 | 51 | if n == 0 { 52 | return s, nil 53 | } 54 | 55 | t := make([]byte, len(s)-2*n) 56 | j := 0 57 | for i := 0; i < len(s); { 58 | switch s[i] { 59 | case '%': 60 | t[j] = unhex(s[i+1])<<4 | unhex(s[i+2]) 61 | j++ 62 | i += 3 63 | default: 64 | t[j] = s[i] 65 | j++ 66 | i++ 67 | } 68 | } 69 | return string(t), nil 70 | } 71 | -------------------------------------------------------------------------------- /vendor/gopkg.in/stack.v1/README.md: -------------------------------------------------------------------------------- 1 | [![GoDoc](https://godoc.org/github.com/go-stack/stack?status.svg)](https://godoc.org/github.com/go-stack/stack) [![Go Report Card](https://goreportcard.com/badge/go-stack/stack)](https://goreportcard.com/report/go-stack/stack) 2 | 3 | # stack 4 | 5 | Package stack implements utilities to capture, manipulate, and format call 6 | stacks. It provides a simpler API than package runtime. 7 | 8 | The implementation takes care of the minutia and special cases of interpreting 9 | the program counter (pc) values returned by runtime.Callers. 10 | 11 | ## Versioning 12 | 13 | Package stack publishes releases via [semver](http://semver.org/) compatible Git 14 | tags prefixed with a single 'v'. The master branch always contains the latest 15 | release. The develop branch contains unreleased commits. 16 | 17 | ## Formatting 18 | 19 | Package stack's types implement fmt.Formatter, which provides a simple and 20 | flexible way to declaratively configure formatting when used with logging or 21 | error tracking packages. 22 | 23 | ```go 24 | func DoTheThing() { 25 | c := stack.Caller(0) 26 | log.Print(c) // "source.go:10" 27 | log.Printf("%+v", c) // "pkg/path/source.go:10" 28 | log.Printf("%n", c) // "DoTheThing" 29 | 30 | s := stack.Trace().TrimRuntime() 31 | log.Print(s) // "[source.go:15 caller.go:42 main.go:14]" 32 | } 33 | ``` 34 | 35 | See the docs for all of the supported formatting options. 36 | -------------------------------------------------------------------------------- /vendor/github.com/lib/pq/buf.go: -------------------------------------------------------------------------------- 1 | package pq 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "github.com/lib/pq/oid" 7 | ) 8 | 9 | type readBuf []byte 10 | 11 | func (b *readBuf) int32() (n int) { 12 | n = int(int32(binary.BigEndian.Uint32(*b))) 13 | *b = (*b)[4:] 14 | return 15 | } 16 | 17 | func (b *readBuf) oid() (n oid.Oid) { 18 | n = oid.Oid(binary.BigEndian.Uint32(*b)) 19 | *b = (*b)[4:] 20 | return 21 | } 22 | 23 | func (b *readBuf) int16() (n int) { 24 | n = int(binary.BigEndian.Uint16(*b)) 25 | *b = (*b)[2:] 26 | return 27 | } 28 | 29 | func (b *readBuf) string() string { 30 | i := bytes.IndexByte(*b, 0) 31 | if i < 0 { 32 | errorf("invalid message format; expected string terminator") 33 | } 34 | s := (*b)[:i] 35 | *b = (*b)[i+1:] 36 | return string(s) 37 | } 38 | 39 | func (b *readBuf) next(n int) (v []byte) { 40 | v = (*b)[:n] 41 | *b = (*b)[n:] 42 | return 43 | } 44 | 45 | func (b *readBuf) byte() byte { 46 | return b.next(1)[0] 47 | } 48 | 49 | type writeBuf []byte 50 | 51 | func (b *writeBuf) int32(n int) { 52 | x := make([]byte, 4) 53 | binary.BigEndian.PutUint32(x, uint32(n)) 54 | *b = append(*b, x...) 55 | } 56 | 57 | func (b *writeBuf) int16(n int) { 58 | x := make([]byte, 2) 59 | binary.BigEndian.PutUint16(x, uint16(n)) 60 | *b = append(*b, x...) 61 | } 62 | 63 | func (b *writeBuf) string(s string) { 64 | *b = append(*b, (s + "\000")...) 65 | } 66 | 67 | func (b *writeBuf) byte(c byte) { 68 | *b = append(*b, c) 69 | } 70 | 71 | func (b *writeBuf) bytes(v []byte) { 72 | *b = append(*b, v...) 73 | } 74 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/code.google.com/p/go.net/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/lib/pq/oid/gen.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | // Generate the table of OID values 4 | // Run with 'go run gen.go'. 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | "os" 11 | "os/exec" 12 | 13 | "database/sql" 14 | _ "github.com/lib/pq" 15 | ) 16 | 17 | func main() { 18 | datname := os.Getenv("PGDATABASE") 19 | sslmode := os.Getenv("PGSSLMODE") 20 | 21 | if datname == "" { 22 | os.Setenv("PGDATABASE", "pqgotest") 23 | } 24 | 25 | if sslmode == "" { 26 | os.Setenv("PGSSLMODE", "disable") 27 | } 28 | 29 | db, err := sql.Open("postgres", "") 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | cmd := exec.Command("gofmt") 34 | cmd.Stderr = os.Stderr 35 | w, err := cmd.StdinPipe() 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | f, err := os.Create("types.go") 40 | if err != nil { 41 | log.Fatal(err) 42 | } 43 | cmd.Stdout = f 44 | err = cmd.Start() 45 | if err != nil { 46 | log.Fatal(err) 47 | } 48 | fmt.Fprintln(w, "// generated by 'go run gen.go'; do not edit") 49 | fmt.Fprintln(w, "\npackage oid") 50 | fmt.Fprintln(w, "const (") 51 | rows, err := db.Query(` 52 | SELECT typname, oid 53 | FROM pg_type WHERE oid < 10000 54 | ORDER BY oid; 55 | `) 56 | if err != nil { 57 | log.Fatal(err) 58 | } 59 | var name string 60 | var oid int 61 | for rows.Next() { 62 | err = rows.Scan(&name, &oid) 63 | if err != nil { 64 | log.Fatal(err) 65 | } 66 | fmt.Fprintf(w, "T_%s Oid = %d\n", name, oid) 67 | } 68 | if err = rows.Err(); err != nil { 69 | log.Fatal(err) 70 | } 71 | fmt.Fprintln(w, ")") 72 | w.Close() 73 | cmd.Wait() 74 | } 75 | -------------------------------------------------------------------------------- /vendor/github.com/spf13/pflag/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Alex Ogier. All rights reserved. 2 | Copyright (c) 2012 The Go Authors. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following disclaimer 12 | in the documentation and/or other materials provided with the 13 | distribution. 14 | * Neither the name of Google Inc. nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /vendor/github.com/lib/pq/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.0.2 5 | - 1.0.3 6 | - 1.1 7 | - 1.2 8 | - 1.3 9 | - tip 10 | 11 | before_install: 12 | - psql --version 13 | - sudo /etc/init.d/postgresql stop 14 | - sudo apt-get -y --purge remove postgresql libpq-dev libpq5 postgresql-client-common postgresql-common 15 | - sudo rm -rf /var/lib/postgresql 16 | - wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - 17 | - sudo sh -c "echo deb http://apt.postgresql.org/pub/repos/apt/ precise-pgdg main $PGVERSION >> /etc/apt/sources.list.d/postgresql.list" 18 | - sudo apt-get update -qq 19 | - sudo apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::="--force-confnew" install postgresql-$PGVERSION postgresql-server-dev-$PGVERSION postgresql-contrib-$PGVERSION 20 | - sudo chmod 777 /etc/postgresql/$PGVERSION/main/pg_hba.conf 21 | - sudo echo "local all postgres trust" > /etc/postgresql/$PGVERSION/main/pg_hba.conf 22 | - sudo echo "local all all trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf 23 | - sudo echo "host all all 127.0.0.1/32 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf 24 | - sudo echo "host all all ::1/128 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf 25 | - sudo /etc/init.d/postgresql restart 26 | 27 | env: 28 | matrix: 29 | - PGVERSION=9.3 30 | - PGVERSION=9.2 31 | - PGVERSION=9.1 32 | - PGVERSION=9.0 33 | - PGVERSION=8.4 34 | 35 | script: 36 | - env PGUSER=postgres go test -v ./... 37 | 38 | before_script: 39 | - psql -c 'create database pqgotest;' -U postgres 40 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | 7 | "stackmachine.com/logtrace" 8 | "stackmachine.com/vhost" 9 | 10 | "github.com/kyleconroy/deckbrew/api" 11 | "github.com/kyleconroy/deckbrew/brew" 12 | "github.com/kyleconroy/deckbrew/config" 13 | "github.com/kyleconroy/deckbrew/image" 14 | "github.com/kyleconroy/deckbrew/web" 15 | "github.com/opentracing/opentracing-go" 16 | "github.com/spf13/cobra" 17 | ) 18 | 19 | func addCommand(root *cobra.Command, name, desc string, run func() error) { 20 | var command = &cobra.Command{ 21 | Use: name, 22 | Short: desc, 23 | Run: func(cmd *cobra.Command, args []string) { 24 | err := run() 25 | if err != nil { 26 | log.Fatalf("command-error %s", err) 27 | } 28 | }, 29 | } 30 | root.AddCommand(command) 31 | } 32 | 33 | func main() { 34 | log.SetFlags(0) 35 | opentracing.InitGlobalTracer(&logtrace.Tracer{}) 36 | 37 | var rootCmd = &cobra.Command{Use: "deckbrew"} 38 | addCommand(rootCmd, "migrate", "Migrate the database to the latest scheme", api.MigrateDatabase) 39 | addCommand(rootCmd, "serve", "Start and serve the REST API", Serve) 40 | addCommand(rootCmd, "sync", "Add new cards to the card database", api.SyncCards) 41 | rootCmd.Execute() 42 | } 43 | 44 | func Serve() error { 45 | cfg, err := config.FromEnv() 46 | if err != nil { 47 | return err 48 | } 49 | 50 | // Make sure to keep around some idle connections 51 | cfg.DB.DB.SetMaxIdleConns(10) 52 | 53 | reader, err := brew.NewReader(cfg) 54 | if err != nil { 55 | return err 56 | } 57 | 58 | return http.ListenAndServe(":"+cfg.Port, vhost.Handler{ 59 | cfg.HostAPI: api.New(cfg, reader), 60 | cfg.HostWeb: web.New(cfg, reader), 61 | cfg.HostImage: image.New(), 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /web/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | DeckBrew - Build better decks together 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
The Deckbrew API will shutdown on May 1st, 2018
15 |
16 |
17 |
18 |
19 |

DeckBrew

23 |
24 |
25 |
26 |
27 |
28 |
29 |

Deckbrew is shutting down

30 |

31 | On May 1st, 2018, the Deckbrew API will shut down. When I set out to build Deckbrew, there were no REST APIs for Magic cards. While I always wanted to do more with Deckbrew, I could never find the time. Thankfully, Scryfall showed up on the scene about an year ago. They have a dedicated team and an API that is better in every way. 32 |

33 |

34 | I've opened up the Deckbrew codebase for anyone that is still interested in what the API looked like at the time. 35 |

36 |
37 |
38 |
39 | 43 |
44 | 45 | 46 | -------------------------------------------------------------------------------- /web/static/css/normalize.css: -------------------------------------------------------------------------------- 1 | html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;}body{margin:0;}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block;}audio,canvas,progress,video{display:inline-block;vertical-align:baseline;}audio:not([controls]){display:none;height:0;}[hidden],template{display:none;}a{background:transparent;}a:active,a:hover{outline:0;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:bold;}dfn{font-style:italic;}h1{font-size:2em;margin:0.67em 0;}mark{background:#ff0;color:#000;}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-0.5em;}sub{bottom:-0.25em;}img{border:0;}svg:not(:root){overflow:hidden;}figure{margin:1em 40px;}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0;}pre{overflow:auto;}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em;}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0;}button{overflow:visible;}button,select{text-transform:none;}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;}button[disabled],html input[disabled]{cursor:default;}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}input{line-height:normal;}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto;}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none;}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em;}legend{border:0;padding:0;}textarea{overflow:auto;}optgroup{font-weight:bold;}table{border-collapse:collapse;border-spacing:0;}td,th{padding:0;} -------------------------------------------------------------------------------- /vendor/github.com/opentracing/opentracing-go/noop.go: -------------------------------------------------------------------------------- 1 | package opentracing 2 | 3 | // A NoopTracer is a trivial implementation of Tracer for which all operations 4 | // are no-ops. 5 | type NoopTracer struct{} 6 | 7 | type noopSpan struct{} 8 | 9 | var ( 10 | defaultNoopSpan = noopSpan{} 11 | defaultNoopTracer = NoopTracer{} 12 | ) 13 | 14 | const ( 15 | emptyString = "" 16 | ) 17 | 18 | // noopSpan: 19 | func (n noopSpan) SetTag(key string, value interface{}) Span { return n } 20 | func (n noopSpan) Finish() {} 21 | func (n noopSpan) FinishWithOptions(opts FinishOptions) {} 22 | func (n noopSpan) SetBaggageItem(key, val string) Span { return n } 23 | func (n noopSpan) BaggageItem(key string) string { return emptyString } 24 | func (n noopSpan) LogEvent(event string) {} 25 | func (n noopSpan) LogEventWithPayload(event string, payload interface{}) {} 26 | func (n noopSpan) Log(data LogData) {} 27 | func (n noopSpan) SetOperationName(operationName string) Span { return n } 28 | func (n noopSpan) Tracer() Tracer { return defaultNoopTracer } 29 | 30 | // StartSpan belongs to the Tracer interface. 31 | func (n NoopTracer) StartSpan(operationName string) Span { 32 | return defaultNoopSpan 33 | } 34 | 35 | // StartSpanWithOptions belongs to the Tracer interface. 36 | func (n NoopTracer) StartSpanWithOptions(opts StartSpanOptions) Span { 37 | return defaultNoopSpan 38 | } 39 | 40 | // Inject belongs to the Tracer interface. 41 | func (n NoopTracer) Inject(sp Span, format interface{}, carrier interface{}) error { 42 | return nil 43 | } 44 | 45 | // Join belongs to the Tracer interface. 46 | func (n NoopTracer) Join(operationName string, format interface{}, carrier interface{}) (Span, error) { 47 | return nil, ErrTraceNotFound 48 | } 49 | -------------------------------------------------------------------------------- /vendor/github.com/lib/pq/url.go: -------------------------------------------------------------------------------- 1 | package pq 2 | 3 | import ( 4 | "fmt" 5 | nurl "net/url" 6 | "sort" 7 | "strings" 8 | ) 9 | 10 | // ParseURL no longer needs to be used by clients of this library since supplying a URL as a 11 | // connection string to sql.Open() is now supported: 12 | // 13 | // sql.Open("postgres", "postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full") 14 | // 15 | // It remains exported here for backwards-compatibility. 16 | // 17 | // ParseURL converts a url to a connection string for driver.Open. 18 | // Example: 19 | // 20 | // "postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full" 21 | // 22 | // converts to: 23 | // 24 | // "user=bob password=secret host=1.2.3.4 port=5432 dbname=mydb sslmode=verify-full" 25 | // 26 | // A minimal example: 27 | // 28 | // "postgres://" 29 | // 30 | // This will be blank, causing driver.Open to use all of the defaults 31 | func ParseURL(url string) (string, error) { 32 | u, err := nurl.Parse(url) 33 | if err != nil { 34 | return "", err 35 | } 36 | 37 | if u.Scheme != "postgres" { 38 | return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme) 39 | } 40 | 41 | var kvs []string 42 | escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`) 43 | accrue := func(k, v string) { 44 | if v != "" { 45 | kvs = append(kvs, k+"="+escaper.Replace(v)) 46 | } 47 | } 48 | 49 | if u.User != nil { 50 | v := u.User.Username() 51 | accrue("user", v) 52 | 53 | v, _ = u.User.Password() 54 | accrue("password", v) 55 | } 56 | 57 | i := strings.Index(u.Host, ":") 58 | if i < 0 { 59 | accrue("host", u.Host) 60 | } else { 61 | accrue("host", u.Host[:i]) 62 | accrue("port", u.Host[i+1:]) 63 | } 64 | 65 | if u.Path != "" { 66 | accrue("dbname", u.Path[1:]) 67 | } 68 | 69 | q := u.Query() 70 | for k := range q { 71 | accrue(k, q.Get(k)) 72 | } 73 | 74 | sort.Strings(kvs) // Makes testing easier (not a performance concern) 75 | return strings.Join(kvs, " "), nil 76 | } 77 | -------------------------------------------------------------------------------- /web/static/css/syntax.css: -------------------------------------------------------------------------------- 1 | .highlight{background:#ffffff;}.highlight .c{color:#999988;font-style:italic}.highlight .err{color:#a61717;background-color:#e3d2d2}.highlight .k{font-weight:bold}.highlight .o{font-weight:bold}.highlight .cm{color:#999988;font-style:italic}.highlight .cp{color:#999999;font-weight:bold}.highlight .c1{color:#999988;font-style:italic}.highlight .cs{color:#999999;font-weight:bold;font-style:italic}.highlight .gd{color:#000000;background-color:#ffdddd}.highlight .gd .x{color:#000000;background-color:#ffaaaa}.highlight .ge{font-style:italic}.highlight .gr{color:#aa0000}.highlight .gh{color:#999999}.highlight .gi{color:#000000;background-color:#ddffdd}.highlight .gi .x{color:#000000;background-color:#aaffaa}.highlight .go{color:#888888}.highlight .gp{color:#555555}.highlight .gs{font-weight:bold}.highlight .gu{color:#aaaaaa}.highlight .gt{color:#aa0000}.highlight .kc{font-weight:bold}.highlight .kd{font-weight:bold}.highlight .kp{font-weight:bold}.highlight .kr{font-weight:bold}.highlight .kt{color:#445588;font-weight:bold}.highlight .m{color:#009999}.highlight .s{color:#d14}.highlight .na{color:#008080}.highlight .nb{color:#0086B3}.highlight .nc{color:#445588;font-weight:bold}.highlight .no{color:#008080}.highlight .ni{color:#800080}.highlight .ne{color:#990000;font-weight:bold}.highlight .nf{color:#990000;font-weight:bold}.highlight .nn{color:#555555}.highlight .nt{color:#000080}.highlight .nv{color:#008080}.highlight .ow{font-weight:bold}.highlight .w{color:#bbbbbb}.highlight .mf{color:#009999}.highlight .mh{color:#009999}.highlight .mi{color:#009999}.highlight .mo{color:#009999}.highlight .sb{color:#d14}.highlight .sc{color:#d14}.highlight .sd{color:#d14}.highlight .s2{color:#d14}.highlight .se{color:#d14}.highlight .sh{color:#d14}.highlight .si{color:#d14}.highlight .sx{color:#d14}.highlight .sr{color:#009926}.highlight .s1{color:#d14}.highlight .ss{color:#990073}.highlight .bp{color:#999999}.highlight .vc{color:#008080}.highlight .vg{color:#008080}.highlight .vi{color:#008080}.highlight .il{color:#009999} -------------------------------------------------------------------------------- /vendor/github.com/kyleconroy/migrator/migrator.go: -------------------------------------------------------------------------------- 1 | package migrator 2 | 3 | import ( 4 | "database/sql" 5 | "io/ioutil" 6 | "log" 7 | "path/filepath" 8 | "sort" 9 | "time" 10 | ) 11 | 12 | var create = ` 13 | CREATE TABLE IF NOT EXISTS migrations ( 14 | filename varchar(300) primary key, 15 | ran timestamp DEFAULT NOW() 16 | ) 17 | ` 18 | 19 | var find = ` 20 | SELECT TRUE, ran 21 | FROM migrations 22 | WHERE filename=$1 23 | ` 24 | 25 | var insert = ` 26 | INSERT INTO migrations (filename) 27 | VALUES ($1) 28 | ` 29 | 30 | // Return a sorted list of SQL filenames 31 | func migrations(path string) ([]string, error) { 32 | matches, err := filepath.Glob(path + "/*.sql") 33 | sort.Strings(matches) 34 | return matches, err 35 | } 36 | 37 | func logline(action, filename string) { 38 | log.Printf("action=%s filename=%s", action, filename) 39 | } 40 | 41 | func Run(db *sql.DB, path string) error { 42 | // Load the migrations file names into a slice 43 | // sort those files names 44 | files, err := migrations(path) 45 | if err != nil { 46 | return err 47 | } 48 | 49 | // Create the migrations table if it doesn't exist 50 | if _, err := db.Exec(create); err != nil { 51 | return err 52 | } 53 | 54 | for _, file := range files { 55 | name := filepath.Base(file) 56 | 57 | var found bool 58 | var timestamp time.Time 59 | err := db.QueryRow(find, name).Scan(&found, ×tamp) 60 | if err != nil && err != sql.ErrNoRows { 61 | return err 62 | } 63 | if found { 64 | logline("skipped", name) 65 | continue 66 | } 67 | tx, err := db.Begin() 68 | if err != nil { 69 | return err 70 | } 71 | 72 | query, err := ioutil.ReadFile(file) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | _, err = tx.Exec(string(query)) 78 | if err != nil { 79 | tx.Rollback() 80 | return err 81 | } 82 | 83 | _, err = tx.Exec(insert, name) 84 | if err != nil { 85 | tx.Rollback() 86 | return err 87 | } 88 | err = tx.Commit() 89 | if err != nil { 90 | return err 91 | } 92 | logline("created", name) 93 | } 94 | return nil 95 | } 96 | -------------------------------------------------------------------------------- /vendor/github.com/opentracing/opentracing-go/gocontext.go: -------------------------------------------------------------------------------- 1 | package opentracing 2 | 3 | import "golang.org/x/net/context" 4 | 5 | type contextKey struct{} 6 | 7 | var activeSpanKey = contextKey{} 8 | 9 | // ContextWithSpan returns a new `context.Context` that holds a reference to 10 | // the given `Span`. 11 | func ContextWithSpan(ctx context.Context, span Span) context.Context { 12 | return context.WithValue(ctx, activeSpanKey, span) 13 | } 14 | 15 | // BackgroundContextWithSpan is a convenience wrapper around 16 | // `ContextWithSpan(context.BackgroundContext(), ...)`. 17 | func BackgroundContextWithSpan(span Span) context.Context { 18 | return context.WithValue(context.Background(), activeSpanKey, span) 19 | } 20 | 21 | // SpanFromContext returns the `Span` previously associated with `ctx`, or 22 | // `nil` if no such `Span` could be found. 23 | func SpanFromContext(ctx context.Context) Span { 24 | val := ctx.Value(activeSpanKey) 25 | if span, ok := val.(Span); ok { 26 | return span 27 | } 28 | return nil 29 | } 30 | 31 | // StartSpanFromContext starts and returns a Span with `operationName`, using 32 | // any Span found within `ctx` as a parent. If no such parent could be found, 33 | // StartSpanFromContext creates a root (parentless) Span. 34 | // 35 | // The second return value is a context.Context object built around the 36 | // returned Span. 37 | // 38 | // Example usage: 39 | // 40 | // SomeFunction(ctx context.Context, ...) { 41 | // sp, ctx := opentracing.StartSpanFromContext(ctx, "SomeFunction") 42 | // defer sp.Finish() 43 | // ... 44 | // } 45 | func StartSpanFromContext(ctx context.Context, operationName string) (Span, context.Context) { 46 | return startSpanFromContextWithTracer(ctx, operationName, GlobalTracer()) 47 | } 48 | 49 | // startSpanFromContextWithTracer is factored out for testing purposes. 50 | func startSpanFromContextWithTracer(ctx context.Context, operationName string, tracer Tracer) (Span, context.Context) { 51 | parent := SpanFromContext(ctx) 52 | span := tracer.StartSpanWithOptions(StartSpanOptions{ 53 | OperationName: operationName, 54 | Parent: parent, 55 | }) 56 | return span, ContextWithSpan(ctx, span) 57 | } 58 | -------------------------------------------------------------------------------- /api/mtgjson.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | ) 7 | 8 | type MTGCollection map[string]MTGSet 9 | 10 | type MTGSet struct { 11 | Name string `json:"name"` 12 | Code string `json:"code"` 13 | Released string `json:"releaseDate"` 14 | Border string `json:"border"` 15 | Type string `json:"type"` 16 | Cards []MTGCard `json:"cards"` 17 | } 18 | 19 | type MTGLegality struct { 20 | Format string `json:"format"` 21 | Legality string `json:"legality"` 22 | } 23 | 24 | type MTGCard struct { 25 | Artist string `json:"artist"` 26 | Border string `json:"border"` 27 | Colors []string `json:"colors"` 28 | ConvertedCost float64 `json:"cmc"` 29 | Flavor string `json:"flavor"` 30 | HandModifier int `json:"hand"` 31 | Layout string `json:"layout"` 32 | LifeModifier int `json:"life"` 33 | Loyalty int `json:"loyalty"` 34 | Legalities []MTGLegality `json:"legalities"` 35 | ManaCost string `json:"manaCost"` 36 | MultiverseId int `json:"multiverseid"` 37 | Name string `json:"name"` 38 | Names []string `json:"names"` 39 | Number string `json:"number"` 40 | Power string `json:"power"` 41 | Rarity string `json:"rarity"` 42 | Rulings []MTGRuling `json:"rulings"` 43 | Subtypes []string `json:"subtypes"` 44 | Supertypes []string `json:"supertypes"` 45 | Text string `json:"text"` 46 | Toughness string `json:"toughness"` 47 | Type string `json:"type"` 48 | Types []string `json:"types"` 49 | Watermark string `json:"watermark"` 50 | } 51 | 52 | type MTGRuling struct { 53 | Date string `json:"date"` 54 | Text string `json:"text"` 55 | } 56 | 57 | func LoadCollection(path string) (MTGCollection, error) { 58 | blob, err := ioutil.ReadFile(path) 59 | 60 | if err != nil { 61 | return MTGCollection{}, err 62 | } 63 | 64 | var collection MTGCollection 65 | err = json.Unmarshal(blob, &collection) 66 | return collection, err 67 | } 68 | -------------------------------------------------------------------------------- /vendor/goji.io/handle.go: -------------------------------------------------------------------------------- 1 | package goji 2 | 3 | import ( 4 | "net/http" 5 | 6 | "goji.io/internal" 7 | "golang.org/x/net/context" 8 | ) 9 | 10 | /* 11 | Handle adds a new route to the Mux. Requests that match the given Pattern will 12 | be dispatched to the given http.Handler. If the http.Handler also supports 13 | Handler, that interface will be used instead. 14 | 15 | Routing is performed in the order in which routes are added: the first route 16 | with a matching Pattern will be used. In particular, Goji guarantees that 17 | routing is performed in a manner that is indistinguishable from the following 18 | algorithm: 19 | 20 | // Assume routes is a slice that every call to Handle appends to 21 | for route := range routes { 22 | // For performance, Patterns can opt out of this call to Match. 23 | // See the documentation for Pattern for more. 24 | if ctx2 := route.pattern.Match(ctx, r); ctx2 != nil { 25 | route.handler.ServeHTTPC(ctx2, w, r) 26 | break 27 | } 28 | } 29 | 30 | It is not safe to concurrently register routes from multiple goroutines, or to 31 | register routes concurrently with requests. 32 | */ 33 | func (m *Mux) Handle(p Pattern, h http.Handler) { 34 | gh, ok := h.(Handler) 35 | if !ok { 36 | gh = internal.ContextWrapper{Handler: h} 37 | } 38 | m.router.add(p, gh) 39 | } 40 | 41 | /* 42 | HandleFunc adds a new route to the Mux. It is equivalent to calling Handle on a 43 | handler wrapped with http.HandlerFunc, and is provided only for convenience. 44 | */ 45 | func (m *Mux) HandleFunc(p Pattern, h func(http.ResponseWriter, *http.Request)) { 46 | m.Handle(p, http.HandlerFunc(h)) 47 | } 48 | 49 | /* 50 | HandleC adds a new context-aware route to the Mux. See the documentation for 51 | Handle for more information about the semantics of routing. 52 | 53 | It is not safe to concurrently register routes from multiple goroutines, or to 54 | register routes concurrently with requests. 55 | */ 56 | func (m *Mux) HandleC(p Pattern, h Handler) { 57 | m.router.add(p, h) 58 | } 59 | 60 | /* 61 | HandleFuncC adds a new context-aware route to the Mux. It is equivalent to 62 | calling HandleC on a handler wrapped with HandlerFunc, and is provided for 63 | convenience. 64 | */ 65 | func (m *Mux) HandleFuncC(p Pattern, h func(context.Context, http.ResponseWriter, *http.Request)) { 66 | m.HandleC(p, HandlerFunc(h)) 67 | } 68 | -------------------------------------------------------------------------------- /web/static/css/style.css: -------------------------------------------------------------------------------- 1 | #eol { 2 | position: fixed; 3 | display: block; 4 | width: 100%; 5 | height: 25px; 6 | background: red; 7 | color: #fff; 8 | font-size: 20px; 9 | padding: 5px 0; 10 | text-align: center; 11 | } 12 | 13 | .site { 14 | padding-top: 35px; 15 | } 16 | 17 | body{font-family:Helvetica Neue,Helvetica,sans-sarif;}.content{margin:0 auto;max-width:980px;width:100%;}.header{//background: #fc5a02;background:#540387;border-bottom:#333 1px solid;box-shadow:0 0 10px #ddd;}.page,.wrapper{padding:0 10px;}.page a{color:#000;}.page p{font-size:14px;}h1,h2,h3,h4,h5{font-weight:normal;}.header .title,.header .nav{display:inline-block;vertical-align:middle;width:50%;margin:0;padding:0;}.page p.tagline{display:block;margin:20px auto;width:75%;font-size:25px;font-style:italic;}.header a{text-decoration:none;color:#fff;}.header .nav{text-align:right;}.header .nav li{display:inline-block;}.header .nav li a{display:inline-block;padding:25px 7px 19px;}.header .nav li a.active{border-bottom:6px solid #000;}.footer{margin:0 auto;max-width:980px;width:100%;border-top:#aaa 1px solid;}.footer .legal{margin:0;color:#aaa;padding:5px 0 5px;font-size:10px;}blockquote{display:block;font-family:monospace;margin:0;padding:10px;}.page blockquote p{margin:0;font-size:12px;padding:0;}pre,blockquote{background:#eee;border-radius:4px;border:1px solid #ddd;line-height:14px;}pre{font-size:12px;padding:10px;}th{text-align:left;}table{font-size:12px;width:100%;background-color:transparent;margin-bottom:20px;}table>thead>tr>th,table>tbody>tr>th,table>tfoot>tr>th,table>thead>tr>td,table>tbody>tr>td,table>tfoot>tr>td{padding:8px;line-height:1.428571429;vertical-align:top;border-top:1px solid #ddd;}table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd;}table>caption+thead>tr:first-child>th,table>colgroup+thead>tr:first-child>th,table>thead:first-child>tr:first-child>th,table>caption+thead>tr:first-child>td,table>colgroup+thead>tr:first-child>td,table>thead:first-child>tr:first-child>td{border-top:0;}table>tbody+tbody{border-top:2px solid #ddd;}table col[class*="col-"]{position:static;display:table-column;float:none;}table td[class*="col-"],table th[class*="col-"]{position:static;display:table-cell;float:none;}#embed_signup{width:75%;margin:0px auto 25px;background:none repeat scroll 0% 0% rgb(221,221,221);border-radius:10px;}#embed_signup form{padding:10px;text-align:center}#embed_signup .email{margin-bottom:10px;}#embed_signup label,#embed_signup input{display:inline-block;width:100%;max-width:300px;} 18 | -------------------------------------------------------------------------------- /vendor/goji.io/pattern/pattern.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package pattern contains utilities for Goji Pattern authors. 3 | 4 | Goji users should not import this package. Instead, use the utilities provided 5 | by your Pattern package. If you are looking for an implementation of Pattern, 6 | try Goji's pat subpackage, which contains a simple domain specific language for 7 | specifying routes. 8 | 9 | For Pattern authors, use of this subpackage is entirely optional. Nevertheless, 10 | authors who wish to take advantage of Goji's PathPrefix optimization or who wish 11 | to standardize on a few common interfaces may find this package useful. 12 | */ 13 | package pattern 14 | 15 | import ( 16 | "goji.io/internal" 17 | "golang.org/x/net/context" 18 | ) 19 | 20 | /* 21 | Variable is a standard type for the names of Pattern-bound variables, e.g. 22 | variables extracted from the URL. Pass the name of a variable, cast to this 23 | type, to context.Context.Value to retrieve the value bound to that name. 24 | */ 25 | type Variable string 26 | 27 | type allVariables struct{} 28 | 29 | /* 30 | AllVariables is a standard value which, when passed to context.Context.Value, 31 | returns all variable bindings present in the context, with bindings in newer 32 | contexts overriding values deeper in the stack. The concrete type 33 | 34 | map[Variable]interface{} 35 | 36 | is used for this purpose. If no variables are bound, nil should be returned 37 | instead of an empty map. 38 | */ 39 | var AllVariables = allVariables{} 40 | 41 | /* 42 | Path returns the path that the Goji router uses to perform the PathPrefix 43 | optimization. While this function does not distinguish between the absence of a 44 | path and an empty path, Goji will automatically extract a path from the request 45 | if none is present. 46 | 47 | By convention, paths are stored in their escaped form (i.e., the value returned 48 | by net/url.URL.EscapedPath, and not URL.Path) to ensure that Patterns have as 49 | much discretion as possible (e.g., to behave differently for '/' and '%2f'). 50 | */ 51 | func Path(ctx context.Context) string { 52 | pi := ctx.Value(internal.Path) 53 | if pi == nil { 54 | return "" 55 | } 56 | return pi.(string) 57 | } 58 | 59 | /* 60 | SetPath returns a new context in which the given path is used by the Goji Router 61 | when performing the PathPrefix optimization. See Path for more information about 62 | the intended semantics of this path. 63 | */ 64 | func SetPath(ctx context.Context, path string) context.Context { 65 | return context.WithValue(ctx, internal.Path, path) 66 | } 67 | -------------------------------------------------------------------------------- /vendor/goji.io/middleware/middleware.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package middleware contains utilities for Goji Middleware authors. 3 | 4 | Unless you are writing middleware for your application, you should avoid 5 | importing this package. Instead, use the abstractions provided by your 6 | middleware package. 7 | */ 8 | package middleware 9 | 10 | import ( 11 | "net/http" 12 | 13 | "goji.io" 14 | "goji.io/internal" 15 | "golang.org/x/net/context" 16 | ) 17 | 18 | /* 19 | Pattern returns the most recently matched Pattern, or nil if no pattern was 20 | matched. 21 | */ 22 | func Pattern(ctx context.Context) goji.Pattern { 23 | p := ctx.Value(internal.Pattern) 24 | if p == nil { 25 | return nil 26 | } 27 | return p.(goji.Pattern) 28 | } 29 | 30 | /* 31 | SetPattern returns a new context in which the given Pattern is used as the most 32 | recently matched pattern. 33 | */ 34 | func SetPattern(ctx context.Context, p goji.Pattern) context.Context { 35 | return context.WithValue(ctx, internal.Pattern, p) 36 | } 37 | 38 | /* 39 | Handler returns the handler corresponding to the most recently matched Pattern, 40 | or nil if no pattern was matched. 41 | 42 | Internally, Goji converts net/http.Handlers into goji.Handlers using a wrapper 43 | object. Users who wish to retrieve the original http.Handler they passed to Goji 44 | may call UnwrapHandler. 45 | 46 | The handler returned by this function is the one that will be dispatched to at 47 | the end of the middleware stack. If the returned Handler is nil, http.NotFound 48 | will be used instead. 49 | */ 50 | func Handler(ctx context.Context) goji.Handler { 51 | h := ctx.Value(internal.Handler) 52 | if h == nil { 53 | return nil 54 | } 55 | return h.(goji.Handler) 56 | } 57 | 58 | /* 59 | SetHandler returns a new context in which the given Handler was most recently 60 | matched and which consequently will be dispatched to. 61 | */ 62 | func SetHandler(ctx context.Context, h goji.Handler) context.Context { 63 | return context.WithValue(ctx, internal.Handler, h) 64 | } 65 | 66 | /* 67 | UnwrapHandler extracts the original http.Handler from a Goji-wrapped Handler 68 | object, or returns nil if the given Handler has not been wrapped in this way. 69 | 70 | This function is necessary because Goji uses goji.Handler as its native data 71 | type internally, and uses a wrapper struct to convert all http.Handlers it is 72 | passed into goji.Handlers. 73 | */ 74 | func UnwrapHandler(h goji.Handler) http.Handler { 75 | if cw, ok := h.(internal.ContextWrapper); ok { 76 | return cw.Handler 77 | } 78 | return nil 79 | } 80 | -------------------------------------------------------------------------------- /api/migrations/0001_create_card_table.sql: -------------------------------------------------------------------------------- 1 | CREATE EXTENSION pg_trgm; 2 | 3 | CREATE TABLE cards ( 4 | id varchar(150) primary key, 5 | name varchar(200) DEFAULT '', 6 | mana_cost varchar(45) DEFAULT '', 7 | toughness varchar(6) DEFAULT '', 8 | power varchar(6) DEFAULT '', 9 | types varchar(20)[] DEFAULT '{}', 10 | rarities varchar(15)[] DEFAULT '{}', 11 | sets varchar(6)[] DEFAULT '{}', 12 | subtypes varchar(100)[] DEFAULT '{}', 13 | supertypes varchar(100)[] DEFAULT '{}', 14 | colors varchar(5)[] DEFAULT '{}', 15 | formats varchar(9)[] DEFAULT '{}', 16 | status varchar(10)[] DEFAULT '{}', 17 | mids varchar(20)[] DEFAULT '{}', 18 | multicolor boolean DEFAULT false, 19 | record text DEFAULT '', 20 | rules text DEFAULT '', 21 | loyalty integer DEFAULT 0, 22 | cmc integer DEFAULT 0 23 | ); 24 | 25 | CREATE TABLE sets ( 26 | id varchar(6) primary key, 27 | name varchar(200) DEFAULT '', 28 | border varchar(40) DEFAULT '', 29 | type varchar(32) DEFAULT '' 30 | ); 31 | 32 | CREATE INDEX cards_name_id ON cards(id); 33 | CREATE INDEX cards_power_index ON cards(power); 34 | CREATE INDEX cards_toughness_index ON cards(toughness); 35 | CREATE INDEX cards_names_sort_index ON cards(name); 36 | CREATE INDEX cards_multicolor_index ON cards(multicolor); 37 | CREATE INDEX cards_types_index ON cards USING GIN(types); 38 | CREATE INDEX cards_subtypes_index ON cards USING GIN(subtypes); 39 | CREATE INDEX cards_supertypes_index ON cards USING GIN(supertypes); 40 | CREATE INDEX cards_colors_index ON cards USING GIN(colors); 41 | CREATE INDEX cards_sets_index ON cards USING GIN(sets); 42 | CREATE INDEX cards_rarities_index ON cards USING GIN(rarities); 43 | CREATE INDEX cards_status_index ON cards USING GIN(status); 44 | CREATE INDEX cards_formats_index ON cards USING GIN(formats); 45 | CREATE INDEX cards_mids_index ON cards USING GIN(mids); 46 | CREATE INDEX cards_names_index ON cards USING GIN(name gin_trgm_ops); 47 | CREATE INDEX cards_rules_index ON cards USING GIN(rules gin_trgm_ops); 48 | CREATE INDEX sets_type_index ON sets(type); 49 | CREATE INDEX sets_border_index ON sets(border); 50 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/kyleconroy/deckbrew", 3 | "GoVersion": "go1.6", 4 | "GodepVersion": "v62", 5 | "Deps": [ 6 | { 7 | "ImportPath": "code.google.com/p/go.net/html", 8 | "Comment": "null-110", 9 | "Rev": "'2b669bbfb7baae43e63e0cbe3ebb83c9f9ebb3e5'" 10 | }, 11 | { 12 | "ImportPath": "code.google.com/p/go.net/html/atom", 13 | "Comment": "null-110", 14 | "Rev": "'2b669bbfb7baae43e63e0cbe3ebb83c9f9ebb3e5'" 15 | }, 16 | { 17 | "ImportPath": "github.com/kyleconroy/migrator", 18 | "Rev": "2ce60bf29dfa58c507cfd86c30c185090687952f" 19 | }, 20 | { 21 | "ImportPath": "github.com/lib/pq", 22 | "Rev": "b549d8792f245c2dcee7702ddcd844280b8ecb10" 23 | }, 24 | { 25 | "ImportPath": "github.com/lib/pq/oid", 26 | "Rev": "b549d8792f245c2dcee7702ddcd844280b8ecb10" 27 | }, 28 | { 29 | "ImportPath": "github.com/opentracing/opentracing-go", 30 | "Comment": "v0.2-179-g01498ab", 31 | "Rev": "01498abd158dfdbe8e251856bc7d14cb0f046fa3" 32 | }, 33 | { 34 | "ImportPath": "github.com/spf13/cobra", 35 | "Rev": "10a8494a87448bf5003222d9974f166437e7f042" 36 | }, 37 | { 38 | "ImportPath": "github.com/spf13/pflag", 39 | "Rev": "94e98a55fb412fcbcfc302555cb990f5e1590627" 40 | }, 41 | { 42 | "ImportPath": "goji.io", 43 | "Comment": "v1.0", 44 | "Rev": "e1741450ad80d2837134e32ac7b1d0a7a2b11c51" 45 | }, 46 | { 47 | "ImportPath": "goji.io/internal", 48 | "Comment": "v1.0", 49 | "Rev": "e1741450ad80d2837134e32ac7b1d0a7a2b11c51" 50 | }, 51 | { 52 | "ImportPath": "goji.io/middleware", 53 | "Comment": "v1.0", 54 | "Rev": "e1741450ad80d2837134e32ac7b1d0a7a2b11c51" 55 | }, 56 | { 57 | "ImportPath": "goji.io/pat", 58 | "Comment": "v1.0", 59 | "Rev": "e1741450ad80d2837134e32ac7b1d0a7a2b11c51" 60 | }, 61 | { 62 | "ImportPath": "goji.io/pattern", 63 | "Comment": "v1.0", 64 | "Rev": "e1741450ad80d2837134e32ac7b1d0a7a2b11c51" 65 | }, 66 | { 67 | "ImportPath": "golang.org/x/net/context", 68 | "Rev": "0cb26f788dd4625d1956c6fd97ffc4c90669d129" 69 | }, 70 | { 71 | "ImportPath": "gopkg.in/stack.v1", 72 | "Comment": "v1.5.0", 73 | "Rev": "ac4c579c437a23e1a15814c867ccb82122fb383b" 74 | }, 75 | { 76 | "ImportPath": "stackmachine.com/cql", 77 | "Rev": "fdd63775af1ab26c46ce125dbb568120828e5c07" 78 | }, 79 | { 80 | "ImportPath": "stackmachine.com/logtrace", 81 | "Rev": "fdd63775af1ab26c46ce125dbb568120828e5c07" 82 | }, 83 | { 84 | "ImportPath": "stackmachine.com/vhost", 85 | "Rev": "fdd63775af1ab26c46ce125dbb568120828e5c07" 86 | } 87 | ] 88 | } 89 | -------------------------------------------------------------------------------- /vendor/code.google.com/p/go.net/html/atom/atom.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 atom provides integer codes (also known as atoms) for a fixed set of 6 | // frequently occurring HTML strings: tag names and attribute keys such as "p" 7 | // and "id". 8 | // 9 | // Sharing an atom's name between all elements with the same tag can result in 10 | // fewer string allocations when tokenizing and parsing HTML. Integer 11 | // comparisons are also generally faster than string comparisons. 12 | // 13 | // The value of an atom's particular code is not guaranteed to stay the same 14 | // between versions of this package. Neither is any ordering guaranteed: 15 | // whether atom.H1 < atom.H2 may also change. The codes are not guaranteed to 16 | // be dense. The only guarantees are that e.g. looking up "div" will yield 17 | // atom.Div, calling atom.Div.String will return "div", and atom.Div != 0. 18 | package atom 19 | 20 | // Atom is an integer code for a string. The zero value maps to "". 21 | type Atom uint32 22 | 23 | // String returns the atom's name. 24 | func (a Atom) String() string { 25 | start := uint32(a >> 8) 26 | n := uint32(a & 0xff) 27 | if start+n > uint32(len(atomText)) { 28 | return "" 29 | } 30 | return atomText[start : start+n] 31 | } 32 | 33 | func (a Atom) string() string { 34 | return atomText[a>>8 : a>>8+a&0xff] 35 | } 36 | 37 | // fnv computes the FNV hash with an arbitrary starting value h. 38 | func fnv(h uint32, s []byte) uint32 { 39 | for i := range s { 40 | h ^= uint32(s[i]) 41 | h *= 16777619 42 | } 43 | return h 44 | } 45 | 46 | func match(s string, t []byte) bool { 47 | for i, c := range t { 48 | if s[i] != c { 49 | return false 50 | } 51 | } 52 | return true 53 | } 54 | 55 | // Lookup returns the atom whose name is s. It returns zero if there is no 56 | // such atom. The lookup is case sensitive. 57 | func Lookup(s []byte) Atom { 58 | if len(s) == 0 || len(s) > maxAtomLen { 59 | return 0 60 | } 61 | h := fnv(hash0, s) 62 | if a := table[h&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) { 63 | return a 64 | } 65 | if a := table[(h>>16)&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) { 66 | return a 67 | } 68 | return 0 69 | } 70 | 71 | // String returns a string whose contents are equal to s. In that sense, it is 72 | // equivalent to string(s) but may be more efficient. 73 | func String(s []byte) string { 74 | if a := Lookup(s); a != 0 { 75 | return a.String() 76 | } 77 | return string(s) 78 | } 79 | -------------------------------------------------------------------------------- /web/handler.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "fmt" 5 | "html/template" 6 | "net/http" 7 | "net/url" 8 | "strconv" 9 | 10 | "golang.org/x/net/context" 11 | 12 | "github.com/kyleconroy/deckbrew/api" 13 | "github.com/kyleconroy/deckbrew/brew" 14 | "github.com/kyleconroy/deckbrew/config" 15 | _ "github.com/lib/pq" 16 | 17 | "goji.io" 18 | "goji.io/pat" 19 | ) 20 | 21 | const tmpl = ` 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |

{{.Card.Name}}

33 | {{.Card.Name}} 34 | 35 | 36 | ` 37 | 38 | type Web struct { 39 | r brew.Reader 40 | t *template.Template 41 | } 42 | 43 | type CardPage struct { 44 | Card brew.Card 45 | Edition brew.Edition 46 | } 47 | 48 | func (web *Web) HandleCard(ctx context.Context, w http.ResponseWriter, r *http.Request) { 49 | id, err := strconv.Atoi(pat.Param(ctx, "id")) 50 | if err != nil { 51 | http.Error(w, "Invalid ID", http.StatusBadRequest) 52 | return 53 | } 54 | 55 | u, _ := url.Parse(fmt.Sprintf("?multiverseid=%d", id)) 56 | s, err, _ := api.ParseSearch(u) 57 | if err != nil { 58 | http.Error(w, err.Error(), http.StatusBadRequest) 59 | return 60 | } 61 | cards, err := web.r.GetCards(ctx, s) 62 | if err != nil { 63 | http.Error(w, "Error", http.StatusInternalServerError) 64 | return 65 | } 66 | 67 | if len(cards) == 0 { 68 | http.Error(w, "No cards found", http.StatusNotFound) 69 | return 70 | } 71 | 72 | cp := CardPage{Card: cards[0]} 73 | for _, e := range cards[0].Editions { 74 | if e.MultiverseId == id { 75 | cp.Edition = e 76 | } 77 | } 78 | 79 | if err = web.t.Execute(w, cp); err != nil { 80 | http.Error(w, "Error", http.StatusInternalServerError) 81 | return 82 | } 83 | } 84 | 85 | func New(cfg *config.Config, r brew.Reader) http.Handler { 86 | mux := goji.NewMux() 87 | 88 | app := Web{ 89 | r: r, 90 | t: template.Must(template.New("card").Parse(tmpl)), 91 | } 92 | 93 | // Setup middleware 94 | mux.UseC(api.Recover) 95 | mux.UseC(api.Tracing) 96 | 97 | mux.HandleFuncC(pat.Get("/mtg/cards/:id"), app.HandleCard) 98 | mux.Handle(pat.New("/*"), http.FileServer(http.Dir("./web/static/"))) 99 | 100 | return mux 101 | } 102 | -------------------------------------------------------------------------------- /vendor/github.com/lib/pq/README.md: -------------------------------------------------------------------------------- 1 | # pq - A pure Go postgres driver for Go's database/sql package 2 | 3 | [![Build Status](https://travis-ci.org/lib/pq.png?branch=master)](https://travis-ci.org/lib/pq) 4 | 5 | ## Install 6 | 7 | go get github.com/lib/pq 8 | 9 | ## Docs 10 | 11 | For detailed documentation and basic usage examples, please see the package 12 | documentation at . 13 | 14 | ## Tests 15 | 16 | `go test` is used for testing. A running PostgreSQL server is 17 | required, with the ability to log in. The default database to connect 18 | to test with is "pqgotest," but it can be overridden using environment 19 | variables. 20 | 21 | Example: 22 | 23 | PGHOST=/var/run/postgresql go test github.com/lib/pq 24 | 25 | Optionally, a benchmark suite can be run as part of the tests: 26 | 27 | PGHOST=/var/run/postgresql go test -bench . 28 | 29 | ## Features 30 | 31 | * SSL 32 | * Handles bad connections for `database/sql` 33 | * Scan `time.Time` correctly (i.e. `timestamp[tz]`, `time[tz]`, `date`) 34 | * Scan binary blobs correctly (i.e. `bytea`) 35 | * Package for `hstore` support 36 | * COPY FROM support 37 | * pq.ParseURL for converting urls to connection strings for sql.Open. 38 | * Many libpq compatible environment variables 39 | * Unix socket support 40 | * Notifications: `LISTEN`/`NOTIFY` 41 | 42 | ## Future / Things you can help with 43 | 44 | * Better COPY FROM / COPY TO (see discussion in #181) 45 | 46 | ## Thank you (alphabetical) 47 | 48 | Some of these contributors are from the original library `bmizerany/pq.go` whose 49 | code still exists in here. 50 | 51 | * Andy Balholm (andybalholm) 52 | * Ben Berkert (benburkert) 53 | * Bill Mill (llimllib) 54 | * Bjørn Madsen (aeons) 55 | * Blake Gentry (bgentry) 56 | * Brad Fitzpatrick (bradfitz) 57 | * Chris Walsh (cwds) 58 | * Daniel Farina (fdr) 59 | * Everyone at The Go Team 60 | * Evan Shaw (edsrzf) 61 | * Ewan Chou (coocood) 62 | * Federico Romero (federomero) 63 | * Gary Burd (garyburd) 64 | * Heroku (heroku) 65 | * Jason McVetta (jmcvetta) 66 | * Jeremy Jay (pbnjay) 67 | * Joakim Sernbrant (serbaut) 68 | * John Gallagher (jgallagher) 69 | * Kamil Kisiel (kisielk) 70 | * Kelly Dunn (kellydunn) 71 | * Keith Rarick (kr) 72 | * Kir Shatrov (kirs) 73 | * Lann Martin (lann) 74 | * Maciek Sakrejda (deafbybeheading) 75 | * Marc Brinkmann (mbr) 76 | * Marko Tiikkaja (johto) 77 | * Matt Newberry (MattNewberry) 78 | * Matt Robenolt (mattrobenolt) 79 | * Martin Olsen (martinolsen) 80 | * Mike Lewis (mikelikespie) 81 | * Nicolas Patry (Narsil) 82 | * Oliver Tonnhofer (olt) 83 | * Patrick Hayes (phayes) 84 | * Paul Hammond (paulhammond) 85 | * Ryan Smith (ryandotsmith) 86 | * Samuel Stauffer (samuel) 87 | * Timothée Peignier (cyberdelia) 88 | * notedit (notedit) 89 | -------------------------------------------------------------------------------- /vendor/github.com/spf13/cobra/cobra.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013 Steve Francia . 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Commands similar to git, go tools and other modern CLI tools 15 | // inspired by go, go-Commander, gh and subcommand 16 | 17 | package cobra 18 | 19 | import ( 20 | "fmt" 21 | "io" 22 | "reflect" 23 | "strconv" 24 | "strings" 25 | "text/template" 26 | ) 27 | 28 | func Gt(a interface{}, b interface{}) bool { 29 | var left, right int64 30 | av := reflect.ValueOf(a) 31 | 32 | switch av.Kind() { 33 | case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: 34 | left = int64(av.Len()) 35 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 36 | left = av.Int() 37 | case reflect.String: 38 | left, _ = strconv.ParseInt(av.String(), 10, 64) 39 | } 40 | 41 | bv := reflect.ValueOf(b) 42 | 43 | switch bv.Kind() { 44 | case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: 45 | right = int64(bv.Len()) 46 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 47 | right = bv.Int() 48 | case reflect.String: 49 | right, _ = strconv.ParseInt(bv.String(), 10, 64) 50 | } 51 | 52 | return left > right 53 | } 54 | 55 | func Eq(a interface{}, b interface{}) bool { 56 | av := reflect.ValueOf(a) 57 | bv := reflect.ValueOf(b) 58 | 59 | switch av.Kind() { 60 | case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: 61 | panic("Eq called on unsupported type") 62 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 63 | return av.Int() == bv.Int() 64 | case reflect.String: 65 | return av.String() == bv.String() 66 | } 67 | return false 68 | } 69 | 70 | func rpad(s string, padding int) string { 71 | template := fmt.Sprintf("%%-%ds", padding) 72 | return fmt.Sprintf(template, s) 73 | } 74 | 75 | // tmpl executes the given template text on data, writing the result to w. 76 | func tmpl(w io.Writer, text string, data interface{}) error { 77 | t := template.New("top") 78 | t.Funcs(template.FuncMap{ 79 | "trim": strings.TrimSpace, 80 | "rpad": rpad, 81 | "gt": Gt, 82 | "eq": Eq, 83 | }) 84 | template.Must(t.Parse(text)) 85 | return t.Execute(w, data) 86 | } 87 | -------------------------------------------------------------------------------- /api/middleware.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "strconv" 8 | 9 | "github.com/opentracing/opentracing-go" 10 | 11 | "goji.io" 12 | "goji.io/middleware" 13 | "goji.io/pat" 14 | "golang.org/x/net/context" 15 | stack "gopkg.in/stack.v1" 16 | ) 17 | 18 | func Headers(next goji.Handler) goji.Handler { 19 | mw := func(ctx context.Context, w http.ResponseWriter, r *http.Request) { 20 | if r.URL.Path != "/mtg/cards/random" { 21 | w.Header().Set("Cache-Control", "public,max-age=3600") 22 | } 23 | w.Header().Set("Content-Type", "application/json; charset=utf-8") 24 | w.Header().Set("Access-Control-Allow-Origin", "*") 25 | w.Header().Set("Access-Control-Expose-Headers", "link,content-length") 26 | w.Header().Set("License", "The textual information presented through this API about Magic: The Gathering is copyrighted by Wizards of the Coast.") 27 | w.Header().Set("Disclaimer", "This API is not produced, endorsed, supported, or affiliated with Wizards of the Coast.") 28 | w.Header().Set("Pricing", "store.tcgplayer.com allows you to buy cards from any of our vendors, all at the same time, in a simple checkout experience. Shop, Compare & Save with TCGplayer.com!") 29 | w.Header().Set("Strict-Transport-Security", "max-age=86400") 30 | next.ServeHTTPC(ctx, w, r) 31 | } 32 | return goji.HandlerFunc(mw) 33 | } 34 | 35 | const timeFormat = "2006-01-02T15:04:05.999999999Z" 36 | 37 | type responseWriter struct { 38 | status int 39 | http.ResponseWriter 40 | } 41 | 42 | func (crw *responseWriter) WriteHeader(status int) { 43 | crw.status = status 44 | crw.ResponseWriter.WriteHeader(status) 45 | } 46 | 47 | func Tracing(next goji.Handler) goji.Handler { 48 | return goji.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) { 49 | name := "/not-found" 50 | pattern := middleware.Pattern(ctx) 51 | if ppattern, ok := pattern.(*pat.Pattern); ok { 52 | name = ppattern.String() 53 | } 54 | 55 | span, nctx := opentracing.StartSpanFromContext(ctx, name) 56 | defer span.Finish() 57 | 58 | sw := responseWriter{ResponseWriter: w} 59 | next.ServeHTTPC(nctx, &sw, r) 60 | 61 | span.SetTag("http/host", r.Host) 62 | span.SetTag("http/url", r.URL.String()) 63 | span.SetTag("http/response/size", w.Header().Get("Content-Length")) 64 | span.SetTag("http/method", r.Method) 65 | span.SetTag("http/status_code", strconv.Itoa(sw.status)) 66 | }) 67 | } 68 | 69 | func Recover(next goji.Handler) goji.Handler { 70 | return goji.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) { 71 | defer func() { 72 | if err := recover(); err != nil { 73 | log.Println(err, fmt.Sprintf("%+v", stack.Trace())) 74 | http.Error(w, "Internal server error", http.StatusInternalServerError) 75 | } 76 | }() 77 | next.ServeHTTPC(ctx, w, r) 78 | }) 79 | } 80 | -------------------------------------------------------------------------------- /vendor/code.google.com/p/go.net/html/const.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 html 6 | 7 | // Section 12.2.3.2 of the HTML5 specification says "The following elements 8 | // have varying levels of special parsing rules". 9 | // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#the-stack-of-open-elements 10 | var isSpecialElementMap = map[string]bool{ 11 | "address": true, 12 | "applet": true, 13 | "area": true, 14 | "article": true, 15 | "aside": true, 16 | "base": true, 17 | "basefont": true, 18 | "bgsound": true, 19 | "blockquote": true, 20 | "body": true, 21 | "br": true, 22 | "button": true, 23 | "caption": true, 24 | "center": true, 25 | "col": true, 26 | "colgroup": true, 27 | "command": true, 28 | "dd": true, 29 | "details": true, 30 | "dir": true, 31 | "div": true, 32 | "dl": true, 33 | "dt": true, 34 | "embed": true, 35 | "fieldset": true, 36 | "figcaption": true, 37 | "figure": true, 38 | "footer": true, 39 | "form": true, 40 | "frame": true, 41 | "frameset": true, 42 | "h1": true, 43 | "h2": true, 44 | "h3": true, 45 | "h4": true, 46 | "h5": true, 47 | "h6": true, 48 | "head": true, 49 | "header": true, 50 | "hgroup": true, 51 | "hr": true, 52 | "html": true, 53 | "iframe": true, 54 | "img": true, 55 | "input": true, 56 | "isindex": true, 57 | "li": true, 58 | "link": true, 59 | "listing": true, 60 | "marquee": true, 61 | "menu": true, 62 | "meta": true, 63 | "nav": true, 64 | "noembed": true, 65 | "noframes": true, 66 | "noscript": true, 67 | "object": true, 68 | "ol": true, 69 | "p": true, 70 | "param": true, 71 | "plaintext": true, 72 | "pre": true, 73 | "script": true, 74 | "section": true, 75 | "select": true, 76 | "style": true, 77 | "summary": true, 78 | "table": true, 79 | "tbody": true, 80 | "td": true, 81 | "textarea": true, 82 | "tfoot": true, 83 | "th": true, 84 | "thead": true, 85 | "title": true, 86 | "tr": true, 87 | "ul": true, 88 | "wbr": true, 89 | "xmp": true, 90 | } 91 | 92 | func isSpecialElement(element *Node) bool { 93 | switch element.Namespace { 94 | case "", "html": 95 | return isSpecialElementMap[element.Data] 96 | case "svg": 97 | return element.Data == "foreignObject" 98 | } 99 | return false 100 | } 101 | -------------------------------------------------------------------------------- /vendor/goji.io/README.md: -------------------------------------------------------------------------------- 1 | Goji 2 | ==== 3 | 4 | [![GoDoc](https://godoc.org/goji.io?status.svg)](https://godoc.org/goji.io) [![Build Status](https://travis-ci.org/goji/goji.svg?branch=master)](https://travis-ci.org/goji/goji) 5 | 6 | Goji is a HTTP request multiplexer, similar to [`net/http.ServeMux`][servemux]. 7 | It compares incoming requests to a list of registered [Patterns][pattern], and 8 | dispatches to the [Handler][handler] that corresponds to the first matching 9 | Pattern. Goji also supports [Middleware][middleware] (composable shared 10 | functionality applied to every request) and uses the de facto standard 11 | [`x/net/context`][context] to store request-scoped values. 12 | 13 | [servemux]: https://golang.org/pkg/net/http/#ServeMux 14 | [pattern]: https://godoc.org/goji.io#Pattern 15 | [handler]: https://godoc.org/goji.io#Handler 16 | [middleware]: https://godoc.org/goji.io#Mux.Use 17 | [context]: https://godoc.org/golang.org/x/net/context 18 | 19 | 20 | Quick Start 21 | ----------- 22 | 23 | ```go 24 | package main 25 | 26 | import ( 27 | "fmt" 28 | "net/http" 29 | 30 | "goji.io" 31 | "goji.io/pat" 32 | "golang.org/x/net/context" 33 | ) 34 | 35 | func hello(ctx context.Context, w http.ResponseWriter, r *http.Request) { 36 | name := pat.Param(ctx, "name") 37 | fmt.Fprintf(w, "Hello, %s!", name) 38 | } 39 | 40 | func main() { 41 | mux := goji.NewMux() 42 | mux.HandleFuncC(pat.Get("/hello/:name"), hello) 43 | 44 | http.ListenAndServe("localhost:8000", mux) 45 | } 46 | ``` 47 | 48 | Please refer to [Goji's GoDoc Documentation][godoc] for a full API reference. 49 | 50 | [godoc]: https://godoc.org/goji.io 51 | 52 | 53 | Stability 54 | --------- 55 | 56 | Goji's API is stable, and guarantees to never break compatibility with existing 57 | code (under similar rules to the Go project's [guidelines][compat]). Goji is 58 | suitable for use in production. 59 | 60 | [compat]: https://golang.org/doc/go1compat 61 | 62 | 63 | Community / Contributing 64 | ------------------------ 65 | 66 | Goji maintains a mailing list, [gojiberries][berries], where you should feel 67 | welcome to ask questions about the project (no matter how simple!), to announce 68 | projects or libraries built on top of Goji, or to talk about Goji more 69 | generally. Goji's author (Carl Jackson) also loves to hear from users directly 70 | at his personal email address, which is available on his GitHub profile page. 71 | 72 | Contributions to Goji are welcome, however please be advised that due to Goji's 73 | stability guarantees interface changes are unlikely to be accepted. 74 | 75 | All interactions in the Goji community will be held to the high standard of the 76 | broader Go community's [Code of Conduct][conduct]. 77 | 78 | [berries]: https://groups.google.com/forum/#!forum/gojiberries 79 | [conduct]: https://golang.org/conduct 80 | -------------------------------------------------------------------------------- /vendor/goji.io/mux.go: -------------------------------------------------------------------------------- 1 | package goji 2 | 3 | import ( 4 | "net/http" 5 | 6 | "goji.io/internal" 7 | "golang.org/x/net/context" 8 | ) 9 | 10 | /* 11 | Mux is a HTTP multiplexer / router similar to net/http.ServeMux. 12 | 13 | Muxes multiplex traffic between many Handlers by selecting the first applicable 14 | Pattern. They then call a common middleware stack, finally passing control to 15 | the selected Handler. See the documentation on the Handle function for more 16 | information about how routing is performed, the documentation on the Pattern 17 | type for more information about request matching, and the documentation for the 18 | Use method for more about middleware. 19 | 20 | Muxes cannot be configured concurrently from multiple goroutines, nor can they 21 | be configured concurrently with requests. 22 | */ 23 | type Mux struct { 24 | handler Handler 25 | middleware []func(Handler) Handler 26 | router router 27 | root bool 28 | } 29 | 30 | /* 31 | NewMux returns a new Mux with no configured middleware or routes. 32 | */ 33 | func NewMux() *Mux { 34 | m := SubMux() 35 | m.root = true 36 | return m 37 | } 38 | 39 | /* 40 | SubMux returns a new Mux with no configured middleware or routes, and which 41 | inherits routing information from the passed context. This is especially useful 42 | when using one Mux as a Handler registered to another "parent" Mux. 43 | 44 | For example, a common pattern is to organize applications in a way that mirrors 45 | the structure of its URLs: a photo-sharing site might have URLs that start with 46 | "/users/" and URLs that start with "/albums/", and might be organized using 47 | three Muxes: 48 | 49 | root := NewMux() 50 | users := SubMux() 51 | root.HandleC(pat.New("/users/*", users) 52 | albums := SubMux() 53 | root.HandleC(pat.New("/albums/*", albums) 54 | 55 | // e.g., GET /users/carl 56 | users.HandleC(pat.Get("/:name"), renderProfile) 57 | // e.g., POST /albums/ 58 | albums.HandleC(pat.Post("/"), newAlbum) 59 | */ 60 | func SubMux() *Mux { 61 | m := &Mux{} 62 | m.buildChain() 63 | return m 64 | } 65 | 66 | /* 67 | ServeHTTP implements net/http.Handler. It uses context.TODO as the root context 68 | in order to ease the conversion of non-context-aware Handlers to context-aware 69 | ones using static analysis. 70 | 71 | Users who know that their mux sits at the top of the request hierarchy should 72 | consider creating a small helper http.Handler that calls this Mux's ServeHTTPC 73 | function with context.Background. 74 | */ 75 | func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) { 76 | m.ServeHTTPC(context.TODO(), w, r) 77 | } 78 | 79 | /* 80 | ServeHTTPC implements Handler. 81 | */ 82 | func (m *Mux) ServeHTTPC(ctx context.Context, w http.ResponseWriter, r *http.Request) { 83 | if m.root { 84 | ctx = context.WithValue(ctx, internal.Path, r.URL.EscapedPath()) 85 | } 86 | ctx = m.router.route(ctx, r) 87 | m.handler.ServeHTTPC(ctx, w, r) 88 | } 89 | 90 | var _ http.Handler = &Mux{} 91 | var _ Handler = &Mux{} 92 | -------------------------------------------------------------------------------- /vendor/stackmachine.com/logtrace/span.go: -------------------------------------------------------------------------------- 1 | package logtrace 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "sync" 7 | "time" 8 | 9 | ot "github.com/opentracing/opentracing-go" 10 | ) 11 | 12 | type Span struct { 13 | sync.Mutex `json:"-"` 14 | 15 | // A probabilistically unique identifier for a [multi-span] trace. 16 | TraceID uint64 17 | 18 | // A probabilistically unique identifier for a span. 19 | SpanID uint64 20 | 21 | // The SpanID of this Context's parent, or 0 if there is no parent. 22 | ParentSpanID uint64 23 | 24 | // Whether the trace is sampled. 25 | Sampled bool `json:"-"` 26 | 27 | tracer ot.Tracer 28 | 29 | Operation string `json:"Op,omitempty"` 30 | 31 | // We store rather than so that only 32 | // one of the timestamps has global clock uncertainty issues. 33 | Start time.Time `json:"Start"` 34 | Duration time.Duration `json:"-"` 35 | Milliseconds int64 `json:"Duration"` 36 | 37 | // Essentially an extension mechanism. Can be used for many purposes, 38 | // not to be enumerated here. 39 | Tags ot.Tags `json:"Tags,omitempty"` 40 | 41 | // The span's "microlog". 42 | Logs []ot.LogData `json:"Logs,omitempty"` 43 | 44 | // The span's associated baggage. 45 | Baggage map[string]string `json:"Baggage,omitempty"` 46 | } 47 | 48 | func (s *Span) SetTag(key string, val interface{}) ot.Span { 49 | s.Lock() 50 | if s.Tags == nil { 51 | s.Tags = ot.Tags{} 52 | } 53 | s.Tags[key] = val 54 | s.Unlock() 55 | return s 56 | } 57 | 58 | func (s *Span) Finish() { 59 | s.FinishWithOptions(ot.FinishOptions{}) 60 | } 61 | 62 | func (s *Span) FinishWithOptions(opts ot.FinishOptions) { 63 | finishTime := opts.FinishTime 64 | if finishTime.IsZero() { 65 | finishTime = time.Now().UTC() 66 | } 67 | duration := finishTime.Sub(s.Start) 68 | 69 | s.Lock() 70 | if opts.BulkLogData != nil { 71 | s.Logs = append(s.Logs, opts.BulkLogData...) 72 | } 73 | s.Duration = duration 74 | s.Milliseconds = duration.Nanoseconds() / 1000 75 | 76 | if blob, err := json.Marshal(s); err == nil { 77 | log.Println(string(blob)) 78 | } 79 | 80 | s.Unlock() 81 | } 82 | 83 | func (s *Span) SetBaggageItem(key, val string) ot.Span { 84 | s.Lock() 85 | if s.Baggage == nil { 86 | s.Baggage = make(map[string]string) 87 | } 88 | s.Baggage[key] = val 89 | s.Unlock() 90 | return s 91 | } 92 | 93 | func (s *Span) BaggageItem(key string) string { 94 | s.Lock() 95 | val := s.Baggage[key] 96 | s.Unlock() 97 | return val 98 | } 99 | 100 | func (s *Span) LogEvent(event string) { 101 | s.Log(ot.LogData{Event: event}) 102 | } 103 | 104 | func (s *Span) LogEventWithPayload(event string, payload interface{}) { 105 | s.Log(ot.LogData{Event: event, Payload: payload}) 106 | } 107 | 108 | func (s *Span) Log(ld ot.LogData) { 109 | s.Lock() 110 | if ld.Timestamp.IsZero() { 111 | ld.Timestamp = time.Now().UTC() 112 | } 113 | s.Logs = append(s.Logs, ld) 114 | s.Unlock() 115 | } 116 | 117 | func (s *Span) SetOperationName(operationName string) ot.Span { 118 | s.Lock() 119 | s.Operation = operationName 120 | s.Unlock() 121 | return s 122 | } 123 | 124 | func (s *Span) Tracer() ot.Tracer { 125 | return s.tracer 126 | } 127 | -------------------------------------------------------------------------------- /api/soup.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "strings" 5 | 6 | "code.google.com/p/go.net/html" 7 | ) 8 | 9 | func Flatten(n *html.Node) string { 10 | text := "" 11 | if n.Type == html.TextNode { 12 | text += n.Data 13 | } 14 | 15 | for c := n.FirstChild; c != nil; c = c.NextSibling { 16 | text += Flatten(c) 17 | } 18 | 19 | return text 20 | } 21 | 22 | func Find(n *html.Node, selector string) (*html.Node, bool) { 23 | return query(n, strings.Split(selector, " ")) 24 | } 25 | 26 | func FindAll(n *html.Node, selector string) []*html.Node { 27 | return queryall(n, strings.Split(selector, " ")) 28 | } 29 | 30 | func Attr(n *html.Node, key string) string { 31 | for _, a := range n.Attr { 32 | if a.Key == key { 33 | return a.Val 34 | } 35 | } 36 | return "" 37 | } 38 | 39 | func SplitTrimSpace(source, pattern string) []string { 40 | result := []string{} 41 | 42 | for _, val := range strings.Split(strings.TrimSpace(source), pattern) { 43 | result = append(result, strings.TrimSpace(val)) 44 | } 45 | 46 | return result 47 | } 48 | 49 | func queryall(n *html.Node, selectors []string) []*html.Node { 50 | nodes := []*html.Node{} 51 | 52 | if len(selectors) == 0 { 53 | return nodes 54 | } 55 | 56 | match := false 57 | selector := selectors[0] 58 | 59 | if n.Type == html.ElementNode { 60 | if strings.HasPrefix(selector, ".") { 61 | rule := strings.Replace(selector, ".", "", 1) 62 | 63 | classes := strings.Split(Attr(n, "class"), " ") 64 | 65 | for _, class := range classes { 66 | if strings.TrimSpace(class) == rule { 67 | match = true 68 | } 69 | } 70 | } else if strings.HasPrefix(selector, "#") { 71 | match = ("#" + Attr(n, "id")) == selector 72 | } else { 73 | match = (n.Data == selector) 74 | } 75 | } 76 | 77 | if match { 78 | if len(selectors) > 1 { 79 | selectors = selectors[1:] 80 | } else { 81 | nodes = append(nodes, n) 82 | } 83 | } 84 | 85 | for c := n.FirstChild; c != nil; c = c.NextSibling { 86 | for _, node := range queryall(c, selectors) { 87 | nodes = append(nodes, node) 88 | } 89 | } 90 | 91 | return nodes 92 | } 93 | 94 | func query(n *html.Node, selectors []string) (*html.Node, bool) { 95 | if len(selectors) == 0 { 96 | return nil, false 97 | } 98 | 99 | match := false 100 | selector := selectors[0] 101 | 102 | if n.Type == html.ElementNode { 103 | // XXX An element can have multiple classes 104 | if strings.HasPrefix(selector, ".") { 105 | rule := strings.Replace(selector, ".", "", 1) 106 | 107 | classes := strings.Split(Attr(n, "class"), " ") 108 | 109 | for _, class := range classes { 110 | if strings.TrimSpace(class) == rule { 111 | match = true 112 | } 113 | } 114 | } else if strings.HasPrefix(selector, "#") { 115 | match = ("#" + Attr(n, "id")) == selector 116 | } else { 117 | match = (n.Data == selector) 118 | } 119 | } 120 | 121 | if match { 122 | if len(selectors) > 1 { 123 | selectors = selectors[1:] 124 | } else { 125 | return n, true 126 | } 127 | } 128 | 129 | for c := n.FirstChild; c != nil; c = c.NextSibling { 130 | node, found := query(c, selectors) 131 | if found { 132 | return node, found 133 | } 134 | } 135 | 136 | return nil, false 137 | } 138 | -------------------------------------------------------------------------------- /api/handler_test.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "net/url" 10 | "testing" 11 | 12 | "github.com/kyleconroy/deckbrew/brew" 13 | "github.com/kyleconroy/deckbrew/config" 14 | ) 15 | 16 | func TestLinkHeader(t *testing.T) { 17 | url, _ := url.Parse("/cards?foo=bar") 18 | header := LinkHeader("http://localhost:3000", url, 0) 19 | expected := "; rel=\"next\"" 20 | 21 | if header != expected { 22 | t.Errorf("Expected %s not %s", expected, header) 23 | } 24 | 25 | url, _ = url.Parse("/cards?foo=bar&page=1") 26 | header = LinkHeader("http://localhost:3000", url, 1) 27 | expected = "; rel=\"prev\", ; rel=\"next\"" 28 | 29 | if header != expected { 30 | t.Errorf("Expected %s not %s", expected, header) 31 | } 32 | 33 | } 34 | 35 | func TestSlug(t *testing.T) { 36 | name := "Æther Adept?.\"':," 37 | 38 | if Slug(name) != "æther-adept" { 39 | t.Errorf("%s != æther-adept", Slug(name)) 40 | } 41 | } 42 | 43 | func TestApi(t *testing.T) { 44 | cfg, err := config.FromEnv() 45 | if err != nil { 46 | t.Fatal(err) 47 | } 48 | 49 | reader, err := brew.NewReader(cfg) 50 | if err != nil { 51 | t.Fatal(err) 52 | } 53 | 54 | m := New(cfg, reader) 55 | 56 | ts := httptest.NewServer(m) 57 | defer ts.Close() 58 | 59 | urls := []string{ 60 | "/mtg/cards", 61 | "/mtg/cards?type=creature", 62 | "/mtg/cards?subtype=zombie", 63 | "/mtg/cards?supertype=legendary", 64 | "/mtg/cards?color=red", 65 | "/mtg/cards?name=rats", 66 | "/mtg/cards?set=UNH", 67 | "/mtg/cards?rarity=mythic", 68 | "/mtg/cards?rarity=basic", 69 | "/mtg/cards?oracle=you+win+the+game", 70 | "/mtg/cards/time-vault", 71 | "/mtg/cards/typeahead?q=nessian", 72 | "/mtg/sets", 73 | "/mtg/sets/UNH", 74 | "/mtg/colors", 75 | "/mtg/types", 76 | "/mtg/supertypes", 77 | "/mtg/subtypes", 78 | } 79 | 80 | for _, url := range urls { 81 | res, err := http.Get(ts.URL + url) 82 | res.Body.Close() 83 | 84 | if err != nil { 85 | t.Error(err) 86 | } 87 | 88 | if res.StatusCode != 200 { 89 | t.Errorf("Expected %s to return 200, not %d", url, res.StatusCode) 90 | } 91 | } 92 | 93 | // Test Random 94 | res, err := http.Get(ts.URL + "/mtg/cards/random") 95 | res.Body.Close() 96 | if err != nil { 97 | t.Error(err) 98 | } 99 | 100 | if res.Request.URL.String() == "/mtg/cards/random" { 101 | t.Errorf("Expected /mtg/cards/random redirect to a new page") 102 | } 103 | 104 | loadFirstCard := func(u string) (brew.Card, error) { 105 | var card brew.Card 106 | 107 | res, err := http.Get(ts.URL + u) 108 | defer res.Body.Close() 109 | 110 | if err != nil { 111 | return card, err 112 | } 113 | 114 | if res.StatusCode != 200 { 115 | return card, fmt.Errorf("Expected %s to return 200, not %d", u, res.StatusCode) 116 | } 117 | 118 | blob, err := ioutil.ReadAll(res.Body) 119 | 120 | if err != nil { 121 | return card, err 122 | } 123 | 124 | var cards []brew.Card 125 | 126 | err = json.Unmarshal(blob, &cards) 127 | 128 | if err != nil { 129 | return card, err 130 | } 131 | 132 | return cards[0], nil 133 | } 134 | 135 | // Test Paging 136 | pageone, _ := loadFirstCard("/mtg/cards?page=1") 137 | pagetwo, _ := loadFirstCard("/mtg/cards?page=2") 138 | 139 | if pageone.Id == pagetwo.Id { 140 | t.Errorf("Page one and two both have the same first card, %s", pageone.Id) 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /vendor/code.google.com/p/go.net/html/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package html implements an HTML5-compliant tokenizer and parser. 7 | 8 | Tokenization is done by creating a Tokenizer for an io.Reader r. It is the 9 | caller's responsibility to ensure that r provides UTF-8 encoded HTML. 10 | 11 | z := html.NewTokenizer(r) 12 | 13 | Given a Tokenizer z, the HTML is tokenized by repeatedly calling z.Next(), 14 | which parses the next token and returns its type, or an error: 15 | 16 | for { 17 | tt := z.Next() 18 | if tt == html.ErrorToken { 19 | // ... 20 | return ... 21 | } 22 | // Process the current token. 23 | } 24 | 25 | There are two APIs for retrieving the current token. The high-level API is to 26 | call Token; the low-level API is to call Text or TagName / TagAttr. Both APIs 27 | allow optionally calling Raw after Next but before Token, Text, TagName, or 28 | TagAttr. In EBNF notation, the valid call sequence per token is: 29 | 30 | Next {Raw} [ Token | Text | TagName {TagAttr} ] 31 | 32 | Token returns an independent data structure that completely describes a token. 33 | Entities (such as "<") are unescaped, tag names and attribute keys are 34 | lower-cased, and attributes are collected into a []Attribute. For example: 35 | 36 | for { 37 | if z.Next() == html.ErrorToken { 38 | // Returning io.EOF indicates success. 39 | return z.Err() 40 | } 41 | emitToken(z.Token()) 42 | } 43 | 44 | The low-level API performs fewer allocations and copies, but the contents of 45 | the []byte values returned by Text, TagName and TagAttr may change on the next 46 | call to Next. For example, to extract an HTML page's anchor text: 47 | 48 | depth := 0 49 | for { 50 | tt := z.Next() 51 | switch tt { 52 | case ErrorToken: 53 | return z.Err() 54 | case TextToken: 55 | if depth > 0 { 56 | // emitBytes should copy the []byte it receives, 57 | // if it doesn't process it immediately. 58 | emitBytes(z.Text()) 59 | } 60 | case StartTagToken, EndTagToken: 61 | tn, _ := z.TagName() 62 | if len(tn) == 1 && tn[0] == 'a' { 63 | if tt == StartTagToken { 64 | depth++ 65 | } else { 66 | depth-- 67 | } 68 | } 69 | } 70 | } 71 | 72 | Parsing is done by calling Parse with an io.Reader, which returns the root of 73 | the parse tree (the document element) as a *Node. It is the caller's 74 | responsibility to ensure that the Reader provides UTF-8 encoded HTML. For 75 | example, to process each anchor node in depth-first order: 76 | 77 | doc, err := html.Parse(r) 78 | if err != nil { 79 | // ... 80 | } 81 | var f func(*html.Node) 82 | f = func(n *html.Node) { 83 | if n.Type == html.ElementNode && n.Data == "a" { 84 | // Do something with n... 85 | } 86 | for c := n.FirstChild; c != nil; c = c.NextSibling { 87 | f(c) 88 | } 89 | } 90 | f(doc) 91 | 92 | The relevant specifications include: 93 | http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html and 94 | http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html 95 | */ 96 | package html 97 | 98 | // The tokenization algorithm implemented by this package is not a line-by-line 99 | // transliteration of the relatively verbose state-machine in the WHATWG 100 | // specification. A more direct approach is used instead, where the program 101 | // counter implies the state, such as whether it is tokenizing a tag or a text 102 | // node. Specification compliance is verified by checking expected and actual 103 | // outputs over a test suite rather than aiming for algorithmic fidelity. 104 | 105 | // TODO(nigeltao): Does a DOM API belong in this package or a separate one? 106 | // TODO(nigeltao): How does parsing interact with a JavaScript engine? 107 | -------------------------------------------------------------------------------- /vendor/goji.io/goji.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package goji is a minimalistic and flexible HTTP request multiplexer. 3 | 4 | Goji itself has very few features: it is first and foremost a standard set of 5 | interfaces for writing web applications. Several subpackages are distributed 6 | with Goji to provide standard production-ready implementations of several of the 7 | interfaces, however users are also encouraged to implement the interfaces on 8 | their own, especially if their needs are unusual. 9 | */ 10 | package goji 11 | 12 | import ( 13 | "net/http" 14 | 15 | "golang.org/x/net/context" 16 | ) 17 | 18 | /* 19 | Pattern determines whether a given request matches some criteria. Goji users 20 | looking for a concrete type that implements this interface should consider 21 | Goji's "pat" sub-package, which implements a small domain specific language for 22 | HTTP routing. 23 | 24 | Patterns typically only examine a small portion of incoming requests, most 25 | commonly the HTTP method and the URL's RawPath. As an optimization, Goji can 26 | elide calls to your Pattern for requests it knows cannot match. Pattern authors 27 | who wish to take advantage of this functionality (and in some cases an 28 | asymptotic performance improvement) can augment their Pattern implementations 29 | with any of the following methods: 30 | 31 | // HTTPMethods returns a set of HTTP methods that this Pattern matches, 32 | // or nil if it's not possible to determine which HTTP methods might be 33 | // matched. Put another way, requests with HTTP methods not in the 34 | // returned set are guaranteed to never match this Pattern. 35 | HTTPMethods() map[string]struct{} 36 | 37 | // PathPrefix returns a string which all RawPaths that match this 38 | // Pattern must have as a prefix. Put another way, requests with 39 | // RawPaths that do not contain the returned string as a prefix are 40 | // guaranteed to never match this Pattern. 41 | PathPrefix() string 42 | 43 | The presence or lack of these performance improvements should be viewed as an 44 | implementation detail and are not part of Goji's API compatibility guarantee. It 45 | is the responsibility of Pattern authors to ensure that their Match function 46 | always returns correct results, even if these optimizations are not performed. 47 | 48 | All operations on Patterns must be safe for concurrent use by multiple 49 | goroutines. 50 | */ 51 | type Pattern interface { 52 | // Match examines the request and request context to determine if the 53 | // request is a match. If so, it returns a non-nil context.Context 54 | // (likely one derived from the input Context, and perhaps simply the 55 | // input Context unchanged). The returned context may be used to store 56 | // request-scoped data, such as variables extracted from the Request. 57 | // 58 | // Match must not mutate the passed request. 59 | Match(context.Context, *http.Request) context.Context 60 | } 61 | 62 | /* 63 | Handler is a context-aware variant of net/http.Handler. It has the same 64 | semantics as http.Handler, except that a request-scoped context is additionally 65 | passed to the handler function. 66 | */ 67 | type Handler interface { 68 | ServeHTTPC(context.Context, http.ResponseWriter, *http.Request) 69 | } 70 | 71 | /* 72 | HandlerFunc is a context-aware variant of net/http.HandlerFunc. It has the same 73 | semantics as http.HandlerFunc, except that a request-scoped context is 74 | additionally passed to the function. 75 | 76 | HandlerFunc implements both the Handler and http.Handler interfaces. 77 | */ 78 | type HandlerFunc func(context.Context, http.ResponseWriter, *http.Request) 79 | 80 | /* 81 | ServeHTTP implements net/http.Handler. It calls the underlying function with a 82 | context of context.TODO in order to ease the conversion of non-context-aware 83 | Handlers to context-aware ones using static analysis. 84 | */ 85 | func (h HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) { 86 | h(context.TODO(), w, r) 87 | } 88 | 89 | /* 90 | ServeHTTPC implements Handler. 91 | */ 92 | func (h HandlerFunc) ServeHTTPC(ctx context.Context, w http.ResponseWriter, r *http.Request) { 93 | h(ctx, w, r) 94 | } 95 | -------------------------------------------------------------------------------- /vendor/stackmachine.com/logtrace/tracer.go: -------------------------------------------------------------------------------- 1 | package logtrace 2 | 3 | import ( 4 | "math/rand" 5 | "strconv" 6 | "strings" 7 | "sync" 8 | "time" 9 | 10 | ot "github.com/opentracing/opentracing-go" 11 | ) 12 | 13 | var ( 14 | seededIDGen = rand.New(rand.NewSource(time.Now().UnixNano())) 15 | // The golang rand generators are *not* intrinsically thread-safe. 16 | seededIDLock sync.Mutex 17 | ) 18 | 19 | func randomID() uint64 { 20 | seededIDLock.Lock() 21 | id := uint64(seededIDGen.Int63()) 22 | seededIDLock.Unlock() 23 | return id 24 | } 25 | 26 | type Tracer struct { 27 | } 28 | 29 | func (t *Tracer) StartSpan(operationName string) ot.Span { 30 | return t.StartSpanWithOptions( 31 | ot.StartSpanOptions{ 32 | OperationName: operationName, 33 | }) 34 | } 35 | 36 | func (t *Tracer) StartSpanWithOptions(opts ot.StartSpanOptions) ot.Span { 37 | startTime := opts.StartTime 38 | if startTime.IsZero() { 39 | startTime = time.Now().UTC() 40 | } 41 | 42 | // Tags. 43 | tags := opts.Tags 44 | 45 | // Build the new span. This is the only allocation: We'll return this as 46 | // a opentracing.Span. 47 | sp := Span{} 48 | if opts.Parent == nil { 49 | sp.TraceID = randomID() 50 | sp.SpanID = randomID() 51 | } else { 52 | pr := opts.Parent.(*Span) 53 | sp.TraceID = pr.TraceID 54 | sp.SpanID = randomID() 55 | sp.ParentSpanID = pr.SpanID 56 | 57 | pr.Lock() 58 | if l := len(pr.Baggage); l > 0 { 59 | sp.Baggage = make(map[string]string, len(pr.Baggage)) 60 | for k, v := range pr.Baggage { 61 | sp.Baggage[k] = v 62 | } 63 | } 64 | pr.Unlock() 65 | } 66 | 67 | sp.Operation = opts.OperationName 68 | sp.Start = startTime 69 | sp.Tags = tags 70 | return &sp 71 | } 72 | 73 | const ( 74 | prefixTracerState = "logtrace-" 75 | prefixBaggage = "logtrace-baggage-" 76 | tracerStateFieldCount = 3 77 | fieldNameTraceID = prefixTracerState + "traceid" 78 | fieldNameSpanID = prefixTracerState + "spanid" 79 | ) 80 | 81 | func (t *Tracer) Inject(sp ot.Span, format interface{}, carrier interface{}) error { 82 | switch format { 83 | case ot.TextMap: 84 | sc, ok := sp.(*Span) 85 | if !ok { 86 | return ot.ErrInvalidSpan 87 | } 88 | tmw, ok := carrier.(ot.TextMapWriter) 89 | if !ok { 90 | return ot.ErrInvalidCarrier 91 | } 92 | tmw.Set(fieldNameTraceID, strconv.FormatUint(sc.TraceID, 16)) 93 | tmw.Set(fieldNameSpanID, strconv.FormatUint(sc.SpanID, 16)) 94 | sc.Lock() 95 | for k, v := range sc.Baggage { 96 | tmw.Set(prefixBaggage+k, v) 97 | } 98 | sc.Unlock() 99 | return nil 100 | } 101 | return ot.ErrUnsupportedFormat 102 | } 103 | 104 | func (t *Tracer) Join(operationName string, format interface{}, carrier interface{}) (ot.Span, error) { 105 | switch format { 106 | case ot.TextMap: 107 | default: 108 | return nil, ot.ErrUnsupportedFormat 109 | } 110 | 111 | tmr, ok := carrier.(ot.TextMapReader) 112 | if !ok { 113 | return nil, ot.ErrInvalidCarrier 114 | } 115 | var traceID, propagatedSpanID uint64 116 | var err error 117 | decodedBaggage := make(map[string]string) 118 | err = tmr.ForeachKey(func(k, v string) error { 119 | switch strings.ToLower(k) { 120 | case fieldNameTraceID: 121 | traceID, err = strconv.ParseUint(v, 16, 64) 122 | if err != nil { 123 | return ot.ErrTraceCorrupted 124 | } 125 | case fieldNameSpanID: 126 | propagatedSpanID, err = strconv.ParseUint(v, 16, 64) 127 | if err != nil { 128 | return ot.ErrTraceCorrupted 129 | } 130 | default: 131 | lowercaseK := strings.ToLower(k) 132 | if strings.HasPrefix(lowercaseK, prefixBaggage) { 133 | decodedBaggage[strings.TrimPrefix(lowercaseK, prefixBaggage)] = v 134 | } 135 | } 136 | return nil 137 | }) 138 | if err != nil { 139 | return nil, err 140 | } 141 | return t.StartSpanWithOptions(ot.StartSpanOptions{ 142 | Parent: &Span{ 143 | TraceID: traceID, 144 | SpanID: propagatedSpanID, 145 | Baggage: decodedBaggage, 146 | }, 147 | OperationName: operationName, 148 | }), nil 149 | } 150 | -------------------------------------------------------------------------------- /vendor/goji.io/middleware.go: -------------------------------------------------------------------------------- 1 | package goji 2 | 3 | import ( 4 | "net/http" 5 | 6 | "golang.org/x/net/context" 7 | ) 8 | 9 | /* 10 | Use appends a middleware to the Mux's middleware stack. 11 | 12 | Middleware are composable pieces of functionality that augment Handlers. Common 13 | examples of middleware include request loggers, authentication checkers, and 14 | metrics gatherers. 15 | 16 | Middleware are evaluated in the reverse order in which they were added, but the 17 | resulting Handlers execute in "normal" order (i.e., the Handler returned by the 18 | first Middleware to be added gets called first). 19 | 20 | For instance, given middleware A, B, and C, added in that order, Goji will 21 | behave similarly to this snippet: 22 | 23 | augmentedHandler := A(B(C(yourHandler))) 24 | augmentedHandler.ServeHTTPC(ctx, w, r) 25 | 26 | Assuming each of A, B, and C look something like this: 27 | 28 | func A(inner goji.Handler) goji.Handler { 29 | log.Print("A: called") 30 | mw := func(ctx context.Context, w http.ResponseWriter, r *http.Request) { 31 | log.Print("A: before") 32 | inner.ServeHTTPC(ctx, w, r) 33 | log.Print("A: after") 34 | } 35 | return goji.HandlerFunc(mw) 36 | } 37 | 38 | we'd expect to see the following in the log: 39 | 40 | C: called 41 | B: called 42 | A: called 43 | --- 44 | A: before 45 | B: before 46 | C: before 47 | yourHandler: called 48 | C: after 49 | B: after 50 | A: after 51 | 52 | Note that augmentedHandler may be called many times. Put another way, you will 53 | see many invocations of the portion of the log below the divider, and perhaps 54 | only see the portion above the divider a single time. Also note that as an 55 | implementation detail, net/http-style middleware will be called once per 56 | request, even though the Goji-style middleware around them might only ever be 57 | called a single time. 58 | 59 | Middleware in Goji is called after routing has been performed. Therefore it is 60 | possible to examine any routing information placed into the context by Patterns, 61 | or to view or modify the Handler that will be routed to. Middleware authors 62 | should read the documentation for the "middleware" subpackage for more 63 | information about how this is done. 64 | 65 | The http.Handler returned by the given middleware must be safe for concurrent 66 | use by multiple goroutines. It is not safe to concurrently register middleware 67 | from multiple goroutines, or to register middleware concurrently with requests. 68 | */ 69 | func (m *Mux) Use(middleware func(http.Handler) http.Handler) { 70 | m.middleware = append(m.middleware, func(h Handler) Handler { 71 | return outerBridge{middleware, h} 72 | }) 73 | m.buildChain() 74 | } 75 | 76 | /* 77 | UseC appends a context-aware middleware to the Mux's middleware stack. See the 78 | documentation for Use for more information about the semantics of middleware. 79 | 80 | The Handler returned by the given middleware must be safe for concurrent use by 81 | multiple goroutines. It is not safe to concurrently register middleware from 82 | multiple goroutines, or to register middleware concurrently with requests. 83 | */ 84 | func (m *Mux) UseC(middleware func(Handler) Handler) { 85 | m.middleware = append(m.middleware, middleware) 86 | m.buildChain() 87 | } 88 | 89 | // Pre-compile a Handler for us to use during dispatch. Yes, this means that 90 | // adding middleware is quadratic, but it (a) happens during configuration time, 91 | // not at "runtime", and (b) n should ~always be small. 92 | func (m *Mux) buildChain() { 93 | m.handler = dispatch{} 94 | for i := len(m.middleware) - 1; i >= 0; i-- { 95 | m.handler = m.middleware[i](m.handler) 96 | } 97 | } 98 | 99 | type innerBridge struct { 100 | inner Handler 101 | ctx context.Context 102 | } 103 | 104 | func (b innerBridge) ServeHTTP(w http.ResponseWriter, r *http.Request) { 105 | b.inner.ServeHTTPC(b.ctx, w, r) 106 | } 107 | 108 | type outerBridge struct { 109 | mware func(http.Handler) http.Handler 110 | inner Handler 111 | } 112 | 113 | func (b outerBridge) ServeHTTPC(ctx context.Context, w http.ResponseWriter, r *http.Request) { 114 | b.mware(innerBridge{b.inner, ctx}).ServeHTTP(w, r) 115 | } 116 | -------------------------------------------------------------------------------- /vendor/goji.io/router_trie.go: -------------------------------------------------------------------------------- 1 | // +build !goji_router_simple 2 | 3 | package goji 4 | 5 | import ( 6 | "net/http" 7 | "sort" 8 | "strings" 9 | 10 | "goji.io/internal" 11 | "golang.org/x/net/context" 12 | ) 13 | 14 | type router struct { 15 | routes []route 16 | methods map[string]*trieNode 17 | wildcard trieNode 18 | } 19 | 20 | type route struct { 21 | Pattern 22 | Handler 23 | } 24 | 25 | type child struct { 26 | prefix string 27 | node *trieNode 28 | } 29 | 30 | type trieNode struct { 31 | routes []int 32 | children []child 33 | } 34 | 35 | func (rt *router) add(p Pattern, h Handler) { 36 | i := len(rt.routes) 37 | rt.routes = append(rt.routes, route{p, h}) 38 | 39 | var prefix string 40 | if pp, ok := p.(pathPrefix); ok { 41 | prefix = pp.PathPrefix() 42 | } 43 | 44 | var methods map[string]struct{} 45 | if hm, ok := p.(httpMethods); ok { 46 | methods = hm.HTTPMethods() 47 | } 48 | if methods == nil { 49 | rt.wildcard.add(prefix, i) 50 | for _, sub := range rt.methods { 51 | sub.add(prefix, i) 52 | } 53 | } else { 54 | if rt.methods == nil { 55 | rt.methods = make(map[string]*trieNode) 56 | } 57 | 58 | for method := range methods { 59 | if _, ok := rt.methods[method]; !ok { 60 | rt.methods[method] = rt.wildcard.clone() 61 | } 62 | rt.methods[method].add(prefix, i) 63 | } 64 | } 65 | } 66 | 67 | func (rt *router) route(ctx context.Context, r *http.Request) context.Context { 68 | tn := &rt.wildcard 69 | if tn2, ok := rt.methods[r.Method]; ok { 70 | tn = tn2 71 | } 72 | 73 | path := ctx.Value(internal.Path).(string) 74 | 75 | var routes []int 76 | for { 77 | routes = append(routes, tn.routes...) 78 | 79 | i := sort.Search(len(tn.children), func(i int) bool { 80 | p := tn.children[i].prefix 81 | return path < p || strings.HasPrefix(path, p) 82 | }) 83 | if i == len(tn.children) || !strings.HasPrefix(path, tn.children[i].prefix) { 84 | break 85 | } 86 | 87 | path = path[len(tn.children[i].prefix):] 88 | tn = tn.children[i].node 89 | } 90 | sort.Ints(routes) 91 | for _, i := range routes { 92 | if ctx := rt.routes[i].Match(ctx, r); ctx != nil { 93 | return &match{ctx, rt.routes[i].Pattern, rt.routes[i].Handler} 94 | } 95 | } 96 | return &match{Context: ctx} 97 | } 98 | 99 | // We can be a teensy bit more efficient here: we're maintaining a sorted list, 100 | // so we know exactly where to insert the new element. But since that involves 101 | // more bookkeeping and makes the code messier, let's cross that bridge when we 102 | // come to it. 103 | type byPrefix []child 104 | 105 | func (b byPrefix) Len() int { 106 | return len(b) 107 | } 108 | func (b byPrefix) Less(i, j int) bool { 109 | return b[i].prefix < b[j].prefix 110 | } 111 | func (b byPrefix) Swap(i, j int) { 112 | b[i], b[j] = b[j], b[i] 113 | } 114 | 115 | func longestPrefix(a, b string) string { 116 | mlen := len(a) 117 | if len(b) < mlen { 118 | mlen = len(b) 119 | } 120 | for i := 0; i < mlen; i++ { 121 | if a[i] != b[i] { 122 | return a[:i] 123 | } 124 | } 125 | return a[:mlen] 126 | } 127 | 128 | func (tn *trieNode) add(prefix string, idx int) { 129 | if len(prefix) == 0 { 130 | tn.routes = append(tn.routes, idx) 131 | return 132 | } 133 | 134 | ch := prefix[0] 135 | i := sort.Search(len(tn.children), func(i int) bool { 136 | return ch <= tn.children[i].prefix[0] 137 | }) 138 | 139 | for ; i < len(tn.children); i++ { 140 | if tn.children[i].prefix[0] > ch { 141 | break 142 | } 143 | 144 | lp := longestPrefix(prefix, tn.children[i].prefix) 145 | 146 | if tn.children[i].prefix == lp { 147 | tn.children[i].node.add(prefix[len(lp):], idx) 148 | return 149 | } 150 | 151 | split := new(trieNode) 152 | split.children = []child{ 153 | {tn.children[i].prefix[len(lp):], tn.children[i].node}, 154 | } 155 | split.add(prefix[len(lp):], idx) 156 | 157 | tn.children[i].prefix = lp 158 | tn.children[i].node = split 159 | sort.Sort(byPrefix(tn.children)) 160 | return 161 | } 162 | 163 | tn.children = append(tn.children, child{ 164 | prefix: prefix, 165 | node: &trieNode{routes: []int{idx}}, 166 | }) 167 | sort.Sort(byPrefix(tn.children)) 168 | } 169 | 170 | func (tn *trieNode) clone() *trieNode { 171 | clone := new(trieNode) 172 | clone.routes = append(clone.routes, tn.routes...) 173 | clone.children = append(clone.children, tn.children...) 174 | for i := range clone.children { 175 | clone.children[i].node = tn.children[i].node.clone() 176 | } 177 | return clone 178 | } 179 | -------------------------------------------------------------------------------- /vendor/github.com/opentracing/opentracing-go/README.md: -------------------------------------------------------------------------------- 1 | [![Gitter chat](http://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg)](https://gitter.im/opentracing/public) [![Build Status](https://travis-ci.org/opentracing/opentracing-go.svg?branch=master)](https://travis-ci.org/opentracing/opentracing-go) [![GoDoc](https://godoc.org/github.com/opentracing/opentracing-go?status.svg)](http://godoc.org/github.com/opentracing/opentracing-go) 2 | 3 | # OpenTracing API for Go 4 | 5 | This package is a Go platform API for OpenTracing. 6 | 7 | ## Required Reading 8 | 9 | In order to understand the Go platform API, one must first be familiar with the 10 | [OpenTracing project](http://opentracing.io) and 11 | [terminology](http://opentracing.io/spec/) more generally. 12 | 13 | ## API overview for those adding instrumentation 14 | 15 | Everyday consumers of this `opentracing` package really only need to worry 16 | about a couple of key abstractions: the `StartSpan` function, the `Span` 17 | interface, and binding a `Tracer` at `main()`-time. Here are code snippets 18 | demonstrating some important use cases. 19 | 20 | #### Singleton initialization 21 | 22 | The simplest starting point is `./default_tracer.go`. As early as possible, call 23 | 24 | ```go 25 | import "github.com/opentracing/opentracing-go" 26 | import ".../some_tracing_impl" 27 | 28 | func main() { 29 | tracerImpl := some_tracing_impl.New(...) // tracing impl specific 30 | opentracing.InitGlobalTracer(tracerImpl) 31 | ... 32 | } 33 | ``` 34 | 35 | ##### Non-Singleton initialization 36 | 37 | If you prefer direct control to singletons, manage ownership of the 38 | `opentracing.Tracer` implementation explicitly. 39 | 40 | #### Creating a Span given an existing Golang `context.Context` 41 | 42 | If you use `context.Context` in your application, OpenTracing's Go library will 43 | happily use it for `Span` propagation. To start a new (child) `Span`, you can use 44 | `StartSpanFromContext`. 45 | 46 | ```go 47 | func xyz(ctx context.Context, ...) { 48 | ... 49 | ctx, span := opentracing.StartSpanFromContext(ctx, "operation_name") 50 | defer span.Finish() 51 | span.LogEvent("xyz_called") 52 | ... 53 | } 54 | ``` 55 | 56 | #### Starting an empty trace by creating a "root span" 57 | 58 | It's always possible to create a "root" (parentless) `Span`. 59 | 60 | ```go 61 | func xyz() { 62 | ... 63 | sp := opentracing.StartSpan("operation_name") 64 | defer sp.Finish() 65 | sp.LogEvent("xyz_called") 66 | ... 67 | } 68 | ``` 69 | 70 | #### Creating a (child) Span given an existing (parent) Span 71 | 72 | ```go 73 | func xyz(parentSpan opentracing.Span, ...) { 74 | ... 75 | sp := opentracing.StartChildSpan(parentSpan, "operation_name") 76 | defer sp.Finish() 77 | sp.LogEvent("xyz_called") 78 | ... 79 | } 80 | ``` 81 | 82 | #### Serializing to the wire 83 | 84 | ```go 85 | func makeSomeRequest(ctx context.Context) ... { 86 | if span := opentracing.SpanFromContext(ctx); span != nil { 87 | httpClient := &http.Client{} 88 | httpReq, _ := http.NewRequest("GET", "http://myservice/", nil) 89 | 90 | // Transmit the span's TraceContext as HTTP headers on our 91 | // outbound request. 92 | tracer.Inject( 93 | span, 94 | opentracing.TextMap, 95 | opentracing.HTTPHeaderTextMapCarrier(httpReq.Header)) 96 | 97 | resp, err := httpClient.Do(httpReq) 98 | ... 99 | } 100 | ... 101 | } 102 | ``` 103 | 104 | #### Deserializing from the wire 105 | 106 | ```go 107 | http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { 108 | serverSpan, err := opentracing.GlobalTracer().Join( 109 | "serverSpan", 110 | opentracing.TextMap, 111 | opentracing.HTTPHeaderTextMapCarrier(req.Header)) 112 | 113 | if err != nil { 114 | // Create a root span if necessary 115 | serverSpan = opentracing.StartTrace("serverSpan") 116 | } 117 | var goCtx context.Context = ... 118 | goCtx, _ = opentracing.ContextWithSpan(goCtx, serverSpan) 119 | defer serverSpan.Finish() 120 | ... 121 | } 122 | ``` 123 | 124 | #### Goroutine-safety 125 | 126 | The entire public API is goroutine-safe and does not require external 127 | synchronization. 128 | 129 | ## API pointers for those implementing a tracing system 130 | 131 | Tracing system implementors may be able to reuse or copy-paste-modify the `basictracer` package, found [here](https://github.com/opentracing/basictracer-go). In particular, see `basictracer.New(...)`. 132 | 133 | ## API compatibility 134 | 135 | For the time being, "mild" backwards-incompatible changes may be made without changing the major version number. As OpenTracing and `opentracing-go` mature, backwards compatibility will become more of a priority. 136 | -------------------------------------------------------------------------------- /vendor/github.com/spf13/pflag/README.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | pflag is a drop-in replacement for Go's flag package, implementing 4 | POSIX/GNU-style --flags. 5 | 6 | pflag is compatible with the [GNU extensions to the POSIX recommendations 7 | for command-line options][1]. For a more precise description, see the 8 | "Command-line flag syntax" section below. 9 | 10 | [1]: http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html 11 | 12 | pflag is available under the same style of BSD license as the Go language, 13 | which can be found in the LICENSE file. 14 | 15 | ## Installation 16 | 17 | pflag is available using the standard `go get` command. 18 | 19 | Install by running: 20 | 21 | go get github.com/ogier/pflag 22 | 23 | Run tests by running: 24 | 25 | go test github.com/ogier/pflag 26 | 27 | ## Usage 28 | 29 | pflag is a drop-in replacement of Go's native flag package. If you import 30 | pflag under the name "flag" then all code should continue to function 31 | with no changes. 32 | 33 | ``` go 34 | import flag "github.com/ogier/pflag" 35 | ``` 36 | 37 | There is one exception to this: if you directly instantiate the Flag struct 38 | there is one more field "Shorthand" that you will need to set. 39 | Most code never instantiates this struct directly, and instead uses 40 | functions such as String(), BoolVar(), and Var(), and is therefore 41 | unaffected. 42 | 43 | Define flags using flag.String(), Bool(), Int(), etc. 44 | 45 | This declares an integer flag, -flagname, stored in the pointer ip, with type *int. 46 | 47 | ``` go 48 | var ip *int = flag.Int("flagname", 1234, "help message for flagname") 49 | ``` 50 | 51 | If you like, you can bind the flag to a variable using the Var() functions. 52 | 53 | ``` go 54 | var flagvar int 55 | func init() { 56 | flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname") 57 | } 58 | ``` 59 | 60 | Or you can create custom flags that satisfy the Value interface (with 61 | pointer receivers) and couple them to flag parsing by 62 | 63 | ``` go 64 | flag.Var(&flagVal, "name", "help message for flagname") 65 | ``` 66 | 67 | For such flags, the default value is just the initial value of the variable. 68 | 69 | After all flags are defined, call 70 | 71 | ``` go 72 | flag.Parse() 73 | ``` 74 | 75 | to parse the command line into the defined flags. 76 | 77 | Flags may then be used directly. If you're using the flags themselves, 78 | they are all pointers; if you bind to variables, they're values. 79 | 80 | ``` go 81 | fmt.Println("ip has value ", *ip) 82 | fmt.Println("flagvar has value ", flagvar) 83 | ``` 84 | 85 | After parsing, the arguments after the flag are available as the 86 | slice flag.Args() or individually as flag.Arg(i). 87 | The arguments are indexed from 0 through flag.NArg()-1. 88 | 89 | The pflag package also defines some new functions that are not in flag, 90 | that give one-letter shorthands for flags. You can use these by appending 91 | 'P' to the name of any function that defines a flag. 92 | 93 | ``` go 94 | var ip = flag.IntP("flagname", "f", 1234, "help message") 95 | var flagvar bool 96 | func init() { 97 | flag.BoolVarP("boolname", "b", true, "help message") 98 | } 99 | flag.VarP(&flagVar, "varname", "v", 1234, "help message") 100 | ``` 101 | 102 | Shorthand letters can be used with single dashes on the command line. 103 | Boolean shorthand flags can be combined with other shorthand flags. 104 | 105 | The default set of command-line flags is controlled by 106 | top-level functions. The FlagSet type allows one to define 107 | independent sets of flags, such as to implement subcommands 108 | in a command-line interface. The methods of FlagSet are 109 | analogous to the top-level functions for the command-line 110 | flag set. 111 | 112 | ## Command line flag syntax 113 | 114 | ``` 115 | --flag // boolean flags only 116 | --flag=x 117 | ``` 118 | 119 | Unlike the flag package, a single dash before an option means something 120 | different than a double dash. Single dashes signify a series of shorthand 121 | letters for flags. All but the last shorthand letter must be boolean flags. 122 | 123 | ``` 124 | // boolean flags 125 | -f 126 | -abc 127 | 128 | // non-boolean flags 129 | -n 1234 130 | -Ifile 131 | 132 | // mixed 133 | -abcs "hello" 134 | -abcn1234 135 | ``` 136 | 137 | Flag parsing stops after the terminator "--". Unlike the flag package, 138 | flags can be interspersed with arguments anywhere on the command line 139 | before this terminator. 140 | 141 | Integer flags accept 1234, 0664, 0x1234 and may be negative. 142 | Boolean flags (in their long form) accept 1, 0, t, f, true, false, 143 | TRUE, FALSE, True, False. 144 | Duration flags accept any input valid for time.ParseDuration. 145 | 146 | ## More info 147 | 148 | You can see the full reference documentation of the pflag package 149 | [at godoc.org][3], or through go's standard documentation system by 150 | running `godoc -http=:6060` and browsing to 151 | [http://localhost:6060/pkg/github.com/ogier/pflag][2] after 152 | installation. 153 | 154 | [2]: http://localhost:6060/pkg/github.com/ogier/pflag 155 | [3]: http://godoc.org/github.com/ogier/pflag 156 | -------------------------------------------------------------------------------- /vendor/github.com/opentracing/opentracing-go/tracer.go: -------------------------------------------------------------------------------- 1 | package opentracing 2 | 3 | import "time" 4 | 5 | // Tracer is a simple, thin interface for Span creation. 6 | // 7 | // A straightforward implementation is available via the 8 | // `opentracing/basictracer-go` package's `standardtracer.New()'. 9 | type Tracer interface { 10 | // Create, start, and return a new Span with the given `operationName`, all 11 | // without specifying a parent Span that can be used to incorporate the 12 | // newly-returned Span into an existing trace. (I.e., the returned Span is 13 | // the "root" of its trace). 14 | // 15 | // Examples: 16 | // 17 | // var tracer opentracing.Tracer = ... 18 | // 19 | // sp := tracer.StartSpan("GetFeed") 20 | // 21 | // sp := tracer.StartSpanWithOptions(opentracing.SpanOptions{ 22 | // OperationName: "LoggedHTTPRequest", 23 | // Tags: opentracing.Tags{"user_agent", loggedReq.UserAgent}, 24 | // StartTime: loggedReq.Timestamp, 25 | // }) 26 | // 27 | StartSpan(operationName string) Span 28 | StartSpanWithOptions(opts StartSpanOptions) Span 29 | 30 | // Inject() takes the `sp` Span instance and represents it for propagation 31 | // within `carrier`. The actual type of `carrier` depends on the value of 32 | // `format`. 33 | // 34 | // OpenTracing defines a common set of `format` values (see BuiltinFormat), 35 | // and each has an expected carrier type. 36 | // 37 | // Other packages may declare their own `format` values, much like the keys 38 | // used by the `net.Context` package (see 39 | // https://godoc.org/golang.org/x/net/context#WithValue). 40 | // 41 | // Example usage (sans error handling): 42 | // 43 | // carrier := opentracing.HTTPHeaderTextMapCarrier(httpReq.Header) 44 | // tracer.Inject( 45 | // span, 46 | // opentracing.TextMap, 47 | // carrier) 48 | // 49 | // NOTE: All opentracing.Tracer implementations MUST support all 50 | // BuiltinFormats. 51 | // 52 | // Implementations may return opentracing.ErrUnsupportedFormat if `format` 53 | // is or not supported by (or not known by) the implementation. 54 | // 55 | // Implementations may return opentracing.ErrInvalidCarrier or any other 56 | // implementation-specific error if the format is supported but injection 57 | // fails anyway. 58 | // 59 | // See Tracer.Join(). 60 | Inject(sp Span, format interface{}, carrier interface{}) error 61 | 62 | // Join() returns a Span instance with operation name `operationName` given 63 | // `format` and `carrier`. 64 | // 65 | // Join() is responsible for extracting and joining to the trace of a Span 66 | // instance embedded in a format-specific "carrier" object. Typically the 67 | // joining will take place on the server side of an RPC boundary, but 68 | // message queues and other IPC mechanisms are also reasonable places to 69 | // use Join(). 70 | // 71 | // OpenTracing defines a common set of `format` values (see BuiltinFormat), 72 | // and each has an expected carrier type. 73 | // 74 | // Other packages may declare their own `format` values, much like the keys 75 | // used by the `net.Context` package (see 76 | // https://godoc.org/golang.org/x/net/context#WithValue). 77 | // 78 | // Example usage (sans error handling): 79 | // 80 | // carrier := opentracing.HTTPHeaderTextMapCarrier(httpReq.Header) 81 | // span, err := tracer.Join( 82 | // operationName, 83 | // opentracing.TextMap, 84 | // carrier) 85 | // 86 | // NOTE: All opentracing.Tracer implementations MUST support all 87 | // BuiltinFormats. 88 | // 89 | // Return values: 90 | // - A successful join will return a started Span instance and a nil error 91 | // - If there was simply no trace to join with in `carrier`, Join() 92 | // returns (nil, opentracing.ErrTraceNotFound) 93 | // - If `format` is unsupported or unrecognized, Join() returns (nil, 94 | // opentracing.ErrUnsupportedFormat) 95 | // - If there are more fundamental problems with the `carrier` object, 96 | // Join() may return opentracing.ErrInvalidCarrier, 97 | // opentracing.ErrTraceCorrupted, or implementation-specific errors. 98 | // 99 | // See Tracer.Inject(). 100 | Join(operationName string, format interface{}, carrier interface{}) (Span, error) 101 | } 102 | 103 | // StartSpanOptions allows Tracer.StartSpanWithOptions callers to override the 104 | // start timestamp, specify a parent Span, and make sure that Tags are 105 | // available at Span initialization time. 106 | type StartSpanOptions struct { 107 | // OperationName may be empty (and set later via Span.SetOperationName) 108 | OperationName string 109 | 110 | // Parent may specify Span instance that caused the new (child) Span to be 111 | // created. 112 | // 113 | // If nil, start a "root" span (i.e., start a new trace). 114 | Parent Span 115 | 116 | // StartTime overrides the Span's start time, or implicitly becomes 117 | // time.Now() if StartTime.IsZero(). 118 | StartTime time.Time 119 | 120 | // Tags may have zero or more entries; the restrictions on map values are 121 | // identical to those for Span.SetTag(). May be nil. 122 | // 123 | // If specified, the caller hands off ownership of Tags at 124 | // StartSpanWithOptions() invocation time. 125 | Tags map[string]interface{} 126 | } 127 | -------------------------------------------------------------------------------- /brew/tcgplayer.go: -------------------------------------------------------------------------------- 1 | package brew 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | "strings" 7 | ) 8 | 9 | func ReplaceUnicode(name string) string { 10 | replace := map[string]string{ 11 | "Æ": "AE", 12 | "é": "e", 13 | "ö": "o", 14 | "û": "u", 15 | "á": "a", 16 | "â": "a", 17 | "ú": "u", 18 | "à": "a", 19 | } 20 | s := name 21 | 22 | for unicode, ascii := range replace { 23 | s = strings.Replace(s, unicode, ascii, -1) 24 | } 25 | 26 | return s 27 | } 28 | 29 | func TCGSlug(name string) string { 30 | re := regexp.MustCompile(`[,.'"?:]`) 31 | d := strings.Replace(strings.ToLower(name), " ", "-", -1) 32 | return ReplaceUnicode(re.ReplaceAllLiteralString(d, "")) 33 | } 34 | 35 | func TCGCardURL(c *Card) string { 36 | if len(c.Editions) == 1 { 37 | return TCGEditionURL(c, &c.Editions[0]) 38 | } else { 39 | return "http://store.tcgplayer.com/magic/product/show?partner=DECKBREW&ProductName=" + TCGSlug(c.Name) 40 | } 41 | } 42 | 43 | func TCGEditionURL(c *Card, e *Edition) string { 44 | set := TCGSlug(TCGSet(e.SetId, e.Set)) 45 | id := TCGSlug(TCGName(c.Name, e.MultiverseId)) 46 | return fmt.Sprintf("http://store.tcgplayer.com/magic/%s/%s?partner=DECKBREW", set, id) 47 | } 48 | 49 | func NormalizeName(name string) string { 50 | return strings.ToLower(ReplaceUnicode(strings.TrimSpace(name))) 51 | } 52 | 53 | func TCGName(name string, id int) string { 54 | translation := TranslateID(id) 55 | if translation != "" { 56 | return NormalizeName(translation) 57 | } else { 58 | return NormalizeName(name) 59 | } 60 | } 61 | 62 | func TranslateID(id int) string { 63 | switch id { 64 | case 1071: 65 | return "Mishra's Factory (Fall)" 66 | case 1072: 67 | return "Mishra's Factory (Spring)" 68 | case 1073: 69 | return "Mishra's Factory (Summer)" 70 | case 1074: 71 | return "Mishra's Factory (Winter)" 72 | case 1076: 73 | return "Strip Mine (Even Horizon)" 74 | case 1077: 75 | return "Strip Mine (Uneven Horizon)" 76 | case 1078: 77 | return "Strip Mine (No Horizon)" 78 | case 1079: 79 | return "Strip Mine (Tower)" 80 | case 1080, 2888: 81 | return "Urza's Mine (Clawed Sphere)" 82 | case 1081, 2889: 83 | return "Urza's Mine (Mouth)" 84 | case 1082, 2890: 85 | return "Urza's Mine (Pulley)" 86 | case 1083, 2891: 87 | return "Urza's Mine (Tower)" 88 | case 1084, 2892: 89 | return "Urza's Power Plant (Bug)" 90 | case 1085, 2893: 91 | return "Urza's Power Plant (Columns)" 92 | case 1086, 2894: 93 | return "Urza's Power Plant (Sphere)" 94 | case 1087, 2895: 95 | return "Urza's Power Plant (Rock in Pot)" 96 | case 1088, 2896: 97 | return "Urza's Tower (Forest)" 98 | case 1089, 2897: 99 | return "Urza's Tower (Mountains)" 100 | case 2898, 1090: 101 | return "Urza's Tower (Plains)" 102 | case 1091, 2899: 103 | return "Urza's Tower (Shore)" 104 | case 2969: 105 | return "Hungry Mist [Version 1]" 106 | case 2970: 107 | return "Hungry Mist [Version 2]" 108 | case 3021: 109 | return "Mesa Falcon [Version 1]" 110 | case 3022: 111 | return "Mesa Falcon [Version 2]" 112 | case 3207: 113 | return "Reinforcements (Version 2)" 114 | case 4979: 115 | return "Pegasus Token" 116 | case 5472: 117 | return "Soldier Token" 118 | case 5503: 119 | return "Goblin Token" 120 | case 5560: 121 | return "Sheep Token" 122 | case 5601: 123 | return "Zombie Token" 124 | case 5607: 125 | return "Squirrel Token" 126 | case 9757: 127 | return "The Ultimate Nightmare of Wizards of the Coast Cu" 128 | case 9780: 129 | return "B.F.M. (Big Furry Monster Left)" 130 | case 9844: 131 | return "B.F.M. (Big Furry Monster Right)" 132 | case 74237: 133 | return "Our Market Research..." 134 | case 78968: 135 | return "Brothers Yamazaki (160a Sword)" 136 | case 85106: 137 | return "Brothers Yamazaki (160b Pike)" 138 | case 209163: 139 | return "Hornet Token" 140 | case 386322: 141 | return "Goblin Token" 142 | } 143 | return "" 144 | } 145 | 146 | func TCGSet(setId, set string) string { 147 | switch setId { 148 | case "10E": 149 | return "10th Edition" 150 | case "9ED": 151 | return "9th Edition" 152 | case "8ED": 153 | return "8th Edition" 154 | case "7ED": 155 | return "7th Edition" 156 | case "M15": 157 | return "Magic 2015 (M15)" 158 | case "M14": 159 | return "Magic 2014 (M14)" 160 | case "M13": 161 | return "Magic 2013 (M13)" 162 | case "M12": 163 | return "Magic 2012 (M12)" 164 | case "M11": 165 | return "Magic 2011 (M11)" 166 | case "M10": 167 | return "Magic 2010 (M10)" 168 | case "CMD": 169 | return "Commander" 170 | case "HHO": 171 | return "Special Occasion" 172 | case "RAV": 173 | return "Ravnica" 174 | case "DDG": 175 | return "Duel Decks: Knights vs Dragons" 176 | case "DDL": 177 | return "Duel Decks: Heroes vs. Monsters" 178 | case "PC2": 179 | return "Planechase 2012" 180 | case "C13": 181 | return "Commander 2013" 182 | case "C14": 183 | return "Commander 2014" 184 | case "PD2": 185 | return "Premium Deck Series: Fire and Lightning" 186 | case "LEB": 187 | return "Beta Edition" 188 | case "LEA": 189 | return "Alpha Edition" 190 | case "TSB": 191 | return "Timeshifted" 192 | case "MD1": 193 | return "Magic Modern Event Deck" 194 | case "CNS": 195 | return "Conspiracy" 196 | case "DKM": 197 | return "Deckmasters Garfield vs. Finkel" 198 | case "KTK": 199 | return "Khans of Tarkir" 200 | } 201 | return set 202 | } 203 | -------------------------------------------------------------------------------- /vendor/github.com/lib/pq/oid/types.go: -------------------------------------------------------------------------------- 1 | // generated by 'go run gen.go'; do not edit 2 | 3 | package oid 4 | 5 | const ( 6 | T_bool Oid = 16 7 | T_bytea Oid = 17 8 | T_char Oid = 18 9 | T_name Oid = 19 10 | T_int8 Oid = 20 11 | T_int2 Oid = 21 12 | T_int2vector Oid = 22 13 | T_int4 Oid = 23 14 | T_regproc Oid = 24 15 | T_text Oid = 25 16 | T_oid Oid = 26 17 | T_tid Oid = 27 18 | T_xid Oid = 28 19 | T_cid Oid = 29 20 | T_oidvector Oid = 30 21 | T_pg_type Oid = 71 22 | T_pg_attribute Oid = 75 23 | T_pg_proc Oid = 81 24 | T_pg_class Oid = 83 25 | T_json Oid = 114 26 | T_xml Oid = 142 27 | T__xml Oid = 143 28 | T_pg_node_tree Oid = 194 29 | T__json Oid = 199 30 | T_smgr Oid = 210 31 | T_point Oid = 600 32 | T_lseg Oid = 601 33 | T_path Oid = 602 34 | T_box Oid = 603 35 | T_polygon Oid = 604 36 | T_line Oid = 628 37 | T__line Oid = 629 38 | T_cidr Oid = 650 39 | T__cidr Oid = 651 40 | T_float4 Oid = 700 41 | T_float8 Oid = 701 42 | T_abstime Oid = 702 43 | T_reltime Oid = 703 44 | T_tinterval Oid = 704 45 | T_unknown Oid = 705 46 | T_circle Oid = 718 47 | T__circle Oid = 719 48 | T_money Oid = 790 49 | T__money Oid = 791 50 | T_macaddr Oid = 829 51 | T_inet Oid = 869 52 | T__bool Oid = 1000 53 | T__bytea Oid = 1001 54 | T__char Oid = 1002 55 | T__name Oid = 1003 56 | T__int2 Oid = 1005 57 | T__int2vector Oid = 1006 58 | T__int4 Oid = 1007 59 | T__regproc Oid = 1008 60 | T__text Oid = 1009 61 | T__tid Oid = 1010 62 | T__xid Oid = 1011 63 | T__cid Oid = 1012 64 | T__oidvector Oid = 1013 65 | T__bpchar Oid = 1014 66 | T__varchar Oid = 1015 67 | T__int8 Oid = 1016 68 | T__point Oid = 1017 69 | T__lseg Oid = 1018 70 | T__path Oid = 1019 71 | T__box Oid = 1020 72 | T__float4 Oid = 1021 73 | T__float8 Oid = 1022 74 | T__abstime Oid = 1023 75 | T__reltime Oid = 1024 76 | T__tinterval Oid = 1025 77 | T__polygon Oid = 1027 78 | T__oid Oid = 1028 79 | T_aclitem Oid = 1033 80 | T__aclitem Oid = 1034 81 | T__macaddr Oid = 1040 82 | T__inet Oid = 1041 83 | T_bpchar Oid = 1042 84 | T_varchar Oid = 1043 85 | T_date Oid = 1082 86 | T_time Oid = 1083 87 | T_timestamp Oid = 1114 88 | T__timestamp Oid = 1115 89 | T__date Oid = 1182 90 | T__time Oid = 1183 91 | T_timestamptz Oid = 1184 92 | T__timestamptz Oid = 1185 93 | T_interval Oid = 1186 94 | T__interval Oid = 1187 95 | T__numeric Oid = 1231 96 | T_pg_database Oid = 1248 97 | T__cstring Oid = 1263 98 | T_timetz Oid = 1266 99 | T__timetz Oid = 1270 100 | T_bit Oid = 1560 101 | T__bit Oid = 1561 102 | T_varbit Oid = 1562 103 | T__varbit Oid = 1563 104 | T_numeric Oid = 1700 105 | T_refcursor Oid = 1790 106 | T__refcursor Oid = 2201 107 | T_regprocedure Oid = 2202 108 | T_regoper Oid = 2203 109 | T_regoperator Oid = 2204 110 | T_regclass Oid = 2205 111 | T_regtype Oid = 2206 112 | T__regprocedure Oid = 2207 113 | T__regoper Oid = 2208 114 | T__regoperator Oid = 2209 115 | T__regclass Oid = 2210 116 | T__regtype Oid = 2211 117 | T_record Oid = 2249 118 | T_cstring Oid = 2275 119 | T_any Oid = 2276 120 | T_anyarray Oid = 2277 121 | T_void Oid = 2278 122 | T_trigger Oid = 2279 123 | T_language_handler Oid = 2280 124 | T_internal Oid = 2281 125 | T_opaque Oid = 2282 126 | T_anyelement Oid = 2283 127 | T__record Oid = 2287 128 | T_anynonarray Oid = 2776 129 | T_pg_authid Oid = 2842 130 | T_pg_auth_members Oid = 2843 131 | T__txid_snapshot Oid = 2949 132 | T_uuid Oid = 2950 133 | T__uuid Oid = 2951 134 | T_txid_snapshot Oid = 2970 135 | T_fdw_handler Oid = 3115 136 | T_anyenum Oid = 3500 137 | T_tsvector Oid = 3614 138 | T_tsquery Oid = 3615 139 | T_gtsvector Oid = 3642 140 | T__tsvector Oid = 3643 141 | T__gtsvector Oid = 3644 142 | T__tsquery Oid = 3645 143 | T_regconfig Oid = 3734 144 | T__regconfig Oid = 3735 145 | T_regdictionary Oid = 3769 146 | T__regdictionary Oid = 3770 147 | T_anyrange Oid = 3831 148 | T_event_trigger Oid = 3838 149 | T_int4range Oid = 3904 150 | T__int4range Oid = 3905 151 | T_numrange Oid = 3906 152 | T__numrange Oid = 3907 153 | T_tsrange Oid = 3908 154 | T__tsrange Oid = 3909 155 | T_tstzrange Oid = 3910 156 | T__tstzrange Oid = 3911 157 | T_daterange Oid = 3912 158 | T__daterange Oid = 3913 159 | T_int8range Oid = 3926 160 | T__int8range Oid = 3927 161 | ) 162 | -------------------------------------------------------------------------------- /brew/service.go: -------------------------------------------------------------------------------- 1 | package brew 2 | 3 | import ( 4 | "sort" 5 | "strconv" 6 | "strings" 7 | 8 | "golang.org/x/net/context" 9 | ) 10 | 11 | type Reader interface { 12 | GetCards(context.Context, Search) ([]Card, error) 13 | GetCardsByName(context.Context, string) ([]Card, error) 14 | GetCard(context.Context, string) (Card, error) 15 | GetRandomCardID(context.Context) (string, error) 16 | GetSets(context.Context) ([]Set, error) 17 | GetSet(context.Context, string) (Set, error) 18 | GetColors(context.Context) ([]string, error) 19 | GetSupertypes(context.Context) ([]string, error) 20 | GetSubtypes(context.Context) ([]string, error) 21 | GetTypes(context.Context) ([]string, error) 22 | } 23 | 24 | func toUniqueLower(things []string) []string { 25 | seen := map[string]bool{} 26 | sorted := []string{} 27 | for _, thing := range things { 28 | if _, found := seen[thing]; !found { 29 | sorted = append(sorted, strings.ToLower(thing)) 30 | seen[thing] = true 31 | } 32 | } 33 | sort.Strings(sorted) 34 | return sorted 35 | } 36 | 37 | type Search struct { 38 | Colors []string 39 | Formats []string 40 | IncludeMulticolor bool 41 | Multicolor bool 42 | MultiverseIDs []string 43 | Names []string 44 | Rarities []string 45 | Sets []string 46 | Status []string 47 | Subtypes []string 48 | Supertypes []string 49 | Rules []string 50 | Types []string 51 | Limit int 52 | Offset int 53 | Page int 54 | } 55 | 56 | type Card struct { 57 | Name string `json:"name"` 58 | Id string `json:"id"` 59 | Href string `json:"url,omitempty"` 60 | StoreUrl string `json:"store_url"` 61 | Types []string `json:"types,omitempty"` 62 | Supertypes []string `json:"supertypes,omitempty"` 63 | Subtypes []string `json:"subtypes,omitempty"` 64 | Colors []string `json:"colors,omitempty"` 65 | ConvertedCost int `json:"cmc"` 66 | ManaCost string `json:"cost"` 67 | Text string `json:"text"` 68 | Power string `json:"power,omitempty"` 69 | Toughness string `json:"toughness,omitempty"` 70 | Loyalty int `json:"loyalty,omitempty"` 71 | FormatMap map[string]string `json:"formats"` 72 | Editions []Edition `json:"editions,omitempty"` 73 | } 74 | 75 | func (c *Card) Sets() []string { 76 | sets := []string{} 77 | for _, e := range c.Editions { 78 | sets = append(sets, e.SetId) 79 | } 80 | return toUniqueLower(sets) 81 | } 82 | 83 | func (c *Card) Formats() []string { 84 | v := []string{} 85 | for format, status := range c.FormatMap { 86 | if status == "legal" || status == "restricted" { 87 | v = append(v, format) 88 | } 89 | } 90 | return toUniqueLower(v) 91 | } 92 | 93 | func (c *Card) Status() []string { 94 | v := []string{} 95 | for _, status := range c.FormatMap { 96 | v = append(v, status) 97 | } 98 | return toUniqueLower(v) 99 | } 100 | 101 | func (c *Card) Rarities() []string { 102 | r := []string{} 103 | for _, e := range c.Editions { 104 | r = append(r, e.Rarity) 105 | } 106 | return toUniqueLower(r) 107 | } 108 | 109 | func (c *Card) MultiverseIds() []string { 110 | r := []string{} 111 | for _, e := range c.Editions { 112 | r = append(r, strconv.Itoa(e.MultiverseId)) 113 | } 114 | return toUniqueLower(r) 115 | } 116 | 117 | func (c *Card) Multicolor() bool { 118 | return len(c.Colors) > 1 119 | } 120 | 121 | // Don't expose this 122 | func (c *Card) Fill(r router) { 123 | c.Href = r.CardURL(c.Id) 124 | c.StoreUrl = TCGCardURL(c) 125 | 126 | for i, _ := range c.Editions { 127 | e := &c.Editions[i] 128 | e.Href = r.EditionURL(e.MultiverseId) 129 | e.SetUrl = r.SetURL(e.SetId) 130 | e.ImageUrl = r.EditionImageURL(e.MultiverseId) 131 | e.HTMLUrl = r.EditionHtmlURL(e.MultiverseId) 132 | e.StoreUrl = TCGEditionURL(c, e) 133 | e.Price = &Price{ 134 | Low: 0, 135 | Average: 0, 136 | High: 0, 137 | } 138 | } 139 | } 140 | 141 | type Edition struct { 142 | Set string `json:"set"` 143 | SetId string `json:"set_id"` 144 | CardId string `json:"-"` 145 | Watermark string `json:"watermark,omitempty"` 146 | Rarity string `json:"rarity"` 147 | Border string `json:"-"` 148 | Artist string `json:"artist"` 149 | MultiverseId int `json:"multiverse_id"` 150 | Flavor string `json:"flavor,omitempty"` 151 | Number string `json:"number"` 152 | Layout string `json:"layout"` 153 | Price *Price `json:"price,omitempty"` 154 | Href string `json:"url,omitempty"` 155 | ImageUrl string `json:"image_url,omitempty"` 156 | SetUrl string `json:"set_url,omitempty"` 157 | StoreUrl string `json:"store_url"` 158 | HTMLUrl string `json:"html_url"` 159 | } 160 | 161 | type Price struct { 162 | Low int `json:"low"` 163 | Average int `json:"median"` 164 | High int `json:"high"` 165 | } 166 | 167 | type Set struct { 168 | Id string `json:"id"` 169 | Name string `json:"name"` 170 | Border string `json:"border"` 171 | Type string `json:"type"` 172 | Href string `json:"url"` 173 | CardsUrl string `json:"cards_url"` 174 | PriceGuide string `json:"-"` 175 | Priced bool `json:"-"` 176 | } 177 | 178 | func (s *Set) Fill(r router) { 179 | s.Href = r.SetURL(s.Id) 180 | s.CardsUrl = r.SetCardsURL(s.Id) 181 | } 182 | -------------------------------------------------------------------------------- /vendor/code.google.com/p/go.net/html/doctype.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 html 6 | 7 | import ( 8 | "strings" 9 | ) 10 | 11 | // parseDoctype parses the data from a DoctypeToken into a name, 12 | // public identifier, and system identifier. It returns a Node whose Type 13 | // is DoctypeNode, whose Data is the name, and which has attributes 14 | // named "system" and "public" for the two identifiers if they were present. 15 | // quirks is whether the document should be parsed in "quirks mode". 16 | func parseDoctype(s string) (n *Node, quirks bool) { 17 | n = &Node{Type: DoctypeNode} 18 | 19 | // Find the name. 20 | space := strings.IndexAny(s, whitespace) 21 | if space == -1 { 22 | space = len(s) 23 | } 24 | n.Data = s[:space] 25 | // The comparison to "html" is case-sensitive. 26 | if n.Data != "html" { 27 | quirks = true 28 | } 29 | n.Data = strings.ToLower(n.Data) 30 | s = strings.TrimLeft(s[space:], whitespace) 31 | 32 | if len(s) < 6 { 33 | // It can't start with "PUBLIC" or "SYSTEM". 34 | // Ignore the rest of the string. 35 | return n, quirks || s != "" 36 | } 37 | 38 | key := strings.ToLower(s[:6]) 39 | s = s[6:] 40 | for key == "public" || key == "system" { 41 | s = strings.TrimLeft(s, whitespace) 42 | if s == "" { 43 | break 44 | } 45 | quote := s[0] 46 | if quote != '"' && quote != '\'' { 47 | break 48 | } 49 | s = s[1:] 50 | q := strings.IndexRune(s, rune(quote)) 51 | var id string 52 | if q == -1 { 53 | id = s 54 | s = "" 55 | } else { 56 | id = s[:q] 57 | s = s[q+1:] 58 | } 59 | n.Attr = append(n.Attr, Attribute{Key: key, Val: id}) 60 | if key == "public" { 61 | key = "system" 62 | } else { 63 | key = "" 64 | } 65 | } 66 | 67 | if key != "" || s != "" { 68 | quirks = true 69 | } else if len(n.Attr) > 0 { 70 | if n.Attr[0].Key == "public" { 71 | public := strings.ToLower(n.Attr[0].Val) 72 | switch public { 73 | case "-//w3o//dtd w3 html strict 3.0//en//", "-/w3d/dtd html 4.0 transitional/en", "html": 74 | quirks = true 75 | default: 76 | for _, q := range quirkyIDs { 77 | if strings.HasPrefix(public, q) { 78 | quirks = true 79 | break 80 | } 81 | } 82 | } 83 | // The following two public IDs only cause quirks mode if there is no system ID. 84 | if len(n.Attr) == 1 && (strings.HasPrefix(public, "-//w3c//dtd html 4.01 frameset//") || 85 | strings.HasPrefix(public, "-//w3c//dtd html 4.01 transitional//")) { 86 | quirks = true 87 | } 88 | } 89 | if lastAttr := n.Attr[len(n.Attr)-1]; lastAttr.Key == "system" && 90 | strings.ToLower(lastAttr.Val) == "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd" { 91 | quirks = true 92 | } 93 | } 94 | 95 | return n, quirks 96 | } 97 | 98 | // quirkyIDs is a list of public doctype identifiers that cause a document 99 | // to be interpreted in quirks mode. The identifiers should be in lower case. 100 | var quirkyIDs = []string{ 101 | "+//silmaril//dtd html pro v0r11 19970101//", 102 | "-//advasoft ltd//dtd html 3.0 aswedit + extensions//", 103 | "-//as//dtd html 3.0 aswedit + extensions//", 104 | "-//ietf//dtd html 2.0 level 1//", 105 | "-//ietf//dtd html 2.0 level 2//", 106 | "-//ietf//dtd html 2.0 strict level 1//", 107 | "-//ietf//dtd html 2.0 strict level 2//", 108 | "-//ietf//dtd html 2.0 strict//", 109 | "-//ietf//dtd html 2.0//", 110 | "-//ietf//dtd html 2.1e//", 111 | "-//ietf//dtd html 3.0//", 112 | "-//ietf//dtd html 3.2 final//", 113 | "-//ietf//dtd html 3.2//", 114 | "-//ietf//dtd html 3//", 115 | "-//ietf//dtd html level 0//", 116 | "-//ietf//dtd html level 1//", 117 | "-//ietf//dtd html level 2//", 118 | "-//ietf//dtd html level 3//", 119 | "-//ietf//dtd html strict level 0//", 120 | "-//ietf//dtd html strict level 1//", 121 | "-//ietf//dtd html strict level 2//", 122 | "-//ietf//dtd html strict level 3//", 123 | "-//ietf//dtd html strict//", 124 | "-//ietf//dtd html//", 125 | "-//metrius//dtd metrius presentational//", 126 | "-//microsoft//dtd internet explorer 2.0 html strict//", 127 | "-//microsoft//dtd internet explorer 2.0 html//", 128 | "-//microsoft//dtd internet explorer 2.0 tables//", 129 | "-//microsoft//dtd internet explorer 3.0 html strict//", 130 | "-//microsoft//dtd internet explorer 3.0 html//", 131 | "-//microsoft//dtd internet explorer 3.0 tables//", 132 | "-//netscape comm. corp.//dtd html//", 133 | "-//netscape comm. corp.//dtd strict html//", 134 | "-//o'reilly and associates//dtd html 2.0//", 135 | "-//o'reilly and associates//dtd html extended 1.0//", 136 | "-//o'reilly and associates//dtd html extended relaxed 1.0//", 137 | "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//", 138 | "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//", 139 | "-//spyglass//dtd html 2.0 extended//", 140 | "-//sq//dtd html 2.0 hotmetal + extensions//", 141 | "-//sun microsystems corp.//dtd hotjava html//", 142 | "-//sun microsystems corp.//dtd hotjava strict html//", 143 | "-//w3c//dtd html 3 1995-03-24//", 144 | "-//w3c//dtd html 3.2 draft//", 145 | "-//w3c//dtd html 3.2 final//", 146 | "-//w3c//dtd html 3.2//", 147 | "-//w3c//dtd html 3.2s draft//", 148 | "-//w3c//dtd html 4.0 frameset//", 149 | "-//w3c//dtd html 4.0 transitional//", 150 | "-//w3c//dtd html experimental 19960712//", 151 | "-//w3c//dtd html experimental 970421//", 152 | "-//w3c//dtd w3 html//", 153 | "-//w3o//dtd w3 html 3.0//", 154 | "-//webtechs//dtd mozilla html 2.0//", 155 | "-//webtechs//dtd mozilla html//", 156 | } 157 | -------------------------------------------------------------------------------- /api/search.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | "net/url" 6 | "strconv" 7 | "strings" 8 | 9 | "github.com/kyleconroy/deckbrew/brew" 10 | ) 11 | 12 | func toLower(strs []string) []string { 13 | downers := []string{} 14 | for _, s := range strs { 15 | downers = append(downers, strings.ToLower(s)) 16 | } 17 | return downers 18 | } 19 | 20 | func extractPattern(args url.Values, key string) ([]string, error) { 21 | items := []string{} 22 | for _, oracle := range args[key] { 23 | if oracle == "" { 24 | continue 25 | } 26 | if strings.ContainsAny(oracle, "%_") { 27 | return items, fmt.Errorf("Search string can't contain '%%' or '_'") 28 | } 29 | items = append(items, "\"%"+strings.Replace(oracle, "\"", "", -1)+"%\"") 30 | } 31 | return items, nil 32 | } 33 | 34 | func extractStrings(args url.Values, key string, allowed map[string]bool) ([]string, error) { 35 | items := args[key] 36 | 37 | for _, t := range items { 38 | if !allowed[t] { 39 | return items, fmt.Errorf("The %s '%s' is not recognized", key, t) 40 | } 41 | } 42 | 43 | return items, nil 44 | } 45 | 46 | func parseMulticolor(s *brew.Search, args url.Values) error { 47 | switch args.Get("multicolor") { 48 | case "true": 49 | s.IncludeMulticolor = true 50 | s.Multicolor = true 51 | case "false": 52 | s.IncludeMulticolor = true 53 | s.Multicolor = false 54 | case "": 55 | s.IncludeMulticolor = false 56 | return nil 57 | default: 58 | return fmt.Errorf("Multicolor should be either 'true' or 'false'") 59 | } 60 | return nil 61 | } 62 | 63 | func parseMultiverseIDs(s *brew.Search, args url.Values) error { 64 | ids := args["multiverseid"][:] 65 | for _, m := range args["m"] { 66 | ids = append(ids, m) 67 | } 68 | s.MultiverseIDs = ids 69 | return nil 70 | } 71 | 72 | func parseSupertypes(s *brew.Search, args url.Values) (err error) { 73 | s.Supertypes, err = extractStrings(args, "supertype", map[string]bool{ 74 | "legendary": true, 75 | "basic": true, 76 | "world": true, 77 | "snow": true, 78 | "ongoing": true, 79 | }) 80 | return 81 | } 82 | 83 | // TODO: Add validation 84 | func parseSubtypes(s *brew.Search, args url.Values) error { 85 | s.Subtypes = toLower(args["subtype"]) 86 | return nil 87 | } 88 | 89 | // TODO: Add validation 90 | func parseSets(s *brew.Search, args url.Values) error { 91 | s.Sets = toLower(args["set"]) 92 | return nil 93 | } 94 | 95 | func parseColors(s *brew.Search, args url.Values) (err error) { 96 | s.Colors, err = extractStrings(args, "color", map[string]bool{ 97 | "red": true, 98 | "black": true, 99 | "blue": true, 100 | "white": true, 101 | "green": true, 102 | }) 103 | return 104 | } 105 | 106 | func parseStatus(s *brew.Search, args url.Values) (err error) { 107 | s.Status, err = extractStrings(args, "status", map[string]bool{ 108 | "legal": true, 109 | "banned": true, 110 | "restricted": true, 111 | }) 112 | return 113 | } 114 | 115 | func parseFormat(s *brew.Search, args url.Values) (err error) { 116 | s.Formats, err = extractStrings(args, "format", map[string]bool{ 117 | "commander": true, 118 | "standard": true, 119 | "modern": true, 120 | "vintage": true, 121 | "legacy": true, 122 | }) 123 | return 124 | } 125 | 126 | func parseRarity(s *brew.Search, args url.Values) (err error) { 127 | s.Rarities, err = extractStrings(args, "rarity", map[string]bool{ 128 | "common": true, 129 | "uncommon": true, 130 | "rare": true, 131 | "mythic": true, 132 | "special": true, 133 | "basic": true, 134 | }) 135 | return 136 | } 137 | 138 | func parseTypes(s *brew.Search, args url.Values) (err error) { 139 | s.Types, err = extractStrings(args, "type", map[string]bool{ 140 | "creature": true, 141 | "land": true, 142 | "tribal": true, 143 | "phenomenon": true, 144 | "summon": true, 145 | "enchantment": true, 146 | "sorcery": true, 147 | "vanguard": true, 148 | "instant": true, 149 | "planeswalker": true, 150 | "artifact": true, 151 | "plane": true, 152 | "scheme": true, 153 | }) 154 | return 155 | } 156 | 157 | func parseName(s *brew.Search, args url.Values) (err error) { 158 | s.Names, err = extractPattern(args, "name") 159 | return 160 | } 161 | 162 | func parseRules(s *brew.Search, args url.Values) (err error) { 163 | s.Rules, err = extractPattern(args, "oracle") 164 | return 165 | } 166 | 167 | func parsePaging(s *brew.Search, args url.Values) error { 168 | s.Limit = 100 169 | 170 | pagenum := args.Get("page") 171 | if pagenum == "" { 172 | return nil 173 | } 174 | 175 | page, err := strconv.Atoi(pagenum) 176 | if err != nil { 177 | return err 178 | } 179 | 180 | if page < 0 { 181 | return fmt.Errorf("Page parameter must be >= 0") 182 | } 183 | 184 | s.Page = page 185 | s.Offset = s.Page * s.Limit 186 | 187 | return nil 188 | } 189 | 190 | func ParseSearch(u *url.URL) (brew.Search, error, []string) { 191 | args := u.Query() 192 | search := brew.Search{} 193 | 194 | funcs := []func(*brew.Search, url.Values) error{ 195 | parseMulticolor, 196 | parseRarity, 197 | parseTypes, 198 | parseSupertypes, 199 | parseColors, 200 | parseSubtypes, 201 | parseFormat, 202 | parseStatus, 203 | parseMultiverseIDs, 204 | parseSets, 205 | parseName, 206 | parseRules, 207 | parsePaging, 208 | } 209 | 210 | var err error 211 | results := []string{} 212 | 213 | for _, fun := range funcs { 214 | if e := fun(&search, args); e != nil { 215 | results = append(results, e.Error()) 216 | err = fmt.Errorf("Errors while processing the search") 217 | } 218 | } 219 | 220 | // By default, include 100 cards 221 | search.Limit = 100 222 | 223 | return search, err, results 224 | } 225 | -------------------------------------------------------------------------------- /vendor/code.google.com/p/go.net/html/node.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 html 6 | 7 | import ( 8 | "code.google.com/p/go.net/html/atom" 9 | ) 10 | 11 | // A NodeType is the type of a Node. 12 | type NodeType uint32 13 | 14 | const ( 15 | ErrorNode NodeType = iota 16 | TextNode 17 | DocumentNode 18 | ElementNode 19 | CommentNode 20 | DoctypeNode 21 | scopeMarkerNode 22 | ) 23 | 24 | // Section 12.2.3.3 says "scope markers are inserted when entering applet 25 | // elements, buttons, object elements, marquees, table cells, and table 26 | // captions, and are used to prevent formatting from 'leaking'". 27 | var scopeMarker = Node{Type: scopeMarkerNode} 28 | 29 | // A Node consists of a NodeType and some Data (tag name for element nodes, 30 | // content for text) and are part of a tree of Nodes. Element nodes may also 31 | // have a Namespace and contain a slice of Attributes. Data is unescaped, so 32 | // that it looks like "a 0 { 160 | return (*s)[i-1] 161 | } 162 | return nil 163 | } 164 | 165 | // index returns the index of the top-most occurrence of n in the stack, or -1 166 | // if n is not present. 167 | func (s *nodeStack) index(n *Node) int { 168 | for i := len(*s) - 1; i >= 0; i-- { 169 | if (*s)[i] == n { 170 | return i 171 | } 172 | } 173 | return -1 174 | } 175 | 176 | // insert inserts a node at the given index. 177 | func (s *nodeStack) insert(i int, n *Node) { 178 | (*s) = append(*s, nil) 179 | copy((*s)[i+1:], (*s)[i:]) 180 | (*s)[i] = n 181 | } 182 | 183 | // remove removes a node from the stack. It is a no-op if n is not present. 184 | func (s *nodeStack) remove(n *Node) { 185 | i := s.index(n) 186 | if i == -1 { 187 | return 188 | } 189 | copy((*s)[i:], (*s)[i+1:]) 190 | j := len(*s) - 1 191 | (*s)[j] = nil 192 | *s = (*s)[:j] 193 | } 194 | -------------------------------------------------------------------------------- /api/handler.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | "net/url" 8 | "regexp" 9 | "strconv" 10 | "strings" 11 | 12 | "golang.org/x/net/context" 13 | 14 | "github.com/kyleconroy/deckbrew/brew" 15 | "github.com/kyleconroy/deckbrew/config" 16 | _ "github.com/lib/pq" 17 | 18 | "goji.io" 19 | "goji.io/pat" 20 | ) 21 | 22 | func Slug(name string) string { 23 | re := regexp.MustCompile(`[,.'"?:()]`) 24 | d := strings.Replace(strings.ToLower(name), " ", "-", -1) 25 | return re.ReplaceAllLiteralString(d, "") 26 | } 27 | 28 | func JSON(w http.ResponseWriter, code int, val interface{}) { 29 | blob, err := json.MarshalIndent(val, "", " ") 30 | 31 | if err != nil { 32 | w.WriteHeader(http.StatusInternalServerError) 33 | fmt.Fprintf(w, `{"error": "Internal server error :("}"`) 34 | } else { 35 | w.WriteHeader(code) 36 | fmt.Fprintf(w, string(blob)) 37 | } 38 | } 39 | 40 | func Errors(errors ...string) ApiError { 41 | return ApiError{Errors: errors} 42 | } 43 | 44 | func LinkHeader(host string, u *url.URL, page int) string { 45 | if page == 0 { 46 | qstring := u.Query() 47 | qstring.Set("page", "1") 48 | return fmt.Sprintf("<%s%s?%s>; rel=\"next\"", host, u.Path, qstring.Encode()) 49 | } else { 50 | qstring := u.Query() 51 | 52 | qstring.Set("page", strconv.Itoa(page-1)) 53 | prev := fmt.Sprintf("<%s%s?%s>; rel=\"prev\"", host, u.Path, qstring.Encode()) 54 | 55 | qstring.Set("page", strconv.Itoa(page+1)) 56 | next := fmt.Sprintf("<%s%s?%s>; rel=\"next\"", host, u.Path, qstring.Encode()) 57 | 58 | return prev + ", " + next 59 | } 60 | } 61 | 62 | type ApiError struct { 63 | Errors []string `json:"errors"` 64 | } 65 | 66 | type API struct { 67 | c brew.Reader 68 | host string 69 | } 70 | 71 | func (a *API) apiBase() string { 72 | if strings.Contains(a.host, ":") { 73 | return "http://" + a.host 74 | } else { 75 | return "https://" + a.host 76 | } 77 | } 78 | 79 | func (a *API) HandleCards(ctx context.Context, w http.ResponseWriter, r *http.Request) { 80 | s, err, errors := ParseSearch(r.URL) 81 | if err != nil { 82 | JSON(w, http.StatusBadRequest, Errors(errors...)) 83 | return 84 | } 85 | cards, err := a.c.GetCards(ctx, s) 86 | if err != nil { 87 | JSON(w, http.StatusInternalServerError, Errors("Error fetching cards")) 88 | return 89 | } 90 | w.Header().Set("Link", LinkHeader(a.apiBase(), r.URL, s.Page)) 91 | JSON(w, http.StatusOK, cards) 92 | } 93 | 94 | func (a *API) HandleRandomCard(ctx context.Context, w http.ResponseWriter, r *http.Request) { 95 | id, err := a.c.GetRandomCardID(ctx) 96 | switch { 97 | case id == "": 98 | JSON(w, http.StatusNotFound, Errors("No random card can be found")) 99 | case err != nil: 100 | JSON(w, http.StatusInternalServerError, Errors("Can't connect to database")) 101 | default: 102 | url := "/mtg/cards/" + id 103 | http.Redirect(w, r, url, http.StatusFound) 104 | fmt.Fprintf(w, `["`+url+`"]`) 105 | } 106 | } 107 | 108 | func (a *API) HandleCard(ctx context.Context, w http.ResponseWriter, r *http.Request) { 109 | card, err := a.c.GetCard(ctx, pat.Param(ctx, "id")) 110 | if err != nil { 111 | JSON(w, http.StatusNotFound, Errors("Card not found")) 112 | return 113 | } 114 | JSON(w, http.StatusOK, card) 115 | } 116 | 117 | func (a *API) HandleSets(ctx context.Context, w http.ResponseWriter, r *http.Request) { 118 | sets, err := a.c.GetSets(ctx) 119 | if err != nil { 120 | JSON(w, http.StatusNotFound, Errors("Sets not found")) 121 | } else { 122 | JSON(w, http.StatusOK, sets) 123 | } 124 | } 125 | 126 | func (a *API) HandleSet(ctx context.Context, w http.ResponseWriter, r *http.Request) { 127 | card, err := a.c.GetSet(ctx, pat.Param(ctx, "id")) 128 | 129 | if err != nil { 130 | JSON(w, http.StatusNotFound, Errors("Set not found")) 131 | } else { 132 | JSON(w, http.StatusOK, card) 133 | } 134 | } 135 | 136 | func (a *API) HandleTerm(f func(context.Context) ([]string, error)) func(ctx context.Context, w http.ResponseWriter, r *http.Request) { 137 | return func(ctx context.Context, w http.ResponseWriter, r *http.Request) { 138 | terms, err := f(ctx) 139 | if err != nil { 140 | JSON(w, http.StatusNotFound, Errors("no strings found")) 141 | } else { 142 | JSON(w, http.StatusOK, terms) 143 | } 144 | } 145 | } 146 | 147 | func (a *API) HandleTypeahead(ctx context.Context, w http.ResponseWriter, r *http.Request) { 148 | cards, err := a.c.GetCardsByName(ctx, r.URL.Query().Get("q")) 149 | if err != nil { 150 | JSON(w, http.StatusNotFound, Errors(" Can't find any cards that match that search")) 151 | return 152 | } 153 | JSON(w, http.StatusOK, cards) 154 | } 155 | 156 | func NotFound(ctx context.Context, w http.ResponseWriter, r *http.Request) { 157 | JSON(w, http.StatusNotFound, Errors("No endpoint here")) 158 | } 159 | 160 | type term int 161 | 162 | func New(cfg *config.Config, client brew.Reader) http.Handler { 163 | app := API{c: client, host: cfg.HostAPI} 164 | 165 | mux := goji.NewMux() 166 | 167 | // Setup middleware 168 | mux.UseC(Recover) 169 | mux.UseC(Tracing) 170 | mux.UseC(Headers) 171 | mux.UseC(Recover) 172 | 173 | mux.HandleFuncC(pat.Get("/mtg/cards"), app.HandleCards) 174 | mux.HandleFuncC(pat.Get("/mtg/cards/typeahead"), app.HandleTypeahead) 175 | mux.HandleFuncC(pat.Get("/mtg/cards/random"), app.HandleRandomCard) 176 | mux.HandleFuncC(pat.Get("/mtg/cards/:id"), app.HandleCard) 177 | mux.HandleFuncC(pat.Get("/mtg/sets"), app.HandleSets) 178 | mux.HandleFuncC(pat.Get("/mtg/sets/:id"), app.HandleSet) 179 | mux.HandleFuncC(pat.Get("/mtg/colors"), app.HandleTerm(client.GetColors)) 180 | mux.HandleFuncC(pat.Get("/mtg/supertypes"), app.HandleTerm(client.GetSupertypes)) 181 | mux.HandleFuncC(pat.Get("/mtg/subtypes"), app.HandleTerm(client.GetSubtypes)) 182 | mux.HandleFuncC(pat.Get("/mtg/types"), app.HandleTerm(client.GetTypes)) 183 | 184 | return mux 185 | } 186 | -------------------------------------------------------------------------------- /vendor/github.com/opentracing/opentracing-go/propagation.go: -------------------------------------------------------------------------------- 1 | package opentracing 2 | 3 | import ( 4 | "errors" 5 | "net/http" 6 | "net/url" 7 | ) 8 | 9 | /////////////////////////////////////////////////////////////////////////////// 10 | // CORE PROPAGATION INTERFACES: 11 | /////////////////////////////////////////////////////////////////////////////// 12 | 13 | var ( 14 | // ErrUnsupportedFormat occurs when the `format` passed to Tracer.Inject() or 15 | // Tracer.Join() is not recognized by the Tracer implementation. 16 | ErrUnsupportedFormat = errors.New("opentracing: Unknown or unsupported Inject/Join format") 17 | 18 | // ErrTraceNotFound occurs when the `carrier` passed to Tracer.Join() is 19 | // valid and uncorrupted but has insufficient information to join or resume 20 | // a trace. 21 | ErrTraceNotFound = errors.New("opentracing: Trace not found in Join carrier") 22 | 23 | // ErrInvalidSpan errors occur when Tracer.Inject() is asked to operate on 24 | // a Span which it is not prepared to handle (for example, since it was 25 | // created by a different tracer implementation). 26 | ErrInvalidSpan = errors.New("opentracing: Span type incompatible with tracer") 27 | 28 | // ErrInvalidCarrier errors occur when Tracer.Inject() or Tracer.Join() 29 | // implementations expect a different type of `carrier` than they are 30 | // given. 31 | ErrInvalidCarrier = errors.New("opentracing: Invalid Inject/Join carrier") 32 | 33 | // ErrTraceCorrupted occurs when the `carrier` passed to Tracer.Join() is 34 | // of the expected type but is corrupted. 35 | ErrTraceCorrupted = errors.New("opentracing: Trace data corrupted in Join carrier") 36 | ) 37 | 38 | /////////////////////////////////////////////////////////////////////////////// 39 | // BUILTIN PROPAGATION FORMATS: 40 | /////////////////////////////////////////////////////////////////////////////// 41 | 42 | // BuiltinFormat is used to demarcate the values within package `opentracing` 43 | // that are intended for use with the Tracer.Inject() and Tracer.Join() 44 | // methods. 45 | type BuiltinFormat byte 46 | 47 | const ( 48 | // Binary encodes the Span for propagation as opaque binary data. 49 | // 50 | // For Tracer.Inject(): the carrier must be an `io.Writer`. 51 | // 52 | // For Tracer.Join(): the carrier must be an `io.Reader`. 53 | Binary BuiltinFormat = iota 54 | 55 | // TextMap encodes the Span as key:value pairs. 56 | // 57 | // For Tracer.Inject(): the carrier must be a `TextMapWriter`. 58 | // 59 | // For Tracer.Join(): the carrier must be a `TextMapReader`. 60 | // 61 | // See HTTPHeaderTextMapCarrier for an implementation of both TextMapWriter 62 | // and TextMapReader that defers to an http.Header instance for storage. 63 | // For example, Inject(): 64 | // 65 | // carrier := HTTPHeaderTextMapCarrier(httpReq.Header) 66 | // err := span.Tracer().Inject(span, TextMap, carrier) 67 | // 68 | // Or Join(): 69 | // 70 | // carrier := HTTPHeaderTextMapCarrier(httpReq.Header) 71 | // span, err := tracer.Join("opName", TextMap, carrier) 72 | // 73 | TextMap 74 | ) 75 | 76 | // TextMapWriter is the Inject() carrier for the TextMap builtin format. With 77 | // it, the caller can encode a Span for propagation as entries in a multimap of 78 | // unicode strings. 79 | type TextMapWriter interface { 80 | // Set a key:value pair to the carrier. Multiple calls to Set() for the 81 | // same key leads to undefined behavior. 82 | // 83 | // NOTE: Since HTTP headers are a particularly important use case for the 84 | // TextMap carrier, `key` parameters identify their respective values in a 85 | // case-insensitive manner. 86 | // 87 | // NOTE: The backing store for the TextMapWriter may contain unrelated data 88 | // (e.g., arbitrary HTTP headers). As such, the TextMap writer and reader 89 | // should agree on a prefix or other convention to distinguish their 90 | // key:value pairs. 91 | Set(key, val string) 92 | } 93 | 94 | // TextMapReader is the Join() carrier for the TextMap builtin format. With it, 95 | // the caller can decode a propagated Span as entries in a multimap of unicode 96 | // strings. 97 | type TextMapReader interface { 98 | // ForeachKey returns TextMap contents via repeated calls to the `handler` 99 | // function. If any call to `handler` returns a non-nil error, ForeachKey 100 | // terminates and returns that error. 101 | // 102 | // NOTE: A single `key` may appear in multiple calls to `handler` for a 103 | // single `ForeachKey` invocation. 104 | // 105 | // NOTE: The ForeachKey handler *may* be invoked for keys not set by any 106 | // TextMap writer (e.g., totally unrelated HTTP headers). As such, the 107 | // TextMap writer and reader should agree on a prefix or other convention 108 | // to distinguish their key:value pairs. 109 | // 110 | // The "foreach" callback pattern reduces unnecessary copying in some cases 111 | // and also allows implementations to hold locks while the map is read. 112 | ForeachKey(handler func(key, val string) error) error 113 | } 114 | 115 | // HTTPHeaderTextMapCarrier satisfies both TextMapWriter and TextMapReader. 116 | // 117 | type HTTPHeaderTextMapCarrier http.Header 118 | 119 | // Set conforms to the TextMapWriter interface. 120 | func (c HTTPHeaderTextMapCarrier) Set(key, val string) { 121 | h := http.Header(c) 122 | h.Add(key, url.QueryEscape(val)) 123 | } 124 | 125 | // ForeachKey conforms to the TextMapReader interface. 126 | func (c HTTPHeaderTextMapCarrier) ForeachKey(handler func(key, val string) error) error { 127 | for k, vals := range c { 128 | for _, v := range vals { 129 | rawV, err := url.QueryUnescape(v) 130 | if err != nil { 131 | // We don't know if there was an error escaping an 132 | // OpenTracing-related header or something else; as such, we 133 | // continue rather than return the error. 134 | continue 135 | } 136 | if err = handler(k, rawV); err != nil { 137 | return err 138 | } 139 | } 140 | } 141 | return nil 142 | } 143 | -------------------------------------------------------------------------------- /vendor/github.com/lib/pq/copy.go: -------------------------------------------------------------------------------- 1 | package pq 2 | 3 | import ( 4 | "database/sql/driver" 5 | "encoding/binary" 6 | "errors" 7 | "sync/atomic" 8 | ) 9 | 10 | var ( 11 | errCopyInClosed = errors.New("pq: copyin statement has already been closed") 12 | errBinaryCopyNotSupported = errors.New("pq: only text format supported for COPY") 13 | errCopyToNotSupported = errors.New("pq: COPY TO is not supported") 14 | errCopyNotSupportedOutsideTxn = errors.New("pq: COPY is only allowed inside a transaction") 15 | ) 16 | 17 | // CopyIn creates a COPY FROM statement which can be prepared with 18 | // Tx.Prepare(). The target table should be visible in search_path. 19 | func CopyIn(table string, columns ...string) string { 20 | stmt := "COPY " + QuoteIdentifier(table) + " (" 21 | for i, col := range columns { 22 | if i != 0 { 23 | stmt += ", " 24 | } 25 | stmt += QuoteIdentifier(col) 26 | } 27 | stmt += ") FROM STDIN" 28 | return stmt 29 | } 30 | 31 | // CopyInSchema creates a COPY FROM statement which can be prepared with 32 | // Tx.Prepare(). 33 | func CopyInSchema(schema, table string, columns ...string) string { 34 | stmt := "COPY " + QuoteIdentifier(schema) + "." + QuoteIdentifier(table) + " (" 35 | for i, col := range columns { 36 | if i != 0 { 37 | stmt += ", " 38 | } 39 | stmt += QuoteIdentifier(col) 40 | } 41 | stmt += ") FROM STDIN" 42 | return stmt 43 | } 44 | 45 | type copyin struct { 46 | cn *conn 47 | buffer []byte 48 | rowData chan []byte 49 | done chan bool 50 | 51 | closed bool 52 | err error 53 | errorset int32 54 | } 55 | 56 | const ciBufferSize = 64 * 1024 57 | 58 | // flush buffer before the buffer is filled up and needs reallocation 59 | const ciBufferFlushSize = 63 * 1024 60 | 61 | func (cn *conn) prepareCopyIn(q string) (_ driver.Stmt, err error) { 62 | defer errRecover(&err) 63 | 64 | if !cn.isInTransaction() { 65 | return nil, errCopyNotSupportedOutsideTxn 66 | } 67 | 68 | ci := ©in{ 69 | cn: cn, 70 | buffer: make([]byte, 0, ciBufferSize), 71 | rowData: make(chan []byte), 72 | done: make(chan bool), 73 | } 74 | // add CopyData identifier + 4 bytes for message length 75 | ci.buffer = append(ci.buffer, 'd', 0, 0, 0, 0) 76 | 77 | b := cn.writeBuf('Q') 78 | b.string(q) 79 | cn.send(b) 80 | 81 | awaitCopyInResponse: 82 | for { 83 | t, r := cn.recv1() 84 | switch t { 85 | case 'G': 86 | if r.byte() != 0 { 87 | err = errBinaryCopyNotSupported 88 | break awaitCopyInResponse 89 | } 90 | go ci.resploop() 91 | return ci, nil 92 | case 'H': 93 | err = errCopyToNotSupported 94 | break awaitCopyInResponse 95 | case 'E': 96 | err = parseError(r) 97 | case 'Z': 98 | if err == nil { 99 | errorf("unexpected ReadyForQuery in response to COPY") 100 | } 101 | cn.processReadyForQuery(r) 102 | return nil, err 103 | default: 104 | errorf("unknown response for copy query: %q", t) 105 | } 106 | } 107 | 108 | // something went wrong, abort COPY before we return 109 | b = cn.writeBuf('f') 110 | b.string(err.Error()) 111 | cn.send(b) 112 | 113 | for { 114 | t, r := cn.recv1() 115 | switch t { 116 | case 'c', 'C', 'E': 117 | case 'Z': 118 | // correctly aborted, we're done 119 | cn.processReadyForQuery(r) 120 | return nil, err 121 | default: 122 | errorf("unknown response for CopyFail: %q", t) 123 | } 124 | } 125 | 126 | panic("not reached") 127 | } 128 | 129 | func (ci *copyin) flush(buf []byte) { 130 | // set message length (without message identifier) 131 | binary.BigEndian.PutUint32(buf[1:], uint32(len(buf)-1)) 132 | 133 | _, err := ci.cn.c.Write(buf) 134 | if err != nil { 135 | panic(err) 136 | } 137 | } 138 | 139 | func (ci *copyin) resploop() { 140 | for { 141 | t, r := ci.cn.recv1() 142 | switch t { 143 | case 'C': 144 | // complete 145 | case 'Z': 146 | ci.cn.processReadyForQuery(r) 147 | ci.done <- true 148 | return 149 | case 'E': 150 | err := parseError(r) 151 | ci.setError(err) 152 | default: 153 | errorf("unknown response: %q", t) 154 | } 155 | } 156 | } 157 | 158 | func (ci *copyin) isErrorSet() bool { 159 | return atomic.LoadInt32(&ci.errorset) != 0 160 | } 161 | 162 | func (ci *copyin) setError(err error) { 163 | ci.err = err 164 | atomic.StoreInt32(&ci.errorset, 1) 165 | } 166 | 167 | func (ci *copyin) NumInput() int { 168 | return -1 169 | } 170 | 171 | func (ci *copyin) Query(v []driver.Value) (r driver.Rows, err error) { 172 | return nil, ErrNotSupported 173 | } 174 | 175 | // Exec inserts values into the COPY stream. The insert is asynchronous 176 | // and Exec can return errors from previous Exec calls to the same 177 | // COPY stmt. 178 | // 179 | // You need to call Exec(nil) to sync the COPY stream and to get any 180 | // errors from pending data, since Stmt.Close() doesn't return errors 181 | // to the user. 182 | func (ci *copyin) Exec(v []driver.Value) (r driver.Result, err error) { 183 | defer errRecover(&err) 184 | 185 | if ci.closed { 186 | return nil, errCopyInClosed 187 | } 188 | 189 | if ci.isErrorSet() { 190 | return nil, ci.err 191 | } 192 | 193 | if len(v) == 0 { 194 | err = ci.Close() 195 | ci.closed = true 196 | return nil, err 197 | } 198 | 199 | numValues := len(v) 200 | for i, value := range v { 201 | ci.buffer = appendEncodedText(&ci.cn.parameterStatus, ci.buffer, value) 202 | if i < numValues-1 { 203 | ci.buffer = append(ci.buffer, '\t') 204 | } 205 | } 206 | 207 | ci.buffer = append(ci.buffer, '\n') 208 | 209 | if len(ci.buffer) > ciBufferFlushSize { 210 | ci.flush(ci.buffer) 211 | // reset buffer, keep bytes for message identifier and length 212 | ci.buffer = ci.buffer[:5] 213 | } 214 | 215 | return driver.RowsAffected(0), nil 216 | } 217 | 218 | func (ci *copyin) Close() (err error) { 219 | defer errRecover(&err) 220 | 221 | if ci.closed { 222 | return errCopyInClosed 223 | } 224 | 225 | if len(ci.buffer) > 0 { 226 | ci.flush(ci.buffer) 227 | } 228 | // Avoid touching the scratch buffer as resploop could be using it. 229 | err = ci.cn.sendSimpleMessage('c') 230 | if err != nil { 231 | return err 232 | } 233 | 234 | <-ci.done 235 | 236 | if ci.isErrorSet() { 237 | err = ci.err 238 | return err 239 | } 240 | return nil 241 | } 242 | -------------------------------------------------------------------------------- /vendor/stackmachine.com/cql/db.go: -------------------------------------------------------------------------------- 1 | package cql 2 | 3 | import ( 4 | "database/sql" 5 | 6 | ot "github.com/opentracing/opentracing-go" 7 | "golang.org/x/net/context" 8 | ) 9 | 10 | type DB struct { 11 | *sql.DB 12 | } 13 | 14 | func Open(driverName, dataSourceName string) (*DB, error) { 15 | db, err := sql.Open(driverName, dataSourceName) 16 | return &DB{db}, err 17 | } 18 | 19 | type Tx struct { 20 | *sql.Tx 21 | } 22 | 23 | type Stmt struct { 24 | *sql.Stmt 25 | } 26 | 27 | type Row struct { 28 | ctxerr error 29 | *sql.Row 30 | } 31 | 32 | func (r *Row) Scan(dest ...interface{}) error { 33 | if r.ctxerr != nil { 34 | return r.ctxerr 35 | } 36 | return r.Row.Scan(dest...) 37 | } 38 | 39 | func Wrap(db *sql.DB) *DB { 40 | return &DB{db} 41 | } 42 | 43 | func (db *DB) PingC(ctx context.Context) error { 44 | span, _ := ot.StartSpanFromContext(ctx, "Ping") 45 | defer span.Finish() 46 | 47 | result := make(chan error, 1) 48 | 49 | go func() { 50 | result <- db.Ping() 51 | }() 52 | 53 | select { 54 | case <-ctx.Done(): 55 | return ctx.Err() 56 | case err := <-result: 57 | return err 58 | } 59 | } 60 | 61 | func (db *DB) BeginC(ctx context.Context) (*Tx, error) { 62 | span, _ := ot.StartSpanFromContext(ctx, "BeginC") 63 | defer span.Finish() 64 | 65 | type txAndError struct { 66 | tx *sql.Tx 67 | err error 68 | } 69 | result := make(chan txAndError, 1) 70 | 71 | go func() { 72 | tx, err := db.Begin() 73 | result <- txAndError{tx, err} 74 | }() 75 | 76 | select { 77 | case <-ctx.Done(): 78 | go func() { 79 | if r := <-result; r.tx != nil { 80 | r.tx.Rollback() 81 | } 82 | }() 83 | return nil, ctx.Err() 84 | case r := <-result: 85 | return &Tx{r.tx}, r.err 86 | } 87 | } 88 | 89 | type preparer interface { 90 | Prepare(query string) (*sql.Stmt, error) 91 | } 92 | 93 | func prepare(p preparer, ctx context.Context, query string) (*Stmt, error) { 94 | type stmtAndError struct { 95 | stmt *sql.Stmt 96 | err error 97 | } 98 | result := make(chan stmtAndError, 1) 99 | 100 | go func() { 101 | s, err := p.Prepare(query) 102 | result <- stmtAndError{s, err} 103 | }() 104 | 105 | select { 106 | case <-ctx.Done(): 107 | go func() { 108 | if r := <-result; r.stmt != nil { 109 | r.stmt.Close() 110 | } 111 | }() 112 | return nil, ctx.Err() 113 | case r := <-result: 114 | return &Stmt{r.stmt}, r.err 115 | } 116 | } 117 | 118 | func (db *DB) PrepareC(ctx context.Context, query string) (*Stmt, error) { 119 | span, nctx := ot.StartSpanFromContext(ctx, "PrepareC") 120 | defer span.Finish() 121 | return prepare(db, nctx, query) 122 | } 123 | 124 | func (tx *Tx) PrepareC(ctx context.Context, query string) (*Stmt, error) { 125 | span, nctx := ot.StartSpanFromContext(ctx, "PrepareC") 126 | defer span.Finish() 127 | return prepare(tx, nctx, query) 128 | } 129 | 130 | func execute(ctx context.Context, exec func() (sql.Result, error)) (sql.Result, error) { 131 | type resultAndError struct { 132 | result sql.Result 133 | err error 134 | } 135 | result := make(chan resultAndError, 1) 136 | 137 | go func() { 138 | r, err := exec() 139 | result <- resultAndError{r, err} 140 | }() 141 | 142 | select { 143 | case <-ctx.Done(): 144 | var res sql.Result 145 | return res, ctx.Err() 146 | case r := <-result: 147 | return r.result, r.err 148 | } 149 | } 150 | 151 | func (db *DB) ExecC(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { 152 | span, nctx := ot.StartSpanFromContext(ctx, "ExecC") 153 | defer span.Finish() 154 | return execute(nctx, func() (sql.Result, error) { return db.Exec(query, args...) }) 155 | } 156 | 157 | func (s *Stmt) ExecC(ctx context.Context, args ...interface{}) (sql.Result, error) { 158 | span, nctx := ot.StartSpanFromContext(ctx, "ExecC") 159 | defer span.Finish() 160 | return execute(nctx, func() (sql.Result, error) { return s.Exec(args...) }) 161 | } 162 | 163 | func (tx *Tx) ExecC(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { 164 | span, nctx := ot.StartSpanFromContext(ctx, "ExecC") 165 | defer span.Finish() 166 | return execute(nctx, func() (sql.Result, error) { return tx.Exec(query, args...) }) 167 | } 168 | 169 | func query(ctx context.Context, query func() (*sql.Rows, error)) (*sql.Rows, error) { 170 | type rowsAndError struct { 171 | rows *sql.Rows 172 | err error 173 | } 174 | result := make(chan rowsAndError, 1) 175 | 176 | go func() { 177 | s, err := query() 178 | result <- rowsAndError{s, err} 179 | }() 180 | 181 | select { 182 | case <-ctx.Done(): 183 | go func() { 184 | if r := <-result; r.rows != nil { 185 | r.rows.Close() 186 | } 187 | }() 188 | return nil, ctx.Err() 189 | case r := <-result: 190 | return r.rows, r.err 191 | } 192 | } 193 | 194 | func (db *DB) QueryC(ctx context.Context, q string, args ...interface{}) (*sql.Rows, error) { 195 | span, nctx := ot.StartSpanFromContext(ctx, "QueryC") 196 | defer span.Finish() 197 | return query(nctx, func() (*sql.Rows, error) { return db.Query(q, args...) }) 198 | } 199 | 200 | func (s *Stmt) QueryC(ctx context.Context, args ...interface{}) (*sql.Rows, error) { 201 | span, nctx := ot.StartSpanFromContext(ctx, "QueryC") 202 | defer span.Finish() 203 | return query(nctx, func() (*sql.Rows, error) { return s.Query(args...) }) 204 | } 205 | 206 | func (tx *Tx) QueryC(ctx context.Context, q string, args ...interface{}) (*sql.Rows, error) { 207 | span, nctx := ot.StartSpanFromContext(ctx, "QueryC") 208 | defer span.Finish() 209 | return query(nctx, func() (*sql.Rows, error) { return tx.Query(q, args...) }) 210 | } 211 | 212 | func queryrow(ctx context.Context, row func() *sql.Row) *Row { 213 | result := make(chan *sql.Row, 1) 214 | 215 | go func() { 216 | result <- row() 217 | }() 218 | 219 | select { 220 | case <-ctx.Done(): 221 | go func() { 222 | r := <-result 223 | // We call Scan here to make sure that the underlying Rows struct is closed 224 | r.Scan() 225 | }() 226 | return &Row{Row: nil, ctxerr: ctx.Err()} 227 | case r := <-result: 228 | return &Row{Row: r, ctxerr: nil} 229 | } 230 | } 231 | 232 | func (db *DB) QueryRowC(ctx context.Context, query string, args ...interface{}) *Row { 233 | span, nctx := ot.StartSpanFromContext(ctx, "QueryRowC") 234 | defer span.Finish() 235 | return queryrow(nctx, func() *sql.Row { return db.QueryRow(query, args...) }) 236 | } 237 | 238 | func (s *Stmt) QueryRowC(ctx context.Context, args ...interface{}) *Row { 239 | span, nctx := ot.StartSpanFromContext(ctx, "QueryRowC") 240 | defer span.Finish() 241 | return queryrow(nctx, func() *sql.Row { return s.QueryRow(args...) }) 242 | } 243 | 244 | func (tx *Tx) QueryRowC(ctx context.Context, query string, args ...interface{}) *Row { 245 | span, nctx := ot.StartSpanFromContext(ctx, "QueryRowC") 246 | defer span.Finish() 247 | return queryrow(nctx, func() *sql.Row { return tx.QueryRow(query, args...) }) 248 | } 249 | -------------------------------------------------------------------------------- /vendor/code.google.com/p/go.net/html/escape.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package html 6 | 7 | import ( 8 | "bytes" 9 | "strings" 10 | "unicode/utf8" 11 | ) 12 | 13 | // These replacements permit compatibility with old numeric entities that 14 | // assumed Windows-1252 encoding. 15 | // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#consume-a-character-reference 16 | var replacementTable = [...]rune{ 17 | '\u20AC', // First entry is what 0x80 should be replaced with. 18 | '\u0081', 19 | '\u201A', 20 | '\u0192', 21 | '\u201E', 22 | '\u2026', 23 | '\u2020', 24 | '\u2021', 25 | '\u02C6', 26 | '\u2030', 27 | '\u0160', 28 | '\u2039', 29 | '\u0152', 30 | '\u008D', 31 | '\u017D', 32 | '\u008F', 33 | '\u0090', 34 | '\u2018', 35 | '\u2019', 36 | '\u201C', 37 | '\u201D', 38 | '\u2022', 39 | '\u2013', 40 | '\u2014', 41 | '\u02DC', 42 | '\u2122', 43 | '\u0161', 44 | '\u203A', 45 | '\u0153', 46 | '\u009D', 47 | '\u017E', 48 | '\u0178', // Last entry is 0x9F. 49 | // 0x00->'\uFFFD' is handled programmatically. 50 | // 0x0D->'\u000D' is a no-op. 51 | } 52 | 53 | // unescapeEntity reads an entity like "<" from b[src:] and writes the 54 | // corresponding "<" to b[dst:], returning the incremented dst and src cursors. 55 | // Precondition: b[src] == '&' && dst <= src. 56 | // attribute should be true if parsing an attribute value. 57 | func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { 58 | // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#consume-a-character-reference 59 | 60 | // i starts at 1 because we already know that s[0] == '&'. 61 | i, s := 1, b[src:] 62 | 63 | if len(s) <= 1 { 64 | b[dst] = b[src] 65 | return dst + 1, src + 1 66 | } 67 | 68 | if s[i] == '#' { 69 | if len(s) <= 3 { // We need to have at least "&#.". 70 | b[dst] = b[src] 71 | return dst + 1, src + 1 72 | } 73 | i++ 74 | c := s[i] 75 | hex := false 76 | if c == 'x' || c == 'X' { 77 | hex = true 78 | i++ 79 | } 80 | 81 | x := '\x00' 82 | for i < len(s) { 83 | c = s[i] 84 | i++ 85 | if hex { 86 | if '0' <= c && c <= '9' { 87 | x = 16*x + rune(c) - '0' 88 | continue 89 | } else if 'a' <= c && c <= 'f' { 90 | x = 16*x + rune(c) - 'a' + 10 91 | continue 92 | } else if 'A' <= c && c <= 'F' { 93 | x = 16*x + rune(c) - 'A' + 10 94 | continue 95 | } 96 | } else if '0' <= c && c <= '9' { 97 | x = 10*x + rune(c) - '0' 98 | continue 99 | } 100 | if c != ';' { 101 | i-- 102 | } 103 | break 104 | } 105 | 106 | if i <= 3 { // No characters matched. 107 | b[dst] = b[src] 108 | return dst + 1, src + 1 109 | } 110 | 111 | if 0x80 <= x && x <= 0x9F { 112 | // Replace characters from Windows-1252 with UTF-8 equivalents. 113 | x = replacementTable[x-0x80] 114 | } else if x == 0 || (0xD800 <= x && x <= 0xDFFF) || x > 0x10FFFF { 115 | // Replace invalid characters with the replacement character. 116 | x = '\uFFFD' 117 | } 118 | 119 | return dst + utf8.EncodeRune(b[dst:], x), src + i 120 | } 121 | 122 | // Consume the maximum number of characters possible, with the 123 | // consumed characters matching one of the named references. 124 | 125 | for i < len(s) { 126 | c := s[i] 127 | i++ 128 | // Lower-cased characters are more common in entities, so we check for them first. 129 | if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' { 130 | continue 131 | } 132 | if c != ';' { 133 | i-- 134 | } 135 | break 136 | } 137 | 138 | entityName := string(s[1:i]) 139 | if entityName == "" { 140 | // No-op. 141 | } else if attribute && entityName[len(entityName)-1] != ';' && len(s) > i && s[i] == '=' { 142 | // No-op. 143 | } else if x := entity[entityName]; x != 0 { 144 | return dst + utf8.EncodeRune(b[dst:], x), src + i 145 | } else if x := entity2[entityName]; x[0] != 0 { 146 | dst1 := dst + utf8.EncodeRune(b[dst:], x[0]) 147 | return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i 148 | } else if !attribute { 149 | maxLen := len(entityName) - 1 150 | if maxLen > longestEntityWithoutSemicolon { 151 | maxLen = longestEntityWithoutSemicolon 152 | } 153 | for j := maxLen; j > 1; j-- { 154 | if x := entity[entityName[:j]]; x != 0 { 155 | return dst + utf8.EncodeRune(b[dst:], x), src + j + 1 156 | } 157 | } 158 | } 159 | 160 | dst1, src1 = dst+i, src+i 161 | copy(b[dst:dst1], b[src:src1]) 162 | return dst1, src1 163 | } 164 | 165 | // unescape unescapes b's entities in-place, so that "a<b" becomes "a': 214 | esc = ">" 215 | case '"': 216 | // """ is shorter than """. 217 | esc = """ 218 | case '\r': 219 | esc = " " 220 | default: 221 | panic("unrecognized escape character") 222 | } 223 | s = s[i+1:] 224 | if _, err := w.WriteString(esc); err != nil { 225 | return err 226 | } 227 | i = strings.IndexAny(s, escapedChars) 228 | } 229 | _, err := w.WriteString(s) 230 | return err 231 | } 232 | 233 | // EscapeString escapes special characters like "<" to become "<". It 234 | // escapes only five such characters: <, >, &, ' and ". 235 | // UnescapeString(EscapeString(s)) == s always holds, but the converse isn't 236 | // always true. 237 | func EscapeString(s string) string { 238 | if strings.IndexAny(s, escapedChars) == -1 { 239 | return s 240 | } 241 | var buf bytes.Buffer 242 | escape(&buf, s) 243 | return buf.String() 244 | } 245 | 246 | // UnescapeString unescapes entities like "<" to become "<". It unescapes a 247 | // larger range of entities than EscapeString escapes. For example, "á" 248 | // unescapes to "á", as does "á" and "&xE1;". 249 | // UnescapeString(EscapeString(s)) == s always holds, but the converse isn't 250 | // always true. 251 | func UnescapeString(s string) string { 252 | for _, c := range s { 253 | if c == '&' { 254 | return string(unescape([]byte(s), false)) 255 | } 256 | } 257 | return s 258 | } 259 | -------------------------------------------------------------------------------- /vendor/github.com/opentracing/opentracing-go/span.go: -------------------------------------------------------------------------------- 1 | package opentracing 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | "time" 7 | ) 8 | 9 | // Span represents an active, un-finished span in the OpenTracing system. 10 | // 11 | // Spans are created by the Tracer interface. 12 | type Span interface { 13 | // Sets or changes the operation name. 14 | SetOperationName(operationName string) Span 15 | 16 | // Adds a tag to the span. 17 | // 18 | // Tag values can be of arbitrary types, however the treatment of complex 19 | // types is dependent on the underlying tracing system implementation. 20 | // It is expected that most tracing systems will handle primitive types 21 | // like strings and numbers. If a tracing system cannot understand how 22 | // to handle a particular value type, it may ignore the tag, but shall 23 | // not panic. 24 | // 25 | // If there is a pre-existing tag set for `key`, it is overwritten. 26 | SetTag(key string, value interface{}) Span 27 | 28 | // Sets the end timestamp and calls the `Recorder`s RecordSpan() 29 | // internally. 30 | // 31 | // Finish() should be the last call made to any span instance, and to do 32 | // otherwise leads to undefined behavior. 33 | Finish() 34 | // FinishWithOptions is like Finish() but with explicit control over 35 | // timestamps and log data. 36 | FinishWithOptions(opts FinishOptions) 37 | 38 | // LogEvent() is equivalent to 39 | // 40 | // Log(LogData{Event: event}) 41 | // 42 | LogEvent(event string) 43 | 44 | // LogEventWithPayload() is equivalent to 45 | // 46 | // Log(LogData{Event: event, Payload: payload0}) 47 | // 48 | LogEventWithPayload(event string, payload interface{}) 49 | 50 | // Log() records `data` to this Span. 51 | // 52 | // See LogData for semantic details. 53 | Log(data LogData) 54 | 55 | // SetBaggageItem sets a key:value pair on this Span that also 56 | // propagates to future Span children. 57 | // 58 | // SetBaggageItem() enables powerful functionality given a full-stack 59 | // opentracing integration (e.g., arbitrary application data from a mobile 60 | // app can make it, transparently, all the way into the depths of a storage 61 | // system), and with it some powerful costs: use this feature with care. 62 | // 63 | // IMPORTANT NOTE #1: SetBaggageItem() will only propagate trace 64 | // baggage items to *future* children of the Span. 65 | // 66 | // IMPORTANT NOTE #2: Use this thoughtfully and with care. Every key and 67 | // value is copied into every local *and remote* child of this Span, and 68 | // that can add up to a lot of network and cpu overhead. 69 | // 70 | // IMPORTANT NOTE #3: Baggage item keys have a restricted format: 71 | // implementations may wish to use them as HTTP header keys (or key 72 | // suffixes), and of course HTTP headers are case insensitive. 73 | // 74 | // As such, `restrictedKey` MUST match the regular expression 75 | // `(?i:[a-z0-9][-a-z0-9]*)` and is case-insensitive. That is, it must 76 | // start with a letter or number, and the remaining characters must be 77 | // letters, numbers, or hyphens. See CanonicalizeBaggageKey(). If 78 | // `restrictedKey` does not meet these criteria, SetBaggageItem() 79 | // results in undefined behavior. 80 | // 81 | // Returns a reference to this Span for chaining, etc. 82 | SetBaggageItem(restrictedKey, value string) Span 83 | 84 | // Gets the value for a baggage item given its key. Returns the empty string 85 | // if the value isn't found in this Span. 86 | // 87 | // See the `SetBaggageItem` notes about `restrictedKey`. 88 | BaggageItem(restrictedKey string) string 89 | 90 | // Provides access to the Tracer that created this Span. 91 | Tracer() Tracer 92 | } 93 | 94 | // LogData is data associated to a Span. Every LogData instance should specify 95 | // at least one of Event and/or Payload. 96 | type LogData struct { 97 | // The timestamp of the log record; if set to the default value (the unix 98 | // epoch), implementations should use time.Now() implicitly. 99 | Timestamp time.Time 100 | 101 | // Event (if non-empty) should be the stable name of some notable moment in 102 | // the lifetime of a Span. For instance, a Span representing a browser page 103 | // load might add an Event for each of the Performance.timing moments 104 | // here: https://developer.mozilla.org/en-US/docs/Web/API/PerformanceTiming 105 | // 106 | // While it is not a formal requirement, Event strings will be most useful 107 | // if they are *not* unique; rather, tracing systems should be able to use 108 | // them to understand how two similar Spans relate from an internal timing 109 | // perspective. 110 | Event string 111 | 112 | // Payload is a free-form potentially structured object which Tracer 113 | // implementations may retain and record all, none, or part of. 114 | // 115 | // If included, `Payload` should be restricted to data derived from the 116 | // instrumented application; in particular, it should not be used to pass 117 | // semantic flags to a Log() implementation. 118 | // 119 | // For example, an RPC system could log the wire contents in both 120 | // directions, or a SQL library could log the query (with or without 121 | // parameter bindings); tracing implementations may truncate or otherwise 122 | // record only a snippet of these payloads (or may strip out PII, etc, 123 | // etc). 124 | Payload interface{} 125 | } 126 | 127 | // FinishOptions allows Span.FinishWithOptions callers to override the finish 128 | // timestamp and provide log data via a bulk interface. 129 | type FinishOptions struct { 130 | // FinishTime overrides the Span's finish time, or implicitly becomes 131 | // time.Now() if FinishTime.IsZero(). 132 | // 133 | // FinishTime must resolve to a timestamp that's >= the Span's StartTime 134 | // (per StartSpanOptions). 135 | FinishTime time.Time 136 | 137 | // BulkLogData allows the caller to specify the contents of many Log() 138 | // calls with a single slice. May be nil. 139 | // 140 | // None of the LogData.Timestamp values may be .IsZero() (i.e., they must 141 | // be set explicitly). Also, they must be >= the Span's start timestamp and 142 | // <= the FinishTime (or time.Now() if FinishTime.IsZero()). Otherwise the 143 | // behavior of FinishWithOptions() is undefined. 144 | // 145 | // If specified, the caller hands off ownership of BulkLogData at 146 | // FinishWithOptions() invocation time. 147 | BulkLogData []LogData 148 | } 149 | 150 | // Tags are a generic map from an arbitrary string key to an opaque value type. 151 | // The underlying tracing system is responsible for interpreting and 152 | // serializing the values. 153 | type Tags map[string]interface{} 154 | 155 | // Merge incorporates the keys and values from `other` into this `Tags` 156 | // instance, then returns same. 157 | func (t Tags) Merge(other Tags) Tags { 158 | for k, v := range other { 159 | t[k] = v 160 | } 161 | return t 162 | } 163 | 164 | var regexBaggage = regexp.MustCompile("^(?i:[a-z0-9][-a-z0-9]*)$") 165 | 166 | // CanonicalizeBaggageKey returns the canonicalized version of baggage item 167 | // key `key`, and true if and only if the key was valid. 168 | // 169 | // It is more performant to use lowercase keys only. 170 | func CanonicalizeBaggageKey(key string) (string, bool) { 171 | if !regexBaggage.MatchString(key) { 172 | return "", false 173 | } 174 | return strings.ToLower(key), true 175 | } 176 | 177 | // StartChildSpan is a simple helper to start a child span given only its parent (per StartSpanOptions.Parent) and an operation name per Span.SetOperationName. 178 | func StartChildSpan(parent Span, operationName string) Span { 179 | return parent.Tracer().StartSpanWithOptions(StartSpanOptions{ 180 | OperationName: operationName, 181 | Parent: parent, 182 | }) 183 | } 184 | --------------------------------------------------------------------------------