From 9e17df0624675fd741b8b1f06a58a1372f1d22ec Mon Sep 17 00:00:00 2001 From: mokurin000 <1348292515a@gmail.com> Date: Sat, 2 Aug 2025 10:03:02 +0800 Subject: [PATCH] feat: convert music detail to dxrating --- sdgb-api/src/all_net/mod.rs | 2 +- sdgb-api/src/helper/music_db/mod.rs | 4 +- .../dxrating/conversion/mod.rs | 50 ++++++++++++++++--- .../{get_user_rating_api => }/dxrating/mod.rs | 0 .../src/title/model/get_user_music_api/mod.rs | 2 +- .../title/model/get_user_rating_api/mod.rs | 4 +- sdgb-api/src/title/model/mod.rs | 19 ++++--- .../src/title/model/user_login_api/mod.rs | 2 +- sdgb-cli/src/commands.rs | 9 +++- sdgb-cli/src/main.rs | 40 +++++++++++---- 10 files changed, 96 insertions(+), 36 deletions(-) rename sdgb-api/src/title/model/{get_user_rating_api => }/dxrating/conversion/mod.rs (54%) rename sdgb-api/src/title/model/{get_user_rating_api => }/dxrating/mod.rs (100%) diff --git a/sdgb-api/src/all_net/mod.rs b/sdgb-api/src/all_net/mod.rs index 184fa56..15e3f94 100644 --- a/sdgb-api/src/all_net/mod.rs +++ b/sdgb-api/src/all_net/mod.rs @@ -58,7 +58,7 @@ impl QRCode<'_> { 2 => Err(QRLoginError::QRCodeExpired10), 1 => Err(QRLoginError::QRCodeExpired30), 50 => Err(QRLoginError::BadSingature), - error_kind @ _ => Err(QRLoginError::Unknown { error_kind }), + error_kind => Err(QRLoginError::Unknown { error_kind }), } } } diff --git a/sdgb-api/src/helper/music_db/mod.rs b/sdgb-api/src/helper/music_db/mod.rs index 3edcf51..3f1c705 100644 --- a/sdgb-api/src/helper/music_db/mod.rs +++ b/sdgb-api/src/helper/music_db/mod.rs @@ -29,8 +29,8 @@ pub fn preload_db() { _ = &*MUSIC_DB; } -pub fn query_music(music_id: &u32) -> Option<&'static MusicInfo> { - MUSIC_DB.as_ref()?.get(music_id) +pub fn query_music(music_id: u32) -> Option<&'static MusicInfo> { + MUSIC_DB.as_ref()?.get(&music_id) } pub fn query_music_level(music_id: u32, level: u32) -> Option<&'static Level> { MUSIC_DB diff --git a/sdgb-api/src/title/model/get_user_rating_api/dxrating/conversion/mod.rs b/sdgb-api/src/title/model/dxrating/conversion/mod.rs similarity index 54% rename from sdgb-api/src/title/model/get_user_rating_api/dxrating/conversion/mod.rs rename to sdgb-api/src/title/model/dxrating/conversion/mod.rs index 896bbd7..c26600d 100644 --- a/sdgb-api/src/title/model/get_user_rating_api/dxrating/conversion/mod.rs +++ b/sdgb-api/src/title/model/dxrating/conversion/mod.rs @@ -1,8 +1,9 @@ use crate::{ helper::query_music, - title::model::get_user_rating_api::{ - MusicRating, UserRating, + title::model::{ dxrating::{DxCalculatedEntries, DxLevelName, DxMusicRecord, DxSheetId}, + get_user_music_api::UserMusicDetail, + get_user_rating_api::{MusicRating, UserRating}, }, }; @@ -33,21 +34,54 @@ impl TryFrom for DxLevelName { } } -impl TryFrom<&MusicRating> for DxMusicRecord { +impl TryFrom<&UserMusicDetail> for DxMusicRecord { type Error = ConversionError; - fn try_from(value: &MusicRating) -> Result { - let music_title = query_music(&value.music_id) + fn try_from( + &UserMusicDetail { + music_id, + level, + achievement, + .. + }: &UserMusicDetail, + ) -> Result { + let music_title = query_music(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 >= 10000, + level: DxLevelName::try_from(level)?, + dx_version: music_id >= 10000, }, - achievement_rate: (value.achievement as f64) / 10000.0, + achievement_rate: (achievement as f64) / 10000.0, + }) + } +} + +impl TryFrom<&MusicRating> for DxMusicRecord { + type Error = ConversionError; + + fn try_from( + &MusicRating { + music_id, + level, + achievement, + .. + }: &MusicRating, + ) -> Result { + let music_title = query_music(music_id) + .map(|info| info.name.clone()) + .ok_or(ConversionError::MusicNotInDB)?; + + Ok(Self { + sheet_id: DxSheetId { + music_title, + level: DxLevelName::try_from(level)?, + dx_version: music_id >= 10000, + }, + achievement_rate: (achievement as f64) / 10000.0, }) } } diff --git a/sdgb-api/src/title/model/get_user_rating_api/dxrating/mod.rs b/sdgb-api/src/title/model/dxrating/mod.rs similarity index 100% rename from sdgb-api/src/title/model/get_user_rating_api/dxrating/mod.rs rename to sdgb-api/src/title/model/dxrating/mod.rs diff --git a/sdgb-api/src/title/model/get_user_music_api/mod.rs b/sdgb-api/src/title/model/get_user_music_api/mod.rs index b2121bc..fd4fd9b 100644 --- a/sdgb-api/src/title/model/get_user_music_api/mod.rs +++ b/sdgb-api/src/title/model/get_user_music_api/mod.rs @@ -70,7 +70,7 @@ pub struct UserMusicDetail { impl Display for UserMusicDetail { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if let Some(music_title) = query_music(&self.music_id).map(|i| &i.name) { + if let Some(music_title) = query_music(self.music_id).map(|i| &i.name) { f.write_fmt(format_args!("曲目名称: \t{music_title}\n"))?; } f.write_fmt(format_args!("难度名称: \t{}\n", level_name(self.level)))?; diff --git a/sdgb-api/src/title/model/get_user_rating_api/mod.rs b/sdgb-api/src/title/model/get_user_rating_api/mod.rs index b2e34d1..acfbe8c 100644 --- a/sdgb-api/src/title/model/get_user_rating_api/mod.rs +++ b/sdgb-api/src/title/model/get_user_rating_api/mod.rs @@ -151,12 +151,10 @@ impl Display for MusicRating { impl MusicRating { pub fn music_title(&self) -> Option { - Some(query_music(&self.music_id).as_ref()?.name.clone()) + Some(query_music(self.music_id).as_ref()?.name.clone()) } pub fn dx_rating(&self) -> Option { Some(query_music_level(self.music_id, self.level)?.dx_rating(self.achievement)) } } - -pub mod dxrating; diff --git a/sdgb-api/src/title/model/mod.rs b/sdgb-api/src/title/model/mod.rs index 1e43862..60d9185 100644 --- a/sdgb-api/src/title/model/mod.rs +++ b/sdgb-api/src/title/model/mod.rs @@ -14,14 +14,6 @@ mod get_user_data_api; pub use get_user_data_api::{GetUserDataApi, GetUserDataApiResp, UserData}; mod get_user_rating_api; -pub use get_user_rating_api::dxrating::{ - DataVersion, - DxCalculatedEntries, // entries - DxLevelName, // level name - DxMusicRecord, - DxRatingNet, - DxSheetId, -}; pub use get_user_rating_api::{ GetUserRatingApi, GetUserRatingApiResp, // api @@ -32,3 +24,14 @@ pub use get_user_rating_api::{ mod get_user_music_api; pub use get_user_music_api::{GetUserMusicApi, GetUserMusicApiResp, UserMusic}; + + +mod dxrating; +pub use dxrating::{ + DataVersion, + DxCalculatedEntries, // entries + DxLevelName, // level name + DxMusicRecord, + DxRatingNet, + DxSheetId, +}; \ No newline at end of file diff --git a/sdgb-api/src/title/model/user_login_api/mod.rs b/sdgb-api/src/title/model/user_login_api/mod.rs index e135957..f678b36 100644 --- a/sdgb-api/src/title/model/user_login_api/mod.rs +++ b/sdgb-api/src/title/model/user_login_api/mod.rs @@ -62,7 +62,7 @@ impl UserLoginApiResp { 100 => Some(LoginError::AlreadyLogged), 102 => Some(LoginError::QRCodeExpired), 103 => Some(LoginError::AccountUnregistered), - error @ _ => Some(LoginError::Unknown { error }), + error => Some(LoginError::Unknown { error }), } } } diff --git a/sdgb-cli/src/commands.rs b/sdgb-cli/src/commands.rs index e44184f..a761d64 100644 --- a/sdgb-cli/src/commands.rs +++ b/sdgb-cli/src/commands.rs @@ -58,6 +58,13 @@ pub enum Commands { MusicDetail { #[arg(short, long)] user_id: u32, + + /// JSON format. + /// + /// - `origin`: official json response + /// - `dx_rating_net`: DxRatingNet Format + #[arg(short, long, default_value_t = RatingFormat::default())] + format: RatingFormat, }, /// Retrieve full userdata @@ -89,7 +96,7 @@ pub enum Commands { }, } -#[derive(Default, EnumString)] +#[derive(Debug, Default, EnumString)] #[strum(serialize_all = "snake_case")] pub enum RatingFormat { #[default] diff --git a/sdgb-cli/src/main.rs b/sdgb-cli/src/main.rs index 0397ab7..2a03a97 100644 --- a/sdgb-cli/src/main.rs +++ b/sdgb-cli/src/main.rs @@ -18,10 +18,10 @@ use sdgb_api::{ MaiVersionExt, Sdgb1_50, methods::APIMethod, model::{ - DataVersion, DxCalculatedEntries, DxRatingNet, GetUserDataApi, GetUserDataApiResp, - GetUserMusicApi, GetUserMusicApiResp, GetUserPreviewApi, GetUserPreviewApiResp, - GetUserRatingApi, GetUserRatingApiResp, Ping, PingResp, UserLogoutApi, - UserLogoutApiResp, + DataVersion, DxCalculatedEntries, DxMusicRecord, DxRatingNet, GetUserDataApi, + GetUserDataApiResp, GetUserMusicApi, GetUserMusicApiResp, GetUserPreviewApi, + GetUserPreviewApiResp, GetUserRatingApi, GetUserRatingApiResp, Ping, PingResp, + UserLogoutApi, UserLogoutApiResp, }, }, }; @@ -80,7 +80,7 @@ async fn main() -> Result<(), Box> { // TODO: refactor via enum_dispatch match command { - Commands::MusicDetail { user_id } => { + Commands::MusicDetail { user_id, format } => { let mut music_detail = Vec::new(); let mut index = None; @@ -111,13 +111,31 @@ async fn main() -> Result<(), Box> { index = Some(next_index); } - if human_readable { - for detail in music_detail { - println!("{detail}"); - println!("----------"); + match (human_readable, format) { + (true, _) => { + for detail in music_detail { + println!("{detail}"); + println!("----------"); + } + } + (false, RatingFormat::Origin) => json_display(music_detail)?, + (false, RatingFormat::DxRatingNet) => { + let dx_export = Vec::from_iter( + music_detail + .iter() + .map(|music| { + DxMusicRecord::try_from(music).inspect_err(|e| { + warn!("failed to process {}: {e}", music.music_id) + }) + }) + .flatten(), + ); + json_display(dx_export)?; + } + (_, format) => { + error!("{format:?} was not supported yet"); + json_display(())?; } - } else { - json_display(music_detail)?; } } Commands::Rating { user_id, format } => {