1 module router;
2 
3 import events;
4 import std..string : toUpper, toLower;
5 import std.regex;
6 
7 package:
8 
9 class Route(TRequest: IRoutedRequest, TResponse) : EventList!(void, TRequest, TResponse) {
10     private:
11 
12         string _path;
13         Regex!char _compiledPath;
14         string[] _routeParams;
15         EventList!(void, TRequest, TResponse).Trigger _eventTrigger;
16 
17     public:
18 
19         this(string path) {
20             _path = path;
21             _eventTrigger = this.own;
22             _routeParams = extractRouteParams;
23             _compiledPath = compilePathRegex;
24         }
25 
26         @property string path() {
27             return _path;
28         }
29 
30         @property string[] routeParams() {
31             return _routeParams;
32         }
33 
34         @property Regex!char compiledPath() {
35             return _compiledPath;
36         }
37 
38         void execute(string uri, TRequest request, TResponse response) {
39             auto m = match(uri, _compiledPath);
40             string[string] params;
41 
42             if(m.captures.length > 0)
43             {
44                 for(int i = 1; i < m.captures.length; i++) {
45                     params[_routeParams[i-1]] = m.captures[i];
46                 }
47                 request.params = params;
48                 _eventTrigger(request, response);
49             }
50         }
51 
52     private:
53         string[] extractRouteParams() {
54             string[] keys;
55             string c;
56 
57             foreach(m; match(_path, regex(r"(:\w+)", "gm"))){
58                 c = m.captures[1];
59                 keys ~= c[1..c.length];
60             }
61             return keys;
62         }
63 
64         Regex!char compilePathRegex() {
65             auto replaced = replaceAll(_path, regex(r"(:\w+)","g"), "([^/?#]+)");
66             auto compiledRegexp = regex(replaced);
67             return compiledRegexp;
68         }
69 }
70 
71 public:
72 
73 interface IRoutedRequest {
74     @property {
75         string[string] params();
76         void params(string[string] params);
77     }
78 }
79 
80 class VerbHandler(TRequest : IRoutedRequest, TResponse) {
81     private:
82         string _verb;
83         Route!(TRequest, TResponse)[string] _routes;
84 
85     public:
86 
87         this(string normalizedVerb) {
88             _verb = normalizedVerb;
89         }
90 
91         @property {
92             string verb() {
93                 return _verb;
94             }
95         }
96 
97         Route!(TRequest, TResponse) route(string path) {
98             Route!(TRequest, TResponse) route = null;
99             if(path in _routes) {
100                 route = _routes[path];
101             }
102             if(route is null) {
103                 route = _routes[path] = new Route!(TRequest, TResponse)(path);
104             }
105             return route;
106         }
107 
108         void execute(string path, TRequest request, TResponse response) {
109             foreach(r; _routes) {
110                 r.execute(path, request, response);
111             }
112         }
113 }
114 
115 private template BootstrapVerb(string methodName) {
116     const char[] BootstrapVerb = "EventList!(void, TRequest, TResponse) " ~ methodName.toLower ~ "(string path) { return this.map(\"" ~ methodName.toUpper ~ "\", path); }";
117 }
118 
119 
120 class Router(TRequest : IRoutedRequest, TResponse) {
121     private:
122         VerbHandler!(TRequest, TResponse)[string] _verbs;
123 
124         VerbHandler!(TRequest, TResponse) _getVerb(string verb) {
125             string normalizedVerb = verb.toUpper;
126             VerbHandler!(TRequest, TResponse) handler = null;
127             if(normalizedVerb in _verbs) {
128                 handler = _verbs[normalizedVerb];
129             }
130             if(handler is null) {
131                 handler = _verbs[normalizedVerb] = new VerbHandler!(TRequest, TResponse)(normalizedVerb);
132             }
133             return handler;
134         }
135 
136     public:
137 
138         EventList!(void, TRequest, TResponse) map(string verb, string path) {
139             auto handler = _getVerb(verb);
140             return handler.route(path);
141         }
142 
143         mixin(BootstrapVerb!"GET");
144         mixin(BootstrapVerb!"HEAD");
145         mixin(BootstrapVerb!"POST");
146         mixin(BootstrapVerb!"PUT");
147 
148         void execute(string verb, string path, TRequest request, TResponse response) {
149             auto handler = _getVerb(verb);
150             handler.execute(path, request, response);
151         }
152 }