restmail_client/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use error::RestmailClientError;
use serde_json::Value as EmailJson;
use url::Url;
use viaduct::Request;

mod error;

type Result<T> = std::result::Result<T, RestmailClientError>;

/// For a given restmail email, find the first email that satisfies the given predicate.
/// If no email is found, this function sleeps for a few seconds then tries again, up
/// to `max_tries` times.
pub fn find_email<F>(email: &str, predicate: F, max_tries: u8) -> Result<EmailJson>
where
    F: Fn(&EmailJson) -> bool,
{
    let mail_url = url_for_email(email)?;
    log::info!("Checking {} up to {} times.", email, max_tries);
    for i in 0..max_tries {
        let resp: Vec<serde_json::Value> = Request::get(mail_url.clone()).send()?.json()?;
        let mut matching_emails: Vec<serde_json::Value> =
            resp.into_iter().filter(|email| predicate(email)).collect();

        if matching_emails.is_empty() {
            log::info!(
                "Failed to find matching email. Waiting {} seconds and retrying.",
                i + 1
            );
            std::thread::sleep(std::time::Duration::from_secs((i + 1).into()));
            continue;
        }

        if matching_emails.len() > 1 {
            log::info!(
                "Found {} emails that applies (taking latest)",
                matching_emails.len()
            );
            matching_emails.sort_by(|a, b| {
                let a_time = a["receivedAt"].as_u64();
                let b_time = b["receivedAt"].as_u64();
                match (a_time, b_time) {
                    (Some(a_time), Some(b_time)) => b_time.cmp(&a_time),
                    _ => {
                        log::warn!(
                            "Could not de-serialize receivedAt for at least one of the emails."
                        );
                        std::cmp::Ordering::Equal
                    }
                }
            })
        }
        return Ok(matching_emails[0].clone());
    }
    log::info!("Error: Failed to find email after {} tries!", max_tries);
    Err(RestmailClientError::HitRetryMax)
}

pub fn clear_mailbox(email: &str) -> Result<()> {
    let mail_url = url_for_email(email)?;
    log::info!("Clearing restmail for {}.", email);
    Request::delete(mail_url).send()?;
    Ok(())
}

fn username_from_email(email: &str) -> Result<String> {
    let user = email.replace("@restmail.net", "");
    if user.len() == email.len() {
        return Err(RestmailClientError::NotARestmailEmail);
    }
    Ok(user)
}

fn url_for_email(email: &str) -> Result<Url> {
    let restmail_user = username_from_email(email)?;
    let path = format!("/mail/{}", restmail_user);
    Ok(Url::parse("https://restmail.net")?.join(&path)?)
}