feat: support dxratingnet format
This commit is contained in:
@@ -0,0 +1,67 @@
|
|||||||
|
use crate::{
|
||||||
|
helper::MUSIC_DB,
|
||||||
|
title::model::get_user_rating_api::{
|
||||||
|
MusicRating, UserRating,
|
||||||
|
dxrating::{DxCalculatedEntries, DxLevelName, DxMusicRecord, DxSheetId},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
impl DxCalculatedEntries {
|
||||||
|
pub fn from_user_rating_lossy(rating: &UserRating) -> DxCalculatedEntries {
|
||||||
|
let b35 = rating
|
||||||
|
.rating_list
|
||||||
|
.iter()
|
||||||
|
.map(DxMusicRecord::try_from)
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
let b15 = rating
|
||||||
|
.new_rating_list
|
||||||
|
.iter()
|
||||||
|
.map(DxMusicRecord::try_from)
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
DxCalculatedEntries { b35, b15 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u32> for DxLevelName {
|
||||||
|
type Error = ConversionError;
|
||||||
|
|
||||||
|
fn try_from(level: u32) -> Result<Self, Self::Error> {
|
||||||
|
Self::from_repr(level).ok_or(ConversionError::UnknownDifficulty { level })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&MusicRating> for DxMusicRecord {
|
||||||
|
type Error = ConversionError;
|
||||||
|
|
||||||
|
fn try_from(value: &MusicRating) -> Result<Self, Self::Error> {
|
||||||
|
let music_title = MUSIC_DB
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|db| db.get(&value.music_id))
|
||||||
|
.map(|info| info.name.clone())
|
||||||
|
.ok_or(ConversionError::MusicNotInDB)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
sheet_id: DxSheetId {
|
||||||
|
music_title,
|
||||||
|
level: DxLevelName::try_from(value.level)?,
|
||||||
|
dx_version: value.music_id >= 1000,
|
||||||
|
},
|
||||||
|
achievement_rate: (value.achievement as f64) / 10000.0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, snafu::Snafu)]
|
||||||
|
pub enum ConversionError {
|
||||||
|
#[snafu(display("Music was not found in database"))]
|
||||||
|
MusicNotInDB,
|
||||||
|
|
||||||
|
#[snafu(display("Utage difficulty was disallowed"))]
|
||||||
|
UtageDifficulty,
|
||||||
|
|
||||||
|
#[snafu(display("Unknown difficulty: {level}"))]
|
||||||
|
UnknownDifficulty { level: u32 },
|
||||||
|
}
|
||||||
95
sdgb-api/src/title/model/get_user_rating_api/dxrating/mod.rs
Normal file
95
sdgb-api/src/title/model/get_user_rating_api/dxrating/mod.rs
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
/// Full payload for image generate api
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct DxRatingNet {
|
||||||
|
pub calculated_entries: DxCalculatedEntries,
|
||||||
|
|
||||||
|
pub version: DataVersion,
|
||||||
|
/// use `_generic`
|
||||||
|
pub region: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Export/Import format
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct DxCalculatedEntries {
|
||||||
|
pub b35: Vec<DxMusicRecord>,
|
||||||
|
pub b15: Vec<DxMusicRecord>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// full music record
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct DxMusicRecord {
|
||||||
|
pub sheet_id: DxSheetId,
|
||||||
|
pub achievement_rate: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct DxSheetId {
|
||||||
|
pub music_title: String,
|
||||||
|
pub dx_version: bool,
|
||||||
|
pub level: DxLevelName,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, strum::IntoStaticStr, strum::FromRepr)]
|
||||||
|
#[strum(serialize_all = "lowercase")]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum DxLevelName {
|
||||||
|
Basic,
|
||||||
|
Advanced,
|
||||||
|
Expert,
|
||||||
|
Master,
|
||||||
|
ReMaster,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum DataVersion {
|
||||||
|
Buddies,
|
||||||
|
BuddiesPlus,
|
||||||
|
Prism,
|
||||||
|
PrismPlus,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for DataVersion {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(match self {
|
||||||
|
DataVersion::Buddies => "BUDDiES",
|
||||||
|
DataVersion::BuddiesPlus => "BUDDiES PLUS",
|
||||||
|
DataVersion::Prism => "PRiSM",
|
||||||
|
DataVersion::PrismPlus => "PRiSM PLUS",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for DxSheetId {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(&self.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for DxSheetId {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
let mut output = self.music_title.clone();
|
||||||
|
|
||||||
|
if self.dx_version {
|
||||||
|
output += "__dxrt__dx__dxrt__"
|
||||||
|
} else {
|
||||||
|
output += "__dxrt__std__dxrt__"
|
||||||
|
}
|
||||||
|
|
||||||
|
output += self.level.into();
|
||||||
|
|
||||||
|
output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod conversion;
|
||||||
@@ -168,3 +168,5 @@ impl MusicRating {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod dxrating;
|
||||||
|
|||||||
@@ -14,4 +14,11 @@ mod get_user_data_api;
|
|||||||
pub use get_user_data_api::{GetUserDataApi, GetUserDataApiResp};
|
pub use get_user_data_api::{GetUserDataApi, GetUserDataApiResp};
|
||||||
|
|
||||||
mod get_user_rating_api;
|
mod get_user_rating_api;
|
||||||
|
pub use get_user_rating_api::dxrating::{
|
||||||
|
DxCalculatedEntries, // entries
|
||||||
|
DxLevelName, // level name
|
||||||
|
DxMusicRecord,
|
||||||
|
DxRatingNet,
|
||||||
|
DxSheetId,
|
||||||
|
};
|
||||||
pub use get_user_rating_api::{GetUserRatingApi, GetUserRatingApiResp};
|
pub use get_user_rating_api::{GetUserRatingApi, GetUserRatingApiResp};
|
||||||
|
|||||||
@@ -43,6 +43,13 @@ pub enum Commands {
|
|||||||
Rating {
|
Rating {
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
user_id: u32,
|
user_id: u32,
|
||||||
|
|
||||||
|
/// JSON format.
|
||||||
|
///
|
||||||
|
/// - `origin`: official json response
|
||||||
|
/// - `dx_rating_net`: DxRatingNet Format
|
||||||
|
#[arg(short, long, default_value_t = RatingFormat::default())]
|
||||||
|
format: RatingFormat,
|
||||||
},
|
},
|
||||||
|
|
||||||
// below requires login
|
// below requires login
|
||||||
@@ -71,3 +78,14 @@ pub enum Commands {
|
|||||||
timestamp: u64,
|
timestamp: u64,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, EnumString)]
|
||||||
|
#[strum(serialize_all = "snake_case")]
|
||||||
|
pub enum RatingFormat {
|
||||||
|
#[default]
|
||||||
|
/// Official API response
|
||||||
|
Origin,
|
||||||
|
|
||||||
|
/// dxrating.net format
|
||||||
|
DxRatingNet,
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,16 +15,16 @@ use sdgb_api::{
|
|||||||
MaiVersionExt, Sdgb1_40, Sdgb1_50,
|
MaiVersionExt, Sdgb1_40, Sdgb1_50,
|
||||||
methods::APIMethod,
|
methods::APIMethod,
|
||||||
model::{
|
model::{
|
||||||
GetUserDataApi, GetUserDataApiResp, GetUserPreviewApi, GetUserPreviewApiResp,
|
DxCalculatedEntries, GetUserDataApi, GetUserDataApiResp, GetUserPreviewApi,
|
||||||
GetUserRatingApi, GetUserRatingApiResp, Ping, PingResp, UserLogoutApi,
|
GetUserPreviewApiResp, GetUserRatingApi, GetUserRatingApiResp, Ping, PingResp,
|
||||||
UserLogoutApiResp,
|
UserLogoutApi, UserLogoutApiResp,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use spdlog::{error, info, warn};
|
use spdlog::{error, info, warn};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
commands::Cli,
|
commands::{Cli, RatingFormat},
|
||||||
utils::{human_readable_display, json_display, login_action},
|
utils::{human_readable_display, json_display, login_action},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ async fn main() -> Result<(), Box<dyn snafu::Error>> {
|
|||||||
|
|
||||||
// TODO: refactor via enum_dispatch
|
// TODO: refactor via enum_dispatch
|
||||||
match command {
|
match command {
|
||||||
commands::Commands::Rating { user_id } => {
|
commands::Commands::Rating { user_id, format } => {
|
||||||
let rating: GetUserRatingApiResp = Sdgb1_50::request(
|
let rating: GetUserRatingApiResp = Sdgb1_50::request(
|
||||||
&client,
|
&client,
|
||||||
APIMethod::GetUserRatingApi,
|
APIMethod::GetUserRatingApi,
|
||||||
@@ -86,7 +86,13 @@ async fn main() -> Result<(), Box<dyn snafu::Error>> {
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
human_readable_display(rating, human_readable)?;
|
match (human_readable, format) {
|
||||||
|
(true, _) => println!("{rating}"),
|
||||||
|
(false, RatingFormat::Origin) => json_display(rating)?,
|
||||||
|
(false, RatingFormat::DxRatingNet) => json_display(
|
||||||
|
DxCalculatedEntries::from_user_rating_lossy(&rating.user_rating),
|
||||||
|
)?,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
commands::Commands::Logout { user_id, timestamp } => {
|
commands::Commands::Logout { user_id, timestamp } => {
|
||||||
let logout: UserLogoutApiResp = Sdgb1_50::request(
|
let logout: UserLogoutApiResp = Sdgb1_50::request(
|
||||||
|
|||||||
Reference in New Issue
Block a user