1use regex::Regex;
2use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION, CONTENT_TYPE};
3use reqwest::StatusCode;
4use serde::Serialize;
5use std::error::Error;
6use std::time::SystemTime;
7
8mod constants;
9
10pub struct Listen<'a> {
11 pub artist: &'a str,
12 pub track: &'a str,
13 pub album: &'a str,
14}
15
16impl Listen<'_> {
17 pub fn single(&self, uuid_user: &str) -> Result<StatusCode, Box<dyn Error>> {
18 let time = SystemTime::now()
19 .duration_since(SystemTime::UNIX_EPOCH)
20 .unwrap()
21 .as_secs();
22 let song = Submission {
23 listen_type: ListenType::Single,
24 payload: vec![Payload {
25 listened_at: time,
26 track_metadata: TrackMetadata {
27 artist_name: self.artist,
28 track_name: self.track,
29 release_name: self.album,
30 },
31 }],
32 };
33 match submit_single(uuid_user, song) {
34 Ok(status_code) => Ok(status_code),
35 Err(e) => Err(e),
36 }
37 }
38 pub fn playing_now(&self, uuid_user: &str) -> Result<StatusCode, Box<dyn Error>> {
39 let song = Submission {
40 listen_type: ListenType::PlayingNow,
41 payload: vec![Payload {
42 listened_at: 0,
43 track_metadata: TrackMetadata {
44 artist_name: self.artist,
45 track_name: self.track,
46 release_name: self.album,
47 },
48 }],
49 };
50 match submit_playing_now(uuid_user, song) {
51 Ok(status_code) => Ok(status_code),
52 Err(e) => Err(e),
53 }
54 }
55}
56#[derive(Serialize, Default)]
57pub struct Submission<'a> {
58 pub listen_type: ListenType,
59 pub payload: Vec<Payload<'a>>,
60}
61
62#[derive(Serialize, PartialEq)]
63pub enum ListenType {
64 #[serde(rename(serialize = "single"))]
65 Single,
66 #[serde(rename(serialize = "playing_now"))]
67 PlayingNow,
68 #[serde(rename(serialize = "import"))]
69 Import,
70}
71
72impl Default for ListenType {
73 fn default() -> Self {
74 ListenType::Single
75 }
76}
77
78#[derive(Serialize, Default)]
79pub struct Payload<'a> {
80 pub listened_at: u64,
81 pub track_metadata: TrackMetadata<'a>,
82}
83
84#[derive(Serialize, Default)]
85pub struct TrackMetadata<'a> {
86 pub artist_name: &'a str,
87 pub track_name: &'a str,
88 pub release_name: &'a str,
89}
90
91pub fn submit_import(user_uuid: &str, sub: Submission) -> Result<StatusCode, Box<dyn Error>> {
92 if sub.listen_type == ListenType::Import {
93 let mut headers = HeaderMap::new();
94 let mut auth = String::from("Token ");
95 auth.push_str(user_uuid);
96 headers.insert(AUTHORIZATION, HeaderValue::from_str(auth.as_str()).unwrap());
97 headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
98 let j = serde_json::to_string(&sub).unwrap();
99 let res = reqwest::Client::new()
100 .post((constants::API_ROOT_URL.to_string() + "/1/submit-listens").as_str())
101 .headers(headers)
102 .body(j)
103 .send()?;
104 return Ok(res.status());
105 } else {
106 return Err("error submitting import".into());
107 }
108}
109
110pub fn submit_playing_now(user_uuid: &str, sub: Submission) -> Result<StatusCode, Box<dyn Error>> {
111 if sub.listen_type == ListenType::PlayingNow && sub.payload.len() == 1 {
112 let mut headers = HeaderMap::new();
113 let mut auth = String::from("Token ");
114 auth.push_str(user_uuid);
115 headers.insert(AUTHORIZATION, HeaderValue::from_str(auth.as_str()).unwrap());
116 headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
117 let j = serde_json::to_string(&sub).unwrap();
118 let re = Regex::new(r#""listened_at":\d+,"#).unwrap();
119 let result = re.replace_all(j.as_str(), "");
120 let res = reqwest::Client::new()
121 .post((constants::API_ROOT_URL.to_string() + "/1/submit-listens").as_str())
122 .headers(headers)
123 .body(format!("{}", result))
124 .send()?;
125 return Ok(res.status());
126 } else {
127 return Err("error submitting playing_now: payload.len() must be 1".into());
128 }
129}
130
131pub fn submit_single(user_uuid: &str, sub: Submission) -> Result<StatusCode, Box<dyn Error>> {
132 if sub.listen_type == ListenType::Single
133 && sub.payload.len() == 1
134 && sub.payload.first().unwrap().listened_at != 0
135 {
136 let mut headers = HeaderMap::new();
137 let mut auth = String::from("Token ");
138 auth.push_str(user_uuid);
139 headers.insert(AUTHORIZATION, HeaderValue::from_str(auth.as_str()).unwrap());
140 headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
141 let j = serde_json::to_string(&sub).unwrap();
142 let res = reqwest::Client::new()
143 .post((constants::API_ROOT_URL.to_string() + "/1/submit-listens").as_str())
144 .headers(headers)
145 .body(j)
146 .send()?;
147 return Ok(res.status());
148 } else {
149 return Err(
150 "error submitting single: payload.len() must be 1 and listening_at field must be 0"
151 .into(),
152 );
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 #[test]
159 fn it_works() {
160 assert_eq!(2 + 2, 4);
161 }
162}