use std::fmt;
use clap::ValueEnum;
use dialoguer::{Select, theme::ColorfulTheme};
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum Provider {
Gmail,
Fastmail,
Icloud,
Proton,
Custom,
}
const DOMAIN_TABLE: &[(&str, Provider)] = &[
("gmail.com", Provider::Gmail),
("googlemail.com", Provider::Gmail),
("fastmail.com", Provider::Fastmail),
("fastmail.fm", Provider::Fastmail),
("icloud.com", Provider::Icloud),
("me.com", Provider::Icloud),
("mac.com", Provider::Icloud),
];
impl Provider {
pub fn detect(email: &str) -> Option<Provider> {
let domain = email.rsplit('@').next()?.to_ascii_lowercase();
DOMAIN_TABLE
.iter()
.find(|(known, _)| *known == domain)
.map(|(_, provider)| *provider)
}
pub fn default_host_port(&self) -> Option<(&'static str, u16)> {
match self {
Provider::Gmail => Some(("imap.gmail.com", 993)),
Provider::Fastmail => Some(("imap.fastmail.com", 993)),
Provider::Icloud => Some(("imap.mail.me.com", 993)),
Provider::Proton => Some(("127.0.0.1", 1143)),
Provider::Custom => None,
}
}
pub fn accepts_invalid_certs(&self) -> bool {
matches!(self, Provider::Proton)
}
pub fn prompt_select() -> std::io::Result<Provider> {
let options = [
Provider::Gmail,
Provider::Fastmail,
Provider::Icloud,
Provider::Proton,
Provider::Custom,
];
let labels: Vec<String> = options.iter().map(|p| p.to_string()).collect();
let selection = Select::with_theme(&ColorfulTheme::default())
.with_prompt("Select a provider")
.items(&labels)
.default(0)
.interact()?;
Ok(options[selection])
}
}
impl fmt::Display for Provider {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
Provider::Gmail => "gmail",
Provider::Fastmail => "fastmail",
Provider::Icloud => "icloud",
Provider::Proton => "proton",
Provider::Custom => "custom",
};
f.write_str(name)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn detects_known_domains() {
assert_eq!(
Provider::detect("first.last@gmail.com"),
Some(Provider::Gmail)
);
assert_eq!(
Provider::detect("first.last@fastmail.fm"),
Some(Provider::Fastmail)
);
assert_eq!(
Provider::detect("first.last@me.com"),
Some(Provider::Icloud)
);
}
#[test]
fn unrecognized_domain_is_none() {
assert_eq!(Provider::detect("first.last@example.com"), None);
}
#[test]
fn custom_has_no_default_host_port() {
assert_eq!(Provider::Custom.default_host_port(), None);
}
#[test]
fn only_proton_accepts_invalid_certs() {
assert!(Provider::Proton.accepts_invalid_certs());
assert!(!Provider::Gmail.accepts_invalid_certs());
assert!(!Provider::Custom.accepts_invalid_certs());
}
}