about summary refs log tree commit diff
path: root/route.go
blob: 9fc86ee8007fae2eabf795faf3c4ace52194fe6b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package mooh

import (
	"regexp"
	"strings"
)

type fn func(*Request) (Response, error)
type fns map[string]fn

type Route struct {
	Path                    string
	Executors               fns
	Components              []string
	RequiredNamedComponents map[string]bool
	OptionalNamedComponents map[string]bool
	Length                  int
	LengthWithoutOptional   int
	Methods                 map[string]bool
}

type Match struct {
	Path    string
	Route   Route
	Mapping map[string]string
	Method  string
}

var componentIsVariable = regexp.MustCompile("^:")
var componentsIsOptional = regexp.MustCompile("^\\?:")
var namedComponentsRegex = regexp.MustCompile("^:(.*)$")
var convertComponent = regexp.MustCompile("^\\??:(.*)$")

func (self *Route) convertComponentName(name string) string {
	newName := convertComponent.FindStringSubmatch(name)
	return newName[1]
}

func (self *Route) Match(method string, components []string) *Match {

	if self.Methods[method] != true {
		return nil
	}

	if len(components) < self.LengthWithoutOptional || len(components) > self.Length {
		return nil
	}

	mapping := map[string]string{}

	for i, c := range self.Components {
		if componentsIsOptional.MatchString(c) {
			break
		}
		p := components[i]

		if componentIsVariable.MatchString(c) == true {
			mapping[self.convertComponentName(c)] = p
		} else {
			if p != c {
				return nil
			}
		}
	}

	return &Match{
		Path:    strings.Join(components, "/"),
		Route:   *self,
		Method:  method,
		Mapping: mapping,
	}
}

func (self *Route) Execute(request *Request) (Response, error) {
	code := self.Executors[request.Method]
	return code(request)
}

func MakeRoute(path string, method string, code fn) Route {

	components := []string{}

	for _, c := range strings.Split(path, "/") {
		components = append(components, c)
	}

	reqComponents, optComponents := getNamedComponents(components)
	exec := fns{method: code}

	route := Route{
		Path:                    path,
		Executors:               exec,
		Components:              components,
		RequiredNamedComponents: reqComponents,
		OptionalNamedComponents: optComponents,
		Length:                  len(components),
		LengthWithoutOptional:   len(components) - len(optComponents),
		Methods:                 map[string]bool{method: true},
	}

	return route
}

func getNamedComponents(components []string) (map[string]bool, map[string]bool) {
	reqComponents := map[string]bool{}
	optComponents := map[string]bool{}
	for _, c := range components {
		if namedComponentsRegex.MatchString(c) == true {
			reqComponents[c] = true
		} else if componentsIsOptional.MatchString(c) == true {
			optComponents[c] = true
		}
	}
	return reqComponents, optComponents
}