1package 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}
118