1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use crate::utils::parse_from_hmstr;
use anyhow::Result;
use chrono::{DateTime, Local};
use serde::{Deserialize, Serialize};
use serde_json as json;
use std::fmt;
use thiserror::Error;
use tracing::debug;
#[allow(missing_docs)]
#[derive(Debug, Error)]
pub enum MMSError {
#[error("Bad json data")]
BadJSONData(#[from] serde_json::error::Error),
#[error("HTTP request error")]
HTTPRequestError(#[from] ureq::Error),
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct MMStatus {
pub text: String,
pub emoji: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub duration: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expires_at: Option<DateTime<Local>>,
#[serde(skip_serializing)]
base_uri: String,
#[serde(skip_serializing)]
token: String,
}
impl fmt::Display for MMStatus {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}:{} (duration: {:?}, expire at: {:?})",
self.emoji, self.text, self.duration, self.expires_at
)
}
}
impl MMStatus {
pub fn new(text: String, emoji: String, mm_base_uri: String, token: String) -> MMStatus {
let uri = mm_base_uri + "/api/v4/users/me/status/custom";
MMStatus {
text,
emoji,
duration: None,
expires_at: None,
base_uri: uri,
token,
}
}
pub fn expires_at(mut self, time_str: &Option<String>) -> Self {
if let Some(expiry) = parse_from_hmstr(time_str) {
if Local::now() < expiry {
self.expires_at = Some(expiry);
self.duration = Some("date_and_time".to_owned());
} else {
debug!("now {:?} >= expiry {:?}", Local::now(), expiry);
}
}
self
}
pub fn to_json(&self) -> Result<String, MMSError> {
json::to_string(&self).map_err(MMSError::BadJSONData)
}
pub fn send(&self) -> Result<u16, MMSError> {
debug!("Post status: {}", self.to_owned().to_json()?);
let response = ureq::put(&self.base_uri)
.set("Authorization", &("Bearer ".to_owned() + &self.token))
.send_json(serde_json::to_value(&self)?)
.map_err(MMSError::HTTPRequestError)?;
Ok(response.status())
}
}
#[cfg(test)]
mod should {
use super::*;
use httpmock::prelude::*;
#[test]
fn send_required_json_for_mmstatus() -> Result<()> {
let server = MockServer::start();
let mmstatus = MMStatus::new(
"text".to_string(),
"emoji".to_string(),
server.url(""),
"token".to_string(),
);
let server_mock = server.mock(|expect, resp_with| {
expect
.method(PUT)
.header("Authorization", "Bearer token")
.path("/api/v4/users/me/status/custom")
.json_body(serde_json::json!({"emoji":"emoji","text":"text"}
));
resp_with
.status(200)
.header("content-type", "text/html")
.body("ok");
});
let status = mmstatus.send()?;
server_mock.assert();
assert_eq!(status, 200);
Ok(())
}
}