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
type event = { event : string option; data : string list }
type state = { buffer : string }
let empty = { buffer = "" }
let normalize_newlines input =
input |> String.split_on_char '\r' |> String.concat ""
let split_complete_frames buffer =
let normalized = normalize_newlines buffer in
let rec scan acc start =
let rec find_separator index =
if index + 1 >= String.length normalized then None
else if normalized.[index] = '\n' && normalized.[index + 1] = '\n' then
Some index
else find_separator (index + 1)
in
match find_separator start with
| None ->
( List.rev acc,
String.sub normalized start (String.length normalized - start) )
| Some index ->
let frame = String.sub normalized start (index - start) in
scan (frame :: acc) (index + 2)
in
scan [] 0
let parse_line (event, data) line =
if line = "" || line.[0] = ':' then (event, data)
else
match String.index_opt line ':' with
| None -> if line = "event" then (Some "", data) else (event, data)
| Some index ->
let name = String.sub line 0 index in
let value_start =
if index + 1 < String.length line && line.[index + 1] = ' ' then
index + 2
else index + 1
in
let value =
String.sub line value_start (String.length line - value_start)
in
if name = "event" then (Some value, data)
else if name = "data" then (event, data @ [ value ])
else (event, data)
let parse_frame frame =
let event, data =
frame |> String.split_on_char '\n' |> List.fold_left parse_line (None, [])
in
{ event; data }
let feed state chunk =
let frames, remaining = split_complete_frames (state.buffer ^ chunk) in
({ buffer = remaining }, List.map parse_frame frames)
let finish state =
if state.buffer = "" then [] else [ parse_frame state.buffer ]
let data_string event = String.concat "\n" event.data
let is_done event = String.trim (data_string event) = "[DONE]"