1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#![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,
))
}