diff --git a/modules/reitit-core/src/reitit/core.cljc b/modules/reitit-core/src/reitit/core.cljc index f939b12b..1025618a 100644 --- a/modules/reitit-core/src/reitit/core.cljc +++ b/modules/reitit-core/src/reitit/core.cljc @@ -80,6 +80,7 @@ | key | description | | -----------------------------|-------------| | `:reitit.trie/trie-compiler` | Optional trie-compiler. + | `:reitit.trie/parameters` | Optional function to transform path-parameters at creation time (default `identity`)." ([compiled-routes] (linear-router compiled-routes {})) ([compiled-routes opts] @@ -91,7 +92,7 @@ f #(if-let [path (impl/path-for route %)] (->Match p data result (impl/url-decode-coll %) path) (->PartialMatch p data result (impl/url-decode-coll %) path-params))] - [(conj pl (-> (trie/insert nil p (->Match p data result nil nil)) (trie/compile))) + [(conj pl (-> (trie/insert nil p (->Match p data result nil nil) opts) (trie/compile))) (if name (assoc nl name f) nl)])) [[] {}] compiled-routes) @@ -175,6 +176,7 @@ | key | description | | -----------------------------|-------------| | `:reitit.trie/trie-compiler` | Optional trie-compiler. + | `:reitit.trie/parameters` | Optional function to transform path-parameters at creation time (default `identity`)." ([compiled-routes] (trie-router compiled-routes {})) ([compiled-routes opts] @@ -186,7 +188,7 @@ f #(if-let [path (impl/path-for route %)] (->Match p data result (impl/url-decode-coll %) path) (->PartialMatch p data result (impl/url-decode-coll %) path-params))] - [(trie/insert pl p (->Match p data result nil nil)) + [(trie/insert pl p (->Match p data result nil nil) opts) (if name (assoc nl name f) nl)])) [nil {}] compiled-routes) diff --git a/modules/reitit-core/src/reitit/trie.cljc b/modules/reitit-core/src/reitit/trie.cljc index a6ba6251..501693b7 100644 --- a/modules/reitit-core/src/reitit/trie.cljc +++ b/modules/reitit-core/src/reitit/trie.cljc @@ -288,6 +288,18 @@ ;; Managing Tries ;; +#?(:clj + (def record-parameters + "Memoized function to transform parameters into runtime generated Record." + (memoize + (fn [params] + (let [fields (keys params)] + (if (some qualified-keyword? fields) + params + (let [name (gensym "PathParams") + ctor (symbol (str "map->" name))] + (eval `(do (defrecord ~name ~(mapv symbol fields)) (~ctor {})))))))))) + (defn insert "Returns a trie with routes added to it." ([routes] @@ -298,8 +310,10 @@ (insert acc p d)) node routes)) ([node path data] + (insert node path data nil)) + ([node path data {::keys [parameters] :or {parameters identity}}] (let [parts (split-path path) - params (zipmap (->> parts (remove string?) (map :value)) (repeat nil))] + params (parameters (zipmap (->> parts (remove string?) (map :value)) (repeat nil)))] (-insert (or node (-node {})) (split-path path) path params data)))) (defn compiler diff --git a/perf-test/clj/reitit/go_perf_test.clj b/perf-test/clj/reitit/go_perf_test.clj index 176cde7c..4e432c2e 100644 --- a/perf-test/clj/reitit/go_perf_test.clj +++ b/perf-test/clj/reitit/go_perf_test.clj @@ -2,6 +2,7 @@ (:require [criterium.core :as cc] [reitit.perf-utils :refer :all] [reitit.ring :as ring] + [reitit.trie :as trie] [clojure.string :as str])) ;; @@ -295,7 +296,8 @@ (def app (ring/ring-handler (ring/router - (reduce (partial add h) [] routes)) + (reduce (partial add h) [] routes) + {::trie/parameters trie/record-parameters}) (ring/create-default-handler) {:inject-match? false, :inject-router? false})) @@ -335,6 +337,7 @@ ;; 273ns (trie-router, no injects, direct-data) ;; 256ns (trie-router, pre-defined parameters) ;; 237ns (trie-router, single-sweep wild-params) + ;; 191ns (trie-router, record parameters) (let [req (map->Req {:request-method :get, :uri "/repos/julienschmidt/httprouter/stargazers"})] (title "param") (assert (= {:status 200, :body "/repos/:owner/:repo/stargazers"} (app req))) diff --git a/perf-test/clj/reitit/prefix_tree_perf_test.clj b/perf-test/clj/reitit/prefix_tree_perf_test.clj index b35a102a..0e6e2091 100644 --- a/perf-test/clj/reitit/prefix_tree_perf_test.clj +++ b/perf-test/clj/reitit/prefix_tree_perf_test.clj @@ -74,7 +74,7 @@ (trie/compile (reduce (fn [acc [p d]] - (trie/insert acc p d)) + (trie/insert acc p d {::trie/parameters trie/record-parameters})) nil routes)))) (defn bench! [] @@ -117,6 +117,7 @@ ;; 0.300µs (iterate arrays) ;; 0.280µs (list-params) ;; 0.096µs (trie) + ;; 0.083µs (trie, record-params, faster decode) (cc/with-progress-reporting (cc/bench (trie-matcher "/v1/orgs/1/topics")))) diff --git a/test/cljc/reitit/trie_test.cljc b/test/cljc/reitit/trie_test.cljc index 90286d77..1592123c 100644 --- a/test/cljc/reitit/trie_test.cljc +++ b/test/cljc/reitit/trie_test.cljc @@ -34,4 +34,11 @@ (is (= (trie/->Match {} {:a 1}) ((-> (trie/insert nil "" {:a 1}) (trie/compile) - (trie/path-matcher)) "")))) + (trie/path-matcher)) ""))) + + #?(:clj + (let [match ((-> (trie/insert nil "/:a" {:a 1} {::trie/parameters trie/record-parameters}) + (trie/compile) + (trie/path-matcher)) "/a")] + (is (record? (:params match))) + (is (= (trie/->Match {:a "a"} {:a 1}) (update match :params (partial into {})))))))