1use serde::{Deserialize, Serialize};
4
5use crate::{Attachment, EmailAddress, FolderId, GrantId, MessageId, ThreadId};
6
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
36pub struct Message {
37 pub id: MessageId,
39
40 pub grant_id: GrantId,
42
43 #[serde(skip_serializing_if = "Option::is_none")]
45 pub thread_id: Option<ThreadId>,
46
47 #[serde(skip_serializing_if = "Option::is_none")]
49 pub subject: Option<String>,
50
51 #[serde(default)]
53 pub from: Vec<EmailAddress>,
54
55 #[serde(default)]
57 pub to: Vec<EmailAddress>,
58
59 #[serde(default)]
61 pub cc: Vec<EmailAddress>,
62
63 #[serde(default)]
65 pub bcc: Vec<EmailAddress>,
66
67 #[serde(default)]
69 pub reply_to: Vec<EmailAddress>,
70
71 pub date: i64,
73
74 #[serde(skip_serializing_if = "Option::is_none")]
76 pub unread: Option<bool>,
77
78 #[serde(skip_serializing_if = "Option::is_none")]
80 pub starred: Option<bool>,
81
82 #[serde(skip_serializing_if = "Option::is_none")]
84 pub snippet: Option<String>,
85
86 #[serde(skip_serializing_if = "Option::is_none")]
88 pub body: Option<String>,
89
90 #[serde(default)]
92 pub attachments: Vec<Attachment>,
93
94 #[serde(default)]
96 pub folders: Vec<FolderId>,
97
98 #[serde(skip_serializing_if = "Option::is_none")]
100 pub created_at: Option<i64>,
101}
102
103#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
116pub struct MessageQueryParams {
117 #[serde(skip_serializing_if = "Option::is_none")]
119 pub subject: Option<String>,
120
121 #[serde(skip_serializing_if = "Option::is_none")]
123 pub from: Option<String>,
124
125 #[serde(skip_serializing_if = "Option::is_none")]
127 pub to: Option<String>,
128
129 #[serde(skip_serializing_if = "Option::is_none")]
131 pub cc: Option<String>,
132
133 #[serde(skip_serializing_if = "Option::is_none")]
135 pub bcc: Option<String>,
136
137 #[serde(skip_serializing_if = "Option::is_none")]
139 pub any_email: Option<String>,
140
141 #[serde(skip_serializing_if = "Option::is_none")]
143 pub thread_id: Option<String>,
144
145 #[serde(skip_serializing_if = "Option::is_none")]
147 pub unread: Option<bool>,
148
149 #[serde(skip_serializing_if = "Option::is_none")]
151 pub starred: Option<bool>,
152
153 #[serde(skip_serializing_if = "Option::is_none")]
155 pub has_attachment: Option<bool>,
156
157 #[serde(skip_serializing_if = "Option::is_none")]
159 pub received_before: Option<i64>,
160
161 #[serde(skip_serializing_if = "Option::is_none")]
163 pub received_after: Option<i64>,
164
165 #[serde(skip_serializing_if = "Option::is_none")]
167 pub in_: Option<String>,
168
169 #[serde(skip_serializing_if = "Option::is_none")]
171 pub limit: Option<u32>,
172
173 #[serde(skip_serializing_if = "Option::is_none")]
175 pub page_token: Option<String>,
176}
177
178impl MessageQueryParams {
179 pub fn builder() -> MessageQueryParamsBuilder {
181 MessageQueryParamsBuilder::default()
182 }
183}
184
185#[derive(Debug, Clone, Default)]
187pub struct MessageQueryParamsBuilder {
188 params: MessageQueryParams,
189}
190
191impl MessageQueryParamsBuilder {
192 pub fn subject(mut self, subject: impl Into<String>) -> Self {
194 self.params.subject = Some(subject.into());
195 self
196 }
197
198 pub fn from(mut self, from: impl Into<String>) -> Self {
200 self.params.from = Some(from.into());
201 self
202 }
203
204 pub fn to(mut self, to: impl Into<String>) -> Self {
206 self.params.to = Some(to.into());
207 self
208 }
209
210 pub fn cc(mut self, cc: impl Into<String>) -> Self {
212 self.params.cc = Some(cc.into());
213 self
214 }
215
216 pub fn bcc(mut self, bcc: impl Into<String>) -> Self {
218 self.params.bcc = Some(bcc.into());
219 self
220 }
221
222 pub fn any_email(mut self, email: impl Into<String>) -> Self {
224 self.params.any_email = Some(email.into());
225 self
226 }
227
228 pub fn thread_id(mut self, thread_id: impl Into<String>) -> Self {
230 self.params.thread_id = Some(thread_id.into());
231 self
232 }
233
234 pub fn unread(mut self, unread: bool) -> Self {
236 self.params.unread = Some(unread);
237 self
238 }
239
240 pub fn starred(mut self, starred: bool) -> Self {
242 self.params.starred = Some(starred);
243 self
244 }
245
246 pub fn has_attachment(mut self, has_attachment: bool) -> Self {
248 self.params.has_attachment = Some(has_attachment);
249 self
250 }
251
252 pub fn received_before(mut self, timestamp: i64) -> Self {
254 self.params.received_before = Some(timestamp);
255 self
256 }
257
258 pub fn received_after(mut self, timestamp: i64) -> Self {
260 self.params.received_after = Some(timestamp);
261 self
262 }
263
264 pub fn in_folder(mut self, folder_id: impl Into<String>) -> Self {
266 self.params.in_ = Some(folder_id.into());
267 self
268 }
269
270 pub fn limit(mut self, limit: u32) -> Self {
272 self.params.limit = Some(limit);
273 self
274 }
275
276 pub fn page_token(mut self, token: impl Into<String>) -> Self {
278 self.params.page_token = Some(token.into());
279 self
280 }
281
282 pub fn build(self) -> MessageQueryParams {
284 self.params
285 }
286}
287
288#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
300pub struct UpdateMessageRequest {
301 #[serde(skip_serializing_if = "Option::is_none")]
303 pub unread: Option<bool>,
304
305 #[serde(skip_serializing_if = "Option::is_none")]
307 pub starred: Option<bool>,
308
309 #[serde(skip_serializing_if = "Option::is_none")]
311 pub folders: Option<Vec<String>>,
312}
313
314impl UpdateMessageRequest {
315 pub fn builder() -> UpdateMessageRequestBuilder {
317 UpdateMessageRequestBuilder::default()
318 }
319}
320
321#[derive(Debug, Clone, Default)]
323pub struct UpdateMessageRequestBuilder {
324 request: UpdateMessageRequest,
325}
326
327impl UpdateMessageRequestBuilder {
328 pub fn unread(mut self, unread: bool) -> Self {
330 self.request.unread = Some(unread);
331 self
332 }
333
334 pub fn starred(mut self, starred: bool) -> Self {
336 self.request.starred = Some(starred);
337 self
338 }
339
340 pub fn folders(mut self, folders: Vec<String>) -> Self {
342 self.request.folders = Some(folders);
343 self
344 }
345
346 pub fn build(self) -> UpdateMessageRequest {
348 self.request
349 }
350}
351
352#[cfg(test)]
353mod tests {
354 use super::*;
355
356 #[test]
357 fn test_message_creation() {
358 let message = Message {
359 id: MessageId::new("msg_123"),
360 grant_id: GrantId::new("grant_123"),
361 thread_id: Some(ThreadId::new("thread_123")),
362 subject: Some("Test Subject".to_string()),
363 from: vec![],
364 to: vec![],
365 cc: vec![],
366 bcc: vec![],
367 reply_to: vec![],
368 date: 1234567890,
369 unread: Some(true),
370 starred: Some(false),
371 snippet: Some("Test snippet".to_string()),
372 body: Some("Test body".to_string()),
373 attachments: vec![],
374 folders: vec![],
375 created_at: Some(1234567890),
376 };
377
378 assert_eq!(message.id.as_str(), "msg_123");
379 assert_eq!(message.grant_id.as_str(), "grant_123");
380 assert_eq!(message.subject, Some("Test Subject".to_string()));
381 assert_eq!(message.unread, Some(true));
382 }
383
384 #[test]
385 fn test_message_serialization() {
386 let message = Message {
387 id: MessageId::new("msg_123"),
388 grant_id: GrantId::new("grant_123"),
389 thread_id: Some(ThreadId::new("thread_123")),
390 subject: Some("Test".to_string()),
391 from: vec![],
392 to: vec![],
393 cc: vec![],
394 bcc: vec![],
395 reply_to: vec![],
396 date: 1234567890,
397 unread: Some(true),
398 starred: Some(false),
399 snippet: Some("Snippet".to_string()),
400 body: Some("Body".to_string()),
401 attachments: vec![],
402 folders: vec![],
403 created_at: Some(1234567890),
404 };
405
406 let json = serde_json::to_string(&message).unwrap();
407 assert!(json.contains("msg_123"));
408 assert!(json.contains("grant_123"));
409 assert!(json.contains("Test"));
410
411 let deserialized: Message = serde_json::from_str(&json).unwrap();
412 assert_eq!(deserialized, message);
413 }
414
415 #[test]
416 fn test_query_params_builder() {
417 let params = MessageQueryParams::builder()
418 .subject("invoice")
419 .unread(true)
420 .limit(50)
421 .build();
422
423 assert_eq!(params.subject, Some("invoice".to_string()));
424 assert_eq!(params.unread, Some(true));
425 assert_eq!(params.limit, Some(50));
426 }
427
428 #[test]
429 fn test_query_params_all_fields() {
430 let params = MessageQueryParams::builder()
431 .subject("test")
432 .from("sender@example.com")
433 .to("recipient@example.com")
434 .cc("cc@example.com")
435 .bcc("bcc@example.com")
436 .any_email("any@example.com")
437 .thread_id("thread_123")
438 .unread(true)
439 .starred(false)
440 .has_attachment(true)
441 .received_before(2000000000)
442 .received_after(1000000000)
443 .in_folder("folder_123")
444 .limit(100)
445 .page_token("token_abc")
446 .build();
447
448 assert_eq!(params.subject, Some("test".to_string()));
449 assert_eq!(params.from, Some("sender@example.com".to_string()));
450 assert_eq!(params.to, Some("recipient@example.com".to_string()));
451 assert_eq!(params.limit, Some(100));
452 assert_eq!(params.page_token, Some("token_abc".to_string()));
453 }
454
455 #[test]
456 fn test_query_params_serialization() {
457 let params = MessageQueryParams::builder()
458 .subject("test")
459 .unread(true)
460 .limit(50)
461 .build();
462
463 let json = serde_json::to_string(¶ms).unwrap();
464 assert!(json.contains("test"));
465 assert!(json.contains("true"));
466 assert!(json.contains("50"));
467 }
468
469 #[test]
470 fn test_update_message_request_builder() {
471 let update = UpdateMessageRequest::builder()
472 .unread(false)
473 .starred(true)
474 .build();
475
476 assert_eq!(update.unread, Some(false));
477 assert_eq!(update.starred, Some(true));
478 }
479
480 #[test]
481 fn test_update_message_request_with_folders() {
482 let update = UpdateMessageRequest::builder()
483 .unread(false)
484 .folders(vec!["folder1".to_string(), "folder2".to_string()])
485 .build();
486
487 assert_eq!(update.unread, Some(false));
488 assert_eq!(
489 update.folders,
490 Some(vec!["folder1".to_string(), "folder2".to_string()])
491 );
492 }
493
494 #[test]
495 fn test_update_message_request_serialization() {
496 let update = UpdateMessageRequest::builder()
497 .unread(false)
498 .starred(true)
499 .build();
500
501 let json = serde_json::to_string(&update).unwrap();
502 let deserialized: UpdateMessageRequest = serde_json::from_str(&json).unwrap();
503 assert_eq!(deserialized, update);
504 }
505}