diff options
| author | Asko Nõmm <asko@nmm.ee> | 2026-04-11 21:36:24 +0300 |
|---|---|---|
| committer | Asko Nõmm <asko@nmm.ee> | 2026-04-11 21:36:24 +0300 |
| commit | 58d7dba6be314bae223e707dc3d045752ef82691 (patch) | |
| tree | 354d1f03834ee2a7514092382df421a9c86acc50 /src/ruuter | |
| parent | d07c36e53cf2327d56a9f17c734e08f3511ceb2d (diff) | |
Validate conflicting trie parameter names
Throw on conflicting param, optional, and wildcard names at the same trie position and add cross-runtime tests to enforce the behavior.
Diffstat (limited to 'src/ruuter')
| -rw-r--r-- | src/ruuter/core.cljc | 42 |
1 files changed, 30 insertions, 12 deletions
diff --git a/src/ruuter/core.cljc b/src/ruuter/core.cljc index c1c5644..c2e05a1 100644 --- a/src/ruuter/core.cljc +++ b/src/ruuter/core.cljc @@ -62,6 +62,18 @@ :wildcard nil ;; {:param-name kw, :leaves [...]} or nil :leaves []}) ;; routes that terminate here [{:method :get :response fn :path str}] +(defn- throw-conflict + "Throws an exception when two routes define different parameter names at the + same trie position." + [slot-type existing-name new-name path] + (let [msg (str "ruuter: conflicting " slot-type " parameter names at same position — :" + (name existing-name) " and :" (name new-name) + ". Route: " path)] + (throw (ex-info msg {:slot-type slot-type + :existing-name existing-name + :new-name new-name + :path path})))) + (defn- insert-route "Inserts a single route into the trie, returning the updated trie." [trie segments leaf] @@ -76,22 +88,28 @@ (assoc-in trie [:children value] child')) :param - (let [existing (:param trie) - child (if existing (:node existing) (empty-node)) - child' (insert-route child remaining leaf)] - (assoc trie :param {:param-name value :node child'})) + (let [existing (:param trie)] + (when (and existing (not= (:param-name existing) value)) + (throw-conflict "param" (:param-name existing) value (:path leaf))) + (let [child (if existing (:node existing) (empty-node)) + child' (insert-route child remaining leaf)] + (assoc trie :param {:param-name value :node child'}))) :optional - (let [existing (:optional trie) - child (if existing (:node existing) (empty-node)) - child' (insert-route child remaining leaf)] - (assoc trie :optional {:param-name value :node child'})) + (let [existing (:optional trie)] + (when (and existing (not= (:param-name existing) value)) + (throw-conflict "optional" (:param-name existing) value (:path leaf))) + (let [child (if existing (:node existing) (empty-node)) + child' (insert-route child remaining leaf)] + (assoc trie :optional {:param-name value :node child'}))) :wildcard - (let [existing (:wildcard trie) - leaves (if existing (:leaves existing) []) - leaves' (conj leaves leaf)] - (assoc trie :wildcard {:param-name value :leaves leaves'})))))) + (let [existing (:wildcard trie)] + (when (and existing (not= (:param-name existing) value)) + (throw-conflict "wildcard" (:param-name existing) value (:path leaf))) + (let [leaves (if existing (:leaves existing) []) + leaves' (conj leaves leaf)] + (assoc trie :wildcard {:param-name value :leaves leaves'}))))))) (defn compile-routes "Compiles a vector of route maps into a trie structure for efficient |
