1use serde::{Deserialize, Serialize};
4
5use crate::{Attachment, EmailAddress, FolderId, GrantId, MessageId, ThreadId, TrackingOptions};
6use std::fmt;
7
8#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
10#[serde(transparent)]
11pub struct ScheduleId(String);
12
13impl ScheduleId {
14 pub fn new(id: impl Into<String>) -> Self {
16 Self(id.into())
17 }
18
19 pub fn as_str(&self) -> &str {
21 &self.0
22 }
23}
24
25impl fmt::Display for ScheduleId {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 write!(f, "{}", self.0)
28 }
29}
30
31impl From<String> for ScheduleId {
32 fn from(s: String) -> Self {
33 Self(s)
34 }
35}
36
37impl From<&str> for ScheduleId {
38 fn from(s: &str) -> Self {
39 Self(s.to_string())
40 }
41}
42
43#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
45pub struct EmailHeader {
46 pub name: String,
48
49 pub value: String,
51}
52
53#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
87pub struct Message {
88 pub id: MessageId,
90
91 pub grant_id: GrantId,
93
94 #[serde(skip_serializing_if = "Option::is_none")]
96 pub thread_id: Option<ThreadId>,
97
98 #[serde(skip_serializing_if = "Option::is_none")]
100 pub subject: Option<String>,
101
102 #[serde(default)]
104 pub from: Vec<EmailAddress>,
105
106 #[serde(default)]
108 pub to: Vec<EmailAddress>,
109
110 #[serde(default)]
112 pub cc: Vec<EmailAddress>,
113
114 #[serde(default)]
116 pub bcc: Vec<EmailAddress>,
117
118 #[serde(default)]
120 pub reply_to: Vec<EmailAddress>,
121
122 pub date: i64,
124
125 #[serde(skip_serializing_if = "Option::is_none")]
127 pub unread: Option<bool>,
128
129 #[serde(skip_serializing_if = "Option::is_none")]
131 pub starred: Option<bool>,
132
133 #[serde(skip_serializing_if = "Option::is_none")]
135 pub snippet: Option<String>,
136
137 #[serde(skip_serializing_if = "Option::is_none")]
139 pub body: Option<String>,
140
141 #[serde(default)]
143 pub attachments: Vec<Attachment>,
144
145 #[serde(default)]
147 pub folders: Vec<FolderId>,
148
149 #[serde(skip_serializing_if = "Option::is_none")]
151 pub created_at: Option<i64>,
152
153 #[serde(skip_serializing_if = "Option::is_none")]
155 pub headers: Option<Vec<EmailHeader>>,
156
157 #[serde(skip_serializing_if = "Option::is_none")]
159 pub raw_mime: Option<String>,
160
161 #[serde(skip_serializing_if = "Option::is_none")]
163 pub bounce_detected: Option<bool>,
164
165 #[serde(skip_serializing_if = "Option::is_none")]
167 pub schedule_id: Option<ScheduleId>,
168
169 #[serde(skip_serializing_if = "Option::is_none")]
171 pub send_at: Option<i64>,
172}
173
174#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
187pub struct MessageQueryParams {
188 #[serde(skip_serializing_if = "Option::is_none")]
190 pub subject: Option<String>,
191
192 #[serde(skip_serializing_if = "Option::is_none")]
194 pub from: Option<String>,
195
196 #[serde(skip_serializing_if = "Option::is_none")]
198 pub to: Option<String>,
199
200 #[serde(skip_serializing_if = "Option::is_none")]
202 pub cc: Option<String>,
203
204 #[serde(skip_serializing_if = "Option::is_none")]
206 pub bcc: Option<String>,
207
208 #[serde(skip_serializing_if = "Option::is_none")]
210 pub any_email: Option<String>,
211
212 #[serde(skip_serializing_if = "Option::is_none")]
214 pub thread_id: Option<String>,
215
216 #[serde(skip_serializing_if = "Option::is_none")]
218 pub unread: Option<bool>,
219
220 #[serde(skip_serializing_if = "Option::is_none")]
222 pub starred: Option<bool>,
223
224 #[serde(skip_serializing_if = "Option::is_none")]
226 pub has_attachment: Option<bool>,
227
228 #[serde(skip_serializing_if = "Option::is_none")]
230 pub received_before: Option<i64>,
231
232 #[serde(skip_serializing_if = "Option::is_none")]
234 pub received_after: Option<i64>,
235
236 #[serde(skip_serializing_if = "Option::is_none")]
238 pub in_: Option<String>,
239
240 #[serde(skip_serializing_if = "Option::is_none")]
242 pub limit: Option<u32>,
243
244 #[serde(skip_serializing_if = "Option::is_none")]
246 pub page_token: Option<String>,
247
248 #[serde(skip_serializing_if = "Option::is_none")]
262 pub query_imap: Option<String>,
263}
264
265impl MessageQueryParams {
266 pub fn builder() -> MessageQueryParamsBuilder {
268 MessageQueryParamsBuilder::default()
269 }
270}
271
272#[derive(Debug, Clone, Default)]
274pub struct MessageQueryParamsBuilder {
275 params: MessageQueryParams,
276}
277
278impl MessageQueryParamsBuilder {
279 pub fn subject(mut self, subject: impl Into<String>) -> Self {
281 self.params.subject = Some(subject.into());
282 self
283 }
284
285 pub fn from(mut self, from: impl Into<String>) -> Self {
287 self.params.from = Some(from.into());
288 self
289 }
290
291 pub fn to(mut self, to: impl Into<String>) -> Self {
293 self.params.to = Some(to.into());
294 self
295 }
296
297 pub fn cc(mut self, cc: impl Into<String>) -> Self {
299 self.params.cc = Some(cc.into());
300 self
301 }
302
303 pub fn bcc(mut self, bcc: impl Into<String>) -> Self {
305 self.params.bcc = Some(bcc.into());
306 self
307 }
308
309 pub fn any_email(mut self, email: impl Into<String>) -> Self {
311 self.params.any_email = Some(email.into());
312 self
313 }
314
315 pub fn thread_id(mut self, thread_id: impl Into<String>) -> Self {
317 self.params.thread_id = Some(thread_id.into());
318 self
319 }
320
321 pub fn unread(mut self, unread: bool) -> Self {
323 self.params.unread = Some(unread);
324 self
325 }
326
327 pub fn starred(mut self, starred: bool) -> Self {
329 self.params.starred = Some(starred);
330 self
331 }
332
333 pub fn has_attachment(mut self, has_attachment: bool) -> Self {
335 self.params.has_attachment = Some(has_attachment);
336 self
337 }
338
339 pub fn received_before(mut self, timestamp: i64) -> Self {
341 self.params.received_before = Some(timestamp);
342 self
343 }
344
345 pub fn received_after(mut self, timestamp: i64) -> Self {
347 self.params.received_after = Some(timestamp);
348 self
349 }
350
351 pub fn in_folder(mut self, folder_id: impl Into<String>) -> Self {
353 self.params.in_ = Some(folder_id.into());
354 self
355 }
356
357 pub fn limit(mut self, limit: u32) -> Self {
359 self.params.limit = Some(limit);
360 self
361 }
362
363 pub fn page_token(mut self, token: impl Into<String>) -> Self {
365 self.params.page_token = Some(token.into());
366 self
367 }
368
369 pub fn query_imap(mut self, query: impl Into<String>) -> Self {
388 self.params.query_imap = Some(query.into());
389 self
390 }
391
392 pub fn build(self) -> MessageQueryParams {
394 self.params
395 }
396}
397
398#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
410pub struct UpdateMessageRequest {
411 #[serde(skip_serializing_if = "Option::is_none")]
413 pub unread: Option<bool>,
414
415 #[serde(skip_serializing_if = "Option::is_none")]
417 pub starred: Option<bool>,
418
419 #[serde(skip_serializing_if = "Option::is_none")]
421 pub folders: Option<Vec<String>>,
422}
423
424impl UpdateMessageRequest {
425 pub fn builder() -> UpdateMessageRequestBuilder {
427 UpdateMessageRequestBuilder::default()
428 }
429}
430
431#[derive(Debug, Clone, Default)]
433pub struct UpdateMessageRequestBuilder {
434 request: UpdateMessageRequest,
435}
436
437impl UpdateMessageRequestBuilder {
438 pub fn unread(mut self, unread: bool) -> Self {
440 self.request.unread = Some(unread);
441 self
442 }
443
444 pub fn starred(mut self, starred: bool) -> Self {
446 self.request.starred = Some(starred);
447 self
448 }
449
450 pub fn folders(mut self, folders: Vec<String>) -> Self {
452 self.request.folders = Some(folders);
453 self
454 }
455
456 pub fn build(self) -> UpdateMessageRequest {
458 self.request
459 }
460}
461
462#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
475pub struct SendMessageRequest {
476 #[serde(skip_serializing_if = "Option::is_none")]
478 pub subject: Option<String>,
479
480 #[serde(skip_serializing_if = "Option::is_none")]
482 pub body: Option<String>,
483
484 #[serde(default)]
486 pub to: Vec<String>,
487
488 #[serde(default, skip_serializing_if = "Vec::is_empty")]
490 pub cc: Vec<String>,
491
492 #[serde(default, skip_serializing_if = "Vec::is_empty")]
494 pub bcc: Vec<String>,
495
496 #[serde(default, skip_serializing_if = "Vec::is_empty")]
498 pub reply_to: Vec<String>,
499
500 #[serde(default, skip_serializing_if = "Vec::is_empty")]
502 pub attachments: Vec<String>,
503
504 #[serde(skip_serializing_if = "Option::is_none")]
506 pub send_at: Option<i64>,
507
508 #[serde(skip_serializing_if = "Option::is_none")]
510 pub tracking_options: Option<TrackingOptions>,
511
512 #[serde(skip_serializing_if = "Option::is_none")]
514 pub reply_to_message_id: Option<String>,
515}
516
517impl SendMessageRequest {
518 pub fn builder() -> SendMessageRequestBuilder {
520 SendMessageRequestBuilder::default()
521 }
522}
523
524#[derive(Debug, Clone, Default)]
526pub struct SendMessageRequestBuilder {
527 request: SendMessageRequest,
528}
529
530impl SendMessageRequestBuilder {
531 pub fn subject(mut self, subject: impl Into<String>) -> Self {
533 self.request.subject = Some(subject.into());
534 self
535 }
536
537 pub fn body(mut self, body: impl Into<String>) -> Self {
539 self.request.body = Some(body.into());
540 self
541 }
542
543 pub fn to(mut self, to: Vec<String>) -> Self {
545 self.request.to = to;
546 self
547 }
548
549 pub fn add_to(mut self, to: impl Into<String>) -> Self {
551 self.request.to.push(to.into());
552 self
553 }
554
555 pub fn cc(mut self, cc: Vec<String>) -> Self {
557 self.request.cc = cc;
558 self
559 }
560
561 pub fn add_cc(mut self, cc: impl Into<String>) -> Self {
563 self.request.cc.push(cc.into());
564 self
565 }
566
567 pub fn bcc(mut self, bcc: Vec<String>) -> Self {
569 self.request.bcc = bcc;
570 self
571 }
572
573 pub fn add_bcc(mut self, bcc: impl Into<String>) -> Self {
575 self.request.bcc.push(bcc.into());
576 self
577 }
578
579 pub fn reply_to(mut self, reply_to: Vec<String>) -> Self {
581 self.request.reply_to = reply_to;
582 self
583 }
584
585 pub fn attachments(mut self, attachments: Vec<String>) -> Self {
587 self.request.attachments = attachments;
588 self
589 }
590
591 pub fn add_attachment(mut self, attachment_id: impl Into<String>) -> Self {
593 self.request.attachments.push(attachment_id.into());
594 self
595 }
596
597 pub fn send_at(mut self, timestamp: i64) -> Self {
599 self.request.send_at = Some(timestamp);
600 self
601 }
602
603 pub fn tracking(mut self, tracking: TrackingOptions) -> Self {
605 self.request.tracking_options = Some(tracking);
606 self
607 }
608
609 pub fn reply_to_message_id(mut self, message_id: impl Into<String>) -> Self {
611 self.request.reply_to_message_id = Some(message_id.into());
612 self
613 }
614
615 pub fn build(self) -> SendMessageRequest {
617 self.request
618 }
619}
620
621#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
635pub struct CleanMessagesRequest {
636 pub message_ids: Vec<String>,
638
639 #[serde(skip_serializing_if = "Option::is_none")]
641 pub ignore_quotes: Option<bool>,
642
643 #[serde(skip_serializing_if = "Option::is_none")]
645 pub ignore_links: Option<bool>,
646
647 #[serde(skip_serializing_if = "Option::is_none")]
649 pub ignore_images: Option<bool>,
650
651 #[serde(skip_serializing_if = "Option::is_none")]
653 pub ignore_tables: Option<bool>,
654}
655
656impl CleanMessagesRequest {
657 pub fn builder() -> CleanMessagesRequestBuilder {
659 CleanMessagesRequestBuilder::default()
660 }
661}
662
663#[derive(Debug, Clone, Default)]
665pub struct CleanMessagesRequestBuilder {
666 message_ids: Vec<String>,
667 ignore_quotes: Option<bool>,
668 ignore_links: Option<bool>,
669 ignore_images: Option<bool>,
670 ignore_tables: Option<bool>,
671}
672
673impl CleanMessagesRequestBuilder {
674 pub fn message_ids(mut self, message_ids: Vec<String>) -> Self {
676 self.message_ids = message_ids;
677 self
678 }
679
680 pub fn add_message_id(mut self, message_id: impl Into<String>) -> Self {
682 self.message_ids.push(message_id.into());
683 self
684 }
685
686 pub fn ignore_quotes(mut self, ignore: bool) -> Self {
688 self.ignore_quotes = Some(ignore);
689 self
690 }
691
692 pub fn ignore_links(mut self, ignore: bool) -> Self {
694 self.ignore_links = Some(ignore);
695 self
696 }
697
698 pub fn ignore_images(mut self, ignore: bool) -> Self {
700 self.ignore_images = Some(ignore);
701 self
702 }
703
704 pub fn ignore_tables(mut self, ignore: bool) -> Self {
706 self.ignore_tables = Some(ignore);
707 self
708 }
709
710 pub fn build(self) -> CleanMessagesRequest {
712 CleanMessagesRequest {
713 message_ids: self.message_ids,
714 ignore_quotes: self.ignore_quotes,
715 ignore_links: self.ignore_links,
716 ignore_images: self.ignore_images,
717 ignore_tables: self.ignore_tables,
718 }
719 }
720}
721
722#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
724pub struct CleanMessagesResponse {
725 pub cleaned: std::collections::HashMap<String, String>,
727}
728
729#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
731pub struct CleanMessageResponse {
732 #[serde(rename = "id")]
734 pub message_id: MessageId,
735
736 pub conversation: String,
738}
739
740#[cfg(test)]
741mod tests {
742 use super::*;
743
744 #[test]
745 fn test_message_creation() {
746 let message = Message {
747 id: MessageId::new("msg_123"),
748 grant_id: GrantId::new("grant_123"),
749 thread_id: Some(ThreadId::new("thread_123")),
750 subject: Some("Test Subject".to_string()),
751 from: vec![],
752 to: vec![],
753 cc: vec![],
754 bcc: vec![],
755 reply_to: vec![],
756 date: 1234567890,
757 unread: Some(true),
758 starred: Some(false),
759 snippet: Some("Test snippet".to_string()),
760 body: Some("Test body".to_string()),
761 attachments: vec![],
762 folders: vec![],
763 created_at: Some(1234567890),
764 headers: None,
765 raw_mime: None,
766 bounce_detected: None,
767 schedule_id: None,
768 send_at: None,
769 };
770
771 assert_eq!(message.id.as_str(), "msg_123");
772 assert_eq!(message.grant_id.as_str(), "grant_123");
773 assert_eq!(message.subject, Some("Test Subject".to_string()));
774 assert_eq!(message.unread, Some(true));
775 }
776
777 #[test]
778 fn test_message_serialization() {
779 let message = Message {
780 id: MessageId::new("msg_123"),
781 grant_id: GrantId::new("grant_123"),
782 thread_id: Some(ThreadId::new("thread_123")),
783 subject: Some("Test".to_string()),
784 from: vec![],
785 to: vec![],
786 cc: vec![],
787 bcc: vec![],
788 reply_to: vec![],
789 date: 1234567890,
790 unread: Some(true),
791 starred: Some(false),
792 snippet: Some("Snippet".to_string()),
793 body: Some("Body".to_string()),
794 attachments: vec![],
795 folders: vec![],
796 created_at: Some(1234567890),
797 headers: None,
798 raw_mime: None,
799 bounce_detected: None,
800 schedule_id: None,
801 send_at: None,
802 };
803
804 let json = serde_json::to_string(&message).unwrap();
805 assert!(json.contains("msg_123"));
806 assert!(json.contains("grant_123"));
807 assert!(json.contains("Test"));
808
809 let deserialized: Message = serde_json::from_str(&json).unwrap();
810 assert_eq!(deserialized, message);
811 }
812
813 #[test]
814 fn test_query_params_builder() {
815 let params = MessageQueryParams::builder()
816 .subject("invoice")
817 .unread(true)
818 .limit(50)
819 .build();
820
821 assert_eq!(params.subject, Some("invoice".to_string()));
822 assert_eq!(params.unread, Some(true));
823 assert_eq!(params.limit, Some(50));
824 }
825
826 #[test]
827 fn test_query_params_all_fields() {
828 let params = MessageQueryParams::builder()
829 .subject("test")
830 .from("sender@example.com")
831 .to("recipient@example.com")
832 .cc("cc@example.com")
833 .bcc("bcc@example.com")
834 .any_email("any@example.com")
835 .thread_id("thread_123")
836 .unread(true)
837 .starred(false)
838 .has_attachment(true)
839 .received_before(2000000000)
840 .received_after(1000000000)
841 .in_folder("folder_123")
842 .limit(100)
843 .page_token("token_abc")
844 .build();
845
846 assert_eq!(params.subject, Some("test".to_string()));
847 assert_eq!(params.from, Some("sender@example.com".to_string()));
848 assert_eq!(params.to, Some("recipient@example.com".to_string()));
849 assert_eq!(params.limit, Some(100));
850 assert_eq!(params.page_token, Some("token_abc".to_string()));
851 }
852
853 #[test]
854 fn test_query_params_serialization() {
855 let params = MessageQueryParams::builder()
856 .subject("test")
857 .unread(true)
858 .limit(50)
859 .build();
860
861 let json = serde_json::to_string(¶ms).unwrap();
862 assert!(json.contains("test"));
863 assert!(json.contains("true"));
864 assert!(json.contains("50"));
865 }
866
867 #[test]
868 fn test_update_message_request_builder() {
869 let update = UpdateMessageRequest::builder()
870 .unread(false)
871 .starred(true)
872 .build();
873
874 assert_eq!(update.unread, Some(false));
875 assert_eq!(update.starred, Some(true));
876 }
877
878 #[test]
879 fn test_update_message_request_with_folders() {
880 let update = UpdateMessageRequest::builder()
881 .unread(false)
882 .folders(vec!["folder1".to_string(), "folder2".to_string()])
883 .build();
884
885 assert_eq!(update.unread, Some(false));
886 assert_eq!(
887 update.folders,
888 Some(vec!["folder1".to_string(), "folder2".to_string()])
889 );
890 }
891
892 #[test]
893 fn test_update_message_request_serialization() {
894 let update = UpdateMessageRequest::builder()
895 .unread(false)
896 .starred(true)
897 .build();
898
899 let json = serde_json::to_string(&update).unwrap();
900 let deserialized: UpdateMessageRequest = serde_json::from_str(&json).unwrap();
901 assert_eq!(deserialized, update);
902 }
903
904 #[test]
905 fn test_send_message_request_builder() {
906 let request = SendMessageRequest::builder()
907 .subject("Test Subject")
908 .body("Test Body")
909 .to(vec!["recipient@example.com".to_string()])
910 .build();
911
912 assert_eq!(request.subject, Some("Test Subject".to_string()));
913 assert_eq!(request.body, Some("Test Body".to_string()));
914 assert_eq!(request.to, vec!["recipient@example.com".to_string()]);
915 }
916
917 #[test]
918 fn test_send_message_request_with_scheduled_send() {
919 let send_time = 1735689600; let request = SendMessageRequest::builder()
921 .subject("Scheduled Email")
922 .body("This will be sent later")
923 .to(vec!["recipient@example.com".to_string()])
924 .send_at(send_time)
925 .build();
926
927 assert_eq!(request.send_at, Some(send_time));
928 }
929
930 #[test]
931 fn test_send_message_request_with_tracking() {
932 let tracking = TrackingOptions::all_enabled();
933 let request = SendMessageRequest::builder()
934 .subject("Tracked Email")
935 .body("This will track opens and clicks")
936 .to(vec!["recipient@example.com".to_string()])
937 .tracking(tracking.clone())
938 .build();
939
940 assert_eq!(request.tracking_options, Some(tracking));
941 }
942
943 #[test]
944 fn test_clean_messages_request() {
945 let request = CleanMessagesRequest::builder()
946 .message_ids(vec!["msg_1".to_string(), "msg_2".to_string()])
947 .ignore_links(true)
948 .ignore_images(false)
949 .build();
950
951 assert_eq!(request.message_ids.len(), 2);
952 assert_eq!(request.ignore_links, Some(true));
953 assert_eq!(request.ignore_images, Some(false));
954 }
955
956 #[test]
957 fn test_schedule_id_creation() {
958 let schedule_id = ScheduleId::new("schedule_123");
959 assert_eq!(schedule_id.as_str(), "schedule_123");
960 }
961
962 #[test]
963 fn test_schedule_id_from_string() {
964 let schedule_id: ScheduleId = "schedule_456".into();
965 assert_eq!(schedule_id.as_str(), "schedule_456");
966 }
967
968 #[test]
969 fn test_schedule_id_display() {
970 let schedule_id = ScheduleId::new("schedule_789");
971 assert_eq!(format!("{}", schedule_id), "schedule_789");
972 }
973
974 #[test]
975 fn test_email_header_serialization() {
976 let header = EmailHeader {
977 name: "X-Custom-Header".to_string(),
978 value: "custom-value".to_string(),
979 };
980
981 let json = serde_json::to_string(&header).unwrap();
982 assert!(json.contains("X-Custom-Header"));
983 assert!(json.contains("custom-value"));
984
985 let deserialized: EmailHeader = serde_json::from_str(&json).unwrap();
986 assert_eq!(deserialized.name, "X-Custom-Header");
987 assert_eq!(deserialized.value, "custom-value");
988 }
989
990 #[test]
991 fn test_clean_message_response_deserialization() {
992 let json = r#"{"id":"msg_123","conversation":"Clean text here"}"#;
993 let response: CleanMessageResponse = serde_json::from_str(json).unwrap();
994
995 assert_eq!(response.message_id.as_str(), "msg_123");
996 assert_eq!(response.conversation, "Clean text here");
997 }
998
999 #[test]
1000 fn test_message_with_headers() {
1001 let headers = vec![
1002 EmailHeader {
1003 name: "From".to_string(),
1004 value: "sender@example.com".to_string(),
1005 },
1006 EmailHeader {
1007 name: "Subject".to_string(),
1008 value: "Test Email".to_string(),
1009 },
1010 ];
1011
1012 let message = Message {
1013 id: MessageId::new("msg_123"),
1014 grant_id: GrantId::new("grant_123"),
1015 thread_id: None,
1016 subject: Some("Test".to_string()),
1017 from: vec![],
1018 to: vec![],
1019 cc: vec![],
1020 bcc: vec![],
1021 reply_to: vec![],
1022 date: 1234567890,
1023 unread: None,
1024 starred: None,
1025 snippet: None,
1026 body: None,
1027 attachments: vec![],
1028 folders: vec![],
1029 created_at: None,
1030 headers: Some(headers.clone()),
1031 raw_mime: None,
1032 bounce_detected: None,
1033 schedule_id: None,
1034 send_at: None,
1035 };
1036
1037 assert!(message.headers.is_some());
1038 let msg_headers = message.headers.unwrap();
1039 assert_eq!(msg_headers.len(), 2);
1040 assert_eq!(msg_headers[0].name, "From");
1041 assert_eq!(msg_headers[1].name, "Subject");
1042 }
1043
1044 #[test]
1045 fn test_message_with_bounce_detected() {
1046 let message = Message {
1047 id: MessageId::new("msg_123"),
1048 grant_id: GrantId::new("grant_123"),
1049 thread_id: None,
1050 subject: Some("Bounced".to_string()),
1051 from: vec![],
1052 to: vec![],
1053 cc: vec![],
1054 bcc: vec![],
1055 reply_to: vec![],
1056 date: 1234567890,
1057 unread: None,
1058 starred: None,
1059 snippet: None,
1060 body: None,
1061 attachments: vec![],
1062 folders: vec![],
1063 created_at: None,
1064 headers: None,
1065 raw_mime: None,
1066 bounce_detected: Some(true),
1067 schedule_id: None,
1068 send_at: None,
1069 };
1070
1071 assert_eq!(message.bounce_detected, Some(true));
1072 }
1073
1074 #[test]
1075 fn test_message_with_schedule_id() {
1076 let message = Message {
1077 id: MessageId::new("msg_123"),
1078 grant_id: GrantId::new("grant_123"),
1079 thread_id: None,
1080 subject: Some("Scheduled".to_string()),
1081 from: vec![],
1082 to: vec![],
1083 cc: vec![],
1084 bcc: vec![],
1085 reply_to: vec![],
1086 date: 1234567890,
1087 unread: None,
1088 starred: None,
1089 snippet: None,
1090 body: None,
1091 attachments: vec![],
1092 folders: vec![],
1093 created_at: None,
1094 headers: None,
1095 raw_mime: None,
1096 bounce_detected: None,
1097 schedule_id: Some(ScheduleId::new("schedule_123")),
1098 send_at: Some(1735776000),
1099 };
1100
1101 assert!(message.schedule_id.is_some());
1102 assert_eq!(message.schedule_id.unwrap().as_str(), "schedule_123");
1103 assert_eq!(message.send_at, Some(1735776000));
1104 }
1105
1106 #[test]
1107 fn test_query_imap_parameter() {
1108 let params = MessageQueryParams::builder()
1109 .query_imap("FROM \"test@example.com\"")
1110 .build();
1111
1112 assert_eq!(
1113 params.query_imap,
1114 Some("FROM \"test@example.com\"".to_string())
1115 );
1116 }
1117
1118 #[test]
1119 fn test_query_imap_serialization() {
1120 let params = MessageQueryParams {
1121 query_imap: Some("UNSEEN SUBJECT \"urgent\"".to_string()),
1122 ..Default::default()
1123 };
1124
1125 let json = serde_json::to_string(¶ms).unwrap();
1126 assert!(json.contains("query_imap"));
1127 assert!(json.contains("UNSEEN SUBJECT"));
1128 }
1129}