use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(untagged)]
pub enum Recipients {
One(String),
Many(Vec<String>),
}
impl Default for Recipients {
fn default() -> Self {
Recipients::Many(Vec::new())
}
}
impl From<&str> for Recipients {
fn from(value: &str) -> Self {
Recipients::One(value.to_string())
}
}
impl From<String> for Recipients {
fn from(value: String) -> Self {
Recipients::One(value)
}
}
impl From<Vec<String>> for Recipients {
fn from(value: Vec<String>) -> Self {
Recipients::Many(value)
}
}
impl From<Vec<&str>> for Recipients {
fn from(value: Vec<&str>) -> Self {
Recipients::Many(value.into_iter().map(String::from).collect())
}
}
impl<const N: usize> From<[&str; N]> for Recipients {
fn from(value: [&str; N]) -> Self {
Recipients::Many(value.iter().map(|s| s.to_string()).collect())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Tag {
pub name: String,
pub value: String,
}
#[derive(Debug, Clone, Default)]
pub struct ListOptions {
pub limit: Option<u32>,
pub after: Option<String>,
pub before: Option<String>,
}
impl ListOptions {
pub(crate) fn to_query(&self) -> Vec<(&'static str, String)> {
let mut query = Vec::new();
if let Some(limit) = self.limit {
query.push(("limit", limit.to_string()));
}
if let Some(after) = &self.after {
query.push(("after", after.clone()));
}
if let Some(before) = &self.before {
query.push(("before", before.clone()));
}
query
}
}
pub(crate) fn list_query(options: Option<&ListOptions>) -> Vec<(&'static str, String)> {
options.map(ListOptions::to_query).unwrap_or_default()
}
#[derive(Debug, Clone, Deserialize)]
pub struct List<T> {
pub object: String,
pub data: Vec<T>,
pub has_more: bool,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct SendEmailOptions {
pub from: String,
pub to: Recipients,
pub subject: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub html: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub text: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cc: Option<Recipients>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bcc: Option<Recipients>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reply_to: Option<Recipients>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scheduled_at: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<Tag>>,
}
impl SendEmailOptions {
pub fn new(
from: impl Into<String>,
to: impl Into<Recipients>,
subject: impl Into<String>,
) -> Self {
SendEmailOptions {
from: from.into(),
to: to.into(),
subject: subject.into(),
..Default::default()
}
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct CreateEmailResponse {
pub id: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct Email {
pub object: String,
pub id: String,
pub from: String,
pub to: Vec<String>,
pub cc: Option<Vec<String>>,
pub bcc: Option<Vec<String>>,
pub reply_to: Option<Vec<String>>,
pub subject: String,
pub html: Option<String>,
pub text: Option<String>,
pub created_at: String,
pub scheduled_at: Option<String>,
pub message_id: String,
pub last_event: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct CancelEmailResponse {
pub object: String,
pub id: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BatchResponse {
pub data: Vec<CreateEmailResponse>,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct CreateContactOptions {
pub email: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub first_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub unsubscribed: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub properties: Option<HashMap<String, serde_json::Value>>,
}
impl CreateContactOptions {
pub fn new(email: impl Into<String>) -> Self {
CreateContactOptions {
email: email.into(),
..Default::default()
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ContactAddress {
pub id: Option<String>,
pub email: Option<String>,
}
impl ContactAddress {
pub fn id(id: impl Into<String>) -> Self {
ContactAddress {
id: Some(id.into()),
..Default::default()
}
}
pub fn email(email: impl Into<String>) -> Self {
ContactAddress {
email: Some(email.into()),
..Default::default()
}
}
pub(crate) fn key(&self) -> &str {
self.email.as_deref().or(self.id.as_deref()).unwrap_or("")
}
}
impl From<&str> for ContactAddress {
fn from(value: &str) -> Self {
ContactAddress::id(value)
}
}
impl From<String> for ContactAddress {
fn from(value: String) -> Self {
ContactAddress::id(value)
}
}
impl From<&String> for ContactAddress {
fn from(value: &String) -> Self {
ContactAddress::id(value.clone())
}
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct UpdateContactOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub first_name: Option<Option<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_name: Option<Option<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub unsubscribed: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub properties: Option<HashMap<String, serde_json::Value>>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ContactId {
pub object: String,
pub id: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct Contact {
pub object: String,
pub id: String,
pub email: String,
pub first_name: Option<String>,
pub last_name: Option<String>,
pub created_at: String,
pub unsubscribed: bool,
#[serde(default)]
pub properties: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ContactListItem {
pub id: String,
pub email: String,
pub first_name: Option<String>,
pub last_name: Option<String>,
pub created_at: String,
pub unsubscribed: bool,
}
#[derive(Debug, Clone, Deserialize)]
pub struct DeleteContactResponse {
pub object: String,
pub contact: String,
pub deleted: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TopicSubscription {
OptIn,
OptOut,
}
#[derive(Debug, Clone, Serialize)]
pub struct ContactTopicUpdate {
pub id: String,
pub subscription: TopicSubscription,
}
#[derive(Debug, Clone, Deserialize)]
pub struct UpdateContactTopicsResponse {
pub id: String,
}
#[derive(Debug, Clone, Serialize)]
pub struct CreateTopicOptions {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub default_subscription: TopicSubscription,
}
impl CreateTopicOptions {
pub fn new(name: impl Into<String>, default_subscription: TopicSubscription) -> Self {
CreateTopicOptions {
name: name.into(),
description: None,
default_subscription,
}
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct Topic {
pub id: String,
pub name: String,
#[serde(default)]
pub description: Option<String>,
pub default_subscription: TopicSubscription,
pub created_at: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct TopicId {
pub id: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct TopicList {
pub data: Vec<Topic>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct DeleteTopicResponse {
pub id: String,
pub object: String,
pub deleted: bool,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct CreateBroadcastOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub segment_id: Option<String>,
pub from: String,
pub subject: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub html: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub text: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reply_to: Option<Recipients>,
#[serde(skip_serializing_if = "Option::is_none")]
pub topic_id: Option<String>,
}
impl CreateBroadcastOptions {
pub fn new(from: impl Into<String>, subject: impl Into<String>) -> Self {
CreateBroadcastOptions {
from: from.into(),
subject: subject.into(),
..Default::default()
}
}
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct UpdateBroadcastOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub segment_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub from: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subject: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub html: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub text: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reply_to: Option<Recipients>,
#[serde(skip_serializing_if = "Option::is_none")]
pub topic_id: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BroadcastId {
pub id: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BroadcastListItem {
pub id: String,
pub name: Option<String>,
pub segment_id: Option<String>,
pub status: String,
pub created_at: String,
pub scheduled_at: Option<String>,
pub sent_at: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct Broadcast {
pub object: String,
pub id: String,
pub name: Option<String>,
pub segment_id: Option<String>,
pub status: String,
pub created_at: String,
pub scheduled_at: Option<String>,
pub sent_at: Option<String>,
pub from: String,
pub subject: String,
pub reply_to: Option<Vec<String>>,
pub preview_text: Option<String>,
pub topic_id: Option<String>,
pub html: Option<String>,
pub text: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct CancelBroadcastResponse {
pub object: String,
pub id: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct DeleteBroadcastResponse {
pub object: String,
pub id: String,
pub deleted: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SegmentMatch {
All,
Any,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SegmentCondition {
pub field: String,
pub op: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub value: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SegmentFilter {
#[serde(rename = "match")]
pub match_: SegmentMatch,
pub conditions: Vec<SegmentCondition>,
}
#[derive(Debug, Clone, Serialize)]
pub struct CreateSegmentOptions {
pub name: String,
pub filter: SegmentFilter,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct UpdateSegmentOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter: Option<SegmentFilter>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct Segment {
pub object: String,
pub id: String,
pub name: String,
pub filter: SegmentFilter,
pub created_at: String,
#[serde(default)]
pub contact_count: Option<u64>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct DeleteSegmentResponse {
pub object: String,
pub id: String,
pub deleted: bool,
}