···
let full_name t = t.full_name
let short_name t = t.short_name
21
-
Zulip.Jsonu.with_object "user" (fun fields ->
22
-
(* Handle both "user_id" and "id" field names *)
23
-
let user_id_result =
24
-
match Zulip.Jsonu.get_int fields "user_id" with
26
-
| Error _ -> Zulip.Jsonu.get_int fields "id"
28
-
match user_id_result, Zulip.Jsonu.get_string fields "email", Zulip.Jsonu.get_string fields "full_name" with
29
-
| Ok user_id, Ok email, Ok full_name ->
30
-
let short_name = Zulip.Jsonu.get_string_opt fields "short_name" in
31
-
Ok { user_id; email; full_name; short_name }
32
-
| Error e, _, _ | _, Error e, _ | _, _, Error e -> Error e
20
+
let of_json (json : Zulip.Error.json) : (t, Zulip.Error.t) result =
21
+
let open Zulip.Jsonu_syntax in
22
+
(Zulip.Jsonu.with_object "user" @@ fun fields ->
23
+
let* user_id = (field fields "user_id" int) <|> (field fields "id" int) in
24
+
let* email = field fields "email" string in
25
+
let* full_name = field fields "full_name" string in
26
+
let* short_name = field_opt fields "short_name" string in
27
+
Ok { user_id; email; full_name; short_name }) json
(** Reaction representation *)
···
let reaction_type t = t.reaction_type
let user_id t = t.user_id
51
-
Zulip.Jsonu.with_object "reaction" (fun fields ->
52
-
match Zulip.Jsonu.get_string fields "emoji_name",
53
-
Zulip.Jsonu.get_string fields "emoji_code",
54
-
Zulip.Jsonu.get_string fields "reaction_type" with
55
-
| Ok emoji_name, Ok emoji_code, Ok reaction_type ->
56
-
(* Try both "user_id" and "user" -> "user_id" patterns *)
57
-
let user_id_result =
58
-
match Zulip.Jsonu.get_int fields "user_id" with
61
-
match Zulip.Jsonu.get_object fields "user" with
62
-
| Ok user_obj -> Zulip.Jsonu.get_int user_obj "user_id"
63
-
| Error e -> Error e
65
-
(match user_id_result with
66
-
| Ok user_id -> Ok { emoji_name; emoji_code; reaction_type; user_id }
67
-
| Error e -> Error e)
68
-
| Error e, _, _ | _, Error e, _ | _, _, Error e -> Error e
44
+
let of_json (json : Zulip.Error.json) : (t, Zulip.Error.t) result =
45
+
let open Zulip.Jsonu_syntax in
46
+
(Zulip.Jsonu.with_object "reaction" @@ fun fields ->
47
+
let* emoji_name = field fields "emoji_name" string in
48
+
let* emoji_code = field fields "emoji_code" string in
49
+
let* reaction_type = field fields "reaction_type" string in
51
+
(field fields "user_id" int) <|>
52
+
(match field fields "user" object_ with
53
+
| Ok user_obj -> field user_obj "user_id" int
54
+
| Error _ -> fail "user_id not found") in
55
+
Ok { emoji_name; emoji_code; reaction_type; user_id }) json
58
+
let parse_reaction_json json = Reaction.of_json json
59
+
let parse_user_json json = User.of_json json
(** Common message fields *)
···
(** Helper function to parse common fields *)
110
-
Zulip.Jsonu.parse_with_error "common fields" (fun () ->
111
-
Zulip.Jsonu.with_object "message" (fun fields ->
112
-
match Zulip.Jsonu.get_int fields "id",
113
-
Zulip.Jsonu.get_int fields "sender_id",
114
-
Zulip.Jsonu.get_string fields "sender_email",
115
-
Zulip.Jsonu.get_string fields "sender_full_name" with
116
-
| Ok id, Ok sender_id, Ok sender_email, Ok sender_full_name ->
117
-
let sender_short_name = Zulip.Jsonu.get_string_opt fields "sender_short_name" in
118
-
let timestamp = Zulip.Jsonu.get_float_default fields "timestamp" 0.0 in
119
-
let content = Zulip.Jsonu.get_string_default fields "content" "" in
120
-
let content_type = Zulip.Jsonu.get_string_default fields "content_type" "text/html" in
99
+
Zulip.Jsonu.parse_with_error "common fields" @@ fun () ->
100
+
(Zulip.Jsonu.with_object "message" @@ fun fields ->
101
+
let open Zulip.Jsonu_syntax in
102
+
let* id = field fields "id" int in
103
+
let* sender_id = field fields "sender_id" int in
104
+
let* sender_email = field fields "sender_email" string in
105
+
let* sender_full_name = field fields "sender_full_name" string in
106
+
let sender_short_name = field_opt fields "sender_short_name" string |? None in
107
+
let timestamp = field_or fields "timestamp" float 0.0 |? 0.0 in
108
+
let content = field_or fields "content" string "" |? "" in
109
+
let content_type = field_or fields "content_type" string "text/html" |? "text/html" in
123
-
match Zulip.Jsonu.get_array_opt fields "reactions" with
124
-
| Some reactions_json ->
125
-
List.filter_map (fun r ->
126
-
match Reaction.of_json r with
127
-
| Ok reaction -> Some reaction
129
-
Log.warn (fun m -> m "Failed to parse reaction: %s" (Zulip.Error.message err));
112
+
match Zulip.Jsonu.get_array_opt fields "reactions" with
113
+
| Some reactions_json ->
114
+
List.filter_map (fun r ->
115
+
match parse_reaction_json r with
116
+
| Ok reaction -> Some reaction
118
+
Log.warn (fun m -> m "Failed to parse reaction: %s" (Zulip.Error.message err));
135
-
let submessages = Zulip.Jsonu.get_array_opt fields "submessages" |> Option.value ~default:[] in
124
+
let submessages = Zulip.Jsonu.get_array_opt fields "submessages" |> Option.value ~default:[] in
138
-
match Zulip.Jsonu.get_array_opt fields "flags" with
139
-
| Some flags_json ->
140
-
List.filter_map Zulip.Jsonu.to_string_safe flags_json
127
+
match Zulip.Jsonu.get_array_opt fields "flags" with
128
+
| Some flags_json -> List.filter_map Zulip.Jsonu.to_string_safe flags_json
144
-
let is_me_message = Zulip.Jsonu.get_bool_default fields "is_me_message" false in
145
-
let client = Zulip.Jsonu.get_string_default fields "client" "" in
146
-
let gravatar_hash = Zulip.Jsonu.get_string_default fields "gravatar_hash" "" in
147
-
let avatar_url = Zulip.Jsonu.get_string_opt fields "avatar_url" in
132
+
let is_me_message = field_or fields "is_me_message" bool false |? false in
133
+
let client = field_or fields "client" string "" |? "" in
134
+
let gravatar_hash = field_or fields "gravatar_hash" string "" |? "" in
135
+
let avatar_url = field_opt fields "avatar_url" string |? None in
150
-
id; sender_id; sender_email; sender_full_name; sender_short_name;
151
-
timestamp; content; content_type; reactions; submessages;
152
-
flags; is_me_message; client; gravatar_hash; avatar_url
154
-
| Error e, _, _, _ | _, Error e, _, _ | _, _, Error e, _ | _, _, _, Error e -> Error e
138
+
id; sender_id; sender_email; sender_full_name; sender_short_name;
139
+
timestamp; content; content_type; reactions; submessages;
140
+
flags; is_me_message; client; gravatar_hash; avatar_url
Log.debug (fun m -> m "Parsing message JSON: %s" (Zulip.Jsonu.to_string_pretty json));
147
+
let open Zulip.Jsonu_syntax in
match parse_common json with
| Error err -> Error (Zulip.Error.message err)
165
-
Zulip.Jsonu.parse_with_error "message type" (fun () ->
166
-
Zulip.Jsonu.with_object "message" (fun fields ->
167
-
match Zulip.Jsonu.get_string fields "type" with
169
-
(match Zulip.Jsonu.get_array fields "display_recipient" with
170
-
| Ok recipient_json ->
171
-
let users_results = List.map User.of_json recipient_json in
172
-
let users = List.fold_left (fun acc result ->
174
-
| Ok user -> user :: acc
176
-
Log.warn (fun m -> m "Failed to parse user in display_recipient: %s" (Zulip.Error.message err));
178
-
) [] (List.rev users_results) in
151
+
(Zulip.Jsonu.parse_with_error "message type" @@ fun () ->
152
+
(Zulip.Jsonu.with_object "message" @@ fun fields ->
153
+
match Zulip.Jsonu.get_string fields "type" with
155
+
let* recipient_json = field fields "display_recipient" (array (fun x -> Ok x)) in
156
+
let users = List.filter_map (fun u ->
157
+
match parse_user_json u with
158
+
| Ok user -> Some user
160
+
Log.warn (fun m -> m "Failed to parse user in display_recipient: %s" (Zulip.Error.message err));
162
+
) recipient_json in
180
-
if List.length users = 0 && List.length users_results > 0 then
181
-
Error (Zulip.Jsonu.json_error "Failed to parse any users in display_recipient")
183
-
Ok (Private { common; display_recipient = users })
184
-
| Error e -> Error e)
164
+
if List.length users = 0 && List.length recipient_json > 0 then
165
+
fail "Failed to parse any users in display_recipient"
167
+
Ok (Private { common; display_recipient = users })
187
-
(match Zulip.Jsonu.get_string fields "display_recipient",
188
-
Zulip.Jsonu.get_int fields "stream_id",
189
-
Zulip.Jsonu.get_string fields "subject" with
190
-
| Ok display_recipient, Ok stream_id, Ok subject ->
191
-
Ok (Stream { common; display_recipient; stream_id; subject })
192
-
| Error e, _, _ | _, Error e, _ | _, _, Error e -> Error e)
170
+
let* display_recipient = field fields "display_recipient" string in
171
+
let* stream_id = field fields "stream_id" int in
172
+
let* subject = field fields "subject" string in
173
+
Ok (Stream { common; display_recipient; stream_id; subject })
194
-
| Ok unknown_type ->
195
-
Log.warn (fun m -> m "Unknown message type: %s" unknown_type);
196
-
Ok (Unknown { common; raw_json = json })
175
+
| Ok unknown_type ->
176
+
Log.warn (fun m -> m "Unknown message type: %s" unknown_type);
177
+
Ok (Unknown { common; raw_json = json })
199
-
Log.warn (fun m -> m "No message type field found");
200
-
Ok (Unknown { common; raw_json = json })
202
-
) |> Result.map_error Zulip.Error.message
180
+
Log.warn (fun m -> m "No message type field found");
181
+
Ok (Unknown { common; raw_json = json })
182
+
) json) |> Result.map_error Zulip.Error.message
(** Accessor functions *)
let get_common = function