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(())
}
}