summaryrefslogtreecommitdiff
path: root/src/ruuter/core.clj
diff options
context:
space:
mode:
authorAsko Nõmm <asko@bien.ee>2021-10-02 01:26:07 -0300
committerAsko Nõmm <asko@bien.ee>2021-10-02 01:26:07 -0300
commit8a158dd3cff218223afb9dd77c567023795bfc7e (patch)
tree345cfbfdf430bbcfe684d219e20f2f4ef9035912 /src/ruuter/core.clj
Initial commit
Diffstat (limited to 'src/ruuter/core.clj')
-rw-r--r--src/ruuter/core.clj97
1 files changed, 97 insertions, 0 deletions
diff --git a/src/ruuter/core.clj b/src/ruuter/core.clj
new file mode 100644
index 0000000..2504b6f
--- /dev/null
+++ b/src/ruuter/core.clj
@@ -0,0 +1,97 @@
+(ns ruuter.core
+ (:require [clojure.string :as string]
+ [org.httpkit.server :as http]))
+
+
+(def routes [{:path "/"
+ :method :get
+ :response {:status 200
+ :body "Hello, World."}}
+ {:path "/some/page/goes/here"
+ :method :get
+ :response {:status 200
+ :body ":)"}}
+ {:path "/hi/:name"
+ :method :get
+ :response (fn [req]
+ {:status 200
+ :body (str "Hi, " (:name (:params req)))})}
+ {:path :not-found
+ :response {:status 404
+ :body "Not found."}}])
+
+
+(defn- path->regex-path
+ [path]
+ (if (= "/" path)
+ path
+ (->> (string/split path #"/")
+ (map (fn [piece]
+ (if (string/starts-with? piece ":")
+ ".*"
+ piece)))
+ (string/join "/"))))
+
+
+(defn- path+uri->path-params
+ [path uri]
+ (if (= "/" path)
+ {}
+ (let [split-path (string/split path #"/")
+ split-uri (string/split uri #"/")]
+ (into {} (map-indexed
+ (fn [idx item]
+ (when (string/starts-with? item ":")
+ {(keyword (subs item 1)) (get split-uri idx)}))
+ split-path)))))
+
+
+(defn- match-route
+ [routes uri request-method]
+ (->> routes
+ (filter #(not (= :not-found (:path %))))
+ (map #(merge % {:regex-path (path->regex-path (:path %))}))
+ (filter #(and (re-matches (re-pattern (:regex-path %)) uri)
+ (= (:method %) request-method)))
+ first))
+
+
+(defn- route+req->response
+ [{:keys [path response]} {:keys [uri] :as req}]
+ (cond
+ ; responses are maps, so there's no reason they can't be
+ ; direct maps.
+ (map? response)
+ response
+ ; responses can also be functions that return maps, and
+ ; when using a function, you get the whole `req` and params
+ ; with it as well.
+ (fn? response)
+ (response (->> {:params (path+uri->path-params path uri)}
+ (merge req)))
+ ; if by whatever reason we make it here it must mean the
+ ; route is invalid, or doesn't exist, in which case we return
+ ; an error message.
+ :else
+ {:status 404
+ :body "Not found."}))
+
+
+(defn- router
+ [routes {:keys [uri request-method] :as req}]
+ (if-let [route (match-route routes uri request-method)]
+ (route+req->response route req)
+ (route+req->response (->> routes
+ (filter #(= :not-found (:path %)))
+ first) req)))
+
+
+(defn route!
+ ([routes]
+ (route! routes {:port 9600}))
+ ([routes opts]
+ (http/run-server #(router routes %) opts)))
+
+
+(defn -main [& opts]
+ (route! routes)) \ No newline at end of file