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, auth::{DefaultTransport, BearerAuth}, client::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
66pub mod auth;
67pub mod blob;
68pub mod client;
69pub mod error;
70pub mod push;
71pub mod request;
72pub mod sse;
73pub mod ws;
74
75pub use auth::{
76 AuthHeader, AuthProvider, BasicAuth, BearerAuth, BuilderTransport, CustomCaTransport,
77 CustomTransportBuilder, DefaultTransport, HttpClient, NoneAuth, TransportConfig,
78};
79pub use blob::{
80 expand_url_template, BlobUploadResponse, DownloadBlobParams, DownloadBlobSessionParams,
81 UploadBlobParams, UploadBlobSessionParams,
82};
83pub use client::{extract_response, ClientConfig, JmapClient, SubscribeEventsSessionParams};
84pub use error::{
85 ClientError, HttpError, InvalidHeaderValueError, ParseCategory, ParseError, SerializeError,
86 WebSocketError,
87};
88pub use push::StateChange;
89pub use request::{
90 AccountInfo, AccountName, JmapRequestBuilder, JmapUrl, JmapUrlTemplate, Session, Username,
91 WebSocketCapability,
92};
93pub use sse::{parse_sse_block, SseEvent, SseFrame};
94pub use ws::{connect_ws, WsFrame, WsReceiver, WsSender, WsSession};