Make transit module extensible

This commit is contained in:
Andrey Antukh 2022-10-11 14:57:49 +02:00 committed by Andrés Moya
parent b1296ef765
commit 4ece0cdeda

View file

@ -6,6 +6,7 @@
(ns app.common.transit (ns app.common.transit
(:require (:require
[app.common.data :as d]
[app.common.geom.matrix :as gmt] [app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.uri :as uri] [app.common.uri :as uri]
@ -13,7 +14,6 @@
[lambdaisland.uri :as luri] [lambdaisland.uri :as luri]
[linked.core :as lk] [linked.core :as lk]
[linked.set :as lks] [linked.set :as lks]
#?(:cljs ["luxon" :as lxn])) #?(:cljs ["luxon" :as lxn]))
#?(:clj #?(:clj
(:import (:import
@ -28,7 +28,12 @@
lambdaisland.uri.URI lambdaisland.uri.URI
linked.set.LinkedSet))) linked.set.LinkedSet)))
;; --- MISC (def write-handlers (atom nil))
(def read-handlers (atom nil))
(def write-handler-map (atom nil))
(def read-handler-map (atom nil))
;; --- HELPERS
#?(:clj #?(:clj
(defn str->bytes (defn str->bytes
@ -44,151 +49,115 @@
([^bytes data, ^String encoding] ([^bytes data, ^String encoding]
(String. data encoding)))) (String. data encoding))))
#?(:clj (defn add-handlers!
(def ^:private file-write-handler [& handlers]
(t/write-handler (letfn [(adapt-write-handler [{:keys [id class wfn]}]
(constantly "file") [class (t/write-handler (constantly id) wfn)])
(fn [v] (str v)))))
#?(:cljs (adapt-read-handler [{:keys [id rfn]}]
(def bigint-read-handler [id (t/read-handler rfn)])
(t/read-handler
(fn [value]
(js/parseInt value 10)))))
#?(:cljs (merge-and-clean [m1 m2]
(def uuid-read-handler (-> (merge m1 m2)
(t/read-handler uuid))) (d/without-nils)))]
;; --- GEOM (let [rhs (into {}
(comp
(filter :rfn)
(map adapt-read-handler))
handlers)
whs (into {}
(comp
(filter :wfn)
(map adapt-write-handler))
handlers)
cwh (swap! write-handlers merge-and-clean whs)
crh (swap! read-handlers merge-and-clean rhs)]
(def point-write-handler (reset! write-handler-map #?(:clj (t/write-handler-map cwh) :cljs cwh))
(t/write-handler (reset! read-handler-map #?(:clj (t/read-handler-map crh) :cljs crh))
(constantly "point") nil)))
(fn [v] (into {} v))))
(def point-read-handler
(t/read-handler gpt/map->Point))
(def matrix-write-handler
(t/write-handler
(constantly "matrix")
(fn [v] (into {} v))))
(def matrix-read-handler
(t/read-handler (fn [data]
#?(:cljs (gmt/map->Matrix data)
:clj (let [{:keys [a b c d e f]} data]
(gmt/matrix (double a)
(double b)
(double c)
(double d)
(double e)
(double f)))))))
;; --- ORDERED SET
(def ordered-set-write-handler
(t/write-handler
(constantly "ordered-set")
(fn [v] (vec v))))
(def ordered-set-read-handler
(t/read-handler #(into (lk/set) %)))
;; --- DURATION
(def duration-read-handler
#?(:cljs (t/read-handler #(.fromMillis ^js lxn/Duration %))
:clj (t/read-handler #(Duration/ofMillis %))))
(def duration-write-handler
(t/write-handler
(constantly "duration")
(fn [v] (inst-ms v))))
;; --- TIME
(def ^:private instant-read-handler
#?(:clj
(t/read-handler
(fn [v] (-> (Long/parseLong v)
(Instant/ofEpochMilli))))
:cljs
(t/read-handler
(fn [v]
(let [ms (js/parseInt v 10)]
(.fromMillis ^js lxn/DateTime ms))))))
(def ^:private instant-write-handler
(t/write-handler
(constantly "m")
(fn [v] (str (inst-ms v)))))
;; --- URI
(def uri-read-handler
(t/read-handler uri/uri))
(def uri-write-handler
(t/write-handler
(constantly "uri")
(fn [v] (str v))))
;; --- HANDLERS ;; --- HANDLERS
(def +read-handlers+ (add-handlers!
{"matrix" matrix-read-handler #?(:clj
"ordered-set" ordered-set-read-handler {:id "file"
"point" point-read-handler :class File
"duration" duration-read-handler :wfn str
"m" instant-read-handler :rfn identity})
"uri" uri-read-handler
#?@(:cljs ["n" bigint-read-handler
"u" uuid-read-handler])
})
(def +write-handlers+ #?(:cljs
#?(:clj {:id "n"
{Matrix matrix-write-handler :rfn (fn [value]
Point point-write-handler (js/parseInt value 10))})
Instant instant-write-handler #?(:cljs
LinkedSet ordered-set-write-handler {:id "u"
URI uri-write-handler :rfn parse-uuid})
File file-write-handler
OffsetDateTime instant-write-handler} {:id "point"
:cljs :class #?(:clj Point :cljs gpt/Point)
{gmt/Matrix matrix-write-handler :wfn #(into {} %)
gpt/Point point-write-handler :rfn gpt/map->Point}
lxn/DateTime instant-write-handler
lxn/Duration duration-write-handler {:id "matrix"
lks/LinkedSet ordered-set-write-handler :class #?(:clj Matrix :cljs gmt/Matrix)
luri/URI uri-write-handler} :wfn #(into {} %)
)) :rfn #?(:cljs gmt/map->Matrix
:clj (fn [{:keys [a b c d e f]}]
(gmt/matrix (double a)
(double b)
(double c)
(double d)
(double e)
(double f))))}
{:id "ordered-set"
:class #?(:clj LinkedSet :cljs lks/LinkedSet)
:wfn vec
:rfn #(into (lk/set) %)}
{:id "duration"
:class #?(:clj Duration :cljs lxn/Duration)
:rfn (fn [v]
#?(:clj (Duration/ofMillis v)
:cljs (.fromMillis ^js lxn/Duration v)))
:wfn inst-ms}
{:id "m"
:class #?(:clj Instant :cljs lxn/DateTime)
:rfn (fn [v]
#?(:clj (-> (Long/parseLong v)
(Instant/ofEpochMilli))
:cljs (let [ms (js/parseInt v 10)]
(.fromMillis ^js lxn/DateTime ms))))
:wfn (comp str inst-ms)}
#?(:clj
{:id "m"
:class OffsetDateTime
:wfn (comp str inst-ms)})
{:id "uri"
:class #?(:clj URI :cljs luri/URI)
:rfn uri/uri
:wfn str})
;; --- Low-Level Api ;; --- Low-Level Api
#?(:clj
(def read-handlers
(t/read-handler-map +read-handlers+)))
#?(:clj
(def write-handlers
(t/write-handler-map +write-handlers+)))
#?(:clj #?(:clj
(defn reader (defn reader
([istream] ([istream]
(reader istream nil)) (reader istream nil))
([istream {:keys [type] :or {type :json}}] ([istream {:keys [type] :or {type :json}}]
(t/reader istream type {:handlers read-handlers})))) (t/reader istream type {:handlers @read-handler-map}))))
#?(:clj #?(:clj
(defn writer (defn writer
([ostream] ([ostream]
(writer ostream nil)) (writer ostream nil))
([ostream {:keys [type] :or {type :json}}] ([ostream {:keys [type] :or {type :json}}]
(t/writer ostream type {:handlers write-handlers})))) (t/writer ostream type {:handlers @write-handler-map}))))
#?(:clj #?(:clj
(defn read! (defn read!
@ -200,7 +169,6 @@
[writer data] [writer data]
(t/write writer data))) (t/write writer data)))
;; --- High-Level Api ;; --- High-Level Api
#?(:clj #?(:clj
@ -223,7 +191,7 @@
([data opts] ([data opts]
#?(:cljs #?(:cljs
(let [t (:type opts :json) (let [t (:type opts :json)
w (t/writer t {:handlers +write-handlers+})] w (t/writer t {:handlers @write-handler-map})]
(t/write w data)) (t/write w data))
:clj :clj
(->> (encode data opts) (->> (encode data opts)
@ -234,7 +202,7 @@
([data opts] ([data opts]
#?(:cljs #?(:cljs
(let [t (:type opts :json) (let [t (:type opts :json)
r (t/reader t {:handlers +read-handlers+})] r (t/reader t {:handlers @read-handler-map})]
(t/read r data)) (t/read r data))
:clj :clj
(-> (str->bytes data) (-> (str->bytes data)