1 module hunt.http.routing.impl.ParameterPathMatcher;
2 
3 import hunt.http.routing.Matcher;
4 import hunt.http.routing.Router;
5 
6 import hunt.collection;
7 import hunt.Exceptions;
8 import hunt.logging;
9 import hunt.text;
10 
11 import std.array;
12 import std.path;
13 import std.string;
14 import std.regex;
15 
16 /**
17  * 
18  */
19 class ParameterPathMatcher : Matcher {
20 
21     private Map!(int, Map!(ParameterPath, Set!(Router))) _parameterPath;
22 
23     private static class ParameterPath {
24         string rule;
25         string[] paths;
26 
27         this(string rule) {
28             this.rule = rule;
29             paths = pathSplitter(rule).array;
30         }
31 
32         override bool opEquals(Object o) {
33             if (this is o)
34                 return true;
35             ParameterPath that = cast(ParameterPath) o;
36             if (that is null)
37                 return false;
38             return rule == that.rule;
39         }
40 
41         override size_t toHash() @trusted nothrow {
42             return hashOf(rule);
43         }
44 
45         Map!(string, string) match(string[] list) {
46             Map!(string, string) param = new HashMap!(string, string)();
47             for (size_t i = 0; i < list.length; i++) {
48                 string path = paths[i];
49                 string value = list[i];
50 
51                 version(HUNT_HTTP_DEBUG_MORE) info("parameter: ", path, ", value: ", value);
52                 if (path[0] == ':') {  // :id
53                     param.put(path[1..$], value);
54                 } else  {
55                     string pattern = path;
56                     string urlTemplate = path;
57                     auto matches = path.matchFirst(regex(`\{(\w+)(<([^>]+)>)?\}`));
58                     if (!matches.empty) {
59                         version(HUNT_HTTP_DEBUG_MORE) tracef("name: %s, pattern: %s", matches[1], matches[3]);
60                         auto valueMatches = value.matchFirst(regex(matches[3]));
61                         if(valueMatches.empty || valueMatches[0] == value) {
62                             param.put(matches[1], value);
63                         } else {
64                             return null;
65                         }
66                     } else if (path != value) {
67                         return null;
68                     }
69                 }
70             }
71             return param;
72         }
73     }
74 
75     this() {
76         _parameterPath = new HashMap!(int, Map!(ParameterPath, Set!(Router)))();
77     }
78 
79     // private Map!(int, Map!(ParameterPath, Set!(Router))) parameterPath() {
80     //     if (_parameterPath is null) {
81     //         _parameterPath = new HashMap!(int, Map!(ParameterPath, Set!(Router)))();
82     //     }
83     //     return _parameterPath;
84     // }
85 
86     override void add(string rule, Router router) {
87         ParameterPath parameterPath = new ParameterPath(rule);
88         int pathSize = cast(int) parameterPath.paths.length;
89         Map!(ParameterPath, Set!(Router)) p = _parameterPath.get(pathSize);
90         if (p is null) {
91             p = new HashMap!(ParameterPath, Set!(Router))();
92             _parameterPath.put(pathSize, p);
93         }
94 
95         Set!(Router) r = p.get(parameterPath);
96         if (r is null) {
97             r = new HashSet!(Router)();
98             p.put(parameterPath, r);
99         }
100         r.add(router);
101     }
102 
103     override MatchResult match(string value) {
104         if (_parameterPath is null) {
105             return null;
106         }
107 
108         if (value.length == 1) {
109             if (value.charAt(0) == '/') {
110                 return null;
111             } else {
112                 throw new IllegalArgumentException("the path: [" ~ value ~ "] format error");
113             }
114         } else {
115             string[] list = pathSplitter(value).array;
116             Map!(ParameterPath, Set!(Router)) map = _parameterPath.get(cast(int) list.length);
117             if (map !is null && !map.isEmpty()) {
118                 Set!(Router) routers = new HashSet!(Router)();
119                 Map!(Router, Map!(string, string)) parameters = new HashMap!(Router,
120                         Map!(string, string))();
121 
122                 foreach (ParameterPath key, Set!(Router) regRouter; map) {
123                     Map!(string, string) param = key.match(list);
124                     if (param !is null) {
125                         routers.addAll(regRouter);
126                         // regRouter.forEach(router -> parameters.put(router, param));
127                         foreach (router; regRouter)
128                             parameters.put(router, param);
129                     }
130                 }
131 
132                 if (!routers.isEmpty()) {
133                     return new MatchResult(routers, parameters, getMatchType());
134                 } else {
135                     return null;
136                 }
137             } else {
138                 return null;
139             }
140         }
141     }
142 
143     override MatchType getMatchType() {
144         return MatchType.PATH;
145     }
146 }