Skip to main content

resend_rs/
idempotent.rs

1//! A few helpers for adding idempotency keys to requests that support them.
2//!
3//! ### Example
4//!
5//! ```rust,no_run
6//! use resend_rs::{idempotent::IdempotentTrait, types::CreateEmailBaseOptions};
7//! use resend_rs::{Resend, Result};
8//!
9//! #[tokio::main]
10//! async fn main() -> Result<()> {
11//!   let resend = Resend::new("re_123456789");
12//!
13//!   let emails = vec![
14//!     CreateEmailBaseOptions::new(
15//!       "Acme <onboarding@resend.dev>",
16//!       vec!["foo@gmail.com"],
17//!      "hello world",
18//!    )
19//!    .with_html("<h1>it works!</h1>"),
20//!    CreateEmailBaseOptions::new(
21//!      "Acme <onboarding@resend.dev>",
22//!      vec!["bar@outlook.com"],
23//!      "world hello",
24//!    )
25//!    .with_html("<p>it works!</p>"),
26//!  ].with_idempotency_key("welcome-user/123456789");
27//!
28//!  let _emails = resend.batch.send(emails).await?;
29//!
30//!  Ok(())
31//!}
32//! ```
33use crate::types::CreateEmailBaseOptions;
34
35/// Wrapper struct for adding an `idempotency_key` header to data `T`.
36#[derive(Debug, Clone, serde::Serialize)]
37pub struct Idempotent<T> {
38    #[serde(skip)]
39    pub(crate) idempotency_key: Option<String>,
40    #[serde(flatten)]
41    pub(crate) data: T,
42}
43
44/// Implements `From<inner>` only works for concrete types.
45macro_rules! idempotent_from {
46    ($inner:ty) => {
47        impl From<$inner> for Idempotent<$inner> {
48            fn from(value: $inner) -> Self {
49                Self {
50                    idempotency_key: None,
51                    data: value,
52                }
53            }
54        }
55    };
56}
57
58idempotent_from!(CreateEmailBaseOptions);
59idempotent_from!(Vec<CreateEmailBaseOptions>);
60
61/// Used to add easy conversion of trait impls to [`Idempotent`].
62pub trait IdempotentTrait<T> {
63    /// Adds an `Idempotency-Key` header to the request.
64    fn with_idempotency_key(self, idempotency_key: &str) -> Idempotent<T>;
65}
66
67impl<T> IdempotentTrait<Self> for T
68where
69    T: IntoIterator<Item = CreateEmailBaseOptions> + Send,
70{
71    fn with_idempotency_key(self, idempotency_key: &str) -> Idempotent<Self> {
72        Idempotent {
73            idempotency_key: Some(idempotency_key.to_owned()),
74            data: self,
75        }
76    }
77}