Skip to content

Instantly share code, notes, and snippets.

@skliarpawlo
Created March 5, 2019 21:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save skliarpawlo/21d16d59e11d8f2042fdc8bad4037131 to your computer and use it in GitHub Desktop.
Save skliarpawlo/21d16d59e11d8f2042fdc8bad4037131 to your computer and use it in GitHub Desktop.
;;
;; Matcher should recognize and destruct URL by:
;; host: domain
;; path: parts, splitted with "/"
;; queryparam: name/value pairs of query
;; (see examples below)
;;
;; Each string that is started from "?" is a "bind"
;; (recognize matcher) should return nil or seq of binds
;;
(import java.net.URL)
(defn new-pattern [pattern]
"parse input pattern into list of (command, param) tuples"
(let [command-re #"(host|path|queryparam)\(([^)]*)\)"
command-matches (->> (re-seq command-re pattern) (map rest))]
command-matches))
(defn parse-binds [pattern url-part]
"parse binds values from any part of a url"
(let [bind-re #"\?([^?/=&]+)"
parsed-pattern (clojure.string/replace pattern bind-re "([^?/=&]+)")
parsed-re (re-pattern parsed-pattern)
parsed-bind-names (->> (re-seq bind-re pattern) (map last))
parsed-bind-values (->> (re-find parsed-re url-part) rest)
binds-list (map array-map parsed-bind-names parsed-bind-values)
binds-map (apply (partial merge-with into) binds-list)]
(clojure.walk/keywordize-keys binds-map)))
(defn recognize-one [[command pattern] parsed-url]
"process one input pattern only"
(if-let [url-part (case command
"host" (. parsed-url getHost)
"path" (. parsed-url getPath)
"queryparam" (. parsed-url getQuery))]
(if (clojure.string/includes? pattern "?")
(parse-binds pattern url-part)
(if (= pattern url-part) url-part))))
(defn merge-recognized [res match]
"reduce function to generate final result"
(if (or (nil? res) (nil? match)) nil
(if (string? match) res
(into res (seq match)))))
(defn recognize [pattern url]
"recognize all patterns in a url"
(let [url (URL. url)
all-recognized (map #(recognize-one % url) pattern)]
(reduce merge-recognized [] all-recognized)))
;; Tests
(let [youtube (new-pattern "host(?host); path(/watch/?type); queryparam(id=?id)")]
(clojure.test/is
(= (recognize youtube "http://youtube.com/watch/video/?id=1")
[[:host "youtube.com"] [:type "video"] [:id "1"]])))
(let [twitter (new-pattern "host(twitter.com); path(?user/status/?id);")]
(clojure.test/is
(= (recognize twitter "http://twitter.com/bradfitz/status/562360748727611392")
[[:user "bradfitz"] [:id "562360748727611392"]])))
(let [dribbble (new-pattern "host(dribbble.com); path(shots/?id); queryparam(offset=?offset);")]
(clojure.test/is
(= (recognize dribbble "https://dribbble.com/shots/1905065-Travel-Icons-pack?list=users&offset=1")
[[:id "1905065-Travel-Icons-pack"] [:offset "1"]]))
(clojure.test/is
(nil? (recognize dribbble "https://twitter.com/shots/1905065-Travel-Icons-pack?list=users&offset=1")))
(clojure.test/is
(nil? (recognize dribbble "https://dribbble.com/shots/1905065-Travel-Icons-pack?list=users"))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment