Skip to main content

SessionClient

Struct SessionClient 

Source
#[non_exhaustive]
pub struct SessionClient { /* private fields */ }
Expand description

A JmapClient bound to a JMAP session.

Obtain via JmapMetadataExt::with_metadata_session. All JMAP Metadata methods are available on this type without needing to pass &Session on every call.

§Session lifecycle

SessionClient captures the Session at construction time. After re-fetching the session via JmapClient::fetch_session, construct a new SessionClient with the updated session. Reusing a stale SessionClient after session expiry will result in unknownAccount or similar errors from the server.

Clone is derived because JmapClient is itself cheap-to-clone (it already implements Clone and with_metadata_session clones one internally), enabling parallel-task fan-out with one bound session.

Debug is implemented manually to redact the inner JmapClient (which holds an HTTP client and is intentionally not Debug in jmap-base-client); only the Session is shown. This lets callers embed a SessionClient in a #[derive(Debug)] struct without manual impls of their own.

§Thread safety

SessionClient is Send + Sync. Both jmap_base_client::JmapClient (backed by reqwest::Client) and jmap_base_client::Session (plain serde-derived data) are Send + Sync per jmap-base-client’s contract, so this type can be shared across async tasks via Arc<SessionClient> or cloned for per-task ownership.

A Send + Sync regression in a future jmap-base-client release would be a major-version-breaking change for this crate. A compile-time assertion in methods/mod.rs guards against the regression landing silently — see _assert_session_client_send_sync.

Implementations§

Source§

impl SessionClient

Source

pub async fn metadata_get( &self, ids: Option<&[Id]>, properties: Option<&[&str]>, params: Option<MetadataGetParams>, ) -> Result<GetResponse<Metadata>, ClientError>

Fetch Metadata objects by IDs (draft-ietf-jmap-metadata-01 §3.2).

If ids is None, the server returns all Metadata objects for the account, SUBJECT TO the server’s maxObjectsInGet cap (RFC 8620 §5.1). For production use, scope the result set via Self::metadata_query first and pass explicit ids here to avoid requestTooLarge errors when the account holds more objects than the cap.

Pass properties: None to return all fields. Pass params: Some(MetadataGetParams { extra: ... }) to inject vendor / site / private method-level extension args; pass None (or Some(Default::default())) for the standard wire shape.

§Errors
Source

pub async fn metadata_changes( &self, since_state: &State, max_changes: Option<u64>, params: Option<MetadataChangesParams>, ) -> Result<ChangesResponse, ClientError>

Fetch changes to Metadata objects since since_state (draft-ietf-jmap-metadata-01 §3.3).

If has_more_changes is true in the response, call again with new_state as since_state until the flag is false.

params carries the Metadata-specific optional filters filterRelatedType and filterMetadataType. Pass None (or Some(Default::default())) to fetch unfiltered changes. Per §3.3, clients that use these filters MUST re-use the same filter values across subsequent calls to ensure consistent synchronisation, because the returned state token represents the complete account state and is independent of the filter selection.

§Errors
  • ClientError::InvalidArgument if since_state is the empty string (defence-in-depth — State constructed via State::from accepts empty strings, but an empty sinceState is never useful and would otherwise generate a wasted round-trip producing a server-side cannotCalculateChanges).
  • ClientError::InvalidSession if the bound session has no primary account for urn:ietf:params:jmap:metadata.
  • Any transport / protocol variant returned by JmapClient::call — see the matching error list on Self::metadata_get.
Source

pub async fn metadata_set( &self, create: Option<Value>, update: Option<HashMap<Id, PatchObject>>, destroy: Option<Vec<Id>>, if_in_state: Option<&State>, params: Option<MetadataSetParams>, ) -> Result<SetResponse<Metadata>, ClientError>

Create, update, or destroy Metadata objects (draft-ietf-jmap-metadata-01 §3.1).

Pass create, update, and/or destroy as needed. All three are optional; pass None to omit any operation from the request.

