io_jmap/rfc9610/contact_card/
query.rs1use core::fmt;
59
60use alloc::{string::String, vec, vec::Vec};
61
62use secrecy::SecretString;
63use serde::{Deserialize, Serialize, Serializer};
64use thiserror::Error;
65
66use crate::{
67 coroutine::*,
68 jmap_try,
69 rfc8620::{
70 JMAP_CORE_CAPABILITY, error::JmapMethodError, request::JmapBatch,
71 request::JmapResultReference, send::*, session::JmapSession,
72 },
73 rfc9610::{JMAP_CONTACTS_CAPABILITY, contact_card::JmapContactCard},
74};
75
76#[derive(Clone, Debug, Default, Serialize)]
79#[serde(rename_all = "camelCase")]
80pub struct JmapContactCardFilter {
81 #[serde(skip_serializing_if = "Option::is_none")]
83 pub in_address_book: Option<String>,
84 #[serde(skip_serializing_if = "Option::is_none")]
86 pub uid: Option<String>,
87 #[serde(skip_serializing_if = "Option::is_none")]
89 pub has_member: Option<String>,
90 #[serde(skip_serializing_if = "Option::is_none")]
92 pub kind: Option<String>,
93 #[serde(skip_serializing_if = "Option::is_none")]
95 pub created_before: Option<String>,
96 #[serde(skip_serializing_if = "Option::is_none")]
99 pub created_after: Option<String>,
100 #[serde(skip_serializing_if = "Option::is_none")]
102 pub updated_before: Option<String>,
103 #[serde(skip_serializing_if = "Option::is_none")]
106 pub updated_after: Option<String>,
107 #[serde(skip_serializing_if = "Option::is_none")]
109 pub text: Option<String>,
110 #[serde(skip_serializing_if = "Option::is_none")]
112 pub name: Option<String>,
113 #[serde(rename = "name/given", skip_serializing_if = "Option::is_none")]
115 pub name_given: Option<String>,
116 #[serde(rename = "name/surname", skip_serializing_if = "Option::is_none")]
118 pub name_surname: Option<String>,
119 #[serde(rename = "name/surname2", skip_serializing_if = "Option::is_none")]
121 pub name_surname2: Option<String>,
122 #[serde(skip_serializing_if = "Option::is_none")]
124 pub nickname: Option<String>,
125 #[serde(skip_serializing_if = "Option::is_none")]
127 pub organization: Option<String>,
128 #[serde(skip_serializing_if = "Option::is_none")]
130 pub email: Option<String>,
131 #[serde(skip_serializing_if = "Option::is_none")]
133 pub phone: Option<String>,
134 #[serde(skip_serializing_if = "Option::is_none")]
136 pub online_service: Option<String>,
137 #[serde(skip_serializing_if = "Option::is_none")]
139 pub address: Option<String>,
140 #[serde(skip_serializing_if = "Option::is_none")]
142 pub note: Option<String>,
143}
144
145#[derive(Clone, Debug, PartialEq, Eq)]
147pub enum JmapContactCardSortProperty {
148 Created,
150 Updated,
152 NameGiven,
154 NameSurname,
156 NameSurname2,
158}
159
160impl fmt::Display for JmapContactCardSortProperty {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 f.write_str(match self {
163 Self::Created => "created",
164 Self::Updated => "updated",
165 Self::NameGiven => "name/given",
166 Self::NameSurname => "name/surname",
167 Self::NameSurname2 => "name/surname2",
168 })
169 }
170}
171
172impl Serialize for JmapContactCardSortProperty {
173 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
174 s.collect_str(self)
175 }
176}
177
178#[derive(Clone, Debug, Serialize)]
180#[serde(rename_all = "camelCase")]
181pub struct JmapContactCardSortComparator {
182 pub property: JmapContactCardSortProperty,
184 #[serde(skip_serializing_if = "Option::is_none")]
186 pub is_ascending: Option<bool>,
187}
188
189#[derive(Debug, Error)]
192pub enum JmapContactCardQueryError {
193 #[error(
195 "JMAP ContactCard/query failed: missing ContactCard/query response in method_responses"
196 )]
197 MissingQueryResponse,
198 #[error("JMAP ContactCard/query failed: missing ContactCard/get response in method_responses")]
200 MissingGetResponse,
201 #[error("JMAP ContactCard/query failed: {0}")]
203 Send(#[from] JmapSendError),
204 #[error("JMAP ContactCard/query failed: serialize args: {0}")]
206 SerializeArgs(#[source] serde_json::Error),
207 #[error("JMAP ContactCard/query failed: parse ContactCard/query response: {0}")]
209 ParseQueryResponse(#[source] serde_json::Error),
210 #[error("JMAP ContactCard/query failed: parse ContactCard/get response: {0}")]
212 ParseGetResponse(#[source] serde_json::Error),
213 #[error("JMAP ContactCard/query failed: ContactCard/query: {0}")]
215 QueryMethod(JmapMethodError),
216 #[error("JMAP ContactCard/query failed: ContactCard/get: {0}")]
218 GetMethod(JmapMethodError),
219}
220
221#[derive(Clone, Debug, Default)]
223pub struct JmapContactCardQueryOptions {
224 pub filter: Option<JmapContactCardFilter>,
226 pub sort: Option<Vec<JmapContactCardSortComparator>>,
228 pub position: Option<u64>,
230 pub limit: Option<u64>,
232 pub properties: Option<Vec<String>>,
235}
236
237#[derive(Clone, Debug)]
239pub struct JmapContactCardQueryOutput {
240 pub cards: Vec<JmapContactCard>,
242 pub total: Option<u64>,
244 pub position: u64,
246 pub query_state: String,
248 pub keep_alive: bool,
250}
251
252pub struct JmapContactCardQuery {
255 state: State,
256}
257
258impl JmapContactCardQuery {
259 pub fn new(
261 session: &JmapSession,
262 http_auth: &SecretString,
263 opts: JmapContactCardQueryOptions,
264 ) -> Result<Self, JmapContactCardQueryError> {
265 let account_id = session
266 .primary_accounts
267 .get(JMAP_CONTACTS_CAPABILITY)
268 .cloned()
269 .unwrap_or_default();
270 let api_url = &session.api_url;
271
272 let query_args = ContactCardQueryArgs {
273 account_id: &account_id,
274 filter: opts.filter.as_ref(),
275 sort: opts.sort.as_deref(),
276 position: opts.position,
277 limit: opts.limit,
278 calculate_total: true,
279 };
280
281 let mut batch = JmapBatch::new();
282 let query_id = batch.add(
283 "ContactCard/query",
284 serde_json::to_value(&query_args).map_err(JmapContactCardQueryError::SerializeArgs)?,
285 );
286
287 let get_args = ContactCardGetByRefArgs {
288 account_id: &account_id,
289 ids_ref: JmapResultReference {
290 result_of: &query_id,
291 name: "ContactCard/query",
292 path: "/ids",
293 },
294 properties: opts.properties.as_deref(),
295 };
296
297 batch.add(
298 "ContactCard/get",
299 serde_json::to_value(&get_args).map_err(JmapContactCardQueryError::SerializeArgs)?,
300 );
301
302 let request = batch.into_request(vec![
303 JMAP_CORE_CAPABILITY.into(),
304 JMAP_CONTACTS_CAPABILITY.into(),
305 ]);
306
307 Ok(Self {
308 state: State::Send(JmapSend::new(http_auth, api_url, request)?),
309 })
310 }
311}
312
313impl JmapCoroutine for JmapContactCardQuery {
314 type Yield = JmapYield;
315 type Return = Result<JmapContactCardQueryOutput, JmapContactCardQueryError>;
316
317 fn resume(&mut self, arg: Option<&[u8]>) -> JmapCoroutineState<Self::Yield, Self::Return> {
318 match &mut self.state {
319 State::Send(send) => {
320 let JmapSendOutput {
321 response,
322 keep_alive,
323 } = jmap_try!(send, arg);
324
325 let mut responses = response.method_responses.into_iter();
326
327 let Some((query_name, query_args, _)) = responses.next() else {
328 return JmapCoroutineState::Complete(Err(
329 JmapContactCardQueryError::MissingQueryResponse,
330 ));
331 };
332
333 if query_name == "error" {
334 let err = serde_json::from_value::<JmapMethodError>(query_args)
335 .unwrap_or(JmapMethodError::Unknown);
336 return JmapCoroutineState::Complete(Err(
337 JmapContactCardQueryError::QueryMethod(err),
338 ));
339 }
340
341 let query_response =
342 match serde_json::from_value::<ContactCardQueryResponse>(query_args) {
343 Ok(r) => r,
344 Err(err) => {
345 return JmapCoroutineState::Complete(Err(
346 JmapContactCardQueryError::ParseQueryResponse(err),
347 ));
348 }
349 };
350
351 let Some((get_name, get_args, _)) = responses.next() else {
352 return JmapCoroutineState::Complete(Err(
353 JmapContactCardQueryError::MissingGetResponse,
354 ));
355 };
356
357 if get_name == "error" {
358 let err = serde_json::from_value::<JmapMethodError>(get_args)
359 .unwrap_or(JmapMethodError::Unknown);
360 return JmapCoroutineState::Complete(Err(
361 JmapContactCardQueryError::GetMethod(err),
362 ));
363 }
364
365 match serde_json::from_value::<ContactCardGetResponse>(get_args) {
366 Ok(r) => JmapCoroutineState::Complete(Ok(JmapContactCardQueryOutput {
367 cards: r.list,
368 total: query_response.total,
369 position: query_response.position,
370 query_state: query_response.query_state,
371 keep_alive,
372 })),
373 Err(err) => JmapCoroutineState::Complete(Err(
374 JmapContactCardQueryError::ParseGetResponse(err),
375 )),
376 }
377 }
378 }
379 }
380}
381
382enum State {
383 Send(JmapSend),
384}
385
386#[derive(Serialize)]
387#[serde(rename_all = "camelCase")]
388struct ContactCardQueryArgs<'a> {
389 account_id: &'a str,
390 #[serde(skip_serializing_if = "Option::is_none")]
391 filter: Option<&'a JmapContactCardFilter>,
392 #[serde(skip_serializing_if = "Option::is_none")]
393 sort: Option<&'a [JmapContactCardSortComparator]>,
394 #[serde(skip_serializing_if = "Option::is_none")]
395 position: Option<u64>,
396 #[serde(skip_serializing_if = "Option::is_none")]
397 limit: Option<u64>,
398 calculate_total: bool,
399}
400
401#[derive(Serialize)]
402#[serde(rename_all = "camelCase")]
403struct ContactCardGetByRefArgs<'a> {
404 account_id: &'a str,
405 #[serde(rename = "#ids")]
406 ids_ref: JmapResultReference<'a>,
407 #[serde(skip_serializing_if = "Option::is_none")]
408 properties: Option<&'a [String]>,
409}
410
411#[derive(Deserialize)]
412#[serde(rename_all = "camelCase")]
413struct ContactCardQueryResponse {
414 query_state: String,
415 #[serde(default)]
416 total: Option<u64>,
417 #[serde(default)]
418 position: u64,
419}
420
421#[derive(Deserialize)]
422#[serde(rename_all = "camelCase")]
423struct ContactCardGetResponse {
424 list: Vec<JmapContactCard>,
425}