forked from toolshed/abra
		
	
		
			
				
	
	
		
			122 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			122 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package httprule
 | |
| 
 | |
| import (
 | |
| 	"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	opcodeVersion = 1
 | |
| )
 | |
| 
 | |
| // Template is a compiled representation of path templates.
 | |
| type Template struct {
 | |
| 	// Version is the version number of the format.
 | |
| 	Version int
 | |
| 	// OpCodes is a sequence of operations.
 | |
| 	OpCodes []int
 | |
| 	// Pool is a constant pool
 | |
| 	Pool []string
 | |
| 	// Verb is a VERB part in the template.
 | |
| 	Verb string
 | |
| 	// Fields is a list of field paths bound in this template.
 | |
| 	Fields []string
 | |
| 	// Original template (example: /v1/a_bit_of_everything)
 | |
| 	Template string
 | |
| }
 | |
| 
 | |
| // Compiler compiles utilities representation of path templates into marshallable operations.
 | |
| // They can be unmarshalled by runtime.NewPattern.
 | |
| type Compiler interface {
 | |
| 	Compile() Template
 | |
| }
 | |
| 
 | |
| type op struct {
 | |
| 	// code is the opcode of the operation
 | |
| 	code utilities.OpCode
 | |
| 
 | |
| 	// str is a string operand of the code.
 | |
| 	// num is ignored if str is not empty.
 | |
| 	str string
 | |
| 
 | |
| 	// num is a numeric operand of the code.
 | |
| 	num int
 | |
| }
 | |
| 
 | |
| func (w wildcard) compile() []op {
 | |
| 	return []op{
 | |
| 		{code: utilities.OpPush},
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (w deepWildcard) compile() []op {
 | |
| 	return []op{
 | |
| 		{code: utilities.OpPushM},
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (l literal) compile() []op {
 | |
| 	return []op{
 | |
| 		{
 | |
| 			code: utilities.OpLitPush,
 | |
| 			str:  string(l),
 | |
| 		},
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (v variable) compile() []op {
 | |
| 	var ops []op
 | |
| 	for _, s := range v.segments {
 | |
| 		ops = append(ops, s.compile()...)
 | |
| 	}
 | |
| 	ops = append(ops, op{
 | |
| 		code: utilities.OpConcatN,
 | |
| 		num:  len(v.segments),
 | |
| 	}, op{
 | |
| 		code: utilities.OpCapture,
 | |
| 		str:  v.path,
 | |
| 	})
 | |
| 
 | |
| 	return ops
 | |
| }
 | |
| 
 | |
| func (t template) Compile() Template {
 | |
| 	var rawOps []op
 | |
| 	for _, s := range t.segments {
 | |
| 		rawOps = append(rawOps, s.compile()...)
 | |
| 	}
 | |
| 
 | |
| 	var (
 | |
| 		ops    []int
 | |
| 		pool   []string
 | |
| 		fields []string
 | |
| 	)
 | |
| 	consts := make(map[string]int)
 | |
| 	for _, op := range rawOps {
 | |
| 		ops = append(ops, int(op.code))
 | |
| 		if op.str == "" {
 | |
| 			ops = append(ops, op.num)
 | |
| 		} else {
 | |
| 			// eof segment literal represents the "/" path pattern
 | |
| 			if op.str == eof {
 | |
| 				op.str = ""
 | |
| 			}
 | |
| 			if _, ok := consts[op.str]; !ok {
 | |
| 				consts[op.str] = len(pool)
 | |
| 				pool = append(pool, op.str)
 | |
| 			}
 | |
| 			ops = append(ops, consts[op.str])
 | |
| 		}
 | |
| 		if op.code == utilities.OpCapture {
 | |
| 			fields = append(fields, op.str)
 | |
| 		}
 | |
| 	}
 | |
| 	return Template{
 | |
| 		Version:  opcodeVersion,
 | |
| 		OpCodes:  ops,
 | |
| 		Pool:     pool,
 | |
| 		Verb:     t.verb,
 | |
| 		Fields:   fields,
 | |
| 		Template: t.template,
 | |
| 	}
 | |
| }
 |