jmap_base_client/lib.rs
1//! RFC 8620 base JMAP client: auth, session fetch, blob, SSE, and WebSocket.
2//!
3//! Extension-specific clients (`jmap-chat-client`, `jmap-mail-client`) depend on this crate.
4//!
5//! # Usage
6//!
7//! ```rust,no_run
8//! # use jmap_base_client::{JmapClient, DefaultTransport, BearerAuth, ClientConfig};
9//! # async fn example() -> Result<(), jmap_base_client::ClientError> {
10//! let auth = BearerAuth::new("...")?;
11//! let client = JmapClient::new(
12//! DefaultTransport,
13//! auth,
14//! "https://jmap.example.com",
15//! ClientConfig::default(),
16//! )?;
17//! let session = client.fetch_session().await?;
18//! # let _ = session;
19//! # Ok(())
20//! # }
21//! ```
22//!
23//! # `extra` field equality and the `serde_json/preserve_order` feature (bd:JMAP-6r7c.43)
24//!
25//! Every public deserializable struct in this crate carries an
26//! `extra: serde_json::Map<String, serde_json::Value>` field per the
27//! workspace extras-preservation policy (see workspace AGENTS.md). Several
28//! of these structs also derive `PartialEq` / `Eq` so callers can write
29//! `assert_eq!(a, b)` in tests and `if state_a == state_b { ... }` in
30//! application code.
31//!
32//! The derived `PartialEq` impl compares the `extra` field via
33//! [`serde_json::Map`]'s `PartialEq` impl, whose semantics depend on a
34//! third-party feature flag:
35//!
36//! - **Default (this workspace's posture).** `serde_json::Map` is
37//! BTreeMap-backed; equality is order-insensitive (keys are stored in
38//! lexicographic order regardless of insertion order).
39//! - **`serde_json/preserve_order` enabled anywhere in the dep graph.**
40//! `serde_json::Map` switches to `IndexMap` (insertion-order preserved);
41//! equality becomes order-sensitive.
42//!
43//! Two values constructed with the same `extra` entries inserted in
44//! different orders therefore compare EQUAL under the default
45//! configuration and UNEQUAL under `preserve_order`. The feature flag is
46//! a global toggle: any crate in the consumer's dep graph that enables
47//! `preserve_order` flips the semantics for every crate in the graph.
48//!
49//! **In-scope structs**: [`BlobUploadResponse`], [`Session`],
50//! [`AccountInfo`], [`WebSocketCapability`], and [`StateChange`].
51//!
52//! Workspace memory note: `jmap-base-client` (and the full workspace)
53//! does NOT enable `preserve_order`. Consumers in the same posture get
54//! deterministic `==` behaviour on `extra`. Consumers that enable
55//! `preserve_order` elsewhere and need stable equality independent of
56//! that flag should compare the serialised forms instead:
57//!
58//! ```rust,ignore
59//! let a_json = serde_json::to_value(&a)?;
60//! let b_json = serde_json::to_value(&b)?;
61//! assert_eq!(a_json, b_json);
62//! ```
63
64#![forbid(unsafe_code)]
65
66mod auth;
67mod blob;
68mod client;
69mod error;
70mod push;
71mod query;
72mod request;
73mod sse;
74mod ws;
75
76pub use auth::{
77 AuthHeader, AuthProvider, BasicAuth, BearerAuth, BuilderTransport, CustomCaTransport,
78 CustomTransportBuilder, DefaultTransport, HttpClient, NoneAuth, TransportConfig,
79};
80pub use blob::{
81 BlobDownloadStream, BlobUploadBody, BlobUploadResponse, DownloadBlobParams,
82 DownloadBlobSessionParams, UploadBlobParams, UploadBlobSessionParams, UploadBlobStreamParams,
83 expand_url_template, upload_body_from_reader,
84};
85pub use client::{
86 ClientConfig, CloseAfter, JmapClient, SseReconnectPolicy, SubscribeEventsSessionParams,
87 extract_response, method_error_details,
88};
89pub use error::{
90 ClientError, HttpError, HttpErrorKind, InvalidHeaderValueError, ParseCategory, ParseError,
91 SerializeError, WebSocketError, WebSocketErrorKind, to_args,
92};
93pub use push::StateChange;
94pub use query::QueryParams;
95pub use request::{
96 AccountInfo, AccountName, JmapRequestBuilder, JmapUrl, JmapUrlTemplate, Session, Username,
97 WebSocketCapability,
98};
99pub use sse::{SseEvent, SseFrame, parse_sse_block};
100pub use ws::{
101 DEFAULT_WS_MAX_MESSAGE_BYTES, WsFrame, WsReceiver, WsRequestError, WsSender, WsSession,
102 connect_ws, connect_ws_allow_plaintext, connect_ws_with_limit,
103};