use super::pagination::Bound;
use crate::util::validate_limit;
#[derive(Clone, Debug)]
pub struct RecordsLeaderboardId {
pub gamemode: String,
pub scope: Scope,
pub revolution_id: Option<String>,
}
impl RecordsLeaderboardId {
pub fn new(gamemode: &str, scope: Scope, revolution_id: Option<&str>) -> Self {
Self {
gamemode: gamemode.to_owned(),
scope,
revolution_id: revolution_id.map(|s| s.to_owned()),
}
}
pub(crate) fn to_param(&self) -> String {
let scope = match &self.scope {
Scope::Global => "global".to_string(),
Scope::Country(c) => format!("country_{}", c.to_uppercase()),
};
let revolution_id = self.revolution_id.as_deref().unwrap_or("");
format!("{}_{}{}", self.gamemode, scope, revolution_id)
}
}
#[derive(Clone, Debug)]
pub enum Scope {
Global,
Country(String),
}
#[derive(Clone, Debug, Default)]
pub struct SearchCriteria {
pub bound: Option<Bound>,
pub limit: Option<u8>,
}
impl SearchCriteria {
pub fn new() -> Self {
Self::default()
}
pub fn init(&mut self) {
self.bound = None;
self.limit = None;
}
pub fn after(self, bound: [f64; 3]) -> Self {
Self {
bound: Some(Bound::After(bound)),
..self
}
}
pub fn before(self, bound: [f64; 3]) -> Self {
Self {
bound: Some(Bound::Before(bound)),
..self
}
}
pub fn limit(self, limit: u8) -> Self {
validate_limit(limit);
Self {
limit: Some(limit),
..self
}
}
pub(crate) fn validate_limit(&self) {
if let Some(self_limit) = self.limit {
validate_limit(self_limit)
}
}
pub(crate) fn build(self) -> Vec<(String, String)> {
let mut result = Vec::new();
if let Some(b) = self.bound {
result.push(b.to_query_param());
}
if let Some(l) = self.limit {
result.push(("limit".to_string(), l.to_string()));
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn records_leaderboard_id_new_creates_default() {
let id = RecordsLeaderboardId::new("40l", Scope::Global, None);
assert_eq!(id.gamemode, "40l");
assert!(matches!(id.scope, Scope::Global));
assert!(id.revolution_id.is_none());
}
#[test]
fn records_leaderboard_id_to_param_converts_to_param() {
let id1 = RecordsLeaderboardId::new("40l", Scope::Global, None);
let id2 = RecordsLeaderboardId::new("blitz", Scope::Country("jp".to_string()), None);
let id3 = RecordsLeaderboardId::new("zenith", Scope::Global, Some("@2024w31"));
assert_eq!(id1.to_param(), "40l_global");
assert_eq!(id2.to_param(), "blitz_country_JP");
assert_eq!(id3.to_param(), "zenith_global@2024w31");
}
#[test]
fn search_criteria_new_creates_default() {
let criteria: SearchCriteria = SearchCriteria::new();
assert!(criteria.bound.is_none());
assert!(criteria.limit.is_none());
}
#[test]
fn search_criteria_init_initializes() {
let mut criteria = SearchCriteria::new().after([500000., 0., 0.]).limit(3);
criteria.init();
assert!(criteria.bound.is_none());
assert!(criteria.limit.is_none());
}
#[test]
fn search_criteria_after_sets_upper_bound() {
let criteria = SearchCriteria::new().after([500000.0, 0.0, 0.0]);
assert!(matches!(
criteria.bound,
Some(Bound::After([500000.0, 0.0, 0.0]))
));
}
#[test]
fn search_criteria_before_sets_lower_bound() {
let criteria = SearchCriteria::new().before([500000.0, 0.0, 0.0]);
assert!(matches!(
criteria.bound,
Some(Bound::Before([500000.0, 0.0, 0.0]))
));
}
#[test]
fn search_criteria_limit_sets_valid_limit() {
for i in 1..=100 {
let criteria = SearchCriteria::new().limit(i);
assert_eq!(criteria.limit, Some(i));
}
}
#[test]
#[should_panic]
fn search_criteria_limit_panics_if_out_of_range() {
SearchCriteria::new().limit(0);
SearchCriteria::new().limit(101);
}
#[test]
#[should_panic]
fn search_criteria_validate_limit_panics_if_out_of_range() {
SearchCriteria {
limit: Some(0),
..SearchCriteria::default()
}
.validate_limit();
SearchCriteria {
limit: Some(101),
..SearchCriteria::default()
}
.validate_limit();
}
#[test]
fn search_criteria_build_returns_query_params() {
let criteria = SearchCriteria::new().after([500000., 0., 0.]).limit(3);
let query_params: Vec<(String, String)> = criteria.build();
assert_eq!(
query_params,
vec![
("after".to_string(), "500000:0:0".to_string()),
("limit".to_string(), "3".to_string())
]
);
}
#[test]
fn search_criteria_build_returns_empty_vec_if_no_params() {
let criteria = SearchCriteria::new();
let query_params: Vec<(String, String)> = criteria.build();
assert!(query_params.is_empty());
}
}