ferro_whatsapp/
message.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone)]
7pub enum Message {
8 Text {
10 body: String,
12 },
13 Template {
19 name: String,
21 language: String,
23 parameters: Vec<serde_json::Value>,
25 },
26}
27
28impl Message {
29 pub fn to_api_payload(&self, to: &str) -> serde_json::Value {
33 match self {
34 Message::Text { body } => serde_json::json!({
35 "messaging_product": "whatsapp",
36 "recipient_type": "individual",
37 "to": to,
38 "type": "text",
39 "text": { "body": body }
40 }),
41 Message::Template {
42 name,
43 language,
44 parameters,
45 } => serde_json::json!({
46 "messaging_product": "whatsapp",
47 "recipient_type": "individual",
48 "to": to,
49 "type": "template",
50 "template": {
51 "name": name,
52 "language": { "code": language },
53 "components": parameters
54 }
55 }),
56 }
57 }
58}
59
60#[derive(Debug, Clone)]
62pub struct SendResult {
63 pub wamid: String,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
73#[serde(tag = "type", content = "phone")]
74#[serde(rename_all = "snake_case")]
75pub enum SenderIdentity {
76 Owner(String),
78 Customer(String),
80}
81
82impl SenderIdentity {
83 pub fn phone(&self) -> &str {
85 match self {
86 SenderIdentity::Owner(p) | SenderIdentity::Customer(p) => p,
87 }
88 }
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
93#[serde(rename_all = "snake_case")]
94pub enum DeliveryStatus {
95 Sent,
97 Delivered,
99 Read,
101 Failed,
103 #[serde(other)]
105 Unknown,
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn text_message_produces_correct_meta_api_json() {
114 let msg = Message::Text {
115 body: "Hello World".into(),
116 };
117 let payload = msg.to_api_payload("393401234567");
118
119 assert_eq!(payload["messaging_product"], "whatsapp");
120 assert_eq!(payload["recipient_type"], "individual");
121 assert_eq!(payload["to"], "393401234567");
122 assert_eq!(payload["type"], "text");
123 assert_eq!(payload["text"]["body"], "Hello World");
124 }
125
126 #[test]
127 fn template_message_produces_correct_meta_api_json() {
128 let params = vec![
129 serde_json::json!({"type": "text", "text": "Alberto"}),
130 serde_json::json!({"type": "text", "text": "123456"}),
131 ];
132 let msg = Message::Template {
133 name: "order_confirmation".into(),
134 language: "en_US".into(),
135 parameters: params.clone(),
136 };
137 let payload = msg.to_api_payload("393401234567");
138
139 assert_eq!(payload["messaging_product"], "whatsapp");
140 assert_eq!(payload["recipient_type"], "individual");
141 assert_eq!(payload["to"], "393401234567");
142 assert_eq!(payload["type"], "template");
143 assert_eq!(payload["template"]["name"], "order_confirmation");
144 assert_eq!(payload["template"]["language"]["code"], "en_US");
145 assert_eq!(payload["template"]["components"], serde_json::json!(params));
146 }
147
148 #[test]
149 fn sender_identity_owner_carries_phone_number() {
150 let identity = SenderIdentity::Owner("393401234567".into());
151 assert_eq!(identity.phone(), "393401234567");
152 assert!(matches!(identity, SenderIdentity::Owner(_)));
153 }
154
155 #[test]
156 fn sender_identity_customer_carries_phone_number() {
157 let identity = SenderIdentity::Customer("393409999999".into());
158 assert_eq!(identity.phone(), "393409999999");
159 assert!(matches!(identity, SenderIdentity::Customer(_)));
160 }
161
162 #[test]
163 fn delivery_status_deserializes_known_variants() {
164 assert_eq!(
165 serde_json::from_str::<DeliveryStatus>("\"sent\"").unwrap(),
166 DeliveryStatus::Sent
167 );
168 assert_eq!(
169 serde_json::from_str::<DeliveryStatus>("\"delivered\"").unwrap(),
170 DeliveryStatus::Delivered
171 );
172 assert_eq!(
173 serde_json::from_str::<DeliveryStatus>("\"read\"").unwrap(),
174 DeliveryStatus::Read
175 );
176 assert_eq!(
177 serde_json::from_str::<DeliveryStatus>("\"failed\"").unwrap(),
178 DeliveryStatus::Failed
179 );
180 }
181
182 #[test]
183 fn delivery_status_deserializes_unknown_variant() {
184 let status: DeliveryStatus = serde_json::from_str("\"expired\"").unwrap();
185 assert_eq!(status, DeliveryStatus::Unknown);
186 }
187}