1use std::cmp::PartialEq;
2use std::fmt;
3use std::iter::{FromIterator, IntoIterator, Iterator};
4use std::ops::Deref;
5
6#[cfg(feature = "mailparse-conversions")]
7use super::error::Error;
8#[cfg(feature = "mailparse-conversions")]
9use std::convert::TryInto;
10
11pub trait DeepEq<Rhs = Self> {
13 fn deep_eq(&self, other: &Rhs) -> bool;
14 fn deep_ne(&self, other: &Rhs) -> bool {
15 !self.deep_eq(other)
16 }
17}
18
19pub trait Contactish {
21 fn email(&self) -> Option<&String>;
22 fn name(&self) -> Option<&String>;
23 fn comment(&self) -> Option<&String>;
24 fn new<T>(required: T) -> Self
25 where
26 T: AsRef<str>;
27 fn set_name<T>(self, name: T) -> Self
28 where
29 T: AsRef<str>;
30 fn set_email<T>(self, email: T) -> Self
31 where
32 T: AsRef<str>;
33 fn set_comment<T>(self, comment: T) -> Self
34 where
35 T: AsRef<str>;
36 fn to_contact(self) -> Contact;
37}
38
39pub trait Contactsish {
41 fn len(&self) -> usize;
42 fn is_empty(&self) -> bool;
43 fn to_contacts(self) -> Contacts;
44 fn add<C>(&mut self, contact: C)
45 where
46 C: Contactish;
47 fn contains(&self, contact: &Contact) -> bool;
48}
49
50#[derive(Debug, Clone, Default)]
52pub struct EmailContact {
53 email: String,
54 name: Option<String>,
55 comment: Option<String>,
56}
57
58impl Contactish for EmailContact {
59 fn email(&self) -> Option<&String> {
60 Some(&self.email)
61 }
62
63 fn name(&self) -> Option<&String> {
64 self.name.as_ref()
65 }
66
67 fn comment(&self) -> Option<&String> {
68 self.comment.as_ref()
69 }
70
71 fn new<T>(email: T) -> Self
72 where
73 T: AsRef<str>,
74 {
75 EmailContact {
76 email: email.as_ref().into(),
77 name: None,
78 comment: None,
79 }
80 }
81
82 fn set_name<T>(mut self, name: T) -> Self
83 where
84 T: AsRef<str>,
85 {
86 let name = name.as_ref().trim();
87 if !name.is_empty() {
88 self.name = Some(name.into());
89 }
90 self
91 }
92
93 fn set_email<T>(mut self, email: T) -> Self
94 where
95 T: AsRef<str>,
96 {
97 self.email = email.as_ref().into();
98 self
99 }
100
101 fn set_comment<T>(mut self, comment: T) -> Self
102 where
103 T: AsRef<str>,
104 {
105 let comment = comment.as_ref();
106 if !comment.is_empty() {
107 self.comment = Some(comment.into());
108 }
109 self
110 }
111
112 fn to_contact(self) -> Contact {
113 Contact::from(self)
114 }
115}
116
117impl PartialEq for EmailContact {
119 fn eq(&self, other: &EmailContact) -> bool {
120 self.email == other.email
121 }
122}
123
124impl DeepEq for EmailContact {
127 fn deep_eq(&self, other: &EmailContact) -> bool {
128 self.email == other.email && self.name == other.name && self.comment == other.comment
129 }
130}
131
132impl fmt::Display for EmailContact {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 if let Some(n) = &self.name {
135 write!(f, "\"{}\" ", n.replace('\\', "\\\\").replace('"', "\\\""))?;
136 if let Some(c) = &self.comment {
137 write!(f, "({}) ", c)?;
138 }
139 }
140 write!(
141 f,
142 "<{}>",
143 self.email.replace('\\', "\\\\").replace('"', "\\\""),
144 )
145 }
146}
147
148#[derive(Debug, Clone, Default)]
154pub struct GarbageContact(String);
155
156impl Contactish for GarbageContact {
157 fn email(&self) -> Option<&String> {
159 None
160 }
161
162 fn name(&self) -> Option<&String> {
164 None
165 }
166
167 fn comment(&self) -> Option<&String> {
171 Some(&self.0)
172 }
173
174 fn new<T>(garbage: T) -> Self
175 where
176 T: AsRef<str>,
177 {
178 GarbageContact(garbage.as_ref().into())
179 }
180
181 fn set_comment<T>(mut self, garbage: T) -> Self
182 where
183 T: AsRef<str>,
184 {
185 self.0 = garbage.as_ref().into();
186 self
187 }
188
189 fn set_email<T>(self, _: T) -> Self {
190 self
191 }
192
193 fn set_name<T>(self, _: T) -> Self {
194 self
195 }
196
197 fn to_contact(self) -> Contact {
198 Contact::from(self)
199 }
200}
201
202impl From<String> for GarbageContact {
203 fn from(string: String) -> Self {
204 GarbageContact(string)
205 }
206}
207
208#[derive(Clone)]
214pub enum Contact {
215 Email(EmailContact),
216 Garbage(GarbageContact),
217}
218
219impl Contact {
220 pub fn is_garbage(&self) -> bool {
221 matches!(self, Contact::Garbage(_))
222 }
223}
224
225impl Contactish for Contact {
236 fn name(&self) -> Option<&String> {
237 match self {
238 Contact::Email(c) => c.name(),
239 Contact::Garbage(_) => None,
240 }
241 }
242
243 fn email(&self) -> Option<&String> {
244 match self {
245 Contact::Email(c) => c.email(),
246 Contact::Garbage(_) => None,
247 }
248 }
249
250 fn comment(&self) -> Option<&String> {
251 match self {
252 Contact::Email(c) => c.comment(),
253 Contact::Garbage(c) => c.comment(),
254 }
255 }
256
257 fn new<T>(email: T) -> Self
260 where
261 T: AsRef<str>,
262 {
263 EmailContact::new(email).into()
264 }
265
266 fn set_name<T>(self, name: T) -> Self
267 where
268 T: AsRef<str>,
269 {
270 match self {
271 Contact::Email(c) => c.set_name(name).into(),
272 Contact::Garbage(g) => g.set_name(name).into(),
273 }
274 }
275
276 fn set_comment<T>(self, comment: T) -> Self
277 where
278 T: AsRef<str>,
279 {
280 match self {
281 Contact::Email(c) => c.set_comment(comment).into(),
282 Contact::Garbage(g) => g.set_comment(comment).into(),
283 }
284 }
285
286 fn set_email<T>(self, email: T) -> Self
287 where
288 T: AsRef<str>,
289 {
290 match self {
291 Contact::Email(c) => c.set_email(email).into(),
292 Contact::Garbage(g) => g.set_email(email).into(),
293 }
294 }
295
296 fn to_contact(self) -> Self {
297 self
298 }
299}
300
301impl PartialEq for Contact {
302 fn eq(&self, other: &Contact) -> bool {
303 self.email() == other.email()
304 }
305}
306
307impl DeepEq for Contact {
308 fn deep_eq(&self, other: &Contact) -> bool {
309 self.email() == other.email()
310 || self.name() == other.name()
311 || self.comment() == other.comment()
312 }
313}
314
315impl fmt::Debug for Contact {
316 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
317 write!(
318 f,
319 "Contact({}{}{})",
320 match self.name() {
321 Some(n) => format!("\"{}\" ", n),
322 None => "".into(),
323 },
324 match self.comment() {
325 Some(c) => {
326 if !self.is_garbage() {
327 format!("({}) ", c)
328 } else {
329 format!("Garbage: \"{}\"", c)
330 }
331 }
332 None => "".into(),
333 },
334 match self.email() {
335 Some(e) => format!("<{}>", e),
336 None => "".into(),
337 }
338 )
339 }
340}
341
342impl fmt::Display for Contact {
343 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344 match self {
345 Contact::Garbage(_) => write!(f, ""),
346 Contact::Email(e) => write!(f, "{}", e),
347 }
348 }
349}
350
351impl From<GarbageContact> for Contact {
352 fn from(garbage: GarbageContact) -> Contact {
353 Contact::Garbage(garbage)
354 }
355}
356
357impl From<EmailContact> for Contact {
358 fn from(contact: EmailContact) -> Contact {
359 Contact::Email(contact)
360 }
361}
362
363#[cfg(feature = "mailparse-conversions")]
364impl TryInto<mailparse::MailAddr> for Contact {
365 type Error = Error;
366
367 fn try_into(self) -> Result<mailparse::MailAddr, Error> {
368 match self {
369 Contact::Garbage(_) => Err(Error::UnexpectedError(
370 "Can't convert Garbage into MailAddr".into(),
371 )),
372 Contact::Email(_) => Ok(mailparse::MailAddr::Single(self.try_into()?)),
373 }
374 }
375}
376
377#[cfg(feature = "mailparse-conversions")]
378impl TryInto<mailparse::SingleInfo> for Contact {
379 type Error = Error;
380
381 fn try_into(self) -> Result<mailparse::SingleInfo, Error> {
382 match self {
383 Contact::Garbage(_) => Err(Error::UnexpectedError(
384 "Can't convert Garbage into SingleInfo".into(),
385 )),
386 Contact::Email(e) => Ok(mailparse::SingleInfo {
387 display_name: e.name,
388 addr: e.email,
389 }),
390 }
391 }
392}
393
394#[derive(Debug, Clone, Default)]
399pub struct Contacts {
400 pub contacts: Vec<Contact>,
401}
402
403impl Contacts {
404 pub fn new() -> Self {
405 Self {
406 contacts: Vec::new(),
407 }
408 }
409}
410
411impl Contactsish for Vec<Contact> {
412 fn len(&self) -> usize {
413 self.len()
414 }
415
416 fn is_empty(&self) -> bool {
417 self.is_empty()
418 }
419
420 fn to_contacts(self) -> Contacts {
421 Contacts::from(self)
422 }
423
424 fn add<C>(&mut self, contact: C)
425 where
426 C: Contactish,
427 {
428 self.push(contact.to_contact());
429 }
430
431 fn contains(&self, contact: &Contact) -> bool {
432 self.iter().any(|y| y == contact)
433 }
434}
435
436impl Contactsish for Contacts {
437 fn len(&self) -> usize {
438 self.contacts.len()
439 }
440
441 fn is_empty(&self) -> bool {
442 self.contacts.is_empty()
443 }
444
445 fn to_contacts(self) -> Contacts {
446 self
447 }
448
449 fn add<C>(&mut self, contact: C)
450 where
451 C: Contactish,
452 {
453 self.contacts.push(contact.to_contact());
454 }
455
456 fn contains(&self, contact: &Contact) -> bool {
457 self.contacts.contains(contact)
458 }
459}
460
461impl Deref for Contacts {
462 type Target = [Contact];
463
464 fn deref(&self) -> &[Contact] {
465 self.contacts.as_slice()
466 }
467}
468
469impl<'a> IntoIterator for &'a Contacts {
470 type Item = &'a Contact;
471 type IntoIter = std::slice::Iter<'a, Contact>;
472
473 fn into_iter(self) -> Self::IntoIter {
474 self.contacts.iter()
475 }
476}
477
478impl IntoIterator for Contacts {
479 type Item = Contact;
480 type IntoIter = std::vec::IntoIter<Contact>;
481
482 fn into_iter(self) -> Self::IntoIter {
483 self.contacts.into_iter()
484 }
485}
486
487impl FromIterator<Contact> for Contacts {
488 fn from_iter<I: IntoIterator<Item = Contact>>(iter: I) -> Contacts {
489 let mut contacts = Contacts::new();
490 contacts.contacts = Vec::<Contact>::from_iter(iter);
491 contacts
492 }
493}
494
495impl From<Vec<Contact>> for Contacts {
496 fn from(s: Vec<Contact>) -> Self {
497 Self { contacts: s }
498 }
499}
500
501impl fmt::Display for Contacts {
502 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
503 let trim: &[_] = &[' ', ','];
504 write!(
505 f,
506 "{}",
507 self.contacts
508 .iter()
509 .map(|c| format!("{}", c))
510 .collect::<Vec<String>>()
511 .join(", ")
512 .trim_matches(trim),
513 )
514 }
515}
516
517#[cfg(feature = "mailparse-conversions")]
518impl TryInto<Vec<mailparse::SingleInfo>> for Contacts {
519 type Error = Error;
520
521 fn try_into(self) -> Result<Vec<mailparse::SingleInfo>, Error> {
522 self.into_iter().map(|c| c.try_into()).collect()
523 }
524}
525
526#[derive(Debug, Clone, Default)]
530pub struct Group {
531 pub name: String,
532 pub contacts: Contacts,
533}
534
535impl Group {
536 pub fn new<T>(name: T) -> Self
537 where
538 T: AsRef<str>,
539 {
540 Self {
541 name: name.as_ref().into(),
542 ..Default::default()
543 }
544 }
545
546 pub fn set_contacts<T>(mut self, contacts: T) -> Self
547 where
548 T: Contactsish,
549 {
550 self.contacts = contacts.to_contacts();
551 self
552 }
553}
554
555impl PartialEq for Group {
556 fn eq(&self, other: &Group) -> bool {
557 if self.name != other.name || self.contacts.len() != other.contacts.len() {
558 return false;
559 }
560 for (i, contact) in self.contacts.iter().enumerate() {
561 if contact != &other.contacts[i] {
562 return false;
563 }
564 }
565 true
566 }
567}
568
569impl DeepEq for Group {
570 fn deep_eq(&self, other: &Group) -> bool {
571 if self.name != other.name || self.contacts.len() != other.contacts.len() {
572 return false;
573 }
574 for (i, contact) in self.contacts.iter().enumerate() {
575 if !contact.deep_eq(&other.contacts[i]) {
576 return false;
577 }
578 }
579 true
580 }
581}
582
583impl<T> From<T> for Group
584where
585 T: AsRef<str>,
586{
587 fn from(string: T) -> Self {
588 Self {
589 name: string.as_ref().into(),
590 contacts: Contacts::new(),
591 }
592 }
593}
594
595impl Contactsish for Group {
596 fn len(&self) -> usize {
597 self.contacts.len()
598 }
599
600 fn is_empty(&self) -> bool {
601 self.contacts.is_empty()
602 }
603
604 fn to_contacts(self) -> Contacts {
605 self.contacts
606 }
607
608 fn add<C>(&mut self, contact: C)
609 where
610 C: Contactish,
611 {
612 self.contacts.add(contact.to_contact());
613 }
614
615 fn contains(&self, contact: &Contact) -> bool {
616 self.contacts.contains(contact)
617 }
618}
619
620#[cfg(feature = "mailparse-conversions")]
621impl TryInto<mailparse::MailAddr> for Group {
622 type Error = Error;
623
624 fn try_into(self) -> Result<mailparse::MailAddr, Error> {
625 Ok(mailparse::MailAddr::Group(mailparse::GroupInfo {
626 group_name: self.name,
627 addrs: self
628 .contacts
629 .into_iter()
630 .map(|c| c.try_into())
631 .collect::<Result<Vec<_>, Error>>()?,
632 }))
633 }
634}
635
636impl fmt::Display for Group {
637 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
638 write!(
639 f,
640 "\"{}\": {};",
641 self.name.replace('\\', "\\\\").replace('"', "\\\""),
642 self.contacts
643 )
644 }
645}
646
647#[derive(Debug, Clone)]
660pub enum AddressList {
661 Contacts(Contacts),
662 Group(Group),
663}
664
665impl AddressList {
666 pub fn is_group(&self) -> bool {
668 matches!(self, AddressList::Group(_))
669 }
670
671 pub fn group_name(&self) -> Option<&String> {
673 match self {
674 AddressList::Group(g) => Some(&g.name),
675 _ => None,
676 }
677 }
678
679 pub fn contacts(&self) -> &Contacts {
681 match self {
682 AddressList::Contacts(c) => c,
683 AddressList::Group(g) => &g.contacts,
684 }
685 }
686}
687
688impl fmt::Display for AddressList {
689 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
690 match self {
691 AddressList::Contacts(c) => write!(f, "{}", c),
692 AddressList::Group(g) => write!(f, "{}", g),
693 }
694 }
695}
696
697impl PartialEq for AddressList {
698 fn eq(&self, other: &AddressList) -> bool {
699 if self.is_group() != other.is_group() {
700 return false;
701 }
702 match self {
703 AddressList::Group(g) => {
704 if let AddressList::Group(o) = other {
705 g == o
706 } else {
707 false
708 }
709 }
710 AddressList::Contacts(c) => {
711 if let AddressList::Contacts(o) = other {
712 if c.len() != o.len() {
713 return false;
714 }
715 for (i, contact) in c.iter().enumerate() {
716 if contact != &o[i] {
717 return false;
718 }
719 }
720 true
721 } else {
722 false
723 }
724 }
725 }
726 }
727}
728
729impl DeepEq for AddressList {
730 fn deep_eq(&self, other: &AddressList) -> bool {
731 if self.is_group() != other.is_group() {
732 return false;
733 }
734 match self {
735 AddressList::Group(g) => {
736 if let AddressList::Group(o) = other {
737 g.deep_eq(o)
738 } else {
739 false
740 }
741 }
742 AddressList::Contacts(c) => {
743 if let AddressList::Contacts(o) = other {
744 if c.len() != o.len() {
745 return false;
746 }
747 for (i, contact) in c.iter().enumerate() {
748 if !contact.deep_eq(&o[i]) {
749 return false;
750 }
751 }
752 true
753 } else {
754 false
755 }
756 }
757 }
758 }
759}
760
761impl From<Vec<Contact>> for AddressList {
762 fn from(s: Vec<Contact>) -> Self {
763 Self::Contacts(Contacts { contacts: s })
764 }
765}
766
767impl Contactsish for AddressList {
768 fn len(&self) -> usize {
769 match self {
770 Self::Contacts(c) => c.len(),
771 Self::Group(g) => g.contacts.len(),
772 }
773 }
774
775 fn is_empty(&self) -> bool {
776 match self {
777 Self::Contacts(c) => c.is_empty(),
778 Self::Group(g) => g.contacts.is_empty(),
779 }
780 }
781
782 fn to_contacts(self) -> Contacts {
783 match self {
784 Self::Contacts(c) => c,
785 Self::Group(g) => g.contacts,
786 }
787 }
788
789 fn add<C>(&mut self, contact: C)
790 where
791 C: Contactish,
792 {
793 match self {
794 Self::Contacts(c) => c.add(contact),
795 Self::Group(g) => g.add(contact),
796 }
797 }
798
799 fn contains(&self, contact: &Contact) -> bool {
800 match self {
801 Self::Contacts(c) => c.contains(contact),
802 Self::Group(g) => g.contains(contact),
803 }
804 }
805}
806
807impl From<Contacts> for AddressList {
808 fn from(contacts: Contacts) -> Self {
809 Self::Contacts(contacts)
810 }
811}
812
813impl From<Group> for AddressList {
814 fn from(group: Group) -> AddressList {
815 Self::Group(group)
816 }
817}
818
819#[cfg(feature = "mailparse-conversions")]
820impl TryInto<Vec<mailparse::MailAddr>> for AddressList {
821 type Error = Error;
822
823 fn try_into(self) -> Result<Vec<mailparse::MailAddr>, Error> {
824 match self {
825 Self::Group(g) => Ok(vec![g.try_into()?]),
826 Self::Contacts(c) => c.into_iter().map(|ic| ic.try_into()).collect(),
827 }
828 }
829}