···11+let streams_codec =
22+ Jsont.Object.(
33+ map ~kind:"StreamsResponse" Fun.id
44+ |> mem "streams" (Jsont.list Channel.jsont) ~enc:Fun.id
55+ |> finish)
66+17let list client =
22- let response_codec =
33- Jsont.Object.(
44- map ~kind:"StreamsResponse" (fun streams -> streams)
55- |> mem "streams" (Jsont.list Channel.jsont) ~enc:(fun x -> x)
66- |> finish)
77- in
88 let json = Client.request client ~method_:`GET ~path:"/api/v1/streams" () in
99- match Encode.from_json response_codec json with
1010- | Ok channels -> channels
1111- | Error msg ->
1212- Error.raise_with_context
1313- (Error.make ~code:(Other "json_parse") ~message:msg ())
1414- "parsing channels list"
99+ Error.decode_or_raise streams_codec json "parsing channels list"
15101611let list_all client ?include_public ?include_web_public ?include_subscribed
1712 ?include_all_active ?include_default ?include_owner_subscribed () =
···1914 List.filter_map Fun.id
2015 [
2116 Option.map (fun v -> ("include_public", string_of_bool v)) include_public;
2222- Option.map
2323- (fun v -> ("include_web_public", string_of_bool v))
2424- include_web_public;
2525- Option.map
2626- (fun v -> ("include_subscribed", string_of_bool v))
2727- include_subscribed;
2828- Option.map
2929- (fun v -> ("include_all_active", string_of_bool v))
3030- include_all_active;
3131- Option.map
3232- (fun v -> ("include_default", string_of_bool v))
3333- include_default;
3434- Option.map
3535- (fun v -> ("include_owner_subscribed", string_of_bool v))
3636- include_owner_subscribed;
1717+ Option.map (fun v -> ("include_web_public", string_of_bool v)) include_web_public;
1818+ Option.map (fun v -> ("include_subscribed", string_of_bool v)) include_subscribed;
1919+ Option.map (fun v -> ("include_all_active", string_of_bool v)) include_all_active;
2020+ Option.map (fun v -> ("include_default", string_of_bool v)) include_default;
2121+ Option.map (fun v -> ("include_owner_subscribed", string_of_bool v)) include_owner_subscribed;
3722 ]
3838- in
3939- let response_codec =
4040- Jsont.Object.(
4141- map ~kind:"StreamsResponse" (fun streams -> streams)
4242- |> mem "streams" (Jsont.list Channel.jsont) ~enc:(fun x -> x)
4343- |> finish)
4423 in
4524 let json =
4625 Client.request client ~method_:`GET ~path:"/api/v1/streams" ~params ()
4726 in
4848- match Encode.from_json response_codec json with
4949- | Ok channels -> channels
5050- | Error msg ->
5151- Error.raise_with_context
5252- (Error.make ~code:(Other "json_parse") ~message:msg ())
5353- "parsing channels list"
2727+ Error.decode_or_raise streams_codec json "parsing channels list"
54285529let get_id client ~name =
5630 let encoded_name = Uri.pct_encode name in
5731 let response_codec =
5832 Jsont.Object.(
5959- map ~kind:"StreamIdResponse" (fun id -> id)
6060- |> mem "stream_id" Jsont.int ~enc:(fun x -> x)
3333+ map ~kind:"StreamIdResponse" Fun.id
3434+ |> mem "stream_id" Jsont.int ~enc:Fun.id
6135 |> finish)
6236 in
6337 let json =
···6539 ~path:("/api/v1/get_stream_id?stream=" ^ encoded_name)
6640 ()
6741 in
6868- match Encode.from_json response_codec json with
6969- | Ok id -> id
7070- | Error msg ->
7171- Error.raise_with_context
7272- (Error.make ~code:(Other "json_parse") ~message:msg ())
7373- "getting stream id for %s" name
4242+ Error.decode_or_raise response_codec json (Printf.sprintf "getting stream id for %s" name)
74437544let get_by_id client ~stream_id =
7645 let response_codec =
7746 Jsont.Object.(
7878- map ~kind:"StreamResponse" (fun stream -> stream)
7979- |> mem "stream" Channel.jsont ~enc:(fun x -> x)
4747+ map ~kind:"StreamResponse" Fun.id
4848+ |> mem "stream" Channel.jsont ~enc:Fun.id
8049 |> finish)
8150 in
8251 let json =
···8453 ~path:("/api/v1/streams/" ^ string_of_int stream_id)
8554 ()
8655 in
8787- match Encode.from_json response_codec json with
8888- | Ok channel -> channel
8989- | Error msg ->
9090- Error.raise_with_context
9191- (Error.make ~code:(Other "json_parse") ~message:msg ())
9292- "getting stream %d" stream_id
5656+ Error.decode_or_raise response_codec json (Printf.sprintf "getting stream %d" stream_id)
93579458type create_options = {
9559 name : string;
···292256let get_subscriptions client =
293257 let response_codec =
294258 Jsont.Object.(
295295- map ~kind:"SubscriptionsResponse" (fun subs -> subs)
296296- |> mem "subscriptions" (Jsont.list Channel.Subscription.jsont)
297297- ~enc:(fun x -> x)
259259+ map ~kind:"SubscriptionsResponse" Fun.id
260260+ |> mem "subscriptions" (Jsont.list Channel.Subscription.jsont) ~enc:Fun.id
298261 |> finish)
299262 in
300263 let json =
301264 Client.request client ~method_:`GET ~path:"/api/v1/users/me/subscriptions"
302265 ()
303266 in
304304- match Encode.from_json response_codec json with
305305- | Ok subs -> subs
306306- | Error msg ->
307307- Error.raise_with_context
308308- (Error.make ~code:(Other "json_parse") ~message:msg ())
309309- "parsing subscriptions"
267267+ Error.decode_or_raise response_codec json "parsing subscriptions"
310268311269let get_subscription_status client ~user_id ~stream_id =
312270 let response_codec =
313271 Jsont.Object.(
314314- map ~kind:"SubscriptionStatusResponse" (fun status -> status)
315315- |> mem "is_subscribed" Jsont.bool ~enc:(fun x -> x)
272272+ map ~kind:"SubscriptionStatusResponse" Fun.id
273273+ |> mem "is_subscribed" Jsont.bool ~enc:Fun.id
316274 |> finish)
317275 in
318276 let json =
···322280 ^ string_of_int stream_id)
323281 ()
324282 in
325325- match Encode.from_json response_codec json with
326326- | Ok status -> status
327327- | Error msg ->
328328- Error.raise_with_context
329329- (Error.make ~code:(Other "json_parse") ~message:msg ())
330330- "checking subscription status"
283283+ Error.decode_or_raise response_codec json "checking subscription status"
331284332285let update_subscription_settings client ~stream_id ?color ?is_muted ?pin_to_top
333286 ?desktop_notifications ?audible_notifications ?push_notifications
···395348let get_topics client ~stream_id =
396349 let response_codec =
397350 Jsont.Object.(
398398- map ~kind:"TopicsResponse" (fun topics -> topics)
399399- |> mem "topics" (Jsont.list Topic.jsont) ~enc:(fun x -> x)
351351+ map ~kind:"TopicsResponse" Fun.id
352352+ |> mem "topics" (Jsont.list Topic.jsont) ~enc:Fun.id
400353 |> finish)
401354 in
402355 let json =
···404357 ~path:("/api/v1/users/me/" ^ string_of_int stream_id ^ "/topics")
405358 ()
406359 in
407407- match Encode.from_json response_codec json with
408408- | Ok topics -> topics
409409- | Error msg ->
410410- Error.raise_with_context
411411- (Error.make ~code:(Other "json_parse") ~message:msg ())
412412- "getting topics for stream %d" stream_id
360360+ Error.decode_or_raise response_codec json (Printf.sprintf "getting topics for stream %d" stream_id)
413361414362let delete_topic client ~stream_id ~topic =
415363 let params = [ ("topic_name", topic) ] in
···439387let get_muted_topics client =
440388 let response_codec =
441389 Jsont.Object.(
442442- map ~kind:"MutedTopicsResponse" (fun topics -> topics)
390390+ map ~kind:"MutedTopicsResponse" Fun.id
443391 |> mem "muted_topics"
444392 (Jsont.list
445393 (Jsont.Object.(
···447395 (stream_id, topic))
448396 |> mem "stream_id" Jsont.int ~enc:fst
449397 |> mem "topic_name" Jsont.string ~enc:snd
450450- |> mem "date_muted" Jsont.int ~dec_absent:0 ~enc:(fun _ -> 0)
398398+ |> mem "date_muted" Jsont.int ~dec_absent:0 ~enc:(Fun.const 0)
451399 |> finish)))
452452- ~enc:(fun x -> x)
400400+ ~enc:Fun.id
453401 |> finish)
454402 in
455403 let json =
456404 Client.request client ~method_:`GET
457405 ~path:"/api/v1/users/me/subscriptions/muted_topics" ()
458406 in
459459- match Encode.from_json response_codec json with
460460- | Ok topics -> topics
461461- | Error msg ->
462462- Error.raise_with_context
463463- (Error.make ~code:(Other "json_parse") ~message:msg ())
464464- "getting muted topics"
407407+ Error.decode_or_raise response_codec json "getting muted topics"
465408466409let get_subscribers client ~stream_id =
467410 let response_codec =
468411 Jsont.Object.(
469469- map ~kind:"SubscribersResponse" (fun subs -> subs)
470470- |> mem "subscribers" (Jsont.list Jsont.int) ~enc:(fun x -> x)
412412+ map ~kind:"SubscribersResponse" Fun.id
413413+ |> mem "subscribers" (Jsont.list Jsont.int) ~enc:Fun.id
471414 |> finish)
472415 in
473416 let json =
···475418 ~path:("/api/v1/streams/" ^ string_of_int stream_id ^ "/members")
476419 ()
477420 in
478478- match Encode.from_json response_codec json with
479479- | Ok subs -> subs
480480- | Error msg ->
481481- Error.raise_with_context
482482- (Error.make ~code:(Other "json_parse") ~message:msg ())
483483- "getting subscribers for stream %d" stream_id
421421+ Error.decode_or_raise response_codec json (Printf.sprintf "getting subscribers for stream %d" stream_id)
484422485423let get_email_address client ~stream_id =
486424 let response_codec =
487425 Jsont.Object.(
488488- map ~kind:"EmailAddressResponse" (fun email -> email)
489489- |> mem "email" Jsont.string ~enc:(fun x -> x)
426426+ map ~kind:"EmailAddressResponse" Fun.id
427427+ |> mem "email" Jsont.string ~enc:Fun.id
490428 |> finish)
491429 in
492430 let json =
···494432 ~path:("/api/v1/streams/" ^ string_of_int stream_id ^ "/email_address")
495433 ()
496434 in
497497- match Encode.from_json response_codec json with
498498- | Ok email -> email
499499- | Error msg ->
500500- Error.raise_with_context
501501- (Error.make ~code:(Other "json_parse") ~message:msg ())
502502- "getting email address for stream %d" stream_id
435435+ Error.decode_or_raise response_codec json (Printf.sprintf "getting email address for stream %d" stream_id)
+19-27
lib/zulip/client.ml
···40404141 (* Convert params to URL query string if provided *)
4242 let url =
4343- match params with
4444- | Some p ->
4545- let uri = Uri.of_string url in
4646- let uri =
4747- List.fold_left (fun u (k, v) -> Uri.add_query_param' u (k, v)) uri p
4848- in
4949- Uri.to_string uri
5050- | None -> url
4343+ params
4444+ |> Option.map (fun p ->
4545+ Uri.of_string url
4646+ |> Fun.flip (List.fold_left (fun u (k, v) -> Uri.add_query_param' u (k, v))) p
4747+ |> Uri.to_string)
4848+ |> Option.value ~default:url
5149 in
52505351 (* Prepare request body if provided *)
5452 let body_opt =
5555- match body with
5656- | Some body_str ->
5757- let mime =
5858- match content_type with
5959- | Some ct when String.starts_with ~prefix:"multipart/form-data" ct ->
6060- (* Custom Content-Type for multipart *)
6161- Requests.Mime.of_string ct
6262- | Some "application/json" -> Requests.Mime.json
6363- | Some "application/x-www-form-urlencoded" | None ->
6464- (* Default for form data *)
6565- if
6666- String.contains body_str '='
6767- && not (String.contains body_str '{')
6868- then Requests.Mime.form
6969- else Requests.Mime.json
7070- | Some ct -> Requests.Mime.of_string ct
7171- in
7272- Some (Requests.Body.of_string mime body_str)
7373- | None -> None
5353+ body |> Option.map (fun body_str ->
5454+ let mime =
5555+ match content_type with
5656+ | Some ct when String.starts_with ~prefix:"multipart/form-data" ct ->
5757+ Requests.Mime.of_string ct
5858+ | Some "application/json" -> Requests.Mime.json
5959+ | Some "application/x-www-form-urlencoded" | None ->
6060+ if String.contains body_str '=' && not (String.contains body_str '{')
6161+ then Requests.Mime.form
6262+ else Requests.Mime.json
6363+ | Some ct -> Requests.Mime.of_string ct
6464+ in
6565+ Requests.Body.of_string mime body_str)
7466 in
75677668 (* Make the request *)
+16-9
lib/zulip/error.ml
···7878 |> finish)
79798080let of_json json =
8181- match Encode.from_json jsont json with
8282- | Ok err -> (
8181+ Encode.from_json jsont json
8282+ |> Result.to_option
8383+ |> Option.map (fun err ->
8384 match json with
8485 | Jsont.Object (fields, _) ->
8585- let assoc = List.map (fun ((k, _), v) -> (k, v)) fields in
8686 let extra =
8787- List.filter
8888- (fun (k, _) -> k <> "code" && k <> "msg" && k <> "result")
8989- assoc
8787+ fields
8888+ |> List.map (fun ((k, _), v) -> (k, v))
8989+ |> List.filter (fun (k, _) -> k <> "code" && k <> "msg" && k <> "result")
9090 in
9191- Some { err with extra }
9292- | _ -> Some err)
9393- | Error _ -> None
9191+ { err with extra }
9292+ | _ -> err)
9393+9494+let decode_or_raise codec json context =
9595+ match Encode.from_json codec json with
9696+ | Ok v -> v
9797+ | Error msg ->
9898+ raise_with_context
9999+ (make ~code:(Other "json_parse") ~message:msg ())
100100+ "%s" context
+4
lib/zulip/error.mli
···7777val of_json : Jsont.json -> t option
7878(** [of_json json] attempts to parse a Zulip API error response.
7979 Returns [None] if the JSON does not represent an error. *)
8080+8181+val decode_or_raise : 'a Jsont.t -> Jsont.json -> string -> 'a
8282+(** [decode_or_raise codec json context] decodes JSON using the codec,
8383+ or raises a Zulip error with the given context if decoding fails. *)
+5-5
lib/zulip/event.ml
···1717 Jsont.of_of_string ~kind:"Event_type.t" of_string ~enc:Event_type.to_string
18181919(* Jsont codec for event.
2020- Note: We decode id and type, but keep the full JSON as data for flexibility *)
2020+ We decode id and type, and use keep_unknown to preserve all other fields as data *)
2121let jsont =
2222 let kind = "Event" in
2323 let doc = "A Zulip event from the event queue" in
2424- (* We use a custom decoder that captures the full JSON as data *)
2525- let make id type_ =
2626- (* The data field will be populated separately *)
2727- { id; type_; data = Jsont.Null ((), Jsont.Meta.none) }
2424+ let make id type_ (data : Jsont.json) =
2525+ { id; type_; data }
2826 in
2727+ let enc_data t = t.data in
2928 Jsont.Object.map ~kind ~doc make
3029 |> Jsont.Object.mem "id" Jsont.int ~enc:id
3130 |> Jsont.Object.mem "type" event_type_jsont ~enc:type_
3131+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:enc_data
3232 |> Jsont.Object.finish
+18-43
lib/zulip/event_queue.ml
···6969 ]
7070 in
71717272- (match event_types_str with
7373- | Some types ->
7474- Log.debug (fun m ->
7575- m "Registering with event_types: %s" (String.concat "," types))
7676- | None -> ());
7272+ Option.iter (fun types ->
7373+ Log.debug (fun m -> m "Registering with event_types: %s" (String.concat "," types)))
7474+ event_types_str;
77757876 let json =
7977 Client.request client ~method_:`POST ~path:"/api/v1/register" ~params ()
8078 in
8181- match Encode.from_json Register_response.codec json with
8282- | Ok response ->
8383- { id = response.queue_id; last_event_id = response.last_event_id }
8484- | Error msg ->
8585- Error.raise_with_context
8686- (Error.make ~code:(Other "json_parse") ~message:msg ())
8787- "parsing register response"
7979+ let response = Error.decode_or_raise Register_response.codec json "parsing register response" in
8080+ { id = response.queue_id; last_event_id = response.last_event_id }
88818982let id t = t.id
9083let last_event_id t = t.last_event_id
···10194 (match List.assoc_opt "events" assoc with
10295 | Some (Jsont.Array (items, _)) ->
10396 let events =
104104- List.fold_left
105105- (fun acc item ->
106106- match Encode.from_json Event.jsont item with
107107- | Ok event -> event :: acc
108108- | Error _ -> acc)
109109- [] items
110110- |> List.rev
9797+ items |> List.filter_map (fun item ->
9898+ Encode.from_json Event.jsont item |> Result.to_option)
11199 in
112100 { events }
113101 | Some _ -> { events = [] }
···121109end
122110123111let get_events t client ?last_event_id ?dont_block () =
124124- let event_id =
125125- match last_event_id with Some id -> id | None -> t.last_event_id
126126- in
112112+ let event_id = Option.value last_event_id ~default:t.last_event_id in
127113 let params =
128114 [ ("queue_id", t.id); ("last_event_id", string_of_int event_id) ]
129129- @ (match dont_block with
130130- | Some true -> [ ("dont_block", "true") ]
131131- | _ -> [])
115115+ @ (if dont_block = Some true then [ ("dont_block", "true") ] else [])
132116 in
133117 let json =
134118 Client.request client ~method_:`GET ~path:"/api/v1/events" ~params ()
135119 in
136136- match Encode.from_json Events_response.codec json with
137137- | Ok response ->
138138- Log.debug (fun m ->
139139- m "Got %d events from API" (List.length response.events));
140140- (* Update internal last_event_id *)
141141- (match response.events with
142142- | [] -> ()
143143- | events ->
144144- let max_id =
145145- List.fold_left (fun acc e -> max acc (Event.id e)) event_id events
146146- in
147147- t.last_event_id <- max_id);
148148- response.events
149149- | Error msg ->
150150- Log.warn (fun m -> m "Failed to parse events response: %s" msg);
151151- Error.raise_with_context
152152- (Error.make ~code:(Other "json_parse") ~message:msg ())
153153- "parsing events from queue %s" t.id
120120+ let response = Error.decode_or_raise Events_response.codec json (Printf.sprintf "parsing events from queue %s" t.id) in
121121+ Log.debug (fun m -> m "Got %d events from API" (List.length response.events));
122122+ (* Update internal last_event_id *)
123123+ (match response.events with
124124+ | [] -> ()
125125+ | events ->
126126+ let max_id = List.fold_left (fun acc e -> max acc (Event.id e)) event_id events in
127127+ t.last_event_id <- max_id);
128128+ response.events
154129155130let delete t client =
156131 let params = [ ("queue_id", t.id) ] in
+7-22
lib/zulip/messages.ml
···55 Client.request client ~method_:`POST ~path:"/api/v1/messages" ~body
66 ~content_type ()
77 in
88- match Encode.from_json Message_response.jsont response with
99- | Ok msg_response -> msg_response
1010- | Error msg ->
1111- Error.raise_with_context
1212- (Error.make ~code:(Other "json_parse") ~message:msg ())
1313- "parsing message response"
88+ Error.decode_or_raise Message_response.jsont response "parsing message response"
1491510let get client ~message_id =
1611 Client.request client ~method_:`GET
···2015let get_raw client ~message_id =
2116 let response_codec =
2217 Jsont.Object.(
2323- map ~kind:"RawMessageResponse" (fun content -> content)
2424- |> mem "raw_content" Jsont.string ~enc:(fun x -> x)
1818+ map ~kind:"RawMessageResponse" Fun.id
1919+ |> mem "raw_content" Jsont.string ~enc:Fun.id
2520 |> finish)
2621 in
2722 let json =
···3025 ~params:[ ("apply_markdown", "false") ]
3126 ()
3227 in
3333- match Encode.from_json response_codec json with
3434- | Ok content -> content
3535- | Error msg ->
3636- Error.raise_with_context
3737- (Error.make ~code:(Other "json_parse") ~message:msg ())
3838- "getting raw message %d" message_id
2828+ Error.decode_or_raise response_codec json (Printf.sprintf "getting raw message %d" message_id)
39294030type anchor = Newest | Oldest | First_unread | Message_id of int
4131···207197 let params = [ ("content", content) ] in
208198 let response_codec =
209199 Jsont.Object.(
210210- map ~kind:"RenderResponse" (fun rendered -> rendered)
211211- |> mem "rendered" Jsont.string ~enc:(fun x -> x)
200200+ map ~kind:"RenderResponse" Fun.id
201201+ |> mem "rendered" Jsont.string ~enc:Fun.id
212202 |> finish)
213203 in
214204 let json =
215205 Client.request client ~method_:`POST ~path:"/api/v1/messages/render" ~params
216206 ()
217207 in
218218- match Encode.from_json response_codec json with
219219- | Ok rendered -> rendered
220220- | Error msg ->
221221- Error.raise_with_context
222222- (Error.make ~code:(Other "json_parse") ~message:msg ())
223223- "rendering message"
208208+ Error.decode_or_raise response_codec json "rendering message"
224209225210let upload_file _client ~filename:_ =
226211 (* TODO: Implement file upload using multipart/form-data *)
+2-6
lib/zulip/user.ml
···117117118118let pp fmt t =
119119 let delivery =
120120- match t.delivery_email with
121121- | Some email -> Printf.sprintf ", delivery_email=%s" email
122122- | None -> ""
120120+ Option.fold ~none:"" ~some:(Printf.sprintf ", delivery_email=%s") t.delivery_email
123121 in
124122 let uid =
125125- match t.user_id with
126126- | Some id -> Printf.sprintf ", user_id=%d" id
127127- | None -> ""
123123+ Option.fold ~none:"" ~some:(Printf.sprintf ", user_id=%d") t.user_id
128124 in
129125 Format.fprintf fmt "User{email=%s, full_name=%s%s%s}" t.email t.full_name uid
130126 delivery
+39-105
lib/zulip/users.ml
···11let list client =
22 let response_codec =
33 Jsont.Object.(
44- map ~kind:"UsersResponse" (fun members -> members)
55- |> mem "members" (Jsont.list User.jsont) ~enc:(fun x -> x)
44+ map ~kind:"UsersResponse" Fun.id
55+ |> mem "members" (Jsont.list User.jsont) ~enc:Fun.id
66 |> finish)
77 in
88 let json = Client.request client ~method_:`GET ~path:"/api/v1/users" () in
99- match Encode.from_json response_codec json with
1010- | Ok users -> users
1111- | Error msg ->
1212- Error.raise_with_context
1313- (Error.make ~code:(Other "json_parse") ~message:msg ())
1414- "parsing users list"
99+ Error.decode_or_raise response_codec json "parsing users list"
15101611let list_all client ?client_gravatar ?include_custom_profile_fields () =
1712 let params =
1813 List.filter_map Fun.id
1914 [
2020- Option.map
2121- (fun v -> ("client_gravatar", string_of_bool v))
2222- client_gravatar;
2323- Option.map
2424- (fun v -> ("include_custom_profile_fields", string_of_bool v))
2525- include_custom_profile_fields;
1515+ Option.map (fun v -> ("client_gravatar", string_of_bool v)) client_gravatar;
1616+ Option.map (fun v -> ("include_custom_profile_fields", string_of_bool v)) include_custom_profile_fields;
2617 ]
2718 in
2819 let response_codec =
2920 Jsont.Object.(
3030- map ~kind:"UsersResponse" (fun members -> members)
3131- |> mem "members" (Jsont.list User.jsont) ~enc:(fun x -> x)
2121+ map ~kind:"UsersResponse" Fun.id
2222+ |> mem "members" (Jsont.list User.jsont) ~enc:Fun.id
3223 |> finish)
3324 in
3425 let json =
3526 Client.request client ~method_:`GET ~path:"/api/v1/users" ~params ()
3627 in
3737- match Encode.from_json response_codec json with
3838- | Ok users -> users
3939- | Error msg ->
4040- Error.raise_with_context
4141- (Error.make ~code:(Other "json_parse") ~message:msg ())
4242- "parsing users list"
2828+ Error.decode_or_raise response_codec json "parsing users list"
43294430let user_response_codec =
4531 Jsont.Object.(
4646- map ~kind:"UserResponse" (fun user -> user)
4747- |> mem "user" User.jsont ~enc:(fun x -> x)
3232+ map ~kind:"UserResponse" Fun.id
3333+ |> mem "user" User.jsont ~enc:Fun.id
4834 |> finish)
49355036let get client ~email =
5137 let json =
5238 Client.request client ~method_:`GET ~path:("/api/v1/users/" ^ email) ()
5339 in
5454- match Encode.from_json user_response_codec json with
5555- | Ok user -> user
5656- | Error _ -> (
5757- match Encode.from_json User.jsont json with
5858- | Ok user -> user
5959- | Error msg ->
6060- Error.raise_with_context
6161- (Error.make ~code:(Other "json_parse") ~message:msg ())
6262- "parsing user %s" email)
4040+ Encode.from_json user_response_codec json
4141+ |> Result.fold
4242+ ~ok:Fun.id
4343+ ~error:(fun _ ->
4444+ Error.decode_or_raise User.jsont json (Printf.sprintf "parsing user %s" email))
63456446let get_by_id client ~user_id ?include_custom_profile_fields () =
6547 let params =
6648 List.filter_map Fun.id
6749 [
6868- Option.map
6969- (fun v -> ("include_custom_profile_fields", string_of_bool v))
7070- include_custom_profile_fields;
5050+ Option.map (fun v -> ("include_custom_profile_fields", string_of_bool v)) include_custom_profile_fields;
7151 ]
7252 in
7353 let json =
···7555 ~path:("/api/v1/users/" ^ string_of_int user_id)
7656 ~params ()
7757 in
7878- match Encode.from_json user_response_codec json with
7979- | Ok user -> user
8080- | Error _ -> (
8181- match Encode.from_json User.jsont json with
8282- | Ok user -> user
8383- | Error msg ->
8484- Error.raise_with_context
8585- (Error.make ~code:(Other "json_parse") ~message:msg ())
8686- "parsing user id %d" user_id)
5858+ Encode.from_json user_response_codec json
5959+ |> Result.fold
6060+ ~ok:Fun.id
6161+ ~error:(fun _ ->
6262+ Error.decode_or_raise User.jsont json (Printf.sprintf "parsing user id %d" user_id))
87638864let me client =
8965 let json = Client.request client ~method_:`GET ~path:"/api/v1/users/me" () in
9090- match Encode.from_json User.jsont json with
9191- | Ok user -> user
9292- | Error msg ->
9393- Error.raise_with_context
9494- (Error.make ~code:(Other "json_parse") ~message:msg ())
9595- "parsing current user"
6666+ Error.decode_or_raise User.jsont json "parsing current user"
96679768let me_pointer client =
9869 let response_codec =
9970 Jsont.Object.(
100100- map ~kind:"PointerResponse" (fun pointer -> pointer)
101101- |> mem "pointer" Jsont.int ~enc:(fun x -> x)
7171+ map ~kind:"PointerResponse" Fun.id
7272+ |> mem "pointer" Jsont.int ~enc:Fun.id
10273 |> finish)
10374 in
10475 let json =
10576 Client.request client ~method_:`GET ~path:"/api/v1/users/me/pointer" ()
10677 in
107107- match Encode.from_json response_codec json with
108108- | Ok pointer -> pointer
109109- | Error msg ->
110110- Error.raise_with_context
111111- (Error.make ~code:(Other "json_parse") ~message:msg ())
112112- "getting pointer"
7878+ Error.decode_or_raise response_codec json "getting pointer"
1137911480let update_me_pointer client ~pointer =
11581 let params = [ ("pointer", string_of_int pointer) ] in
···159125 in
160126 ()
161127128128+let alert_words_codec =
129129+ Jsont.Object.(
130130+ map ~kind:"AlertWordsResponse" Fun.id
131131+ |> mem "alert_words" (Jsont.list Jsont.string) ~enc:Fun.id
132132+ |> finish)
133133+162134let get_alert_words client =
163163- let response_codec =
164164- Jsont.Object.(
165165- map ~kind:"AlertWordsResponse" (fun words -> words)
166166- |> mem "alert_words" (Jsont.list Jsont.string) ~enc:(fun x -> x)
167167- |> finish)
168168- in
169135 let json =
170136 Client.request client ~method_:`GET ~path:"/api/v1/users/me/alert_words" ()
171137 in
172172- match Encode.from_json response_codec json with
173173- | Ok words -> words
174174- | Error msg ->
175175- Error.raise_with_context
176176- (Error.make ~code:(Other "json_parse") ~message:msg ())
177177- "getting alert words"
138138+ Error.decode_or_raise alert_words_codec json "getting alert words"
178139179140let add_alert_words client ~words =
180141 let params =
181142 [ ("alert_words", Encode.to_json_string (Jsont.list Jsont.string) words) ]
182143 in
183183- let response_codec =
184184- Jsont.Object.(
185185- map ~kind:"AlertWordsResponse" (fun words -> words)
186186- |> mem "alert_words" (Jsont.list Jsont.string) ~enc:(fun x -> x)
187187- |> finish)
188188- in
189144 let json =
190145 Client.request client ~method_:`POST ~path:"/api/v1/users/me/alert_words"
191146 ~params ()
192147 in
193193- match Encode.from_json response_codec json with
194194- | Ok words -> words
195195- | Error msg ->
196196- Error.raise_with_context
197197- (Error.make ~code:(Other "json_parse") ~message:msg ())
198198- "adding alert words"
148148+ Error.decode_or_raise alert_words_codec json "adding alert words"
199149200150let remove_alert_words client ~words =
201151 let params =
202152 [ ("alert_words", Encode.to_json_string (Jsont.list Jsont.string) words) ]
203203- in
204204- let response_codec =
205205- Jsont.Object.(
206206- map ~kind:"AlertWordsResponse" (fun words -> words)
207207- |> mem "alert_words" (Jsont.list Jsont.string) ~enc:(fun x -> x)
208208- |> finish)
209153 in
210154 let json =
211155 Client.request client ~method_:`DELETE ~path:"/api/v1/users/me/alert_words"
212156 ~params ()
213157 in
214214- match Encode.from_json response_codec json with
215215- | Ok words -> words
216216- | Error msg ->
217217- Error.raise_with_context
218218- (Error.make ~code:(Other "json_parse") ~message:msg ())
219219- "removing alert words"
158158+ Error.decode_or_raise alert_words_codec json "removing alert words"
220159221160type status_emoji = {
222161 emoji_name : string;
···380319let get_muted_users client =
381320 let response_codec =
382321 Jsont.Object.(
383383- map ~kind:"MutedUsersResponse" (fun users -> users)
322322+ map ~kind:"MutedUsersResponse" Fun.id
384323 |> mem "muted_users"
385324 (Jsont.list
386325 (Jsont.Object.(
387326 map ~kind:"MutedUser" (fun id _ts -> id)
388388- |> mem "id" Jsont.int ~enc:(fun x -> x)
389389- |> mem "timestamp" Jsont.int ~dec_absent:0 ~enc:(fun _ -> 0)
327327+ |> mem "id" Jsont.int ~enc:Fun.id
328328+ |> mem "timestamp" Jsont.int ~dec_absent:0 ~enc:(Fun.const 0)
390329 |> finish)))
391391- ~enc:(fun x -> x)
330330+ ~enc:Fun.id
392331 |> finish)
393332 in
394333 let json =
395334 Client.request client ~method_:`GET ~path:"/api/v1/users/me/muted_users" ()
396335 in
397397- match Encode.from_json response_codec json with
398398- | Ok users -> users
399399- | Error msg ->
400400- Error.raise_with_context
401401- (Error.make ~code:(Other "json_parse") ~message:msg ())
402402- "getting muted users"
336336+ Error.decode_or_raise response_codec json "getting muted users"
403337404338let mute_user client ~user_id =
405339 let _response =
+7-14
lib/zulip_bot/bot.ml
···1818 | Jsont.Object (fields, _) ->
1919 let assoc = List.map (fun ((k, _), v) -> (k, v)) fields in
2020 let get_int key =
2121- match List.assoc_opt key assoc with
2222- | Some (Jsont.Number (f, _)) -> int_of_float f
2323- | _ -> 0
2121+ List.assoc_opt key assoc
2222+ |> Option.fold ~none:0 ~some:(function Jsont.Number (f, _) -> int_of_float f | _ -> 0)
2423 in
2524 let get_string key =
2626- match List.assoc_opt key assoc with
2727- | Some (Jsont.String (s, _)) -> s
2828- | _ -> ""
2525+ List.assoc_opt key assoc
2626+ |> Option.fold ~none:"" ~some:(function Jsont.String (s, _) -> s | _ -> "")
2927 in
3028 { user_id = get_int "user_id"; email = get_string "email"; full_name = get_string "full_name" }
3129 | _ ->
···7977 match event_data with
8078 | Jsont.Object (fields, _) ->
8179 let assoc = List.map (fun ((k, _), v) -> (k, v)) fields in
8282- let msg =
8383- match List.assoc_opt "message" assoc with
8484- | Some m -> m
8585- | None -> event_data
8686- in
8080+ let msg = List.assoc_opt "message" assoc |> Option.value ~default:event_data in
8781 let flgs =
8888- match List.assoc_opt "flags" assoc with
8989- | Some (Jsont.Array (f, _)) -> f
9090- | _ -> []
8282+ List.assoc_opt "flags" assoc
8383+ |> Option.fold ~none:[] ~some:(function Jsont.Array (f, _) -> f | _ -> [])
9184 in
9285 (msg, flgs)
9386 | _ -> (event_data, [])