use crate::{Client, Code, RoliError};
use reqwest::header;
use serde::{Deserialize, Serialize};
const DEALS_ACTIVITY_API: &str = "https://www.rolimons.com/api/activity2";
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Copy)]
pub enum Activity {
PriceUpdate(PriceUpdate),
RapUpdate(RapUpdate),
}
#[derive(
Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize, Copy,
)]
pub struct PriceUpdate {
pub timestamp: u64,
pub item_id: u64,
pub price: u64,
}
#[derive(
Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize, Copy,
)]
pub struct RapUpdate {
pub timestamp: u64,
pub item_id: u64,
pub rap: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct DealsActivityResponse {
success: bool,
activities: Vec<Vec<Code>>,
}
impl Activity {
fn from_raw(codes: Vec<Code>) -> Result<Self, RoliError> {
if codes.len() != 5 {
return Err(RoliError::MalformedResponse);
}
let is_price_update = codes[1].to_i64()? == 0;
let timestamp = codes[0].to_i64()? as u64;
let item_id = codes[2].to_i64()? as u64;
match is_price_update {
true => {
let price = codes[4].to_i64()? as u64;
Ok(Activity::PriceUpdate(PriceUpdate {
timestamp,
item_id,
price,
}))
}
false => {
let rap = codes[4].to_i64()? as u64;
Ok(Activity::RapUpdate(RapUpdate {
timestamp,
item_id,
rap,
}))
}
}
}
}
impl Client {
pub async fn deals_activity(&self) -> Result<Vec<Activity>, RoliError> {
let request_result = self
.reqwest_client
.get(DEALS_ACTIVITY_API)
.header(header::USER_AGENT, crate::USER_AGENT)
.send()
.await;
match request_result {
Ok(response) => {
let status_code = response.status().as_u16();
match status_code {
200 => {
let raw = match response.json::<DealsActivityResponse>().await {
Ok(x) => x,
Err(_) => return Err(RoliError::MalformedResponse),
};
if !raw.success {
return Err(RoliError::RequestReturnedUnsuccessful);
}
let mut activities = Vec::new();
for raw_activity_codes in raw.activities {
let activity = Activity::from_raw(raw_activity_codes)?;
activities.push(activity)
}
Ok(activities)
}
429 => Err(RoliError::TooManyRequests),
500 => Err(RoliError::InternalServerError),
_ => Err(RoliError::UnidentifiedStatusCode(status_code)),
}
}
Err(e) => Err(RoliError::ReqwestError(e)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_price_update() {
let codes = vec![
Code::Integer(1678939600),
Code::Integer(0),
Code::String(String::from("3016210752")),
Code::Integer(0),
Code::Integer(108),
];
let activity = Activity::PriceUpdate(PriceUpdate {
timestamp: 1678939600,
item_id: 3016210752,
price: 108,
});
assert_eq!(Activity::from_raw(codes).unwrap(), activity);
}
#[test]
fn test_valid_rap_update() {
let codes = vec![
Code::Integer(1678939605),
Code::Integer(1),
Code::String(String::from("3016210752")),
Code::Integer(0),
Code::Integer(92),
];
let activity = Activity::RapUpdate(RapUpdate {
timestamp: 1678939605,
item_id: 3016210752,
rap: 92,
});
assert_eq!(Activity::from_raw(codes).unwrap(), activity);
}
#[test]
fn test_invalid_codes_length() {
let codes = vec![
Code::Integer(1678939600),
Code::Integer(0),
Code::String(String::from("3016210752")),
Code::Integer(0),
];
assert!(Activity::from_raw(codes).is_err());
}
#[test]
fn test_invalid_is_price_update() {
let codes = vec![
Code::Integer(1678939600),
Code::String(String::from("invalid")),
Code::String(String::from("3016210752")),
Code::Integer(0),
Code::Integer(108),
];
assert!(Activity::from_raw(codes).is_err());
}
#[test]
fn test_invalid_timestamp() {
let codes = vec![
Code::String(String::from("invalid")),
Code::Integer(0),
Code::String(String::from("3016210752")),
Code::Integer(0),
Code::Integer(108),
];
assert!(Activity::from_raw(codes).is_err());
}
#[test]
fn test_invalid_item_id() {
let codes = vec![
Code::Integer(1678939600),
Code::Integer(0),
Code::String(String::from("invalid")),
Code::Integer(0),
Code::Integer(108),
];
assert!(Activity::from_raw(codes).is_err());
}
#[test]
fn test_invalid_price() {
let codes = vec![
Code::Integer(1678939600),
Code::Integer(0),
Code::String(String::from("3016210752")),
Code::Integer(0),
Code::String(String::from("invalid")),
];
assert!(Activity::from_raw(codes).is_err());
}
#[test]
fn test_invalid_rap() {
let codes = vec![
Code::Integer(1678939605),
Code::Integer(1),
Code::String(String::from("3016210752")),
Code::Integer(0),
Code::String(String::from("invalid")),
];
assert!(Activity::from_raw(codes).is_err());
}
}