#![allow(
clippy::len_zero,
clippy::tabs_in_doc_comments,
clippy::collapsible_if,
clippy::needless_bool,
clippy::too_many_arguments
)]
#[cfg(feature = "serde")]
extern crate serde_ as serde;
mod extension_traits;
#[macro_use]
mod common;
pub use common::structs::*;
pub mod v1;
pub mod v2;
pub mod web;
thiserror_lite::err_enum! {
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
#[error("User not found")]
UserNotFound,
#[error("Username and password combination not found")]
InvalidLogin,
#[error("Score not found")]
ScoreNotFound,
#[error("Song not found")]
SongNotFound,
#[error("Chart not tracked")]
ChartNotTracked,
#[error("Favorite already exists")]
ChartAlreadyFavorited,
#[error("Database error")]
DatabaseError,
#[error("Goal already exists")]
GoalAlreadyExists,
#[error("Chart already exists")]
ChartAlreadyAdded,
#[error("The uploaded file is not a valid XML file")]
InvalidXml,
#[error("No users registered")]
NoUsersFound,
#[error("General network error ({0})")]
NetworkError(String),
#[error("Internal web server error (HTTP {status_code})")]
ServerIsDown { status_code: u16 },
#[error("Error while parsing the json sent by the server ({0})")]
InvalidJson(#[from] serde_json::Error),
#[error("Sever responded to query with an unrecognized error message ({0})")]
UnknownApiError(String),
#[error("Server sent a payload that doesn't match expectations (debug: {0:?})")]
InvalidDataStructure(String),
#[error("Server timed out")]
Timeout,
#[error("Server response was empty")]
EmptyServerResponse
}
}
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
if e.kind() == std::io::ErrorKind::TimedOut {
Self::Timeout
} else {
Self::NetworkError(e.to_string())
}
}
}
fn rate_limit(last_request: &mut std::time::Instant, request_cooldown: std::time::Duration) {
let now = std::time::Instant::now();
let time_since_last_request = now.duration_since(*last_request);
if time_since_last_request < request_cooldown {
std::thread::sleep(request_cooldown - time_since_last_request);
}
*last_request = now;
}
pub fn rescore<S, W>(
replay: &Replay,
num_hit_mines: u32,
num_dropped_holds: u32,
judge: &etterna::Judge,
) -> Option<etterna::Wifescore>
where
S: etterna::ScoringSystem,
W: etterna::Wife,
{
let mut lanes = replay.split_into_lanes()?;
for lane in lanes.iter_mut() {
lane.note_seconds.sort_by(|a, b| a.partial_cmp(b).unwrap());
lane.hit_seconds.sort_by(|a, b| a.partial_cmp(b).unwrap());
}
Some(etterna::rescore::<S, W>(
&lanes,
num_hit_mines,
num_dropped_holds,
judge,
))
}