myc_core/domain/dtos/
email.rs

1use base64::{engine::general_purpose, Engine};
2use mycelium_base::utils::errors::{invalid_arg_err, MappedErrors};
3use regex::Regex;
4use serde::{Deserialize, Serialize};
5use utoipa::ToSchema;
6
7#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
8#[serde(rename_all = "camelCase")]
9pub struct Email {
10    pub username: String,
11    pub domain: String,
12}
13
14impl Email {
15    pub fn from_string(email: String) -> Result<Email, MappedErrors> {
16        let re = Regex::new(
17            r"^([a-zA-Z0-9_\-+]([a-zA-Z0-9_\-+.]*[a-zA-Z0-9_+])?)@([a-zA-Z0-9.-]+\.[a-zA-Z]{1,})"
18        ).unwrap();
19
20        let cap = match re.captures(email.as_str()) {
21            None => {
22                return invalid_arg_err(format!(
23                    "Invalid Email format: {:?}",
24                    email.to_owned()
25                ))
26                .as_error();
27            }
28            Some(res) => res,
29        };
30
31        let username = match cap.get(1) {
32            None => {
33                return invalid_arg_err("Invalid Email username.".to_string())
34                    .as_error();
35            }
36            Some(val) => val.as_str().to_string(),
37        };
38
39        let domain = match cap.get(3) {
40            None => {
41                return invalid_arg_err("Invalid Email domain.".to_string())
42                    .as_error();
43            }
44            Some(val) => val.as_str().to_string(),
45        };
46
47        Ok(Email { username, domain })
48    }
49
50    pub fn email(&self) -> String {
51        format!(
52            "{}@{}",
53            self.username.to_lowercase(),
54            self.domain.to_lowercase()
55        )
56    }
57
58    /// Get redacted email
59    ///
60    /// Return only the first and last letters o the email.username and the
61    /// domain
62    ///
63    pub fn redacted_email(&self) -> String {
64        Self::redact_email(&self.email())
65    }
66
67    /// Get encoded email
68    ///
69    /// Return the email encoded in base64
70    ///
71    pub fn encoded_email(&self) -> String {
72        general_purpose::STANDARD.encode(self.email().as_bytes())
73    }
74
75    /// Effectively redact the email
76    ///
77    /// This is a static function used to expose the functionality
78    ///
79    pub fn redact_email(email: &str) -> String {
80        let email = match Email::from_string(email.to_string()) {
81            Ok(email) => email,
82            Err(e) => {
83                tracing::warn!("Invalid Email format: {:?}", e);
84                return email.to_owned();
85            }
86        };
87
88        let binding = email.username.to_lowercase();
89        let username = binding.chars();
90        let domain = email.domain.to_lowercase();
91
92        let username_redacted = format!(
93            "{}{}{}",
94            username.to_owned().next().unwrap(),
95            "*".repeat(3),
96            username.last().unwrap()
97        );
98
99        format!("{}@{}", username_redacted, domain)
100    }
101}
102
103impl ToString for Email {
104    fn to_string(&self) -> String {
105        self.email()
106    }
107}
108
109// ? --------------------------------------------------------------------------
110// ? TESTS
111// ? --------------------------------------------------------------------------
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn test_parse_email_works() {
119        let email_string = "sgelias@outlook.com".to_string();
120
121        let email = Email::from_string(email_string.to_owned()).unwrap();
122
123        assert_eq!(email.username, "sgelias".to_string());
124        assert_eq!(email.domain, "outlook.com".to_string());
125    }
126
127    #[test]
128    fn test_get_email_works() {
129        for (is_valid, email_string) in vec![
130            (true, "mycelium-default-users@biotrop.com.br".to_string()),
131            (true, "myceliumDefaultUsers@biotrop.com.br".to_string()),
132            (true, "mycelium-default-users@biotrop.com".to_string()),
133            (true, "myceliumDefaultUsers@biotrop.com".to_string()),
134            (false, "mycelium-default-users@biotrop".to_string()),
135            (false, "myceliumDefaultUsers@biotrop".to_string()),
136        ] {
137            let email = Email::from_string(email_string.to_owned());
138
139            if is_valid {
140                assert_eq!(email.unwrap().email(), email_string.to_lowercase());
141            } else {
142                assert!(email.is_err());
143            }
144        }
145    }
146}