summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/olivere/elastic/uritemplates
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/olivere/elastic/uritemplates')
-rw-r--r--vendor/github.com/olivere/elastic/uritemplates/LICENSE18
-rw-r--r--vendor/github.com/olivere/elastic/uritemplates/uritemplates.go359
-rw-r--r--vendor/github.com/olivere/elastic/uritemplates/utils.go13
-rw-r--r--vendor/github.com/olivere/elastic/uritemplates/utils_test.go105
4 files changed, 495 insertions, 0 deletions
diff --git a/vendor/github.com/olivere/elastic/uritemplates/LICENSE b/vendor/github.com/olivere/elastic/uritemplates/LICENSE
new file mode 100644
index 000000000..de9c88cb6
--- /dev/null
+++ b/vendor/github.com/olivere/elastic/uritemplates/LICENSE
@@ -0,0 +1,18 @@
+Copyright (c) 2013 Joshua Tacoma
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/olivere/elastic/uritemplates/uritemplates.go b/vendor/github.com/olivere/elastic/uritemplates/uritemplates.go
new file mode 100644
index 000000000..8a84813fe
--- /dev/null
+++ b/vendor/github.com/olivere/elastic/uritemplates/uritemplates.go
@@ -0,0 +1,359 @@
+// Copyright 2013 Joshua Tacoma. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package uritemplates is a level 4 implementation of RFC 6570 (URI
+// Template, http://tools.ietf.org/html/rfc6570).
+//
+// To use uritemplates, parse a template string and expand it with a value
+// map:
+//
+// template, _ := uritemplates.Parse("https://api.github.com/repos{/user,repo}")
+// values := make(map[string]interface{})
+// values["user"] = "jtacoma"
+// values["repo"] = "uritemplates"
+// expanded, _ := template.ExpandString(values)
+// fmt.Printf(expanded)
+//
+package uritemplates
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "reflect"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+var (
+ unreserved = regexp.MustCompile("[^A-Za-z0-9\\-._~]")
+ reserved = regexp.MustCompile("[^A-Za-z0-9\\-._~:/?#[\\]@!$&'()*+,;=]")
+ validname = regexp.MustCompile("^([A-Za-z0-9_\\.]|%[0-9A-Fa-f][0-9A-Fa-f])+$")
+ hex = []byte("0123456789ABCDEF")
+)
+
+func pctEncode(src []byte) []byte {
+ dst := make([]byte, len(src)*3)
+ for i, b := range src {
+ buf := dst[i*3 : i*3+3]
+ buf[0] = 0x25
+ buf[1] = hex[b/16]
+ buf[2] = hex[b%16]
+ }
+ return dst
+}
+
+func escape(s string, allowReserved bool) (escaped string) {
+ if allowReserved {
+ escaped = string(reserved.ReplaceAllFunc([]byte(s), pctEncode))
+ } else {
+ escaped = string(unreserved.ReplaceAllFunc([]byte(s), pctEncode))
+ }
+ return escaped
+}
+
+// A UriTemplate is a parsed representation of a URI template.
+type UriTemplate struct {
+ raw string
+ parts []templatePart
+}
+
+// Parse parses a URI template string into a UriTemplate object.
+func Parse(rawtemplate string) (template *UriTemplate, err error) {
+ template = new(UriTemplate)
+ template.raw = rawtemplate
+ split := strings.Split(rawtemplate, "{")
+ template.parts = make([]templatePart, len(split)*2-1)
+ for i, s := range split {
+ if i == 0 {
+ if strings.Contains(s, "}") {
+ err = errors.New("unexpected }")
+ break
+ }
+ template.parts[i].raw = s
+ } else {
+ subsplit := strings.Split(s, "}")
+ if len(subsplit) != 2 {
+ err = errors.New("malformed template")
+ break
+ }
+ expression := subsplit[0]
+ template.parts[i*2-1], err = parseExpression(expression)
+ if err != nil {
+ break
+ }
+ template.parts[i*2].raw = subsplit[1]
+ }
+ }
+ if err != nil {
+ template = nil
+ }
+ return template, err
+}
+
+type templatePart struct {
+ raw string
+ terms []templateTerm
+ first string
+ sep string
+ named bool
+ ifemp string
+ allowReserved bool
+}
+
+type templateTerm struct {
+ name string
+ explode bool
+ truncate int
+}
+
+func parseExpression(expression string) (result templatePart, err error) {
+ switch expression[0] {
+ case '+':
+ result.sep = ","
+ result.allowReserved = true
+ expression = expression[1:]
+ case '.':
+ result.first = "."
+ result.sep = "."
+ expression = expression[1:]
+ case '/':
+ result.first = "/"
+ result.sep = "/"
+ expression = expression[1:]
+ case ';':
+ result.first = ";"
+ result.sep = ";"
+ result.named = true
+ expression = expression[1:]
+ case '?':
+ result.first = "?"
+ result.sep = "&"
+ result.named = true
+ result.ifemp = "="
+ expression = expression[1:]
+ case '&':
+ result.first = "&"
+ result.sep = "&"
+ result.named = true
+ result.ifemp = "="
+ expression = expression[1:]
+ case '#':
+ result.first = "#"
+ result.sep = ","
+ result.allowReserved = true
+ expression = expression[1:]
+ default:
+ result.sep = ","
+ }
+ rawterms := strings.Split(expression, ",")
+ result.terms = make([]templateTerm, len(rawterms))
+ for i, raw := range rawterms {
+ result.terms[i], err = parseTerm(raw)
+ if err != nil {
+ break
+ }
+ }
+ return result, err
+}
+
+func parseTerm(term string) (result templateTerm, err error) {
+ if strings.HasSuffix(term, "*") {
+ result.explode = true
+ term = term[:len(term)-1]
+ }
+ split := strings.Split(term, ":")
+ if len(split) == 1 {
+ result.name = term
+ } else if len(split) == 2 {
+ result.name = split[0]
+ var parsed int64
+ parsed, err = strconv.ParseInt(split[1], 10, 0)
+ result.truncate = int(parsed)
+ } else {
+ err = errors.New("multiple colons in same term")
+ }
+ if !validname.MatchString(result.name) {
+ err = errors.New("not a valid name: " + result.name)
+ }
+ if result.explode && result.truncate > 0 {
+ err = errors.New("both explode and prefix modifers on same term")
+ }
+ return result, err
+}
+
+// Expand expands a URI template with a set of values to produce a string.
+func (self *UriTemplate) Expand(value interface{}) (string, error) {
+ values, ismap := value.(map[string]interface{})
+ if !ismap {
+ if m, ismap := struct2map(value); !ismap {
+ return "", errors.New("expected map[string]interface{}, struct, or pointer to struct.")
+ } else {
+ return self.Expand(m)
+ }
+ }
+ var buf bytes.Buffer
+ for _, p := range self.parts {
+ err := p.expand(&buf, values)
+ if err != nil {
+ return "", err
+ }
+ }
+ return buf.String(), nil
+}
+
+func (self *templatePart) expand(buf *bytes.Buffer, values map[string]interface{}) error {
+ if len(self.raw) > 0 {
+ buf.WriteString(self.raw)
+ return nil
+ }
+ var zeroLen = buf.Len()
+ buf.WriteString(self.first)
+ var firstLen = buf.Len()
+ for _, term := range self.terms {
+ value, exists := values[term.name]
+ if !exists {
+ continue
+ }
+ if buf.Len() != firstLen {
+ buf.WriteString(self.sep)
+ }
+ switch v := value.(type) {
+ case string:
+ self.expandString(buf, term, v)
+ case []interface{}:
+ self.expandArray(buf, term, v)
+ case map[string]interface{}:
+ if term.truncate > 0 {
+ return errors.New("cannot truncate a map expansion")
+ }
+ self.expandMap(buf, term, v)
+ default:
+ if m, ismap := struct2map(value); ismap {
+ if term.truncate > 0 {
+ return errors.New("cannot truncate a map expansion")
+ }
+ self.expandMap(buf, term, m)
+ } else {
+ str := fmt.Sprintf("%v", value)
+ self.expandString(buf, term, str)
+ }
+ }
+ }
+ if buf.Len() == firstLen {
+ original := buf.Bytes()[:zeroLen]
+ buf.Reset()
+ buf.Write(original)
+ }
+ return nil
+}
+
+func (self *templatePart) expandName(buf *bytes.Buffer, name string, empty bool) {
+ if self.named {
+ buf.WriteString(name)
+ if empty {
+ buf.WriteString(self.ifemp)
+ } else {
+ buf.WriteString("=")
+ }
+ }
+}
+
+func (self *templatePart) expandString(buf *bytes.Buffer, t templateTerm, s string) {
+ if len(s) > t.truncate && t.truncate > 0 {
+ s = s[:t.truncate]
+ }
+ self.expandName(buf, t.name, len(s) == 0)
+ buf.WriteString(escape(s, self.allowReserved))
+}
+
+func (self *templatePart) expandArray(buf *bytes.Buffer, t templateTerm, a []interface{}) {
+ if len(a) == 0 {
+ return
+ } else if !t.explode {
+ self.expandName(buf, t.name, false)
+ }
+ for i, value := range a {
+ if t.explode && i > 0 {
+ buf.WriteString(self.sep)
+ } else if i > 0 {
+ buf.WriteString(",")
+ }
+ var s string
+ switch v := value.(type) {
+ case string:
+ s = v
+ default:
+ s = fmt.Sprintf("%v", v)
+ }
+ if len(s) > t.truncate && t.truncate > 0 {
+ s = s[:t.truncate]
+ }
+ if self.named && t.explode {
+ self.expandName(buf, t.name, len(s) == 0)
+ }
+ buf.WriteString(escape(s, self.allowReserved))
+ }
+}
+
+func (self *templatePart) expandMap(buf *bytes.Buffer, t templateTerm, m map[string]interface{}) {
+ if len(m) == 0 {
+ return
+ }
+ if !t.explode {
+ self.expandName(buf, t.name, len(m) == 0)
+ }
+ var firstLen = buf.Len()
+ for k, value := range m {
+ if firstLen != buf.Len() {
+ if t.explode {
+ buf.WriteString(self.sep)
+ } else {
+ buf.WriteString(",")
+ }
+ }
+ var s string
+ switch v := value.(type) {
+ case string:
+ s = v
+ default:
+ s = fmt.Sprintf("%v", v)
+ }
+ if t.explode {
+ buf.WriteString(escape(k, self.allowReserved))
+ buf.WriteRune('=')
+ buf.WriteString(escape(s, self.allowReserved))
+ } else {
+ buf.WriteString(escape(k, self.allowReserved))
+ buf.WriteRune(',')
+ buf.WriteString(escape(s, self.allowReserved))
+ }
+ }
+}
+
+func struct2map(v interface{}) (map[string]interface{}, bool) {
+ value := reflect.ValueOf(v)
+ switch value.Type().Kind() {
+ case reflect.Ptr:
+ return struct2map(value.Elem().Interface())
+ case reflect.Struct:
+ m := make(map[string]interface{})
+ for i := 0; i < value.NumField(); i++ {
+ tag := value.Type().Field(i).Tag
+ var name string
+ if strings.Contains(string(tag), ":") {
+ name = tag.Get("uri")
+ } else {
+ name = strings.TrimSpace(string(tag))
+ }
+ if len(name) == 0 {
+ name = value.Type().Field(i).Name
+ }
+ m[name] = value.Field(i).Interface()
+ }
+ return m, true
+ }
+ return nil, false
+}
diff --git a/vendor/github.com/olivere/elastic/uritemplates/utils.go b/vendor/github.com/olivere/elastic/uritemplates/utils.go
new file mode 100644
index 000000000..399ef4623
--- /dev/null
+++ b/vendor/github.com/olivere/elastic/uritemplates/utils.go
@@ -0,0 +1,13 @@
+package uritemplates
+
+func Expand(path string, expansions map[string]string) (string, error) {
+ template, err := Parse(path)
+ if err != nil {
+ return "", err
+ }
+ values := make(map[string]interface{})
+ for k, v := range expansions {
+ values[k] = v
+ }
+ return template.Expand(values)
+}
diff --git a/vendor/github.com/olivere/elastic/uritemplates/utils_test.go b/vendor/github.com/olivere/elastic/uritemplates/utils_test.go
new file mode 100644
index 000000000..633949b6f
--- /dev/null
+++ b/vendor/github.com/olivere/elastic/uritemplates/utils_test.go
@@ -0,0 +1,105 @@
+package uritemplates
+
+import (
+ "testing"
+)
+
+type ExpandTest struct {
+ in string
+ expansions map[string]string
+ want string
+}
+
+var expandTests = []ExpandTest{
+ // #0: no expansions
+ {
+ "http://www.golang.org/",
+ map[string]string{},
+ "http://www.golang.org/",
+ },
+ // #1: one expansion, no escaping
+ {
+ "http://www.golang.org/{bucket}/delete",
+ map[string]string{
+ "bucket": "red",
+ },
+ "http://www.golang.org/red/delete",
+ },
+ // #2: one expansion, with hex escapes
+ {
+ "http://www.golang.org/{bucket}/delete",
+ map[string]string{
+ "bucket": "red/blue",
+ },
+ "http://www.golang.org/red%2Fblue/delete",
+ },
+ // #3: one expansion, with space
+ {
+ "http://www.golang.org/{bucket}/delete",
+ map[string]string{
+ "bucket": "red or blue",
+ },
+ "http://www.golang.org/red%20or%20blue/delete",
+ },
+ // #4: expansion not found
+ {
+ "http://www.golang.org/{object}/delete",
+ map[string]string{
+ "bucket": "red or blue",
+ },
+ "http://www.golang.org//delete",
+ },
+ // #5: multiple expansions
+ {
+ "http://www.golang.org/{one}/{two}/{three}/get",
+ map[string]string{
+ "one": "ONE",
+ "two": "TWO",
+ "three": "THREE",
+ },
+ "http://www.golang.org/ONE/TWO/THREE/get",
+ },
+ // #6: utf-8 characters
+ {
+ "http://www.golang.org/{bucket}/get",
+ map[string]string{
+ "bucket": "£100",
+ },
+ "http://www.golang.org/%C2%A3100/get",
+ },
+ // #7: punctuations
+ {
+ "http://www.golang.org/{bucket}/get",
+ map[string]string{
+ "bucket": `/\@:,.*~`,
+ },
+ "http://www.golang.org/%2F%5C%40%3A%2C.%2A~/get",
+ },
+ // #8: mis-matched brackets
+ {
+ "http://www.golang.org/{bucket/get",
+ map[string]string{
+ "bucket": "red",
+ },
+ "",
+ },
+ // #9: "+" prefix for suppressing escape
+ // See also: http://tools.ietf.org/html/rfc6570#section-3.2.3
+ {
+ "http://www.golang.org/{+topic}",
+ map[string]string{
+ "topic": "/topics/myproject/mytopic",
+ },
+ // The double slashes here look weird, but it's intentional
+ "http://www.golang.org//topics/myproject/mytopic",
+ },
+}
+
+func TestExpand(t *testing.T) {
+ for i, test := range expandTests {
+ got, _ := Expand(test.in, test.expansions)
+ if got != test.want {
+ t.Errorf("got %q expected %q in test %d", got, test.want, i)
+ }
+ }
+}