diff --git a/src/gd.rs b/src/gd.rs index f9f558e..40faf0d 100644 --- a/src/gd.rs +++ b/src/gd.rs @@ -59,8 +59,7 @@ impl Song { req::Client::new() .post("http://boomlings.com/database/getGJSongInfo.php") .body(format!( - r#" {{ "secret": "Wmfd2893gb7", "songID": {} }} "#, - id + r#" {{ "secret": "Wmfd2893gb7", "songID": {id} }} "#, )) .send()? .text()? @@ -79,38 +78,38 @@ impl Song { impl SongResponse { pub fn id(&self) -> Option { - self.0[0].and_then(|s| s.parse().ok()) + self.0[0].as_deref().and_then(|s| s.parse().ok()) } - pub fn name(&self) -> Option { - self.0[1] + pub fn name(&self) -> Option<&str> { + self.0[1].as_deref() } pub fn artist_id(&self) -> Option { - self.0[2].and_then(|s| s.parse().ok()) + self.0[2].as_deref().and_then(|s| s.parse().ok()) } - pub fn artist_name(&self) -> Option { - self.0[3] + pub fn artist_name(&self) -> Option<&str> { + self.0[3].as_deref() } pub fn size(&self) -> Option { - self.0[4].and_then(|s| s.parse().ok()) + self.0[4].as_deref().and_then(|s| s.parse().ok()) } - pub fn video_id(&self) -> Option { - self.0[5] + pub fn video_id(&self) -> Option<&str> { + self.0[5].as_deref() } - pub fn youtube_url(&self) -> Option { - self.0[6] + pub fn youtube_url(&self) -> Option<&str> { + self.0[6].as_deref() } pub fn song_priority(&self) -> Option { - self.0[8].and_then(|s| s.parse().ok()) + self.0[8].as_deref().and_then(|s| s.parse().ok()) } pub fn download_link(&self) -> Option { - self.0[9].and_then(|url| urlencoding::decode(&url).ok().map(|url| url.into_owned())) + self.0[9].as_deref().and_then(|url| urlencoding::decode(url).ok().map(std::borrow::Cow::into_owned)) } } impl OuterLevel { - pub fn load_all() -> Vec { + pub fn load_all() -> Vec { let plist = get_local_level_plist(); - let levels: Vec = plist + let levels: Vec = plist .as_dictionary() .and_then(|dict| dict.get("LLM_01")) .unwrap() @@ -125,7 +124,7 @@ impl OuterLevel { builder.with_name(title.as_string().unwrap().into()); } if let Some(rev) = props.get("k46") { - builder.with_revision(rev.as_signed_integer().unwrap().into()); + builder.with_revision(rev.as_signed_integer().unwrap()); } builder.build_outer_level().unwrap() }) @@ -134,30 +133,28 @@ impl OuterLevel { } pub fn display_name(&self) -> LayoutJob { - match self.revision { - Some(rev) => { - let mut job = LayoutJob::default(); - job.append(&format!("{} ", self.name), 0f32, TextFormat::default()); - job.append( - &format!("(rev {})", rev), - 0f32, - TextFormat { - italics: true, - ..Default::default() - }, - ); - job - } - None => { - let mut job = LayoutJob::default(); - job.append(&self.name, 0f32, TextFormat::default()); - job - } + if let Some(rev) = self.revision { + let mut job = LayoutJob::default(); + job.append(&format!("{} ", self.name), 0f32, TextFormat::default()); + job.append( + &format!("(rev {rev})"), + 0f32, + TextFormat { + italics: true, + ..Default::default() + }, + ); + job + } + else { + let mut job = LayoutJob::default(); + job.append(&self.name, 0f32, TextFormat::default()); + job } } } -pub fn gd_path() -> PathBuf { +pub fn save_path() -> PathBuf { let mut path_buf = home::home_dir().unwrap(); #[cfg(unix)] path_buf.extend( @@ -183,22 +180,13 @@ pub fn gd_path() -> PathBuf { path_buf } +#[derive(Default)] struct LevelBuilder { name: Option, song: Option, revision: Option, } -impl Default for LevelBuilder { - fn default() -> Self { - Self { - name: None, - song: None, - revision: None, - } - } -} - impl LevelBuilder { fn new() -> Self { Self::default() @@ -245,7 +233,7 @@ impl LevelBuilder { fn get_local_level_plist() -> Value { let raw_save_data = { let mut save_file = - File::open(gd_path().join("CCLocalLevels.dat")).expect("No save file found!"); + File::open(save_path().join("CCLocalLevels.dat")).expect("No save file found!"); let mut sd = Vec::new(); save_file.read_to_end(&mut sd).unwrap(); sd @@ -258,7 +246,7 @@ fn get_local_level_plist() -> Value { let data_post_b64 = URL_SAFE.decode(data_post_xor).unwrap(); let mut decoder = GzDecoder::<&[u8]>::new(data_post_b64.as_ref()); let mut plist = String::new(); - if let Err(_) = decoder.read_to_string(&mut plist) { + if decoder.read_to_string(&mut plist).is_err() { println!("Warning: Game save likely corrupted (gzip decode failed)"); } Value::from_reader(Cursor::new(plist)).unwrap() diff --git a/src/main.rs b/src/main.rs index 746fb01..9189fad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,9 @@ #![feature(iter_array_chunks)] +//#![warn(clippy::all, clippy::pedantic, clippy::nursery)] mod gd; mod music; -use eframe; use eframe::egui; use reqwest::blocking as req; use std::boxed::Box; @@ -109,41 +109,43 @@ impl From for eframe::epaint::Color32 { impl Song { pub fn try_new(gd_song: gd::Song) -> Result { - if let gd::Song::Newgrounds { id } = &gd_song { + if let gd::Song::Newgrounds { id } = gd_song { let song_response = gd_song.get_response(); - let song_path = gd::gd_path().join(format!("{}.mp3", id)); - File::open(&song_path) - .or_else(|_| { - song_response.clone().map_err(|e| e.into()).and_then( - |response: gd::SongResponse| -> Result { - let song_blob = response - .download_link() - .ok_or(SongError::MissingLink) - .and_then(|link| Ok(req::get(link)?.bytes()?))?; - let mut file = File::open(&song_path)?; - file.write(&song_blob); - Ok(file) - }, - ) - }) - .and_then(|file| { - let name = song_response + let song_path = gd::save_path().join(format!("{id}.mp3")); + + let (file, name) = match (File::open(&song_path), song_response) { + (Ok(file), response) => { + let name = response .ok() - .and_then(|response| response.name()) - .unwrap_or("".into()); - let decoder = rodio::Decoder::new_mp3(file)?; - Ok(Song { - name, - id: *id, - decoder, - }) - }) + .and_then(|response| response.name().map(Into::into)) + .unwrap_or_default(); + + (file, name) + } + (Err(_), Ok(response)) => { + let song_blob = response + .download_link() + .ok_or(SongError::MissingLink) + .and_then(|link| Ok(req::get(link)?.bytes()?))?; + + let mut file = File::open(&song_path)?; + file.write_all(&song_blob)?; + + (file, response.name().unwrap_or("").into()) + } + (Err(err), Err(_)) => return Err(err.into()), + }; + + let decoder = rodio::Decoder::new_mp3(file)?; + + Ok(Self { name, id, decoder }) } else { Err(SongError::NotNewgrounds) } } } + impl Editor { pub fn beat_rate_widget(&mut self) -> BeatRateWidget { BeatRateWidget { @@ -316,7 +318,7 @@ impl PipeDash { ui.add(self.editor.lines_widget(Color::Green)); ui.add(self.editor.lines_widget(Color::Orange)); ui.add(self.editor.lines_widget(Color::Yellow)); - ui.add(self.editor.waveform_widget(&song)); + ui.add(self.editor.waveform_widget(song)); } }); }); @@ -324,7 +326,7 @@ impl PipeDash { fn handle_messages(&mut self) { for message in self.msg_queue.drain(..) { - println!("{:?}", message); + println!("{message:?}"); match message { Message::LevelSelected(idx) => { self.selected_level = Some(idx); diff --git a/src/music.rs b/src/music.rs index 406a7bb..7e1e353 100644 --- a/src/music.rs +++ b/src/music.rs @@ -72,14 +72,14 @@ impl BeatRate { /// Changes: when the time signature changes, the bar immediately resets impl StaticTimeSignature { - pub fn new(numerator: u32, denominator: u32) -> Self { + pub const fn new(numerator: u32, denominator: u32) -> Self { Self { numerator, denominator, } } - fn beats_per_bar(&self) -> BeatPosition { + fn beats_per_bar(self) -> BeatPosition { (self.numerator as f32).into() } } @@ -113,7 +113,7 @@ impl TimeSignature { .1 } } - none => self.initial, + None => self.initial, } }