#![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]
mod client;
mod types;
mod util;
#[cfg(feature = "webhook-verify")]
mod verify;
pub use types::*;
#[cfg(feature = "webhook-verify")]
#[cfg_attr(docsrs, doc(cfg(feature = "webhook-verify")))]
pub use verify::*;
pub use client::scope::{
ApiKeys, Domains, Drafts, InboxScope, Inboxes, Lists, Metrics, OrgScope, PodScope, Scope,
Scoped, Threads, Webhooks,
};
pub const DEFAULT_BASE_URL: &str = "https://api.agentmail.to";
pub const DEFAULT_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(30);
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("AGENTMAIL_API_KEY is not set")]
MissingApiKey,
#[error("transport error: {0}")]
Transport(#[from] reqwest::Error),
#[error("AgentMail answered {status}: {body}")]
Api {
status: reqwest::StatusCode,
body: String,
},
#[error("undecodable AgentMail response ({reason}): {body}")]
Decode {
reason: String,
body: String,
},
#[error("attachment has no download_url")]
NoDownloadUrl,
}
#[cfg(feature = "retries")]
#[cfg_attr(docsrs, doc(cfg(feature = "retries")))]
#[derive(Clone, Debug)]
pub struct RetryPolicy {
pub max_retries: u32,
pub base_delay: std::time::Duration,
pub max_delay: std::time::Duration,
}
#[cfg(feature = "retries")]
impl Default for RetryPolicy {
fn default() -> Self {
RetryPolicy {
max_retries: 2,
base_delay: std::time::Duration::from_millis(500),
max_delay: std::time::Duration::from_secs(8),
}
}
}
pub struct Client {
http: reqwest::Client,
base_url: String,
api_key: String,
#[cfg(feature = "retries")]
retry_policy: RetryPolicy,
}
impl std::fmt::Debug for Client {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Client")
.field("base_url", &self.base_url)
.field("api_key", &"[redacted]")
.finish_non_exhaustive()
}
}
impl Client {
pub fn new(api_key: impl Into<String>, base_url: impl Into<String>) -> Self {
let _ = rustls::crypto::ring::default_provider().install_default();
Client {
http: reqwest::Client::builder()
.timeout(DEFAULT_TIMEOUT)
.build()
.expect("reqwest client"),
base_url: base_url.into().trim_end_matches('/').to_string(),
api_key: api_key.into(),
#[cfg(feature = "retries")]
retry_policy: RetryPolicy::default(),
}
}
pub fn from_env() -> Result<Self, Error> {
let key = std::env::var("AGENTMAIL_API_KEY").map_err(|_| Error::MissingApiKey)?;
let base =
std::env::var("AGENTMAIL_BASE_URL").unwrap_or_else(|_| DEFAULT_BASE_URL.to_string());
Ok(Self::new(key, base))
}
#[cfg(feature = "retries")]
#[cfg_attr(docsrs, doc(cfg(feature = "retries")))]
pub fn with_retry_policy(mut self, policy: RetryPolicy) -> Self {
self.retry_policy = policy;
self
}
}