hi_push/apns/
mod.rs

1mod types;
2
3use async_trait::async_trait;
4pub use self::types::*;
5
6use super::Error;
7
8use reqwest::header;
9
10pub struct Client {
11    cli: reqwest::Client,
12    production: bool,
13}
14
15impl Client {
16    pub fn new(certs: &[u8], password: &str) -> Result<Self, Error> {
17        let cert = reqwest::Identity::from_pkcs12_der(certs, password).unwrap();
18
19        let cli = reqwest::Client::builder().identity(cert).build().unwrap();
20
21        Ok(Self {
22            cli,
23            production: false,
24        })
25    }
26
27    pub async fn _push<'b>(&self, notification: &'b Notification<'_>) -> Result<Response, Error> {
28        let url = self.build_url(&notification.device_token);
29
30        let mut headers = header::HeaderMap::new();
31
32        headers.append(
33            "apns-topic",
34            header::HeaderValue::from_str(notification.topic).unwrap(),
35        );
36
37        if let Some(id) = notification.id {
38            headers.append(
39                "apns-id",
40                header::HeaderValue::from_str(&id.to_string()).unwrap(),
41            );
42        }
43
44        if let Some(expire) = notification.expiration {
45            headers.append(
46                "apns-expiration",
47                header::HeaderValue::from_str(&expire.to_string()).unwrap(),
48            );
49        }
50
51        if let Some(collapse_id) = &notification.collapse_id {
52            headers.append(
53                "apns-collapse-id",
54                header::HeaderValue::from_str(collapse_id.as_str()).unwrap(),
55            );
56        }
57
58        if let Some(priority) = notification.priority {
59            headers.append(
60                "apns-priority",
61                header::HeaderValue::from_str(&priority.to_uint().to_string()).unwrap(),
62            );
63        }
64
65        if let Some(push_type) = &notification.apns_push_type {
66            headers.append(
67                "apns-push-type",
68                header::HeaderValue::from_str(push_type.as_str()).unwrap(),
69            );
70        }
71
72        let request = ApnsRequest {
73            aps: &notification.payload,
74            custom: notification.custom.as_ref(),
75        };
76        let resp = self
77            .cli
78            .post(&url)
79            .headers(headers)
80            .json(&request)
81            .send()
82            .await
83            .unwrap();
84
85        let status_code = resp.status();
86
87        let headers = resp.headers().clone();
88
89        let apns_id = headers.get("apns-id").unwrap().clone();
90
91        let apns_id = apns_id.to_str().unwrap();
92
93        let mut resp = resp.json::<Response>().await.unwrap();
94
95        resp.apns_id = apns_id.to_string();
96        resp.status_code = status_code;
97        resp.token = notification.device_token.to_string();
98
99        Ok(resp)
100    }
101
102    pub async fn push<'b>(
103        &self,
104        notification: &'b [Notification<'_>],
105    ) -> Result<BatchResponse, Error> {
106        let mut resps = BatchResponse::default();
107
108        for notify in notification {
109            let resp = self._push(notify).await;
110            match resp {
111                Ok(res) => {
112                    if res.reason == ApiErrorReason::BadDeviceToken {
113                        resps.responses.push(res);
114                    }
115                }
116                Err(err) => {
117                    resps.failure += 1
118                }
119            }
120        }
121        resps.failure = (notification.len() as i64 - resps.success) as i64;
122        Ok(resps)
123    }
124
125    fn build_url(&self, device_token: &str) -> String {
126        let root = if self.production {
127            APN_URL_PRODUCTION
128        } else {
129            APN_URL_DEV
130        };
131        format!("{}/3/device/{}", root, device_token)
132    }
133}
134
135#[async_trait]
136impl<'a> super::Pusher<'a, Notification<'a>, Response> for Client {
137    async fn push(&self, msg: &'a Notification) -> Result<Response, Error> {
138        self._push(msg).await
139    }
140}
141
142#[cfg(test)]
143mod test {
144    #[test]
145    fn test_cert() {
146
147    }
148}