mirror of
https://github.com/metosin/reitit.git
synced 2025-12-18 08:51:12 +00:00
Implement catch-all from trie
This commit is contained in:
parent
fe0ea19e31
commit
eaee4ca38d
4 changed files with 59 additions and 9 deletions
|
|
@ -13,8 +13,8 @@ import java.util.*;
|
||||||
|
|
||||||
public class Trie {
|
public class Trie {
|
||||||
|
|
||||||
private static String decode(char[] chars, int i, int j, boolean hasPercent, boolean hasPlus) {
|
private static String decode(char[] chars, int offset, int count, boolean hasPercent, boolean hasPlus) {
|
||||||
final String s = new String(chars, i, j);
|
final String s = new String(chars, offset, count);
|
||||||
try {
|
try {
|
||||||
if (hasPercent) {
|
if (hasPercent) {
|
||||||
return URLDecoder.decode(hasPlus ? s.replace("+", "%2B") : s, "UTF-8");
|
return URLDecoder.decode(hasPlus ? s.replace("+", "%2B") : s, "UTF-8");
|
||||||
|
|
@ -24,6 +24,20 @@ public class Trie {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String decode(char[] chars, int offset, int count) {
|
||||||
|
boolean hasPercent = false;
|
||||||
|
boolean hasPlus = false;
|
||||||
|
for (int j = offset; j < offset + count; j++) {
|
||||||
|
if (chars[j] == '%') {
|
||||||
|
hasPercent = true;
|
||||||
|
} else if (chars[j] == '+') {
|
||||||
|
hasPlus = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.err.println();
|
||||||
|
return decode(chars, offset, count, hasPercent, hasPlus);
|
||||||
|
}
|
||||||
|
|
||||||
public static class Match {
|
public static class Match {
|
||||||
final ITransientMap params = PersistentArrayMap.EMPTY.asTransient();
|
final ITransientMap params = PersistentArrayMap.EMPTY.asTransient();
|
||||||
public Object data;
|
public Object data;
|
||||||
|
|
@ -36,7 +50,7 @@ public class Trie {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
Map<Object, Object> m = new HashMap<>();
|
Map<Object, Object> m = new HashMap<>();
|
||||||
m.put(Keyword.intern("data"), data);
|
m.put(Keyword.intern("data"), data);
|
||||||
m.put(Keyword.intern("params"), params.persistent());
|
m.put(Keyword.intern("params"), parameters());
|
||||||
return m.toString();
|
return m.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -163,6 +177,35 @@ public class Trie {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static CatchAllMatcher catchAllMatcher(Keyword parameter, Object data) {
|
||||||
|
return new CatchAllMatcher(parameter, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class CatchAllMatcher implements Matcher {
|
||||||
|
private final Keyword parameter;
|
||||||
|
private final Object data;
|
||||||
|
|
||||||
|
CatchAllMatcher(Keyword parameter, Object data) {
|
||||||
|
this.parameter = parameter;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Match match(int i, Path path, Match match) {
|
||||||
|
if (i < path.value.length) {
|
||||||
|
match.params.assoc(parameter, decode(path.value, i, path.size - i));
|
||||||
|
match.data = data;
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "[" + parameter + " " + new DataMatcher(data) + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static LinearMatcher linearMatcher(List<Matcher> childs) {
|
public static LinearMatcher linearMatcher(List<Matcher> childs) {
|
||||||
return new LinearMatcher(childs);
|
return new LinearMatcher(childs);
|
||||||
}
|
}
|
||||||
|
|
@ -198,6 +241,10 @@ public class Trie {
|
||||||
return matcher.match(0, new Path(path), new Match());
|
return matcher.match(0, new Path(path), new Match());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Matcher scanner(List<Matcher> matchers) {
|
||||||
|
return new LinearMatcher(matchers);
|
||||||
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
||||||
//Matcher matcher = new StaticMatcher("/kikka", new StaticMatcher("/kukka", new DataMatcher(1)));
|
//Matcher matcher = new StaticMatcher("/kikka", new StaticMatcher("/kukka", new DataMatcher(1)));
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
(ns reitit.core
|
(ns reitit.core
|
||||||
(:require [meta-merge.core :refer [meta-merge]]
|
(:require [meta-merge.core :refer [meta-merge]]
|
||||||
[clojure.string :as str]
|
[clojure.string :as str]
|
||||||
[reitit.segment :as segment]
|
|
||||||
[reitit.trie :as trie]
|
[reitit.trie :as trie]
|
||||||
[reitit.impl :as impl #?@(:cljs [:refer [Route]])])
|
[reitit.impl :as impl #?@(:cljs [:refer [Route]])])
|
||||||
#?(:clj
|
#?(:clj
|
||||||
|
|
@ -176,12 +175,12 @@
|
||||||
f #(if-let [path (impl/path-for route %)]
|
f #(if-let [path (impl/path-for route %)]
|
||||||
(->Match p data result (impl/url-decode-coll %) path)
|
(->Match p data result (impl/url-decode-coll %) path)
|
||||||
(->PartialMatch p data result % path-params))]
|
(->PartialMatch p data result % path-params))]
|
||||||
[(conj pl (-> (segment/insert nil p (->Match p data result nil nil)) (segment/compile)))
|
[(conj pl (-> (trie/insert nil p (->Match p data result nil nil)) (trie/compile)))
|
||||||
(if name (assoc nl name f) nl)]))
|
(if name (assoc nl name f) nl)]))
|
||||||
[[] {}]
|
[[] {}]
|
||||||
compiled-routes)
|
compiled-routes)
|
||||||
lookup (impl/fast-map nl)
|
lookup (impl/fast-map nl)
|
||||||
scanner (segment/scanner pl)
|
scanner (trie/scanner pl)
|
||||||
routes (uncompile-routes compiled-routes)]
|
routes (uncompile-routes compiled-routes)]
|
||||||
^{:type ::router}
|
^{:type ::router}
|
||||||
(reify
|
(reify
|
||||||
|
|
@ -197,7 +196,7 @@
|
||||||
(route-names [_]
|
(route-names [_]
|
||||||
names)
|
names)
|
||||||
(match-by-path [_ path]
|
(match-by-path [_ path]
|
||||||
(if-let [match (segment/lookup scanner path)]
|
(if-let [match (trie/lookup scanner path)]
|
||||||
(-> (:data match)
|
(-> (:data match)
|
||||||
(assoc :path-params (:path-params match))
|
(assoc :path-params (:path-params match))
|
||||||
(assoc :path path))))
|
(assoc :path path))))
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,8 @@
|
||||||
(let [matchers (cond-> []
|
(let [matchers (cond-> []
|
||||||
data (conj (Trie/dataMatcher data))
|
data (conj (Trie/dataMatcher data))
|
||||||
children (into (for [[p c] children] (Trie/staticMatcher p (compile c))))
|
children (into (for [[p c] children] (Trie/staticMatcher p (compile c))))
|
||||||
wilds (into (for [[p c] wilds] (Trie/wildMatcher p (compile c)))))]
|
wilds (into (for [[p c] wilds] (Trie/wildMatcher p (compile c))))
|
||||||
|
catch-all (into (for [[p c] catch-all] (Trie/catchAllMatcher (first p) (:data c)))))]
|
||||||
(if (rest matchers)
|
(if (rest matchers)
|
||||||
(Trie/linearMatcher matchers)
|
(Trie/linearMatcher matchers)
|
||||||
(first matchers))))
|
(first matchers))))
|
||||||
|
|
@ -108,6 +109,9 @@
|
||||||
(if-let [match ^Trie$Match (Trie/lookup matcher ^String path)]
|
(if-let [match ^Trie$Match (Trie/lookup matcher ^String path)]
|
||||||
(->Match (.data match) (.parameters match))))
|
(->Match (.data match) (.parameters match))))
|
||||||
|
|
||||||
|
(defn scanner [compiled-tries]
|
||||||
|
(Trie/scanner compiled-tries))
|
||||||
|
|
||||||
;;
|
;;
|
||||||
;; matcher
|
;; matcher
|
||||||
;;
|
;;
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@
|
||||||
"modules/reitit-sieppari/src"
|
"modules/reitit-sieppari/src"
|
||||||
"modules/reitit-pedestal/src"]
|
"modules/reitit-pedestal/src"]
|
||||||
|
|
||||||
;:java-source-paths ["modules/reitit-core/java-src"]
|
:java-source-paths ["modules/reitit-core/java-src"]
|
||||||
|
|
||||||
:dependencies [[org.clojure/clojure "1.10.0"]
|
:dependencies [[org.clojure/clojure "1.10.0"]
|
||||||
[org.clojure/clojurescript "1.10.439"]
|
[org.clojure/clojurescript "1.10.439"]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue