use chrono::{DateTime, Utc};
use serde::Deserialize;
use std::fmt;
use super::snowflake;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum TimeFrame {
#[default]
AllTime,
FiveYears,
ThreeYears,
OneYear,
NineMonths,
SixMonths,
NinetyDays,
ThirtyDays,
SevenDays,
ThreeDays,
OneDay,
TwelveHours,
SixHours,
}
impl TimeFrame {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::AllTime => "alltime",
Self::FiveYears => "5y",
Self::ThreeYears => "3y",
Self::OneYear => "1y",
Self::NineMonths => "270d",
Self::SixMonths => "180d",
Self::NinetyDays => "90d",
Self::ThirtyDays => "30d",
Self::SevenDays => "7d",
Self::ThreeDays => "3d",
Self::OneDay => "1d",
Self::TwelveHours => "12h",
Self::SixHours => "6h",
}
}
}
impl fmt::Display for TimeFrame {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum DataType {
#[default]
MonthlyVotes,
TotalVotes,
ServerCount,
ReviewCount,
}
impl DataType {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::MonthlyVotes => "monthly_votes",
Self::TotalVotes => "total_votes",
Self::ServerCount => "server_count",
Self::ReviewCount => "review_count",
}
}
}
impl fmt::Display for DataType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
pub struct HistoricalDataPoint {
pub time: DateTime<Utc>,
#[serde(deserialize_with = "snowflake::as_string::deserialize")]
pub id: u64,
pub monthly_votes: Option<i64>,
pub total_votes: Option<i64>,
pub server_count: Option<i64>,
pub review_count: Option<i64>,
}
impl HistoricalDataPoint {
#[must_use]
pub const fn value_for(&self, data_type: DataType) -> Option<i64> {
match data_type {
DataType::MonthlyVotes => self.monthly_votes,
DataType::TotalVotes => self.total_votes,
DataType::ServerCount => self.server_count,
DataType::ReviewCount => self.review_count,
}
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
pub struct HistoricalDataResponse {
pub data: Vec<HistoricalDataPoint>,
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
pub struct CompareHistoricalResponse {
#[serde(deserialize_with = "snowflake::map_as_string_keys::deserialize")]
pub data: std::collections::HashMap<u64, Vec<HistoricalDataPoint>>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_time_frame_as_str() {
assert_eq!(TimeFrame::AllTime.as_str(), "alltime");
assert_eq!(TimeFrame::ThirtyDays.as_str(), "30d");
assert_eq!(TimeFrame::TwelveHours.as_str(), "12h");
}
#[test]
fn test_data_type_as_str() {
assert_eq!(DataType::MonthlyVotes.as_str(), "monthly_votes");
assert_eq!(DataType::ServerCount.as_str(), "server_count");
}
#[test]
fn test_historical_data_point_deserialization() {
let json = r#"{
"time": "2025-03-31T00:00:00.000Z",
"id": "583807014896140293",
"monthly_votes": 265,
"total_votes": 1000,
"server_count": 500
}"#;
let point: HistoricalDataPoint = serde_json::from_str(json).unwrap();
assert_eq!(point.id, 583_807_014_896_140_293);
assert_eq!(point.monthly_votes, Some(265));
assert_eq!(point.total_votes, Some(1000));
assert_eq!(point.server_count, Some(500));
assert_eq!(point.review_count, None);
}
#[test]
fn test_historical_data_point_value_for() {
let point = HistoricalDataPoint {
time: Utc::now(),
id: 432_610_292_342_587_392,
monthly_votes: Some(100),
total_votes: Some(500),
server_count: None,
review_count: Some(10),
};
assert_eq!(point.value_for(DataType::MonthlyVotes), Some(100));
assert_eq!(point.value_for(DataType::TotalVotes), Some(500));
assert_eq!(point.value_for(DataType::ServerCount), None);
assert_eq!(point.value_for(DataType::ReviewCount), Some(10));
}
#[test]
fn test_historical_response_deserialization() {
let json = r#"{
"data": [
{
"time": "2025-03-31T00:00:00.000Z",
"id": "583807014896140293",
"monthly_votes": 265
}
]
}"#;
let response: HistoricalDataResponse = serde_json::from_str(json).unwrap();
assert_eq!(response.data.len(), 1);
assert_eq!(response.data[0].monthly_votes, Some(265));
}
#[test]
fn test_time_frame_default() {
assert_eq!(TimeFrame::default(), TimeFrame::AllTime);
}
#[test]
fn test_data_type_default() {
assert_eq!(DataType::default(), DataType::MonthlyVotes);
}
}