about summary refs log tree commit diff
path: root/router.go
blob: 4c9dfabb7b6ce544163b1156c24598ea830a44aa (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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package mooh

import (
	"errors"
	"fmt"
	"net/http"
	"strings"
)

type Router struct {
	routes     []*Route
	knownPaths map[string]map[string]bool
}

func BuildRouter() Router {
	router := Router{}
	router.knownPaths = map[string]map[string]bool{}
	return router
}

func (self *Router) routeIsKnown(route *Route) bool {
	if self.knownPaths[route.Path] == nil {
		self.knownPaths[route.Path] = map[string]bool{}
		return false
	} else if self.knownPaths[route.Path][route.Method] == false {
		return false
	}
	return true
}

func (self *Router) AddRoute(route *Route) error {
	if self.routeIsKnown(route) == true {
		return errors.New(fmt.Sprintf("The route %s with the method %s already exist.", route.Path, route.Method))
	}

	route.init()
	self.routes = append(self.routes, route)
	self.knownPaths[route.Path][route.Method] = true
	return nil
}

func (self *Router) Match(request *http.Request) (*Match, error) {

	matches := []*Match{}

	method := request.Method
	components := strings.Split(request.URL.Path, "/")

	for _, r := range self.routes {
		match := r.Match(method, components)
		if match != nil {
			matches = append(matches, match)
		}
	}

	if len(matches) == 0 {
		return nil, nil
	} else if len(matches) == 1 {
		return matches[0], nil
	} else {
		return self.disambiguateMatches(request.URL.Path, matches)
	}
	return nil, nil
}

func (self *Router) disambiguateMatches(path string, matches []*Match) (*Match, error) {
	min := -1
	found := []*Match{}

	for _, m := range matches {
		req := m.Route.requiredNamedComponents
		vars := len(req)
		if min == -1 || vars < min {
			found = append(found, m)
			min = vars
		} else if vars == min {
			found = append(found, m)
		}
	}

	if len(found) > 1 {
		msg := fmt.Sprintf("Ambiguous match: path %s could match any of:", path)
		for _, f := range found {
			msg = fmt.Sprintf("%s %s", msg, f.Route.Path)
		}
		err := errors.New(msg)
		return nil, err
	}
	return found[0], nil
}

func (self *Router) GetRouteList() []string {
	routes := make([]string, len(self.knownPaths))
	i := 0
	for path, _ := range self.knownPaths {
		routes[i] = path
		i = i + 1
	}
	return routes
}

func (self *Router) HasPath(path string) bool {
	if self.knownPaths[path] != nil {
		return true
	}
	return false
}

func (self *Router) RemovePath(path string) error {
	p := self.HasPath(path)
	if p == false {
		return errors.New("foo")
	}
	delete(self.knownPaths, path)

	newRoutes := []*Route{}

	for _, p := range self.routes {
		if p.Path != path {
			newRoutes = append(newRoutes, p)
		}
	}
	self.routes = newRoutes
	return nil
}

func (self *Router) GetAllRoutes() []*Route {
	return self.routes
}

func (self *Router) GetAllRoutesByMethods (method string) []*Route {
	routes := []*Route{}
	for _, r := range self.routes {
		if r.Method == method {
			routes = append(routes, r)
		}
	}
	return routes
}

func (self *Router) GetMethodsForPath(path string) []string {
	p := self.knownPaths[path]
	m := make([]string, len(p))
	i := 0
	for k, _ := range p {
		m[i] = k
		i = i + 1
	}
	return m
}