use serde::{Deserialize, Serialize};
#[derive(Debug, Clone)]
pub enum Message {
Text {
body: String,
},
Template {
name: String,
language: String,
parameters: Vec<serde_json::Value>,
},
}
impl Message {
pub fn to_api_payload(&self, to: &str) -> serde_json::Value {
match self {
Message::Text { body } => serde_json::json!({
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": to,
"type": "text",
"text": { "body": body }
}),
Message::Template {
name,
language,
parameters,
} => serde_json::json!({
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": to,
"type": "template",
"template": {
"name": name,
"language": { "code": language },
"components": parameters
}
}),
}
}
}
#[derive(Debug, Clone)]
pub struct SendResult {
pub wamid: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", content = "phone")]
#[serde(rename_all = "snake_case")]
pub enum SenderIdentity {
Owner(String),
Customer(String),
}
impl SenderIdentity {
pub fn phone(&self) -> &str {
match self {
SenderIdentity::Owner(p) | SenderIdentity::Customer(p) => p,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum DeliveryStatus {
Sent,
Delivered,
Read,
Failed,
#[serde(other)]
Unknown,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn text_message_produces_correct_meta_api_json() {
let msg = Message::Text {
body: "Hello World".into(),
};
let payload = msg.to_api_payload("393401234567");
assert_eq!(payload["messaging_product"], "whatsapp");
assert_eq!(payload["recipient_type"], "individual");
assert_eq!(payload["to"], "393401234567");
assert_eq!(payload["type"], "text");
assert_eq!(payload["text"]["body"], "Hello World");
}
#[test]
fn template_message_produces_correct_meta_api_json() {
let params = vec![
serde_json::json!({"type": "text", "text": "Alberto"}),
serde_json::json!({"type": "text", "text": "123456"}),
];
let msg = Message::Template {
name: "order_confirmation".into(),
language: "en_US".into(),
parameters: params.clone(),
};
let payload = msg.to_api_payload("393401234567");
assert_eq!(payload["messaging_product"], "whatsapp");
assert_eq!(payload["recipient_type"], "individual");
assert_eq!(payload["to"], "393401234567");
assert_eq!(payload["type"], "template");
assert_eq!(payload["template"]["name"], "order_confirmation");
assert_eq!(payload["template"]["language"]["code"], "en_US");
assert_eq!(payload["template"]["components"], serde_json::json!(params));
}
#[test]
fn sender_identity_owner_carries_phone_number() {
let identity = SenderIdentity::Owner("393401234567".into());
assert_eq!(identity.phone(), "393401234567");
assert!(matches!(identity, SenderIdentity::Owner(_)));
}
#[test]
fn sender_identity_customer_carries_phone_number() {
let identity = SenderIdentity::Customer("393409999999".into());
assert_eq!(identity.phone(), "393409999999");
assert!(matches!(identity, SenderIdentity::Customer(_)));
}
#[test]
fn delivery_status_deserializes_known_variants() {
assert_eq!(
serde_json::from_str::<DeliveryStatus>("\"sent\"").unwrap(),
DeliveryStatus::Sent
);
assert_eq!(
serde_json::from_str::<DeliveryStatus>("\"delivered\"").unwrap(),
DeliveryStatus::Delivered
);
assert_eq!(
serde_json::from_str::<DeliveryStatus>("\"read\"").unwrap(),
DeliveryStatus::Read
);
assert_eq!(
serde_json::from_str::<DeliveryStatus>("\"failed\"").unwrap(),
DeliveryStatus::Failed
);
}
#[test]
fn delivery_status_deserializes_unknown_variant() {
let status: DeliveryStatus = serde_json::from_str("\"expired\"").unwrap();
assert_eq!(status, DeliveryStatus::Unknown);
}
}