avila_cell/
lib.rs

1//! # avila-cell
2//!
3//! **Células Digitais - Protocolos de Email**
4//!
5//! Assim como células são a primeira forma de vida que pode se comunicar,
6//! processar informações e se replicar, os protocolos de email (SMTP, POP3, IMAP)
7//! são as primeiras formas de "vida digital" que podem:
8//!
9//! - Comunicar (enviar/receber mensagens)
10//! - Processar (parse, validação, roteamento)
11//! - Persistir (armazenamento de mensagens)
12//!
13//! ## Protocolos Suportados
14//!
15//! - **SMTP** (Simple Mail Transfer Protocol) - Envio de emails
16//! - **POP3** (Post Office Protocol v3) - Recebimento de emails
17//! - **IMAP** (Internet Message Access Protocol) - Acesso a caixas de email
18//!
19//! ## Filosofia
20//!
21//! Email é a forma mais fundamental de comunicação digital assíncrona.
22//! É o "DNA" da internet - simples, robusto, descentralizado.
23
24#![warn(missing_docs)]
25
26use avila_error::{Error, ErrorKind, Result};
27
28pub mod smtp;
29pub mod pop3;
30pub mod imap;
31pub mod message;
32pub mod mime;
33pub mod encoding;
34pub mod auth;
35pub mod queue;
36pub mod template;
37pub mod dkim;
38pub mod pool;
39pub mod classifier;
40pub mod calendar;
41
42/// Library version
43pub const VERSION: &str = env!("CARGO_PKG_VERSION");
44
45/// Email address structure
46#[derive(Debug, Clone, PartialEq, Eq, Hash)]
47pub struct EmailAddress {
48    /// Parte local (antes do @)
49    pub local: String,
50    /// Domínio (depois do @)
51    pub domain: String,
52    /// Nome de exibição opcional
53    pub display_name: Option<String>,
54}
55
56impl EmailAddress {
57    /// Creates new email address
58    pub fn new(email: impl AsRef<str>) -> Result<Self> {
59        let email = email.as_ref();
60        let parts: Vec<&str> = email.split('@').collect();
61
62        if parts.len() != 2 {
63            return Err(Error::new(
64                ErrorKind::InvalidInput,
65                format!("Email inválido: {}", email),
66            ));
67        }
68
69        Ok(Self {
70            local: parts[0].to_string(),
71            domain: parts[1].to_string(),
72            display_name: None,
73        })
74    }
75
76    /// Converts to complete string
77    pub fn to_string(&self) -> String {
78        format!("{}@{}", self.local, self.domain)
79    }
80
81    /// Validates email format
82    pub fn is_valid(&self) -> bool {
83        !self.local.is_empty() && !self.domain.is_empty() && self.domain.contains('.')
84    }
85
86    /// Sets display name
87    pub fn with_name(mut self, name: impl Into<String>) -> Self {
88        self.display_name = Some(name.into());
89        self
90    }
91
92    /// Formats for RFC 5322 (with display name if present)
93    pub fn to_rfc5322(&self) -> String {
94        match &self.display_name {
95            Some(name) => format!("\"{}\" <{}@{}>", name, self.local, self.domain),
96            None => format!("{}@{}", self.local, self.domain),
97        }
98    }
99}
100
101impl std::fmt::Display for EmailAddress {
102    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103        write!(f, "{}@{}", self.local, self.domain)
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    #[test]
112    fn test_email_address() {
113        let email = EmailAddress::new("test@example.com").unwrap();
114        assert_eq!(email.local, "test");
115        assert_eq!(email.domain, "example.com");
116        assert!(email.is_valid());
117    }
118
119    #[test]
120    fn test_invalid_email() {
121        assert!(EmailAddress::new("invalid").is_err());
122        assert!(EmailAddress::new("no@domain").is_ok()); // Ok mas is_valid() = false
123    }
124}