ephemeral_email 0.2.0

A Rust library for generating temporary email addresses.
Documentation
use std::{sync::Arc, vec};

use crate::client::Client;
use crate::{Domain, InboxCreationError, Message, MessageFetcherError};
use futures::lock::Mutex;

use super::{Inbox, Provider, ProviderType};

pub(crate) struct FakeMailNetProvider {}

pub(crate) struct FakeMailNetMessageFetcher {
    client: Client,
}

impl FakeMailNetProvider {
    pub fn new() -> Self {
        Self {}
    }
}

#[derive(serde::Deserialize)]
struct IndexResponse {
    email: String,
}

#[derive(serde::Deserialize)]
struct EmailListEntry {
    #[serde(rename = "predmet")]
    subject: String,
    #[serde(rename = "od")]
    from: String,
}

async fn get_csrf_token(client: &Client) -> Result<String, InboxCreationError> {
    let response = client
        .get("https://www.fakemail.net/")
        .send()
        .await?
        .text()
        .await?;
    let csrf_token = response
        .split_once("CSRF=\"")
        .ok_or(InboxCreationError::CreationError(
            "Failed to find CSRF token".to_string(),
        ))?
        .1
        .split_once("\"")
        .ok_or(InboxCreationError::CreationError(
            "Failed to extract CSRF token".to_string(),
        ))?
        .0;
    Ok(csrf_token.to_string())
}

#[async_trait::async_trait]
impl Provider for FakeMailNetProvider {
    async fn new_random_inbox_from_name(
        &mut self,
        name: &str,
    ) -> Result<Inbox, InboxCreationError> {
        let client = Client::builder().cookie_store(true).build()?;
        let csrf_token = get_csrf_token(&client).await?;
        let response = client
            .get("https://www.fakemail.net/index/index")
            .header("X-Requested-With", "XMLHttpRequest")
            .query(&[("csrf_token", csrf_token)])
            .send()
            .await?;
        if !response.status().is_success() {
            return Err(InboxCreationError::CreationError(
                response.text().await.unwrap_or_default(),
            ));
        }
        let check_response = client
            .post("https://www.fakemail.net/index/email-check")
            .header("X-Requested-With", "XMLHttpRequest")
            .form(&[("email", name), ("format", "json")])
            .send()
            .await?
            .text()
            .await?;
        if check_response != "\"ok\"" {
            return Err(InboxCreationError::NameTaken(name.into()));
        }

        let create_response = client
            .post("https://www.fakemail.net/index/new-email")
            .header("X-Requested-With", "XMLHttpRequest")
            .form(&[("emailInput", name), ("format", "json")])
            .send()
            .await?
            .text()
            .await?;
        if create_response != "\"ok\"" {
            return Err(InboxCreationError::CreationError(create_response));
        }
        let csrf_token = get_csrf_token(&client).await?;
        let response = client
            .get("https://www.fakemail.net/index/index")
            .header("X-Requested-With", "XMLHttpRequest")
            .query(&[("csrf_token", csrf_token)])
            .send()
            .await?
            .text()
            .await?;
        let response = response.trim();
        let index_response: IndexResponse = serde_json::from_str(response.trim()).map_err(|e| {
            InboxCreationError::CreationError(format!("Failed to parse JSON: {}", e))
        })?;
        Ok(Inbox {
            message_fetcher: Arc::new(Mutex::new(FakeMailNetMessageFetcher { client })),
            email_address: index_response.email.parse()?,
        })
    }

    fn get_provider_type(&self) -> ProviderType {
        ProviderType::FakeMailNet
    }

    fn get_domains(&self) -> Vec<Domain> {
        vec![]
    }
}

#[async_trait::async_trait]
impl super::MessageFetcher for FakeMailNetMessageFetcher {
    async fn fetch_messages(&mut self) -> Result<Vec<Message>, MessageFetcherError> {
        let email_list_response = self
            .client
            .get("https://www.fakemail.net/index/refresh")
            .header("X-Requested-With", "XMLHttpRequest")
            .send()
            .await?
            .text()
            .await?;
        let email_list: Vec<EmailListEntry> = serde_json::from_str(email_list_response.trim())
            .map_err(|e| MessageFetcherError::FetchError(format!("Failed to parse JSON: {}", e)))?;

        let mut messages = Vec::new();
        for (i, email) in email_list.iter().rev().enumerate().skip(1) {
            let email_content = self
                .client
                .get(format!("https://www.fakemail.net/email/id/{}", i + 1))
                .header("X-Requested-With", "XMLHttpRequest")
                .send()
                .await?
                .text()
                .await?;
            messages.push(Message {
                from: email.from.clone(),
                subject: email.subject.clone(),
                body: email_content,
            });
        }
        Ok(messages)
    }
}