Add routing
This commit is contained in:
parent
1368b7b69b
commit
4316fb2a73
7 changed files with 534 additions and 109 deletions
192
router.odin
192
router.odin
|
|
@ -1,42 +1,188 @@
|
|||
#+private package
|
||||
package fjord
|
||||
|
||||
import "base:runtime"
|
||||
import "core:path/slashpath"
|
||||
import "core:strings"
|
||||
import http "http"
|
||||
import rxt "radix_tree"
|
||||
|
||||
EndpointHandler :: struct($Error_Type: typeid) {
|
||||
method_handlers: map[http.Method]#type proc(
|
||||
request: ^http.Request,
|
||||
) -> (
|
||||
http.Response,
|
||||
Error_Type,
|
||||
),
|
||||
path_variables: []string,
|
||||
PathVariable :: struct {
|
||||
name: string,
|
||||
}
|
||||
|
||||
Router :: struct {
|
||||
radix_tree: rxt.RadixTree(EndpointHandler),
|
||||
Static :: struct {}
|
||||
|
||||
RouterNodeType :: union {
|
||||
Static,
|
||||
PathVariable,
|
||||
}
|
||||
|
||||
router_init :: proc(router: ^Router, allocator := context.allocator) {
|
||||
rxt.init(&router.radix_tree, allocator)
|
||||
RouterNode :: struct($Error_Type: typeid) {
|
||||
type: RouterNodeType,
|
||||
handlers: map[http.Method]#type proc(request: ^http.Request) -> (http.Response, Error_Type),
|
||||
children: map[string]^RouterNode(Error_Type),
|
||||
}
|
||||
|
||||
router_destroy :: proc(router : ^Router) {
|
||||
rxt.destroy(&router.radix_tree)
|
||||
Router :: struct($Error_Type: typeid) {
|
||||
root_node: ^RouterNode(Error_Type),
|
||||
allocator: runtime.Allocator,
|
||||
}
|
||||
|
||||
router_lookup :: proc(router: ^Router, key: string) -> (endpoint_handler: EndpointHandler, ok: bool) {
|
||||
return rxt.lookup(&router.radix_tree, key)
|
||||
@(private = "file")
|
||||
router_node_init :: proc(
|
||||
router: ^Router($Error_Type),
|
||||
router_node: ^RouterNode(Error_Type),
|
||||
maybe_path_var_name: Maybe(string),
|
||||
) {
|
||||
router_node.children = make(
|
||||
map[string]^RouterNode(Error_Type),
|
||||
router.allocator,
|
||||
)
|
||||
|
||||
router_node.handlers = make(
|
||||
map[http.Method]#type proc(
|
||||
request: ^http.Request,
|
||||
) -> (
|
||||
http.Response,
|
||||
Error_Type,
|
||||
),
|
||||
router.allocator,
|
||||
)
|
||||
|
||||
path_var_name, ok := maybe_path_var_name.?
|
||||
if ok {
|
||||
router_node.type = PathVariable {
|
||||
name = path_var_name,
|
||||
}
|
||||
} else {
|
||||
router_node.type = Static{}
|
||||
}
|
||||
}
|
||||
|
||||
router_add_route :: proc(router: ^Router, key: []string, value: EndpointHandler) {
|
||||
joined_path := slashpath.join(key, context.allocator)
|
||||
defer delete(joined_path)
|
||||
router_init :: proc(
|
||||
router: ^Router($Error_Type),
|
||||
allocator: runtime.Allocator,
|
||||
) {
|
||||
router^ = Router(Error_Type) {
|
||||
root_node = new(RouterNode(Error_Type), allocator),
|
||||
allocator = allocator,
|
||||
}
|
||||
|
||||
rxt.insert(&router.radix_tree, joined_path[:], value)
|
||||
router_node_init(router, router.root_node, nil)
|
||||
}
|
||||
|
||||
router_remove_route :: proc(router: ^Router, key: string) -> (ok: bool){
|
||||
return rxt.remove(&router.radix_tree, key)
|
||||
router_destroy :: proc(router: ^Router($Error_Type)) {
|
||||
stack := make([dynamic]^RouterNode(Error_Type), router.allocator)
|
||||
defer delete(stack)
|
||||
|
||||
append(&stack, router.root_node)
|
||||
|
||||
for len(stack) > 0 {
|
||||
node := pop(&stack)
|
||||
for _, child in node.children {
|
||||
append(&stack, child)
|
||||
}
|
||||
|
||||
delete(node.handlers)
|
||||
delete(node.children)
|
||||
free(node)
|
||||
}
|
||||
}
|
||||
|
||||
router_lookup :: proc(
|
||||
router: ^Router($Error_Type),
|
||||
method: http.Method,
|
||||
path: []string,
|
||||
allocator := context.temp_allocator,
|
||||
) -> (
|
||||
handler: #type proc(request: ^http.Request) -> (http.Response, Error_Type),
|
||||
path_variables: map[string]string,
|
||||
ok: bool,
|
||||
) {
|
||||
path_variables = make(map[string]string, allocator)
|
||||
current_node := router.root_node
|
||||
|
||||
for segment_value in path {
|
||||
if child, found := current_node.children[segment_value]; found {
|
||||
current_node = child
|
||||
continue
|
||||
}
|
||||
|
||||
maybe_path_child: Maybe(^RouterNode(Error_Type)) = nil
|
||||
for name, child in current_node.children {
|
||||
switch type in child.type {
|
||||
case PathVariable:
|
||||
maybe_path_child = child
|
||||
path_variables[type.name] = segment_value
|
||||
break
|
||||
case Static:
|
||||
}
|
||||
}
|
||||
|
||||
if path_child, ok := maybe_path_child.?; ok {
|
||||
current_node = path_child
|
||||
} else {
|
||||
return handler, path_variables, false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if handler, ok := current_node.handlers[method]; ok {
|
||||
return handler, path_variables, true
|
||||
} else {
|
||||
return handler, path_variables, false
|
||||
}
|
||||
}
|
||||
|
||||
router_add_route :: proc(
|
||||
router: ^Router($Error_Type),
|
||||
method: http.Method,
|
||||
path: []string,
|
||||
handler: #type proc(request: ^http.Request) -> (http.Response, Error_Type),
|
||||
) {
|
||||
current_node := router.root_node
|
||||
|
||||
for segment_name in path {
|
||||
node, ok := current_node.children[segment_name]
|
||||
if !ok {
|
||||
new_node := new(RouterNode(Error_Type))
|
||||
|
||||
if strings.has_prefix(segment_name, ":") {
|
||||
router_node_init(router, new_node, segment_name[1:])
|
||||
} else {
|
||||
router_node_init(router, new_node, nil)
|
||||
}
|
||||
|
||||
current_node.children[segment_name] = new_node
|
||||
current_node = new_node
|
||||
continue
|
||||
}
|
||||
|
||||
current_node = node
|
||||
}
|
||||
|
||||
current_node.handlers[method] = handler
|
||||
}
|
||||
|
||||
router_remove_route :: proc(
|
||||
router: ^Router($Error_Type),
|
||||
method: http.Method,
|
||||
path: []string,
|
||||
) -> (
|
||||
ok: bool,
|
||||
) {
|
||||
current_node := router.root_node
|
||||
|
||||
for segment_name in path {
|
||||
node, ok := current_node.children[segment_name]
|
||||
if !ok do return false
|
||||
current_node = node
|
||||
}
|
||||
|
||||
if method not_in current_node.handlers {
|
||||
return false
|
||||
}
|
||||
|
||||
delete_key(¤t_node.handlers, method)
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue