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 }