postal_api 0.2.1

A Rust implementation for the Postal mail delivery platform.
Documentation
//! This is an API Wrapper for the [Postal](https://postalserver.io/) Mail delivery service.
//! It takes inspiration from [postal-node](https://github.com/postalserver/postal-node), trying
//! to add additional validation into the MessageBuilder where possible.
//!
//! This is currently in early development and mainly for internal Use.
//!
//! ## Roadmap
//! - [ ] Messages
//!     - [ ] Builder for Raw Messages
//!     - [x] Builder for Messages
//!     - [ ] Attachments
//! - [ ] Send Messages
//!     - [x] Send Message
//!     - [ ] Send Raw Message
//! - [ ] Postal API Errors
//!     - [x] Auth Errors
//!     - [x] Message Errors
//!     - [ ] Raw Message Errors
//!
//! # Basic usage
//! ```no_run
//! use postal_api::PostalClient;
//! use postal_api::message::MessageBuilder;
//!
//! // create a postal client
//! let client = PostalClient::new(
//!     "http://example.com".to_string(),
//!     "my_super_secret_api_key".to_string()
//! );
//!
//! // set only things we require to send a message at all
//! let message = MessageBuilder::new()
//!     .add_to("someone@example.com")
//!     .unwrap()
//!     .set_from("me@example2.com")
//!     .set_plain_body("HELLO WORLD!")
//!     .build()
//!     .unwrap();
//!
//! client.send_message(message);
//! ```
use std::future::Future;

use response::PostalData;
use serde::Serialize;

pub mod error;
pub mod message;
pub mod response;

use crate::error::PostalClientError;
use crate::message::PostalMessage;
use crate::response::{MessagesData, PostalResponse};

#[derive(Debug, Clone)]
pub struct PostalClient {
    client: reqwest::Client,
    host: String,
    api_key: String,
}

impl PostalClient {
    /// Create a new PostalClient, creating a new [reqwest::Client]
    pub fn new(host: String, api_key: String) -> Self {
        let client = reqwest::Client::new();

        Self {
            client,
            host,
            api_key,
        }
    }

    fn send_request<'a, T: Serialize + 'a>(
        &'a self,
        controller: &'a str,
        action: &'a str,
        parameters: T,
    ) -> std::pin::Pin<Box<impl Future<Output = Result<MessagesData, PostalClientError>> + 'a>>
    {
        Box::pin(async move {
            let response = self
                .client
                .post(format!("{}/api/v1/{controller}/{action}", self.host))
                .json(&parameters)
                .header("X-Server-API-Key", &self.api_key)
                .send()
                .await?;

            let res_json: PostalResponse = response.json().await?;

            match res_json.data {
                PostalData::Success(res) => Ok(res),
                PostalData::Error(err) => Err(err.code.into()),
            }
        })
    }

    pub fn send_message<'a>(
        &'a self,
        message: PostalMessage<'a>,
    ) -> std::pin::Pin<Box<impl Future<Output = Result<MessagesData, PostalClientError>> + 'a>>
    {
        self.send_request("send", "message", message)
    }
}