create is Option<serde_json::Value> and MUST be a JSON object whose keys are caller-chosen creation ids (RFC 8620 §5.3 — the creation id strings may be referenced via the #-prefixed result- reference syntax in RFC 8620 §3.7) and whose values are partial Metadata objects per draft-ietf-jmap-metadata-01 §3.1 (one of the three @type variants: Annotation per §2.1.1, ImapMetadata per §2.1.2, WebDavMetadata per §2.1.3, each with at minimum relatedType, relatedId, and an optional isPrivate). The server assigns the final id and returns it under created in the response. Wire-shape mismatches (e.g. passing an array or a primitive) are rejected by the server as invalidArguments after a network round-trip.

update is Option<HashMap<Id, PatchObject>> (RFC 8620 §5.3). Wire format is unchanged from a plain JSON object because PatchObject is #[serde(transparent)]; the typed parameter binds the JSON Pointer key + null-leaf removal contract to the type system.

The asymmetry between typed update and untyped create is deliberate: PatchObject is a stable workspace-defined type with one JSON-Pointer-keyed shape, whereas a Metadata create object is one of three @type variants and the spec accepts the full JSON wire shape (including vendor-defined extras at every level). Forcing create through a single Rust type would fight the spec’s extensibility model. Mirrors the canonical mail-client email_set signature.

Pass if_in_state: Some(&state) to use an optimistic-concurrency guard (RFC 8620 §5.3): the server returns stateMismatch if the current state does not match. Required for safe read-modify-write given the (relatedType, relatedId, @type, isPrivate) uniqueness invariant.

Pass params: Some(MetadataSetParams { extra: ... }) to inject vendor / site / private method-level extension args; pass None (or Some(Default::default())) for the standard wire shape.

The server enforces uniqueness on the tuple (relatedType, relatedId, @type, isPrivate) and may return alreadyExists, forbidden (if maySetPrivate: false and isPrivate: true is requested), overQuota, or invalidProperties SetErrors per §3.1.

§Errors
  • ClientError::InvalidArgument if all three of create, update, and destroy are None (an all-None /set is a no-op round-trip; if_in_state and params alone are not sufficient to make the request useful, so the client rejects it before sending).
  • ClientError::InvalidArgument if serde_json::to_value fails when serializing the update map. In practice this can occur only under pathological conditions (allocation failure on a very large HashMap; a PatchObject whose internal JSON tree exceeds serde_json’s recursion limit). The size of update is otherwise bounded only by available memory; the wire request is buffered by the HTTP client (reqwest’s RequestBuilder::json serializes the body upfront via serde_json::to_vec), so the transient peak holds the source HashMap, the intermediate serde_json::Value tree, and the serialized Vec<u8> body simultaneously — roughly 3-4× the HashMap’s in-memory size. Callers dealing with thousands of patches per call may prefer to batch across multiple metadata_set invocations to bound that transient peak.
  • ClientError::InvalidSession if the bound session has no primary account for urn:ietf:params:jmap:metadata.
  • Any transport / protocol variant returned by JmapClient::call — see the matching error list on Self::metadata_get. Per-creation /set failures (e.g. alreadyExists, forbidden, overQuota, invalidProperties) appear in SetResponse::not_created / SetResponse::not_updated / SetResponse::not_destroyed as SetError values, not as Err. A stateMismatch on if_in_state surfaces as MethodError with error_type == "stateMismatch".
Source

pub async fn metadata_query( &self, filter: Option<Value>, sort: Option<Value>, position: Option<u64>, limit: Option<u64>, params: Option<MetadataQueryParams>, ) -> Result<QueryResponse, ClientError>

Query Metadata IDs with optional filter and sort (draft-ietf-jmap-metadata-01 §3.4).

Pass filter: None and sort: None to return all Metadata IDs with server-default ordering. Use position and limit for pagination.

The filter value is typed by the caller; the canonical type is jmap_metadata_types::MetadataFilterCondition (§3.4.1) wrapped in jmap_types::query::Filter. This builder accepts a serde_json::Value so callers can mix filter conditions with FilterOperator algebra without forcing a single type through the API.

Construct a filter via serde_json::to_value on the typed shape, or inline as a JSON literal:

ⓘ
// Match Annotations on a specific Email.
let filter = serde_json::json!({
    "relatedType": "Email",
    "relatedId":   "EM456",
    "@type":       "Annotation"
});
// Match Annotations on either of two Emails (FilterOperator algebra).
let filter = serde_json::json!({
    "operator": "OR",
    "conditions": [
        { "relatedType": "Email", "relatedId": "EM456" },
        { "relatedType": "Email", "relatedId": "EM789" }
    ]
});

Sort comparators MUST set property to one of id, @type, relatedType, relatedId, or isPrivate per §3.4.2; id MUST be supported by every server, the others SHOULD be supported.

Construct sort as an array of comparator objects:

ⓘ
// Ascending by @type, then by id.
let sort = serde_json::json!([
    { "property": "@type", "isAscending": true },
    { "property": "id",    "isAscending": true }
]);

Pass params: Some(MetadataQueryParams { extra: ... }) to inject vendor / site / private method-level extension args; pass None (or Some(Default::default())) for the standard wire shape.

§Errors
  • ClientError::InvalidSession if the bound session has no primary account for urn:ietf:params:jmap:metadata.
  • Any transport / protocol variant returned by JmapClient::call — see the matching error list on Self::metadata_get. RFC 8620 §5.5 defines additional method-level errors specific to /query (anchorNotFound, unsupportedFilter, unsupportedSort, tooManyChanges); they surface here as MethodError with the corresponding error_type string.
Source

pub async fn metadata_query_changes( &self, since_query_state: &State, max_changes: Option<u64>, filter: Option<Value>, sort: Option<Value>, up_to_id: Option<&Id>, calculate_total: Option<bool>, params: Option<MetadataQueryChangesParams>, ) -> Result<QueryChangesResponse, ClientError>

Fetch query-result changes for Metadata since since_query_state (draft-ietf-jmap-metadata-01 §3.5).

Returns which Metadata IDs were removed from or added to the query result set since the given state. max_changes may be None.

filter and sort MUST match the filter / sort passed to the original Metadata/query call that returned since_query_state — RFC 8620 §5.6 is explicit that the server uses them to compute which entries entered or left the result set.

up_to_id is the highest-index id the client has cached; calculate_total requests the new total result count.

Pass params: Some(MetadataQueryChangesParams { extra: ... }) to inject vendor / site / private method-level extension args; pass None (or Some(Default::default())) for the standard wire shape.

§Errors
Source§

impl SessionClient

Source

pub fn client(&self) -> &JmapClient

Borrow the underlying JmapClient.

Useful for ad-hoc operations outside the typed JMAP method surface — for example, calling JmapClient::upload / JmapClient::download_blob, or constructing a JmapClient::event_source subscription using the bound session’s event_source_url.

Source

pub fn session(&self) -> &Session

Borrow the captured Session.

SessionClient captures the Session at construction time. After re-fetching the session via JmapClient::fetch_session, callers should construct a new SessionClient. This accessor lets a caller compare the captured session’s state field against a freshly fetched session to detect staleness, or inspect accountCapabilities / primary_accounts for capability-specific metadata not exposed via the typed JMAP method surface.

Source

pub fn metadata_account_id(&self) -> Result<&str, ClientError>

Return the primary account id for urn:ietf:params:jmap:metadata, or Err(InvalidSession) if the session has no primary account for that capability.

Trait Implementations§

Source§

impl Clone for SessionClient

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SessionClient

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more