1
- from lightbug_http import HTTPRequest, HTTPResponse, NotFound
2
1
from lightbug_api.service import not_found
2
+ from utils.variant import Variant
3
+ from collections import Dict, Optional
4
+ from collections.dict import _DictEntryIter
5
+ from lightbug_http import NotFound, OK , HTTPService, HTTPRequest, HTTPResponse
6
+ from lightbug_http.strings import RequestMethod
7
+
8
+ alias MAX_SUB_ROUTER_DEPTH = 20
9
+
10
+
11
+ struct RouterErrors :
12
+ alias ROUTE_NOT_FOUND_ERROR = " ROUTE_NOT_FOUND_ERROR"
13
+ alias INVALID_PATH_ERROR = " INVALID_PATH_ERROR"
14
+ alias INVALID_PATH_FRAGMENT_ERROR = " INVALID_PATH_FRAGMENT_ERROR"
15
+
16
+
17
+ alias HTTPHandler = fn (req: HTTPRequest) - > HTTPResponse
18
+
19
+
20
+ @value
21
+ struct HandlerMeta :
22
+ var handler : HTTPHandler
23
+
24
+
25
+ alias HTTPHandlersMap = Dict[String, HandlerMeta]
26
+
3
27
4
28
5
29
struct APIRoute (CollectionElement ):
@@ -34,10 +58,11 @@ struct APIRoute(CollectionElement):
34
58
self .handler = existing.handler
35
59
self .operation_id = existing.operation_id^
36
60
37
-
38
61
@value
39
- struct Router :
40
- var routes : List[APIRoute]
62
+ struct RouterBase[is_main_app: Bool = False ](HTTPService):
63
+ var path_fragment : String
64
+ var sub_routers : Dict[String, RouterBase[False ]]
65
+ var routes : Dict[String, HTTPHandlersMap]
41
66
42
67
fn __init__ (out self ):
43
68
self .routes = List[APIRoute]()
@@ -52,3 +77,111 @@ struct Router:
52
77
out self , path : String, method : String, handler : fn (HTTPRequest) - > HTTPResponse, operation_id : String
53
78
):
54
79
self .routes.append(APIRoute(path, method, handler, operation_id))
80
+ fn __init__ (out self : Self) raises :
81
+ if not is_main_app:
82
+ raise Error(" Sub-router requires url path fragment it will manage" )
83
+ self .__init__ (path_fragment = " /" )
84
+
85
+ fn __init__ (out self : Self, path_fragment : String) raises :
86
+ self .path_fragment = path_fragment
87
+ self .sub_routers = Dict[String, RouterBase[False ]]()
88
+ self .routes = Dict[String, HTTPHandlersMap]()
89
+
90
+ self .routes[RequestMethod.head.value] = HTTPHandlersMap()
91
+ self .routes[RequestMethod.get.value] = HTTPHandlersMap()
92
+ self .routes[RequestMethod.put.value] = HTTPHandlersMap()
93
+ self .routes[RequestMethod.post.value] = HTTPHandlersMap()
94
+ self .routes[RequestMethod.patch.value] = HTTPHandlersMap()
95
+ self .routes[RequestMethod.delete.value] = HTTPHandlersMap()
96
+ self .routes[RequestMethod.options.value] = HTTPHandlersMap()
97
+
98
+ if not self ._validate_path_fragment(path_fragment):
99
+ raise Error(RouterErrors.INVALID_PATH_FRAGMENT_ERROR )
100
+
101
+ fn _route (
102
+ mut self , partial_path : String, method : String, depth : Int = 0
103
+ ) raises -> HandlerMeta:
104
+ if depth > MAX_SUB_ROUTER_DEPTH :
105
+ raise Error(RouterErrors.ROUTE_NOT_FOUND_ERROR )
106
+
107
+ var sub_router_name : String = " "
108
+ var remaining_path : String = " "
109
+ var handler_path = partial_path
110
+
111
+ if partial_path:
112
+ # TODO : (Hrist) Update to lightbug_http.uri.URIDelimiters.PATH when available
113
+ var fragments = partial_path.split(" /" , 1 )
114
+
115
+ sub_router_name = fragments[0 ]
116
+ if len (fragments) == 2 :
117
+ remaining_path = fragments[1 ]
118
+ else :
119
+ remaining_path = " "
120
+
121
+ else :
122
+ # TODO : (Hrist) Update to lightbug_http.uri.URIDelimiters.PATH when available
123
+ handler_path = " /"
124
+
125
+ if sub_router_name in self .sub_routers:
126
+ return self .sub_routers[sub_router_name]._route(
127
+ remaining_path, method, depth + 1
128
+ )
129
+ elif handler_path in self .routes[method]:
130
+ return self .routes[method][handler_path]
131
+ else :
132
+ raise Error(RouterErrors.ROUTE_NOT_FOUND_ERROR )
133
+
134
+ fn func (mut self , req : HTTPRequest) raises -> HTTPResponse:
135
+ var uri = req.uri
136
+ # TODO : (Hrist) Update to lightbug_http.uri.URIDelimiters.PATH when available
137
+ var path = uri.path.split(" /" , 1 )[1 ]
138
+ var route_handler_meta : HandlerMeta
139
+ try :
140
+ route_handler_meta = self ._route(path, req.method)
141
+ except e:
142
+ if str (e) == RouterErrors.ROUTE_NOT_FOUND_ERROR :
143
+ return NotFound(uri.path)
144
+ raise e
145
+
146
+ return route_handler_meta.handler(req)
147
+
148
+ fn _validate_path_fragment (self , path_fragment : String) -> Bool:
149
+ # TODO : Validate fragment
150
+ return True
151
+
152
+ fn _validate_path (self , path : String) -> Bool:
153
+ # TODO : Validate path
154
+ return True
155
+
156
+ fn add_router (mut self , owned router : RouterBase[False ]) raises -> None :
157
+ self .sub_routers[router.path_fragment] = router
158
+
159
+ fn add_route (
160
+ mut self ,
161
+ partial_path : String,
162
+ handler : HTTPHandler,
163
+ method : RequestMethod = RequestMethod.get,
164
+ ) raises -> None :
165
+ if not self ._validate_path(partial_path):
166
+ raise Error(RouterErrors.INVALID_PATH_ERROR )
167
+ var handler_meta = HandlerMeta(handler)
168
+
169
+ self .routes[method.value][partial_path] = handler_meta^
170
+
171
+ fn get (
172
+ inout self ,
173
+ path : String,
174
+ handler : HTTPHandler,
175
+ ) raises :
176
+ self .add_route(path, handler, RequestMethod.get)
177
+
178
+ fn post (
179
+ inout self ,
180
+ path : String,
181
+ handler : HTTPHandler,
182
+ ) raises :
183
+ self .add_route(path, handler, RequestMethod.post)
184
+
185
+
186
+ alias RootRouter = RouterBase[True ]
187
+ alias Router = RouterBase[False ]
0 commit comments