-
Notifications
You must be signed in to change notification settings - Fork 0
/
event.clj
157 lines (131 loc) · 3.91 KB
/
event.clj
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
(ns clj-sse-client.event
(:require
[clojure.string :as str]
[clojure.spec.alpha :as s]))
(defn parse-int
[s]
(try
(Integer/parseInt s)
(catch Exception _ nil)))
(defn not-blank [s] (if (str/blank? s) nil s))
(defn non-empty [s] (if (.equals "" s) nil s))
(defprotocol IStep
(-step [this state]))
(defprotocol IEffect
(-effect [this state]))
(defprotocol IState
(-accept [state event])
(-emit [state event]))
(defrecord Message [last-event-id type data])
(s/def ::message #(instance? Message %))
(defrecord State [id event data]
IState
(-accept [this event]
(-step event this))
(-emit [this event]
(-effect event this)))
(s/def ::state #(instance? State %))
(defrecord Dispatch []
IStep
(-step [_ {:keys [id] :or {id ""}}] (->State id "" ""))
IEffect
(-effect [_ state]
(let [data (:data state)]
(if (.equals data "")
nil
(let [data (if (str/ends-with? data "\n")
(.substring data 0 (dec (.length data)))
data)
event (:event state)
event (if (str/blank? event) "message" event)
id (:id state)]
(->Message id event data))))))
(def +dispatch+ (->Dispatch))
(defrecord Ignore []
IStep
(-step [_ state] state)
IEffect
(-effect [_ _]))
(def +ignore+ (->Ignore))
(defrecord Event [event]
IStep
(-step [_ state] (assoc state :event event))
IEffect
(-effect [_ _]))
(defrecord Data [data]
IStep
(-step [_ {data' :data :as state}]
(assoc state :data (if-let [data' (non-empty data')]
(str data' data "\n")
(str data "\n"))))
IEffect
(-effect [_ _]))
(defrecord Id [^String id]
IStep
(-step [_ state]
(if (.contains id "\u0000")
state
(assoc state :id id)))
IEffect
(-effect [_ _]))
(defrecord Retry [n]
IStep
(-step [_ state] state)
IEffect
(-effect [_ _] n))
(defn -conform-value
"Collect the characters on the line after the first U+003A COLON
character (:), and let value be that string. If value starts with a
U+0020 SPACE character, remove it from value."
[^String value]
(when value (.stripLeading value)))
(defn -parse-dispatch
[field value]
(let [value (-conform-value value)]
(case field
"event" (->Event value)
"data" (->Data value)
"id" (->Id value)
"retry" (if-let [n (parse-int value)]
(->Retry n)
+ignore+)
+ignore+)))
(defn parse
"If the line is empty (a blank line)
- Dispatch the event, as defined below.
If the line starts with a U+003A COLON character (:)
- Ignore the line.
If the line contains a U+003A COLON character (:)
- Collect the characters on the line before the first U+003A COLON
character (:), and let field be that string.
- Collect the characters on the line after the first U+003A COLON
character (:), and let value be that string. If value starts with a
U+0020 SPACE character, remove it from value.
- Process the field using the steps described below, using field as the
field name and value as the field value.
Otherwise, the string is not empty but does not contain a U+003A COLON character (:)
- Process the field using the steps described below, using the whole
line as the field name, and the empty string as the field value."
[line]
(cond
(str/blank? line) +dispatch+
(str/starts-with? line ":") +ignore+
(str/includes? line ":")
(let [[field value] (str/split line #":" 2)]
(-parse-dispatch field value))
:else (-parse-dispatch line "")))
(defrecord Unset []
IState
(-accept [this event]
(let [clazz (class event)]
(cond
(instance? Dispatch clazz) this
(instance? Ignore clazz) this
:else (-accept (->State "" "" "") event))))
(-emit [_ _]))
(defn step
[state event]
(-step event state))
(defn effect
[state event]
(-effect event state))