1use std::collections::HashMap;
14use std::fs::File;
15use std::str::FromStr;
16
17use reqwest::blocking::Client as SyncClient;
18use reqwest::Method;
19
20use anyhow::Result;
21use serde::de::DeserializeOwned;
22
23#[cfg(feature = "async")]
24mod async_gotify;
25mod response_types;
26#[cfg(feature = "async")]
27pub use crate::async_gotify::*;
28pub use crate::response_types::*;
29
30pub struct Gotify<'a> {
31 base_url: &'a str,
32 app_token: Option<&'a str>,
33 client_token: Option<&'a str>,
34}
35
36impl<'a> Gotify<'a> {
37 pub fn new(base_url: &'a str, app_token: Option<&'a str>, client_token: Option<&'a str>) -> Self {
39 Self {
40 base_url: base_url.trim_end_matches("/"),
41 app_token: app_token,
42 client_token: client_token,
43 }
44 }
45
46 pub fn config(mut self, base_url: Option<&'a str>, app_token: Option<&'a str>, client_token: Option<&'a str>) -> Self {
48 if let Some(url) = base_url {
49 self.base_url = url.trim_end_matches("/");
50 }
51 if let Some(token) = app_token {
52 self.app_token = Some(token);
53 }
54 if let Some(token) = client_token {
55 self.client_token = Some(token);
56 }
57 self
58 }
59}
60
61pub struct SyncGotify<'a> {
63 gotify: Gotify<'a>,
64 client: SyncClient,
65}
66
67impl<'a> SyncGotify<'a> {
68 pub fn new(base_url: &'a str, app_token: Option<&'a str>, client_token: Option<&'a str>) -> Self {
69 let gotify = Gotify::new(base_url, app_token, client_token);
70 let client = SyncClient::new();
71 Self { gotify, client }
72 }
73 pub fn from(gotify: Gotify<'a>) -> Self {
74 let client = SyncClient::new();
75 Self { gotify, client }
76 }
77
78 fn get_token(&self, auth_mode: Option<&str>) -> Option<&'a str> {
79 if let Some(mode) = auth_mode {
80 if mode == "app" {
81 self.gotify.app_token
82 } else {
83 self.gotify.client_token
84 }
85 } else {
86 self.gotify.app_token
87 }
88 }
89
90 fn do_request<T: DeserializeOwned>(
91 &self,
92 method: &str,
93 endpoint_url: &str,
94 data: Option<HashMap<String, Option<String>>>,
95 file: Option<File>,
96 auth_mode: Option<&str>,
97 ) -> Result<T> {
98 let method = Method::from_str(&method)?;
99 let request_url = format!("{}{}", self.gotify.base_url, endpoint_url);
100
101 let mut request = self.client.request(method.clone(), request_url);
102 if let Some(f) = file {
103 request = request.body(f);
104 } else {
105 if let Some(data) = data {
106 match method {
107 Method::GET => {
108 request = request.query(&data);
109 }
110 _ => {
111 request = request.json(&data);
112 }
113 }
114 request = request.json(&data);
115 }
116 }
117 let token = self.get_token(auth_mode).expect("missing token");
118 request = request.header("X-Gotify-Key", token);
119 let response = request.send()?;
120 Ok(response.json::<T>()?)
121 }
122
123 pub fn applications(&self) -> Result<Vec<Application>> {
124 self.do_request("get", "/application", None, None, None)
125 }
126
127 pub fn create_application(&self, name: String, description: String) -> Result<Application> {
128 let mut data = HashMap::new();
129 data.insert("name".to_owned(), Some(name));
130 data.insert("description".to_owned(), Some(description));
131 self.do_request("post", "/application", Some(data), None, None)
132 }
133
134 pub fn update_application(
135 &self,
136 id: i32,
137 name: String,
138 description: Option<String>,
139 ) -> Result<Application> {
140 let mut data = HashMap::new();
141 data.insert("name".to_string(), Some(name));
142 data.insert("description".to_string(), description);
143 self.do_request("put", &format!("/application/{id}"), Some(data), None, None)
144 }
145
146 pub fn delete_application(&self, id: i32) -> Result<()> {
147 self.do_request("delete", &format!("/application/{id}"), None, None, None)
148 }
149
150 pub fn upload_application_image(&self, id: i32, image: File) -> Result<Application> {
151 self.do_request(
152 "post",
153 &format!("/application/{id}/image"),
154 None,
155 Some(image),
156 None,
157 )
158 }
159
160 pub fn get_messages(
161 &self,
162 app_id: Option<i32>,
163 limit: Option<i32>,
164 since: Option<i32>,
165 ) -> Result<PagedMessages> {
166 let mut data = HashMap::new();
167 data.insert("limit".to_string(), limit.map(|i| i.to_string()));
168 data.insert("since".to_string(), since.map(|i| i.to_string()));
169 if let Some(id) = app_id {
170 self.do_request(
171 "get",
172 &format!("/application/{id}/message"),
173 Some(data),
174 None,
175 None,
176 )
177 } else {
178 self.do_request("get", "/message", Some(data), None, None)
179 }
180 }
181
182 pub fn create_message(
183 &self,
184 message: String,
185 priority: Option<i32>,
186 title: Option<String>,
187 ) -> Result<Message> {
189 let mut data = HashMap::new();
190 data.insert("message".to_string(), Some(message));
191 data.insert("priority".to_string(), priority.map(|i| i.to_string()));
192 data.insert("title".to_string(), title);
193 self.do_request("post", "/message", Some(data), None, Some("app"))
194 }
195
196 pub fn delete_messages(&self, app_id: Option<i32>) -> Result<()> {
197 if let Some(id) = app_id {
198 self.do_request(
199 "delete",
200 &format!("/application/{id}/message"),
201 None,
202 None,
203 None,
204 )
205 } else {
206 self.do_request("delete", &format!("/message"), None, None, None)
207 }
208 }
209
210 pub fn delete_message(&self, msg_id: i32) -> Result<()> {
211 self.do_request("delete", &format!("/message/{msg_id}"), None, None, None)
212 }
213
214 pub fn get_clients(&self) -> Result<Vec<Client>> {
215 self.do_request("get", "/client", None, None, None)
216 }
217
218 pub fn create_client(&self, name: String) -> Result<Client> {
219 let mut data = HashMap::new();
220 data.insert("name".to_string(), Some(name));
221 self.do_request("post", "/client", Some(data), None, None)
222 }
223
224 pub fn update_client(&self, id: i32, name: String) -> Result<Client> {
225 let mut data = HashMap::new();
226 data.insert("name".to_string(), Some(name));
227 self.do_request("put", &format!("/client/{id}"), Some(data), None, None)
228 }
229
230 pub fn delete_client(&self, id: i32) -> Result<()> {
231 self.do_request("delete", &format!("/client/{id}"), None, None, None)
232 }
233
234 pub fn get_current_user(&self) -> Result<User> {
235 self.do_request("get", "/current/user", None, None, None)
236 }
237
238 pub fn set_password(&self, passwd: String) -> Result<()> {
239 let mut data = HashMap::new();
240 data.insert("pass".to_string(), Some(passwd));
241 self.do_request("get", "/current/user/password", Some(data), None, None)
242 }
243
244 pub fn get_users(&self) -> Result<Vec<User>> {
245 self.do_request("get", "/user", None, None, None)
246 }
247
248 pub fn create_user(&self, name: String, passwd: String, admin: Option<bool>) -> Result<User> {
249 let mut data = HashMap::new();
250 data.insert("pass".to_string(), Some(passwd));
251 data.insert("name".to_string(), Some(name));
252 data.insert(
253 "admin".to_string(),
254 Some(if admin.unwrap_or(false) {
255 "true".to_string()
256 } else {
257 "false".to_string()
258 }),
259 );
260 self.do_request("post", "/user", Some(data), None, None)
261 }
262
263 pub fn get_user(&self, id: i32) -> Result<User> {
264 self.do_request("get", &format!("/user/{id}"), None, None, None)
265 }
266
267 pub fn update_user(
268 &self,
269 id: i32,
270 name: Option<String>,
271 passwd: Option<String>,
272 admin: Option<bool>,
273 ) -> Result<User> {
274 let mut data = HashMap::new();
275 data.insert("pass".to_string(), passwd);
276 data.insert("name".to_string(), name);
277 data.insert(
278 "admin".to_string(),
279 Some(if admin.unwrap_or(false) {
280 "true".to_string()
281 } else {
282 "false".to_string()
283 }),
284 );
285 self.do_request("put", &format!("/user/{id}"), Some(data), None, None)
286 }
287
288 pub fn delete_user(&self, id: i32) -> Result<()> {
289 self.do_request("delete", &format!("/user/{id}"), None, None, None)
290 }
291
292 pub fn get_health(&self) -> Result<Health> {
293 self.do_request("get", "/health", None, None, None)
294 }
295
296 pub fn get_plugins(&self) -> Result<Vec<PluginConf>> {
297 self.do_request("get", "/plugins", None, None, None)
298 }
299
300 pub fn get_plugin_config(&self, id: i32) -> Result<PluginConf> {
301 self.do_request("get", &format!("/plugins/{id}/config"), None, None, None)
302 }
303
304 pub fn disable_plugin(&self, id: i32) -> Result<()> {
310 self.do_request("post", &format!("/plugins/{id}/disable"), None, None, None)
311 }
312
313 pub fn get_plugin_display(&self, id: i32) -> Result<String> {
314 self.do_request("get", &format!("/plugins/{id}/display"), None, None, None)
315 }
316
317 pub fn enable_plugin(&self, id: i32) -> Result<()> {
318 self.do_request("post", &format!("/plugins/{id}/enable"), None, None, None)
319 }
320
321 pub fn get_version(&self) -> Result<VersionInfo> {
322 self.do_request("get", "/version", None, None, None)
323 }
324}