/
ednize.cljs
91 lines (72 loc) · 2.26 KB
/
ednize.cljs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
(ns fipp.ednize
(:require [clojure.string :as s]
[fipp.util :refer [edn?]]))
(defprotocol IEdn
"Perform a shallow conversion to an Edn data structure."
(-edn [x]))
(defprotocol IOverride
"Mark object as preferring its custom IEdn behavior.")
(defn override? [x]
(satisfies? IEdn x))
;;TODO Automated test cases for all of these!
;;XXX Usages of type/pr-str seem wrong...
(defn edn [x]
(cond
(override? x)
(-edn x)
(object? x)
(tagged-literal 'js
(into {} (for [k (js-keys x)]
[(keyword k) (aget x k)])))
(array? x)
(tagged-literal 'js (vec x))
(satisfies? IDeref x)
(let [pending? (and (satisfies? IPending x)
(not (realized? x)))
;; Can this throw and yield status :failed like in JVM Clojure?
val (when-not pending?
@x)
status (if pending?
:pending
:ready)]
(tagged-literal 'object
[(-> x type pr-str symbol)
{:status status :val val}]))
(instance? js/Date x)
(tagged-literal 'inst
(let [normalize (fn [n len]
(loop [ns (str n)]
(if (< (count ns) len)
(recur (str "0" ns))
ns)))]
(str (.getUTCFullYear x) "-"
(normalize (inc (.getUTCMonth x)) 2) "-"
(normalize (.getUTCDate x) 2) "T"
(normalize (.getUTCHours x) 2) ":"
(normalize (.getUTCMinutes x) 2) ":"
(normalize (.getUTCSeconds x) 2) "."
(normalize (.getUTCMilliseconds x) 3) "-"
"00:00")))
(satisfies? IEdn x)
(-edn x)
(edn? x)
x
:else
;;TODO Something better.
(tagged-literal 'object [(-> x type pr-str symbol)])
))
(extend-protocol IEdn
UUID
(-edn [x]
(tagged-literal 'uuid (str x)))
ExceptionInfo
(-edn [x]
(tagged-literal 'ExceptionInfo
(merge {:message (ex-message x)
:data (ex-data x)}
(when-let [cause (ex-cause x)]
{:cause cause}))))
)
(defn record->tagged [x]
(tagged-literal (apply symbol (s/split (-> x type pr-str) #"/" 2))
(into {} x)))