123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- // Copyright 2016, Google Inc.
- // All rights reserved.
- //
- // Redistribution and use in source and binary forms, with or without
- // modification, are permitted provided that the following conditions are
- // met:
- //
- // * Redistributions of source code must retain the above copyright
- // notice, this list of conditions and the following disclaimer.
- // * Redistributions in binary form must reproduce the above
- // copyright notice, this list of conditions and the following disclaimer
- // in the documentation and/or other materials provided with the
- // distribution.
- // * Neither the name of Google Inc. nor the names of its
- // contributors may be used to endorse or promote products derived from
- // this software without specific prior written permission.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- package gax
- import (
- "fmt"
- "io"
- "strings"
- )
- // This parser follows the syntax of path templates, from
- // https://github.com/googleapis/googleapis/blob/master/google/api/http.proto.
- // The differences are that there is no custom verb, we allow the initial slash
- // to be absent, and that we are not strict as
- // https://tools.ietf.org/html/rfc6570 about the characters in identifiers and
- // literals.
- type pathTemplateParser struct {
- r *strings.Reader
- runeCount int // the number of the current rune in the original string
- nextVar int // the number to use for the next unnamed variable
- seenName map[string]bool // names we've seen already
- seenPathWildcard bool // have we seen "**" already?
- }
- func parsePathTemplate(template string) (pt *PathTemplate, err error) {
- p := &pathTemplateParser{
- r: strings.NewReader(template),
- seenName: map[string]bool{},
- }
- // Handle panics with strings like errors.
- // See pathTemplateParser.error, below.
- defer func() {
- if x := recover(); x != nil {
- errmsg, ok := x.(errString)
- if !ok {
- panic(x)
- }
- pt = nil
- err = ParseError{p.runeCount, template, string(errmsg)}
- }
- }()
- segs := p.template()
- // If there is a path wildcard, set its length. We can't do this
- // until we know how many segments we've got all together.
- for i, seg := range segs {
- if _, ok := seg.matcher.(pathWildcardMatcher); ok {
- segs[i].matcher = pathWildcardMatcher(len(segs) - i - 1)
- break
- }
- }
- return &PathTemplate{segments: segs}, nil
- }
- // Used to indicate errors "thrown" by this parser. We don't use string because
- // many parts of the standard library panic with strings.
- type errString string
- // Terminates parsing immediately with an error.
- func (p *pathTemplateParser) error(msg string) {
- panic(errString(msg))
- }
- // Template = [ "/" ] Segments
- func (p *pathTemplateParser) template() []segment {
- var segs []segment
- if p.consume('/') {
- // Initial '/' needs an initial empty matcher.
- segs = append(segs, segment{matcher: labelMatcher("")})
- }
- return append(segs, p.segments("")...)
- }
- // Segments = Segment { "/" Segment }
- func (p *pathTemplateParser) segments(name string) []segment {
- var segs []segment
- for {
- subsegs := p.segment(name)
- segs = append(segs, subsegs...)
- if !p.consume('/') {
- break
- }
- }
- return segs
- }
- // Segment = "*" | "**" | LITERAL | Variable
- func (p *pathTemplateParser) segment(name string) []segment {
- if p.consume('*') {
- if name == "" {
- name = fmt.Sprintf("$%d", p.nextVar)
- p.nextVar++
- }
- if p.consume('*') {
- if p.seenPathWildcard {
- p.error("multiple '**' disallowed")
- }
- p.seenPathWildcard = true
- // We'll change 0 to the right number at the end.
- return []segment{{name: name, matcher: pathWildcardMatcher(0)}}
- }
- return []segment{{name: name, matcher: wildcardMatcher(0)}}
- }
- if p.consume('{') {
- if name != "" {
- p.error("recursive named bindings are not allowed")
- }
- return p.variable()
- }
- return []segment{{name: name, matcher: labelMatcher(p.literal())}}
- }
- // Variable = "{" FieldPath [ "=" Segments ] "}"
- // "{" is already consumed.
- func (p *pathTemplateParser) variable() []segment {
- // Simplification: treat FieldPath as LITERAL, instead of IDENT { '.' IDENT }
- name := p.literal()
- if p.seenName[name] {
- p.error(name + " appears multiple times")
- }
- p.seenName[name] = true
- var segs []segment
- if p.consume('=') {
- segs = p.segments(name)
- } else {
- // "{var}" is equivalent to "{var=*}"
- segs = []segment{{name: name, matcher: wildcardMatcher(0)}}
- }
- if !p.consume('}') {
- p.error("expected '}'")
- }
- return segs
- }
- // A literal is any sequence of characters other than a few special ones.
- // The list of stop characters is not quite the same as in the template RFC.
- func (p *pathTemplateParser) literal() string {
- lit := p.consumeUntil("/*}{=")
- if lit == "" {
- p.error("empty literal")
- }
- return lit
- }
- // Read runes until EOF or one of the runes in stopRunes is encountered.
- // If the latter, unread the stop rune. Return the accumulated runes as a string.
- func (p *pathTemplateParser) consumeUntil(stopRunes string) string {
- var runes []rune
- for {
- r, ok := p.readRune()
- if !ok {
- break
- }
- if strings.IndexRune(stopRunes, r) >= 0 {
- p.unreadRune()
- break
- }
- runes = append(runes, r)
- }
- return string(runes)
- }
- // If the next rune is r, consume it and return true.
- // Otherwise, leave the input unchanged and return false.
- func (p *pathTemplateParser) consume(r rune) bool {
- rr, ok := p.readRune()
- if !ok {
- return false
- }
- if r == rr {
- return true
- }
- p.unreadRune()
- return false
- }
- // Read the next rune from the input. Return it.
- // The second return value is false at EOF.
- func (p *pathTemplateParser) readRune() (rune, bool) {
- r, _, err := p.r.ReadRune()
- if err == io.EOF {
- return r, false
- }
- if err != nil {
- p.error(err.Error())
- }
- p.runeCount++
- return r, true
- }
- // Put the last rune that was read back on the input.
- func (p *pathTemplateParser) unreadRune() {
- if err := p.r.UnreadRune(); err != nil {
- p.error(err.Error())
- }
- p.runeCount--
- }
|