1use crate::mattermost::BaseSession;
3use crate::utils::parse_from_hmstr;
4use anyhow::Result;
5use chrono::{DateTime, Local};
6use derivative::Derivative;
7use serde::{Deserialize, Serialize};
8use serde_json as json;
9use std::fmt;
10use thiserror::Error;
11use tracing::debug;
12
13#[allow(missing_docs)]
15#[derive(Debug, Error)]
16pub enum MMSError {
17 #[error("Bad json data")]
18 BadJSONData(#[from] serde_json::error::Error),
19 #[error("HTTP request error")]
20 HTTPRequestError(#[from] ureq::Error),
21 #[error("Mattermost login error")]
22 LoginError(#[from] anyhow::Error),
23}
24
25#[derive(Derivative, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
28#[derivative(Debug)]
29pub struct MMStatus {
30 pub text: String,
32 pub emoji: String,
34 #[serde(skip_serializing_if = "Option::is_none")]
36 pub duration: Option<String>,
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub expires_at: Option<DateTime<Local>>,
40}
41
42impl fmt::Display for MMStatus {
43 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
44 write!(
45 f,
46 "{}:{} (duration: {:?}, expire at: {:?})",
47 self.emoji, self.text, self.duration, self.expires_at
48 )
49 }
50}
51
52impl MMStatus {
53 pub fn new(text: String, emoji: String) -> MMStatus {
56 MMStatus {
57 text,
58 emoji,
59 duration: None,
60 expires_at: None,
61 }
62 }
63 pub fn expires_at(&mut self, time_str: &Option<String>) {
65 if let Some(expiry) = parse_from_hmstr(time_str) {
67 if Local::now() < expiry {
68 self.expires_at = Some(expiry);
69 self.duration = Some("date_and_time".to_owned());
70 } else {
71 debug!("now {:?} >= expiry {:?}", Local::now(), expiry);
72 }
73 }
74 }
76 pub fn to_json(&self) -> Result<String, MMSError> {
78 json::to_string(&self).map_err(MMSError::BadJSONData)
79 }
80
81 #[allow(clippy::borrowed_box)] pub fn _send(&self, session: &Box<dyn BaseSession>) -> Result<ureq::Response, ureq::Error> {
84 let token = session
85 .token()
86 .expect("Internal Error: token is unset in current session");
87 let uri = session.base_uri().to_owned() + "/api/v4/users/me/status/custom";
88 ureq::put(&uri)
89 .set("Authorization", &("Bearer ".to_owned() + token))
90 .send_json(serde_json::to_value(&self).unwrap_or_else(|e| {
91 panic!(
92 "Serialization of MMStatus '{:?}' failed with {:?}",
93 &self, &e
94 )
95 }))
96 }
97 pub fn send(&mut self, session: &mut Box<dyn BaseSession>) -> Result<ureq::Response, MMSError> {
99 debug!("Post status: {}", self.to_owned().to_json()?);
100 match self._send(session) {
101 Ok(response) => Ok(response),
102 Err(ureq::Error::Status(code, response)) => {
103 if code == 401 {
106 session.login().map_err(MMSError::LoginError)?;
108 self._send(session)
109 } else {
110 Err(ureq::Error::Status(code, response))
111 }
112 }
113 Err(e) => Err(e),
114 }
115 .map_err(MMSError::HTTPRequestError)
116 }
117}
118
119#[cfg(test)]
120mod send_should {
121 use super::*;
122 use crate::mattermost::{BaseSession, Session};
123 use httpmock::prelude::*;
124 use test_log::test; #[test]
126 fn send_required_json() -> Result<()> {
127 let server = MockServer::start();
129 let mut mmstatus = MMStatus::new("text".into(), "emoji".into());
130
131 let server_mock = server.mock(|expect, resp_with| {
133 expect
134 .method(PUT)
135 .header("Authorization", "Bearer token")
136 .path("/api/v4/users/me/status/custom")
137 .json_body(serde_json::json!({"emoji":"emoji","text":"text"}
138 ));
139 resp_with
140 .status(200)
141 .header("content-type", "text/html")
142 .body("ok");
143 });
144
145 let mut session: Box<dyn BaseSession> =
147 Box::new(Session::new(&server.url("")).with_token("token"));
148 let resp = mmstatus.send(&mut session)?;
149
150 server_mock.assert();
152 assert_eq!(resp.status(), 200);
154 Ok(())
155 }
156 #[test]
157 fn catch_api_error() -> Result<()> {
158 let server = MockServer::start();
160 let mut mmstatus = MMStatus::new("text".into(), "emoji".into());
161
162 let server_mock = server.mock(|expect, resp_with| {
164 expect
165 .method(PUT)
166 .header("Authorization", "Bearer token")
167 .path("/api/v4/users/me/status/custom")
168 .json_body(serde_json::json!({"emoji":"emoji","text":"text"}
169 ));
170 resp_with
171 .status(500)
172 .header("content-type", "text/html")
173 .body("Internal error");
174 });
175
176 let mut session: Box<dyn BaseSession> =
178 Box::new(Session::new(&server.url("")).with_token("token"));
179 let resp = mmstatus.send(&mut session);
180 assert!(resp.is_err());
181
182 server_mock.assert();
184 Ok(())
185 }
186}