/
matcher.cr
70 lines (61 loc) · 1.96 KB
/
matcher.cr
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
# Add routes and match routes
#
# 'T' is the type of the 'payload'. The 'payload' is what will be returned
# if the route matches.
#
# ## Example
#
# ```crystal
# # 'T' will be 'Symbol'
# router = LuckyRouter::Matcher(Symbol).new
#
# # Tell the router what payload to return if matched
# router.add("get", "/users", :index)
#
# # This will return :index
# router.match("get", "/users").payload # :index
# ```
class LuckyRouter::Matcher(T)
# aliases to help clarify what the @routes has is made of
alias RoutePartsSize = Int32
alias HttpMethod = String
# starting point from which all fragments are located
getter root = Fragment(T).new
def add(method : String, path : String, payload : T)
all_path_parts = path.split("/")
optional_parts = [] of String
all_path_parts.each do |part|
if part.starts_with?("?")
optional_parts << part.gsub("?", "")
end
end
path_without_optional_params = all_path_parts.reject(&.starts_with?("?")).join("/")
process_and_add_path(method, path_without_optional_params, payload)
optional_parts.each do |optional_part|
path_without_optional_params += "/#{optional_part}"
process_and_add_path(method, path_without_optional_params, payload)
end
end
private def process_and_add_path(method : String, path : String, payload : T)
parts = extract_parts(path)
if method.downcase == "get"
root.process_parts(parts, "head", payload)
end
root.process_parts(parts, method, payload)
end
def match(method : String, path_to_match : String) : Match(T)?
parts_to_match = extract_parts(path_to_match)
match = root.find(parts_to_match, method)
if match.is_a?(Match)
match
end
end
def match!(method : String, path_to_match : String) : Match(T)
match(method, path_to_match) || raise "No matching route found for: #{path_to_match}"
end
private def extract_parts(path)
parts = path.split("/")
parts.pop if parts.last.blank?
parts
end
end