parcelwing 0.1.0

Official Rust SDK for the Parcel Wing API.
Documentation
use std::sync::Arc;

use crate::error::Error;
use crate::http::HttpClient;
use crate::types::{
    contact_sort_by_to_string, sort_order_to_string, BatchCreateContactsData,
    BatchCreateContactsResult, Contact, ContactCreateRequest, ContactListParams, ContactStatus,
    ContactUpdateRequest, DeletionResponse, ListResponse, ResourceResponse,
};

#[derive(Clone, Debug)]
pub struct ContactsResource {
    http: Arc<HttpClient>,
}

impl ContactsResource {
    pub(crate) fn new(http: Arc<HttpClient>) -> Self {
        Self { http }
    }

    pub async fn list(&self, params: ContactListParams) -> Result<ListResponse<Contact>, Error> {
        self.http
            .get("/api/contacts", Some(contact_query(params)))
            .await
    }

    pub async fn create(&self, input: ContactCreateRequest) -> Result<Contact, Error> {
        let response: ResourceResponse<Contact> = self.http.post("/api/contacts", &input).await?;
        Ok(response.data)
    }

    pub async fn create_batch(
        &self,
        input: Vec<ContactCreateRequest>,
    ) -> Result<BatchCreateContactsData, Error> {
        let response: BatchCreateContactsResult = self.http.post("/api/contacts", &input).await?;
        Ok(response.data)
    }

    pub async fn get(&self, id: impl AsRef<str>) -> Result<Contact, Error> {
        let response: ResourceResponse<Contact> = self
            .http
            .get(&format!("/api/contacts/{}", encode(id.as_ref())), None)
            .await?;

        Ok(response.data)
    }

    pub async fn update(
        &self,
        id: impl AsRef<str>,
        input: ContactUpdateRequest,
    ) -> Result<Contact, Error> {
        let response: ResourceResponse<Contact> = self
            .http
            .patch(&format!("/api/contacts/{}", encode(id.as_ref())), &input)
            .await?;

        Ok(response.data)
    }

    pub async fn delete(&self, id: impl AsRef<str>) -> Result<DeletionResponse, Error> {
        self.http
            .delete(&format!("/api/contacts/{}", encode(id.as_ref())))
            .await
    }
}

fn contact_query(params: ContactListParams) -> Vec<(&'static str, String)> {
    let mut query = Vec::new();

    if let Some(value) = params.page {
        query.push(("page", value.to_string()));
    }
    if let Some(value) = params.limit {
        query.push(("limit", value.to_string()));
    }
    if let Some(value) = params.status {
        query.push(("status", contact_status_to_string(&value).to_string()));
    }
    if let Some(value) = params.search {
        query.push(("search", value));
    }
    if let Some(value) = params.external_id {
        query.push(("external_id", value));
    }
    if let Some(value) = params.sort_by {
        query.push(("sort_by", contact_sort_by_to_string(&value).to_string()));
    }
    if let Some(value) = params.sort_order {
        query.push(("sort_order", sort_order_to_string(&value).to_string()));
    }

    query
}

fn contact_status_to_string(value: &ContactStatus) -> &'static str {
    match value {
        ContactStatus::Active => "active",
        ContactStatus::Unsubscribed => "unsubscribed",
        ContactStatus::Bounced => "bounced",
        ContactStatus::SpamComplaint => "spam_complaint",
        ContactStatus::Inactive => "inactive",
    }
}

fn encode(value: &str) -> String {
    url::form_urlencoded::byte_serialize(value.as_bytes()).collect()
}