blob: 95a9a2545000593d18aef53d549af92e35412ce0 [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001package restful
2
3// Copyright 2013 Ernest Micklei. All rights reserved.
4// Use of this source code is governed by a license
5// that can be found in the LICENSE file.
6
7import (
8 "bytes"
9 "fmt"
10 "regexp"
11 "strings"
12)
13
14// PathExpression holds a compiled path expression (RegExp) needed to match against
15// Http request paths and to extract path parameter values.
16type pathExpression struct {
17 LiteralCount int // the number of literal characters (means those not resulting from template variable substitution)
18 VarNames []string // the names of parameters (enclosed by {}) in the path
19 VarCount int // the number of named parameters (enclosed by {}) in the path
20 Matcher *regexp.Regexp
21 Source string // Path as defined by the RouteBuilder
22 tokens []string
23}
24
25// NewPathExpression creates a PathExpression from the input URL path.
26// Returns an error if the path is invalid.
27func newPathExpression(path string) (*pathExpression, error) {
28 expression, literalCount, varNames, varCount, tokens := templateToRegularExpression(path)
29 compiled, err := regexp.Compile(expression)
30 if err != nil {
31 return nil, err
32 }
33 return &pathExpression{literalCount, varNames, varCount, compiled, expression, tokens}, nil
34}
35
36// http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-370003.7.3
37func templateToRegularExpression(template string) (expression string, literalCount int, varNames []string, varCount int, tokens []string) {
38 var buffer bytes.Buffer
39 buffer.WriteString("^")
40 //tokens = strings.Split(template, "/")
41 tokens = tokenizePath(template)
42 for _, each := range tokens {
43 if each == "" {
44 continue
45 }
46 buffer.WriteString("/")
47 if strings.HasPrefix(each, "{") {
48 // check for regular expression in variable
49 colon := strings.Index(each, ":")
50 var varName string
51 if colon != -1 {
52 // extract expression
53 varName = strings.TrimSpace(each[1:colon])
54 paramExpr := strings.TrimSpace(each[colon+1 : len(each)-1])
55 if paramExpr == "*" { // special case
56 buffer.WriteString("(.*)")
57 } else {
58 buffer.WriteString(fmt.Sprintf("(%s)", paramExpr)) // between colon and closing moustache
59 }
60 } else {
61 // plain var
62 varName = strings.TrimSpace(each[1 : len(each)-1])
63 buffer.WriteString("([^/]+?)")
64 }
65 varNames = append(varNames, varName)
66 varCount += 1
67 } else {
68 literalCount += len(each)
69 encoded := each // TODO URI encode
70 buffer.WriteString(regexp.QuoteMeta(encoded))
71 }
72 }
73 return strings.TrimRight(buffer.String(), "/") + "(/.*)?$", literalCount, varNames, varCount, tokens
74}