#[non_exhaustive]pub struct ContactCard {Show 35 fields
pub id: Option<Id>,
pub address_book_ids: Option<BTreeMap<Id, bool>>,
pub version: Option<String>,
pub created: Option<UTCDate>,
pub kind: Option<String>,
pub language: Option<String>,
pub members: Option<BTreeMap<String, bool>>,
pub prod_id: Option<String>,
pub related_to: Option<Value>,
pub uid: Option<String>,
pub updated: Option<UTCDate>,
pub name: Option<Value>,
pub nicknames: Option<Value>,
pub organizations: Option<Value>,
pub speak_to_as: Option<Value>,
pub titles: Option<Value>,
pub emails: Option<Value>,
pub online_services: Option<Value>,
pub phones: Option<Value>,
pub preferred_languages: Option<Value>,
pub calendars: Option<Value>,
pub scheduling_addresses: Option<Value>,
pub addresses: Option<Value>,
pub crypto_keys: Option<Value>,
pub directories: Option<Value>,
pub links: Option<Value>,
pub media: Option<Value>,
pub localizations: Option<BTreeMap<String, PatchObject>>,
pub anniversaries: Option<Value>,
pub keywords: Option<BTreeMap<String, bool>>,
pub notes: Option<Value>,
pub personal_info: Option<Value>,
pub metadata: Option<Map<String, Value>>,
pub private_metadata: Option<Map<String, Value>>,
pub extra: Map<String, Value>,
}Expand description
A JMAP ContactCard object (RFC 9610 §3).
Wraps the JSContact Card format (RFC 9553 §2) with two JMAP-specific
additions: id and
address_book_ids.
All fields use Option because:
- RFC 9553 makes most fields optional.
- JMAP
propertiesargument allows partial responses; absent fields must not fail deserialization.
Complex sub-object maps (addresses, phones, emails, etc.) use
serde_json::Value to avoid coupling this crate to every nested
JSContact type.
§Construction
ContactCard::default() returns a card with every field unset,
useful for incremental construction (e.g. building a ContactCard/set create argument by patching the wanted fields). It is not a valid
wire payload on its own: RFC 9610 §3 requires id (server-set) and
addressBookIds on full-object responses, and RFC 9553 §2.1 requires
@type (implicit) plus version and uid on creation. The handler
layer (jmap-contacts-server) is responsible for rejecting partial
cards on /set create; servers building /get responses MUST fill in
id and addressBookIds before serializing. JMAP-glx8.16.
§Size and depth
Consumers of ContactCard SHOULD impose a request-body size limit
at the HTTP layer before deserializing. The 22 Sloppy-Value
fields (see the inline note above name) accept
arbitrarily-large JSON blobs: a 50 MiB JSON body deserializes into
a ~150 MiB serde_json::Value tree.
serde_json’s default 128-level recursion limit protects against
stack overflow from deeply-nested input (see the negative test
deeply_nested_sloppy_field_beyond_serde_json_limit_errors) but
does not bound heap memory or parser CPU. A {"name":{"x":{"x": {...127 levels of single-key nesting...}}}} payload is small on
the wire but produces a deep Value tree; the same wire bytes
re-shaped as a wide-array ({"name":{"a":[{},{},...]}}) consumes
proportional memory.
This crate ships no body-size constant: the limit is consumer policy, not type-crate policy, consistent with the workspace’s transport-less posture (see workspace AGENTS.md “What this workspace builds”). The reference handler convention is a 10 MiB body cap; production consumers MAY raise or lower it. JMAP-glx8.24.
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.id: Option<Id>Server-assigned immutable identifier (JMAP addition).
address_book_ids: Option<BTreeMap<Id, bool>>Set of AddressBook ids this card belongs to (JMAP addition; wire
name addressBookIds).
Represented as BTreeMap<Id, bool> because the JMAP wire format
uses a JSON object with boolean values (RFC 9610 §3). Values are
always true in full-object responses; per RFC 9610 §3, “each
value MUST be true” — the type does not enforce that constraint,
so callers (and jmap-contacts-server) MUST reject false
values with invalidProperties per RFC 8620 §5.3. The map shape
is also used in PatchObject updates (RFC 8620 §5.3) where a
null value removes an entry.
This shape matches the canonical Email.mailbox_ids precedent
in jmap-mail-types. JMAP-glx8.11.
version: Option<String>JSContact version; MUST be "1.0" when present.
created: Option<UTCDate>Date and time when the Card was created (RFC 8620 UTCDate format:
YYYY-MM-DDTHH:MM:SS[.SSS]Z, exactly the Z UTC suffix).
UTCDate is a transparent String newtype in jmap-types; it
performs no parse validation at construction. Callers building a
ContactCard/set argument MUST supply a syntactically valid
UTCDate; the handler layer rejects malformed values with
invalidProperties. JMAP-glx8.18.
kind: Option<String>Kind of entity: "individual", "group", "org", "location",
"device", "application", or a vendor-specific value.
language: Option<String>BCP 47 language tag for the primary language of the Card.
members: Option<BTreeMap<String, bool>>For group cards (kind = "group"): set of member UIDs → true
(RFC 9553 §2.1.6).
Values are always true; per RFC 9553 §2.1.6, “each value MUST
be true” — the type does not enforce that constraint, so callers
(and jmap-contacts-server) MUST reject false values with
invalidProperties per RFC 8620 §5.3. The map shape is also
used in PatchObject updates (RFC 8620 §5.3) where a null value
removes an entry. JMAP-glx8.11.
prod_id: Option<String>Identifier for the product that created the Card.
Cards related to this one. Map of uid → Relation object.
uid: Option<String>Globally unique identifier for this Card (often a UUID URN).
updated: Option<UTCDate>Date and time when the Card was last modified (UTCDate; server-set).
See created for the format and validation
contract. JMAP-glx8.18.
name: Option<Value>The name of the entity. JSContact Name object (complex sub-object).
nicknames: Option<Value>Nicknames map: id → Nickname object.
organizations: Option<Value>Organizations map: id → Organization object.
speak_to_as: Option<Value>How to address the entity in spoken or written language.
titles: Option<Value>Titles map: id → Title object.
emails: Option<Value>Email addresses map: id → EmailAddress object.
online_services: Option<Value>Online services map: id → OnlineService object.
phones: Option<Value>Phone numbers map: id → Phone object.
preferred_languages: Option<Value>Preferred languages map: id → LanguagePref object.
calendars: Option<Value>Calendars map: id → Calendar object.
scheduling_addresses: Option<Value>Scheduling addresses map: id → SchedulingAddress object.
addresses: Option<Value>Postal addresses map: id → Address object.
crypto_keys: Option<Value>Cryptographic keys map: id → CryptoKey object.
directories: Option<Value>Directories map: id → Directory object.
links: Option<Value>Links map: id → Link object.
media: Option<Value>Media map: id → Media object (may include JMAP blobId extension).
localizations: Option<BTreeMap<String, PatchObject>>Localization patches map: BCP 47 language-tag →
PatchObject.
Wire format is byte-identical to a plain JSON object via
#[serde(transparent)] on PatchObject. The typed shape enforces the
outer object-map structure at the type system level; inner patch
values remain opaque JSON (per RFC 8620 §5.3).
anniversaries: Option<Value>Anniversaries map: id → Anniversary object.
keywords: Option<BTreeMap<String, bool>>Keywords set: keyword → true (RFC 9553 §2.8.2 String[Boolean]).
Typed because the wire shape is closed by spec and matches the
sibling members and address_book_ids fields. JMAP-glx8.3.
Values are always true; the type does not enforce that
constraint, so callers MUST reject false values per RFC 9553
§2.8.2. The map shape is also used in PatchObject updates
(RFC 8620 §5.3) where a null value removes an entry.
JMAP-glx8.11.
notes: Option<Value>Notes map: id → Note object.
personal_info: Option<Value>Personal information map: id → PersonalInfo object.
metadata: Option<Map<String, Value>>Shared metadata, keyed by namespace identifier
(draft-ietf-jmap-metadata-04 §2). Each value is a JSON object (§2.1).
None when absent, which -04 §2 treats as {}; in an update a
null value “is equivalent to {}” (-04 §3.2) and also decodes as
None.
private_metadata: Option<Map<String, Value>>The authenticated user’s own metadata, keyed by namespace
identifier (draft-ietf-jmap-metadata-04 §2). Same shape and None
handling as metadata.
extra: Map<String, Value>Catch-all for vendor / site / private extension fields not covered by the typed fields above. Preserves unknown fields across deserialize/serialize round-trip per workspace extras-preservation policy (see workspace AGENTS.md).
§Collision contract
extra MUST NOT contain a key that matches one of the typed
wire-format field names (id, addressBookIds, version,
created, kind, language, members, prodId, relatedTo,
uid, updated, name, nicknames, organizations,
speakToAs, titles, emails, onlineServices, phones,
preferredLanguages, calendars, schedulingAddresses,
addresses, cryptoKeys, directories, links, media,
localizations, anniversaries, keywords, notes,
personalInfo, metadata, privateMetadata). On deserialize, #[serde(flatten)] consumes
matching keys into their typed fields first; truly unknown keys
land in extra. On serialize, a colliding key in extra is
emitted as a duplicate JSON object key alongside the typed
field. RFC 8259 §4 leaves duplicate-key handling
implementation-defined; JMAP servers MAY accept, reject, or
last-wins.
In short: treat extra as a write-only catch-all for unknown
keys discovered at deserialize, and do not programmatically
insert keys that match a typed field. JMAP-glx8.19.
See ContactCard::validate_extras for a runtime pre-serialize
check that detects this hazard. JMAP-glx8.25.
Implementations§
Source§impl ContactCard
impl ContactCard
Sourcepub fn validate_extras(&self) -> Result<(), CollisionError>
pub fn validate_extras(&self) -> Result<(), CollisionError>
Check that no extra key shadows a typed
wire-format field of this struct. Returns
Err(CollisionError) listing any colliding
keys; otherwise returns Ok(()).
Recommended pre-serialize hook for producers who construct
ContactCard values programmatically and need to guarantee
that the resulting JSON does not contain duplicate object keys.
See the extra collision contract for the
underlying hazard. JMAP-glx8.25.
§Errors
Returns CollisionError when one or more keys in
extra match one of the camelCase wire-format
names of this struct’s typed fields.
Trait Implementations§
Source§impl Clone for ContactCard
impl Clone for ContactCard
Source§impl Debug for ContactCard
impl Debug for ContactCard
Source§impl Default for ContactCard
impl Default for ContactCard
Source§impl<'de> Deserialize<'de> for ContactCard
impl<'de> Deserialize<'de> for ContactCard
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
impl GetObject for ContactCard
Source§impl JmapObject for ContactCard
impl JmapObject for ContactCard
Source§impl QueryObject for ContactCard
impl QueryObject for ContactCard
Source§type Comparator = ContactCardComparator
type Comparator = ContactCardComparator
Source§impl Serialize for ContactCard
impl Serialize for ContactCard
Source§impl SetObject for ContactCard
RFC 9610 §3 (rfc9610.txt:305): id is “(immutable; server-set)”.
addressBookIds and the RFC 9553 Card properties carry neither
attribute.
impl SetObject for ContactCard
RFC 9610 §3 (rfc9610.txt:305): id is “(immutable; server-set)”.
addressBookIds and the RFC 9553 Card properties carry neither
attribute.
draft-ietf-jmap-metadata-04 §3.2: clients write metadata and
privateMetadata through Foo/set.
Source§const SERVER_SET: &'static [&'static str]
const SERVER_SET: &'static [&'static str]
/set method text never accepts from a
client, on create or update, is server-set too. Read moreSource§const CLIENT_SETTABLE: &'static [&'static str]
const CLIENT_SETTABLE: &'static [&'static str]
SERVER_SET: the ones a
client may set through /set. Read moreSource§const IMMUTABLE: &'static [&'static str]
const IMMUTABLE: &'static [&'static str]
CLIENT_SETTABLE properties the spec marks “immutable”: “The
value MUST NOT change after the object is created.” (RFC 8620 §1.1).
A property the /set method text accepts on create but not on update
is immutable too. Read more