Advertisement
Guest User

Untitled

a guest
Apr 17th, 2015
230
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 3.86 KB | None | 0 0
  1. (ns fuzzy-urls.url
  2. (:require [clojure.string :as string]
  3. [clojure.walk :refer (keywordize-keys)]))
  4.  
  5. (defn- maybe
  6. "Helper function for creating predicates that might be nil."
  7. [pred]
  8. (some-fn nil? pred))
  9.  
  10. ; A Url is composed of seven fields:
  11. ;
  12. ; http://sky@www:801/cgi-bin/finger?name=shriram&host=nw#top
  13. ; {1-} {2} {3} {4}{---5---------} {----6-------------} {7}
  14. ;
  15. ; 1 = scheme, 2 = user, 3 = host, 4 = port,
  16. ; 5 = path (two elements), 6 = query, 7 = fragment
  17. ;
  18. ; The types of these fields are as follows:
  19. ; - scheme: string / nil
  20. ; - user: string / nil
  21. ; - host: string / nil
  22. ; - port: int / nil
  23. ; - path: [string]
  24. ; - query: {keyword, string / nil}
  25. ; - fragment: string / nil
  26.  
  27. (defrecord Url [scheme user host port path query fragment])
  28. (defn url? [x] (instance? Url x))
  29.  
  30. (defn make-url
  31. "Convenience constructor for Url."
  32. [& {:keys [scheme user host port path query fragment]
  33. :or {path []
  34. query {}}}]
  35. (->Url scheme user host port path query fragment))
  36.  
  37. ; This regex is taken mostly verbatim from RFC 3986, Appendix B.
  38. ; It has been modified slightly to disable capturing of uninteresting groups.
  39. ; The resulting capture groups correspond to the following fields:
  40. ; (1) scheme (without trailing colon)
  41. ; (2) user+host+port (no leading or trailing slashes)
  42. ; (3) path (with leading slash)
  43. ; (4) query string (without leading question mark)
  44. ; (5) fragment (without leading hash mark)
  45. (def ^:private url-pattern
  46. #"^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:?([^#]*))?(?:#(.*))?")
  47.  
  48. (def ^:private host-pattern
  49. #"^(?:([^@]*)@)?(.+?)(?::(d+))?$")
  50.  
  51.  
  52. (defn- path-string->list
  53. "Converts a path string (which may be nil) to a vector of path elements."
  54. [path]
  55. {:pre [((maybe string?) path)]
  56. :post [(vector? %) (every? string? %)]}
  57. ; drop the first element of the path because it contains a leading slash
  58. (into [] (and path (rest (string/split path #"/")))))
  59.  
  60. (defn- query-map?
  61. "A predicate for determining if a map is a valid representation of a query string."
  62. [query]
  63. {:pre [(map? query)]}
  64. (every?
  65. (fn [[k v]]
  66. (and (keyword? k)
  67. ((maybe string?) v)))
  68. query))
  69.  
  70. (defn- query-string->map
  71. "Converts a query string (which may be nil) to a map representation."
  72. [query]
  73. {:pre [((maybe string?) query)]
  74. :post [(map? %) (query-map? %)]}
  75. (if-not query
  76. {}
  77. (let [elements (string/split query #"&")
  78. pairs (map #(string/split % #"=" 2) elements)]
  79. (keywordize-keys (into {} ; this is necessary when v is nil
  80. (for [[k v] pairs] [k v]))))))
  81.  
  82. (defn- query-map->string
  83. "Converts a map representation of a query string to a string."
  84. [query]
  85. {:pre [(query-map? query)]
  86. :post [(string? %)]}
  87. (string/join
  88. "&"
  89. (for [[k v] query]
  90. (str (name k) "=" v))))
  91.  
  92. (defn string->url
  93. "Parses a string into a url. Malformed or incomplete urls are supported,
  94. and the relevant fields will be left nil."
  95. [string]
  96. {:pre [(string? string)]
  97. :post [(url? %)]}
  98. (let [[_ scheme user+host+port path query fragment]
  99. (re-matches url-pattern string)
  100. [_ user host port]
  101. (if user+host+port (re-matches host-pattern user+host+port) [])]
  102. (->Url (some-> scheme string/lower-case)
  103. user
  104. (some-> host string/lower-case)
  105. (some-> port Integer/parseInt)
  106. (path-string->list path)
  107. (query-string->map query) fragment)))
  108.  
  109. (defn url->string
  110. "Gets the string representation of a url. Missing portions are not included
  111. in the result."
  112. [url]
  113. {:pre [(url? url)]
  114. :post [(string? %)]}
  115. (let [{:keys [scheme user host port path query fragment]} url]
  116. (str
  117. (some-> scheme (str "://"))
  118. (some-> user (str "@"))
  119. host
  120. (some->> port (str ":"))
  121. (when-not (empty? path) (str "/" (string/join "/" path)))
  122. (when-not (empty? query) (str "?" (query-map->string query)))
  123. (some->> fragment (str "#")))))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement