1use crate::analyze::AnalyzeEmails;
2#[cfg(feature = "imap")]
3use crate::imap::Imap;
4#[cfg(feature = "pop3")]
5use crate::pop3::Pop3;
6#[cfg(feature = "smtp")]
7use crate::smtp::Smtp;
8use serde::{Deserialize, Serialize};
9use std::collections::BTreeMap;
10use std::io::Error;
11use std::path::PathBuf;
12use std::{fs, io};
13
14#[cfg(feature = "imap")]
15mod imap;
16#[cfg(feature = "pop3")]
17mod pop3;
18#[cfg(feature = "smtp")]
19mod smtp;
20
21pub mod analyze;
22pub mod pools;
23
24#[derive(Clone, Debug)]
25pub struct Mail {
26 protocol: Protocol,
28 host: String,
30 port: u16,
32 name: String,
34 mail: String,
36 pass: String,
38 email_type: EmailType,
40}
41
42impl Mail {
43 pub fn new_new(config: Config) -> Result<Self, String> {
44 let default = config.default.clone();
45 let connection = config
46 .connections
47 .get(&default)
48 .ok_or_else(|| format!("默认配置不存在: {default}"))?
49 .clone();
50 Ok(Self {
51 protocol: connection.protocol,
52 host: connection.host,
53 port: connection.port,
54 name: connection.name,
55 mail: connection.mail,
56 pass: connection.pass,
57 email_type: connection.email_type,
58 })
59 }
60 pub fn new(protocol: &str, host: &str, port: u16, name: &str, mail: &str, pass: &str) -> Self {
61 Self {
62 protocol: Protocol::from(protocol),
63 host: host.to_string(),
64 port,
65 name: name.to_string(),
66 mail: mail.to_string(),
67 pass: pass.to_string(),
68 email_type: EmailType::from(mail),
69 }
70 }
71 pub fn analyze(data: Vec<u8>, debug: bool) -> io::Result<AnalyzeEmails> {
73 AnalyzeEmails::new(data, debug)
74 }
75 pub fn receive(&mut self) -> Box<dyn Receive> {
77 match self.protocol {
78 #[cfg(feature = "pop3")]
79 Protocol::POP3 => Box::new(Pop3::new(
80 self.host.clone(),
81 self.port,
82 self.name.clone(),
83 self.mail.clone(),
84 self.pass.clone(),
85 self.email_type.clone(),
86 )),
87 #[cfg(feature = "smtp")]
88 Protocol::Smtp => Box::new(Smtp::new(
89 self.host.clone(),
90 self.port,
91 self.name.clone(),
92 self.mail.clone(),
93 self.pass.clone(),
94 self.email_type.clone(),
95 )),
96 #[cfg(feature = "imap")]
97 Protocol::Imap => Box::new(Imap::new(
98 self.host.clone(),
99 self.port,
100 self.name.clone(),
101 self.mail.clone(),
102 self.pass.clone(),
103 self.email_type.clone(),
104 )),
105 _ => Box::new(UnsupportedReceive {
106 protocol: self.protocol.clone(),
107 }),
108 }
109 }
110 pub fn sender(&mut self) -> Box<dyn Sender> {
112 match self.protocol {
113 #[cfg(feature = "pop3")]
114 Protocol::POP3 => Box::new(Pop3::new(
115 self.host.clone(),
116 self.port,
117 self.name.clone(),
118 self.mail.clone(),
119 self.pass.clone(),
120 self.email_type.clone(),
121 )),
122 #[cfg(feature = "smtp")]
123 Protocol::Smtp => Box::new(Smtp::new(
124 self.host.clone(),
125 self.port,
126 self.name.clone(),
127 self.mail.clone(),
128 self.pass.clone(),
129 self.email_type.clone(),
130 )),
131 #[cfg(feature = "imap")]
132 Protocol::Imap => Box::new(Imap::new(
133 self.host.clone(),
134 self.port,
135 self.name.clone(),
136 self.mail.clone(),
137 self.pass.clone(),
138 self.email_type.clone(),
139 )),
140 _ => Box::new(UnsupportedSender {
141 protocol: self.protocol.clone(),
142 }),
143 }
144 }
145}
146
147pub trait Receive {
148 fn get_total(&mut self) -> Result<usize, Error>;
150 fn get_id_list(&mut self) -> Result<Vec<String>, Error>;
152 fn get_id_info(&mut self, id: &str) -> Result<(Vec<u8>, usize, String), Error>;
155 fn del_id(&mut self, id: &str) -> Result<bool, Error>;
157
158 fn get_uid_list(&mut self) -> Result<Vec<String>, Error> {
162 self.get_id_list()
163 }
164
165 fn get_new_since(&mut self, _last_uid: &str) -> Result<Vec<String>, Error> {
169 Err(Error::other("此协议不支持增量查询"))
170 }
171
172 fn get_validity(&mut self) -> Result<Option<u64>, Error> {
176 Ok(None)
177 }
178
179 fn get_max_uid(&mut self) -> Result<Option<u64>, Error> {
181 Ok(None)
182 }
183
184 fn get_uidl_map(&mut self) -> Result<Vec<(String, String)>, Error> {
187 Err(Error::other("此协议不支持 UIDL"))
188 }
189}
190
191pub trait Sender {
192 fn send(&mut self) -> Result<(Vec<u8>, usize, String), Error>;
195
196 fn set_from(&mut self, name: &str) -> &mut dyn Sender;
200
201 fn set_to(&mut self, name: &str, mail: &str) -> &mut dyn Sender;
205 fn set_cc(&mut self, name: &str, mail: &str) -> &mut dyn Sender;
209 fn set_subject(&mut self, subject: &str) -> &mut dyn Sender;
211 fn set_body(&mut self, text: &str, html: &str) -> &mut dyn Sender;
213 fn set_file(&mut self, name: &str, data: Vec<u8>, content_type: &str) -> &mut dyn Sender;
215}
216
217struct UnsupportedReceive {
218 protocol: Protocol,
219}
220
221impl UnsupportedReceive {
222 fn err(&self, action: &str) -> Error {
223 Error::other(format!("{action} 不支持协议: {:?}", self.protocol))
224 }
225}
226
227impl Receive for UnsupportedReceive {
228 fn get_total(&mut self) -> Result<usize, Error> {
229 Err(self.err("获取邮件总数"))
230 }
231
232 fn get_id_list(&mut self) -> Result<Vec<String>, Error> {
233 Err(self.err("获取邮件ID列表"))
234 }
235
236 fn get_id_info(&mut self, _id: &str) -> Result<(Vec<u8>, usize, String), Error> {
237 Err(self.err("获取邮件内容"))
238 }
239
240 fn del_id(&mut self, _id: &str) -> Result<bool, Error> {
241 Err(self.err("删除邮件"))
242 }
243}
244
245struct UnsupportedSender {
246 protocol: Protocol,
247}
248
249impl UnsupportedSender {
250 fn err(&self, action: &str) -> Error {
251 Error::other(format!("{action} 不支持协议: {:?}", self.protocol))
252 }
253}
254
255impl Sender for UnsupportedSender {
256 fn send(&mut self) -> Result<(Vec<u8>, usize, String), Error> {
257 Err(self.err("发送邮件"))
258 }
259
260 fn set_from(&mut self, _name: &str) -> &mut dyn Sender {
261 self
262 }
263
264 fn set_to(&mut self, _name: &str, _mail: &str) -> &mut dyn Sender {
265 self
266 }
267
268 fn set_cc(&mut self, _name: &str, _mail: &str) -> &mut dyn Sender {
269 self
270 }
271
272 fn set_subject(&mut self, _subject: &str) -> &mut dyn Sender {
273 self
274 }
275
276 fn set_body(&mut self, _text: &str, _html: &str) -> &mut dyn Sender {
277 self
278 }
279
280 fn set_file(&mut self, _name: &str, _data: Vec<u8>, _content_type: &str) -> &mut dyn Sender {
281 self
282 }
283}
284
285#[derive(Clone, Debug, Deserialize, Serialize)]
286enum Protocol {
287 POP3,
288 Smtp,
289 Imap,
290 None,
291}
292
293impl From<&str> for Protocol {
294 fn from(name: &str) -> Self {
295 let name = name.to_lowercase();
296 match name.as_str() {
297 "pop3" => Protocol::POP3,
298 "imap" => Protocol::Imap,
299 "smtp" => Protocol::Smtp,
300 _ => Protocol::None,
301 }
302 }
303}
304
305#[derive(Clone, Debug, Deserialize, Serialize)]
306enum EmailType {
307 Google,
308 Qq,
309 None,
310}
311impl From<&str> for EmailType {
312 fn from(email: &str) -> Self {
313 match email {
314 x if x.contains("@gmail.com") => EmailType::Google,
315 x if x.contains("@qq.com") => EmailType::Qq,
316 _ => EmailType::None,
317 }
318 }
319}
320
321#[derive(Clone, Debug, Deserialize, Serialize)]
322pub struct Config {
323 default: String,
324 connections: BTreeMap<String, Connection>,
325}
326impl Default for Config {
327 fn default() -> Self {
328 Self::new()
329 }
330}
331
332impl Config {
333 pub fn create(config_file: PathBuf, pkg_name: bool) -> Config {
334 #[derive(Clone, Debug, Deserialize, Serialize)]
335 pub struct ConfigNew {
336 pub br_email: Config,
337 }
338 impl ConfigNew {
339 pub fn new() -> ConfigNew {
340 let mut connections = BTreeMap::new();
341 connections.insert("my_name".to_string(), Connection::default());
342 Self {
343 br_email: Config {
344 default: "my_name".to_string(),
345 connections,
346 },
347 }
348 }
349 }
350 match fs::read_to_string(config_file.clone()) {
351 Ok(e) => {
352 if pkg_name {
353 let data = ConfigNew::new();
354 toml::from_str::<ConfigNew>(&e)
355 .unwrap_or_else(|_| {
356 if let Ok(toml) = toml::to_string(&data) {
357 let toml = format!("{e}\r\n{toml}");
358 if let Some(path) = config_file.to_str() {
359 let _ = fs::write(path, toml);
360 }
361 }
362 data
363 })
364 .br_email
365 } else {
366 let data = Config::new();
367 toml::from_str::<Config>(&e).unwrap_or_else(|_| {
368 if let Ok(toml) = toml::to_string(&data) {
369 let toml = format!("{e}\r\n{toml}");
370 if let Some(path) = config_file.to_str() {
371 let _ = fs::write(path, toml);
372 }
373 }
374 data
375 })
376 }
377 }
378 Err(_) => {
379 if pkg_name {
380 let data = ConfigNew::new();
381 if let Some(parent) = config_file.parent() {
382 let _ = fs::create_dir_all(parent);
383 }
384 if let Ok(toml) = toml::to_string(&data) {
385 if let Some(path) = config_file.to_str() {
386 let _ = fs::write(path, toml);
387 }
388 }
389 data.br_email
390 } else {
391 let data = Config::new();
392 if let Some(parent) = config_file.parent() {
393 let _ = fs::create_dir_all(parent);
394 }
395 if let Ok(toml) = toml::to_string(&data) {
396 if let Some(path) = config_file.to_str() {
397 let _ = fs::write(path, toml);
398 }
399 }
400 data
401 }
402 }
403 }
404 }
405 pub fn new() -> Self {
406 let mut connections = BTreeMap::new();
407 connections.insert("my_name".to_string(), Connection::default());
408 Self {
409 default: "my_name".to_string(),
410 connections,
411 }
412 }
413}
414#[derive(Clone, Debug, Deserialize, Serialize)]
415struct Connection {
416 protocol: Protocol,
418 host: String,
420 port: u16,
422 name: String,
424 mail: String,
426 pass: String,
428 email_type: EmailType,
430}
431impl Default for Connection {
432 fn default() -> Self {
433 Self {
434 protocol: Protocol::Smtp,
435 host: "smtp.exmail.qq.com".to_string(),
436 port: 25,
437 name: "发件人名".to_string(),
438 mail: "xxx@qq.com".to_string(),
439 pass: "*******".to_string(),
440 email_type: EmailType::Qq,
441 }
442 }
443}
444
445#[cfg(test)]
446mod tests {
447 use super::*;
448 use std::time::{SystemTime, UNIX_EPOCH};
449
450 fn temp_config_path(test_name: &str) -> PathBuf {
451 let nanos = SystemTime::now()
452 .duration_since(UNIX_EPOCH)
453 .unwrap()
454 .as_nanos();
455 std::env::temp_dir().join(format!(
456 "br_email_{test_name}_{}_{}.toml",
457 std::process::id(),
458 nanos
459 ))
460 }
461
462 #[test]
463 fn test_protocol_from() {
464 assert!(matches!(Protocol::from("pop3"), Protocol::POP3));
465 assert!(matches!(Protocol::from("POP3"), Protocol::POP3));
466 assert!(matches!(Protocol::from("imap"), Protocol::Imap));
467 assert!(matches!(Protocol::from("IMAP"), Protocol::Imap));
468 assert!(matches!(Protocol::from("smtp"), Protocol::Smtp));
469 assert!(matches!(Protocol::from("SMTP"), Protocol::Smtp));
470 assert!(matches!(Protocol::from("unknown"), Protocol::None));
471 }
472
473 #[test]
474 fn test_email_type_from() {
475 assert!(matches!(
476 EmailType::from("user@gmail.com"),
477 EmailType::Google
478 ));
479 assert!(matches!(EmailType::from("user@qq.com"), EmailType::Qq));
480 assert!(matches!(
481 EmailType::from("user@outlook.com"),
482 EmailType::None
483 ));
484 }
485
486 #[test]
487 fn test_mail_new() {
488 let mail = Mail::new(
489 "imap",
490 "imap.gmail.com",
491 993,
492 "test",
493 "user@gmail.com",
494 "pass",
495 );
496 assert!(matches!(mail.protocol, Protocol::Imap));
497 assert_eq!(mail.host, "imap.gmail.com");
498 assert_eq!(mail.port, 993);
499 assert_eq!(mail.name, "test");
500 assert_eq!(mail.mail, "user@gmail.com");
501 assert_eq!(mail.pass, "pass");
502 }
503
504 #[test]
505 fn test_config_new() {
506 let config = Config::new();
507 assert_eq!(config.default, "my_name");
508 assert!(config.connections.contains_key("my_name"));
509 }
510
511 #[test]
512 fn test_config_default() {
513 let config = Config::default();
514 assert_eq!(config.default, "my_name");
515 }
516
517 #[test]
518 fn test_connection_default() {
519 let conn = Connection::default();
520 assert!(matches!(conn.protocol, Protocol::Smtp));
521 assert_eq!(conn.host, "smtp.exmail.qq.com");
522 assert_eq!(conn.port, 25);
523 }
524
525 #[test]
526 fn test_receive_dispatch_for_supported_protocols() {
527 let mut pop3_mail = Mail::new("pop3", "localhost", 995, "n", "m@m.com", "p");
528 let _pop3_receiver = pop3_mail.receive();
529
530 let mut smtp_mail = Mail::new("smtp", "localhost", 587, "n", "m@m.com", "p");
531 let _smtp_receiver = smtp_mail.receive();
532
533 let mut imap_mail = Mail::new("imap", "localhost", 993, "n", "m@m.com", "p");
534 let _imap_receiver = imap_mail.receive();
535 }
536
537 #[test]
538 fn test_sender_dispatch_for_supported_protocols() {
539 let mut pop3_mail = Mail::new("pop3", "localhost", 995, "n", "m@m.com", "p");
540 let mut pop3_sender = pop3_mail.sender();
541 pop3_sender.set_from("sender");
542 pop3_sender.set_to("to", "to@test.com");
543 pop3_sender.set_cc("cc", "cc@test.com");
544 pop3_sender.set_subject("subject");
545 pop3_sender.set_body("text", "<p>html</p>");
546 pop3_sender.set_file("attachment.txt", vec![1, 2, 3], "text/plain");
547
548 let mut smtp_mail = Mail::new("smtp", "localhost", 587, "n", "m@m.com", "p");
549 let mut smtp_sender = smtp_mail.sender();
550 smtp_sender.set_from("sender");
551 smtp_sender.set_to("to", "to@test.com");
552 smtp_sender.set_cc("cc", "cc@test.com");
553 smtp_sender.set_subject("subject");
554 smtp_sender.set_body("text", "<p>html</p>");
555 smtp_sender.set_file("attachment.txt", vec![1, 2, 3], "text/plain");
556
557 let mut imap_mail = Mail::new("imap", "localhost", 993, "n", "m@m.com", "p");
558 let mut imap_sender = imap_mail.sender();
559 imap_sender.set_from("sender");
560 imap_sender.set_to("to", "to@test.com");
561 imap_sender.set_cc("cc", "cc@test.com");
562 imap_sender.set_subject("subject");
563 imap_sender.set_body("text", "<p>html</p>");
564 imap_sender.set_file("attachment.txt", vec![1, 2, 3], "text/plain");
565 }
566
567 #[test]
568 fn test_config_create_file_not_exists_without_pkg_name() {
569 let config_path = temp_config_path("create_without_pkg_missing");
570 std::fs::remove_file(&config_path).ok();
571
572 let config = Config::create(config_path.clone(), false);
573 assert_eq!(config.default, "my_name");
574 assert!(config.connections.contains_key("my_name"));
575 assert!(config_path.exists());
576
577 std::fs::remove_file(&config_path).ok();
578 }
579
580 #[test]
581 fn test_config_create_file_not_exists_with_pkg_name() {
582 let config_path = temp_config_path("create_with_pkg_missing");
583 std::fs::remove_file(&config_path).ok();
584
585 let config = Config::create(config_path.clone(), true);
586 assert_eq!(config.default, "my_name");
587 assert!(config.connections.contains_key("my_name"));
588 let file_content = std::fs::read_to_string(&config_path).unwrap();
589 assert!(file_content.contains("[br_email]"));
590
591 std::fs::remove_file(&config_path).ok();
592 }
593
594 #[test]
595 fn test_config_create_existing_valid_toml_without_pkg_name() {
596 let config_path = temp_config_path("create_without_pkg_valid");
597 std::fs::remove_file(&config_path).ok();
598
599 let config_content = r#"default = "my_name"
600
601[connections.my_name]
602protocol = "Smtp"
603host = "smtp.test.com"
604port = 25
605name = "test"
606mail = "test@test.com"
607pass = "pass"
608email_type = "None"
609"#;
610 std::fs::write(&config_path, config_content).unwrap();
611
612 let config = Config::create(config_path.clone(), false);
613 assert_eq!(config.default, "my_name");
614 assert_eq!(
615 config.connections.get("my_name").unwrap().host,
616 "smtp.test.com"
617 );
618
619 std::fs::remove_file(&config_path).ok();
620 }
621
622 #[test]
623 fn test_config_create_existing_valid_toml_with_pkg_name() {
624 let config_path = temp_config_path("create_with_pkg_valid");
625 std::fs::remove_file(&config_path).ok();
626
627 let config_content = r#"[br_email]
628default = "my_name"
629
630[br_email.connections.my_name]
631protocol = "Smtp"
632host = "smtp.test.com"
633port = 25
634name = "test"
635mail = "test@test.com"
636pass = "pass"
637email_type = "None"
638"#;
639 std::fs::write(&config_path, config_content).unwrap();
640
641 let config = Config::create(config_path.clone(), true);
642 assert_eq!(config.default, "my_name");
643 assert_eq!(
644 config.connections.get("my_name").unwrap().host,
645 "smtp.test.com"
646 );
647
648 std::fs::remove_file(&config_path).ok();
649 }
650
651 #[test]
652 fn test_config_create_existing_invalid_toml_with_pkg_name_fallback() {
653 let config_path = temp_config_path("create_with_pkg_invalid");
654 std::fs::remove_file(&config_path).ok();
655
656 std::fs::write(&config_path, "invalid toml content!!!").unwrap();
657 let config = Config::create(config_path.clone(), true);
658
659 assert_eq!(config.default, "my_name");
660 assert!(config.connections.contains_key("my_name"));
661 let file_content = std::fs::read_to_string(&config_path).unwrap();
662 assert!(file_content.contains("invalid toml content!!!"));
663 assert!(file_content.contains("[br_email]"));
664
665 std::fs::remove_file(&config_path).ok();
666 }
667
668 #[test]
669 fn test_config_create_existing_invalid_toml_without_pkg_name_fallback() {
670 let config_path = temp_config_path("create_without_pkg_invalid");
671 std::fs::remove_file(&config_path).ok();
672
673 std::fs::write(&config_path, "invalid toml content!!!").unwrap();
674 let config = Config::create(config_path.clone(), false);
675
676 assert_eq!(config.default, "my_name");
677 assert!(config.connections.contains_key("my_name"));
678 let file_content = std::fs::read_to_string(&config_path).unwrap();
679 assert!(file_content.contains("invalid toml content!!!"));
680 assert!(file_content.contains("[connections.my_name]"));
681
682 std::fs::remove_file(&config_path).ok();
683 }
684
685 #[test]
686 fn test_mail_new_new_success() {
687 let mut config = Config::new();
688 config.default = "test_conn".to_string();
689 config.connections.insert(
690 "test_conn".to_string(),
691 Connection {
692 protocol: Protocol::Imap,
693 host: "imap.test.com".to_string(),
694 port: 993,
695 name: "Test".to_string(),
696 mail: "test@test.com".to_string(),
697 pass: "password".to_string(),
698 email_type: EmailType::None,
699 },
700 );
701
702 let mail = Mail::new_new(config).unwrap();
703 assert_eq!(mail.host, "imap.test.com");
704 assert_eq!(mail.port, 993);
705 }
706
707 #[test]
708 fn test_mail_new_new_missing_default() {
709 let mut config = Config::new();
710 config.default = "nonexistent".to_string();
711
712 let result = Mail::new_new(config);
713 assert!(result.is_err());
714 }
715
716 #[test]
717 fn test_unsupported_receive() {
718 let mut mail = Mail::new("unknown", "host", 123, "name", "mail", "pass");
719 let mut receiver = mail.receive();
720
721 assert!(receiver.get_total().is_err());
722 assert!(receiver.get_id_list().is_err());
723 assert!(receiver.get_id_info("1").is_err());
724 assert!(receiver.del_id("1").is_err());
725 }
726
727 #[test]
728 fn test_unsupported_sender() {
729 let mut mail = Mail::new("unknown", "host", 123, "name", "mail", "pass");
730 let mut sender = mail.sender();
731
732 assert!(sender.send().is_err());
733 sender.set_from("test");
734 sender.set_to("name", "mail");
735 sender.set_cc("name", "mail");
736 sender.set_subject("subject");
737 sender.set_body("text", "html");
738 sender.set_file("name", vec![], "type");
739 }
740
741 #[test]
742 fn test_receive_trait_default_methods() {
743 let mut mail = Mail::new("unknown", "host", 123, "name", "mail", "pass");
744 let mut receiver = mail.receive();
745
746 assert!(receiver.get_uid_list().is_err());
747 assert!(receiver.get_new_since("100").is_err());
748 assert!(receiver.get_validity().unwrap().is_none());
749 assert!(receiver.get_max_uid().unwrap().is_none());
750 assert!(receiver.get_uidl_map().is_err());
751 }
752
753 #[test]
754 fn test_mail_analyze_valid() {
755 let email_data = b"From: sender@example.com\r\n\
756To: receiver@example.com\r\n\
757Subject: Test\r\n\
758Content-Type: text/plain\r\n\
759Date: 01 Jan 2024 12:00:00 +0000\r\n\
760\r\n\
761Body"
762 .to_vec();
763
764 let result = Mail::analyze(email_data, false);
765 assert!(result.is_ok());
766 let email = result.unwrap();
767 assert_eq!(email.subject, "Test");
768 }
769
770 #[test]
771 fn test_mail_analyze_invalid() {
772 let invalid_data = b"invalid email".to_vec();
773 let result = Mail::analyze(invalid_data, false);
774 assert!(result.is_err());
775 }
776
777 #[test]
778 fn test_mail_with_different_protocols() {
779 let pop3_mail = Mail::new("pop3", "pop.test.com", 995, "name", "user@test.com", "pass");
780 assert!(matches!(pop3_mail.protocol, Protocol::POP3));
781
782 let smtp_mail = Mail::new(
783 "smtp",
784 "smtp.test.com",
785 587,
786 "name",
787 "user@test.com",
788 "pass",
789 );
790 assert!(matches!(smtp_mail.protocol, Protocol::Smtp));
791
792 let imap_mail = Mail::new(
793 "imap",
794 "imap.test.com",
795 993,
796 "name",
797 "user@test.com",
798 "pass",
799 );
800 assert!(matches!(imap_mail.protocol, Protocol::Imap));
801 }
802
803 #[test]
804 fn test_email_type_detection() {
805 let gmail = Mail::new(
806 "imap",
807 "imap.gmail.com",
808 993,
809 "name",
810 "user@gmail.com",
811 "pass",
812 );
813 assert!(matches!(gmail.email_type, EmailType::Google));
814
815 let qq = Mail::new("imap", "imap.qq.com", 993, "name", "user@qq.com", "pass");
816 assert!(matches!(qq.email_type, EmailType::Qq));
817
818 let other = Mail::new(
819 "imap",
820 "imap.other.com",
821 993,
822 "name",
823 "user@other.com",
824 "pass",
825 );
826 assert!(matches!(other.email_type, EmailType::None));
827 }
828}