restmail_client/
lib.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use error::RestmailClientError;
6use error_support::{info, warn};
7use serde_json::Value as EmailJson;
8use url::Url;
9use viaduct::Request;
10
11mod error;
12
13type Result<T> = std::result::Result<T, RestmailClientError>;
14
15/// For a given restmail email, find the first email that satisfies the given predicate.
16/// If no email is found, this function sleeps for a few seconds then tries again, up
17/// to `max_tries` times.
18pub fn find_email<F>(email: &str, predicate: F, max_tries: u8) -> Result<EmailJson>
19where
20    F: Fn(&EmailJson) -> bool,
21{
22    let mail_url = url_for_email(email)?;
23    info!("Checking {} up to {} times.", email, max_tries);
24    for i in 0..max_tries {
25        let resp: Vec<serde_json::Value> = Request::get(mail_url.clone()).send()?.json()?;
26        let mut matching_emails: Vec<serde_json::Value> =
27            resp.into_iter().filter(|email| predicate(email)).collect();
28
29        if matching_emails.is_empty() {
30            info!(
31                "Failed to find matching email. Waiting {} seconds and retrying.",
32                i + 1
33            );
34            std::thread::sleep(std::time::Duration::from_secs((i + 1).into()));
35            continue;
36        }
37
38        if matching_emails.len() > 1 {
39            info!(
40                "Found {} emails that applies (taking latest)",
41                matching_emails.len()
42            );
43            matching_emails.sort_by(|a, b| {
44                let a_time = a["receivedAt"].as_u64();
45                let b_time = b["receivedAt"].as_u64();
46                match (a_time, b_time) {
47                    (Some(a_time), Some(b_time)) => b_time.cmp(&a_time),
48                    _ => {
49                        warn!("Could not de-serialize receivedAt for at least one of the emails.");
50                        std::cmp::Ordering::Equal
51                    }
52                }
53            })
54        }
55        return Ok(matching_emails[0].clone());
56    }
57    info!("Error: Failed to find email after {} tries!", max_tries);
58    Err(RestmailClientError::HitRetryMax)
59}
60
61pub fn clear_mailbox(email: &str) -> Result<()> {
62    let mail_url = url_for_email(email)?;
63    info!("Clearing restmail for {}.", email);
64    Request::delete(mail_url).send()?;
65    Ok(())
66}
67
68fn username_from_email(email: &str) -> Result<String> {
69    let user = email.replace("@restmail.net", "");
70    if user.len() == email.len() {
71        return Err(RestmailClientError::NotARestmailEmail);
72    }
73    Ok(user)
74}
75
76fn url_for_email(email: &str) -> Result<Url> {
77    let restmail_user = username_from_email(email)?;
78    let path = format!("/mail/{}", restmail_user);
79    Ok(Url::parse("https://restmail.net")?.join(&path)?)
80}