1mod test;
2
3#[cfg(feature = "non-blocking")]
4use std::collections::HashMap;
5#[cfg(feature = "delay")]
6use tokio::task::JoinHandle;
7#[cfg(feature = "delay")]
8use tokio::time::sleep;
9#[cfg(feature = "blocking")]
10use ureq::{SerdeMap, SerdeValue};
11
12#[cfg(feature = "delay")]
13pub type DelayResultHandler = tokio::task::JoinHandle<Result<(), Error>>;
14
15#[cfg(feature = "blocking")]
17#[derive(Debug, Clone)]
18pub struct IftWHClient {
19 client: ureq::Agent,
20 api_key: String,
21}
22
23pub struct WebHookData {
24 value1: Option<String>,
25 value2: Option<String>,
26 value3: Option<String>,
27}
28
29impl WebHookData {
30 pub fn new(value1: Option<&str>, value2: Option<&str>, value3: Option<&str>) -> Option<Self> {
31 Some(Self {
32 value1: value1.map(|s| s.to_string()),
33 value2: value2.map(|s| s.to_string()),
34 value3: value3.map(|s| s.to_string()),
35 })
36 }
37}
38
39#[cfg(feature = "blocking")]
41impl IftWHClient {
42 pub fn new(api_key: &str) -> Self {
43 let client = ureq::Agent::new();
44 Self {
45 client,
46 api_key: api_key.to_string(),
47 }
48 }
49
50 pub fn trigger(&self, event_name: &str, data: Option<WebHookData>) -> Result<(), Error> {
51 let url = format!(
52 "https://maker.ifttt.com/trigger/{event}/with/key/{key}",
53 event = event_name,
54 key = self.api_key
55 );
56 match data {
57 Some(data) => {
58 let value = make_serde_value(data);
59 let res = self
60 .client
61 .post(&url)
62 .set("Content-Type", "application/json")
63 .send_json(value)?;
64 if res.status() != 200 {
65 return Err(Error::IftttResponseError);
66 }
67 return Ok(());
68 }
69 None => {
70 let res = self.client.post(&url).call()?;
71 if res.status() != 200 {
72 return Err(Error::IftttResponseError);
73 }
74 return Ok(());
75 }
76 }
77 }
78}
79
80#[cfg(feature = "non-blocking")]
82#[derive(Debug, Clone)]
83pub struct AsyncIftWHClient {
84 client: reqwest::Client,
85 api_key: String,
86}
87
88#[cfg(feature = "non-blocking")]
89impl AsyncIftWHClient {
90 pub fn new(api_key: &str) -> Self {
91 let client = reqwest::Client::new();
92 Self {
93 client,
94 api_key: api_key.to_string(),
95 }
96 }
97
98 pub async fn trigger(&self, event_name: &str, data: Option<WebHookData>) -> Result<(), Error> {
99 let url = format!(
100 "https://maker.ifttt.com/trigger/{event}/with/key/{key}",
101 event = event_name,
102 key = self.api_key
103 );
104 match data {
105 Some(data) => {
106 let map = nonblocking_make_serde_value(data);
107 let res = self.client.post(&url).json(&map).send().await?;
108
109 if res.status() != reqwest::StatusCode::OK {
110 return Err(Error::IftttResponseError);
111 }
112 return Ok(());
113 }
114 None => {
115 let res = self.client.post(&url).send().await?;
116 if res.status() != reqwest::StatusCode::OK {
117 return Err(Error::IftttResponseError);
118 }
119 return Ok(());
120 }
121 }
122 }
123
124 #[cfg(feature = "delay")]
126 pub fn trigger_with_delay(
127 self,
128 event_name: &str,
129 data: Option<WebHookData>,
130 delay_time: std::time::Duration,
131 ) -> DelayResultHandler {
132 let url = format!(
133 "https://maker.ifttt.com/trigger/{event}/with/key/{key}",
134 event = event_name,
135 key = self.api_key
136 );
137 match data {
138 Some(data) => {
139 let map = nonblocking_make_serde_value(data);
140 let handler: JoinHandle<Result<(), Error>> = tokio::spawn(async move {
141 sleep(delay_time).await;
142 let res = self.client.post(&url).json(&map).send().await?;
143 if res.status() != reqwest::StatusCode::OK {
144 return Err(Error::IftttResponseError);
145 };
146 Ok(())
147 });
148 return handler;
149 }
150 None => {
151 let handler: JoinHandle<Result<(), Error>> = tokio::spawn(async move {
152 sleep(delay_time).await;
153 let res = self.client.post(&url).send().await?;
154 if res.status() != reqwest::StatusCode::OK {
155 return Err(Error::IftttResponseError);
156 }
157 Ok(())
158 });
159 return handler;
160 }
161 }
162 }
163}
164
165#[derive(Debug)]
166pub enum Error {
167 #[cfg(feature = "blocking")]
168 BlockingRequestError(ureq::Error),
169 #[cfg(feature = "non-blocking")]
170 NonBlockingRequestError(reqwest::Error),
171 IftttResponseError,
172}
173#[cfg(feature = "blocking")]
174impl From<ureq::Error> for Error {
175 fn from(e: ureq::Error) -> Self {
176 Self::BlockingRequestError(e)
177 }
178}
179#[cfg(feature = "non-blocking")]
180impl From<reqwest::Error> for Error {
181 fn from(e: reqwest::Error) -> Self {
182 Self::NonBlockingRequestError(e)
183 }
184}
185
186#[inline]
187#[cfg(feature = "blocking")]
188fn make_serde_value(data: WebHookData) -> SerdeValue {
189 let mut map = SerdeMap::new();
190 if let Some(value1) = data.value1 {
191 map.insert("value1".to_string(), SerdeValue::String(value1));
192 }
193 if let Some(value2) = data.value2 {
194 map.insert("value2".to_string(), SerdeValue::String(value2));
195 }
196 if let Some(value3) = data.value3 {
197 map.insert("value3".to_string(), SerdeValue::String(value3));
198 }
199 let value = SerdeValue::Object(map);
200 value
201}
202
203#[inline]
204#[cfg(feature = "non-blocking")]
205fn nonblocking_make_serde_value(data: WebHookData) -> HashMap<String, String> {
206 let mut map: HashMap<String, String> = HashMap::new();
207 if let Some(value1) = data.value1 {
208 map.insert("value1".to_string(), value1);
209 }
210 if let Some(value2) = data.value2 {
211 map.insert("value2".to_string(), value2);
212 }
213 if let Some(value3) = data.value3 {
214 map.insert("value3".to_string(), value3);
215 }
216 map
217}