Expand description
ClientError and the opaque wrapper types (HttpError,
WebSocketError, InvalidHeaderValueError, ParseError,
SerializeError) that hide reqwest, tokio_tungstenite, and
the underlying JSON parser from this crate’s public API.
§SemVer policy
reqwest and tokio-tungstenite are private dependencies of this
crate. Their types do not appear in any public function signature,
variant payload, or From impl. The wrapper types (HttpError,
WebSocketError, InvalidHeaderValueError) expose a curated set of
diagnostic accessors that return primitive types only, so this crate can
bump the underlying transport’s major version without breaking
downstream callers.
Internal construction goes through pub(crate) helpers on
ClientError (from_reqwest, from_ws, from_invalid_header) —
downstream consumers cannot construct the transport-error variants and
never need to.
§serde_json is a partially-wrapped dependency (bd:JMAP-6r7c.26)
ParseError and SerializeError wrap serde_json::Error for the
Parse and Serialize
variant payloads. The wrappers expose only primitive accessors
(line, column,
classify returning the workspace-local
ParseCategory enum), so pattern-matching code on these variants
is insulated from a future JSON-parser swap (e.g. simd-json,
serde_json_lenient) — the variant payload type is stable across
such a swap.
The asymmetry with HttpError / WebSocketError: extension client
crates (jmap-mail-client, jmap-chat-client, etc.) need to surface
their own JSON parse / serialize failures as ClientError::Parse
and ClientError::Serialize, so this crate publishes
ClientError::from_parse and ClientError::from_serialize as
the construction path. Those constructors take a
serde_json::Error in their signature, so serde_json remains a
transitively-public dependency for crates that call those helpers
directly. A future JSON-parser swap would deprecate
from_parse(serde_json::Error) in favor of an analogous helper for
the new parser; the variant payload type (ParseError) does not
change.
§Do not simplify the wrappers (bd:JMAP-6r7c.16)
A future contributor reading this module may suggest “just put
reqwest::Error in the variant — downstream users want the full
reqwest API”. That is the wrong simplification. The wrapper-types
pattern is load-bearing for five independent reasons; all five must
be re-derived from first principles before the wrappers can be
removed:
- SemVer-bump isolation.
reqwest::Errorandtungstenite::Errorare#[non_exhaustive]from third-party crates that bump major versions independently of this crate. Exposing them in this crate’s public API turns every transitivereqwestmajor bump into a SemVer break for every downstream extension client (jmap-mail-client,jmap-chat-client,jmap-calendars-client, etc., all eight planned extensions). - Transport replaceability. This crate may swap the HTTP /
WebSocket transport entirely —
ureq,hyper-utildirectly, acurl-backed transport for an unusual deployment — without breaking downstream. The only thing downstream binds to is the wrapper’s accessor signature; the wrapped type is private and can be replaced in-place. - Curated accessor surface. Not every
reqwest::Errormethod is mirrored. Adding new diagnostic surface (e.g.WebSocketError::close_code()) requires a deliberatepub fndecision in this file, which surfaces in code review. An unwrapped error would silently grow the surface everyreqwestminor release. - Opaque construction. The
pub(crate) from_reqwest/from_ws/from_invalid_headerhelpers onClientErrormean downstream cannot construct the transport-error variants even if they wanted to. That keeps the variants genuinely opaque — no “well, the public field is areqwest::Error, so downstream can match on it” loophole. - Workspace policy alignment. This crate’s
AGENTS.md“Design Constraints” table documents the wrappers as settled after bd:JMAP-6lsm.22. The workspaceAGENTS.md“TLS stack” rule additionally forbids native-tls / openssl; allowingreqwest::Errorto leak would re-couple downstream to thereqwestfeature-set decisions this crate has already made.
The accessor set on each wrapper is deliberately minimal. Resist
requests to “just expose reqwest::Error” without re-arguing all
five reasons above.
Structs§
- Http
Error - HTTP transport error reported by the underlying HTTP client.
- Invalid
Header Value Error - A header value (typically an authentication token) contained bytes that are not valid for an HTTP header.
- Parse
Error - JSON deserialize / parse error reported by the underlying JSON parser.
- Serialize
Error - JSON serialize error reported by the underlying JSON serializer.
- WebSocket
Error - WebSocket transport error reported by the underlying WebSocket client.
Enums§
- Client
Error - Errors produced by the base JMAP client.
- Http
Error Kind - Classification of an
HttpErrorreturned byHttpError::kind. - Parse
Category - Classification of a
ParseError/SerializeErrorfailure. - WebSocket
Error Kind - Classification of a
WebSocketErrorreturned byWebSocketError::kind.