use crate::{Client, Code, RoliError};
use reqwest::header;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
const ITEM_DETAILS_API: &str = "https://www.rolimons.com/itemapi/itemdetails";
#[derive(
Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize, Copy,
)]
pub enum Demand {
#[default]
Unassigned,
Terrible,
Low,
Normal,
High,
Amazing,
}
#[derive(
Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize, Copy,
)]
pub enum Trend {
#[default]
Unassigned,
Lowering,
Unstable,
Stable,
Raising,
Fluctuating,
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)]
pub struct ItemDetails {
pub item_id: u64,
pub item_name: String,
pub acronym: Option<String>,
pub rap: u64,
pub valued: bool,
pub value: u64,
pub demand: Demand,
pub trend: Trend,
pub projected: bool,
pub hyped: bool,
pub rare: bool,
}
#[derive(Default, Serialize, Deserialize)]
struct AllItemDetailsResponse {
success: bool,
item_count: u64,
items: HashMap<String, Vec<Code>>,
}
impl ItemDetails {
fn from_raw(item_id: u64, codes: Vec<Code>) -> Result<Self, RoliError> {
let item_name = codes[0].to_string();
let acronym = {
if codes[1].to_string().is_empty() {
None
} else {
Some(codes[1].to_string())
}
};
let rap = codes[2].to_i64()? as u64;
let valued = codes[3].to_i64()? != -1;
let value = codes[4].to_i64()? as u64;
let demand = match codes[5].to_i64()? {
-1 => Demand::Unassigned,
0 => Demand::Terrible,
1 => Demand::Low,
2 => Demand::Normal,
3 => Demand::High,
4 => Demand::Amazing,
_ => return Err(RoliError::MalformedResponse),
};
let trend = match codes[6].to_i64()? {
-1 => Trend::Unassigned,
0 => Trend::Lowering,
1 => Trend::Unstable,
2 => Trend::Stable,
3 => Trend::Raising,
4 => Trend::Fluctuating,
_ => return Err(RoliError::MalformedResponse),
};
let projected = match codes[7].to_i64()? {
1 => true,
-1 => false,
_ => return Err(RoliError::MalformedResponse),
};
let hyped = match codes[8].to_i64()? {
1 => true,
-1 => false,
_ => return Err(RoliError::MalformedResponse),
};
let rare = match codes[9].to_i64()? {
1 => true,
-1 => false,
_ => return Err(RoliError::MalformedResponse),
};
Ok(ItemDetails {
item_id,
item_name,
acronym,
rap,
valued,
value,
demand,
trend,
projected,
hyped,
rare,
})
}
}
impl AllItemDetailsResponse {
fn into_vec(self) -> Result<Vec<ItemDetails>, RoliError> {
let mut item_details_vec = Vec::new();
for (item_id_string, codes) in self.items {
let item_id = match item_id_string.parse() {
Ok(x) => x,
Err(_) => return Err(RoliError::MalformedResponse),
};
let item_details = ItemDetails::from_raw(item_id, codes)?;
item_details_vec.push(item_details);
}
Ok(item_details_vec)
}
}
impl Client {
pub async fn all_item_details(&self) -> Result<Vec<ItemDetails>, RoliError> {
let request_result = self
.reqwest_client
.get(ITEM_DETAILS_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::<AllItemDetailsResponse>().await {
Ok(x) => x,
Err(_) => return Err(RoliError::MalformedResponse),
};
if !raw.success {
return Err(RoliError::RequestReturnedUnsuccessful);
}
let item_details = raw.into_vec()?;
Ok(item_details)
}
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_from_raw_valid_data() {
let item_id = 123;
let codes = vec![
Code::String("Test item name".to_string()),
Code::String("TI".to_string()),
Code::Integer(100),
Code::Integer(1),
Code::Integer(200),
Code::Integer(3),
Code::Integer(4),
Code::Integer(1),
Code::Integer(1),
Code::Integer(1),
];
let result = ItemDetails::from_raw(item_id, codes);
assert!(result.is_ok());
let item_details = result.unwrap();
assert_eq!(item_details.item_id, item_id);
assert_eq!(item_details.item_name, "Test item name");
assert_eq!(item_details.acronym, Some("TI".to_string()));
assert_eq!(item_details.rap, 100);
assert!(item_details.valued);
assert_eq!(item_details.value, 200);
assert_eq!(item_details.demand, Demand::High);
assert_eq!(item_details.trend, Trend::Fluctuating);
assert!(item_details.projected);
assert!(item_details.hyped);
assert!(item_details.rare);
}
#[test]
fn test_from_raw_invalid_data() {
let item_id = 123;
let codes = vec![
Code::String("Test item name".to_string()),
Code::String("TI".to_string()),
Code::String("Invalid".to_string()),
Code::Integer(-1),
Code::Integer(200),
Code::Integer(3),
Code::Integer(4),
Code::Integer(1),
Code::Integer(1),
Code::Integer(1),
];
let result = ItemDetails::from_raw(item_id, codes);
assert!(result.is_err());
}
}