blob: 437039a3daeeb6c2e970a468535439700a47f7ac [file] [log] [blame]
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -07001package httprule
2
3import (
4 "github.com/grpc-ecosystem/grpc-gateway/utilities"
5)
6
7const (
8 opcodeVersion = 1
9)
10
11// Template is a compiled representation of path templates.
12type Template struct {
13 // Version is the version number of the format.
14 Version int
15 // OpCodes is a sequence of operations.
16 OpCodes []int
17 // Pool is a constant pool
18 Pool []string
19 // Verb is a VERB part in the template.
20 Verb string
21 // Fields is a list of field paths bound in this template.
22 Fields []string
23 // Original template (example: /v1/a_bit_of_everything)
24 Template string
25}
26
27// Compiler compiles utilities representation of path templates into marshallable operations.
28// They can be unmarshalled by runtime.NewPattern.
29type Compiler interface {
30 Compile() Template
31}
32
33type op struct {
34 // code is the opcode of the operation
35 code utilities.OpCode
36
37 // str is a string operand of the code.
38 // num is ignored if str is not empty.
39 str string
40
41 // num is a numeric operand of the code.
42 num int
43}
44
45func (w wildcard) compile() []op {
46 return []op{
47 {code: utilities.OpPush},
48 }
49}
50
51func (w deepWildcard) compile() []op {
52 return []op{
53 {code: utilities.OpPushM},
54 }
55}
56
57func (l literal) compile() []op {
58 return []op{
59 {
60 code: utilities.OpLitPush,
61 str: string(l),
62 },
63 }
64}
65
66func (v variable) compile() []op {
67 var ops []op
68 for _, s := range v.segments {
69 ops = append(ops, s.compile()...)
70 }
71 ops = append(ops, op{
72 code: utilities.OpConcatN,
73 num: len(v.segments),
74 }, op{
75 code: utilities.OpCapture,
76 str: v.path,
77 })
78
79 return ops
80}
81
82func (t template) Compile() Template {
83 var rawOps []op
84 for _, s := range t.segments {
85 rawOps = append(rawOps, s.compile()...)
86 }
87
88 var (
89 ops []int
90 pool []string
91 fields []string
92 )
93 consts := make(map[string]int)
94 for _, op := range rawOps {
95 ops = append(ops, int(op.code))
96 if op.str == "" {
97 ops = append(ops, op.num)
98 } else {
99 if _, ok := consts[op.str]; !ok {
100 consts[op.str] = len(pool)
101 pool = append(pool, op.str)
102 }
103 ops = append(ops, consts[op.str])
104 }
105 if op.code == utilities.OpCapture {
106 fields = append(fields, op.str)
107 }
108 }
109 return Template{
110 Version: opcodeVersion,
111 OpCodes: ops,
112 Pool: pool,
113 Verb: t.verb,
114 Fields: fields,
115 Template: t.template,
116 }
117}