Skip to main content

ContactCard

Struct ContactCard 

Source
#[non_exhaustive]
pub struct ContactCard {
Show 33 fields pub id: Option<Id>, pub address_book_ids: Option<HashMap<Id, bool>>, pub version: Option<String>, pub created: Option<UTCDate>, pub kind: Option<String>, pub language: Option<String>, pub members: Option<HashMap<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<HashMap<String, PatchObject>>, pub anniversaries: Option<Value>, pub keywords: Option<HashMap<String, bool>>, pub notes: Option<Value>, pub personal_info: Option<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 properties argument 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
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional 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<HashMap<Id, bool>>

Set of AddressBook ids this card belongs to (JMAP addition; wire name addressBookIds).

Represented as HashMap<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<HashMap<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.

§related_to: Option<Value>

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<HashMap<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<HashMap<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.

§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). 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

Source

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

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 ContactCard

Source§

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

Formats the value using the given formatter. Read more
Source§

impl Default for ContactCard

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl<'de> Deserialize<'de> for ContactCard

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl GetObject for ContactCard

Source§

impl JmapObject for ContactCard

Source§

const TYPE_NAME: &'static str = "ContactCard"

The JMAP type name string (e.g. "Email", "Mailbox", "Chat").
Source§

type Property = ContactCardProperty

The property selector enum for this type (server-side only, no serde).
Source§

impl QueryObject for ContactCard

Source§

type Filter = ContactCardFilterCondition

The filter condition type. Read more
Source§

type Comparator = ContactCardComparator

The comparator type for sort operations.
Source§

impl Serialize for ContactCard

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl SetObject for ContactCard

Source§

type Patch = PatchObject

The patch type for update operations. 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> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

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> 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.