feat: support dxratingnet format

This commit is contained in:
mokurin000
2025-08-01 18:16:16 +08:00
parent 953feee4c4
commit 4e07eaf2e0
6 changed files with 201 additions and 6 deletions

View File

@@ -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 },
}

View 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;

View File

@@ -168,3 +168,5 @@ impl MusicRating {
)
}
}
pub mod dxrating;

View File

@@ -14,4 +14,11 @@ mod get_user_data_api;
pub use get_user_data_api::{GetUserDataApi, GetUserDataApiResp};
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};