├── .gitignore ├── .godir ├── Dockerfile ├── Godeps ├── Godeps.json └── Readme ├── README.md ├── cmd ├── timer │ └── main.go └── webapp │ ├── main.go │ └── webapp ├── commands.png ├── commands ├── commands.go └── commands_test.go ├── config └── config.go ├── configs.toml.sample ├── docker-compose.yml ├── handlers └── handlers.go ├── migrations ├── 20170709002322-create_reminders.sql └── 20170709002410-add_due_dt.sql ├── router └── router.go └── vendor ├── github.com ├── BurntSushi │ └── toml │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── COMPATIBLE │ │ ├── COPYING │ │ ├── Makefile │ │ ├── README.md │ │ ├── decode.go │ │ ├── decode_meta.go │ │ ├── doc.go │ │ ├── encode.go │ │ ├── encoding_types.go │ │ ├── encoding_types_1.1.go │ │ ├── lex.go │ │ ├── parse.go │ │ ├── session.vim │ │ ├── type_check.go │ │ └── type_fields.go ├── gorilla │ └── context │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ ├── context.go │ │ └── doc.go ├── jasonlvhit │ └── gocron │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── README.md │ │ └── gocron.go ├── jinzhu │ └── now │ │ ├── Guardfile │ │ ├── README.md │ │ ├── main.go │ │ └── now.go ├── julienschmidt │ └── httprouter │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ ├── path.go │ │ ├── router.go │ │ └── tree.go ├── justinas │ └── alice │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ └── chain.go └── mattn │ └── go-sqlite3 │ ├── .gitignore │ ├── .travis.yml │ ├── LICENSE │ ├── README.md │ ├── backup.go │ ├── callback.go │ ├── doc.go │ ├── error.go │ ├── sqlite3-binding.c │ ├── sqlite3-binding.h │ ├── sqlite3.go │ ├── sqlite3_fts5.go │ ├── sqlite3_go18.go │ ├── sqlite3_icu.go │ ├── sqlite3_json1.go │ ├── sqlite3_libsqlite3.go │ ├── sqlite3_load_extension.go │ ├── sqlite3_omit_load_extension.go │ ├── sqlite3_other.go │ ├── sqlite3_type.go │ ├── sqlite3_windows.go │ ├── sqlite3ext.h │ ├── tracecallback.go │ └── tracecallback_noimpl.go └── golang.org └── x └── net ├── AUTHORS ├── CONTRIBUTORS ├── LICENSE ├── PATENTS └── context ├── context.go ├── go17.go └── pre_go17.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.toml 2 | docker/*.toml 3 | reminders.db 4 | dbconfig.yml 5 | test.go 6 | -------------------------------------------------------------------------------- /.godir: -------------------------------------------------------------------------------- 1 | github.com/aranair/remindbot 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.9 2 | 3 | ADD configs.toml /go/bin/ 4 | 5 | ADD . /go/src/github.com/aranair/remindbot 6 | WORKDIR /go/src/github.com/aranair/remindbot 7 | 8 | # RUN go get ./... 9 | RUN go get github.com/tools/godep 10 | RUN godep restore 11 | RUN go install ./... 12 | 13 | WORKDIR /go/src/github.com/aranair/remindbot 14 | WORKDIR /go/bin/ 15 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/aranair/remindbot", 3 | "GoVersion": "go1.6", 4 | "GodepVersion": "v79", 5 | "Packages": [ 6 | "./..." 7 | ], 8 | "Deps": [ 9 | { 10 | "ImportPath": "github.com/BurntSushi/toml", 11 | "Comment": "v0.2.0-21-g9906417", 12 | "Rev": "99064174e013895bbd9b025c31100bd1d9b590ca" 13 | }, 14 | { 15 | "ImportPath": "github.com/gorilla/context", 16 | "Comment": "v1.1-7-g08b5f42", 17 | "Rev": "08b5f424b9271eedf6f9f0ce86cb9396ed337a42" 18 | }, 19 | { 20 | "ImportPath": "github.com/jasonlvhit/gocron", 21 | "Rev": "42a5804d37aa0b9239b265e894a16b8edbf52d54" 22 | }, 23 | { 24 | "ImportPath": "github.com/jinzhu/now", 25 | "Rev": "d939ba741945c047cac69c329c5fb0d6b4a06520" 26 | }, 27 | { 28 | "ImportPath": "github.com/julienschmidt/httprouter", 29 | "Comment": "v1.1-40-g5dd70ee", 30 | "Rev": "5dd70ee059943e81987a817fa1a755b11dd119c1" 31 | }, 32 | { 33 | "ImportPath": "github.com/justinas/alice", 34 | "Comment": "1.0.0-16-g1051eaf", 35 | "Rev": "1051eaf52fcafdd87ead59d28b065f1fcb8274ec" 36 | }, 37 | { 38 | "ImportPath": "github.com/mattn/go-sqlite3", 39 | "Comment": "v1.2.0-10-g2d44dec", 40 | "Rev": "2d44decb4941c9cdf72c22297b7890faf7da9bcb" 41 | }, 42 | { 43 | "ImportPath": "golang.org/x/net/context", 44 | "Rev": "45e771701b814666a7eb299e6c7a57d0b1799e91" 45 | } 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Hazel 2 | ------ 3 | 4 | ### What is this? 5 | 6 | - A Telegram Bot written in Golang - Parses messages and records reminders. 7 | - A Cron-like Bot - Checks for overdue reminders periodically. 8 | - Docker / Docker-Compose 9 | - Sqlite3 10 | - Nginx / Self-Signed SSL Cert 11 | - Digital Ocean 12 | - Github hooks for deployment 13 | 14 | ### Walkthrough of Code 15 | 16 | - Part 1: [https://aranair.github.io/posts/2016/12/25/how-to-set-up-golang-telegram-bot-with-webhooks/][1] 17 | - Part 2: [https://aranair.github.io/posts/2017/01/21/how-i-deployed-golang-bot-on-digital-ocean/][2] 18 | - Part 3: [https://aranair.github.io/posts/2017/08/20/golang-telegram-bot-migrations-cronjobs-and-refactors/][3] 19 | 20 | ### See It in Action 21 | 22 | ![Commands!](https://github.com/aranair/remindbot/blob/master/commands.png?raw=true "Commands") 23 | 24 | ### Commands 25 | 26 | - Hazel, anything: `replies with korean Hello :P` 27 | - remind buy dinner 28 | - remind me to do this and this 29 | - remind me to sleep :9jul 10pm 30 | - remind me to buy chocolate :today 10pm 31 | - remind me to buy a gift :tomorrow 10pm 32 | - clear 2 33 | - clearall 34 | - list 35 | 36 | ### Resetting Item Number for Resetting 37 | 38 | - Unfortunately will deprecate support for multi-tenant bots but I've enabled this for personal use. 39 | 40 | ### How to Run? 41 | 42 | - Create a `configs.toml` file 43 | - `docker-compose up` or `go run cmd/webapp/main.go` 44 | 45 | ### How to Deploy? 46 | 47 | - Set up git hooks in production 48 | - `git push production master` 49 | 50 | Sample post-receive hook 51 | 52 | ```bash 53 | #!/bin/sh 54 | 55 | git --work-tree=/var/app/remindbot --git-dir=/var/repo/site.git checkout -f 56 | cd /var/app/remindbot 57 | docker-compose build 58 | docker-compose down 59 | docker-compose -d 60 | ``` 61 | 62 | ### External Package Dependencies 63 | 64 | - No Telegram-"api" packages were used, just some regex and http. 65 | - github.com/mattn/go-sqlite3 66 | - github.com/BurntSushi/toml 67 | - github.com/justinas/alice 68 | - github.com/gorilla/context 69 | - github.com/julienschmidt/httprouter 70 | 71 | ### License 72 | 73 | MIT 74 | 75 | [1]: https://aranair.github.io/posts/2016/12/25/how-to-set-up-golang-telegram-bot-with-webhooks/ 76 | [2]: https://aranair.github.io/posts/2017/01/21/how-i-deployed-golang-bot-on-digital-ocean/ 77 | [3]: https://aranair.github.io/posts/2017/08/20/golang-telegram-bot-migrations-cronjobs-and-refactors/ 78 | -------------------------------------------------------------------------------- /cmd/timer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/aranair/remindbot/commands" 7 | "github.com/aranair/remindbot/config" 8 | "github.com/aranair/remindbot/handlers" 9 | 10 | "database/sql" 11 | _ "github.com/mattn/go-sqlite3" 12 | 13 | "github.com/BurntSushi/toml" 14 | "github.com/jasonlvhit/gocron" 15 | ) 16 | 17 | func main() { 18 | var conf config.Config 19 | 20 | _, err := toml.DecodeFile("configs.toml", &conf) 21 | checkErr(err) 22 | 23 | fmt.Println(conf) 24 | db := initDB(conf.DB.Datapath) 25 | defer db.Close() 26 | 27 | ac := handlers.NewAppContext(db, conf, commands.NewCommandList()) 28 | gocron.Every(5).Minutes().Do(ac.CheckDue, conf.BOT.MainChatId, true) 29 | fmt.Println("Starting timer") 30 | <-gocron.Start() 31 | } 32 | 33 | func initDB(datapath string) *sql.DB { 34 | db, err := sql.Open("sqlite3", datapath+"/reminders.db") 35 | checkErr(err) 36 | return db 37 | } 38 | 39 | func checkErr(err error) { 40 | if err != nil { 41 | panic(err) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /cmd/webapp/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/aranair/remindbot/commands" 8 | "github.com/aranair/remindbot/config" 9 | "github.com/aranair/remindbot/handlers" 10 | 11 | router "github.com/aranair/remindbot/router" 12 | 13 | "database/sql" 14 | _ "github.com/mattn/go-sqlite3" 15 | 16 | "github.com/BurntSushi/toml" 17 | "github.com/justinas/alice" 18 | ) 19 | 20 | func task(ac handlers.AppContext, chatId int64, text string) { 21 | ac.SendText(chatId, text) 22 | } 23 | 24 | func main() { 25 | var conf config.Config 26 | 27 | _, err := toml.DecodeFile("configs.toml", &conf) 28 | checkErr(err) 29 | 30 | fmt.Println(conf) 31 | db := initDB(conf.DB.Datapath) 32 | defer db.Close() 33 | 34 | ac := handlers.NewAppContext(db, conf, commands.NewCommandList()) 35 | 36 | stack := alice.New() 37 | 38 | r := router.New() 39 | r.POST("/reminders", stack.ThenFunc(ac.CommandHandler)) 40 | 41 | http.ListenAndServe(":8080", r) 42 | fmt.Println("Server starting at port 8080.") 43 | } 44 | 45 | func initDB(datapath string) *sql.DB { 46 | db, err := sql.Open("sqlite3", datapath+"/reminders.db") 47 | checkErr(err) 48 | return db 49 | } 50 | 51 | func checkErr(err error) { 52 | if err != nil { 53 | panic(err) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /cmd/webapp/webapp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aranair/remindbot/a5b0df041c4a8b0ea686a023b2242b11a2437e64/cmd/webapp/webapp -------------------------------------------------------------------------------- /commands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aranair/remindbot/a5b0df041c4a8b0ea686a023b2242b11a2437e64/commands.png -------------------------------------------------------------------------------- /commands/commands.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "regexp" 5 | s "strings" 6 | "time" 7 | 8 | "github.com/jinzhu/now" 9 | ) 10 | 11 | type Commands struct { 12 | rmt *regexp.Regexp 13 | cd *regexp.Regexp 14 | r *regexp.Regexp 15 | l *regexp.Regexp 16 | rn *regexp.Regexp 17 | c *regexp.Regexp 18 | cl *regexp.Regexp 19 | hazel *regexp.Regexp 20 | } 21 | 22 | func NewCommandList() Commands { 23 | now.TimeFormats = append(now.TimeFormats, "2Jan 15:04 2006") 24 | now.TimeFormats = append(now.TimeFormats, "2Jan 3:04pm 2006") 25 | now.TimeFormats = append(now.TimeFormats, "2Jan 3pm 2006") 26 | 27 | now.TimeFormats = append(now.TimeFormats, "2Jan 2006 15:04") 28 | now.TimeFormats = append(now.TimeFormats, "2Jan 2006 3:04pm") 29 | now.TimeFormats = append(now.TimeFormats, "2Jan 2006 3pm") 30 | 31 | now.TimeFormats = append(now.TimeFormats, "2Jan 15:04") 32 | now.TimeFormats = append(now.TimeFormats, "2Jan 3:04pm") 33 | now.TimeFormats = append(now.TimeFormats, "2Jan 3pm") 34 | 35 | now.TimeFormats = append(now.TimeFormats, "2Jan") 36 | 37 | return Commands{ 38 | rmt: compileRegexp(`(?im)^(remind){1}(?: me to)? ([^:\r\n]*)(?::?)(.*)$`), 39 | cd: compileRegexp(`(?im)^(check due)$`), 40 | l: compileRegexp(`(?im)^(list)$`), 41 | c: compileRegexp(`(?im)^(clear) (\d+)$`), 42 | rn: compileRegexp(`(?im)^(renum)$`), 43 | cl: compileRegexp(`(?im)^(clearall)$`), 44 | hazel: compileRegexp(`(?im)(hazel)(?:!|~)?$`), 45 | } 46 | } 47 | 48 | func compileRegexp(s string) *regexp.Regexp { 49 | r, _ := regexp.Compile(s) 50 | return r 51 | } 52 | 53 | func (c *Commands) Extract(t string) (string, string, time.Time) { 54 | var a []string 55 | var r1, r2, r3 = "", "", "" 56 | 57 | // sg, _ := time.LoadLocation("Singapore") 58 | us, _ := time.LoadLocation("America/New_York") 59 | utc, _ := time.LoadLocation("UTC") 60 | 61 | 62 | a = c.rmt.FindStringSubmatch(t) 63 | if len(a) == 4 { 64 | r1, r2, r3 = a[1], a[2], a[3] 65 | } 66 | 67 | a = c.cd.FindStringSubmatch(t) 68 | if len(a) == 2 { 69 | r1 = a[1] 70 | } 71 | 72 | a = c.l.FindStringSubmatch(t) 73 | if len(a) == 2 { 74 | r1 = a[1] 75 | } 76 | 77 | a = c.c.FindStringSubmatch(t) 78 | if len(a) == 3 { 79 | r1, r2 = a[1], a[2] 80 | } 81 | 82 | a = c.rn.FindStringSubmatch(t) 83 | if len(a) == 2 { 84 | r1 = a[1] 85 | } 86 | 87 | a = c.cl.FindStringSubmatch(t) 88 | if len(a) == 2 { 89 | r1 = a[1] 90 | } 91 | 92 | a = c.hazel.FindStringSubmatch(t) 93 | if len(a) == 2 { 94 | r1 = a[1] 95 | } 96 | 97 | r1 = s.ToLower(s.TrimSpace(r1)) 98 | r2 = s.ToLower(s.TrimSpace(r2)) 99 | r3 = s.ToLower(s.TrimSpace(r3)) 100 | 101 | // ddt = now.Parse(r3 + " " + strconv.Itoa(time.now().Year())) 102 | 103 | // Replace tmr strings 104 | tmrRegex := compileRegexp(`(?im)^tomorrow|tmr|tml`) 105 | r3 = tmrRegex.ReplaceAllString(r3, time.Now().AddDate(0, 0, 1).Format("2Jan")) 106 | 107 | todayRegex := compileRegexp(`(?im)today`) 108 | r3 = todayRegex.ReplaceAllString(r3, time.Now().Format("2Jan")) 109 | 110 | ddt, err := now.Parse(r3) 111 | 112 | var r3t time.Time 113 | if err == nil { 114 | ddt = time.Date(ddt.Year(), ddt.Month(), ddt.Day(), ddt.Hour(), ddt.Minute(), 0, 0, us) 115 | r3t = ddt.In(utc) 116 | } else { 117 | r3t = time.Time{} 118 | } 119 | 120 | return r1, r2, r3t 121 | } 122 | -------------------------------------------------------------------------------- /commands/commands_test.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import "testing" 4 | import "time" 5 | 6 | func checkExpected(t *testing.T, actual string, expected string) { 7 | if actual != expected { 8 | t.Fatalf("Expected '%s' but got '%s'", expected, actual) 9 | } 10 | } 11 | 12 | func checkTime(t *testing.T, actual time.Time, expected time.Time) { 13 | if actual.UTC() != expected.UTC() { 14 | t.Fatalf("Expected '%s' but got '%s'", expected, actual) 15 | } 16 | } 17 | 18 | func checkNilTime(t *testing.T, actual *time.Time) { 19 | if !actual.IsZero() { 20 | t.Fatalf("Expected nil but got '%s'", actual) 21 | } 22 | } 23 | 24 | var cmds = NewCommandList() 25 | var cmd string 26 | var txt string 27 | var dds time.Time 28 | 29 | func TestRemindWithColonToday(t *testing.T) { 30 | utc, _ := time.LoadLocation("UTC") 31 | now := time.Now() 32 | expected := time.Date(now.Year(), now.Month(), now.Day(), 22, 35, 0, 0, time.Now().Location()).In(utc) 33 | 34 | cmd, txt, dds = cmds.Extract("remind do this:today 10:35pm") 35 | checkExpected(t, cmd, "remind") 36 | checkExpected(t, txt, "do this") 37 | checkTime(t, dds, expected) 38 | 39 | cmd, txt, dds = cmds.Extract("remind do this:ToDay 10:35pm") 40 | checkExpected(t, cmd, "remind") 41 | checkExpected(t, txt, "do this") 42 | checkTime(t, dds, expected) 43 | } 44 | 45 | func TestRemindWithColonTomorrow(t *testing.T) { 46 | utc, _ := time.LoadLocation("UTC") 47 | now := time.Now() 48 | expected := time.Date(now.Year(), now.Month(), now.Day()+1, 22, 35, 0, 0, time.Now().Location()).In(utc) 49 | 50 | cmd, txt, dds = cmds.Extract("remind do this:toMorrow 10:35pm") 51 | checkExpected(t, cmd, "remind") 52 | checkExpected(t, txt, "do this") 53 | checkTime(t, dds, expected) 54 | 55 | cmd, txt, dds = cmds.Extract("remind do this:tmr 10:35pm") 56 | checkExpected(t, cmd, "remind") 57 | checkExpected(t, txt, "do this") 58 | checkTime(t, dds, expected) 59 | 60 | cmd, txt, dds = cmds.Extract("remind do this:tml 10:35pm") 61 | checkExpected(t, cmd, "remind") 62 | checkExpected(t, txt, "do this") 63 | checkTime(t, dds, expected) 64 | } 65 | 66 | func TestRemindWithColon(t *testing.T) { 67 | utc, _ := time.LoadLocation("UTC") 68 | now := time.Now() 69 | expected := time.Date(now.Year(), 6, 9, 22, 30, 0, 0, time.Now().Location()).In(utc) 70 | 71 | cmd, txt, dds = cmds.Extract("remind do this:9jun 10:30pm") 72 | checkExpected(t, cmd, "remind") 73 | checkExpected(t, txt, "do this") 74 | checkTime(t, dds, expected) 75 | 76 | cmd, txt, dds = cmds.Extract("remind do this: 9jun 10:30pm") 77 | checkExpected(t, cmd, "remind") 78 | checkExpected(t, txt, "do this") 79 | checkTime(t, dds, expected) 80 | 81 | cmd, txt, dds = cmds.Extract("remind do this : 9jun 10:30pm") 82 | checkExpected(t, cmd, "remind") 83 | checkExpected(t, txt, "do this") 84 | checkTime(t, dds, expected) 85 | 86 | cmd, txt, dds = cmds.Extract("remind me to do this:9jun 10:30pm") 87 | checkExpected(t, cmd, "remind") 88 | checkExpected(t, txt, "do this") 89 | checkTime(t, dds, expected) 90 | 91 | cmd, txt, dds = cmds.Extract("remind me to do this: 9jun 10:30pm") 92 | checkExpected(t, cmd, "remind") 93 | checkExpected(t, txt, "do this") 94 | checkTime(t, dds, expected) 95 | 96 | cmd, txt, dds = cmds.Extract("remind me to do this : 9jun 10:30pm") 97 | checkExpected(t, cmd, "remind") 98 | checkExpected(t, txt, "do this") 99 | checkTime(t, dds, expected) 100 | } 101 | 102 | func TestRemindWithoutColon(t *testing.T) { 103 | cmd, txt, dds = cmds.Extract("remind me to do this") 104 | checkExpected(t, cmd, "remind") 105 | checkExpected(t, txt, "do this") 106 | checkNilTime(t, &dds) 107 | 108 | cmd, txt, dds = cmds.Extract("remind do this") 109 | checkExpected(t, cmd, "remind") 110 | checkExpected(t, txt, "do this") 111 | checkNilTime(t, &dds) 112 | } 113 | 114 | func TestList(t *testing.T) { 115 | cmd, txt, dds = cmds.Extract("list") 116 | checkExpected(t, cmd, "list") 117 | checkExpected(t, txt, "") 118 | checkNilTime(t, &dds) 119 | 120 | cmd, txt, dds = cmds.Extract("List") 121 | checkExpected(t, cmd, "list") 122 | checkExpected(t, txt, "") 123 | checkNilTime(t, &dds) 124 | 125 | cmd, txt, dds = cmds.Extract("listen this is not a list") 126 | checkExpected(t, cmd, "") 127 | checkExpected(t, txt, "") 128 | checkNilTime(t, &dds) 129 | } 130 | 131 | func TestClear(t *testing.T) { 132 | cmd, txt, dds = cmds.Extract("clear 2") 133 | checkExpected(t, cmd, "clear") 134 | checkExpected(t, txt, "2") 135 | checkNilTime(t, &dds) 136 | 137 | cmd, txt, dds = cmds.Extract("clearance sale") 138 | checkExpected(t, cmd, "") 139 | checkExpected(t, txt, "") 140 | checkNilTime(t, &dds) 141 | } 142 | 143 | func TestRenum(t *testing.T) { 144 | cmd, txt, dds = cmds.Extract("renum") 145 | checkExpected(t, cmd, "renum") 146 | checkExpected(t, txt, "") 147 | checkNilTime(t, &dds) 148 | 149 | cmd, txt, dds = cmds.Extract("renum-extra-random-characters") 150 | checkExpected(t, cmd, "") 151 | checkExpected(t, txt, "") 152 | checkNilTime(t, &dds) 153 | } 154 | 155 | func TestHazel(t *testing.T) { 156 | cmd, txt, dds = cmds.Extract("hazel") 157 | checkExpected(t, cmd, "hazel") 158 | checkExpected(t, txt, "") 159 | checkNilTime(t, &dds) 160 | 161 | cmd, txt, dds = cmds.Extract("hazel~") 162 | checkExpected(t, cmd, "hazel") 163 | checkExpected(t, txt, "") 164 | checkNilTime(t, &dds) 165 | 166 | cmd, txt, dds = cmds.Extract("hazel!") 167 | checkExpected(t, cmd, "hazel") 168 | checkExpected(t, txt, "") 169 | checkNilTime(t, &dds) 170 | 171 | cmd, txt, dds = cmds.Extract("hazelnut") 172 | checkExpected(t, cmd, "") 173 | checkExpected(t, txt, "") 174 | checkNilTime(t, &dds) 175 | } 176 | 177 | func TestClearall(t *testing.T) { 178 | cmd, txt, dds = cmds.Extract("clearall") 179 | checkExpected(t, cmd, "clearall") 180 | checkExpected(t, txt, "") 181 | checkNilTime(t, &dds) 182 | 183 | cmd, txt, dds = cmds.Extract("clearallrandomchar") 184 | checkExpected(t, cmd, "") 185 | checkExpected(t, txt, "") 186 | checkNilTime(t, &dds) 187 | } 188 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Config struct { 4 | BOT bot `toml:"bot"` 5 | DB database `toml:"database"` 6 | } 7 | 8 | type database struct { 9 | User string `toml:"user"` 10 | Password string `toml:"password"` 11 | Datapath string `toml:"datapath"` 12 | } 13 | 14 | type bot struct { 15 | BotId string `toml:"bot_id"` 16 | ApiKey string `toml:"api_key"` 17 | MainChatId int64 `toml:"main_chat_id"` 18 | } 19 | -------------------------------------------------------------------------------- /configs.toml.sample: -------------------------------------------------------------------------------- 1 | [bot] 2 | bot_id = "YOUR_BOT_ID" 3 | api_key = "YOUR_API_KEY" 4 | [database] 5 | datapath = "PATH_TO_SQLITEE_FOLDER" 6 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | base: 4 | build: . 5 | hazel: 6 | extends: base 7 | ports: 8 | - "8080:8080" 9 | expose: 10 | - "8080" 11 | volumes: 12 | - /var/data:/var/data 13 | entrypoint: 14 | - webapp 15 | timer: 16 | extends: base 17 | volumes: 18 | - /var/data:/var/data 19 | entrypoint: 20 | - timer 21 | -------------------------------------------------------------------------------- /handlers/handlers.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "database/sql" 5 | "encoding/json" 6 | "fmt" 7 | "log" 8 | "net/http" 9 | "net/url" 10 | "strconv" 11 | s "strings" 12 | "time" 13 | 14 | "github.com/aranair/remindbot/commands" 15 | "github.com/aranair/remindbot/config" 16 | ) 17 | 18 | type Update struct { 19 | Id int64 `json:"update_id"` 20 | Msg Message `json:"message"` 21 | } 22 | 23 | type Message struct { 24 | Id int64 `json:"message_id"` 25 | Text string `json:"text"` 26 | Chat Chat `json:"chat"` 27 | } 28 | 29 | type Chat struct { 30 | Id int64 `json:"id"` 31 | Title string `json:"title"` 32 | } 33 | 34 | type AppContext struct { 35 | db *sql.DB 36 | conf config.Config 37 | cmds commands.Commands 38 | loc *time.Location 39 | } 40 | 41 | type Reminder struct { 42 | Id int64 `sql:id` 43 | Content string `sql:content` 44 | Created time.Time `sql:created` 45 | DueDt time.Time `sql:due_dt` 46 | ChatId int64 `sql:chat_id` 47 | } 48 | 49 | func NewAppContext(db *sql.DB, conf config.Config, cmds commands.Commands) AppContext { 50 | us, _ := time.LoadLocation("America/New_York") 51 | return AppContext{db: db, conf: conf, cmds: cmds, loc: us} 52 | } 53 | 54 | func (ac *AppContext) CommandHandler(w http.ResponseWriter, r *http.Request) { 55 | var update Update 56 | 57 | decoder := json.NewDecoder(r.Body) 58 | if err := decoder.Decode(&update); err != nil { 59 | log.Println(err) 60 | } else { 61 | log.Println(update.Msg.Text) 62 | } 63 | 64 | cmd, txt, ddt := ac.cmds.Extract(update.Msg.Text) 65 | chatId := update.Msg.Chat.Id 66 | 67 | switch s.ToLower(cmd) { 68 | case "remind": 69 | ac.save(txt, ddt, chatId) 70 | case "check due": 71 | ac.CheckDue(chatId, false) 72 | case "list": 73 | ac.list(chatId) 74 | case "renum": 75 | ac.renum(chatId) 76 | case "clear": 77 | i, _ := strconv.Atoi(txt) 78 | ac.clear(i, chatId) 79 | case "clearall": 80 | ac.clearall(chatId) 81 | case "hazel": 82 | ac.SendText(chatId, "안녕~~~") 83 | } 84 | } 85 | 86 | func (ac *AppContext) save(txt string, ddt time.Time, chatId int64) { 87 | now := time.Now().Format(time.RFC3339) 88 | 89 | fmt.Println(ddt) 90 | _, err := ac.db.Exec( 91 | `INSERT INTO reminders(content, created, chat_id, due_dt) VALUES ($1, $2, $3, $4)`, 92 | txt, 93 | now, 94 | chatId, 95 | ddt.Format(time.RFC3339)) 96 | 97 | checkErr(err) 98 | ac.SendText(chatId, "Araseo~ remember liao!") 99 | } 100 | 101 | func (ac *AppContext) clear(id int, chatId int64) { 102 | _, err := ac.db.Exec(`DELETE FROM reminders WHERE chat_id=$1 AND id=$2`, chatId, id) 103 | checkErr(err) 104 | // "🎉" 105 | ac.SendText(chatId, "Pew!") 106 | } 107 | 108 | func (ac *AppContext) clearall(chatId int64) { 109 | _, err := ac.db.Exec(`DELETE FROM reminders WHERE chat_id=$1`, chatId) 110 | checkErr(err) 111 | ac.SendText(chatId, "Pew Pew Pew!") 112 | } 113 | 114 | func (ac *AppContext) list(chatId int64) { 115 | rows, err := ac.db.Query(`SELECT id, content, due_dt FROM reminders WHERE chat_id=$1`, chatId) 116 | checkErr(err) 117 | defer rows.Close() 118 | 119 | var arr []string 120 | var i int64 121 | var c string 122 | var dt time.Time 123 | 124 | for rows.Next() { 125 | _ = rows.Scan(&i, &c, &dt) 126 | line := "• " + c + " (`" + strconv.Itoa(int(i)) + "`)" 127 | if !dt.IsZero() { 128 | line = line + " - due " + dt.In(ac.loc).Format("2 Jan 3:04PM") 129 | } 130 | arr = append(arr, line) 131 | } 132 | text := s.Join(arr, "\n") 133 | 134 | if len(text) < 5 { 135 | text = "No current reminders, hiak~" 136 | } 137 | 138 | ac.SendText(chatId, text) 139 | } 140 | 141 | func timeSinceLabel(d time.Time) string { 142 | var duration = time.Since(d) 143 | var durationNum int 144 | var unit string 145 | 146 | if int(duration.Hours()) == 0 { 147 | durationNum = int(duration.Minutes()) 148 | unit = "min" 149 | } else if duration.Hours() < 24 { 150 | durationNum = int(duration.Hours()) 151 | unit = "hour" 152 | } else { 153 | durationNum = int(duration.Hours()) / 24 154 | unit = "day" 155 | } 156 | 157 | if durationNum > 1 { 158 | unit = unit + "s" 159 | } 160 | 161 | return " `" + strconv.Itoa(int(durationNum)) + " " + unit + "`" 162 | } 163 | 164 | // This resets numbers for everyone! 165 | func (ac *AppContext) renum(chatId int64) { 166 | rows, err := ac.db.Query(`SELECT content, due_dt, created, chat_id FROM reminders`) 167 | checkErr(err) 168 | defer rows.Close() 169 | 170 | var arr []Reminder 171 | var c string 172 | var dt time.Time 173 | var ct time.Time 174 | var cid int64 175 | 176 | for rows.Next() { 177 | _ = rows.Scan(&c, &dt, &ct, &cid) 178 | arr = append(arr, Reminder{Content: c, DueDt: dt, Created: ct, ChatId: cid}) 179 | } 180 | 181 | _, err = ac.db.Exec(`DELETE FROM reminders`) 182 | checkErr(err) 183 | 184 | _, err = ac.db.Exec(`DELETE FROM sqlite_sequence WHERE name='reminders';`) 185 | checkErr(err) 186 | 187 | for _, r := range arr { 188 | _, err := ac.db.Exec(`INSERT INTO reminders(content, due_dt, created, chat_id) VALUES ($1, $2, $3, $4)`, r.Content, r.DueDt, r.Created, r.ChatId) 189 | checkErr(err) 190 | } 191 | 192 | ac.list(chatId) 193 | } 194 | 195 | func (ac *AppContext) CheckDue(chatId int64, timedCheck bool) { 196 | fmt.Println(time.Now().Format(time.RFC3339)) 197 | rows, err := ac.db.Query( 198 | `SELECT id, content, due_dt FROM reminders WHERE chat_id=$1 and due_dt<=$2 and due_dt!=$3`, 199 | chatId, 200 | time.Now().Format(time.RFC3339), 201 | "0001-01-01T00:00:00Z", 202 | ) 203 | fmt.Println(err) 204 | fmt.Println(rows) 205 | 206 | checkErr(err) 207 | defer rows.Close() 208 | 209 | var arr []string 210 | var i int64 211 | var c string 212 | var dt time.Time 213 | 214 | arr = append(arr, "Overdue:") 215 | for rows.Next() { 216 | _ = rows.Scan(&i, &c, &dt) 217 | line := "• " + c + " (`" + strconv.Itoa(int(i)) + "`)" 218 | if !dt.IsZero() { 219 | line = line + " - due " + dt.In(ac.loc).Format("2 Jan 3:04PM") 220 | } 221 | arr = append(arr, line) 222 | } 223 | text := s.Join(arr, "\n") 224 | fmt.Println(text) 225 | 226 | if len(text) < 10 { 227 | text = "No overdues, keke~" 228 | if !timedCheck { 229 | ac.SendText(chatId, text) 230 | } 231 | } else { 232 | ac.SendText(chatId, text) 233 | } 234 | } 235 | 236 | func (ac *AppContext) SendText(chatId int64, text string) { 237 | link := "https://api.telegram.org/bot{botId}:{apiKey}/sendMessage?chat_id={chatId}&text={text}&parse_mode=Markdown" 238 | link = s.Replace(link, "{botId}", ac.conf.BOT.BotId, -1) 239 | link = s.Replace(link, "{apiKey}", ac.conf.BOT.ApiKey, -1) 240 | link = s.Replace(link, "{chatId}", strconv.FormatInt(chatId, 10), -1) 241 | link = s.Replace(link, "{text}", url.QueryEscape(text), -1) 242 | 243 | fmt.Println(link) 244 | 245 | _, _ = http.Get(link) 246 | } 247 | 248 | func checkErr(err error) { 249 | if err != nil { 250 | panic(err) 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /migrations/20170709002322-create_reminders.sql: -------------------------------------------------------------------------------- 1 | 2 | -- +migrate Up 3 | CREATE TABLE IF NOT EXISTS reminders( 4 | id INTEGER PRIMARY KEY AUTOINCREMENT, 5 | content TEXT, 6 | chat_id INTEGER, 7 | created DATETIME 8 | ); 9 | 10 | -- +migrate Down 11 | DROP TABLE reminders; 12 | -------------------------------------------------------------------------------- /migrations/20170709002410-add_due_dt.sql: -------------------------------------------------------------------------------- 1 | 2 | -- +migrate Up 3 | ALTER TABLE reminders 4 | ADD due_dt DATETIME; 5 | 6 | -- +migrate Down 7 | CREATE TABLE t1_backup AS SELECT id,content,chat_id,created FROM reminders; 8 | DROP TABLE reminders; 9 | ALTER TABLE t1_backup RENAME TO reminders; 10 | -------------------------------------------------------------------------------- /router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gorilla/context" 7 | "github.com/julienschmidt/httprouter" 8 | ) 9 | 10 | type router struct { 11 | *httprouter.Router 12 | } 13 | 14 | func New() *router { 15 | return &router{httprouter.New()} 16 | } 17 | 18 | func (r *router) OPTIONS(path string, h http.Handler) { 19 | r.Handle("OPTIONS", path, wrapHandler(h)) 20 | } 21 | 22 | func (r *router) GET(path string, h http.Handler) { 23 | r.Handle("GET", path, wrapHandler(h)) 24 | } 25 | 26 | func (r *router) POST(path string, h http.Handler) { 27 | r.Handle("POST", path, wrapHandler(h)) 28 | } 29 | 30 | func (r *router) PUT(path string, h http.Handler) { 31 | r.Handle("PUT", path, wrapHandler(h)) 32 | } 33 | 34 | func (r *router) PATCH(path string, h http.Handler) { 35 | r.Handle("PATCH", path, wrapHandler(h)) 36 | } 37 | 38 | func (r *router) DELETE(path string, h http.Handler) { 39 | r.Handle("DELETE", path, wrapHandler(h)) 40 | } 41 | 42 | func (r *router) HEAD(path string, h http.Handler) { 43 | r.Handle("HEAD", path, wrapHandler(h)) 44 | } 45 | 46 | func wrapHandler(h http.Handler) httprouter.Handle { 47 | return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { 48 | context.Set(r, "params", ps) 49 | h.ServeHTTP(w, r) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/.gitignore: -------------------------------------------------------------------------------- 1 | TAGS 2 | tags 3 | .*.swp 4 | tomlcheck/tomlcheck 5 | toml.test 6 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.1 4 | - 1.2 5 | - 1.3 6 | - 1.4 7 | - 1.5 8 | - 1.6 9 | - tip 10 | install: 11 | - go install ./... 12 | - go get github.com/BurntSushi/toml-test 13 | script: 14 | - export PATH="$PATH:$HOME/gopath/bin" 15 | - make test 16 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/COMPATIBLE: -------------------------------------------------------------------------------- 1 | Compatible with TOML version 2 | [v0.2.0](https://github.com/mojombo/toml/blob/master/versions/toml-v0.2.0.md) 3 | 4 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/COPYING: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | 15 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/Makefile: -------------------------------------------------------------------------------- 1 | install: 2 | go install ./... 3 | 4 | test: install 5 | go test -v 6 | toml-test toml-test-decoder 7 | toml-test -encoder toml-test-encoder 8 | 9 | fmt: 10 | gofmt -w *.go */*.go 11 | colcheck *.go */*.go 12 | 13 | tags: 14 | find ./ -name '*.go' -print0 | xargs -0 gotags > TAGS 15 | 16 | push: 17 | git push origin master 18 | git push github master 19 | 20 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/README.md: -------------------------------------------------------------------------------- 1 | ## TOML parser and encoder for Go with reflection 2 | 3 | TOML stands for Tom's Obvious, Minimal Language. This Go package provides a 4 | reflection interface similar to Go's standard library `json` and `xml` 5 | packages. This package also supports the `encoding.TextUnmarshaler` and 6 | `encoding.TextMarshaler` interfaces so that you can define custom data 7 | representations. (There is an example of this below.) 8 | 9 | Spec: https://github.com/mojombo/toml 10 | 11 | Compatible with TOML version 12 | [v0.2.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.2.0.md) 13 | 14 | Documentation: http://godoc.org/github.com/BurntSushi/toml 15 | 16 | Installation: 17 | 18 | ```bash 19 | go get github.com/BurntSushi/toml 20 | ``` 21 | 22 | Try the toml validator: 23 | 24 | ```bash 25 | go get github.com/BurntSushi/toml/cmd/tomlv 26 | tomlv some-toml-file.toml 27 | ``` 28 | 29 | [![Build status](https://api.travis-ci.org/BurntSushi/toml.png)](https://travis-ci.org/BurntSushi/toml) 30 | 31 | 32 | ### Testing 33 | 34 | This package passes all tests in 35 | [toml-test](https://github.com/BurntSushi/toml-test) for both the decoder 36 | and the encoder. 37 | 38 | ### Examples 39 | 40 | This package works similarly to how the Go standard library handles `XML` 41 | and `JSON`. Namely, data is loaded into Go values via reflection. 42 | 43 | For the simplest example, consider some TOML file as just a list of keys 44 | and values: 45 | 46 | ```toml 47 | Age = 25 48 | Cats = [ "Cauchy", "Plato" ] 49 | Pi = 3.14 50 | Perfection = [ 6, 28, 496, 8128 ] 51 | DOB = 1987-07-05T05:45:00Z 52 | ``` 53 | 54 | Which could be defined in Go as: 55 | 56 | ```go 57 | type Config struct { 58 | Age int 59 | Cats []string 60 | Pi float64 61 | Perfection []int 62 | DOB time.Time // requires `import time` 63 | } 64 | ``` 65 | 66 | And then decoded with: 67 | 68 | ```go 69 | var conf Config 70 | if _, err := toml.Decode(tomlData, &conf); err != nil { 71 | // handle error 72 | } 73 | ``` 74 | 75 | You can also use struct tags if your struct field name doesn't map to a TOML 76 | key value directly: 77 | 78 | ```toml 79 | some_key_NAME = "wat" 80 | ``` 81 | 82 | ```go 83 | type TOML struct { 84 | ObscureKey string `toml:"some_key_NAME"` 85 | } 86 | ``` 87 | 88 | ### Using the `encoding.TextUnmarshaler` interface 89 | 90 | Here's an example that automatically parses duration strings into 91 | `time.Duration` values: 92 | 93 | ```toml 94 | [[song]] 95 | name = "Thunder Road" 96 | duration = "4m49s" 97 | 98 | [[song]] 99 | name = "Stairway to Heaven" 100 | duration = "8m03s" 101 | ``` 102 | 103 | Which can be decoded with: 104 | 105 | ```go 106 | type song struct { 107 | Name string 108 | Duration duration 109 | } 110 | type songs struct { 111 | Song []song 112 | } 113 | var favorites songs 114 | if _, err := toml.Decode(blob, &favorites); err != nil { 115 | log.Fatal(err) 116 | } 117 | 118 | for _, s := range favorites.Song { 119 | fmt.Printf("%s (%s)\n", s.Name, s.Duration) 120 | } 121 | ``` 122 | 123 | And you'll also need a `duration` type that satisfies the 124 | `encoding.TextUnmarshaler` interface: 125 | 126 | ```go 127 | type duration struct { 128 | time.Duration 129 | } 130 | 131 | func (d *duration) UnmarshalText(text []byte) error { 132 | var err error 133 | d.Duration, err = time.ParseDuration(string(text)) 134 | return err 135 | } 136 | ``` 137 | 138 | ### More complex usage 139 | 140 | Here's an example of how to load the example from the official spec page: 141 | 142 | ```toml 143 | # This is a TOML document. Boom. 144 | 145 | title = "TOML Example" 146 | 147 | [owner] 148 | name = "Tom Preston-Werner" 149 | organization = "GitHub" 150 | bio = "GitHub Cofounder & CEO\nLikes tater tots and beer." 151 | dob = 1979-05-27T07:32:00Z # First class dates? Why not? 152 | 153 | [database] 154 | server = "192.168.1.1" 155 | ports = [ 8001, 8001, 8002 ] 156 | connection_max = 5000 157 | enabled = true 158 | 159 | [servers] 160 | 161 | # You can indent as you please. Tabs or spaces. TOML don't care. 162 | [servers.alpha] 163 | ip = "10.0.0.1" 164 | dc = "eqdc10" 165 | 166 | [servers.beta] 167 | ip = "10.0.0.2" 168 | dc = "eqdc10" 169 | 170 | [clients] 171 | data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it 172 | 173 | # Line breaks are OK when inside arrays 174 | hosts = [ 175 | "alpha", 176 | "omega" 177 | ] 178 | ``` 179 | 180 | And the corresponding Go types are: 181 | 182 | ```go 183 | type tomlConfig struct { 184 | Title string 185 | Owner ownerInfo 186 | DB database `toml:"database"` 187 | Servers map[string]server 188 | Clients clients 189 | } 190 | 191 | type ownerInfo struct { 192 | Name string 193 | Org string `toml:"organization"` 194 | Bio string 195 | DOB time.Time 196 | } 197 | 198 | type database struct { 199 | Server string 200 | Ports []int 201 | ConnMax int `toml:"connection_max"` 202 | Enabled bool 203 | } 204 | 205 | type server struct { 206 | IP string 207 | DC string 208 | } 209 | 210 | type clients struct { 211 | Data [][]interface{} 212 | Hosts []string 213 | } 214 | ``` 215 | 216 | Note that a case insensitive match will be tried if an exact match can't be 217 | found. 218 | 219 | A working example of the above can be found in `_examples/example.{go,toml}`. 220 | 221 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/decode_meta.go: -------------------------------------------------------------------------------- 1 | package toml 2 | 3 | import "strings" 4 | 5 | // MetaData allows access to meta information about TOML data that may not 6 | // be inferrable via reflection. In particular, whether a key has been defined 7 | // and the TOML type of a key. 8 | type MetaData struct { 9 | mapping map[string]interface{} 10 | types map[string]tomlType 11 | keys []Key 12 | decoded map[string]bool 13 | context Key // Used only during decoding. 14 | } 15 | 16 | // IsDefined returns true if the key given exists in the TOML data. The key 17 | // should be specified hierarchially. e.g., 18 | // 19 | // // access the TOML key 'a.b.c' 20 | // IsDefined("a", "b", "c") 21 | // 22 | // IsDefined will return false if an empty key given. Keys are case sensitive. 23 | func (md *MetaData) IsDefined(key ...string) bool { 24 | if len(key) == 0 { 25 | return false 26 | } 27 | 28 | var hash map[string]interface{} 29 | var ok bool 30 | var hashOrVal interface{} = md.mapping 31 | for _, k := range key { 32 | if hash, ok = hashOrVal.(map[string]interface{}); !ok { 33 | return false 34 | } 35 | if hashOrVal, ok = hash[k]; !ok { 36 | return false 37 | } 38 | } 39 | return true 40 | } 41 | 42 | // Type returns a string representation of the type of the key specified. 43 | // 44 | // Type will return the empty string if given an empty key or a key that 45 | // does not exist. Keys are case sensitive. 46 | func (md *MetaData) Type(key ...string) string { 47 | fullkey := strings.Join(key, ".") 48 | if typ, ok := md.types[fullkey]; ok { 49 | return typ.typeString() 50 | } 51 | return "" 52 | } 53 | 54 | // Key is the type of any TOML key, including key groups. Use (MetaData).Keys 55 | // to get values of this type. 56 | type Key []string 57 | 58 | func (k Key) String() string { 59 | return strings.Join(k, ".") 60 | } 61 | 62 | func (k Key) maybeQuotedAll() string { 63 | var ss []string 64 | for i := range k { 65 | ss = append(ss, k.maybeQuoted(i)) 66 | } 67 | return strings.Join(ss, ".") 68 | } 69 | 70 | func (k Key) maybeQuoted(i int) string { 71 | quote := false 72 | for _, c := range k[i] { 73 | if !isBareKeyChar(c) { 74 | quote = true 75 | break 76 | } 77 | } 78 | if quote { 79 | return "\"" + strings.Replace(k[i], "\"", "\\\"", -1) + "\"" 80 | } 81 | return k[i] 82 | } 83 | 84 | func (k Key) add(piece string) Key { 85 | newKey := make(Key, len(k)+1) 86 | copy(newKey, k) 87 | newKey[len(k)] = piece 88 | return newKey 89 | } 90 | 91 | // Keys returns a slice of every key in the TOML data, including key groups. 92 | // Each key is itself a slice, where the first element is the top of the 93 | // hierarchy and the last is the most specific. 94 | // 95 | // The list will have the same order as the keys appeared in the TOML data. 96 | // 97 | // All keys returned are non-empty. 98 | func (md *MetaData) Keys() []Key { 99 | return md.keys 100 | } 101 | 102 | // Undecoded returns all keys that have not been decoded in the order in which 103 | // they appear in the original TOML document. 104 | // 105 | // This includes keys that haven't been decoded because of a Primitive value. 106 | // Once the Primitive value is decoded, the keys will be considered decoded. 107 | // 108 | // Also note that decoding into an empty interface will result in no decoding, 109 | // and so no keys will be considered decoded. 110 | // 111 | // In this sense, the Undecoded keys correspond to keys in the TOML document 112 | // that do not have a concrete type in your representation. 113 | func (md *MetaData) Undecoded() []Key { 114 | undecoded := make([]Key, 0, len(md.keys)) 115 | for _, key := range md.keys { 116 | if !md.decoded[key.String()] { 117 | undecoded = append(undecoded, key) 118 | } 119 | } 120 | return undecoded 121 | } 122 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package toml provides facilities for decoding and encoding TOML configuration 3 | files via reflection. There is also support for delaying decoding with 4 | the Primitive type, and querying the set of keys in a TOML document with the 5 | MetaData type. 6 | 7 | The specification implemented: https://github.com/mojombo/toml 8 | 9 | The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify 10 | whether a file is a valid TOML document. It can also be used to print the 11 | type of each key in a TOML document. 12 | 13 | Testing 14 | 15 | There are two important types of tests used for this package. The first is 16 | contained inside '*_test.go' files and uses the standard Go unit testing 17 | framework. These tests are primarily devoted to holistically testing the 18 | decoder and encoder. 19 | 20 | The second type of testing is used to verify the implementation's adherence 21 | to the TOML specification. These tests have been factored into their own 22 | project: https://github.com/BurntSushi/toml-test 23 | 24 | The reason the tests are in a separate project is so that they can be used by 25 | any implementation of TOML. Namely, it is language agnostic. 26 | */ 27 | package toml 28 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/encoding_types.go: -------------------------------------------------------------------------------- 1 | // +build go1.2 2 | 3 | package toml 4 | 5 | // In order to support Go 1.1, we define our own TextMarshaler and 6 | // TextUnmarshaler types. For Go 1.2+, we just alias them with the 7 | // standard library interfaces. 8 | 9 | import ( 10 | "encoding" 11 | ) 12 | 13 | // TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here 14 | // so that Go 1.1 can be supported. 15 | type TextMarshaler encoding.TextMarshaler 16 | 17 | // TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined 18 | // here so that Go 1.1 can be supported. 19 | type TextUnmarshaler encoding.TextUnmarshaler 20 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/encoding_types_1.1.go: -------------------------------------------------------------------------------- 1 | // +build !go1.2 2 | 3 | package toml 4 | 5 | // These interfaces were introduced in Go 1.2, so we add them manually when 6 | // compiling for Go 1.1. 7 | 8 | // TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here 9 | // so that Go 1.1 can be supported. 10 | type TextMarshaler interface { 11 | MarshalText() (text []byte, err error) 12 | } 13 | 14 | // TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined 15 | // here so that Go 1.1 can be supported. 16 | type TextUnmarshaler interface { 17 | UnmarshalText(text []byte) error 18 | } 19 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/session.vim: -------------------------------------------------------------------------------- 1 | au BufWritePost *.go silent!make tags > /dev/null 2>&1 2 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/type_check.go: -------------------------------------------------------------------------------- 1 | package toml 2 | 3 | // tomlType represents any Go type that corresponds to a TOML type. 4 | // While the first draft of the TOML spec has a simplistic type system that 5 | // probably doesn't need this level of sophistication, we seem to be militating 6 | // toward adding real composite types. 7 | type tomlType interface { 8 | typeString() string 9 | } 10 | 11 | // typeEqual accepts any two types and returns true if they are equal. 12 | func typeEqual(t1, t2 tomlType) bool { 13 | if t1 == nil || t2 == nil { 14 | return false 15 | } 16 | return t1.typeString() == t2.typeString() 17 | } 18 | 19 | func typeIsHash(t tomlType) bool { 20 | return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash) 21 | } 22 | 23 | type tomlBaseType string 24 | 25 | func (btype tomlBaseType) typeString() string { 26 | return string(btype) 27 | } 28 | 29 | func (btype tomlBaseType) String() string { 30 | return btype.typeString() 31 | } 32 | 33 | var ( 34 | tomlInteger tomlBaseType = "Integer" 35 | tomlFloat tomlBaseType = "Float" 36 | tomlDatetime tomlBaseType = "Datetime" 37 | tomlString tomlBaseType = "String" 38 | tomlBool tomlBaseType = "Bool" 39 | tomlArray tomlBaseType = "Array" 40 | tomlHash tomlBaseType = "Hash" 41 | tomlArrayHash tomlBaseType = "ArrayHash" 42 | ) 43 | 44 | // typeOfPrimitive returns a tomlType of any primitive value in TOML. 45 | // Primitive values are: Integer, Float, Datetime, String and Bool. 46 | // 47 | // Passing a lexer item other than the following will cause a BUG message 48 | // to occur: itemString, itemBool, itemInteger, itemFloat, itemDatetime. 49 | func (p *parser) typeOfPrimitive(lexItem item) tomlType { 50 | switch lexItem.typ { 51 | case itemInteger: 52 | return tomlInteger 53 | case itemFloat: 54 | return tomlFloat 55 | case itemDatetime: 56 | return tomlDatetime 57 | case itemString: 58 | return tomlString 59 | case itemMultilineString: 60 | return tomlString 61 | case itemRawString: 62 | return tomlString 63 | case itemRawMultilineString: 64 | return tomlString 65 | case itemBool: 66 | return tomlBool 67 | } 68 | p.bug("Cannot infer primitive type of lex item '%s'.", lexItem) 69 | panic("unreachable") 70 | } 71 | 72 | // typeOfArray returns a tomlType for an array given a list of types of its 73 | // values. 74 | // 75 | // In the current spec, if an array is homogeneous, then its type is always 76 | // "Array". If the array is not homogeneous, an error is generated. 77 | func (p *parser) typeOfArray(types []tomlType) tomlType { 78 | // Empty arrays are cool. 79 | if len(types) == 0 { 80 | return tomlArray 81 | } 82 | 83 | theType := types[0] 84 | for _, t := range types[1:] { 85 | if !typeEqual(theType, t) { 86 | p.panicf("Array contains values of type '%s' and '%s', but "+ 87 | "arrays must be homogeneous.", theType, t) 88 | } 89 | } 90 | return tomlArray 91 | } 92 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/type_fields.go: -------------------------------------------------------------------------------- 1 | package toml 2 | 3 | // Struct field handling is adapted from code in encoding/json: 4 | // 5 | // Copyright 2010 The Go Authors. All rights reserved. 6 | // Use of this source code is governed by a BSD-style 7 | // license that can be found in the Go distribution. 8 | 9 | import ( 10 | "reflect" 11 | "sort" 12 | "sync" 13 | ) 14 | 15 | // A field represents a single field found in a struct. 16 | type field struct { 17 | name string // the name of the field (`toml` tag included) 18 | tag bool // whether field has a `toml` tag 19 | index []int // represents the depth of an anonymous field 20 | typ reflect.Type // the type of the field 21 | } 22 | 23 | // byName sorts field by name, breaking ties with depth, 24 | // then breaking ties with "name came from toml tag", then 25 | // breaking ties with index sequence. 26 | type byName []field 27 | 28 | func (x byName) Len() int { return len(x) } 29 | 30 | func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 31 | 32 | func (x byName) Less(i, j int) bool { 33 | if x[i].name != x[j].name { 34 | return x[i].name < x[j].name 35 | } 36 | if len(x[i].index) != len(x[j].index) { 37 | return len(x[i].index) < len(x[j].index) 38 | } 39 | if x[i].tag != x[j].tag { 40 | return x[i].tag 41 | } 42 | return byIndex(x).Less(i, j) 43 | } 44 | 45 | // byIndex sorts field by index sequence. 46 | type byIndex []field 47 | 48 | func (x byIndex) Len() int { return len(x) } 49 | 50 | func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 51 | 52 | func (x byIndex) Less(i, j int) bool { 53 | for k, xik := range x[i].index { 54 | if k >= len(x[j].index) { 55 | return false 56 | } 57 | if xik != x[j].index[k] { 58 | return xik < x[j].index[k] 59 | } 60 | } 61 | return len(x[i].index) < len(x[j].index) 62 | } 63 | 64 | // typeFields returns a list of fields that TOML should recognize for the given 65 | // type. The algorithm is breadth-first search over the set of structs to 66 | // include - the top struct and then any reachable anonymous structs. 67 | func typeFields(t reflect.Type) []field { 68 | // Anonymous fields to explore at the current level and the next. 69 | current := []field{} 70 | next := []field{{typ: t}} 71 | 72 | // Count of queued names for current level and the next. 73 | count := map[reflect.Type]int{} 74 | nextCount := map[reflect.Type]int{} 75 | 76 | // Types already visited at an earlier level. 77 | visited := map[reflect.Type]bool{} 78 | 79 | // Fields found. 80 | var fields []field 81 | 82 | for len(next) > 0 { 83 | current, next = next, current[:0] 84 | count, nextCount = nextCount, map[reflect.Type]int{} 85 | 86 | for _, f := range current { 87 | if visited[f.typ] { 88 | continue 89 | } 90 | visited[f.typ] = true 91 | 92 | // Scan f.typ for fields to include. 93 | for i := 0; i < f.typ.NumField(); i++ { 94 | sf := f.typ.Field(i) 95 | if sf.PkgPath != "" && !sf.Anonymous { // unexported 96 | continue 97 | } 98 | opts := getOptions(sf.Tag) 99 | if opts.skip { 100 | continue 101 | } 102 | index := make([]int, len(f.index)+1) 103 | copy(index, f.index) 104 | index[len(f.index)] = i 105 | 106 | ft := sf.Type 107 | if ft.Name() == "" && ft.Kind() == reflect.Ptr { 108 | // Follow pointer. 109 | ft = ft.Elem() 110 | } 111 | 112 | // Record found field and index sequence. 113 | if opts.name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { 114 | tagged := opts.name != "" 115 | name := opts.name 116 | if name == "" { 117 | name = sf.Name 118 | } 119 | fields = append(fields, field{name, tagged, index, ft}) 120 | if count[f.typ] > 1 { 121 | // If there were multiple instances, add a second, 122 | // so that the annihilation code will see a duplicate. 123 | // It only cares about the distinction between 1 or 2, 124 | // so don't bother generating any more copies. 125 | fields = append(fields, fields[len(fields)-1]) 126 | } 127 | continue 128 | } 129 | 130 | // Record new anonymous struct to explore in next round. 131 | nextCount[ft]++ 132 | if nextCount[ft] == 1 { 133 | f := field{name: ft.Name(), index: index, typ: ft} 134 | next = append(next, f) 135 | } 136 | } 137 | } 138 | } 139 | 140 | sort.Sort(byName(fields)) 141 | 142 | // Delete all fields that are hidden by the Go rules for embedded fields, 143 | // except that fields with TOML tags are promoted. 144 | 145 | // The fields are sorted in primary order of name, secondary order 146 | // of field index length. Loop over names; for each name, delete 147 | // hidden fields by choosing the one dominant field that survives. 148 | out := fields[:0] 149 | for advance, i := 0, 0; i < len(fields); i += advance { 150 | // One iteration per name. 151 | // Find the sequence of fields with the name of this first field. 152 | fi := fields[i] 153 | name := fi.name 154 | for advance = 1; i+advance < len(fields); advance++ { 155 | fj := fields[i+advance] 156 | if fj.name != name { 157 | break 158 | } 159 | } 160 | if advance == 1 { // Only one field with this name 161 | out = append(out, fi) 162 | continue 163 | } 164 | dominant, ok := dominantField(fields[i : i+advance]) 165 | if ok { 166 | out = append(out, dominant) 167 | } 168 | } 169 | 170 | fields = out 171 | sort.Sort(byIndex(fields)) 172 | 173 | return fields 174 | } 175 | 176 | // dominantField looks through the fields, all of which are known to 177 | // have the same name, to find the single field that dominates the 178 | // others using Go's embedding rules, modified by the presence of 179 | // TOML tags. If there are multiple top-level fields, the boolean 180 | // will be false: This condition is an error in Go and we skip all 181 | // the fields. 182 | func dominantField(fields []field) (field, bool) { 183 | // The fields are sorted in increasing index-length order. The winner 184 | // must therefore be one with the shortest index length. Drop all 185 | // longer entries, which is easy: just truncate the slice. 186 | length := len(fields[0].index) 187 | tagged := -1 // Index of first tagged field. 188 | for i, f := range fields { 189 | if len(f.index) > length { 190 | fields = fields[:i] 191 | break 192 | } 193 | if f.tag { 194 | if tagged >= 0 { 195 | // Multiple tagged fields at the same level: conflict. 196 | // Return no field. 197 | return field{}, false 198 | } 199 | tagged = i 200 | } 201 | } 202 | if tagged >= 0 { 203 | return fields[tagged], true 204 | } 205 | // All remaining fields have the same length. If there's more than one, 206 | // we have a conflict (two fields named "X" at the same level) and we 207 | // return no field. 208 | if len(fields) > 1 { 209 | return field{}, false 210 | } 211 | return fields[0], true 212 | } 213 | 214 | var fieldCache struct { 215 | sync.RWMutex 216 | m map[reflect.Type][]field 217 | } 218 | 219 | // cachedTypeFields is like typeFields but uses a cache to avoid repeated work. 220 | func cachedTypeFields(t reflect.Type) []field { 221 | fieldCache.RLock() 222 | f := fieldCache.m[t] 223 | fieldCache.RUnlock() 224 | if f != nil { 225 | return f 226 | } 227 | 228 | // Compute fields without lock. 229 | // Might duplicate effort but won't hold other computations back. 230 | f = typeFields(t) 231 | if f == nil { 232 | f = []field{} 233 | } 234 | 235 | fieldCache.Lock() 236 | if fieldCache.m == nil { 237 | fieldCache.m = map[reflect.Type][]field{} 238 | } 239 | fieldCache.m[t] = f 240 | fieldCache.Unlock() 241 | return f 242 | } 243 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/context/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | 4 | matrix: 5 | include: 6 | - go: 1.3 7 | - go: 1.4 8 | - go: 1.5 9 | - go: 1.6 10 | - go: 1.7 11 | - go: tip 12 | allow_failures: 13 | - go: tip 14 | 15 | script: 16 | - go get -t -v ./... 17 | - diff -u <(echo -n) <(gofmt -d .) 18 | - go vet $(go list ./... | grep -v /vendor/) 19 | - go test -v -race ./... 20 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/context/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Rodrigo Moraes. 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/gorilla/context/README.md: -------------------------------------------------------------------------------- 1 | context 2 | ======= 3 | [![Build Status](https://travis-ci.org/gorilla/context.png?branch=master)](https://travis-ci.org/gorilla/context) 4 | 5 | gorilla/context is a general purpose registry for global request variables. 6 | 7 | > Note: gorilla/context, having been born well before `context.Context` existed, does not play well 8 | > with the shallow copying of the request that [`http.Request.WithContext`](https://golang.org/pkg/net/http/#Request.WithContext) (added to net/http Go 1.7 onwards) performs. You should either use *just* gorilla/context, or moving forward, the new `http.Request.Context()`. 9 | 10 | Read the full documentation here: http://www.gorillatoolkit.org/pkg/context 11 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/context/context.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Gorilla 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 context 6 | 7 | import ( 8 | "net/http" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | var ( 14 | mutex sync.RWMutex 15 | data = make(map[*http.Request]map[interface{}]interface{}) 16 | datat = make(map[*http.Request]int64) 17 | ) 18 | 19 | // Set stores a value for a given key in a given request. 20 | func Set(r *http.Request, key, val interface{}) { 21 | mutex.Lock() 22 | if data[r] == nil { 23 | data[r] = make(map[interface{}]interface{}) 24 | datat[r] = time.Now().Unix() 25 | } 26 | data[r][key] = val 27 | mutex.Unlock() 28 | } 29 | 30 | // Get returns a value stored for a given key in a given request. 31 | func Get(r *http.Request, key interface{}) interface{} { 32 | mutex.RLock() 33 | if ctx := data[r]; ctx != nil { 34 | value := ctx[key] 35 | mutex.RUnlock() 36 | return value 37 | } 38 | mutex.RUnlock() 39 | return nil 40 | } 41 | 42 | // GetOk returns stored value and presence state like multi-value return of map access. 43 | func GetOk(r *http.Request, key interface{}) (interface{}, bool) { 44 | mutex.RLock() 45 | if _, ok := data[r]; ok { 46 | value, ok := data[r][key] 47 | mutex.RUnlock() 48 | return value, ok 49 | } 50 | mutex.RUnlock() 51 | return nil, false 52 | } 53 | 54 | // GetAll returns all stored values for the request as a map. Nil is returned for invalid requests. 55 | func GetAll(r *http.Request) map[interface{}]interface{} { 56 | mutex.RLock() 57 | if context, ok := data[r]; ok { 58 | result := make(map[interface{}]interface{}, len(context)) 59 | for k, v := range context { 60 | result[k] = v 61 | } 62 | mutex.RUnlock() 63 | return result 64 | } 65 | mutex.RUnlock() 66 | return nil 67 | } 68 | 69 | // GetAllOk returns all stored values for the request as a map and a boolean value that indicates if 70 | // the request was registered. 71 | func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) { 72 | mutex.RLock() 73 | context, ok := data[r] 74 | result := make(map[interface{}]interface{}, len(context)) 75 | for k, v := range context { 76 | result[k] = v 77 | } 78 | mutex.RUnlock() 79 | return result, ok 80 | } 81 | 82 | // Delete removes a value stored for a given key in a given request. 83 | func Delete(r *http.Request, key interface{}) { 84 | mutex.Lock() 85 | if data[r] != nil { 86 | delete(data[r], key) 87 | } 88 | mutex.Unlock() 89 | } 90 | 91 | // Clear removes all values stored for a given request. 92 | // 93 | // This is usually called by a handler wrapper to clean up request 94 | // variables at the end of a request lifetime. See ClearHandler(). 95 | func Clear(r *http.Request) { 96 | mutex.Lock() 97 | clear(r) 98 | mutex.Unlock() 99 | } 100 | 101 | // clear is Clear without the lock. 102 | func clear(r *http.Request) { 103 | delete(data, r) 104 | delete(datat, r) 105 | } 106 | 107 | // Purge removes request data stored for longer than maxAge, in seconds. 108 | // It returns the amount of requests removed. 109 | // 110 | // If maxAge <= 0, all request data is removed. 111 | // 112 | // This is only used for sanity check: in case context cleaning was not 113 | // properly set some request data can be kept forever, consuming an increasing 114 | // amount of memory. In case this is detected, Purge() must be called 115 | // periodically until the problem is fixed. 116 | func Purge(maxAge int) int { 117 | mutex.Lock() 118 | count := 0 119 | if maxAge <= 0 { 120 | count = len(data) 121 | data = make(map[*http.Request]map[interface{}]interface{}) 122 | datat = make(map[*http.Request]int64) 123 | } else { 124 | min := time.Now().Unix() - int64(maxAge) 125 | for r := range data { 126 | if datat[r] < min { 127 | clear(r) 128 | count++ 129 | } 130 | } 131 | } 132 | mutex.Unlock() 133 | return count 134 | } 135 | 136 | // ClearHandler wraps an http.Handler and clears request values at the end 137 | // of a request lifetime. 138 | func ClearHandler(h http.Handler) http.Handler { 139 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 140 | defer Clear(r) 141 | h.ServeHTTP(w, r) 142 | }) 143 | } 144 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/context/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Gorilla 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 context stores values shared during a request lifetime. 7 | 8 | Note: gorilla/context, having been born well before `context.Context` existed, 9 | does not play well > with the shallow copying of the request that 10 | [`http.Request.WithContext`](https://golang.org/pkg/net/http/#Request.WithContext) 11 | (added to net/http Go 1.7 onwards) performs. You should either use *just* 12 | gorilla/context, or moving forward, the new `http.Request.Context()`. 13 | 14 | For example, a router can set variables extracted from the URL and later 15 | application handlers can access those values, or it can be used to store 16 | sessions values to be saved at the end of a request. There are several 17 | others common uses. 18 | 19 | The idea was posted by Brad Fitzpatrick to the go-nuts mailing list: 20 | 21 | http://groups.google.com/group/golang-nuts/msg/e2d679d303aa5d53 22 | 23 | Here's the basic usage: first define the keys that you will need. The key 24 | type is interface{} so a key can be of any type that supports equality. 25 | Here we define a key using a custom int type to avoid name collisions: 26 | 27 | package foo 28 | 29 | import ( 30 | "github.com/gorilla/context" 31 | ) 32 | 33 | type key int 34 | 35 | const MyKey key = 0 36 | 37 | Then set a variable. Variables are bound to an http.Request object, so you 38 | need a request instance to set a value: 39 | 40 | context.Set(r, MyKey, "bar") 41 | 42 | The application can later access the variable using the same key you provided: 43 | 44 | func MyHandler(w http.ResponseWriter, r *http.Request) { 45 | // val is "bar". 46 | val := context.Get(r, foo.MyKey) 47 | 48 | // returns ("bar", true) 49 | val, ok := context.GetOk(r, foo.MyKey) 50 | // ... 51 | } 52 | 53 | And that's all about the basic usage. We discuss some other ideas below. 54 | 55 | Any type can be stored in the context. To enforce a given type, make the key 56 | private and wrap Get() and Set() to accept and return values of a specific 57 | type: 58 | 59 | type key int 60 | 61 | const mykey key = 0 62 | 63 | // GetMyKey returns a value for this package from the request values. 64 | func GetMyKey(r *http.Request) SomeType { 65 | if rv := context.Get(r, mykey); rv != nil { 66 | return rv.(SomeType) 67 | } 68 | return nil 69 | } 70 | 71 | // SetMyKey sets a value for this package in the request values. 72 | func SetMyKey(r *http.Request, val SomeType) { 73 | context.Set(r, mykey, val) 74 | } 75 | 76 | Variables must be cleared at the end of a request, to remove all values 77 | that were stored. This can be done in an http.Handler, after a request was 78 | served. Just call Clear() passing the request: 79 | 80 | context.Clear(r) 81 | 82 | ...or use ClearHandler(), which conveniently wraps an http.Handler to clear 83 | variables at the end of a request lifetime. 84 | 85 | The Routers from the packages gorilla/mux and gorilla/pat call Clear() 86 | so if you are using either of them you don't need to clear the context manually. 87 | */ 88 | package context 89 | -------------------------------------------------------------------------------- /vendor/github.com/jasonlvhit/gocron/.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 | -------------------------------------------------------------------------------- /vendor/github.com/jasonlvhit/gocron/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, 辣椒面 2 | 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 met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /vendor/github.com/jasonlvhit/gocron/README.md: -------------------------------------------------------------------------------- 1 | ## goCron: A Golang Job Scheduling Package. 2 | [![GoDoc](https://godoc.org/github.com/golang/gddo?status.svg)](http://godoc.org/github.com/jasonlvhit/gocron) 3 | [![Stories in Ready](https://badge.waffle.io/jasonlvhit/gocron.png?label=ready&title=Ready)](https://waffle.io/jasonlvhit/gocron) 4 | 5 | goCron is a Golang job scheduling package which lets you run Go functions periodically at pre-determined interval using a simple, human-friendly syntax. 6 | 7 | goCron is a Golang implementation of Ruby module [clockwork]() and Python job scheduling package [schedule](), and personally, this package is my first Golang program, just for fun and practice. 8 | 9 | See also this two great articles: 10 | * [Rethinking Cron](http://adam.heroku.com/past/2010/4/13/rethinking_cron/) 11 | * [Replace Cron with Clockwork](http://adam.heroku.com/past/2010/6/30/replace_cron_with_clockwork/) 12 | 13 | Back to this package, you could just use this simple API as below, to run a cron scheduler. 14 | 15 | ``` go 16 | package main 17 | 18 | import ( 19 | "fmt" 20 | "github.com/jasonlvhit/gocron" 21 | ) 22 | 23 | func task() { 24 | fmt.Println("I am runnning task.") 25 | } 26 | 27 | func taskWithParams(a int, b string) { 28 | fmt.Println(a, b) 29 | } 30 | 31 | func main() { 32 | // Do jobs with params 33 | gocron.Every(1).Second().Do(taskWithParams, 1, "hello") 34 | 35 | // Do jobs without params 36 | gocron.Every(1).Second().Do(task) 37 | gocron.Every(2).Seconds().Do(task) 38 | gocron.Every(1).Minute().Do(task) 39 | gocron.Every(2).Minutes().Do(task) 40 | gocron.Every(1).Hour().Do(task) 41 | gocron.Every(2).Hours().Do(task) 42 | gocron.Every(1).Day().Do(task) 43 | gocron.Every(2).Days().Do(task) 44 | 45 | // Do jobs on specific weekday 46 | gocron.Every(1).Monday().Do(task) 47 | gocron.Every(1).Thursday().Do(task) 48 | 49 | // function At() take a string like 'hour:min' 50 | gocron.Every(1).Day().At("10:30").Do(task) 51 | gocron.Every(1).Monday().At("18:30").Do(task) 52 | 53 | // remove, clear and next_run 54 | _, time := gocron.NextRun() 55 | fmt.Println(time) 56 | 57 | gocron.Remove(task) 58 | gocron.Clear() 59 | 60 | // function Start start all the pending jobs 61 | <- gocron.Start() 62 | 63 | // also , you can create a your new scheduler, 64 | // to run two scheduler concurrently 65 | s := gocron.NewScheduler() 66 | s.Every(3).Seconds().Do(task) 67 | <- s.Start() 68 | 69 | } 70 | ``` 71 | and full test cases and [document](http://godoc.org/github.com/jasonlvhit/gocron) will be coming soon. 72 | 73 | Once again, thanks to the great works of Ruby clockwork and Python schedule package. BSD license is used, see the file License for detail. 74 | 75 | Hava fun! 76 | -------------------------------------------------------------------------------- /vendor/github.com/jasonlvhit/gocron/gocron.go: -------------------------------------------------------------------------------- 1 | // goCron : A Golang Job Scheduling Package. 2 | // 3 | // An in-process scheduler for periodic jobs that uses the builder pattern 4 | // for configuration. Schedule lets you run Golang functions periodically 5 | // at pre-determined intervals using a simple, human-friendly syntax. 6 | // 7 | // Inspired by the Ruby module clockwork 8 | // and 9 | // Python package schedule 10 | // 11 | // See also 12 | // http://adam.heroku.com/past/2010/4/13/rethinking_cron/ 13 | // http://adam.heroku.com/past/2010/6/30/replace_cron_with_clockwork/ 14 | // 15 | // Copyright 2014 Jason Lyu. jasonlvhit@gmail.com . 16 | // All rights reserved. 17 | // Use of this source code is governed by a BSD-style . 18 | // license that can be found in the LICENSE file. 19 | package gocron 20 | 21 | import ( 22 | "errors" 23 | "reflect" 24 | "runtime" 25 | "sort" 26 | "time" 27 | ) 28 | 29 | // Time location, default set by the time.Local (*time.Location) 30 | var loc = time.Local 31 | 32 | // Change the time location 33 | func ChangeLoc(newLocation *time.Location) { 34 | loc = newLocation 35 | } 36 | 37 | // Max number of jobs, hack it if you need. 38 | const MAXJOBNUM = 10000 39 | 40 | type Job struct { 41 | 42 | // pause interval * unit bettween runs 43 | interval uint64 44 | 45 | // the job jobFunc to run, func[jobFunc] 46 | jobFunc string 47 | // time units, ,e.g. 'minutes', 'hours'... 48 | unit string 49 | // optional time at which this job runs 50 | atTime string 51 | 52 | // datetime of last run 53 | lastRun time.Time 54 | // datetime of next run 55 | nextRun time.Time 56 | // cache the period between last an next run 57 | period time.Duration 58 | 59 | // Specific day of the week to start on 60 | startDay time.Weekday 61 | 62 | // Map for the function task store 63 | funcs map[string]interface{} 64 | 65 | // Map for function and params of function 66 | fparams map[string]([]interface{}) 67 | } 68 | 69 | // Create a new job with the time interval. 70 | func NewJob(intervel uint64) *Job { 71 | return &Job{ 72 | intervel, 73 | "", "", "", 74 | time.Unix(0, 0), 75 | time.Unix(0, 0), 0, 76 | time.Sunday, 77 | make(map[string]interface{}), 78 | make(map[string]([]interface{})), 79 | } 80 | } 81 | 82 | // True if the job should be run now 83 | func (j *Job) shouldRun() bool { 84 | return time.Now().After(j.nextRun) 85 | } 86 | 87 | //Run the job and immdiately reschedulei it 88 | func (j *Job) run() (result []reflect.Value, err error) { 89 | f := reflect.ValueOf(j.funcs[j.jobFunc]) 90 | params := j.fparams[j.jobFunc] 91 | if len(params) != f.Type().NumIn() { 92 | err = errors.New("The number of param is not adapted.") 93 | return 94 | } 95 | in := make([]reflect.Value, len(params)) 96 | for k, param := range params { 97 | in[k] = reflect.ValueOf(param) 98 | } 99 | result = f.Call(in) 100 | j.lastRun = time.Now() 101 | j.scheduleNextRun() 102 | return 103 | } 104 | 105 | // for given function fn , get the name of funciton. 106 | func getFunctionName(fn interface{}) string { 107 | return runtime.FuncForPC(reflect.ValueOf((fn)).Pointer()).Name() 108 | } 109 | 110 | // Specifies the jobFunc that should be called every time the job runs 111 | // 112 | func (j *Job) Do(jobFun interface{}, params ...interface{}) { 113 | typ := reflect.TypeOf(jobFun) 114 | if typ.Kind() != reflect.Func { 115 | panic("only function can be schedule into the job queue.") 116 | } 117 | 118 | fname := getFunctionName(jobFun) 119 | j.funcs[fname] = jobFun 120 | j.fparams[fname] = params 121 | j.jobFunc = fname 122 | //schedule the next run 123 | j.scheduleNextRun() 124 | } 125 | 126 | // s.Every(1).Day().At("10:30").Do(task) 127 | // s.Every(1).Monday().At("10:30").Do(task) 128 | func (j *Job) At(t string) *Job { 129 | hour := int((t[0]-'0')*10 + (t[1] - '0')) 130 | min := int((t[3]-'0')*10 + (t[4] - '0')) 131 | if hour < 0 || hour > 23 || min < 0 || min > 59 { 132 | panic("time format error.") 133 | } 134 | // time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) 135 | mock := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), int(hour), int(min), 0, 0, loc) 136 | 137 | if j.unit == "days" { 138 | if time.Now().After(mock) { 139 | j.lastRun = mock 140 | } else { 141 | j.lastRun = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day()-1, hour, min, 0, 0, loc) 142 | } 143 | } else if j.unit == "weeks" { 144 | if time.Now().After(mock) { 145 | i := mock.Weekday() - j.startDay 146 | if i < 0 { 147 | i = 7 + i 148 | } 149 | j.lastRun = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day()-int(i), hour, min, 0, 0, loc) 150 | } else { 151 | j.lastRun = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day()-7, hour, min, 0, 0, loc) 152 | } 153 | } 154 | return j 155 | } 156 | 157 | //Compute the instant when this job should run next 158 | func (j *Job) scheduleNextRun() { 159 | if j.lastRun == time.Unix(0, 0) { 160 | if j.unit == "weeks" { 161 | i := time.Now().Weekday() - j.startDay 162 | if i < 0 { 163 | i = 7 + i 164 | } 165 | j.lastRun = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day()-int(i), 0, 0, 0, 0, loc) 166 | 167 | } else { 168 | j.lastRun = time.Now() 169 | } 170 | } 171 | 172 | if j.period != 0 { 173 | // translate all the units to the Seconds 174 | j.nextRun = j.lastRun.Add(j.period * time.Second) 175 | } else { 176 | switch j.unit { 177 | case "minutes": 178 | j.period = time.Duration(j.interval * 60) 179 | break 180 | case "hours": 181 | j.period = time.Duration(j.interval * 60 * 60) 182 | break 183 | case "days": 184 | j.period = time.Duration(j.interval * 60 * 60 * 24) 185 | break 186 | case "weeks": 187 | j.period = time.Duration(j.interval * 60 * 60 * 24 * 7) 188 | break 189 | case "seconds": 190 | j.period = time.Duration(j.interval) 191 | } 192 | j.nextRun = j.lastRun.Add(j.period * time.Second) 193 | } 194 | } 195 | 196 | // the follow functions set the job's unit with seconds,minutes,hours... 197 | 198 | // Set the unit with second 199 | func (j *Job) Second() (job *Job) { 200 | if j.interval != 1 { 201 | panic("") 202 | } 203 | job = j.Seconds() 204 | return 205 | } 206 | 207 | // Set the unit with seconds 208 | func (j *Job) Seconds() (job *Job) { 209 | j.unit = "seconds" 210 | return j 211 | } 212 | 213 | // Set the unit with minute, which interval is 1 214 | func (j *Job) Minute() (job *Job) { 215 | if j.interval != 1 { 216 | panic("") 217 | } 218 | job = j.Minutes() 219 | return 220 | } 221 | 222 | //set the unit with minute 223 | func (j *Job) Minutes() (job *Job) { 224 | j.unit = "minutes" 225 | return j 226 | } 227 | 228 | //set the unit with hour, which interval is 1 229 | func (j *Job) Hour() (job *Job) { 230 | if j.interval != 1 { 231 | panic("") 232 | } 233 | job = j.Hours() 234 | return 235 | } 236 | 237 | // Set the unit with hours 238 | func (j *Job) Hours() (job *Job) { 239 | j.unit = "hours" 240 | return j 241 | } 242 | 243 | // Set the job's unit with day, which interval is 1 244 | func (j *Job) Day() (job *Job) { 245 | if j.interval != 1 { 246 | panic("") 247 | } 248 | job = j.Days() 249 | return 250 | } 251 | 252 | // Set the job's unit with days 253 | func (j *Job) Days() *Job { 254 | j.unit = "days" 255 | return j 256 | } 257 | 258 | /* 259 | // Set the unit with week, which the interval is 1 260 | func (j *Job) Week() (job *Job) { 261 | if j.interval != 1 { 262 | panic("") 263 | } 264 | job = j.Weeks() 265 | return 266 | } 267 | 268 | */ 269 | 270 | // s.Every(1).Monday().Do(task) 271 | // Set the start day with Monday 272 | func (j *Job) Monday() (job *Job) { 273 | if j.interval != 1 { 274 | panic("") 275 | } 276 | j.startDay = 1 277 | job = j.Weeks() 278 | return 279 | } 280 | 281 | // Set the start day with Tuesday 282 | func (j *Job) Tuesday() (job *Job) { 283 | if j.interval != 1 { 284 | panic("") 285 | } 286 | j.startDay = 2 287 | job = j.Weeks() 288 | return 289 | } 290 | 291 | // Set the start day woth Wednesday 292 | func (j *Job) Wednesday() (job *Job) { 293 | if j.interval != 1 { 294 | panic("") 295 | } 296 | j.startDay = 3 297 | job = j.Weeks() 298 | return 299 | } 300 | 301 | // Set the start day with thursday 302 | func (j *Job) Thursday() (job *Job) { 303 | if j.interval != 1 { 304 | panic("") 305 | } 306 | j.startDay = 4 307 | job = j.Weeks() 308 | return 309 | } 310 | 311 | // Set the start day with friday 312 | func (j *Job) Friday() (job *Job) { 313 | if j.interval != 1 { 314 | panic("") 315 | } 316 | j.startDay = 5 317 | job = j.Weeks() 318 | return 319 | } 320 | 321 | // Set the start day with saturday 322 | func (j *Job) Saturday() (job *Job) { 323 | if j.interval != 1 { 324 | panic("") 325 | } 326 | j.startDay = 6 327 | job = j.Weeks() 328 | return 329 | } 330 | 331 | // Set the start day with sunday 332 | func (j *Job) Sunday() (job *Job) { 333 | if j.interval != 1 { 334 | panic("") 335 | } 336 | j.startDay = 0 337 | job = j.Weeks() 338 | return 339 | } 340 | 341 | //Set the units as weeks 342 | func (j *Job) Weeks() *Job { 343 | j.unit = "weeks" 344 | return j 345 | } 346 | 347 | // Class Scheduler, the only data member is the list of jobs. 348 | type Scheduler struct { 349 | // Array store jobs 350 | jobs [MAXJOBNUM]*Job 351 | 352 | // Size of jobs which jobs holding. 353 | size int 354 | } 355 | 356 | // Scheduler implements the sort.Interface{} for sorting jobs, by the time nextRun 357 | 358 | func (s *Scheduler) Len() int { 359 | return s.size 360 | } 361 | 362 | func (s *Scheduler) Swap(i, j int) { 363 | s.jobs[i], s.jobs[j] = s.jobs[j], s.jobs[i] 364 | } 365 | 366 | func (s *Scheduler) Less(i, j int) bool { 367 | return s.jobs[j].nextRun.After(s.jobs[i].nextRun) 368 | } 369 | 370 | // Create a new scheduler 371 | func NewScheduler() *Scheduler { 372 | return &Scheduler{[MAXJOBNUM]*Job{}, 0} 373 | } 374 | 375 | // Get the current runnable jobs, which shouldRun is True 376 | func (s *Scheduler) getRunnableJobs() (running_jobs [MAXJOBNUM]*Job, n int) { 377 | runnableJobs := [MAXJOBNUM]*Job{} 378 | n = 0 379 | sort.Sort(s) 380 | for i := 0; i < s.size; i++ { 381 | if s.jobs[i].shouldRun() { 382 | 383 | runnableJobs[n] = s.jobs[i] 384 | //fmt.Println(runnableJobs) 385 | n++ 386 | } else { 387 | break 388 | } 389 | } 390 | return runnableJobs, n 391 | } 392 | 393 | // Datetime when the next job should run. 394 | func (s *Scheduler) NextRun() (*Job, time.Time) { 395 | if s.size <= 0 { 396 | return nil, time.Now() 397 | } 398 | sort.Sort(s) 399 | return s.jobs[0], s.jobs[0].nextRun 400 | } 401 | 402 | // Schedule a new periodic job 403 | func (s *Scheduler) Every(interval uint64) *Job { 404 | job := NewJob(interval) 405 | s.jobs[s.size] = job 406 | s.size++ 407 | return job 408 | } 409 | 410 | // Run all the jobs that are scheduled to run. 411 | func (s *Scheduler) RunPending() { 412 | runnableJobs, n := s.getRunnableJobs() 413 | 414 | if n != 0 { 415 | for i := 0; i < n; i++ { 416 | runnableJobs[i].run() 417 | } 418 | } 419 | } 420 | 421 | // Run all jobs regardless if they are scheduled to run or not 422 | func (s *Scheduler) RunAll() { 423 | for i := 0; i < s.size; i++ { 424 | s.jobs[i].run() 425 | } 426 | } 427 | 428 | // Run all jobs with delay seconds 429 | func (s *Scheduler) RunAllwithDelay(d int) { 430 | for i := 0; i < s.size; i++ { 431 | s.jobs[i].run() 432 | time.Sleep(time.Duration(d)) 433 | } 434 | } 435 | 436 | // Remove specific job j 437 | func (s *Scheduler) Remove(j interface{}) { 438 | i := 0 439 | for ; i < s.size; i++ { 440 | if s.jobs[i].jobFunc == getFunctionName(j) { 441 | break 442 | } 443 | } 444 | 445 | for j := (i + 1); j < s.size; j++ { 446 | s.jobs[i] = s.jobs[j] 447 | i++ 448 | } 449 | s.size = s.size - 1 450 | } 451 | 452 | // Delete all scheduled jobs 453 | func (s *Scheduler) Clear() { 454 | for i := 0; i < s.size; i++ { 455 | s.jobs[i] = nil 456 | } 457 | s.size = 0 458 | } 459 | 460 | // Start all the pending jobs 461 | // Add seconds ticker 462 | func (s *Scheduler) Start() chan bool { 463 | stopped := make(chan bool, 1) 464 | ticker := time.NewTicker(1 * time.Second) 465 | 466 | go func() { 467 | for { 468 | select { 469 | case <-ticker.C: 470 | s.RunPending() 471 | case <-stopped: 472 | return 473 | } 474 | } 475 | }() 476 | 477 | return stopped 478 | } 479 | 480 | // The following methods are shortcuts for not having to 481 | // create a Schduler instance 482 | 483 | var defaultScheduler = NewScheduler() 484 | var jobs = defaultScheduler.jobs 485 | 486 | // Schedule a new periodic job 487 | func Every(interval uint64) *Job { 488 | return defaultScheduler.Every(interval) 489 | } 490 | 491 | // Run all jobs that are scheduled to run 492 | // 493 | // Please note that it is *intended behavior that run_pending() 494 | // does not run missed jobs*. For example, if you've registered a job 495 | // that should run every minute and you only call run_pending() 496 | // in one hour increments then your job won't be run 60 times in 497 | // between but only once. 498 | func RunPending() { 499 | defaultScheduler.RunPending() 500 | } 501 | 502 | // Run all jobs regardless if they are scheduled to run or not. 503 | func RunAll() { 504 | defaultScheduler.RunAll() 505 | } 506 | 507 | // Run all the jobs with a delay in seconds 508 | // 509 | // A delay of `delay` seconds is added between each job. This can help 510 | // to distribute the system load generated by the jobs more evenly over 511 | // time. 512 | func RunAllwithDelay(d int) { 513 | defaultScheduler.RunAllwithDelay(d) 514 | } 515 | 516 | // Run all jobs that are scheduled to run 517 | func Start() chan bool { 518 | return defaultScheduler.Start() 519 | } 520 | 521 | // Clear 522 | func Clear() { 523 | defaultScheduler.Clear() 524 | } 525 | 526 | // Remove 527 | func Remove(j interface{}) { 528 | defaultScheduler.Remove(j) 529 | } 530 | 531 | // NextRun gets the next running time 532 | func NextRun() (job *Job, time time.Time) { 533 | return defaultScheduler.NextRun() 534 | } 535 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/now/Guardfile: -------------------------------------------------------------------------------- 1 | guard 'gotest' do 2 | watch(%r{\.go$}) 3 | end 4 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/now/README.md: -------------------------------------------------------------------------------- 1 | ## Now 2 | 3 | Now is a time toolkit for golang 4 | 5 | #### Why the project named `Now`? 6 | 7 | ```go 8 | now.BeginningOfDay() 9 | ``` 10 | `now` is quite readable, aha? 11 | 12 | #### But `now` is so common I can't search the project with my favorite search engine 13 | 14 | * Star it in github [https://github.com/jinzhu/now](https://github.com/jinzhu/now) 15 | * Documentation at [godoc.org](https://godoc.org/github.com/jinzhu/now) 16 | 17 | ## Install 18 | 19 | ``` 20 | go get -u github.com/jinzhu/now 21 | ``` 22 | 23 | ### Usage 24 | 25 | ```go 26 | import "github.com/jinzhu/now" 27 | 28 | time.Now() // 2013-11-18 17:51:49.123456789 Mon 29 | 30 | now.BeginningOfMinute() // 2013-11-18 17:51:00 Mon 31 | now.BeginningOfHour() // 2013-11-18 17:00:00 Mon 32 | now.BeginningOfDay() // 2013-11-18 00:00:00 Mon 33 | now.BeginningOfWeek() // 2013-11-17 00:00:00 Sun 34 | now.FirstDayMonday = true // Set Monday as first day, default is Sunday 35 | now.BeginningOfWeek() // 2013-11-18 00:00:00 Mon 36 | now.BeginningOfMonth() // 2013-11-01 00:00:00 Fri 37 | now.BeginningOfQuarter() // 2013-10-01 00:00:00 Tue 38 | now.BeginningOfYear() // 2013-01-01 00:00:00 Tue 39 | 40 | now.EndOfMinute() // 2013-11-18 17:51:59.999999999 Mon 41 | now.EndOfHour() // 2013-11-18 17:59:59.999999999 Mon 42 | now.EndOfDay() // 2013-11-18 23:59:59.999999999 Mon 43 | now.EndOfWeek() // 2013-11-23 23:59:59.999999999 Sat 44 | now.FirstDayMonday = true // Set Monday as first day, default is Sunday 45 | now.EndOfWeek() // 2013-11-24 23:59:59.999999999 Sun 46 | now.EndOfMonth() // 2013-11-30 23:59:59.999999999 Sat 47 | now.EndOfQuarter() // 2013-12-31 23:59:59.999999999 Tue 48 | now.EndOfYear() // 2013-12-31 23:59:59.999999999 Tue 49 | 50 | 51 | // Use another time 52 | t := time.Date(2013, 02, 18, 17, 51, 49, 123456789, time.Now().Location()) 53 | now.New(t).EndOfMonth() // 2013-02-28 23:59:59.999999999 Thu 54 | 55 | 56 | // Don't want be bothered with the First Day setting, Use Monday, Sunday 57 | now.Monday() // 2013-11-18 00:00:00 Mon 58 | now.Sunday() // 2013-11-24 00:00:00 Sun (Next Sunday) 59 | now.EndOfSunday() // 2013-11-24 23:59:59.999999999 Sun (End of next Sunday) 60 | 61 | t := time.Date(2013, 11, 24, 17, 51, 49, 123456789, time.Now().Location()) // 2013-11-24 17:51:49.123456789 Sun 62 | now.New(t).Monday() // 2013-11-18 00:00:00 Sun (Last Monday if today is Sunday) 63 | now.New(t).Sunday() // 2013-11-24 00:00:00 Sun (Beginning Of Today if today is Sunday) 64 | now.New(t).EndOfSunday() // 2013-11-24 23:59:59.999999999 Sun (End of Today if today is Sunday) 65 | ``` 66 | 67 | #### Parse String 68 | 69 | ```go 70 | time.Now() // 2013-11-18 17:51:49.123456789 Mon 71 | 72 | // Parse(string) (time.Time, error) 73 | t, err := now.Parse("12:20") // 2013-11-18 12:20:00, nil 74 | t, err := now.Parse("1999-12-12 12:20") // 1999-12-12 12:20:00, nil 75 | t, err := now.Parse("99:99") // 2013-11-18 12:20:00, Can't parse string as time: 99:99 76 | 77 | // MustParse(string) time.Time 78 | now.MustParse("2013-01-13") // 2013-01-13 00:00:00 79 | now.MustParse("02-17") // 2013-02-17 00:00:00 80 | now.MustParse("2-17") // 2013-02-17 00:00:00 81 | now.MustParse("8") // 2013-11-18 08:00:00 82 | now.MustParse("2002-10-12 22:14") // 2002-10-12 22:14:00 83 | now.MustParse("99:99") // panic: Can't parse string as time: 99:99 84 | ``` 85 | 86 | Extend `now` to support more formats is quite easy, just update `TimeFormats` variable with `time.Format` like time layout 87 | 88 | ```go 89 | now.TimeFormats = append(now.TimeFormats, "02 Jan 2006 15:04") 90 | ``` 91 | 92 | Please send me pull requests if you want a format to be supported officially 93 | 94 | 95 | # Supporting the project 96 | 97 | [![http://patreon.com/jinzhu](http://patreon_public_assets.s3.amazonaws.com/sized/becomeAPatronBanner.png)](http://patreon.com/jinzhu) 98 | 99 | 100 | # Author 101 | 102 | **jinzhu** 103 | 104 | * 105 | * 106 | * 107 | 108 | ## License 109 | 110 | Released under the [MIT License](http://www.opensource.org/licenses/MIT). 111 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/now/main.go: -------------------------------------------------------------------------------- 1 | // Package now is a time toolkit for golang. 2 | // 3 | // More details README here: https://github.com/jinzhu/now 4 | // 5 | // import "github.com/jinzhu/now" 6 | // 7 | // now.BeginningOfMinute() // 2013-11-18 17:51:00 Mon 8 | // now.BeginningOfDay() // 2013-11-18 00:00:00 Mon 9 | // now.EndOfDay() // 2013-11-18 23:59:59.999999999 Mon 10 | package now 11 | 12 | import "time" 13 | 14 | var FirstDayMonday bool 15 | var TimeFormats = []string{"1/2/2006", "1/2/2006 15:4:5", "2006-1-2 15:4:5", "2006-1-2 15:4", "2006-1-2", "1-2", "15:4:5", "15:4", "15", "15:4:5 Jan 2, 2006 MST", "2006-01-02 15:04:05.999999999 -0700 MST"} 16 | 17 | type Now struct { 18 | time.Time 19 | } 20 | 21 | func New(t time.Time) *Now { 22 | return &Now{t} 23 | } 24 | 25 | func BeginningOfMinute() time.Time { 26 | return New(time.Now()).BeginningOfMinute() 27 | } 28 | 29 | func BeginningOfHour() time.Time { 30 | return New(time.Now()).BeginningOfHour() 31 | } 32 | 33 | func BeginningOfDay() time.Time { 34 | return New(time.Now()).BeginningOfDay() 35 | } 36 | 37 | func BeginningOfWeek() time.Time { 38 | return New(time.Now()).BeginningOfWeek() 39 | } 40 | 41 | func BeginningOfMonth() time.Time { 42 | return New(time.Now()).BeginningOfMonth() 43 | } 44 | 45 | func BeginningOfQuarter() time.Time { 46 | return New(time.Now()).BeginningOfQuarter() 47 | } 48 | 49 | func BeginningOfYear() time.Time { 50 | return New(time.Now()).BeginningOfYear() 51 | } 52 | 53 | func EndOfMinute() time.Time { 54 | return New(time.Now()).EndOfMinute() 55 | } 56 | 57 | func EndOfHour() time.Time { 58 | return New(time.Now()).EndOfHour() 59 | } 60 | 61 | func EndOfDay() time.Time { 62 | return New(time.Now()).EndOfDay() 63 | } 64 | 65 | func EndOfWeek() time.Time { 66 | return New(time.Now()).EndOfWeek() 67 | } 68 | 69 | func EndOfMonth() time.Time { 70 | return New(time.Now()).EndOfMonth() 71 | } 72 | 73 | func EndOfQuarter() time.Time { 74 | return New(time.Now()).EndOfQuarter() 75 | } 76 | 77 | func EndOfYear() time.Time { 78 | return New(time.Now()).EndOfYear() 79 | } 80 | 81 | func Monday() time.Time { 82 | return New(time.Now()).Monday() 83 | } 84 | 85 | func Sunday() time.Time { 86 | return New(time.Now()).Sunday() 87 | } 88 | 89 | func EndOfSunday() time.Time { 90 | return New(time.Now()).EndOfSunday() 91 | } 92 | 93 | func Parse(strs ...string) (time.Time, error) { 94 | return New(time.Now()).Parse(strs...) 95 | } 96 | 97 | func MustParse(strs ...string) time.Time { 98 | return New(time.Now()).MustParse(strs...) 99 | } 100 | 101 | func Between(time1, time2 string) bool { 102 | return New(time.Now()).Between(time1, time2) 103 | } 104 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/now/now.go: -------------------------------------------------------------------------------- 1 | package now 2 | 3 | import ( 4 | "errors" 5 | "regexp" 6 | "time" 7 | ) 8 | 9 | func (now *Now) BeginningOfMinute() time.Time { 10 | return now.Truncate(time.Minute) 11 | } 12 | 13 | func (now *Now) BeginningOfHour() time.Time { 14 | return now.Truncate(time.Hour) 15 | } 16 | 17 | func (now *Now) BeginningOfDay() time.Time { 18 | d := time.Duration(-now.Hour()) * time.Hour 19 | return now.BeginningOfHour().Add(d) 20 | } 21 | 22 | func (now *Now) BeginningOfWeek() time.Time { 23 | t := now.BeginningOfDay() 24 | weekday := int(t.Weekday()) 25 | if FirstDayMonday { 26 | if weekday == 0 { 27 | weekday = 7 28 | } 29 | weekday = weekday - 1 30 | } 31 | 32 | d := time.Duration(-weekday) * 24 * time.Hour 33 | return t.Add(d) 34 | } 35 | 36 | func (now *Now) BeginningOfMonth() time.Time { 37 | t := now.BeginningOfDay() 38 | d := time.Duration(-int(t.Day())+1) * 24 * time.Hour 39 | return t.Add(d) 40 | } 41 | 42 | func (now *Now) BeginningOfQuarter() time.Time { 43 | month := now.BeginningOfMonth() 44 | offset := (int(month.Month()) - 1) % 3 45 | return month.AddDate(0, -offset, 0) 46 | } 47 | 48 | func (now *Now) BeginningOfYear() time.Time { 49 | t := now.BeginningOfDay() 50 | d := time.Duration(-int(t.YearDay())+1) * 24 * time.Hour 51 | return t.Truncate(time.Hour).Add(d) 52 | } 53 | 54 | func (now *Now) EndOfMinute() time.Time { 55 | return now.BeginningOfMinute().Add(time.Minute - time.Nanosecond) 56 | } 57 | 58 | func (now *Now) EndOfHour() time.Time { 59 | return now.BeginningOfHour().Add(time.Hour - time.Nanosecond) 60 | } 61 | 62 | func (now *Now) EndOfDay() time.Time { 63 | return now.BeginningOfDay().Add(24*time.Hour - time.Nanosecond) 64 | } 65 | 66 | func (now *Now) EndOfWeek() time.Time { 67 | return now.BeginningOfWeek().AddDate(0, 0, 7).Add(-time.Nanosecond) 68 | } 69 | 70 | func (now *Now) EndOfMonth() time.Time { 71 | return now.BeginningOfMonth().AddDate(0, 1, 0).Add(-time.Nanosecond) 72 | } 73 | 74 | func (now *Now) EndOfQuarter() time.Time { 75 | return now.BeginningOfQuarter().AddDate(0, 3, 0).Add(-time.Nanosecond) 76 | } 77 | 78 | func (now *Now) EndOfYear() time.Time { 79 | return now.BeginningOfYear().AddDate(1, 0, 0).Add(-time.Nanosecond) 80 | } 81 | 82 | func (now *Now) Monday() time.Time { 83 | t := now.BeginningOfDay() 84 | weekday := int(t.Weekday()) 85 | if weekday == 0 { 86 | weekday = 7 87 | } 88 | d := time.Duration(-weekday+1) * 24 * time.Hour 89 | return t.Truncate(time.Hour).Add(d) 90 | } 91 | 92 | func (now *Now) Sunday() time.Time { 93 | t := now.BeginningOfDay() 94 | weekday := int(t.Weekday()) 95 | if weekday == 0 { 96 | return t 97 | } else { 98 | d := time.Duration(7-weekday) * 24 * time.Hour 99 | return t.Truncate(time.Hour).Add(d) 100 | } 101 | } 102 | 103 | func (now *Now) EndOfSunday() time.Time { 104 | return now.Sunday().Add(24*time.Hour - time.Nanosecond) 105 | } 106 | 107 | func parseWithFormat(str string) (t time.Time, err error) { 108 | for _, format := range TimeFormats { 109 | t, err = time.Parse(format, str) 110 | if err == nil { 111 | return 112 | } 113 | } 114 | err = errors.New("Can't parse string as time: " + str) 115 | return 116 | } 117 | 118 | func (now *Now) Parse(strs ...string) (t time.Time, err error) { 119 | var setCurrentTime bool 120 | parseTime := []int{} 121 | currentTime := []int{now.Second(), now.Minute(), now.Hour(), now.Day(), int(now.Month()), now.Year()} 122 | currentLocation := now.Location() 123 | 124 | for _, str := range strs { 125 | onlyTime := regexp.MustCompile(`^\s*\d+(:\d+)*\s*$`).MatchString(str) // match 15:04:05, 15 126 | 127 | t, err = parseWithFormat(str) 128 | location := t.Location() 129 | if location.String() == "UTC" { 130 | location = currentLocation 131 | } 132 | 133 | if err == nil { 134 | parseTime = []int{t.Second(), t.Minute(), t.Hour(), t.Day(), int(t.Month()), t.Year()} 135 | onlyTime = onlyTime && (parseTime[3] == 1) && (parseTime[4] == 1) 136 | 137 | for i, v := range parseTime { 138 | // Don't reset hour, minute, second if it is a time only string 139 | if onlyTime && i <= 2 { 140 | continue 141 | } 142 | 143 | // Fill up missed information with current time 144 | if v == 0 { 145 | if setCurrentTime { 146 | parseTime[i] = currentTime[i] 147 | } 148 | } else { 149 | setCurrentTime = true 150 | } 151 | 152 | // Default day and month is 1, fill up it if missing it 153 | if onlyTime { 154 | if i == 3 || i == 4 { 155 | parseTime[i] = currentTime[i] 156 | continue 157 | } 158 | } 159 | } 160 | } 161 | 162 | if len(parseTime) > 0 { 163 | t = time.Date(parseTime[5], time.Month(parseTime[4]), parseTime[3], parseTime[2], parseTime[1], parseTime[0], 0, location) 164 | currentTime = []int{t.Second(), t.Minute(), t.Hour(), t.Day(), int(t.Month()), t.Year()} 165 | } 166 | } 167 | return 168 | } 169 | 170 | func (now *Now) MustParse(strs ...string) (t time.Time) { 171 | t, err := now.Parse(strs...) 172 | if err != nil { 173 | panic(err) 174 | } 175 | return t 176 | } 177 | 178 | func (now *Now) Between(time1, time2 string) bool { 179 | restime := now.MustParse(time1) 180 | restime2 := now.MustParse(time2) 181 | return now.After(restime) && now.Before(restime2) 182 | } 183 | -------------------------------------------------------------------------------- /vendor/github.com/julienschmidt/httprouter/.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: go 3 | go: 4 | - 1.1 5 | - 1.2 6 | - 1.3 7 | - 1.4 8 | - 1.5 9 | - 1.6 10 | - 1.7 11 | - tip 12 | -------------------------------------------------------------------------------- /vendor/github.com/julienschmidt/httprouter/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Julien Schmidt. All rights reserved. 2 | 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * The names of the contributors may not be used to endorse or promote 12 | products derived from this software without specific prior written 13 | permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL JULIEN SCHMIDT BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /vendor/github.com/julienschmidt/httprouter/README.md: -------------------------------------------------------------------------------- 1 | # HttpRouter [![Build Status](https://travis-ci.org/julienschmidt/httprouter.svg?branch=master)](https://travis-ci.org/julienschmidt/httprouter) [![GoDoc](https://godoc.org/github.com/julienschmidt/httprouter?status.svg)](http://godoc.org/github.com/julienschmidt/httprouter) 2 | 3 | HttpRouter is a lightweight high performance HTTP request router (also called *multiplexer* or just *mux* for short) for [Go](https://golang.org/). 4 | 5 | In contrast to the [default mux][http.ServeMux] of Go's `net/http` package, this router supports variables in the routing pattern and matches against the request method. It also scales better. 6 | 7 | The router is optimized for high performance and a small memory footprint. It scales well even with very long paths and a large number of routes. A compressing dynamic trie (radix tree) structure is used for efficient matching. 8 | 9 | ## Features 10 | 11 | **Only explicit matches:** With other routers, like [`http.ServeMux`][http.ServeMux], a requested URL path could match multiple patterns. Therefore they have some awkward pattern priority rules, like *longest match* or *first registered, first matched*. By design of this router, a request can only match exactly one or no route. As a result, there are also no unintended matches, which makes it great for SEO and improves the user experience. 12 | 13 | **Stop caring about trailing slashes:** Choose the URL style you like, the router automatically redirects the client if a trailing slash is missing or if there is one extra. Of course it only does so, if the new path has a handler. If you don't like it, you can [turn off this behavior](https://godoc.org/github.com/julienschmidt/httprouter#Router.RedirectTrailingSlash). 14 | 15 | **Path auto-correction:** Besides detecting the missing or additional trailing slash at no extra cost, the router can also fix wrong cases and remove superfluous path elements (like `../` or `//`). Is [CAPTAIN CAPS LOCK](http://www.urbandictionary.com/define.php?term=Captain+Caps+Lock) one of your users? HttpRouter can help him by making a case-insensitive look-up and redirecting him to the correct URL. 16 | 17 | **Parameters in your routing pattern:** Stop parsing the requested URL path, just give the path segment a name and the router delivers the dynamic value to you. Because of the design of the router, path parameters are very cheap. 18 | 19 | **Zero Garbage:** The matching and dispatching process generates zero bytes of garbage. In fact, the only heap allocations that are made, is by building the slice of the key-value pairs for path parameters. If the request path contains no parameters, not a single heap allocation is necessary. 20 | 21 | **Best Performance:** [Benchmarks speak for themselves][benchmark]. See below for technical details of the implementation. 22 | 23 | **No more server crashes:** You can set a [Panic handler][Router.PanicHandler] to deal with panics occurring during handling a HTTP request. The router then recovers and lets the `PanicHandler` log what happened and deliver a nice error page. 24 | 25 | **Perfect for APIs:** The router design encourages to build sensible, hierarchical RESTful APIs. Moreover it has builtin native support for [OPTIONS requests](http://zacstewart.com/2012/04/14/http-options-method.html) and `405 Method Not Allowed` replies. 26 | 27 | Of course you can also set **custom [`NotFound`][Router.NotFound] and [`MethodNotAllowed`](https://godoc.org/github.com/julienschmidt/httprouter#Router.MethodNotAllowed) handlers** and [**serve static files**][Router.ServeFiles]. 28 | 29 | ## Usage 30 | 31 | This is just a quick introduction, view the [GoDoc](http://godoc.org/github.com/julienschmidt/httprouter) for details. 32 | 33 | Let's start with a trivial example: 34 | 35 | ```go 36 | package main 37 | 38 | import ( 39 | "fmt" 40 | "github.com/julienschmidt/httprouter" 41 | "net/http" 42 | "log" 43 | ) 44 | 45 | func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { 46 | fmt.Fprint(w, "Welcome!\n") 47 | } 48 | 49 | func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { 50 | fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name")) 51 | } 52 | 53 | func main() { 54 | router := httprouter.New() 55 | router.GET("/", Index) 56 | router.GET("/hello/:name", Hello) 57 | 58 | log.Fatal(http.ListenAndServe(":8080", router)) 59 | } 60 | ``` 61 | 62 | ### Named parameters 63 | 64 | As you can see, `:name` is a *named parameter*. The values are accessible via `httprouter.Params`, which is just a slice of `httprouter.Param`s. You can get the value of a parameter either by its index in the slice, or by using the `ByName(name)` method: `:name` can be retrived by `ByName("name")`. 65 | 66 | Named parameters only match a single path segment: 67 | 68 | ``` 69 | Pattern: /user/:user 70 | 71 | /user/gordon match 72 | /user/you match 73 | /user/gordon/profile no match 74 | /user/ no match 75 | ``` 76 | 77 | **Note:** Since this router has only explicit matches, you can not register static routes and parameters for the same path segment. For example you can not register the patterns `/user/new` and `/user/:user` for the same request method at the same time. The routing of different request methods is independent from each other. 78 | 79 | ### Catch-All parameters 80 | 81 | The second type are *catch-all* parameters and have the form `*name`. Like the name suggests, they match everything. Therefore they must always be at the **end** of the pattern: 82 | 83 | ``` 84 | Pattern: /src/*filepath 85 | 86 | /src/ match 87 | /src/somefile.go match 88 | /src/subdir/somefile.go match 89 | ``` 90 | 91 | ## How does it work? 92 | 93 | The router relies on a tree structure which makes heavy use of *common prefixes*, it is basically a *compact* [*prefix tree*](https://en.wikipedia.org/wiki/Trie) (or just [*Radix tree*](https://en.wikipedia.org/wiki/Radix_tree)). Nodes with a common prefix also share a common parent. Here is a short example what the routing tree for the `GET` request method could look like: 94 | 95 | ``` 96 | Priority Path Handle 97 | 9 \ *<1> 98 | 3 ├s nil 99 | 2 |├earch\ *<2> 100 | 1 |└upport\ *<3> 101 | 2 ├blog\ *<4> 102 | 1 | └:post nil 103 | 1 | └\ *<5> 104 | 2 ├about-us\ *<6> 105 | 1 | └team\ *<7> 106 | 1 └contact\ *<8> 107 | ``` 108 | 109 | Every `*` represents the memory address of a handler function (a pointer). If you follow a path trough the tree from the root to the leaf, you get the complete route path, e.g `\blog\:post\`, where `:post` is just a placeholder ([*parameter*](#named-parameters)) for an actual post name. Unlike hash-maps, a tree structure also allows us to use dynamic parts like the `:post` parameter, since we actually match against the routing patterns instead of just comparing hashes. [As benchmarks show][benchmark], this works very well and efficient. 110 | 111 | Since URL paths have a hierarchical structure and make use only of a limited set of characters (byte values), it is very likely that there are a lot of common prefixes. This allows us to easily reduce the routing into ever smaller problems. Moreover the router manages a separate tree for every request method. For one thing it is more space efficient than holding a method->handle map in every single node, for another thing is also allows us to greatly reduce the routing problem before even starting the look-up in the prefix-tree. 112 | 113 | For even better scalability, the child nodes on each tree level are ordered by priority, where the priority is just the number of handles registered in sub nodes (children, grandchildren, and so on..). This helps in two ways: 114 | 115 | 1. Nodes which are part of the most routing paths are evaluated first. This helps to make as much routes as possible to be reachable as fast as possible. 116 | 2. It is some sort of cost compensation. The longest reachable path (highest cost) can always be evaluated first. The following scheme visualizes the tree structure. Nodes are evaluated from top to bottom and from left to right. 117 | 118 | ``` 119 | ├------------ 120 | ├--------- 121 | ├----- 122 | ├---- 123 | ├-- 124 | ├-- 125 | └- 126 | ``` 127 | 128 | ## Why doesn't this work with `http.Handler`? 129 | 130 | **It does!** The router itself implements the `http.Handler` interface. Moreover the router provides convenient [adapters for `http.Handler`][Router.Handler]s and [`http.HandlerFunc`][Router.HandlerFunc]s which allows them to be used as a [`httprouter.Handle`][Router.Handle] when registering a route. The only disadvantage is, that no parameter values can be retrieved when a `http.Handler` or `http.HandlerFunc` is used, since there is no efficient way to pass the values with the existing function parameters. Therefore [`httprouter.Handle`][Router.Handle] has a third function parameter. 131 | 132 | Just try it out for yourself, the usage of HttpRouter is very straightforward. The package is compact and minimalistic, but also probably one of the easiest routers to set up. 133 | 134 | ## Where can I find Middleware *X*? 135 | 136 | This package just provides a very efficient request router with a few extra features. The router is just a [`http.Handler`][http.Handler], you can chain any http.Handler compatible middleware before the router, for example the [Gorilla handlers](http://www.gorillatoolkit.org/pkg/handlers). Or you could [just write your own](https://justinas.org/writing-http-middleware-in-go/), it's very easy! 137 | 138 | Alternatively, you could try [a web framework based on HttpRouter](#web-frameworks-based-on-httprouter). 139 | 140 | ### Multi-domain / Sub-domains 141 | 142 | Here is a quick example: Does your server serve multiple domains / hosts? 143 | You want to use sub-domains? 144 | Define a router per host! 145 | 146 | ```go 147 | // We need an object that implements the http.Handler interface. 148 | // Therefore we need a type for which we implement the ServeHTTP method. 149 | // We just use a map here, in which we map host names (with port) to http.Handlers 150 | type HostSwitch map[string]http.Handler 151 | 152 | // Implement the ServerHTTP method on our new type 153 | func (hs HostSwitch) ServeHTTP(w http.ResponseWriter, r *http.Request) { 154 | // Check if a http.Handler is registered for the given host. 155 | // If yes, use it to handle the request. 156 | if handler := hs[r.Host]; handler != nil { 157 | handler.ServeHTTP(w, r) 158 | } else { 159 | // Handle host names for wich no handler is registered 160 | http.Error(w, "Forbidden", 403) // Or Redirect? 161 | } 162 | } 163 | 164 | func main() { 165 | // Initialize a router as usual 166 | router := httprouter.New() 167 | router.GET("/", Index) 168 | router.GET("/hello/:name", Hello) 169 | 170 | // Make a new HostSwitch and insert the router (our http handler) 171 | // for example.com and port 12345 172 | hs := make(HostSwitch) 173 | hs["example.com:12345"] = router 174 | 175 | // Use the HostSwitch to listen and serve on port 12345 176 | log.Fatal(http.ListenAndServe(":12345", hs)) 177 | } 178 | ``` 179 | 180 | ### Basic Authentication 181 | 182 | Another quick example: Basic Authentication (RFC 2617) for handles: 183 | 184 | ```go 185 | package main 186 | 187 | import ( 188 | "fmt" 189 | "log" 190 | "net/http" 191 | 192 | "github.com/julienschmidt/httprouter" 193 | ) 194 | 195 | func BasicAuth(h httprouter.Handle, requiredUser, requiredPassword string) httprouter.Handle { 196 | return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { 197 | // Get the Basic Authentication credentials 198 | user, password, hasAuth := r.BasicAuth() 199 | 200 | if hasAuth && user == requiredUser && password == requiredPassword { 201 | // Delegate request to the given handle 202 | h(w, r, ps) 203 | } else { 204 | // Request Basic Authentication otherwise 205 | w.Header().Set("WWW-Authenticate", "Basic realm=Restricted") 206 | http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) 207 | } 208 | } 209 | } 210 | 211 | func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { 212 | fmt.Fprint(w, "Not protected!\n") 213 | } 214 | 215 | func Protected(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { 216 | fmt.Fprint(w, "Protected!\n") 217 | } 218 | 219 | func main() { 220 | user := "gordon" 221 | pass := "secret!" 222 | 223 | router := httprouter.New() 224 | router.GET("/", Index) 225 | router.GET("/protected/", BasicAuth(Protected, user, pass)) 226 | 227 | log.Fatal(http.ListenAndServe(":8080", router)) 228 | } 229 | ``` 230 | 231 | ## Chaining with the NotFound handler 232 | 233 | **NOTE: It might be required to set [`Router.HandleMethodNotAllowed`][Router.HandleMethodNotAllowed] to `false` to avoid problems.** 234 | 235 | You can use another [`http.Handler`][http.Handler], for example another router, to handle requests which could not be matched by this router by using the [`Router.NotFound`][Router.NotFound] handler. This allows chaining. 236 | 237 | ### Static files 238 | 239 | The `NotFound` handler can for example be used to serve static files from the root path `/` (like an `index.html` file along with other assets): 240 | 241 | ```go 242 | // Serve static files from the ./public directory 243 | router.NotFound = http.FileServer(http.Dir("public")) 244 | ``` 245 | 246 | But this approach sidesteps the strict core rules of this router to avoid routing problems. A cleaner approach is to use a distinct sub-path for serving files, like `/static/*filepath` or `/files/*filepath`. 247 | 248 | ## Web Frameworks based on HttpRouter 249 | 250 | If the HttpRouter is a bit too minimalistic for you, you might try one of the following more high-level 3rd-party web frameworks building upon the HttpRouter package: 251 | 252 | * [Ace](https://github.com/plimble/ace): Blazing fast Go Web Framework 253 | * [api2go](https://github.com/manyminds/api2go): A JSON API Implementation for Go 254 | * [Gin](https://github.com/gin-gonic/gin): Features a martini-like API with much better performance 255 | * [Goat](https://github.com/bahlo/goat): A minimalistic REST API server in Go 256 | * [Hikaru](https://github.com/najeira/hikaru): Supports standalone and Google AppEngine 257 | * [Hitch](https://github.com/nbio/hitch): Hitch ties httprouter, [httpcontext](https://github.com/nbio/httpcontext), and middleware up in a bow 258 | * [httpway](https://github.com/corneldamian/httpway): Simple middleware extension with context for httprouter and a server with gracefully shutdown support 259 | * [kami](https://github.com/guregu/kami): A tiny web framework using x/net/context 260 | * [Medeina](https://github.com/imdario/medeina): Inspired by Ruby's Roda and Cuba 261 | * [Neko](https://github.com/rocwong/neko): A lightweight web application framework for Golang 262 | * [River](https://github.com/abiosoft/river): River is a simple and lightweight REST server 263 | * [Roxanna](https://github.com/iamthemuffinman/Roxanna): An amalgamation of httprouter, better logging, and hot reload 264 | * [siesta](https://github.com/VividCortex/siesta): Composable HTTP handlers with contexts 265 | * [xmux](https://github.com/rs/xmux): xmux is a httprouter fork on top of xhandler (net/context aware) 266 | 267 | [benchmark]: 268 | [http.Handler]: 270 | [Router.Handle]: 271 | [Router.HandleMethodNotAllowed]: 272 | [Router.Handler]: 273 | [Router.HandlerFunc]: 274 | [Router.NotFound]: 275 | [Router.PanicHandler]: 276 | [Router.ServeFiles]: 277 | -------------------------------------------------------------------------------- /vendor/github.com/julienschmidt/httprouter/path.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Julien Schmidt. All rights reserved. 2 | // Based on the path package, Copyright 2009 The Go Authors. 3 | // Use of this source code is governed by a BSD-style license that can be found 4 | // in the LICENSE file. 5 | 6 | package httprouter 7 | 8 | // CleanPath is the URL version of path.Clean, it returns a canonical URL path 9 | // for p, eliminating . and .. elements. 10 | // 11 | // The following rules are applied iteratively until no further processing can 12 | // be done: 13 | // 1. Replace multiple slashes with a single slash. 14 | // 2. Eliminate each . path name element (the current directory). 15 | // 3. Eliminate each inner .. path name element (the parent directory) 16 | // along with the non-.. element that precedes it. 17 | // 4. Eliminate .. elements that begin a rooted path: 18 | // that is, replace "/.." by "/" at the beginning of a path. 19 | // 20 | // If the result of this process is an empty string, "/" is returned 21 | func CleanPath(p string) string { 22 | // Turn empty string into "/" 23 | if p == "" { 24 | return "/" 25 | } 26 | 27 | n := len(p) 28 | var buf []byte 29 | 30 | // Invariants: 31 | // reading from path; r is index of next byte to process. 32 | // writing to buf; w is index of next byte to write. 33 | 34 | // path must start with '/' 35 | r := 1 36 | w := 1 37 | 38 | if p[0] != '/' { 39 | r = 0 40 | buf = make([]byte, n+1) 41 | buf[0] = '/' 42 | } 43 | 44 | trailing := n > 2 && p[n-1] == '/' 45 | 46 | // A bit more clunky without a 'lazybuf' like the path package, but the loop 47 | // gets completely inlined (bufApp). So in contrast to the path package this 48 | // loop has no expensive function calls (except 1x make) 49 | 50 | for r < n { 51 | switch { 52 | case p[r] == '/': 53 | // empty path element, trailing slash is added after the end 54 | r++ 55 | 56 | case p[r] == '.' && r+1 == n: 57 | trailing = true 58 | r++ 59 | 60 | case p[r] == '.' && p[r+1] == '/': 61 | // . element 62 | r++ 63 | 64 | case p[r] == '.' && p[r+1] == '.' && (r+2 == n || p[r+2] == '/'): 65 | // .. element: remove to last / 66 | r += 2 67 | 68 | if w > 1 { 69 | // can backtrack 70 | w-- 71 | 72 | if buf == nil { 73 | for w > 1 && p[w] != '/' { 74 | w-- 75 | } 76 | } else { 77 | for w > 1 && buf[w] != '/' { 78 | w-- 79 | } 80 | } 81 | } 82 | 83 | default: 84 | // real path element. 85 | // add slash if needed 86 | if w > 1 { 87 | bufApp(&buf, p, w, '/') 88 | w++ 89 | } 90 | 91 | // copy element 92 | for r < n && p[r] != '/' { 93 | bufApp(&buf, p, w, p[r]) 94 | w++ 95 | r++ 96 | } 97 | } 98 | } 99 | 100 | // re-append trailing slash 101 | if trailing && w > 1 { 102 | bufApp(&buf, p, w, '/') 103 | w++ 104 | } 105 | 106 | if buf == nil { 107 | return p[:w] 108 | } 109 | return string(buf[:w]) 110 | } 111 | 112 | // internal helper to lazily create a buffer if necessary 113 | func bufApp(buf *[]byte, s string, w int, c byte) { 114 | if *buf == nil { 115 | if s[w] == c { 116 | return 117 | } 118 | 119 | *buf = make([]byte, len(s)) 120 | copy(*buf, s[:w]) 121 | } 122 | (*buf)[w] = c 123 | } 124 | -------------------------------------------------------------------------------- /vendor/github.com/julienschmidt/httprouter/router.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Julien Schmidt. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be found 3 | // in the LICENSE file. 4 | 5 | // Package httprouter is a trie based high performance HTTP request router. 6 | // 7 | // A trivial example is: 8 | // 9 | // package main 10 | // 11 | // import ( 12 | // "fmt" 13 | // "github.com/julienschmidt/httprouter" 14 | // "net/http" 15 | // "log" 16 | // ) 17 | // 18 | // func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { 19 | // fmt.Fprint(w, "Welcome!\n") 20 | // } 21 | // 22 | // func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { 23 | // fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name")) 24 | // } 25 | // 26 | // func main() { 27 | // router := httprouter.New() 28 | // router.GET("/", Index) 29 | // router.GET("/hello/:name", Hello) 30 | // 31 | // log.Fatal(http.ListenAndServe(":8080", router)) 32 | // } 33 | // 34 | // The router matches incoming requests by the request method and the path. 35 | // If a handle is registered for this path and method, the router delegates the 36 | // request to that function. 37 | // For the methods GET, POST, PUT, PATCH and DELETE shortcut functions exist to 38 | // register handles, for all other methods router.Handle can be used. 39 | // 40 | // The registered path, against which the router matches incoming requests, can 41 | // contain two types of parameters: 42 | // Syntax Type 43 | // :name named parameter 44 | // *name catch-all parameter 45 | // 46 | // Named parameters are dynamic path segments. They match anything until the 47 | // next '/' or the path end: 48 | // Path: /blog/:category/:post 49 | // 50 | // Requests: 51 | // /blog/go/request-routers match: category="go", post="request-routers" 52 | // /blog/go/request-routers/ no match, but the router would redirect 53 | // /blog/go/ no match 54 | // /blog/go/request-routers/comments no match 55 | // 56 | // Catch-all parameters match anything until the path end, including the 57 | // directory index (the '/' before the catch-all). Since they match anything 58 | // until the end, catch-all parameters must always be the final path element. 59 | // Path: /files/*filepath 60 | // 61 | // Requests: 62 | // /files/ match: filepath="/" 63 | // /files/LICENSE match: filepath="/LICENSE" 64 | // /files/templates/article.html match: filepath="/templates/article.html" 65 | // /files no match, but the router would redirect 66 | // 67 | // The value of parameters is saved as a slice of the Param struct, consisting 68 | // each of a key and a value. The slice is passed to the Handle func as a third 69 | // parameter. 70 | // There are two ways to retrieve the value of a parameter: 71 | // // by the name of the parameter 72 | // user := ps.ByName("user") // defined by :user or *user 73 | // 74 | // // by the index of the parameter. This way you can also get the name (key) 75 | // thirdKey := ps[2].Key // the name of the 3rd parameter 76 | // thirdValue := ps[2].Value // the value of the 3rd parameter 77 | package httprouter 78 | 79 | import ( 80 | "net/http" 81 | ) 82 | 83 | // Handle is a function that can be registered to a route to handle HTTP 84 | // requests. Like http.HandlerFunc, but has a third parameter for the values of 85 | // wildcards (variables). 86 | type Handle func(http.ResponseWriter, *http.Request, Params) 87 | 88 | // Param is a single URL parameter, consisting of a key and a value. 89 | type Param struct { 90 | Key string 91 | Value string 92 | } 93 | 94 | // Params is a Param-slice, as returned by the router. 95 | // The slice is ordered, the first URL parameter is also the first slice value. 96 | // It is therefore safe to read values by the index. 97 | type Params []Param 98 | 99 | // ByName returns the value of the first Param which key matches the given name. 100 | // If no matching Param is found, an empty string is returned. 101 | func (ps Params) ByName(name string) string { 102 | for i := range ps { 103 | if ps[i].Key == name { 104 | return ps[i].Value 105 | } 106 | } 107 | return "" 108 | } 109 | 110 | // Router is a http.Handler which can be used to dispatch requests to different 111 | // handler functions via configurable routes 112 | type Router struct { 113 | trees map[string]*node 114 | 115 | // Enables automatic redirection if the current route can't be matched but a 116 | // handler for the path with (without) the trailing slash exists. 117 | // For example if /foo/ is requested but a route only exists for /foo, the 118 | // client is redirected to /foo with http status code 301 for GET requests 119 | // and 307 for all other request methods. 120 | RedirectTrailingSlash bool 121 | 122 | // If enabled, the router tries to fix the current request path, if no 123 | // handle is registered for it. 124 | // First superfluous path elements like ../ or // are removed. 125 | // Afterwards the router does a case-insensitive lookup of the cleaned path. 126 | // If a handle can be found for this route, the router makes a redirection 127 | // to the corrected path with status code 301 for GET requests and 307 for 128 | // all other request methods. 129 | // For example /FOO and /..//Foo could be redirected to /foo. 130 | // RedirectTrailingSlash is independent of this option. 131 | RedirectFixedPath bool 132 | 133 | // If enabled, the router checks if another method is allowed for the 134 | // current route, if the current request can not be routed. 135 | // If this is the case, the request is answered with 'Method Not Allowed' 136 | // and HTTP status code 405. 137 | // If no other Method is allowed, the request is delegated to the NotFound 138 | // handler. 139 | HandleMethodNotAllowed bool 140 | 141 | // If enabled, the router automatically replies to OPTIONS requests. 142 | // Custom OPTIONS handlers take priority over automatic replies. 143 | HandleOPTIONS bool 144 | 145 | // Configurable http.Handler which is called when no matching route is 146 | // found. If it is not set, http.NotFound is used. 147 | NotFound http.Handler 148 | 149 | // Configurable http.Handler which is called when a request 150 | // cannot be routed and HandleMethodNotAllowed is true. 151 | // If it is not set, http.Error with http.StatusMethodNotAllowed is used. 152 | // The "Allow" header with allowed request methods is set before the handler 153 | // is called. 154 | MethodNotAllowed http.Handler 155 | 156 | // Function to handle panics recovered from http handlers. 157 | // It should be used to generate a error page and return the http error code 158 | // 500 (Internal Server Error). 159 | // The handler can be used to keep your server from crashing because of 160 | // unrecovered panics. 161 | PanicHandler func(http.ResponseWriter, *http.Request, interface{}) 162 | } 163 | 164 | // Make sure the Router conforms with the http.Handler interface 165 | var _ http.Handler = New() 166 | 167 | // New returns a new initialized Router. 168 | // Path auto-correction, including trailing slashes, is enabled by default. 169 | func New() *Router { 170 | return &Router{ 171 | RedirectTrailingSlash: true, 172 | RedirectFixedPath: true, 173 | HandleMethodNotAllowed: true, 174 | HandleOPTIONS: true, 175 | } 176 | } 177 | 178 | // GET is a shortcut for router.Handle("GET", path, handle) 179 | func (r *Router) GET(path string, handle Handle) { 180 | r.Handle("GET", path, handle) 181 | } 182 | 183 | // HEAD is a shortcut for router.Handle("HEAD", path, handle) 184 | func (r *Router) HEAD(path string, handle Handle) { 185 | r.Handle("HEAD", path, handle) 186 | } 187 | 188 | // OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle) 189 | func (r *Router) OPTIONS(path string, handle Handle) { 190 | r.Handle("OPTIONS", path, handle) 191 | } 192 | 193 | // POST is a shortcut for router.Handle("POST", path, handle) 194 | func (r *Router) POST(path string, handle Handle) { 195 | r.Handle("POST", path, handle) 196 | } 197 | 198 | // PUT is a shortcut for router.Handle("PUT", path, handle) 199 | func (r *Router) PUT(path string, handle Handle) { 200 | r.Handle("PUT", path, handle) 201 | } 202 | 203 | // PATCH is a shortcut for router.Handle("PATCH", path, handle) 204 | func (r *Router) PATCH(path string, handle Handle) { 205 | r.Handle("PATCH", path, handle) 206 | } 207 | 208 | // DELETE is a shortcut for router.Handle("DELETE", path, handle) 209 | func (r *Router) DELETE(path string, handle Handle) { 210 | r.Handle("DELETE", path, handle) 211 | } 212 | 213 | // Handle registers a new request handle with the given path and method. 214 | // 215 | // For GET, POST, PUT, PATCH and DELETE requests the respective shortcut 216 | // functions can be used. 217 | // 218 | // This function is intended for bulk loading and to allow the usage of less 219 | // frequently used, non-standardized or custom methods (e.g. for internal 220 | // communication with a proxy). 221 | func (r *Router) Handle(method, path string, handle Handle) { 222 | if path[0] != '/' { 223 | panic("path must begin with '/' in path '" + path + "'") 224 | } 225 | 226 | if r.trees == nil { 227 | r.trees = make(map[string]*node) 228 | } 229 | 230 | root := r.trees[method] 231 | if root == nil { 232 | root = new(node) 233 | r.trees[method] = root 234 | } 235 | 236 | root.addRoute(path, handle) 237 | } 238 | 239 | // Handler is an adapter which allows the usage of an http.Handler as a 240 | // request handle. 241 | func (r *Router) Handler(method, path string, handler http.Handler) { 242 | r.Handle(method, path, 243 | func(w http.ResponseWriter, req *http.Request, _ Params) { 244 | handler.ServeHTTP(w, req) 245 | }, 246 | ) 247 | } 248 | 249 | // HandlerFunc is an adapter which allows the usage of an http.HandlerFunc as a 250 | // request handle. 251 | func (r *Router) HandlerFunc(method, path string, handler http.HandlerFunc) { 252 | r.Handler(method, path, handler) 253 | } 254 | 255 | // ServeFiles serves files from the given file system root. 256 | // The path must end with "/*filepath", files are then served from the local 257 | // path /defined/root/dir/*filepath. 258 | // For example if root is "/etc" and *filepath is "passwd", the local file 259 | // "/etc/passwd" would be served. 260 | // Internally a http.FileServer is used, therefore http.NotFound is used instead 261 | // of the Router's NotFound handler. 262 | // To use the operating system's file system implementation, 263 | // use http.Dir: 264 | // router.ServeFiles("/src/*filepath", http.Dir("/var/www")) 265 | func (r *Router) ServeFiles(path string, root http.FileSystem) { 266 | if len(path) < 10 || path[len(path)-10:] != "/*filepath" { 267 | panic("path must end with /*filepath in path '" + path + "'") 268 | } 269 | 270 | fileServer := http.FileServer(root) 271 | 272 | r.GET(path, func(w http.ResponseWriter, req *http.Request, ps Params) { 273 | req.URL.Path = ps.ByName("filepath") 274 | fileServer.ServeHTTP(w, req) 275 | }) 276 | } 277 | 278 | func (r *Router) recv(w http.ResponseWriter, req *http.Request) { 279 | if rcv := recover(); rcv != nil { 280 | r.PanicHandler(w, req, rcv) 281 | } 282 | } 283 | 284 | // Lookup allows the manual lookup of a method + path combo. 285 | // This is e.g. useful to build a framework around this router. 286 | // If the path was found, it returns the handle function and the path parameter 287 | // values. Otherwise the third return value indicates whether a redirection to 288 | // the same path with an extra / without the trailing slash should be performed. 289 | func (r *Router) Lookup(method, path string) (Handle, Params, bool) { 290 | if root := r.trees[method]; root != nil { 291 | return root.getValue(path) 292 | } 293 | return nil, nil, false 294 | } 295 | 296 | func (r *Router) allowed(path, reqMethod string) (allow string) { 297 | if path == "*" { // server-wide 298 | for method := range r.trees { 299 | if method == "OPTIONS" { 300 | continue 301 | } 302 | 303 | // add request method to list of allowed methods 304 | if len(allow) == 0 { 305 | allow = method 306 | } else { 307 | allow += ", " + method 308 | } 309 | } 310 | } else { // specific path 311 | for method := range r.trees { 312 | // Skip the requested method - we already tried this one 313 | if method == reqMethod || method == "OPTIONS" { 314 | continue 315 | } 316 | 317 | handle, _, _ := r.trees[method].getValue(path) 318 | if handle != nil { 319 | // add request method to list of allowed methods 320 | if len(allow) == 0 { 321 | allow = method 322 | } else { 323 | allow += ", " + method 324 | } 325 | } 326 | } 327 | } 328 | if len(allow) > 0 { 329 | allow += ", OPTIONS" 330 | } 331 | return 332 | } 333 | 334 | // ServeHTTP makes the router implement the http.Handler interface. 335 | func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { 336 | if r.PanicHandler != nil { 337 | defer r.recv(w, req) 338 | } 339 | 340 | path := req.URL.Path 341 | 342 | if root := r.trees[req.Method]; root != nil { 343 | if handle, ps, tsr := root.getValue(path); handle != nil { 344 | handle(w, req, ps) 345 | return 346 | } else if req.Method != "CONNECT" && path != "/" { 347 | code := 301 // Permanent redirect, request with GET method 348 | if req.Method != "GET" { 349 | // Temporary redirect, request with same method 350 | // As of Go 1.3, Go does not support status code 308. 351 | code = 307 352 | } 353 | 354 | if tsr && r.RedirectTrailingSlash { 355 | if len(path) > 1 && path[len(path)-1] == '/' { 356 | req.URL.Path = path[:len(path)-1] 357 | } else { 358 | req.URL.Path = path + "/" 359 | } 360 | http.Redirect(w, req, req.URL.String(), code) 361 | return 362 | } 363 | 364 | // Try to fix the request path 365 | if r.RedirectFixedPath { 366 | fixedPath, found := root.findCaseInsensitivePath( 367 | CleanPath(path), 368 | r.RedirectTrailingSlash, 369 | ) 370 | if found { 371 | req.URL.Path = string(fixedPath) 372 | http.Redirect(w, req, req.URL.String(), code) 373 | return 374 | } 375 | } 376 | } 377 | } 378 | 379 | if req.Method == "OPTIONS" { 380 | // Handle OPTIONS requests 381 | if r.HandleOPTIONS { 382 | if allow := r.allowed(path, req.Method); len(allow) > 0 { 383 | w.Header().Set("Allow", allow) 384 | return 385 | } 386 | } 387 | } else { 388 | // Handle 405 389 | if r.HandleMethodNotAllowed { 390 | if allow := r.allowed(path, req.Method); len(allow) > 0 { 391 | w.Header().Set("Allow", allow) 392 | if r.MethodNotAllowed != nil { 393 | r.MethodNotAllowed.ServeHTTP(w, req) 394 | } else { 395 | http.Error(w, 396 | http.StatusText(http.StatusMethodNotAllowed), 397 | http.StatusMethodNotAllowed, 398 | ) 399 | } 400 | return 401 | } 402 | } 403 | } 404 | 405 | // Handle 404 406 | if r.NotFound != nil { 407 | r.NotFound.ServeHTTP(w, req) 408 | } else { 409 | http.NotFound(w, req) 410 | } 411 | } 412 | -------------------------------------------------------------------------------- /vendor/github.com/justinas/alice/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | matrix: 4 | include: 5 | - go: 1.0 6 | - go: 1.1 7 | - go: 1.2 8 | - go: 1.3 9 | - go: 1.4 10 | - go: 1.5 11 | - go: 1.6 12 | - go: 1.7 13 | - go: tip 14 | allow_failures: 15 | - go: tip 16 | -------------------------------------------------------------------------------- /vendor/github.com/justinas/alice/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Justinas Stankevicius 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/justinas/alice/README.md: -------------------------------------------------------------------------------- 1 | # Alice 2 | 3 | [![GoDoc](https://godoc.org/github.com/golang/gddo?status.svg)](http://godoc.org/github.com/justinas/alice) 4 | [![Build Status](https://travis-ci.org/justinas/alice.svg?branch=master)](https://travis-ci.org/justinas/alice) 5 | [![Coverage](http://gocover.io/_badge/github.com/justinas/alice)](http://gocover.io/github.com/justinas/alice) 6 | 7 | Alice provides a convenient way to chain 8 | your HTTP middleware functions and the app handler. 9 | 10 | In short, it transforms 11 | 12 | ```go 13 | Middleware1(Middleware2(Middleware3(App))) 14 | ``` 15 | 16 | to 17 | 18 | ```go 19 | alice.New(Middleware1, Middleware2, Middleware3).Then(App) 20 | ``` 21 | 22 | ### Why? 23 | 24 | None of the other middleware chaining solutions 25 | behaves exactly like Alice. 26 | Alice is as minimal as it gets: 27 | in essence, it's just a for loop that does the wrapping for you. 28 | 29 | Check out [this blog post](http://justinas.org/alice-painless-middleware-chaining-for-go/) 30 | for explanation how Alice is different from other chaining solutions. 31 | 32 | ### Usage 33 | 34 | Your middleware constructors should have the form of 35 | 36 | ```go 37 | func (http.Handler) http.Handler 38 | ``` 39 | 40 | Some middleware provide this out of the box. 41 | For ones that don't, it's trivial to write one yourself. 42 | 43 | ```go 44 | func myStripPrefix(h http.Handler) http.Handler { 45 | return http.StripPrefix("/old", h) 46 | } 47 | ``` 48 | 49 | This complete example shows the full power of Alice. 50 | 51 | ```go 52 | package main 53 | 54 | import ( 55 | "net/http" 56 | "time" 57 | 58 | "github.com/throttled/throttled" 59 | "github.com/justinas/alice" 60 | "github.com/justinas/nosurf" 61 | ) 62 | 63 | func timeoutHandler(h http.Handler) http.Handler { 64 | return http.TimeoutHandler(h, 1*time.Second, "timed out") 65 | } 66 | 67 | func myApp(w http.ResponseWriter, r *http.Request) { 68 | w.Write([]byte("Hello world!")) 69 | } 70 | 71 | func main() { 72 | th := throttled.Interval(throttled.PerSec(10), 1, &throttled.VaryBy{Path: true}, 50) 73 | myHandler := http.HandlerFunc(myApp) 74 | 75 | chain := alice.New(th.Throttle, timeoutHandler, nosurf.NewPure).Then(myHandler) 76 | http.ListenAndServe(":8000", chain) 77 | } 78 | ``` 79 | 80 | Here, the request will pass [throttled](https://github.com/PuerkitoBio/throttled) first, 81 | then an http.TimeoutHandler we've set up, 82 | then [nosurf](https://github.com/justinas/nosurf) 83 | and will finally reach our handler. 84 | 85 | Note that Alice makes **no guarantees** for 86 | how one or another piece of middleware will behave. 87 | Once it passes the execution to the outer layer of middleware, 88 | it has no saying in whether middleware will execute the inner handlers. 89 | This is intentional behavior. 90 | 91 | Alice works with Go 1.0 and higher. 92 | 93 | ### Contributing 94 | 95 | 0. Find an issue that bugs you / open a new one. 96 | 1. Discuss. 97 | 2. Branch off, commit, test. 98 | 3. Make a pull request / attach the commits to the issue. 99 | -------------------------------------------------------------------------------- /vendor/github.com/justinas/alice/chain.go: -------------------------------------------------------------------------------- 1 | // Package alice provides a convenient way to chain http handlers. 2 | package alice 3 | 4 | import "net/http" 5 | 6 | // A constructor for a piece of middleware. 7 | // Some middleware use this constructor out of the box, 8 | // so in most cases you can just pass somepackage.New 9 | type Constructor func(http.Handler) http.Handler 10 | 11 | // Chain acts as a list of http.Handler constructors. 12 | // Chain is effectively immutable: 13 | // once created, it will always hold 14 | // the same set of constructors in the same order. 15 | type Chain struct { 16 | constructors []Constructor 17 | } 18 | 19 | // New creates a new chain, 20 | // memorizing the given list of middleware constructors. 21 | // New serves no other function, 22 | // constructors are only called upon a call to Then(). 23 | func New(constructors ...Constructor) Chain { 24 | return Chain{append(([]Constructor)(nil), constructors...)} 25 | } 26 | 27 | // Then chains the middleware and returns the final http.Handler. 28 | // New(m1, m2, m3).Then(h) 29 | // is equivalent to: 30 | // m1(m2(m3(h))) 31 | // When the request comes in, it will be passed to m1, then m2, then m3 32 | // and finally, the given handler 33 | // (assuming every middleware calls the following one). 34 | // 35 | // A chain can be safely reused by calling Then() several times. 36 | // stdStack := alice.New(ratelimitHandler, csrfHandler) 37 | // indexPipe = stdStack.Then(indexHandler) 38 | // authPipe = stdStack.Then(authHandler) 39 | // Note that constructors are called on every call to Then() 40 | // and thus several instances of the same middleware will be created 41 | // when a chain is reused in this way. 42 | // For proper middleware, this should cause no problems. 43 | // 44 | // Then() treats nil as http.DefaultServeMux. 45 | func (c Chain) Then(h http.Handler) http.Handler { 46 | if h == nil { 47 | h = http.DefaultServeMux 48 | } 49 | 50 | for i := range c.constructors { 51 | h = c.constructors[len(c.constructors)-1-i](h) 52 | } 53 | 54 | return h 55 | } 56 | 57 | // ThenFunc works identically to Then, but takes 58 | // a HandlerFunc instead of a Handler. 59 | // 60 | // The following two statements are equivalent: 61 | // c.Then(http.HandlerFunc(fn)) 62 | // c.ThenFunc(fn) 63 | // 64 | // ThenFunc provides all the guarantees of Then. 65 | func (c Chain) ThenFunc(fn http.HandlerFunc) http.Handler { 66 | if fn == nil { 67 | return c.Then(nil) 68 | } 69 | return c.Then(fn) 70 | } 71 | 72 | // Append extends a chain, adding the specified constructors 73 | // as the last ones in the request flow. 74 | // 75 | // Append returns a new chain, leaving the original one untouched. 76 | // 77 | // stdChain := alice.New(m1, m2) 78 | // extChain := stdChain.Append(m3, m4) 79 | // // requests in stdChain go m1 -> m2 80 | // // requests in extChain go m1 -> m2 -> m3 -> m4 81 | func (c Chain) Append(constructors ...Constructor) Chain { 82 | newCons := make([]Constructor, 0, len(c.constructors)+len(constructors)) 83 | newCons = append(newCons, c.constructors...) 84 | newCons = append(newCons, constructors...) 85 | 86 | return Chain{newCons} 87 | } 88 | 89 | // Extend extends a chain by adding the specified chain 90 | // as the last one in the request flow. 91 | // 92 | // Extend returns a new chain, leaving the original one untouched. 93 | // 94 | // stdChain := alice.New(m1, m2) 95 | // ext1Chain := alice.New(m3, m4) 96 | // ext2Chain := stdChain.Extend(ext1Chain) 97 | // // requests in stdChain go m1 -> m2 98 | // // requests in ext1Chain go m3 -> m4 99 | // // requests in ext2Chain go m1 -> m2 -> m3 -> m4 100 | // 101 | // Another example: 102 | // aHtmlAfterNosurf := alice.New(m2) 103 | // aHtml := alice.New(m1, func(h http.Handler) http.Handler { 104 | // csrf := nosurf.New(h) 105 | // csrf.SetFailureHandler(aHtmlAfterNosurf.ThenFunc(csrfFail)) 106 | // return csrf 107 | // }).Extend(aHtmlAfterNosurf) 108 | // // requests to aHtml hitting nosurfs success handler go m1 -> nosurf -> m2 -> target-handler 109 | // // requests to aHtml hitting nosurfs failure handler go m1 -> nosurf -> m2 -> csrfFail 110 | func (c Chain) Extend(chain Chain) Chain { 111 | return c.Append(chain.constructors...) 112 | } 113 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/.gitignore: -------------------------------------------------------------------------------- 1 | *.db 2 | *.exe 3 | *.dll 4 | *.o 5 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: required 3 | dist: trusty 4 | env: 5 | - GOTAGS= 6 | - GOTAGS=libsqlite3 7 | - GOTAGS=trace 8 | #- GOTAGS="libsqlite3 trace" # trusty is too old for this 9 | go: 10 | - 1.5 11 | - 1.6 12 | - tip 13 | before_install: 14 | - go get github.com/mattn/goveralls 15 | - go get golang.org/x/tools/cmd/cover 16 | script: 17 | - $HOME/gopath/bin/goveralls -repotoken 3qJVUE0iQwqnCbmNcDsjYu1nh4J4KIFXx 18 | - go test -race -v . -tags "$GOTAGS" 19 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Yasuhiro Matsumoto 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 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/README.md: -------------------------------------------------------------------------------- 1 | go-sqlite3 2 | ========== 3 | 4 | [![Build Status](https://travis-ci.org/mattn/go-sqlite3.svg?branch=master)](https://travis-ci.org/mattn/go-sqlite3) 5 | [![Coverage Status](https://coveralls.io/repos/mattn/go-sqlite3/badge.svg?branch=master)](https://coveralls.io/r/mattn/go-sqlite3?branch=master) 6 | [![GoDoc](https://godoc.org/github.com/mattn/go-sqlite3?status.svg)](http://godoc.org/github.com/mattn/go-sqlite3) 7 | 8 | Description 9 | ----------- 10 | 11 | sqlite3 driver conforming to the built-in database/sql interface 12 | 13 | Installation 14 | ------------ 15 | 16 | This package can be installed with the go get command: 17 | 18 | go get github.com/mattn/go-sqlite3 19 | 20 | _go-sqlite3_ is *cgo* package. 21 | If you want to build your app using go-sqlite3, you need gcc. 22 | However, if you install _go-sqlite3_ with `go install github.com/mattn/go-sqlite3`, you don't need gcc to build your app anymore. 23 | 24 | Documentation 25 | ------------- 26 | 27 | API documentation can be found here: http://godoc.org/github.com/mattn/go-sqlite3 28 | 29 | Examples can be found under the `./_example` directory 30 | 31 | FAQ 32 | --- 33 | 34 | * Want to build go-sqlite3 with libsqlite3 on my linux. 35 | 36 | Use `go build --tags "libsqlite3 linux"` 37 | 38 | * Want to build go-sqlite3 with libsqlite3 on OS X. 39 | 40 | Install sqlite3 from homebrew: `brew install sqlite3` 41 | 42 | Use `go build --tags "libsqlite3 darwin"` 43 | 44 | * Want to build go-sqlite3 with icu extension. 45 | 46 | Use `go build --tags "icu"` 47 | 48 | * Can't build go-sqlite3 on windows 64bit. 49 | 50 | > Probably, you are using go 1.0, go1.0 has a problem when it comes to compiling/linking on windows 64bit. 51 | > See: https://github.com/mattn/go-sqlite3/issues/27 52 | 53 | * Getting insert error while query is opened. 54 | 55 | > You can pass some arguments into the connection string, for example, a URI. 56 | > See: https://github.com/mattn/go-sqlite3/issues/39 57 | 58 | * Do you want to cross compile? mingw on Linux or Mac? 59 | 60 | > See: https://github.com/mattn/go-sqlite3/issues/106 61 | > See also: http://www.limitlessfx.com/cross-compile-golang-app-for-windows-from-linux.html 62 | 63 | * Want to get time.Time with current locale 64 | 65 | Use `loc=auto` in SQLite3 filename schema like `file:foo.db?loc=auto`. 66 | 67 | * Can use this in multiple routines concurrently? 68 | 69 | Yes for readonly. But, No for writable. See #50, #51, #209. 70 | 71 | License 72 | ------- 73 | 74 | MIT: http://mattn.mit-license.org/2012 75 | 76 | sqlite3-binding.c, sqlite3-binding.h, sqlite3ext.h 77 | 78 | The -binding suffix was added to avoid build failures under gccgo. 79 | 80 | In this repository, those files are an amalgamation of code that was copied from SQLite3. The license of that code is the same as the license of SQLite3. 81 | 82 | Author 83 | ------ 84 | 85 | Yasuhiro Matsumoto (a.k.a mattn) 86 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/backup.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2014 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package sqlite3 7 | 8 | /* 9 | #ifndef USE_LIBSQLITE3 10 | #include 11 | #else 12 | #include 13 | #endif 14 | #include 15 | */ 16 | import "C" 17 | import ( 18 | "runtime" 19 | "unsafe" 20 | ) 21 | 22 | // SQLiteBackup implement interface of Backup. 23 | type SQLiteBackup struct { 24 | b *C.sqlite3_backup 25 | } 26 | 27 | // Backup make backup from src to dest. 28 | func (c *SQLiteConn) Backup(dest string, conn *SQLiteConn, src string) (*SQLiteBackup, error) { 29 | destptr := C.CString(dest) 30 | defer C.free(unsafe.Pointer(destptr)) 31 | srcptr := C.CString(src) 32 | defer C.free(unsafe.Pointer(srcptr)) 33 | 34 | if b := C.sqlite3_backup_init(c.db, destptr, conn.db, srcptr); b != nil { 35 | bb := &SQLiteBackup{b: b} 36 | runtime.SetFinalizer(bb, (*SQLiteBackup).Finish) 37 | return bb, nil 38 | } 39 | return nil, c.lastError() 40 | } 41 | 42 | // Step to backs up for one step. Calls the underlying `sqlite3_backup_step` 43 | // function. This function returns a boolean indicating if the backup is done 44 | // and an error signalling any other error. Done is returned if the underlying 45 | // C function returns SQLITE_DONE (Code 101) 46 | func (b *SQLiteBackup) Step(p int) (bool, error) { 47 | ret := C.sqlite3_backup_step(b.b, C.int(p)) 48 | if ret == C.SQLITE_DONE { 49 | return true, nil 50 | } else if ret != 0 && ret != C.SQLITE_LOCKED && ret != C.SQLITE_BUSY { 51 | return false, Error{Code: ErrNo(ret)} 52 | } 53 | return false, nil 54 | } 55 | 56 | // Remaining return whether have the rest for backup. 57 | func (b *SQLiteBackup) Remaining() int { 58 | return int(C.sqlite3_backup_remaining(b.b)) 59 | } 60 | 61 | // PageCount return count of pages. 62 | func (b *SQLiteBackup) PageCount() int { 63 | return int(C.sqlite3_backup_pagecount(b.b)) 64 | } 65 | 66 | // Finish close backup. 67 | func (b *SQLiteBackup) Finish() error { 68 | return b.Close() 69 | } 70 | 71 | // Close close backup. 72 | func (b *SQLiteBackup) Close() error { 73 | ret := C.sqlite3_backup_finish(b.b) 74 | 75 | // sqlite3_backup_finish() never fails, it just returns the 76 | // error code from previous operations, so clean up before 77 | // checking and returning an error 78 | b.b = nil 79 | runtime.SetFinalizer(b, nil) 80 | 81 | if ret != 0 { 82 | return Error{Code: ErrNo(ret)} 83 | } 84 | return nil 85 | } 86 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/callback.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2014 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package sqlite3 7 | 8 | // You can't export a Go function to C and have definitions in the C 9 | // preamble in the same file, so we have to have callbackTrampoline in 10 | // its own file. Because we need a separate file anyway, the support 11 | // code for SQLite custom functions is in here. 12 | 13 | /* 14 | #ifndef USE_LIBSQLITE3 15 | #include 16 | #else 17 | #include 18 | #endif 19 | #include 20 | 21 | void _sqlite3_result_text(sqlite3_context* ctx, const char* s); 22 | void _sqlite3_result_blob(sqlite3_context* ctx, const void* b, int l); 23 | */ 24 | import "C" 25 | 26 | import ( 27 | "errors" 28 | "fmt" 29 | "math" 30 | "reflect" 31 | "sync" 32 | "unsafe" 33 | ) 34 | 35 | //export callbackTrampoline 36 | func callbackTrampoline(ctx *C.sqlite3_context, argc int, argv **C.sqlite3_value) { 37 | args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc] 38 | fi := lookupHandle(uintptr(C.sqlite3_user_data(ctx))).(*functionInfo) 39 | fi.Call(ctx, args) 40 | } 41 | 42 | //export stepTrampoline 43 | func stepTrampoline(ctx *C.sqlite3_context, argc C.int, argv **C.sqlite3_value) { 44 | args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:int(argc):int(argc)] 45 | ai := lookupHandle(uintptr(C.sqlite3_user_data(ctx))).(*aggInfo) 46 | ai.Step(ctx, args) 47 | } 48 | 49 | //export doneTrampoline 50 | func doneTrampoline(ctx *C.sqlite3_context) { 51 | handle := uintptr(C.sqlite3_user_data(ctx)) 52 | ai := lookupHandle(handle).(*aggInfo) 53 | ai.Done(ctx) 54 | } 55 | 56 | // Use handles to avoid passing Go pointers to C. 57 | 58 | type handleVal struct { 59 | db *SQLiteConn 60 | val interface{} 61 | } 62 | 63 | var handleLock sync.Mutex 64 | var handleVals = make(map[uintptr]handleVal) 65 | var handleIndex uintptr = 100 66 | 67 | func newHandle(db *SQLiteConn, v interface{}) uintptr { 68 | handleLock.Lock() 69 | defer handleLock.Unlock() 70 | i := handleIndex 71 | handleIndex++ 72 | handleVals[i] = handleVal{db, v} 73 | return i 74 | } 75 | 76 | func lookupHandle(handle uintptr) interface{} { 77 | handleLock.Lock() 78 | defer handleLock.Unlock() 79 | r, ok := handleVals[handle] 80 | if !ok { 81 | if handle >= 100 && handle < handleIndex { 82 | panic("deleted handle") 83 | } else { 84 | panic("invalid handle") 85 | } 86 | } 87 | return r.val 88 | } 89 | 90 | func deleteHandles(db *SQLiteConn) { 91 | handleLock.Lock() 92 | defer handleLock.Unlock() 93 | for handle, val := range handleVals { 94 | if val.db == db { 95 | delete(handleVals, handle) 96 | } 97 | } 98 | } 99 | 100 | // This is only here so that tests can refer to it. 101 | type callbackArgRaw C.sqlite3_value 102 | 103 | type callbackArgConverter func(*C.sqlite3_value) (reflect.Value, error) 104 | 105 | type callbackArgCast struct { 106 | f callbackArgConverter 107 | typ reflect.Type 108 | } 109 | 110 | func (c callbackArgCast) Run(v *C.sqlite3_value) (reflect.Value, error) { 111 | val, err := c.f(v) 112 | if err != nil { 113 | return reflect.Value{}, err 114 | } 115 | if !val.Type().ConvertibleTo(c.typ) { 116 | return reflect.Value{}, fmt.Errorf("cannot convert %s to %s", val.Type(), c.typ) 117 | } 118 | return val.Convert(c.typ), nil 119 | } 120 | 121 | func callbackArgInt64(v *C.sqlite3_value) (reflect.Value, error) { 122 | if C.sqlite3_value_type(v) != C.SQLITE_INTEGER { 123 | return reflect.Value{}, fmt.Errorf("argument must be an INTEGER") 124 | } 125 | return reflect.ValueOf(int64(C.sqlite3_value_int64(v))), nil 126 | } 127 | 128 | func callbackArgBool(v *C.sqlite3_value) (reflect.Value, error) { 129 | if C.sqlite3_value_type(v) != C.SQLITE_INTEGER { 130 | return reflect.Value{}, fmt.Errorf("argument must be an INTEGER") 131 | } 132 | i := int64(C.sqlite3_value_int64(v)) 133 | val := false 134 | if i != 0 { 135 | val = true 136 | } 137 | return reflect.ValueOf(val), nil 138 | } 139 | 140 | func callbackArgFloat64(v *C.sqlite3_value) (reflect.Value, error) { 141 | if C.sqlite3_value_type(v) != C.SQLITE_FLOAT { 142 | return reflect.Value{}, fmt.Errorf("argument must be a FLOAT") 143 | } 144 | return reflect.ValueOf(float64(C.sqlite3_value_double(v))), nil 145 | } 146 | 147 | func callbackArgBytes(v *C.sqlite3_value) (reflect.Value, error) { 148 | switch C.sqlite3_value_type(v) { 149 | case C.SQLITE_BLOB: 150 | l := C.sqlite3_value_bytes(v) 151 | p := C.sqlite3_value_blob(v) 152 | return reflect.ValueOf(C.GoBytes(p, l)), nil 153 | case C.SQLITE_TEXT: 154 | l := C.sqlite3_value_bytes(v) 155 | c := unsafe.Pointer(C.sqlite3_value_text(v)) 156 | return reflect.ValueOf(C.GoBytes(c, l)), nil 157 | default: 158 | return reflect.Value{}, fmt.Errorf("argument must be BLOB or TEXT") 159 | } 160 | } 161 | 162 | func callbackArgString(v *C.sqlite3_value) (reflect.Value, error) { 163 | switch C.sqlite3_value_type(v) { 164 | case C.SQLITE_BLOB: 165 | l := C.sqlite3_value_bytes(v) 166 | p := (*C.char)(C.sqlite3_value_blob(v)) 167 | return reflect.ValueOf(C.GoStringN(p, l)), nil 168 | case C.SQLITE_TEXT: 169 | c := (*C.char)(unsafe.Pointer(C.sqlite3_value_text(v))) 170 | return reflect.ValueOf(C.GoString(c)), nil 171 | default: 172 | return reflect.Value{}, fmt.Errorf("argument must be BLOB or TEXT") 173 | } 174 | } 175 | 176 | func callbackArgGeneric(v *C.sqlite3_value) (reflect.Value, error) { 177 | switch C.sqlite3_value_type(v) { 178 | case C.SQLITE_INTEGER: 179 | return callbackArgInt64(v) 180 | case C.SQLITE_FLOAT: 181 | return callbackArgFloat64(v) 182 | case C.SQLITE_TEXT: 183 | return callbackArgString(v) 184 | case C.SQLITE_BLOB: 185 | return callbackArgBytes(v) 186 | case C.SQLITE_NULL: 187 | // Interpret NULL as a nil byte slice. 188 | var ret []byte 189 | return reflect.ValueOf(ret), nil 190 | default: 191 | panic("unreachable") 192 | } 193 | } 194 | 195 | func callbackArg(typ reflect.Type) (callbackArgConverter, error) { 196 | switch typ.Kind() { 197 | case reflect.Interface: 198 | if typ.NumMethod() != 0 { 199 | return nil, errors.New("the only supported interface type is interface{}") 200 | } 201 | return callbackArgGeneric, nil 202 | case reflect.Slice: 203 | if typ.Elem().Kind() != reflect.Uint8 { 204 | return nil, errors.New("the only supported slice type is []byte") 205 | } 206 | return callbackArgBytes, nil 207 | case reflect.String: 208 | return callbackArgString, nil 209 | case reflect.Bool: 210 | return callbackArgBool, nil 211 | case reflect.Int64: 212 | return callbackArgInt64, nil 213 | case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint: 214 | c := callbackArgCast{callbackArgInt64, typ} 215 | return c.Run, nil 216 | case reflect.Float64: 217 | return callbackArgFloat64, nil 218 | case reflect.Float32: 219 | c := callbackArgCast{callbackArgFloat64, typ} 220 | return c.Run, nil 221 | default: 222 | return nil, fmt.Errorf("don't know how to convert to %s", typ) 223 | } 224 | } 225 | 226 | func callbackConvertArgs(argv []*C.sqlite3_value, converters []callbackArgConverter, variadic callbackArgConverter) ([]reflect.Value, error) { 227 | var args []reflect.Value 228 | 229 | if len(argv) < len(converters) { 230 | return nil, fmt.Errorf("function requires at least %d arguments", len(converters)) 231 | } 232 | 233 | for i, arg := range argv[:len(converters)] { 234 | v, err := converters[i](arg) 235 | if err != nil { 236 | return nil, err 237 | } 238 | args = append(args, v) 239 | } 240 | 241 | if variadic != nil { 242 | for _, arg := range argv[len(converters):] { 243 | v, err := variadic(arg) 244 | if err != nil { 245 | return nil, err 246 | } 247 | args = append(args, v) 248 | } 249 | } 250 | return args, nil 251 | } 252 | 253 | type callbackRetConverter func(*C.sqlite3_context, reflect.Value) error 254 | 255 | func callbackRetInteger(ctx *C.sqlite3_context, v reflect.Value) error { 256 | switch v.Type().Kind() { 257 | case reflect.Int64: 258 | case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint: 259 | v = v.Convert(reflect.TypeOf(int64(0))) 260 | case reflect.Bool: 261 | b := v.Interface().(bool) 262 | if b { 263 | v = reflect.ValueOf(int64(1)) 264 | } else { 265 | v = reflect.ValueOf(int64(0)) 266 | } 267 | default: 268 | return fmt.Errorf("cannot convert %s to INTEGER", v.Type()) 269 | } 270 | 271 | C.sqlite3_result_int64(ctx, C.sqlite3_int64(v.Interface().(int64))) 272 | return nil 273 | } 274 | 275 | func callbackRetFloat(ctx *C.sqlite3_context, v reflect.Value) error { 276 | switch v.Type().Kind() { 277 | case reflect.Float64: 278 | case reflect.Float32: 279 | v = v.Convert(reflect.TypeOf(float64(0))) 280 | default: 281 | return fmt.Errorf("cannot convert %s to FLOAT", v.Type()) 282 | } 283 | 284 | C.sqlite3_result_double(ctx, C.double(v.Interface().(float64))) 285 | return nil 286 | } 287 | 288 | func callbackRetBlob(ctx *C.sqlite3_context, v reflect.Value) error { 289 | if v.Type().Kind() != reflect.Slice || v.Type().Elem().Kind() != reflect.Uint8 { 290 | return fmt.Errorf("cannot convert %s to BLOB", v.Type()) 291 | } 292 | i := v.Interface() 293 | if i == nil || len(i.([]byte)) == 0 { 294 | C.sqlite3_result_null(ctx) 295 | } else { 296 | bs := i.([]byte) 297 | C._sqlite3_result_blob(ctx, unsafe.Pointer(&bs[0]), C.int(len(bs))) 298 | } 299 | return nil 300 | } 301 | 302 | func callbackRetText(ctx *C.sqlite3_context, v reflect.Value) error { 303 | if v.Type().Kind() != reflect.String { 304 | return fmt.Errorf("cannot convert %s to TEXT", v.Type()) 305 | } 306 | C._sqlite3_result_text(ctx, C.CString(v.Interface().(string))) 307 | return nil 308 | } 309 | 310 | func callbackRet(typ reflect.Type) (callbackRetConverter, error) { 311 | switch typ.Kind() { 312 | case reflect.Slice: 313 | if typ.Elem().Kind() != reflect.Uint8 { 314 | return nil, errors.New("the only supported slice type is []byte") 315 | } 316 | return callbackRetBlob, nil 317 | case reflect.String: 318 | return callbackRetText, nil 319 | case reflect.Bool, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint: 320 | return callbackRetInteger, nil 321 | case reflect.Float32, reflect.Float64: 322 | return callbackRetFloat, nil 323 | default: 324 | return nil, fmt.Errorf("don't know how to convert to %s", typ) 325 | } 326 | } 327 | 328 | func callbackError(ctx *C.sqlite3_context, err error) { 329 | cstr := C.CString(err.Error()) 330 | defer C.free(unsafe.Pointer(cstr)) 331 | C.sqlite3_result_error(ctx, cstr, -1) 332 | } 333 | 334 | // Test support code. Tests are not allowed to import "C", so we can't 335 | // declare any functions that use C.sqlite3_value. 336 | func callbackSyntheticForTests(v reflect.Value, err error) callbackArgConverter { 337 | return func(*C.sqlite3_value) (reflect.Value, error) { 338 | return v, err 339 | } 340 | } 341 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package sqlite3 provides interface to SQLite3 databases. 3 | 4 | This works as a driver for database/sql. 5 | 6 | Installation 7 | 8 | go get github.com/mattn/go-sqlite3 9 | 10 | Supported Types 11 | 12 | Currently, go-sqlite3 supports the following data types. 13 | 14 | +------------------------------+ 15 | |go | sqlite3 | 16 | |----------|-------------------| 17 | |nil | null | 18 | |int | integer | 19 | |int64 | integer | 20 | |float64 | float | 21 | |bool | integer | 22 | |[]byte | blob | 23 | |string | text | 24 | |time.Time | timestamp/datetime| 25 | +------------------------------+ 26 | 27 | SQLite3 Extension 28 | 29 | You can write your own extension module for sqlite3. For example, below is an 30 | extension for a Regexp matcher operation. 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | SQLITE_EXTENSION_INIT1 38 | static void regexp_func(sqlite3_context *context, int argc, sqlite3_value **argv) { 39 | if (argc >= 2) { 40 | const char *target = (const char *)sqlite3_value_text(argv[1]); 41 | const char *pattern = (const char *)sqlite3_value_text(argv[0]); 42 | const char* errstr = NULL; 43 | int erroff = 0; 44 | int vec[500]; 45 | int n, rc; 46 | pcre* re = pcre_compile(pattern, 0, &errstr, &erroff, NULL); 47 | rc = pcre_exec(re, NULL, target, strlen(target), 0, 0, vec, 500); 48 | if (rc <= 0) { 49 | sqlite3_result_error(context, errstr, 0); 50 | return; 51 | } 52 | sqlite3_result_int(context, 1); 53 | } 54 | } 55 | 56 | #ifdef _WIN32 57 | __declspec(dllexport) 58 | #endif 59 | int sqlite3_extension_init(sqlite3 *db, char **errmsg, 60 | const sqlite3_api_routines *api) { 61 | SQLITE_EXTENSION_INIT2(api); 62 | return sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, 63 | (void*)db, regexp_func, NULL, NULL); 64 | } 65 | 66 | It needs to be built as a so/dll shared library. And you need to register 67 | the extension module like below. 68 | 69 | sql.Register("sqlite3_with_extensions", 70 | &sqlite3.SQLiteDriver{ 71 | Extensions: []string{ 72 | "sqlite3_mod_regexp", 73 | }, 74 | }) 75 | 76 | Then, you can use this extension. 77 | 78 | rows, err := db.Query("select text from mytable where name regexp '^golang'") 79 | 80 | Connection Hook 81 | 82 | You can hook and inject your code when the connection is established. database/sql 83 | doesn't provide a way to get native go-sqlite3 interfaces. So if you want, 84 | you need to set ConnectHook and get the SQLiteConn. 85 | 86 | sql.Register("sqlite3_with_hook_example", 87 | &sqlite3.SQLiteDriver{ 88 | ConnectHook: func(conn *sqlite3.SQLiteConn) error { 89 | sqlite3conn = append(sqlite3conn, conn) 90 | return nil 91 | }, 92 | }) 93 | 94 | Go SQlite3 Extensions 95 | 96 | If you want to register Go functions as SQLite extension functions, 97 | call RegisterFunction from ConnectHook. 98 | 99 | regex = func(re, s string) (bool, error) { 100 | return regexp.MatchString(re, s) 101 | } 102 | sql.Register("sqlite3_with_go_func", 103 | &sqlite3.SQLiteDriver{ 104 | ConnectHook: func(conn *sqlite3.SQLiteConn) error { 105 | return conn.RegisterFunc("regexp", regex, true) 106 | }, 107 | }) 108 | 109 | See the documentation of RegisterFunc for more details. 110 | 111 | */ 112 | package sqlite3 113 | 114 | import "C" 115 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/error.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2014 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package sqlite3 7 | 8 | import "C" 9 | 10 | // ErrNo inherit errno. 11 | type ErrNo int 12 | 13 | // ErrNoMask is mask code. 14 | const ErrNoMask C.int = 0xff 15 | 16 | // ErrNoExtended is extended errno. 17 | type ErrNoExtended int 18 | 19 | // Error implement sqlite error code. 20 | type Error struct { 21 | Code ErrNo /* The error code returned by SQLite */ 22 | ExtendedCode ErrNoExtended /* The extended error code returned by SQLite */ 23 | err string /* The error string returned by sqlite3_errmsg(), 24 | this usually contains more specific details. */ 25 | } 26 | 27 | // result codes from http://www.sqlite.org/c3ref/c_abort.html 28 | var ( 29 | ErrError = ErrNo(1) /* SQL error or missing database */ 30 | ErrInternal = ErrNo(2) /* Internal logic error in SQLite */ 31 | ErrPerm = ErrNo(3) /* Access permission denied */ 32 | ErrAbort = ErrNo(4) /* Callback routine requested an abort */ 33 | ErrBusy = ErrNo(5) /* The database file is locked */ 34 | ErrLocked = ErrNo(6) /* A table in the database is locked */ 35 | ErrNomem = ErrNo(7) /* A malloc() failed */ 36 | ErrReadonly = ErrNo(8) /* Attempt to write a readonly database */ 37 | ErrInterrupt = ErrNo(9) /* Operation terminated by sqlite3_interrupt() */ 38 | ErrIoErr = ErrNo(10) /* Some kind of disk I/O error occurred */ 39 | ErrCorrupt = ErrNo(11) /* The database disk image is malformed */ 40 | ErrNotFound = ErrNo(12) /* Unknown opcode in sqlite3_file_control() */ 41 | ErrFull = ErrNo(13) /* Insertion failed because database is full */ 42 | ErrCantOpen = ErrNo(14) /* Unable to open the database file */ 43 | ErrProtocol = ErrNo(15) /* Database lock protocol error */ 44 | ErrEmpty = ErrNo(16) /* Database is empty */ 45 | ErrSchema = ErrNo(17) /* The database schema changed */ 46 | ErrTooBig = ErrNo(18) /* String or BLOB exceeds size limit */ 47 | ErrConstraint = ErrNo(19) /* Abort due to constraint violation */ 48 | ErrMismatch = ErrNo(20) /* Data type mismatch */ 49 | ErrMisuse = ErrNo(21) /* Library used incorrectly */ 50 | ErrNoLFS = ErrNo(22) /* Uses OS features not supported on host */ 51 | ErrAuth = ErrNo(23) /* Authorization denied */ 52 | ErrFormat = ErrNo(24) /* Auxiliary database format error */ 53 | ErrRange = ErrNo(25) /* 2nd parameter to sqlite3_bind out of range */ 54 | ErrNotADB = ErrNo(26) /* File opened that is not a database file */ 55 | ErrNotice = ErrNo(27) /* Notifications from sqlite3_log() */ 56 | ErrWarning = ErrNo(28) /* Warnings from sqlite3_log() */ 57 | ) 58 | 59 | // Error return error message from errno. 60 | func (err ErrNo) Error() string { 61 | return Error{Code: err}.Error() 62 | } 63 | 64 | // Extend return extended errno. 65 | func (err ErrNo) Extend(by int) ErrNoExtended { 66 | return ErrNoExtended(int(err) | (by << 8)) 67 | } 68 | 69 | // Error return error message that is extended code. 70 | func (err ErrNoExtended) Error() string { 71 | return Error{Code: ErrNo(C.int(err) & ErrNoMask), ExtendedCode: err}.Error() 72 | } 73 | 74 | // Error return error message. 75 | func (err Error) Error() string { 76 | if err.err != "" { 77 | return err.err 78 | } 79 | return errorString(err) 80 | } 81 | 82 | // result codes from http://www.sqlite.org/c3ref/c_abort_rollback.html 83 | var ( 84 | ErrIoErrRead = ErrIoErr.Extend(1) 85 | ErrIoErrShortRead = ErrIoErr.Extend(2) 86 | ErrIoErrWrite = ErrIoErr.Extend(3) 87 | ErrIoErrFsync = ErrIoErr.Extend(4) 88 | ErrIoErrDirFsync = ErrIoErr.Extend(5) 89 | ErrIoErrTruncate = ErrIoErr.Extend(6) 90 | ErrIoErrFstat = ErrIoErr.Extend(7) 91 | ErrIoErrUnlock = ErrIoErr.Extend(8) 92 | ErrIoErrRDlock = ErrIoErr.Extend(9) 93 | ErrIoErrDelete = ErrIoErr.Extend(10) 94 | ErrIoErrBlocked = ErrIoErr.Extend(11) 95 | ErrIoErrNoMem = ErrIoErr.Extend(12) 96 | ErrIoErrAccess = ErrIoErr.Extend(13) 97 | ErrIoErrCheckReservedLock = ErrIoErr.Extend(14) 98 | ErrIoErrLock = ErrIoErr.Extend(15) 99 | ErrIoErrClose = ErrIoErr.Extend(16) 100 | ErrIoErrDirClose = ErrIoErr.Extend(17) 101 | ErrIoErrSHMOpen = ErrIoErr.Extend(18) 102 | ErrIoErrSHMSize = ErrIoErr.Extend(19) 103 | ErrIoErrSHMLock = ErrIoErr.Extend(20) 104 | ErrIoErrSHMMap = ErrIoErr.Extend(21) 105 | ErrIoErrSeek = ErrIoErr.Extend(22) 106 | ErrIoErrDeleteNoent = ErrIoErr.Extend(23) 107 | ErrIoErrMMap = ErrIoErr.Extend(24) 108 | ErrIoErrGetTempPath = ErrIoErr.Extend(25) 109 | ErrIoErrConvPath = ErrIoErr.Extend(26) 110 | ErrLockedSharedCache = ErrLocked.Extend(1) 111 | ErrBusyRecovery = ErrBusy.Extend(1) 112 | ErrBusySnapshot = ErrBusy.Extend(2) 113 | ErrCantOpenNoTempDir = ErrCantOpen.Extend(1) 114 | ErrCantOpenIsDir = ErrCantOpen.Extend(2) 115 | ErrCantOpenFullPath = ErrCantOpen.Extend(3) 116 | ErrCantOpenConvPath = ErrCantOpen.Extend(4) 117 | ErrCorruptVTab = ErrCorrupt.Extend(1) 118 | ErrReadonlyRecovery = ErrReadonly.Extend(1) 119 | ErrReadonlyCantLock = ErrReadonly.Extend(2) 120 | ErrReadonlyRollback = ErrReadonly.Extend(3) 121 | ErrReadonlyDbMoved = ErrReadonly.Extend(4) 122 | ErrAbortRollback = ErrAbort.Extend(2) 123 | ErrConstraintCheck = ErrConstraint.Extend(1) 124 | ErrConstraintCommitHook = ErrConstraint.Extend(2) 125 | ErrConstraintForeignKey = ErrConstraint.Extend(3) 126 | ErrConstraintFunction = ErrConstraint.Extend(4) 127 | ErrConstraintNotNull = ErrConstraint.Extend(5) 128 | ErrConstraintPrimaryKey = ErrConstraint.Extend(6) 129 | ErrConstraintTrigger = ErrConstraint.Extend(7) 130 | ErrConstraintUnique = ErrConstraint.Extend(8) 131 | ErrConstraintVTab = ErrConstraint.Extend(9) 132 | ErrConstraintRowID = ErrConstraint.Extend(10) 133 | ErrNoticeRecoverWAL = ErrNotice.Extend(1) 134 | ErrNoticeRecoverRollback = ErrNotice.Extend(2) 135 | ErrWarningAutoIndex = ErrWarning.Extend(1) 136 | ) 137 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_fts5.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2014 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | // +build fts5 6 | 7 | package sqlite3 8 | 9 | /* 10 | #cgo CFLAGS: -DSQLITE_ENABLE_FTS5 11 | #cgo LDFLAGS: -lm 12 | */ 13 | import "C" 14 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_go18.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2014 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build go1.8 7 | 8 | package sqlite3 9 | 10 | import ( 11 | "database/sql/driver" 12 | "errors" 13 | 14 | "context" 15 | ) 16 | 17 | // Ping implement Pinger. 18 | func (c *SQLiteConn) Ping(ctx context.Context) error { 19 | if c.db == nil { 20 | return errors.New("Connection was closed") 21 | } 22 | return nil 23 | } 24 | 25 | // QueryContext implement QueryerContext. 26 | func (c *SQLiteConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { 27 | list := make([]namedValue, len(args)) 28 | for i, nv := range args { 29 | list[i] = namedValue(nv) 30 | } 31 | return c.query(ctx, query, list) 32 | } 33 | 34 | // ExecContext implement ExecerContext. 35 | func (c *SQLiteConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { 36 | list := make([]namedValue, len(args)) 37 | for i, nv := range args { 38 | list[i] = namedValue(nv) 39 | } 40 | return c.exec(ctx, query, list) 41 | } 42 | 43 | // PrepareContext implement ConnPrepareContext. 44 | func (c *SQLiteConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) { 45 | return c.prepare(ctx, query) 46 | } 47 | 48 | // BeginTx implement ConnBeginTx. 49 | func (c *SQLiteConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { 50 | return c.begin(ctx) 51 | } 52 | 53 | // QueryContext implement QueryerContext. 54 | func (s *SQLiteStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { 55 | list := make([]namedValue, len(args)) 56 | for i, nv := range args { 57 | list[i] = namedValue(nv) 58 | } 59 | return s.query(ctx, list) 60 | } 61 | 62 | // ExecContext implement ExecerContext. 63 | func (s *SQLiteStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { 64 | list := make([]namedValue, len(args)) 65 | for i, nv := range args { 66 | list[i] = namedValue(nv) 67 | } 68 | return s.exec(ctx, list) 69 | } 70 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_icu.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2014 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | // +build icu 6 | 7 | package sqlite3 8 | 9 | /* 10 | #cgo LDFLAGS: -licuuc -licui18n 11 | #cgo CFLAGS: -DSQLITE_ENABLE_ICU 12 | */ 13 | import "C" 14 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_json1.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2014 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | // +build json1 6 | 7 | package sqlite3 8 | 9 | /* 10 | #cgo CFLAGS: -DSQLITE_ENABLE_JSON1 11 | */ 12 | import "C" 13 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_libsqlite3.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2014 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | // +build libsqlite3 6 | 7 | package sqlite3 8 | 9 | /* 10 | #cgo CFLAGS: -DUSE_LIBSQLITE3 11 | #cgo linux LDFLAGS: -lsqlite3 12 | #cgo darwin LDFLAGS: -L/usr/local/opt/sqlite/lib -lsqlite3 13 | */ 14 | import "C" 15 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_load_extension.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2014 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | // +build !sqlite_omit_load_extension 6 | 7 | package sqlite3 8 | 9 | /* 10 | #ifndef USE_LIBSQLITE3 11 | #include 12 | #else 13 | #include 14 | #endif 15 | #include 16 | */ 17 | import "C" 18 | import ( 19 | "errors" 20 | "unsafe" 21 | ) 22 | 23 | func (c *SQLiteConn) loadExtensions(extensions []string) error { 24 | rv := C.sqlite3_enable_load_extension(c.db, 1) 25 | if rv != C.SQLITE_OK { 26 | return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) 27 | } 28 | 29 | for _, extension := range extensions { 30 | cext := C.CString(extension) 31 | defer C.free(unsafe.Pointer(cext)) 32 | rv = C.sqlite3_load_extension(c.db, cext, nil, nil) 33 | if rv != C.SQLITE_OK { 34 | return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) 35 | } 36 | } 37 | 38 | rv = C.sqlite3_enable_load_extension(c.db, 0) 39 | if rv != C.SQLITE_OK { 40 | return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) 41 | } 42 | return nil 43 | } 44 | 45 | // LoadExtension load the sqlite3 extension. 46 | func (c *SQLiteConn) LoadExtension(lib string, entry string) error { 47 | rv := C.sqlite3_enable_load_extension(c.db, 1) 48 | if rv != C.SQLITE_OK { 49 | return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) 50 | } 51 | 52 | clib := C.CString(lib) 53 | defer C.free(unsafe.Pointer(clib)) 54 | centry := C.CString(entry) 55 | defer C.free(unsafe.Pointer(centry)) 56 | 57 | rv = C.sqlite3_load_extension(c.db, clib, centry, nil) 58 | if rv != C.SQLITE_OK { 59 | return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) 60 | } 61 | 62 | rv = C.sqlite3_enable_load_extension(c.db, 0) 63 | if rv != C.SQLITE_OK { 64 | return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) 65 | } 66 | 67 | return nil 68 | } 69 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_omit_load_extension.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2014 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | // +build sqlite_omit_load_extension 6 | 7 | package sqlite3 8 | 9 | /* 10 | #cgo CFLAGS: -DSQLITE_OMIT_LOAD_EXTENSION 11 | */ 12 | import "C" 13 | import ( 14 | "errors" 15 | ) 16 | 17 | func (c *SQLiteConn) loadExtensions(extensions []string) error { 18 | return errors.New("Extensions have been disabled for static builds") 19 | } 20 | 21 | func (c *SQLiteConn) LoadExtension(lib string, entry string) error { 22 | return errors.New("Extensions have been disabled for static builds") 23 | } 24 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_other.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2014 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | // +build !windows 6 | 7 | package sqlite3 8 | 9 | /* 10 | #cgo CFLAGS: -I. 11 | #cgo linux LDFLAGS: -ldl 12 | */ 13 | import "C" 14 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_type.go: -------------------------------------------------------------------------------- 1 | package sqlite3 2 | 3 | /* 4 | #ifndef USE_LIBSQLITE3 5 | #include 6 | #else 7 | #include 8 | #endif 9 | */ 10 | import "C" 11 | import ( 12 | "reflect" 13 | "time" 14 | ) 15 | 16 | // ColumnTypeDatabaseTypeName implement RowsColumnTypeDatabaseTypeName. 17 | func (rc *SQLiteRows) ColumnTypeDatabaseTypeName(i int) string { 18 | return C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i))) 19 | } 20 | 21 | /* 22 | func (rc *SQLiteRows) ColumnTypeLength(index int) (length int64, ok bool) { 23 | return 0, false 24 | } 25 | 26 | func (rc *SQLiteRows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) { 27 | return 0, 0, false 28 | } 29 | */ 30 | 31 | // ColumnTypeNullable implement RowsColumnTypeNullable. 32 | func (rc *SQLiteRows) ColumnTypeNullable(i int) (nullable, ok bool) { 33 | return true, true 34 | } 35 | 36 | // ColumnTypeScanType implement RowsColumnTypeScanType. 37 | func (rc *SQLiteRows) ColumnTypeScanType(i int) reflect.Type { 38 | switch C.sqlite3_column_type(rc.s.s, C.int(i)) { 39 | case C.SQLITE_INTEGER: 40 | switch C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i))) { 41 | case "timestamp", "datetime", "date": 42 | return reflect.TypeOf(time.Time{}) 43 | case "boolean": 44 | return reflect.TypeOf(false) 45 | } 46 | return reflect.TypeOf(int64(0)) 47 | case C.SQLITE_FLOAT: 48 | return reflect.TypeOf(float64(0)) 49 | case C.SQLITE_BLOB: 50 | return reflect.SliceOf(reflect.TypeOf(byte(0))) 51 | case C.SQLITE_NULL: 52 | return reflect.TypeOf(nil) 53 | case C.SQLITE_TEXT: 54 | return reflect.TypeOf("") 55 | } 56 | return reflect.SliceOf(reflect.TypeOf(byte(0))) 57 | } 58 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2014 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | // +build windows 6 | 7 | package sqlite3 8 | 9 | /* 10 | #cgo CFLAGS: -I. -fno-stack-check -fno-stack-protector -mno-stack-arg-probe 11 | #cgo windows,386 CFLAGS: -D_USE_32BIT_TIME_T 12 | #cgo LDFLAGS: -lmingwex -lmingw32 13 | */ 14 | import "C" 15 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/tracecallback.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016 Yasuhiro Matsumoto . 2 | // TODO: add "Gimpl do foo" team? 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | // +build trace 7 | 8 | package sqlite3 9 | 10 | /* 11 | #ifndef USE_LIBSQLITE3 12 | #include 13 | #else 14 | #include 15 | #endif 16 | #include 17 | 18 | void stepTrampoline(sqlite3_context*, int, sqlite3_value**); 19 | void doneTrampoline(sqlite3_context*); 20 | int traceCallbackTrampoline(unsigned int traceEventCode, void *ctx, void *p, void *x); 21 | */ 22 | import "C" 23 | 24 | import ( 25 | "errors" 26 | "fmt" 27 | "reflect" 28 | "strings" 29 | "sync" 30 | "unsafe" 31 | ) 32 | 33 | // Trace... constants identify the possible events causing callback invocation. 34 | // Values are same as the corresponding SQLite Trace Event Codes. 35 | const ( 36 | TraceStmt = C.SQLITE_TRACE_STMT 37 | TraceProfile = C.SQLITE_TRACE_PROFILE 38 | TraceRow = C.SQLITE_TRACE_ROW 39 | TraceClose = C.SQLITE_TRACE_CLOSE 40 | ) 41 | 42 | type TraceInfo struct { 43 | // Pack together the shorter fields, to keep the struct smaller. 44 | // On a 64-bit machine there would be padding 45 | // between EventCode and ConnHandle; having AutoCommit here is "free": 46 | EventCode uint32 47 | AutoCommit bool 48 | ConnHandle uintptr 49 | 50 | // Usually filled, unless EventCode = TraceClose = SQLITE_TRACE_CLOSE: 51 | // identifier for a prepared statement: 52 | StmtHandle uintptr 53 | 54 | // Two strings filled when EventCode = TraceStmt = SQLITE_TRACE_STMT: 55 | // (1) either the unexpanded SQL text of the prepared statement, or 56 | // an SQL comment that indicates the invocation of a trigger; 57 | // (2) expanded SQL, if requested and if (1) is not an SQL comment. 58 | StmtOrTrigger string 59 | ExpandedSQL string // only if requested (TraceConfig.WantExpandedSQL = true) 60 | 61 | // filled when EventCode = TraceProfile = SQLITE_TRACE_PROFILE: 62 | // estimated number of nanoseconds that the prepared statement took to run: 63 | RunTimeNanosec int64 64 | 65 | DBError Error 66 | } 67 | 68 | // TraceUserCallback gives the signature for a trace function 69 | // provided by the user (Go application programmer). 70 | // SQLite 3.14 documentation (as of September 2, 2016) 71 | // for SQL Trace Hook = sqlite3_trace_v2(): 72 | // The integer return value from the callback is currently ignored, 73 | // though this may change in future releases. Callback implementations 74 | // should return zero to ensure future compatibility. 75 | type TraceUserCallback func(TraceInfo) int 76 | 77 | type TraceConfig struct { 78 | Callback TraceUserCallback 79 | EventMask C.uint 80 | WantExpandedSQL bool 81 | } 82 | 83 | func fillDBError(dbErr *Error, db *C.sqlite3) { 84 | // See SQLiteConn.lastError(), in file 'sqlite3.go' at the time of writing (Sept 5, 2016) 85 | dbErr.Code = ErrNo(C.sqlite3_errcode(db)) 86 | dbErr.ExtendedCode = ErrNoExtended(C.sqlite3_extended_errcode(db)) 87 | dbErr.err = C.GoString(C.sqlite3_errmsg(db)) 88 | } 89 | 90 | func fillExpandedSQL(info *TraceInfo, db *C.sqlite3, pStmt unsafe.Pointer) { 91 | if pStmt == nil { 92 | panic("No SQLite statement pointer in P arg of trace_v2 callback") 93 | } 94 | 95 | expSQLiteCStr := C.sqlite3_expanded_sql((*C.sqlite3_stmt)(pStmt)) 96 | if expSQLiteCStr == nil { 97 | fillDBError(&info.DBError, db) 98 | return 99 | } 100 | info.ExpandedSQL = C.GoString(expSQLiteCStr) 101 | } 102 | 103 | //export traceCallbackTrampoline 104 | func traceCallbackTrampoline( 105 | traceEventCode C.uint, 106 | // Parameter named 'C' in SQLite docs = Context given at registration: 107 | ctx unsafe.Pointer, 108 | // Parameter named 'P' in SQLite docs (Primary event data?): 109 | p unsafe.Pointer, 110 | // Parameter named 'X' in SQLite docs (eXtra event data?): 111 | xValue unsafe.Pointer) C.int { 112 | 113 | if ctx == nil { 114 | panic(fmt.Sprintf("No context (ev 0x%x)", traceEventCode)) 115 | } 116 | 117 | contextDB := (*C.sqlite3)(ctx) 118 | connHandle := uintptr(ctx) 119 | 120 | var traceConf TraceConfig 121 | var found bool 122 | if traceEventCode == TraceClose { 123 | // clean up traceMap: 'pop' means get and delete 124 | traceConf, found = popTraceMapping(connHandle) 125 | } else { 126 | traceConf, found = lookupTraceMapping(connHandle) 127 | } 128 | 129 | if !found { 130 | panic(fmt.Sprintf("Mapping not found for handle 0x%x (ev 0x%x)", 131 | connHandle, traceEventCode)) 132 | } 133 | 134 | var info TraceInfo 135 | 136 | info.EventCode = uint32(traceEventCode) 137 | info.AutoCommit = (int(C.sqlite3_get_autocommit(contextDB)) != 0) 138 | info.ConnHandle = connHandle 139 | 140 | switch traceEventCode { 141 | case TraceStmt: 142 | info.StmtHandle = uintptr(p) 143 | 144 | var xStr string 145 | if xValue != nil { 146 | xStr = C.GoString((*C.char)(xValue)) 147 | } 148 | info.StmtOrTrigger = xStr 149 | if !strings.HasPrefix(xStr, "--") { 150 | // Not SQL comment, therefore the current event 151 | // is not related to a trigger. 152 | // The user might want to receive the expanded SQL; 153 | // let's check: 154 | if traceConf.WantExpandedSQL { 155 | fillExpandedSQL(&info, contextDB, p) 156 | } 157 | } 158 | 159 | case TraceProfile: 160 | info.StmtHandle = uintptr(p) 161 | 162 | if xValue == nil { 163 | panic("NULL pointer in X arg of trace_v2 callback for SQLITE_TRACE_PROFILE event") 164 | } 165 | 166 | info.RunTimeNanosec = *(*int64)(xValue) 167 | 168 | // sample the error //TODO: is it safe? is it useful? 169 | fillDBError(&info.DBError, contextDB) 170 | 171 | case TraceRow: 172 | info.StmtHandle = uintptr(p) 173 | 174 | case TraceClose: 175 | handle := uintptr(p) 176 | if handle != info.ConnHandle { 177 | panic(fmt.Sprintf("Different conn handle 0x%x (expected 0x%x) in SQLITE_TRACE_CLOSE event.", 178 | handle, info.ConnHandle)) 179 | } 180 | 181 | default: 182 | // Pass unsupported events to the user callback (if configured); 183 | // let the user callback decide whether to panic or ignore them. 184 | } 185 | 186 | // Do not execute user callback when the event was not requested by user! 187 | // Remember that the Close event is always selected when 188 | // registering this callback trampoline with SQLite --- for cleanup. 189 | // In the future there may be more events forced to "selected" in SQLite 190 | // for the driver's needs. 191 | if traceConf.EventMask&traceEventCode == 0 { 192 | return 0 193 | } 194 | 195 | r := 0 196 | if traceConf.Callback != nil { 197 | r = traceConf.Callback(info) 198 | } 199 | return C.int(r) 200 | } 201 | 202 | type traceMapEntry struct { 203 | config TraceConfig 204 | } 205 | 206 | var traceMapLock sync.Mutex 207 | var traceMap = make(map[uintptr]traceMapEntry) 208 | 209 | func addTraceMapping(connHandle uintptr, traceConf TraceConfig) { 210 | traceMapLock.Lock() 211 | defer traceMapLock.Unlock() 212 | 213 | oldEntryCopy, found := traceMap[connHandle] 214 | if found { 215 | panic(fmt.Sprintf("Adding trace config %v: handle 0x%x already registered (%v).", 216 | traceConf, connHandle, oldEntryCopy.config)) 217 | } 218 | traceMap[connHandle] = traceMapEntry{config: traceConf} 219 | fmt.Printf("Added trace config %v: handle 0x%x.\n", traceConf, connHandle) 220 | } 221 | 222 | func lookupTraceMapping(connHandle uintptr) (TraceConfig, bool) { 223 | traceMapLock.Lock() 224 | defer traceMapLock.Unlock() 225 | 226 | entryCopy, found := traceMap[connHandle] 227 | return entryCopy.config, found 228 | } 229 | 230 | // 'pop' = get and delete from map before returning the value to the caller 231 | func popTraceMapping(connHandle uintptr) (TraceConfig, bool) { 232 | traceMapLock.Lock() 233 | defer traceMapLock.Unlock() 234 | 235 | entryCopy, found := traceMap[connHandle] 236 | if found { 237 | delete(traceMap, connHandle) 238 | fmt.Printf("Pop handle 0x%x: deleted trace config %v.\n", connHandle, entryCopy.config) 239 | } 240 | return entryCopy.config, found 241 | } 242 | 243 | // RegisterAggregator makes a Go type available as a SQLite aggregation function. 244 | // 245 | // Because aggregation is incremental, it's implemented in Go with a 246 | // type that has 2 methods: func Step(values) accumulates one row of 247 | // data into the accumulator, and func Done() ret finalizes and 248 | // returns the aggregate value. "values" and "ret" may be any type 249 | // supported by RegisterFunc. 250 | // 251 | // RegisterAggregator takes as implementation a constructor function 252 | // that constructs an instance of the aggregator type each time an 253 | // aggregation begins. The constructor must return a pointer to a 254 | // type, or an interface that implements Step() and Done(). 255 | // 256 | // The constructor function and the Step/Done methods may optionally 257 | // return an error in addition to their other return values. 258 | // 259 | // See _example/go_custom_funcs for a detailed example. 260 | func (c *SQLiteConn) RegisterAggregator(name string, impl interface{}, pure bool) error { 261 | var ai aggInfo 262 | ai.constructor = reflect.ValueOf(impl) 263 | t := ai.constructor.Type() 264 | if t.Kind() != reflect.Func { 265 | return errors.New("non-function passed to RegisterAggregator") 266 | } 267 | if t.NumOut() != 1 && t.NumOut() != 2 { 268 | return errors.New("SQLite aggregator constructors must return 1 or 2 values") 269 | } 270 | if t.NumOut() == 2 && !t.Out(1).Implements(reflect.TypeOf((*error)(nil)).Elem()) { 271 | return errors.New("Second return value of SQLite function must be error") 272 | } 273 | if t.NumIn() != 0 { 274 | return errors.New("SQLite aggregator constructors must not have arguments") 275 | } 276 | 277 | agg := t.Out(0) 278 | switch agg.Kind() { 279 | case reflect.Ptr, reflect.Interface: 280 | default: 281 | return errors.New("SQlite aggregator constructor must return a pointer object") 282 | } 283 | stepFn, found := agg.MethodByName("Step") 284 | if !found { 285 | return errors.New("SQlite aggregator doesn't have a Step() function") 286 | } 287 | step := stepFn.Type 288 | if step.NumOut() != 0 && step.NumOut() != 1 { 289 | return errors.New("SQlite aggregator Step() function must return 0 or 1 values") 290 | } 291 | if step.NumOut() == 1 && !step.Out(0).Implements(reflect.TypeOf((*error)(nil)).Elem()) { 292 | return errors.New("type of SQlite aggregator Step() return value must be error") 293 | } 294 | 295 | stepNArgs := step.NumIn() 296 | start := 0 297 | if agg.Kind() == reflect.Ptr { 298 | // Skip over the method receiver 299 | stepNArgs-- 300 | start++ 301 | } 302 | if step.IsVariadic() { 303 | stepNArgs-- 304 | } 305 | for i := start; i < start+stepNArgs; i++ { 306 | conv, err := callbackArg(step.In(i)) 307 | if err != nil { 308 | return err 309 | } 310 | ai.stepArgConverters = append(ai.stepArgConverters, conv) 311 | } 312 | if step.IsVariadic() { 313 | conv, err := callbackArg(t.In(start + stepNArgs).Elem()) 314 | if err != nil { 315 | return err 316 | } 317 | ai.stepVariadicConverter = conv 318 | // Pass -1 to sqlite so that it allows any number of 319 | // arguments. The call helper verifies that the minimum number 320 | // of arguments is present for variadic functions. 321 | stepNArgs = -1 322 | } 323 | 324 | doneFn, found := agg.MethodByName("Done") 325 | if !found { 326 | return errors.New("SQlite aggregator doesn't have a Done() function") 327 | } 328 | done := doneFn.Type 329 | doneNArgs := done.NumIn() 330 | if agg.Kind() == reflect.Ptr { 331 | // Skip over the method receiver 332 | doneNArgs-- 333 | } 334 | if doneNArgs != 0 { 335 | return errors.New("SQlite aggregator Done() function must have no arguments") 336 | } 337 | if done.NumOut() != 1 && done.NumOut() != 2 { 338 | return errors.New("SQLite aggregator Done() function must return 1 or 2 values") 339 | } 340 | if done.NumOut() == 2 && !done.Out(1).Implements(reflect.TypeOf((*error)(nil)).Elem()) { 341 | return errors.New("second return value of SQLite aggregator Done() function must be error") 342 | } 343 | 344 | conv, err := callbackRet(done.Out(0)) 345 | if err != nil { 346 | return err 347 | } 348 | ai.doneRetConverter = conv 349 | ai.active = make(map[int64]reflect.Value) 350 | ai.next = 1 351 | 352 | // ai must outlast the database connection, or we'll have dangling pointers. 353 | c.aggregators = append(c.aggregators, &ai) 354 | 355 | cname := C.CString(name) 356 | defer C.free(unsafe.Pointer(cname)) 357 | opts := C.SQLITE_UTF8 358 | if pure { 359 | opts |= C.SQLITE_DETERMINISTIC 360 | } 361 | rv := sqlite3_create_function(c.db, cname, C.int(stepNArgs), C.int(opts), newHandle(c, &ai), nil, C.stepTrampoline, C.doneTrampoline) 362 | if rv != C.SQLITE_OK { 363 | return c.lastError() 364 | } 365 | return nil 366 | } 367 | 368 | // SetTrace installs or removes the trace callback for the given database connection. 369 | // It's not named 'RegisterTrace' because only one callback can be kept and called. 370 | // Calling SetTrace a second time on same database connection 371 | // overrides (cancels) any prior callback and all its settings: 372 | // event mask, etc. 373 | func (c *SQLiteConn) SetTrace(requested *TraceConfig) error { 374 | connHandle := uintptr(unsafe.Pointer(c.db)) 375 | 376 | _, _ = popTraceMapping(connHandle) 377 | 378 | if requested == nil { 379 | // The traceMap entry was deleted already by popTraceMapping(): 380 | // can disable all events now, no need to watch for TraceClose. 381 | err := c.setSQLiteTrace(0) 382 | return err 383 | } 384 | 385 | reqCopy := *requested 386 | 387 | // Disable potentially expensive operations 388 | // if their result will not be used. We are doing this 389 | // just in case the caller provided nonsensical input. 390 | if reqCopy.EventMask&TraceStmt == 0 { 391 | reqCopy.WantExpandedSQL = false 392 | } 393 | 394 | addTraceMapping(connHandle, reqCopy) 395 | 396 | // The callback trampoline function does cleanup on Close event, 397 | // regardless of the presence or absence of the user callback. 398 | // Therefore it needs the Close event to be selected: 399 | actualEventMask := uint(reqCopy.EventMask | TraceClose) 400 | err := c.setSQLiteTrace(actualEventMask) 401 | return err 402 | } 403 | 404 | func (c *SQLiteConn) setSQLiteTrace(sqliteEventMask uint) error { 405 | rv := C.sqlite3_trace_v2(c.db, 406 | C.uint(sqliteEventMask), 407 | (*[0]byte)(unsafe.Pointer(C.traceCallbackTrampoline)), 408 | unsafe.Pointer(c.db)) // Fourth arg is same as first: we are 409 | // passing the database connection handle as callback context. 410 | 411 | if rv != C.SQLITE_OK { 412 | return c.lastError() 413 | } 414 | return nil 415 | } 416 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/tracecallback_noimpl.go: -------------------------------------------------------------------------------- 1 | // +build !trace 2 | 3 | package sqlite3 4 | 5 | import "errors" 6 | 7 | // RegisterAggregator register the aggregator. 8 | func (c *SQLiteConn) RegisterAggregator(name string, impl interface{}, pure bool) error { 9 | return errors.New("This feature is not implemented") 10 | } 11 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/AUTHORS: -------------------------------------------------------------------------------- 1 | # This source code refers to The Go Authors for copyright purposes. 2 | # The master list of authors is in the main Go distribution, 3 | # visible at http://tip.golang.org/AUTHORS. 4 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # This source code was written by the Go contributors. 2 | # The master list of contributors is in the main Go distribution, 3 | # visible at http://tip.golang.org/CONTRIBUTORS. 4 | -------------------------------------------------------------------------------- /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/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/golang.org/x/net/context/context.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 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 context defines the Context type, which carries deadlines, 6 | // cancelation signals, and other request-scoped values across API boundaries 7 | // and between processes. 8 | // 9 | // Incoming requests to a server should create a Context, and outgoing calls to 10 | // servers should accept a Context. The chain of function calls between must 11 | // propagate the Context, optionally replacing it with a modified copy created 12 | // using WithDeadline, WithTimeout, WithCancel, or WithValue. 13 | // 14 | // Programs that use Contexts should follow these rules to keep interfaces 15 | // consistent across packages and enable static analysis tools to check context 16 | // propagation: 17 | // 18 | // Do not store Contexts inside a struct type; instead, pass a Context 19 | // explicitly to each function that needs it. The Context should be the first 20 | // parameter, typically named ctx: 21 | // 22 | // func DoSomething(ctx context.Context, arg Arg) error { 23 | // // ... use ctx ... 24 | // } 25 | // 26 | // Do not pass a nil Context, even if a function permits it. Pass context.TODO 27 | // if you are unsure about which Context to use. 28 | // 29 | // Use context Values only for request-scoped data that transits processes and 30 | // APIs, not for passing optional parameters to functions. 31 | // 32 | // The same Context may be passed to functions running in different goroutines; 33 | // Contexts are safe for simultaneous use by multiple goroutines. 34 | // 35 | // See http://blog.golang.org/context for example code for a server that uses 36 | // Contexts. 37 | package context 38 | 39 | import "time" 40 | 41 | // A Context carries a deadline, a cancelation signal, and other values across 42 | // API boundaries. 43 | // 44 | // Context's methods may be called by multiple goroutines simultaneously. 45 | type Context interface { 46 | // Deadline returns the time when work done on behalf of this context 47 | // should be canceled. Deadline returns ok==false when no deadline is 48 | // set. Successive calls to Deadline return the same results. 49 | Deadline() (deadline time.Time, ok bool) 50 | 51 | // Done returns a channel that's closed when work done on behalf of this 52 | // context should be canceled. Done may return nil if this context can 53 | // never be canceled. Successive calls to Done return the same value. 54 | // 55 | // WithCancel arranges for Done to be closed when cancel is called; 56 | // WithDeadline arranges for Done to be closed when the deadline 57 | // expires; WithTimeout arranges for Done to be closed when the timeout 58 | // elapses. 59 | // 60 | // Done is provided for use in select statements: 61 | // 62 | // // Stream generates values with DoSomething and sends them to out 63 | // // until DoSomething returns an error or ctx.Done is closed. 64 | // func Stream(ctx context.Context, out chan<- Value) error { 65 | // for { 66 | // v, err := DoSomething(ctx) 67 | // if err != nil { 68 | // return err 69 | // } 70 | // select { 71 | // case <-ctx.Done(): 72 | // return ctx.Err() 73 | // case out <- v: 74 | // } 75 | // } 76 | // } 77 | // 78 | // See http://blog.golang.org/pipelines for more examples of how to use 79 | // a Done channel for cancelation. 80 | Done() <-chan struct{} 81 | 82 | // Err returns a non-nil error value after Done is closed. Err returns 83 | // Canceled if the context was canceled or DeadlineExceeded if the 84 | // context's deadline passed. No other values for Err are defined. 85 | // After Done is closed, successive calls to Err return the same value. 86 | Err() error 87 | 88 | // Value returns the value associated with this context for key, or nil 89 | // if no value is associated with key. Successive calls to Value with 90 | // the same key returns the same result. 91 | // 92 | // Use context values only for request-scoped data that transits 93 | // processes and API boundaries, not for passing optional parameters to 94 | // functions. 95 | // 96 | // A key identifies a specific value in a Context. Functions that wish 97 | // to store values in Context typically allocate a key in a global 98 | // variable then use that key as the argument to context.WithValue and 99 | // Context.Value. A key can be any type that supports equality; 100 | // packages should define keys as an unexported type to avoid 101 | // collisions. 102 | // 103 | // Packages that define a Context key should provide type-safe accessors 104 | // for the values stores using that key: 105 | // 106 | // // Package user defines a User type that's stored in Contexts. 107 | // package user 108 | // 109 | // import "golang.org/x/net/context" 110 | // 111 | // // User is the type of value stored in the Contexts. 112 | // type User struct {...} 113 | // 114 | // // key is an unexported type for keys defined in this package. 115 | // // This prevents collisions with keys defined in other packages. 116 | // type key int 117 | // 118 | // // userKey is the key for user.User values in Contexts. It is 119 | // // unexported; clients use user.NewContext and user.FromContext 120 | // // instead of using this key directly. 121 | // var userKey key = 0 122 | // 123 | // // NewContext returns a new Context that carries value u. 124 | // func NewContext(ctx context.Context, u *User) context.Context { 125 | // return context.WithValue(ctx, userKey, u) 126 | // } 127 | // 128 | // // FromContext returns the User value stored in ctx, if any. 129 | // func FromContext(ctx context.Context) (*User, bool) { 130 | // u, ok := ctx.Value(userKey).(*User) 131 | // return u, ok 132 | // } 133 | Value(key interface{}) interface{} 134 | } 135 | 136 | // Background returns a non-nil, empty Context. It is never canceled, has no 137 | // values, and has no deadline. It is typically used by the main function, 138 | // initialization, and tests, and as the top-level Context for incoming 139 | // requests. 140 | func Background() Context { 141 | return background 142 | } 143 | 144 | // TODO returns a non-nil, empty Context. Code should use context.TODO when 145 | // it's unclear which Context to use or it is not yet available (because the 146 | // surrounding function has not yet been extended to accept a Context 147 | // parameter). TODO is recognized by static analysis tools that determine 148 | // whether Contexts are propagated correctly in a program. 149 | func TODO() Context { 150 | return todo 151 | } 152 | 153 | // A CancelFunc tells an operation to abandon its work. 154 | // A CancelFunc does not wait for the work to stop. 155 | // After the first call, subsequent calls to a CancelFunc do nothing. 156 | type CancelFunc func() 157 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/context/go17.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 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 | // +build go1.7 6 | 7 | package context 8 | 9 | import ( 10 | "context" // standard library's context, as of Go 1.7 11 | "time" 12 | ) 13 | 14 | var ( 15 | todo = context.TODO() 16 | background = context.Background() 17 | ) 18 | 19 | // Canceled is the error returned by Context.Err when the context is canceled. 20 | var Canceled = context.Canceled 21 | 22 | // DeadlineExceeded is the error returned by Context.Err when the context's 23 | // deadline passes. 24 | var DeadlineExceeded = context.DeadlineExceeded 25 | 26 | // WithCancel returns a copy of parent with a new Done channel. The returned 27 | // context's Done channel is closed when the returned cancel function is called 28 | // or when the parent context's Done channel is closed, whichever happens first. 29 | // 30 | // Canceling this context releases resources associated with it, so code should 31 | // call cancel as soon as the operations running in this Context complete. 32 | func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { 33 | ctx, f := context.WithCancel(parent) 34 | return ctx, CancelFunc(f) 35 | } 36 | 37 | // WithDeadline returns a copy of the parent context with the deadline adjusted 38 | // to be no later than d. If the parent's deadline is already earlier than d, 39 | // WithDeadline(parent, d) is semantically equivalent to parent. The returned 40 | // context's Done channel is closed when the deadline expires, when the returned 41 | // cancel function is called, or when the parent context's Done channel is 42 | // closed, whichever happens first. 43 | // 44 | // Canceling this context releases resources associated with it, so code should 45 | // call cancel as soon as the operations running in this Context complete. 46 | func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { 47 | ctx, f := context.WithDeadline(parent, deadline) 48 | return ctx, CancelFunc(f) 49 | } 50 | 51 | // WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). 52 | // 53 | // Canceling this context releases resources associated with it, so code should 54 | // call cancel as soon as the operations running in this Context complete: 55 | // 56 | // func slowOperationWithTimeout(ctx context.Context) (Result, error) { 57 | // ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) 58 | // defer cancel() // releases resources if slowOperation completes before timeout elapses 59 | // return slowOperation(ctx) 60 | // } 61 | func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { 62 | return WithDeadline(parent, time.Now().Add(timeout)) 63 | } 64 | 65 | // WithValue returns a copy of parent in which the value associated with key is 66 | // val. 67 | // 68 | // Use context Values only for request-scoped data that transits processes and 69 | // APIs, not for passing optional parameters to functions. 70 | func WithValue(parent Context, key interface{}, val interface{}) Context { 71 | return context.WithValue(parent, key, val) 72 | } 73 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/context/pre_go17.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 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 | // +build !go1.7 6 | 7 | package context 8 | 9 | import ( 10 | "errors" 11 | "fmt" 12 | "sync" 13 | "time" 14 | ) 15 | 16 | // An emptyCtx is never canceled, has no values, and has no deadline. It is not 17 | // struct{}, since vars of this type must have distinct addresses. 18 | type emptyCtx int 19 | 20 | func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { 21 | return 22 | } 23 | 24 | func (*emptyCtx) Done() <-chan struct{} { 25 | return nil 26 | } 27 | 28 | func (*emptyCtx) Err() error { 29 | return nil 30 | } 31 | 32 | func (*emptyCtx) Value(key interface{}) interface{} { 33 | return nil 34 | } 35 | 36 | func (e *emptyCtx) String() string { 37 | switch e { 38 | case background: 39 | return "context.Background" 40 | case todo: 41 | return "context.TODO" 42 | } 43 | return "unknown empty Context" 44 | } 45 | 46 | var ( 47 | background = new(emptyCtx) 48 | todo = new(emptyCtx) 49 | ) 50 | 51 | // Canceled is the error returned by Context.Err when the context is canceled. 52 | var Canceled = errors.New("context canceled") 53 | 54 | // DeadlineExceeded is the error returned by Context.Err when the context's 55 | // deadline passes. 56 | var DeadlineExceeded = errors.New("context deadline exceeded") 57 | 58 | // WithCancel returns a copy of parent with a new Done channel. The returned 59 | // context's Done channel is closed when the returned cancel function is called 60 | // or when the parent context's Done channel is closed, whichever happens first. 61 | // 62 | // Canceling this context releases resources associated with it, so code should 63 | // call cancel as soon as the operations running in this Context complete. 64 | func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { 65 | c := newCancelCtx(parent) 66 | propagateCancel(parent, c) 67 | return c, func() { c.cancel(true, Canceled) } 68 | } 69 | 70 | // newCancelCtx returns an initialized cancelCtx. 71 | func newCancelCtx(parent Context) *cancelCtx { 72 | return &cancelCtx{ 73 | Context: parent, 74 | done: make(chan struct{}), 75 | } 76 | } 77 | 78 | // propagateCancel arranges for child to be canceled when parent is. 79 | func propagateCancel(parent Context, child canceler) { 80 | if parent.Done() == nil { 81 | return // parent is never canceled 82 | } 83 | if p, ok := parentCancelCtx(parent); ok { 84 | p.mu.Lock() 85 | if p.err != nil { 86 | // parent has already been canceled 87 | child.cancel(false, p.err) 88 | } else { 89 | if p.children == nil { 90 | p.children = make(map[canceler]bool) 91 | } 92 | p.children[child] = true 93 | } 94 | p.mu.Unlock() 95 | } else { 96 | go func() { 97 | select { 98 | case <-parent.Done(): 99 | child.cancel(false, parent.Err()) 100 | case <-child.Done(): 101 | } 102 | }() 103 | } 104 | } 105 | 106 | // parentCancelCtx follows a chain of parent references until it finds a 107 | // *cancelCtx. This function understands how each of the concrete types in this 108 | // package represents its parent. 109 | func parentCancelCtx(parent Context) (*cancelCtx, bool) { 110 | for { 111 | switch c := parent.(type) { 112 | case *cancelCtx: 113 | return c, true 114 | case *timerCtx: 115 | return c.cancelCtx, true 116 | case *valueCtx: 117 | parent = c.Context 118 | default: 119 | return nil, false 120 | } 121 | } 122 | } 123 | 124 | // removeChild removes a context from its parent. 125 | func removeChild(parent Context, child canceler) { 126 | p, ok := parentCancelCtx(parent) 127 | if !ok { 128 | return 129 | } 130 | p.mu.Lock() 131 | if p.children != nil { 132 | delete(p.children, child) 133 | } 134 | p.mu.Unlock() 135 | } 136 | 137 | // A canceler is a context type that can be canceled directly. The 138 | // implementations are *cancelCtx and *timerCtx. 139 | type canceler interface { 140 | cancel(removeFromParent bool, err error) 141 | Done() <-chan struct{} 142 | } 143 | 144 | // A cancelCtx can be canceled. When canceled, it also cancels any children 145 | // that implement canceler. 146 | type cancelCtx struct { 147 | Context 148 | 149 | done chan struct{} // closed by the first cancel call. 150 | 151 | mu sync.Mutex 152 | children map[canceler]bool // set to nil by the first cancel call 153 | err error // set to non-nil by the first cancel call 154 | } 155 | 156 | func (c *cancelCtx) Done() <-chan struct{} { 157 | return c.done 158 | } 159 | 160 | func (c *cancelCtx) Err() error { 161 | c.mu.Lock() 162 | defer c.mu.Unlock() 163 | return c.err 164 | } 165 | 166 | func (c *cancelCtx) String() string { 167 | return fmt.Sprintf("%v.WithCancel", c.Context) 168 | } 169 | 170 | // cancel closes c.done, cancels each of c's children, and, if 171 | // removeFromParent is true, removes c from its parent's children. 172 | func (c *cancelCtx) cancel(removeFromParent bool, err error) { 173 | if err == nil { 174 | panic("context: internal error: missing cancel error") 175 | } 176 | c.mu.Lock() 177 | if c.err != nil { 178 | c.mu.Unlock() 179 | return // already canceled 180 | } 181 | c.err = err 182 | close(c.done) 183 | for child := range c.children { 184 | // NOTE: acquiring the child's lock while holding parent's lock. 185 | child.cancel(false, err) 186 | } 187 | c.children = nil 188 | c.mu.Unlock() 189 | 190 | if removeFromParent { 191 | removeChild(c.Context, c) 192 | } 193 | } 194 | 195 | // WithDeadline returns a copy of the parent context with the deadline adjusted 196 | // to be no later than d. If the parent's deadline is already earlier than d, 197 | // WithDeadline(parent, d) is semantically equivalent to parent. The returned 198 | // context's Done channel is closed when the deadline expires, when the returned 199 | // cancel function is called, or when the parent context's Done channel is 200 | // closed, whichever happens first. 201 | // 202 | // Canceling this context releases resources associated with it, so code should 203 | // call cancel as soon as the operations running in this Context complete. 204 | func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { 205 | if cur, ok := parent.Deadline(); ok && cur.Before(deadline) { 206 | // The current deadline is already sooner than the new one. 207 | return WithCancel(parent) 208 | } 209 | c := &timerCtx{ 210 | cancelCtx: newCancelCtx(parent), 211 | deadline: deadline, 212 | } 213 | propagateCancel(parent, c) 214 | d := deadline.Sub(time.Now()) 215 | if d <= 0 { 216 | c.cancel(true, DeadlineExceeded) // deadline has already passed 217 | return c, func() { c.cancel(true, Canceled) } 218 | } 219 | c.mu.Lock() 220 | defer c.mu.Unlock() 221 | if c.err == nil { 222 | c.timer = time.AfterFunc(d, func() { 223 | c.cancel(true, DeadlineExceeded) 224 | }) 225 | } 226 | return c, func() { c.cancel(true, Canceled) } 227 | } 228 | 229 | // A timerCtx carries a timer and a deadline. It embeds a cancelCtx to 230 | // implement Done and Err. It implements cancel by stopping its timer then 231 | // delegating to cancelCtx.cancel. 232 | type timerCtx struct { 233 | *cancelCtx 234 | timer *time.Timer // Under cancelCtx.mu. 235 | 236 | deadline time.Time 237 | } 238 | 239 | func (c *timerCtx) Deadline() (deadline time.Time, ok bool) { 240 | return c.deadline, true 241 | } 242 | 243 | func (c *timerCtx) String() string { 244 | return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now())) 245 | } 246 | 247 | func (c *timerCtx) cancel(removeFromParent bool, err error) { 248 | c.cancelCtx.cancel(false, err) 249 | if removeFromParent { 250 | // Remove this timerCtx from its parent cancelCtx's children. 251 | removeChild(c.cancelCtx.Context, c) 252 | } 253 | c.mu.Lock() 254 | if c.timer != nil { 255 | c.timer.Stop() 256 | c.timer = nil 257 | } 258 | c.mu.Unlock() 259 | } 260 | 261 | // WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). 262 | // 263 | // Canceling this context releases resources associated with it, so code should 264 | // call cancel as soon as the operations running in this Context complete: 265 | // 266 | // func slowOperationWithTimeout(ctx context.Context) (Result, error) { 267 | // ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) 268 | // defer cancel() // releases resources if slowOperation completes before timeout elapses 269 | // return slowOperation(ctx) 270 | // } 271 | func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { 272 | return WithDeadline(parent, time.Now().Add(timeout)) 273 | } 274 | 275 | // WithValue returns a copy of parent in which the value associated with key is 276 | // val. 277 | // 278 | // Use context Values only for request-scoped data that transits processes and 279 | // APIs, not for passing optional parameters to functions. 280 | func WithValue(parent Context, key interface{}, val interface{}) Context { 281 | return &valueCtx{parent, key, val} 282 | } 283 | 284 | // A valueCtx carries a key-value pair. It implements Value for that key and 285 | // delegates all other calls to the embedded Context. 286 | type valueCtx struct { 287 | Context 288 | key, val interface{} 289 | } 290 | 291 | func (c *valueCtx) String() string { 292 | return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val) 293 | } 294 | 295 | func (c *valueCtx) Value(key interface{}) interface{} { 296 | if c.key == key { 297 | return c.val 298 | } 299 | return c.Context.Value(key) 300 | } 301 | --------------------------------------------------------------------------------