diff --git a/src/app.rs b/src/app.rs index 173fe0e..2cb612b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,13 +1,11 @@ use std::{ - error::Error, env, + error::Error, path::PathBuf, sync::{ - mpsc::Receiver, - Arc, - Mutex, - MutexGuard, atomic::{AtomicBool, AtomicU64, Ordering}, + mpsc::Receiver, + Arc, Mutex, MutexGuard, }, thread, thread::JoinHandle, @@ -24,16 +22,15 @@ use ratatui::{ use rodio::OutputStream; use crate::{ + components::{FileBrowser, FileBrowserSelection, Help, Library, Playlists, Queue}, config::Theme, - structs::{Song, Action, Actions, OnAction}, player::Player, state::State, + structs::{Action, Actions, OnAction, OnActionMut, ScreenAction, Song}, term::set_terminal, - ui::{KeyboardHandlerRef, KeyboardHandlerMut, TopBar, Component}, + ui::{Component, KeyboardHandlerMut, KeyboardHandlerRef, TopBar}, Command, - components::{FileBrowser, FileBrowserSelection, Library, Playlists, Queue, Help}, }; -use crate::structs::{OnActionMut, ScreenAction}; pub struct App<'a> { must_quit: bool, @@ -91,21 +88,23 @@ impl<'a> App<'a> { player.play_song(song); } }); - library.on_select_songs_fn({ // selected artist/album + library.on_select_songs_fn({ + // selected artist/album let queue = queue.clone(); let library = library.clone(); move |songs| { log::trace!(target: "::app.library", "on_select_songs_fn -> adding songs to queue"); queue.append(&mut std::collections::VecDeque::from(songs)); - library.on_key(KeyEvent::new(KeyCode::Down, KeyModifiers::NONE)); // hackish way to "select_next()" + // hackish way to "select_next()": + library.on_key(KeyEvent::new(KeyCode::Down, KeyModifiers::NONE)); } }); let playlist = Arc::new(Playlists::new(theme)); playlist.on_enter_song({ let queue = queue.clone(); - move |song | { + move |song| { queue.add_back(song); } }); @@ -136,7 +135,14 @@ impl<'a> App<'a> { let media_library = Arc::clone(&library); move |(s, key_event)| { - Self::on_file_browser_select(player.as_ref(), queue.as_ref(), playlists.as_ref(), media_library.as_ref(), s, key_event); + Self::on_file_browser_select( + player.as_ref(), + queue.as_ref(), + playlists.as_ref(), + media_library.as_ref(), + s, + key_event, + ); } }); @@ -171,7 +177,7 @@ impl<'a> App<'a> { } } - fn file_browser(&self) -> MutexGuard> { + fn file_browser(&self) -> MutexGuard> { self.browser.lock().unwrap() } @@ -226,27 +232,30 @@ impl<'a> App<'a> { let player_command_receiver = self.player_command_receiver.clone(); let player = self.player.clone(); - let t = thread::Builder::new().name("media_key_rx".to_string()).spawn(move || { - loop { - match player_command_receiver.lock().unwrap().recv() { - Ok(Command::PlayPause) => { - player.toggle(); - } - Ok(Command::Next) => { - player.stop(); - } - Ok(Command::Quit) => { - log::debug!("Received Command::Quit"); - break; - } - Err(err) => { - log::error!("Channel error: {}", err); - break; + let t = thread::Builder::new() + .name("media_key_rx".to_string()) + .spawn(move || { + loop { + match player_command_receiver.lock().unwrap().recv() { + Ok(Command::PlayPause) => { + player.toggle(); + } + Ok(Command::Next) => { + player.stop(); + } + Ok(Command::Quit) => { + log::debug!("Received Command::Quit"); + break; + } + Err(err) => { + log::error!("Channel error: {}", err); + break; + } } } - } - log::trace!("spawn_media_key_receiver_thread loop exit"); - }).unwrap(); + log::trace!("spawn_media_key_receiver_thread loop exit"); + }) + .unwrap(); self.player_command_receiver_thread = Some(t); } @@ -349,13 +358,13 @@ impl<'a> WidgetRef for &App<'a> { .style(Style::default().bg(self.theme.background)) .render(area, buf); - let [area_top, _, area_center, area_bottom] = - Layout::vertical([ - Constraint::Length(1), - Constraint::Length(1), - Constraint::Min(0), - Constraint::Length(3), - ]).areas(area); + let [area_top, _, area_center, area_bottom] = Layout::vertical([ + Constraint::Length(1), + Constraint::Length(1), + Constraint::Min(0), + Constraint::Length(3), + ]) + .areas(area); let screen_titles: Vec<&str> = self.screens.iter().map(|screen| screen.0.as_str()).collect(); @@ -373,8 +382,12 @@ impl<'a> WidgetRef for &App<'a> { }; match component { - Component::RefArc(ref s) => { s.render_ref(area_center, buf); } - Component::Mut(ref s) => { s.lock().unwrap().render_ref(area_center, buf); } + Component::RefArc(ref s) => { + s.render_ref(area_center, buf); + } + Component::Mut(ref s) => { + s.lock().unwrap().render_ref(area_center, buf); + } _ => {} } @@ -385,15 +398,13 @@ impl<'a> WidgetRef for &App<'a> { impl<'a> OnActionMut for App<'a> { fn on_action(&mut self, action: Action) { match action { - Action::ScreenAction(action) => { - match action { - ScreenAction::Library => { self.focused_screen = 0 } - ScreenAction::Playlists => { self.focused_screen = 1 } - ScreenAction::Queue => { self.focused_screen = 2 } - ScreenAction::FileBrowser => { self.focused_screen = 3 } - ScreenAction::Help => { self.focused_screen = 4 } - } - } + Action::ScreenAction(action) => match action { + ScreenAction::Library => self.focused_screen = 0, + ScreenAction::Playlists => self.focused_screen = 1, + ScreenAction::Queue => self.focused_screen = 2, + ScreenAction::FileBrowser => self.focused_screen = 3, + ScreenAction::Help => self.focused_screen = 4, + }, _ => {} } } diff --git a/src/auto_update.rs b/src/auto_update.rs index c50ab20..4d51469 100644 --- a/src/auto_update.rs +++ b/src/auto_update.rs @@ -40,8 +40,8 @@ pub enum ReleasesError { impl std::fmt::Debug for ReleasesError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - ReleasesError::Reqwest(e) => { e.fmt(f) } - ReleasesError::TomlFileError(e) => { e.fmt(f) } + ReleasesError::Reqwest(e) => e.fmt(f), + ReleasesError::TomlFileError(e) => e.fmt(f), } } } @@ -73,9 +73,7 @@ pub fn get_releases() -> Result<(), ReleasesError> { log::debug!(target: "::get_releases", "Got releases = {releases:#?}"); - let releases = Releases { - releases, - }; + let releases = Releases { releases }; write_toml_file("releases", &releases)?; @@ -93,7 +91,6 @@ pub fn can_i_has_rls() -> Result<(), ReleasesError> { log::trace!(target: target, "we has rls published_at = {:?}", releases.releases.get(0).map(|r| r.published_at.as_str())); - Ok(()) } diff --git a/src/bye.rs b/src/bye.rs index 8f50b88..bbcbf26 100644 --- a/src/bye.rs +++ b/src/bye.rs @@ -1,4 +1,4 @@ -use std::time::{UNIX_EPOCH, Duration, SystemTime}; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; const BYE_N: usize = 9; const BYE: [&str; BYE_N] = [ @@ -15,7 +15,9 @@ const BYE: [&str; BYE_N] = [ /// The important things in life pub fn bye() -> &'static str { - let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or(Duration::from_secs(0)); + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or(Duration::from_secs(0)); let now = now.as_micros() as usize; let i = now % BYE_N; BYE[i] diff --git a/src/components.rs b/src/components.rs index e22a533..ff319ba 100644 --- a/src/components.rs +++ b/src/components.rs @@ -1,14 +1,14 @@ mod file_browser; -mod library; -mod queue; mod help; -mod playlists; +mod library; mod list; +mod playlists; +mod queue; mod rendering_error; pub use file_browser::{directory_to_songs_and_folders, FileBrowser, FileBrowserSelection}; +pub use help::Help; pub use library::Library; +pub use list::List; pub use playlists::Playlists; pub use queue::Queue; -pub use help::Help; -pub use list::List; diff --git a/src/components/file_browser.rs b/src/components/file_browser.rs index 61107a5..bd1443e 100644 --- a/src/components/file_browser.rs +++ b/src/components/file_browser.rs @@ -1,7 +1,7 @@ pub mod file_browser; -pub mod widget; -pub mod keyboard_handler; mod file_browser_selection; +pub mod keyboard_handler; +pub mod widget; pub use file_browser::*; -pub use file_browser_selection::{FileBrowserSelection, directory_to_songs_and_folders}; +pub use file_browser_selection::{directory_to_songs_and_folders, FileBrowserSelection}; diff --git a/src/components/file_browser/file_browser.rs b/src/components/file_browser/file_browser.rs index 5201f88..80453c4 100644 --- a/src/components/file_browser/file_browser.rs +++ b/src/components/file_browser/file_browser.rs @@ -1,16 +1,10 @@ -use std::{ - path::PathBuf, - sync::Mutex, -}; +use std::{path::PathBuf, sync::Mutex}; use crossterm::event::{KeyCode, KeyEvent}; use crate::config::Theme; -use super::file_browser_selection::{ - FileBrowserSelection, - directory_to_songs_and_folders, -}; +use super::file_browser_selection::{directory_to_songs_and_folders, FileBrowserSelection}; pub struct FileBrowser<'a> { on_select_fn: Box, @@ -100,7 +94,9 @@ impl<'a> FileBrowser<'a> { } pub fn navigate_up(&mut self) { - let Some(parent) = self.current_directory.as_path().parent().map(|p| p.to_path_buf()) else { return }; + let Some(parent) = self.current_directory.as_path().parent().map(|p| p.to_path_buf()) else { + return; + }; self.items = directory_to_songs_and_folders(&parent); self.select_current_directory(); self.current_directory = parent; @@ -183,7 +179,12 @@ impl<'a> FileBrowser<'a> { return None; } - if self.items[i].to_path().to_string_lossy().to_lowercase().contains(&s.to_lowercase()) { + if self.items[i] + .to_path() + .to_string_lossy() + .to_lowercase() + .contains(&s.to_lowercase()) + { return Some(i); } } @@ -236,8 +237,6 @@ impl<'a> FileBrowser<'a> { }; } - - fn padding_top(&self) -> usize { 6 } @@ -248,12 +247,12 @@ impl<'a> FileBrowser<'a> { pub fn set_offset(&mut self, i: usize, padding: usize) { self.offset = if i > padding { - i.saturating_sub(padding).min(self.items.len().saturating_sub(*self.height.lock().unwrap())) + i.saturating_sub(padding) + .min(self.items.len().saturating_sub(*self.height.lock().unwrap())) } else { 0 }; } - } impl Drop for FileBrowser<'_> { diff --git a/src/components/file_browser/file_browser_selection.rs b/src/components/file_browser/file_browser_selection.rs index 5b5fa98..1dd1739 100644 --- a/src/components/file_browser/file_browser_selection.rs +++ b/src/components/file_browser/file_browser_selection.rs @@ -1,13 +1,8 @@ -use std::{ - fs, - fs::DirEntry, - path::PathBuf, - cmp::Ordering, -}; +use std::{cmp::Ordering, fs, fs::DirEntry, path::PathBuf}; use crate::{ - structs::{Song, Jolt}, cue::CueSheet, + structs::{Jolt, Song}, }; const VALID_EXTENSIONS: [&str; 7] = ["mp3", "mp4", "m4a", "wav", "flac", "ogg", "aac"]; @@ -33,10 +28,10 @@ impl FileBrowserSelection { pub fn to_path(&self) -> PathBuf { match self { - FileBrowserSelection::Song(s) => { s.path.clone() } - FileBrowserSelection::CueSheet(cs) => { cs.cue_sheet_file_path() } - FileBrowserSelection::Directory(p) => { p.clone() } - FileBrowserSelection::Jolt(j) => { j.path.clone() } + FileBrowserSelection::Song(s) => s.path.clone(), + FileBrowserSelection::CueSheet(cs) => cs.cue_sheet_file_path(), + FileBrowserSelection::Directory(p) => p.clone(), + FileBrowserSelection::Jolt(j) => j.path.clone(), } } } @@ -44,31 +39,24 @@ impl FileBrowserSelection { impl PartialEq for FileBrowserSelection { fn eq(&self, other: &Self) -> bool { match self { - FileBrowserSelection::Directory(path) => { - match other { - FileBrowserSelection::Directory(other_path) => path == other_path, - _ => false, - } - } - FileBrowserSelection::CueSheet(cue_sheet) => { - match other { - FileBrowserSelection::CueSheet(other_cue_sheet) => - cue_sheet.cue_sheet_file_path() == other_cue_sheet.cue_sheet_file_path(), - _ => false, - } - } - FileBrowserSelection::Song(song) => { - match other { - FileBrowserSelection::Song(other_song) => song.path == other_song.path, - _ => false, - } - } - FileBrowserSelection::Jolt(jolt) => { - match other { - FileBrowserSelection::Jolt(j) => jolt.path == j.path, - _ => false, + FileBrowserSelection::Directory(path) => match other { + FileBrowserSelection::Directory(other_path) => path == other_path, + _ => false, + }, + FileBrowserSelection::CueSheet(cue_sheet) => match other { + FileBrowserSelection::CueSheet(other_cue_sheet) => { + cue_sheet.cue_sheet_file_path() == other_cue_sheet.cue_sheet_file_path() } - } + _ => false, + }, + FileBrowserSelection::Song(song) => match other { + FileBrowserSelection::Song(other_song) => song.path == other_song.path, + _ => false, + }, + FileBrowserSelection::Jolt(jolt) => match other { + FileBrowserSelection::Jolt(j) => jolt.path == j.path, + _ => false, + }, } } } @@ -98,8 +86,9 @@ impl PartialOrd for FileBrowserSelection { match other { FileBrowserSelection::Directory(_) => Ordering::Greater, FileBrowserSelection::Jolt(_) => Ordering::Greater, - FileBrowserSelection::CueSheet(other_cue_sheet) => - cue_sheet.cue_sheet_file_path().cmp(&other_cue_sheet.cue_sheet_file_path()), + FileBrowserSelection::CueSheet(other_cue_sheet) => cue_sheet + .cue_sheet_file_path() + .cmp(&other_cue_sheet.cue_sheet_file_path()), _ => Ordering::Less, } } @@ -120,7 +109,7 @@ impl Ord for FileBrowserSelection { } } -fn dir_entry_to_file_browser_selection(entry: &DirEntry) -> Option{ +fn dir_entry_to_file_browser_selection(entry: &DirEntry) -> Option { if dir_entry_is_dir(&entry) { Some(FileBrowserSelection::Directory(entry.path())) } else if dir_entry_is_song(&entry) { @@ -129,10 +118,12 @@ fn dir_entry_to_file_browser_selection(entry: &DirEntry) -> Option { log::warn!("dir_entry_to_file_browser_selection {:#?} {:#?}", &entry.path(), err); None - }, + } } } else if dir_entry_is_cue(&entry) { - Some(FileBrowserSelection::CueSheet(CueSheet::from_file(&entry.path()).unwrap())) + Some(FileBrowserSelection::CueSheet( + CueSheet::from_file(&entry.path()).unwrap(), + )) } else if dir_entry_is_jolt_file(&entry) { match Jolt::from_path(entry.path()) { Ok(jolt) => Some(FileBrowserSelection::Jolt(jolt)), @@ -168,7 +159,10 @@ pub fn dir_entry_is_file(dir_entry: &DirEntry) -> bool { pub fn dir_entry_is_dir(dir_entry: &DirEntry) -> bool { let Ok(ft) = dir_entry.file_type() else { - log::error!("dir_entry_is_dir: .file_type() returned error for {:?}", dir_entry.path()); + log::error!( + "dir_entry_is_dir: .file_type() returned error for {:?}", + dir_entry.path() + ); return false; }; @@ -200,17 +194,11 @@ pub fn dir_entry_is_song(dir_entry: &DirEntry) -> bool { } pub fn dir_entry_has_cue_extension(dir_entry: &DirEntry) -> bool { - dir_entry - .path() - .extension() - .is_some_and(|e| e == "cue") + dir_entry.path().extension().is_some_and(|e| e == "cue") } pub fn dir_entry_is_jolt_file(dir_entry: &DirEntry) -> bool { - dir_entry - .path() - .file_name() - .is_some_and(|e| e == ".jolt") + dir_entry.path().file_name().is_some_and(|e| e == ".jolt") } pub fn dir_entry_is_cue(dir_entry: &DirEntry) -> bool { diff --git a/src/components/file_browser/keyboard_handler.rs b/src/components/file_browser/keyboard_handler.rs index da49c77..ca7cb5d 100644 --- a/src/components/file_browser/keyboard_handler.rs +++ b/src/components/file_browser/keyboard_handler.rs @@ -20,10 +20,10 @@ impl<'a> FileBrowser<'a> { KeyCode::Backspace => self.navigate_up(), KeyCode::Down => { self.select_next(); - }, + } KeyCode::Up => { self.select_previous(); - }, + } // KeyCode::PageUp => self.items.previous_by(5), // KeyCode::PageDown => self.items.next_by(5), KeyCode::End => self.select_last(), @@ -33,7 +33,7 @@ impl<'a> FileBrowser<'a> { } KeyCode::Enter | KeyCode::Char(_) => { self.enter_selection(key); - }, + } _ => {} } } diff --git a/src/components/file_browser/widget.rs b/src/components/file_browser/widget.rs index 4c50ff9..a79205e 100644 --- a/src/components/file_browser/widget.rs +++ b/src/components/file_browser/widget.rs @@ -6,16 +6,7 @@ use ratatui::{ layout::{Alignment, Constraint, Layout, Rect}, prelude::{Line, Modifier, Span, Style}, text::Text, - widgets::{ - block::Position, - Block, - Borders, - List, - WidgetRef, - StatefulWidget, - ListState, - ListItem, - }, + widgets::{block::Position, Block, Borders, List, ListItem, ListState, StatefulWidget, WidgetRef}, }; use crate::config::Theme; @@ -51,11 +42,14 @@ impl<'a> WidgetRef for FileBrowser<'a> { fl, area_main_left, buf, - &mut ListState::default().with_offset(self.offset).with_selected(Some(self.selected_index)), + &mut ListState::default() + .with_offset(self.offset) + .with_selected(Some(self.selected_index)), ); - let [_separator_left, _, _separator_right] = Layout::horizontal([Constraint::Min(1), Constraint::Length(1), Constraint::Min(1)]) - .areas(area_main_separator); + let [_separator_left, _, _separator_right] = + Layout::horizontal([Constraint::Min(1), Constraint::Length(1), Constraint::Min(1)]) + .areas(area_main_separator); } } @@ -64,13 +58,12 @@ fn create_areas(area: Rect) -> (Rect, Rect, Rect, Rect) { .horizontal_margin(2) .areas(area); - let [area_main_left, area_main_separator, area_main_right] = - Layout::horizontal([ - Constraint::Percentage(50), - Constraint::Length(5), - Constraint::Percentage(50), - ]) - .areas(area_main); + let [area_main_left, area_main_separator, area_main_right] = Layout::horizontal([ + Constraint::Percentage(50), + Constraint::Length(5), + Constraint::Percentage(50), + ]) + .areas(area_main); (area_top, area_main_left, area_main_separator, area_main_right) } diff --git a/src/components/help/help.rs b/src/components/help/help.rs index 83da411..7a3cfa8 100644 --- a/src/components/help/help.rs +++ b/src/components/help/help.rs @@ -1,15 +1,12 @@ use crossterm::event::{KeyCode, KeyEvent}; use ratatui::{ + buffer::Buffer, layout::{Alignment, Constraint, Layout, Rect}, style::{Modifier, Style}, widgets::{Block, BorderType, Borders, Cell, Row, Table, TableState, WidgetRef}, - buffer::Buffer, }; -use crate::{ - config::Theme, - ui::{KeyboardHandlerMut}, -}; +use crate::{config::Theme, ui::KeyboardHandlerMut}; pub struct Help<'a> { theme: Theme, @@ -76,7 +73,7 @@ impl<'a> KeyboardHandlerMut<'a> for Help<'a> { match key.code { KeyCode::Down | KeyCode::Char('j') => self.next(), KeyCode::Up | KeyCode::Char('k') => self.previous(), - _ => {}, + _ => {} } } } @@ -100,11 +97,7 @@ impl<'a> WidgetRef for Help<'a> { .map(|h| Cell::from(*h).style(Style::default().fg(self.theme.foreground_selected))); let header = Row::new(header) - .style( - Style::default() - .bg(self.theme.background) - .fg(self.theme.foreground), - ) + .style(Style::default().bg(self.theme.background).fg(self.theme.foreground)) .height(1) .bottom_margin(0); @@ -130,11 +123,7 @@ impl<'a> WidgetRef for Help<'a> { .title_alignment(Alignment::Center) .border_type(BorderType::Plain), ) - .style( - Style::default() - .fg(self.theme.foreground) - .bg(self.theme.background), - ) + .style(Style::default().fg(self.theme.foreground).bg(self.theme.background)) .row_highlight_style( Style::default() .add_modifier(Modifier::BOLD) @@ -145,12 +134,9 @@ impl<'a> WidgetRef for Help<'a> { ratatui::widgets::StatefulWidgetRef::render_ref(&table, area_main, buf, &mut self.state.clone()); // table.render_ref(layout[0], buf, &mut self.state.clone()); - } } - - impl Drop for Help<'_> { fn drop(&mut self) { log::trace!("HelpTab.drop()"); diff --git a/src/components/library.rs b/src/components/library.rs index 506d24f..8322be7 100644 --- a/src/components/library.rs +++ b/src/components/library.rs @@ -1,6 +1,6 @@ +pub mod keyboard_handler; pub mod library; pub mod widget; -pub mod keyboard_handler; mod album_tree; diff --git a/src/components/library/album_tree.rs b/src/components/library/album_tree.rs index 52644c5..bd9be26 100644 --- a/src/components/library/album_tree.rs +++ b/src/components/library/album_tree.rs @@ -1,7 +1,7 @@ +mod album_tree_item; mod component; -mod widget; mod keyboard_handler; -mod album_tree_item; +mod widget; -pub use component::AlbumTree; pub use album_tree_item::AlbumTreeItem; +pub use component::AlbumTree; diff --git a/src/components/library/album_tree/album_tree_item.rs b/src/components/library/album_tree/album_tree_item.rs index ad1ce0a..a552b4a 100644 --- a/src/components/library/album_tree/album_tree_item.rs +++ b/src/components/library/album_tree/album_tree_item.rs @@ -12,18 +12,10 @@ pub enum AlbumTreeItem { impl Ord for AlbumTreeItem { fn cmp(&self, other: &Self) -> Ordering { match (self, other) { - (AlbumTreeItem::Artist(a), AlbumTreeItem::Artist(b)) => { - a.cmp(b) - } - (AlbumTreeItem::Artist(_), AlbumTreeItem::Album(_, _)) => { - Ordering::Greater - } - (AlbumTreeItem::Album(_, _), AlbumTreeItem::Artist(_)) => { - Ordering::Less - } - (AlbumTreeItem::Album(_, ref a), AlbumTreeItem::Album(_, ref b)) => { - a.cmp(b) - } + (AlbumTreeItem::Artist(a), AlbumTreeItem::Artist(b)) => a.cmp(b), + (AlbumTreeItem::Artist(_), AlbumTreeItem::Album(_, _)) => Ordering::Greater, + (AlbumTreeItem::Album(_, _), AlbumTreeItem::Artist(_)) => Ordering::Less, + (AlbumTreeItem::Album(_, ref a), AlbumTreeItem::Album(_, ref b)) => a.cmp(b), } } } diff --git a/src/components/library/album_tree/component.rs b/src/components/library/album_tree/component.rs index fee1ea8..2c505b9 100644 --- a/src/components/library/album_tree/component.rs +++ b/src/components/library/album_tree/component.rs @@ -1,13 +1,9 @@ -use std::{ - sync::{ - atomic::{AtomicUsize, Ordering as AtomicOrdering}, - Mutex, - }, +use std::sync::{ + atomic::{AtomicUsize, Ordering as AtomicOrdering}, + Mutex, }; -use crate::{ - config::Theme, -}; +use crate::config::Theme; use super::AlbumTreeItem; @@ -81,12 +77,15 @@ impl<'a> AlbumTree<'a> { let selected_album = self.selected_album.load(AtomicOrdering::SeqCst); let Some(album) = artist.albums.get(selected_album) else { - log::error!("artist {} selected_album {selected_album} >= len {}", artist.artist, artist.albums.len()); + log::error!( + "artist {} selected_album {selected_album} >= len {}", + artist.artist, + artist.albums.len() + ); panic!("no album at selected index!"); }; Some(AlbumTreeItem::Album(artist.artist.clone(), album.clone())) - } } diff --git a/src/components/library/album_tree/keyboard_handler.rs b/src/components/library/album_tree/keyboard_handler.rs index 57f4cba..9c566cd 100644 --- a/src/components/library/album_tree/keyboard_handler.rs +++ b/src/components/library/album_tree/keyboard_handler.rs @@ -2,17 +2,11 @@ use std::sync::atomic::Ordering; use crossterm::event::{KeyCode, KeyEvent}; -use crate::{ - ui::KeyboardHandlerRef, -}; +use crate::ui::KeyboardHandlerRef; -use super::{ - component::AlbumTree, - album_tree_item::AlbumTreeItem, -}; +use super::{album_tree_item::AlbumTreeItem, component::AlbumTree}; impl<'a> KeyboardHandlerRef<'a> for AlbumTree<'a> { - fn on_key(&self, key: KeyEvent) { let target = "::ArtistList.on_key"; log::trace!(target: target, "{:?}", key); @@ -26,14 +20,14 @@ impl<'a> KeyboardHandlerRef<'a> for AlbumTree<'a> { return; }; self.on_select_fn.lock().unwrap()(item); - }, + } KeyCode::Enter => { let Some(item) = self.selected_item() else { log::warn!(target: target, "No selected item"); return; }; self.on_confirm_fn.lock().unwrap()(item); - }, + } KeyCode::Char(' ') => { let selected_artist = self.selected_artist.load(Ordering::SeqCst); let mut artist_list = self.artist_list.lock().unwrap(); @@ -50,7 +44,7 @@ impl<'a> KeyboardHandlerRef<'a> for AlbumTree<'a> { let item = AlbumTreeItem::Album(selected_artist.artist.clone(), album.clone()); self.on_select_fn.lock().unwrap()(item); } - }, + } KeyCode::Delete => { let selected_artist = self.selected_artist.load(Ordering::SeqCst); let selected_album = self.selected_album.load(Ordering::SeqCst); @@ -103,7 +97,7 @@ impl<'a> KeyboardHandlerRef<'a> for AlbumTree<'a> { self.on_delete_fn.lock().unwrap()(removed_item); self.on_select_fn.lock().unwrap()(newly_selected_item); - }, + } KeyCode::Char(char) => { let item = { let mut filter = self.filter.lock().unwrap(); @@ -114,7 +108,8 @@ impl<'a> KeyboardHandlerRef<'a> for AlbumTree<'a> { for i in 0..artists.len() { let entry = &mut artists[i]; - entry.is_match = entry.artist.contains(filter.as_str()) || entry.artist.to_lowercase().contains(filter.to_lowercase().as_str()); + entry.is_match = entry.artist.contains(filter.as_str()) + || entry.artist.to_lowercase().contains(filter.to_lowercase().as_str()); } let selected_artist_index = self.selected_artist.load(Ordering::SeqCst); @@ -146,14 +141,12 @@ impl<'a> KeyboardHandlerRef<'a> for AlbumTree<'a> { entry.is_match = false; } } - _ => {}, + _ => {} } } } - impl<'a> AlbumTree<'a> { - fn on_artist_list_directional_key(&self, key: KeyEvent) { let artists = self.artist_list.lock().unwrap(); let length = { @@ -204,23 +197,29 @@ impl<'a> AlbumTree<'a> { height.saturating_sub(padding).saturating_sub(1) }; - let visible_items: usize = artists.iter().take(i as usize).filter(|a| a.is_open).map(|a| a.albums.len()).sum(); + let visible_items: usize = artists + .iter() + .take(i as usize) + .filter(|a| a.is_open) + .map(|a| a.albums.len()) + .sum(); let visible_items = visible_items as i32 + i + j; - if (key.code == KeyCode::Up && visible_items < offset + padding + 1) || (key.code == KeyCode::Down && visible_items > offset + padding) { + if (key.code == KeyCode::Up && visible_items < offset + padding + 1) + || (key.code == KeyCode::Down && visible_items > offset + padding) + { offset = if visible_items > padding { visible_items - padding } else { 0 }; } - - }, + } KeyCode::Home => { i = 0; j = 0; offset = 0; - }, + } KeyCode::End => { i = artists.len() as i32 - 1; let artist = artists.get(i.max(0) as usize).unwrap(); @@ -230,8 +229,8 @@ impl<'a> AlbumTree<'a> { 0 } as i32; offset = length - 1 - height + padding; - }, - _ => {}, + } + _ => {} } offset = offset.min(length - height).max(0); @@ -241,5 +240,4 @@ impl<'a> AlbumTree<'a> { self.selected_artist.store(i as usize, Ordering::SeqCst); self.selected_album.store(j as usize, Ordering::SeqCst); } - } diff --git a/src/components/library/album_tree/widget.rs b/src/components/library/album_tree/widget.rs index 6d8da2d..cada4f8 100644 --- a/src/components/library/album_tree/widget.rs +++ b/src/components/library/album_tree/widget.rs @@ -1,23 +1,21 @@ use std::sync::atomic::Ordering; -use ratatui::{ - buffer::Buffer, - layout::Rect, - style::Style, - widgets::WidgetRef, - text::Line, -}; +use ratatui::{buffer::Buffer, layout::Rect, style::Style, text::Line, widgets::WidgetRef}; use crate::config::Theme; -use super::component::{AlbumTree}; +use super::component::AlbumTree; fn line_style(theme: &Theme, list_has_focus: bool, is_selected: bool, is_search_match: bool) -> Style { if is_selected { if list_has_focus { - Style::default().fg(theme.foreground_selected).bg(theme.background_selected) + Style::default() + .fg(theme.foreground_selected) + .bg(theme.background_selected) } else { - Style::default().fg(theme.foreground_selected).bg(theme.background_selected_blur) + Style::default() + .fg(theme.foreground_selected) + .bg(theme.background_selected_blur) } } else { let c = if is_search_match { @@ -68,11 +66,7 @@ impl<'a> WidgetRef for AlbumTree<'a> { let style = line_style(&self.theme, true, is_selected, is_match); let rect = Rect { - x: if is_album{ - area.x + 2 - } else { - area.x - }, + x: if is_album { area.x + 2 } else { area.x }, y: area.y + i as u16 - offset as u16, width: area.width, height: 1, diff --git a/src/components/library/keyboard_handler.rs b/src/components/library/keyboard_handler.rs index 8bcad87..65faa51 100644 --- a/src/components/library/keyboard_handler.rs +++ b/src/components/library/keyboard_handler.rs @@ -1,12 +1,9 @@ use crossterm::event::{KeyCode, KeyEvent}; -use crate::{ - ui::{KeyboardHandlerRef}, -}; -use super::library::{Library}; +use super::library::Library; +use crate::ui::KeyboardHandlerRef; impl<'a> KeyboardHandlerRef<'a> for Library<'a> { - fn on_key(&self, key: KeyEvent) { log::trace!(target: "::library.on_key", "{:?}", key); diff --git a/src/components/library/library.rs b/src/components/library/library.rs index df345dd..2550ed5 100644 --- a/src/components/library/library.rs +++ b/src/components/library/library.rs @@ -1,25 +1,17 @@ use std::{ cell::Cell, - rc::Rc, - sync::{ - Mutex, - MutexGuard, - }, path::PathBuf, + rc::Rc, + sync::{Mutex, MutexGuard}, }; use crossterm::event::KeyEvent; use crate::{ - structs::{Song}, - config::Theme, - cue::CueSheet, - components::List, - components::list::Direction, - ui::Component, + components::list::Direction, components::List, config::Theme, cue::CueSheet, structs::Song, ui::Component, }; -use super::{album_tree::{AlbumTree, AlbumTreeItem}}; +use super::album_tree::{AlbumTree, AlbumTreeItem}; pub struct Library<'a> { #[allow(dead_code)] @@ -38,7 +30,8 @@ pub struct Library<'a> { impl<'a> Library<'a> { pub fn new(theme: Theme) -> Self { - let on_select_fn: Rc>> = Rc::new(Mutex::new(Box::new(|_, _| {}) as _)); + let on_select_fn: Rc>> = + Rc::new(Mutex::new(Box::new(|_, _| {}) as _)); let on_select_songs_fn: Rc) + 'a>>> = Rc::new(Mutex::new(Box::new(|_| {}) as _)); let songs_by_artist = Rc::new(Mutex::new(crate::files::Library::from_file())); @@ -54,21 +47,21 @@ impl<'a> Library<'a> { let (artist, album) = match item { AlbumTreeItem::Artist(artist) => (artist, None), - AlbumTreeItem::Album(artist, album) => (artist, Some(album)) + AlbumTreeItem::Album(artist, album) => (artist, Some(album)), }; let artist_songs = { let songs = songs.lock().unwrap(); match songs.songs_by_artist.get(artist.as_str()) { - Some(artist_songs) => { - match album { - Some(album) => { - artist_songs.iter().filter(|s| s.album.as_ref().is_some_and(|a| *a == album)).cloned().collect() - } - None => artist_songs.clone(), - } - } + Some(artist_songs) => match album { + Some(album) => artist_songs + .iter() + .filter(|s| s.album.as_ref().is_some_and(|a| *a == album)) + .cloned() + .collect(), + None => artist_songs.clone(), + }, None => { log::error!(target: "::library.album_tree.on_select", "artist with no songs {artist}"); vec![] @@ -88,12 +81,8 @@ impl<'a> Library<'a> { log::trace!(target: "::library.album_tree.on_confirm", "artist confirmed {:?}", item); let (artist, album) = match item { - AlbumTreeItem::Artist(artist) => { - (artist, None) - } - AlbumTreeItem::Album(artist, album) => { - (artist, Some(album)) - } + AlbumTreeItem::Artist(artist) => (artist, None), + AlbumTreeItem::Album(artist, album) => (artist, Some(album)), }; let songs = { @@ -104,7 +93,11 @@ impl<'a> Library<'a> { }; if let Some(album) = album { - songs.iter().filter(|s| s.album.as_ref().is_some_and(|a| *a == album)).cloned().collect() + songs + .iter() + .filter(|s| s.album.as_ref().is_some_and(|a| *a == album)) + .cloned() + .collect() } else { songs.iter().cloned().collect() } @@ -167,9 +160,7 @@ impl<'a> Library<'a> { .and_then(|next_song_album| { songs .iter() - .position(|song| { - song.album.as_ref().is_some_and(|a| a.as_str() == next_song_album) - }) + .position(|song| song.album.as_ref().is_some_and(|a| a.as_str() == next_song_album)) }) } } @@ -252,7 +243,11 @@ impl<'a> Library<'a> { }; let songs = if let Some(selected_album) = selected_album { - songs.iter().filter(|s| s.album.as_ref().is_some_and(|sa| *sa == selected_album)).cloned().collect() + songs + .iter() + .filter(|s| s.album.as_ref().is_some_and(|sa| *sa == selected_album)) + .cloned() + .collect() } else { songs.clone() }; @@ -272,7 +267,6 @@ impl<'a> Library<'a> { let songs = Song::from_dir(path); self.add_songs(songs); } - } impl Drop for Library<'_> { diff --git a/src/components/library/widget.rs b/src/components/library/widget.rs index d3d15a6..68c841f 100644 --- a/src/components/library/widget.rs +++ b/src/components/library/widget.rs @@ -1,8 +1,8 @@ use ratatui::{ - prelude::Widget, buffer::Buffer, layout::{Constraint, Layout, Rect}, - widgets::{WidgetRef}, + prelude::Widget, + widgets::WidgetRef, }; use super::Library; @@ -20,8 +20,8 @@ impl<'a> WidgetRef for Library<'a> { Constraint::Length(5), Constraint::Percentage(50), ]) - .horizontal_margin(2) - .areas(area); + .horizontal_margin(2) + .areas(area); self.album_tree.render_ref(area_left, buf); self.song_list.render_ref(area_right, buf); diff --git a/src/components/list.rs b/src/components/list.rs index 8ed340c..296eb2b 100644 --- a/src/components/list.rs +++ b/src/components/list.rs @@ -2,4 +2,4 @@ mod component; mod keyboard_handler; mod widget; -pub use component::{List, Direction}; +pub use component::{Direction, List}; diff --git a/src/components/list/component.rs b/src/components/list/component.rs index 6c80ab7..c8a6d84 100644 --- a/src/components/list/component.rs +++ b/src/components/list/component.rs @@ -1,15 +1,11 @@ -use std::{ - sync::{ - atomic::{AtomicUsize, AtomicU8, Ordering}, - Mutex, - }, +use std::sync::{ + atomic::{AtomicU8, AtomicUsize, Ordering}, + Mutex, }; use crossterm::event::{KeyCode, KeyEvent}; -use crate::{ - config::Theme, -}; +use crate::config::Theme; #[derive(Eq, PartialEq)] pub enum Direction { @@ -35,7 +31,8 @@ pub struct ListItem { } pub struct List<'a, T: 'a> -where T: std::fmt::Display +where + T: std::fmt::Display, { pub(super) theme: Theme, @@ -63,13 +60,17 @@ where T: std::fmt::Display } impl<'a, T> List<'a, T> -where T: std::fmt::Display +where + T: std::fmt::Display, { pub fn new(theme: Theme, items: Vec) -> Self { - let items = items.into_iter().map(|item| ListItem { - inner: item, - is_match: false, - }).collect(); + let items = items + .into_iter() + .map(|item| ListItem { + inner: item, + is_match: false, + }) + .collect(); Self { theme, @@ -159,10 +160,13 @@ where T: std::fmt::Display pub fn set_items(&self, items: Vec) { self.selected_item_index.store(0, Ordering::SeqCst); self.offset.store(0, Ordering::SeqCst); - *self.items.lock().unwrap() = items.into_iter().map(|item| ListItem { - inner: item, - is_match: false, - }).collect(); + *self.items.lock().unwrap() = items + .into_iter() + .map(|item| ListItem { + inner: item, + is_match: false, + }) + .collect(); } pub fn push_item(&self, item: T) { @@ -188,7 +192,11 @@ where T: std::fmt::Display if filter.is_empty() { item.is_match = false; } else { - item.is_match = item.inner.to_string().to_lowercase().contains(filter.to_lowercase().as_str()); + item.is_match = item + .inner + .to_string() + .to_lowercase() + .contains(filter.to_lowercase().as_str()); } } @@ -202,7 +210,6 @@ where T: std::fmt::Display } } } - } impl Drop for List<'_, T> { diff --git a/src/components/list/keyboard_handler.rs b/src/components/list/keyboard_handler.rs index 9bb900a..6d038a8 100644 --- a/src/components/list/keyboard_handler.rs +++ b/src/components/list/keyboard_handler.rs @@ -1,18 +1,15 @@ -use std::sync::{ - atomic::Ordering, - MutexGuard, -}; +use std::sync::{atomic::Ordering, MutexGuard}; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; -use crate::{ui::KeyboardHandlerRef}; +use crate::ui::KeyboardHandlerRef; use super::component::{Direction, List}; impl<'a, T> KeyboardHandlerRef<'a> for List<'a, T> -where T: 'a + Clone + std::fmt::Display +where + T: 'a + Clone + std::fmt::Display, { - fn on_key(&self, key: KeyEvent) { let target = "::List.on_key"; log::trace!(target: target, "{:?}", key); @@ -25,9 +22,9 @@ where T: 'a + Clone + std::fmt::Display } match key.code { - KeyCode::Up | KeyCode::Down | KeyCode::Home | KeyCode::End | KeyCode::PageUp | KeyCode::PageDown => { + KeyCode::Up | KeyCode::Down | KeyCode::Home | KeyCode::End | KeyCode::PageUp | KeyCode::PageDown => { self.on_directional_key(key); - }, + } KeyCode::Enter => { self.filter_mut(|filter| { filter.clear(); @@ -52,8 +49,7 @@ where T: 'a + Clone + std::fmt::Display self.on_directional_key(KeyEvent::new(KeyCode::Down, KeyModifiers::NONE)); } } - - }, + } KeyCode::Insert => { let f = self.on_insert_fn.lock().unwrap(); let Some(f) = &*f else { @@ -82,7 +78,7 @@ where T: 'a + Clone + std::fmt::Display drop(items); on_delete(removed_item.inner, i); - }, + } KeyCode::Char('r') if key.modifiers == KeyModifiers::CONTROL => { if self.on_rename_fn.lock().unwrap().is_none() { return; @@ -90,18 +86,18 @@ where T: 'a + Clone + std::fmt::Display *rename = self.with_selected_item(|item| Some(item.to_string())); drop(rename); self.on_request_focus_trap_fn.lock().unwrap()(true); - }, + } KeyCode::Char(char) => { self.filter_mut(|filter| { filter.push(char); }); - }, + } KeyCode::Esc => { self.filter_mut(|filter| { filter.clear(); }); - }, - _ => {}, + } + _ => {} } } } @@ -115,7 +111,8 @@ fn is_key_dir_downwards(key_code: KeyCode) -> bool { } impl<'a, T> List<'a, T> -where T: std::fmt::Display + Clone +where + T: std::fmt::Display + Clone, { fn on_directional_key(&self, key: KeyEvent) { let is_filtering = !self.filter.lock().unwrap().is_empty(); @@ -185,11 +182,10 @@ where T: std::fmt::Display + Clone swapped = Some((i as usize, nexti as usize)); i = nexti; } - } else { return; } - }, + } KeyCode::PageUp if !is_filtering => { i -= page_size; } @@ -204,7 +200,7 @@ where T: std::fmt::Display + Clone } else { i = 0; } - }, + } KeyCode::End => { if is_filtering { if let Some(n) = items.iter().rposition(|item| item.is_match) { @@ -213,8 +209,8 @@ where T: std::fmt::Display + Clone } else { i = length - 1; } - }, - _ => {}, + } + _ => {} } if i == initial_i { @@ -225,7 +221,9 @@ where T: std::fmt::Display + Clone self.selected_item_index.store(i as usize, Ordering::SeqCst); let offset = self.offset.load(Ordering::Acquire) as i32; - if (is_key_dir_upwards(key.code) && i < offset + padding) || (is_key_dir_downwards(key.code) && i > offset + padding) { + if (is_key_dir_upwards(key.code) && i < offset + padding) + || (is_key_dir_downwards(key.code) && i > offset + padding) + { let offset = if i > padding { (i - padding).min(length - height).max(0) } else { @@ -256,18 +254,18 @@ where T: std::fmt::Display + Clone match key.code { KeyCode::Char(char) => { rename.push(char); - }, + } KeyCode::Backspace => { if key.modifiers == KeyModifiers::ALT { rename.clear(); } else if rename.len() > 0 { rename.remove(rename.len().saturating_sub(1)); } - }, + } KeyCode::Esc => { *rename_opt = None; self.on_request_focus_trap_fn.lock().unwrap()(false); - }, + } KeyCode::Enter => { if rename.is_empty() { return; @@ -287,5 +285,4 @@ where T: std::fmt::Display + Clone _ => {} } } - } diff --git a/src/components/list/widget.rs b/src/components/list/widget.rs index 7401d85..eb8d10c 100644 --- a/src/components/list/widget.rs +++ b/src/components/list/widget.rs @@ -8,7 +8,7 @@ use ratatui::{ buffer::Buffer, layout::Rect, style::Style, - widgets::{WidgetRef, Widget}, + widgets::{Widget, WidgetRef}, }; use super::component::List; @@ -28,9 +28,13 @@ impl<'a> Widget for ListLine<'a> { Style::default().fg(self.theme.background).bg(self.theme.search) } else if self.is_selected { if self.list_has_focus { - Style::default().fg(self.theme.foreground_selected).bg(self.theme.background_selected) + Style::default() + .fg(self.theme.foreground_selected) + .bg(self.theme.background_selected) } else { - Style::default().fg(self.theme.foreground_selected).bg(self.theme.background_selected_blur) + Style::default() + .fg(self.theme.foreground_selected) + .bg(self.theme.background_selected_blur) } } else { let fg = if self.is_match { @@ -43,11 +47,7 @@ impl<'a> Widget for ListLine<'a> { let line: Cow<'a, str> = if self.is_renaming { let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis(); - let caret = if now % 500 < 250 { - '⎸' - } else { - ' ' - }; + let caret = if now % 500 < 250 { '⎸' } else { ' ' }; format!("{}{}", self.text, caret).into() } else { self.text.into() @@ -59,7 +59,8 @@ impl<'a> Widget for ListLine<'a> { } impl<'a, T> WidgetRef for List<'a, T> -where T: std::fmt::Display, +where + T: std::fmt::Display, { fn render_ref(&self, area: Rect, buf: &mut Buffer) { self.height.store(area.height as usize, Ordering::Relaxed); @@ -108,7 +109,6 @@ where T: std::fmt::Display, }; line.render(area, buf); - } } } diff --git a/src/components/playlists.rs b/src/components/playlists.rs index 4cd4974..86c43dd 100644 --- a/src/components/playlists.rs +++ b/src/components/playlists.rs @@ -1,5 +1,5 @@ +mod keyboard_handler; mod playlists; mod widget; -mod keyboard_handler; pub use playlists::*; diff --git a/src/components/playlists/keyboard_handler.rs b/src/components/playlists/keyboard_handler.rs index 7d9d67e..11afe00 100644 --- a/src/components/playlists/keyboard_handler.rs +++ b/src/components/playlists/keyboard_handler.rs @@ -1,14 +1,10 @@ use crossterm::event::{KeyCode, KeyEvent}; -use crate::{ - components::playlists::playlists::PlaylistScreenElement, - ui::KeyboardHandlerRef, -}; +use crate::{components::playlists::playlists::PlaylistScreenElement, ui::KeyboardHandlerRef}; use super::Playlists; impl<'a> KeyboardHandlerRef<'a> for Playlists<'a> { - fn on_key(&self, key: KeyEvent) { let mut focused_element_guard = self.focused_element.lock().unwrap(); @@ -22,14 +18,13 @@ impl<'a> KeyboardHandlerRef<'a> for Playlists<'a> { PlaylistScreenElement::SongList => PlaylistScreenElement::PlaylistList, }; } - _ if *focused_element_guard == PlaylistScreenElement::PlaylistList => { + _ if *focused_element_guard == PlaylistScreenElement::PlaylistList => { self.playlist_list.on_key(key); - }, - _ if *focused_element_guard == PlaylistScreenElement::SongList => { + } + _ if *focused_element_guard == PlaylistScreenElement::SongList => { self.song_list.on_key(key); - }, - _ => {}, + } + _ => {} } } - } diff --git a/src/components/playlists/playlists.rs b/src/components/playlists/playlists.rs index 8ac5acb..c79ecd4 100644 --- a/src/components/playlists/playlists.rs +++ b/src/components/playlists/playlists.rs @@ -1,17 +1,13 @@ -use std::{ - sync::Mutex, - rc::Rc, - cell::Cell, -}; +use std::{cell::Cell, rc::Rc, sync::Mutex}; use chrono::Local; -use crossterm::event::{KeyEvent}; +use crossterm::event::KeyEvent; use crate::{ - structs::{Song, Playlist}, + components::List, config::Theme, cue::CueSheet, - components::List, + structs::{Playlist, Song}, }; #[derive(Eq, PartialEq)] @@ -33,7 +29,14 @@ impl<'a> Playlists<'a> { pub fn new(theme: Theme) -> Self { let playlists_file = crate::files::Playlists::from_file(); - let song_list = Rc::new(List::new(theme, playlists_file.playlists.get(0).map(|pl| pl.songs.clone()).unwrap_or(vec![]))); + let song_list = Rc::new(List::new( + theme, + playlists_file + .playlists + .get(0) + .map(|pl| pl.songs.clone()) + .unwrap_or(vec![]), + )); let playlist_list = Rc::new(List::new(theme, playlists_file.playlists)); let deleted_playlist_list = Rc::new(List::new(theme, playlists_file.deleted)); @@ -60,9 +63,10 @@ impl<'a> Playlists<'a> { let playlist_list = playlist_list.clone(); let deleted_playlist_list = deleted_playlist_list.clone(); move || { - let playlist = Playlist::new( - format!("New playlist created at {}", Local::now().format("%A %-l:%M:%S%P")), - ); + let playlist = Playlist::new(format!( + "New playlist created at {}", + Local::now().format("%A %-l:%M:%S%P") + )); playlist_list.push_item(playlist); save(&playlist_list, &deleted_playlist_list); } diff --git a/src/components/playlists/widget.rs b/src/components/playlists/widget.rs index c6ff90c..4f2c3f1 100644 --- a/src/components/playlists/widget.rs +++ b/src/components/playlists/widget.rs @@ -1,24 +1,27 @@ -use std::{ - fmt::{Display, Formatter}, -}; +use std::fmt::{Display, Formatter}; use ratatui::{ - prelude::Alignment, buffer::Buffer, layout::{Constraint, Layout, Rect}, - widgets::WidgetRef, + prelude::Alignment, style::Style, + widgets::WidgetRef, }; use super::Playlists; impl Display for crate::structs::Song { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{} - {} - {} - {}", - self.year.as_ref().map(|y| y.to_string()).unwrap_or("(no year)".to_string()), - self.album.clone().unwrap_or("(no album)".to_string()), - self.track.unwrap_or(0), - self.title.clone() + write!( + f, + "{} - {} - {} - {}", + self.year + .as_ref() + .map(|y| y.to_string()) + .unwrap_or("(no year)".to_string()), + self.album.clone().unwrap_or("(no album)".to_string()), + self.track.unwrap_or(0), + self.title.clone() ) } } @@ -36,8 +39,8 @@ impl<'a> WidgetRef for Playlists<'a> { Constraint::Length(5), Constraint::Percentage(50), ]) - .horizontal_margin(2) - .areas(area); + .horizontal_margin(2) + .areas(area); let show_deleted_playlists = self.show_deleted_playlists.get(); @@ -49,7 +52,7 @@ impl<'a> WidgetRef for Playlists<'a> { Constraint::Length(1), Constraint::Percentage(50), ]) - .areas(area_left); + .areas(area_left); self.playlist_list.render_ref(left_top, buf); diff --git a/src/components/queue.rs b/src/components/queue.rs index 0942708..2e01ce6 100644 --- a/src/components/queue.rs +++ b/src/components/queue.rs @@ -1,5 +1,5 @@ -pub mod widget; pub mod keyboard_handler; pub mod queue; +pub mod widget; pub use queue::Queue; diff --git a/src/components/queue/queue.rs b/src/components/queue/queue.rs index cf902d9..08a0f51 100644 --- a/src/components/queue/queue.rs +++ b/src/components/queue/queue.rs @@ -1,14 +1,13 @@ -use std::sync::{ - Arc, Condvar, Mutex, MutexGuard, - atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering}, +use std::{ + collections::VecDeque, + sync::{ + atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering}, + Arc, Condvar, Mutex, MutexGuard, + }, + time::Duration, }; -use std::time::Duration; -use std::collections::VecDeque; -use crate::{ - config::Theme, - structs::Song, -}; +use crate::{config::Theme, structs::Song}; pub struct Queue { pub(super) theme: Theme, @@ -123,7 +122,8 @@ impl Queue { }; self.selected_item_index.fetch_add(1, Ordering::SeqCst); - self.selected_item_index.fetch_min(length.saturating_sub(1), Ordering::SeqCst); + self.selected_item_index + .fetch_min(length.saturating_sub(1), Ordering::SeqCst); } pub fn select_previous(&self) { @@ -134,7 +134,8 @@ impl Queue { }; self.selected_item_index.fetch_sub(1, Ordering::SeqCst); - self.selected_item_index.fetch_min(length.saturating_sub(1), Ordering::SeqCst); + self.selected_item_index + .fetch_min(length.saturating_sub(1), Ordering::SeqCst); } pub fn add_front(&self, song: Song) { diff --git a/src/components/queue/widget.rs b/src/components/queue/widget.rs index ad68238..0a6c217 100644 --- a/src/components/queue/widget.rs +++ b/src/components/queue/widget.rs @@ -9,9 +9,7 @@ use super::queue::Queue; impl WidgetRef for Queue { fn render_ref(&self, area: Rect, buf: &mut Buffer) { - let [area] = Layout::horizontal([ - Constraint::Percentage(100), - ]) + let [area] = Layout::horizontal([Constraint::Percentage(100)]) .horizontal_margin(2) .areas(area); @@ -31,7 +29,7 @@ impl WidgetRef for Queue { queue_list, area, buf, - &mut ListState::default().with_selected(Some(self.selected_song_index())) + &mut ListState::default().with_selected(Some(self.selected_song_index())), ); } } diff --git a/src/components/rendering_error.rs b/src/components/rendering_error.rs index 432434f..68b1f75 100644 --- a/src/components/rendering_error.rs +++ b/src/components/rendering_error.rs @@ -23,8 +23,11 @@ impl WidgetRef for RenderingError { Constraint::Length(1), Constraint::Percentage(50), ]) - .areas(area); + .areas(area); - ratatui::text::Line::from("RENDERING ERROR").style(Style::new().fg(Color::Rgb(255, 255, 255))).centered().render_ref(area_center, buf); + ratatui::text::Line::from("RENDERING ERROR") + .style(Style::new().fg(Color::Rgb(255, 255, 255))) + .centered() + .render_ref(area_center, buf); } } diff --git a/src/cue/cue_sheet.rs b/src/cue/cue_sheet.rs index 6628c04..37ddeff 100644 --- a/src/cue/cue_sheet.rs +++ b/src/cue/cue_sheet.rs @@ -66,9 +66,9 @@ impl Track { while let Some(t) = track_properties.pop() { match t { - CueSheetItem::Title(s) => { track.title = s } - CueSheetItem::Performer(s) => { track.performer = Some(s) } - CueSheetItem::Index(s) => { track.start_time = s } + CueSheetItem::Title(s) => track.title = s, + CueSheetItem::Performer(s) => track.performer = Some(s), + CueSheetItem::Index(s) => track.start_time = s, _ => {} } } @@ -126,9 +126,9 @@ impl CueSheet { while let Some(e) = top_cue_items.pop() { match e { - CueSheetItem::Comment(s) => { sheet.comments.push(s) } - CueSheetItem::Title(s) => { sheet.title = Some(s) } - CueSheetItem::Performer(s) => { sheet.performer = Some(s) } + CueSheetItem::Comment(s) => sheet.comments.push(s), + CueSheetItem::Title(s) => sheet.title = Some(s), + CueSheetItem::Performer(s) => sheet.performer = Some(s), CueSheetItem::File(s, c) => { sheet.file = Some(CueFile::new(s, c)); } diff --git a/src/cue/cue_sheet_item.rs b/src/cue/cue_sheet_item.rs index b1c3cdc..8b29c74 100644 --- a/src/cue/cue_sheet_item.rs +++ b/src/cue/cue_sheet_item.rs @@ -51,9 +51,9 @@ mod tests { use std::collections::VecDeque; use std::path::Path; + use super::*; use crate::cue::cue_line::CueLine; use crate::cue::cue_line_node::CueLineNode; - use super::*; #[test] fn cue_sheet_item_from_cue_line_node() { diff --git a/src/files/library.rs b/src/files/library.rs index 366a496..62d7e2f 100644 --- a/src/files/library.rs +++ b/src/files/library.rs @@ -64,5 +64,4 @@ impl Library { artist_songs.retain(|s| s.album.as_ref().is_some_and(|a| *a != album)); self.save(); } - } diff --git a/src/main.rs b/src/main.rs index 72f7685..0487f42 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,21 +1,21 @@ mod app; +mod auto_update; +mod bye; +mod components; mod config; mod constants; mod cue; mod extensions; +mod files; mod mpris; mod player; +mod source; +mod spawn_terminal; mod state; mod structs; mod term; mod toml; mod ui; -mod source; -mod components; -mod bye; -mod files; -mod auto_update; -mod spawn_terminal; use std::error::Error; use std::io::stdout; @@ -24,7 +24,7 @@ use std::thread; use async_std::task; use colored::{Color, Colorize}; -use flexi_logger::{DeferredNow, FileSpec, Logger, WriteMode, style}; +use flexi_logger::{style, DeferredNow, FileSpec, Logger, WriteMode}; use futures::{ future::FutureExt, // for `.fuse()` pin_mut, @@ -32,13 +32,7 @@ use futures::{ }; use log::{debug, error, info, Record}; -use crate::{ - app::App, - mpris::create_mpris_player, - term::reset_terminal, - bye::bye, - auto_update::auto_update, -}; +use crate::{app::App, auto_update::auto_update, bye::bye, mpris::create_mpris_player, term::reset_terminal}; pub enum Command { PlayPause, diff --git a/src/mpris.rs b/src/mpris.rs index 017013a..1c799fe 100644 --- a/src/mpris.rs +++ b/src/mpris.rs @@ -3,7 +3,9 @@ use std::sync::mpsc::Sender; use crate::Command; -pub async fn create_mpris_player(player_command_sender: Sender) -> Result> { +pub async fn create_mpris_player( + player_command_sender: Sender, +) -> Result> { let player = mpris_server::Player::builder("com.taro-codes.jolteon") .can_play(true) .can_pause(true) @@ -30,7 +32,7 @@ pub async fn create_mpris_player(player_command_sender: Sender) -> Resu }); player.connect_quit(|_player| { - log::trace!("mpris quit"); + log::trace!("mpris quit"); }); player.connect_stop({ diff --git a/src/player.rs b/src/player.rs index feb7b16..65cd790 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,9 +1,8 @@ use std::{ sync::{ - Arc, - Mutex, atomic::{AtomicBool, AtomicU64, Ordering}, mpsc::{channel, Receiver, RecvTimeoutError, Sender}, + Arc, Mutex, }, thread, thread::JoinHandle, @@ -11,20 +10,15 @@ use std::{ }; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; -use ratatui::{ - buffer::Buffer, - layout::Rect, - prelude::Widget, - widgets::WidgetRef, -}; +use ratatui::{buffer::Buffer, layout::Rect, prelude::Widget, widgets::WidgetRef}; use rodio::OutputStreamHandle; use crate::{ - structs::{Action, OnAction, PlayerAction, Song}, - source::{Source, Controls}, - ui::{KeyboardHandlerRef, CurrentlyPlaying}, - config::Theme, components::Queue, + config::Theme, + source::{Controls, Source}, + structs::{Action, OnAction, PlayerAction, Song}, + ui::{CurrentlyPlaying, KeyboardHandlerRef}, }; pub struct Player { @@ -132,180 +126,181 @@ impl Player { }; }; - let thread = thread::Builder::new().name("player".to_string()).spawn(move || { - while let Ok(song) = queue_items.pop() { - let path = song.path.clone(); - let start_time = song.start_time.clone(); - let length = song.length.clone(); - - is_stopped.store(false, Ordering::SeqCst); - - set_currently_playing(Some(song)); - - let periodic_access = { - let is_stopped = is_stopped.clone(); - let must_stop = must_stop.clone(); - let volume = volume.clone(); - let pause = pause.clone(); - let must_seek = must_seek.clone(); - - move |controls: &mut Controls| { - if must_stop.swap(false, Ordering::SeqCst) { - controls.stop(); - controls.skip(); - is_stopped.store(true, Ordering::SeqCst); - log::debug!("periodic access stop"); - return; - } + let thread = thread::Builder::new() + .name("player".to_string()) + .spawn(move || { + while let Ok(song) = queue_items.pop() { + let path = song.path.clone(); + let start_time = song.start_time.clone(); + let length = song.length.clone(); + + is_stopped.store(false, Ordering::SeqCst); + + set_currently_playing(Some(song)); + + let periodic_access = { + let is_stopped = is_stopped.clone(); + let must_stop = must_stop.clone(); + let volume = volume.clone(); + let pause = pause.clone(); + let must_seek = must_seek.clone(); + + move |controls: &mut Controls| { + if must_stop.swap(false, Ordering::SeqCst) { + controls.stop(); + controls.skip(); + is_stopped.store(true, Ordering::SeqCst); + log::debug!("periodic access stop"); + return; + } - controls.set_volume(*volume.lock().unwrap()); - controls.set_paused(pause.load(Ordering::SeqCst)); + controls.set_volume(*volume.lock().unwrap()); + controls.set_paused(pause.load(Ordering::SeqCst)); - if let Some(seek) = must_seek.lock().unwrap().take() { - if let Err(err) = controls.seek(seek) { - log::error!("periodic_access.try_seek() error. {:?}", err) + if let Some(seek) = must_seek.lock().unwrap().take() { + if let Err(err) = controls.seek(seek) { + log::error!("periodic_access.try_seek() error. {:?}", err) + } } } - } - }; - - let wait_until_song_ends = || { - let target = "::wait_until_song_ends"; - log::debug!(target: target, "start"); - must_stop.store(true, Ordering::SeqCst); - - if let Err(err) = song_ended_rx.recv() { - log::error!("ender_recv.recv {:?}", err); - return; - } - - log::debug!(target: target, "ender signal received"); + }; - while song_ended_rx.try_recv().is_ok() {} + let wait_until_song_ends = || { + let target = "::wait_until_song_ends"; + log::debug!(target: target, "start"); + must_stop.store(true, Ordering::SeqCst); - must_stop.store(false, Ordering::SeqCst); - must_seek.lock().unwrap().take(); + if let Err(err) = song_ended_rx.recv() { + log::error!("ender_recv.recv {:?}", err); + return; + } - set_currently_playing(None); + log::debug!(target: target, "ender signal received"); - log::debug!(target: target, "done"); - }; + while song_ended_rx.try_recv().is_ok() {} + must_stop.store(false, Ordering::SeqCst); + must_seek.lock().unwrap().take(); - let mut source = Source::from_file(path, periodic_access, position.clone(), { - let song_ended_tx = song_ended_tx.clone(); - move || { - log::trace!("source.on_playback_ended"); - let _ = song_ended_tx.send(()); - } - }); - - if start_time > Duration::ZERO { - log::debug!("start_time > Duration::ZERO, {:?}", start_time); - if let Err(err) = source.seek(start_time) { - log::error!("start_time > 0 try_seek() error. {:?}", err) - } - } + set_currently_playing(None); - *position.lock().unwrap() = start_time; + log::debug!(target: target, "done"); + }; - log::debug!("output_stream.play_raw()"); - if let Err(err) = output_stream.play_raw(source) { // Does `mixer.add(source)`. Mixer is tied to the CPAL thread, which starts consuming the source automatically. - log::error!("os.play_raw error! {:?}", err); - continue; - } + let mut source = Source::from_file(path, periodic_access, position.clone(), { + let song_ended_tx = song_ended_tx.clone(); + move || { + log::trace!("source.on_playback_ended"); + let _ = song_ended_tx.send(()); + } + }); - // Start looping until the current song ends OR something wakes us up. - // When woken up, we check whether we need to immediately exit. - // If we don't, we recalculate the remaining time until the song ends, - // and then go back to bed. - loop { - let sleepy_time = if pause.load(Ordering::SeqCst) { - Duration::MAX - } else { - let abs_pos = position.lock().unwrap().saturating_sub(start_time); - if abs_pos >= length { - log::debug!("inner loop: pos >= length, {:?} > {:?}", abs_pos, length); - break; + if start_time > Duration::ZERO { + log::debug!("start_time > Duration::ZERO, {:?}", start_time); + if let Err(err) = source.seek(start_time) { + log::error!("start_time > 0 try_seek() error. {:?}", err) } - length - abs_pos - }; + } - // log::debug!("inner loop: sleepy_time! {:?}", sleepy_time); + *position.lock().unwrap() = start_time; - match command_receiver.recv_timeout(sleepy_time) { - Ok(command) => { - log::debug!("Player.Command({:?})", command); - match command { - Command::Quit => { - log::trace!("Player: quitting main loop"); - return; - } - Command::Play => { - pause.store(false, Ordering::SeqCst); - } - Command::Pause => { - pause.store(true, Ordering::SeqCst); - } - Command::Stop => { - break; - } - Command::Seek(seek) => { - // NOTE: "intense" seek causes `ALSA lib pcm.c:8740:(snd_pcm_recover) underrun occurred`. - // See https://github.com/RustAudio/cpal/pull/909 + log::debug!("output_stream.play_raw()"); + if let Err(err) = output_stream.play_raw(source) { + // play_raw does `mixer.add(source)`. Mixer is tied to the CPAL thread, which starts consuming the source automatically. + log::error!("os.play_raw error! {:?}", err); + continue; + } - if seek == 0 { - log::error!("Command::Seek(0)"); - continue; + // Start looping until the current song ends OR something wakes us up. + // When woken up, we check whether we need to immediately exit. + // If we don't, we recalculate the remaining time until the song ends, + // and then go back to bed. + loop { + let sleepy_time = if pause.load(Ordering::SeqCst) { + Duration::MAX + } else { + let abs_pos = position.lock().unwrap().saturating_sub(start_time); + if abs_pos >= length { + log::debug!("inner loop: pos >= length, {:?} > {:?}", abs_pos, length); + break; + } + length - abs_pos + }; + + // log::debug!("inner loop: sleepy_time! {:?}", sleepy_time); + + match command_receiver.recv_timeout(sleepy_time) { + Ok(command) => { + log::debug!("Player.Command({:?})", command); + match command { + Command::Quit => { + log::trace!("Player: quitting main loop"); + return; } - - if is_stopped.load(Ordering::SeqCst) || must_stop.load(Ordering::SeqCst) { - continue; + Command::Play => { + pause.store(false, Ordering::SeqCst); } - - let seek_abs = Duration::from_secs(seek.abs() as u64); - let mut pos = position.lock().unwrap(); - - let target = if seek > 0 { - pos.saturating_add(seek_abs) - } else { - pos.saturating_sub(seek_abs).max(start_time) - }; - - // If we'd seek past song end, skip seeking and just move to next song instead. - if target > length + start_time { - log::debug!("Seeking past end"); + Command::Pause => { + pause.store(true, Ordering::SeqCst); + } + Command::Stop => { break; } - - log::debug!("Seek({:?})", target); - *must_seek.lock().unwrap() = Some(target); - *pos = target; // optimistic update, otherwise sleepy_time will be off - + Command::Seek(seek) => { + // NOTE: "intense" seek causes `ALSA lib pcm.c:8740:(snd_pcm_recover) underrun occurred`. + // See https://github.com/RustAudio/cpal/pull/909 + + if seek == 0 { + log::error!("Command::Seek(0)"); + continue; + } + + if is_stopped.load(Ordering::SeqCst) || must_stop.load(Ordering::SeqCst) { + continue; + } + + let seek_abs = Duration::from_secs(seek.abs() as u64); + let mut pos = position.lock().unwrap(); + + let target = if seek > 0 { + pos.saturating_add(seek_abs) + } else { + pos.saturating_sub(seek_abs).max(start_time) + }; + + // If we'd seek past song end, skip seeking and just move to next song instead. + if target > length + start_time { + log::debug!("Seeking past end"); + break; + } + + log::debug!("Seek({:?})", target); + *must_seek.lock().unwrap() = Some(target); + *pos = target; // optimistic update, otherwise sleepy_time will be off + } } } - } - Err(RecvTimeoutError::Timeout) => { - // Playing song reached its end. We want to move on to the next song. - log::trace!("Player Command Timeout"); - break; - } - Err(RecvTimeoutError::Disconnected) => { - // Most of the time, not a real error. This can happen because the command_sender was dropped, - // which happens when the player itself was dropped, so we just want to exit. - log::warn!("RecvTimeoutError::Disconnected"); - return; + Err(RecvTimeoutError::Timeout) => { + // Playing song reached its end. We want to move on to the next song. + log::trace!("Player Command Timeout"); + break; + } + Err(RecvTimeoutError::Disconnected) => { + // Most of the time, not a real error. This can happen because the command_sender was dropped, + // which happens when the player itself was dropped, so we just want to exit. + log::warn!("RecvTimeoutError::Disconnected"); + return; + } } } - } - while command_receiver.try_recv().is_ok() {} // "drain" the command queue - dropping everything that might have accumulated. + while command_receiver.try_recv().is_ok() {} // "drain" the command queue - dropping everything that might have accumulated. - wait_until_song_ends(); - - } - log::trace!("Player loop exit"); - }).unwrap(); + wait_until_song_ends(); + } + log::trace!("Player loop exit"); + }) + .unwrap(); *self.main_thread.lock().unwrap() = Some(thread); } @@ -324,7 +319,8 @@ impl Player { self.send_command(Command::Play); } else { self.send_command(Command::Pause); - self.paused_animation_start_frame.store(self.frame.load(Ordering::Relaxed), Ordering::Relaxed); + self.paused_animation_start_frame + .store(self.frame.load(Ordering::Relaxed), Ordering::Relaxed); } } @@ -403,7 +399,8 @@ impl WidgetRef for Player { self.queue_items.total_time(), self.queue_items.length(), is_paused, - ).render(area, buf); + ) + .render(area, buf); } } @@ -416,7 +413,7 @@ impl KeyboardHandlerRef<'_> for Player { KeyCode::Char('+') => self.change_volume(0.05), KeyCode::Char(' ') if key.modifiers == KeyModifiers::CONTROL => self.toggle(), KeyCode::Char('g') if key.modifiers == KeyModifiers::CONTROL => self.stop(), - _ => { }, + _ => {} }; } } @@ -424,17 +421,27 @@ impl KeyboardHandlerRef<'_> for Player { impl OnAction for Player { fn on_action(&self, action: Action) { match action { - Action::PlayerAction(action) => { - match action { - PlayerAction::PlayPause => {self.toggle();}, - PlayerAction::Stop => {self.stop();}, - PlayerAction::VolumeUp => {self.change_volume(0.05);}, - PlayerAction::VolumeDown => {self.change_volume(-0.05);}, - PlayerAction::SeekForwards => {self.seek_forward();}, - PlayerAction::SeekBackwards => {self.seek_backward();}, + Action::PlayerAction(action) => match action { + PlayerAction::PlayPause => { + self.toggle(); } - } - _ => {}, + PlayerAction::Stop => { + self.stop(); + } + PlayerAction::VolumeUp => { + self.change_volume(0.05); + } + PlayerAction::VolumeDown => { + self.change_volume(-0.05); + } + PlayerAction::SeekForwards => { + self.seek_forward(); + } + PlayerAction::SeekBackwards => { + self.seek_backward(); + } + }, + _ => {} } } } diff --git a/src/source.rs b/src/source.rs index ae29bd6..28ae897 100644 --- a/src/source.rs +++ b/src/source.rs @@ -5,9 +5,10 @@ use std::sync::{Arc, Mutex}; use std::time::Duration; use rodio::{ - Decoder, - Source as RodioSource, - source::{Amplify, Pausable, PeriodicAccess, SamplesConverter, Skippable, Speed, Stoppable, TrackPosition, SeekError}, + source::{ + Amplify, Pausable, PeriodicAccess, SamplesConverter, SeekError, Skippable, Speed, Stoppable, TrackPosition, + }, + Decoder, Source as RodioSource, }; type FullRodioSource = Stoppable>>>>>>>; @@ -19,7 +20,6 @@ pub struct Controls<'a> { } impl Controls<'_> { - #[inline] pub fn stop(&mut self) { self.src.stop(); @@ -73,11 +73,13 @@ impl Source<()> { mut periodic_access: impl FnMut(&mut Controls) + Send, shared_pos: Arc>, on_playback_end: impl FnOnce() + Send + 'static, - ) -> Source> - { + ) -> Source> { let periodic_access_inner = { Box::new(move |src: &mut FullRodioSource| { - let mut controls = Controls { src, shared_pos: &shared_pos }; + let mut controls = Controls { + src, + shared_pos: &shared_pos, + }; controls.refresh_pos(); periodic_access(&mut controls); }) @@ -106,7 +108,6 @@ impl Source where F: FnMut(&mut FullRodioSource) + Send, { - #[inline] pub fn _inner_mut(&mut self) -> &mut PeriodicRodioSource { &mut self.input diff --git a/src/spawn_terminal.rs b/src/spawn_terminal.rs index b52109d..ad3579c 100644 --- a/src/spawn_terminal.rs +++ b/src/spawn_terminal.rs @@ -1,8 +1,4 @@ -use std::{ - path::PathBuf, - thread, - io::BufRead, -}; +use std::{io::BufRead, path::PathBuf, thread}; #[allow(dead_code)] fn spawn_terminal(cwd: PathBuf) { diff --git a/src/structs.rs b/src/structs.rs index 327274e..e061992 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -1,9 +1,9 @@ -mod song; -mod playlist; -mod jolt; mod action; +mod jolt; +mod playlist; +mod song; -pub use song::Song; -pub use playlist::Playlist; -pub use jolt::Jolt; pub use action::*; +pub use jolt::Jolt; +pub use playlist::Playlist; +pub use song::Song; diff --git a/src/structs/action.rs b/src/structs/action.rs index 32e2b93..128eb21 100644 --- a/src/structs/action.rs +++ b/src/structs/action.rs @@ -1,21 +1,13 @@ -use std::{ - collections::HashMap, - hash::Hash, - fs::read_to_string, - sync::LazyLock, -}; - -use crossterm::{ - event::{KeyCode, KeyEvent, KeyModifiers} -}; +use std::{collections::HashMap, fs::read_to_string, hash::Hash, sync::LazyLock}; + +use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; use serde::{Deserialize, Serialize}; -use crate::toml::{TomlFileError, get_config_file_path}; +use crate::toml::{get_config_file_path, TomlFileError}; static DEFAULT_ACTIONS_STR: &str = include_str!("../../assets/actions.kv"); -static DEFAULT_ACTIONS: LazyLock> = LazyLock::new(|| { - Actions::from_str(DEFAULT_ACTIONS_STR).actions -}); +static DEFAULT_ACTIONS: LazyLock> = + LazyLock::new(|| Actions::from_str(DEFAULT_ACTIONS_STR).actions); #[derive(Eq, PartialEq, Copy, Clone, Debug, Serialize, Deserialize, Hash)] pub struct Shortcut { @@ -31,7 +23,10 @@ impl Shortcut { impl From for Shortcut { fn from(key: KeyEvent) -> Self { - Self { code: key.code, modifiers: key.modifiers } + Self { + code: key.code, + modifiers: key.modifiers, + } } } @@ -200,15 +195,11 @@ impl Actions { log::trace!("actions '{:#?}'", actions); - Self { - actions, - } + Self { actions } } #[allow(dead_code)] - pub fn to_file(&self) { - - } + pub fn to_file(&self) {} pub fn from_file() -> Result { let path = get_config_file_path("shortcuts")?; @@ -227,7 +218,6 @@ pub trait OnAction { fn on_action(&self, action: Action); } - pub trait OnActionMut { fn on_action(&mut self, action: Action); } diff --git a/src/structs/jolt.rs b/src/structs/jolt.rs index 88a2bc9..0094134 100644 --- a/src/structs/jolt.rs +++ b/src/structs/jolt.rs @@ -1,7 +1,4 @@ -use std::{ - fs::read_to_string, - path::PathBuf, -}; +use std::{fs::read_to_string, path::PathBuf}; use serde::{Deserialize, Serialize}; @@ -19,10 +16,7 @@ impl Jolt { let jolt = read_to_string(path.as_path())?; let jolt: Jolt = toml::from_str(&jolt)?; - Ok(Jolt { - path, - ..jolt - }) + Ok(Jolt { path, ..jolt }) } } diff --git a/src/structs/song.rs b/src/structs/song.rs index 77ebd5e..dcc6ec6 100644 --- a/src/structs/song.rs +++ b/src/structs/song.rs @@ -1,21 +1,17 @@ -use std::{ - path::PathBuf, - time::Duration, - cmp::Ordering, -}; +use std::{cmp::Ordering, path::PathBuf, time::Duration}; use lofty::{ + error::LoftyError, file::{AudioFile, TaggedFileExt}, probe::Probe, tag::Accessor, - error::LoftyError, }; use serde::{Deserialize, Serialize}; use crate::{ - structs::Jolt, + components::{directory_to_songs_and_folders, FileBrowserSelection}, cue::CueSheet, - components::{FileBrowserSelection, directory_to_songs_and_folders}, + structs::Jolt, }; #[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)] @@ -120,7 +116,11 @@ impl Song { .map(|t| Song { path: song_path.clone(), length: Duration::ZERO, - artist: jolt.as_ref().and_then(|j| j.artist.clone()).or(performer.clone()).or(t.performer()), + artist: jolt + .as_ref() + .and_then(|j| j.artist.clone()) + .or(performer.clone()) + .or(t.performer()), title: t.title(), start_time: t.start_time(), album: jolt.as_ref().and_then(|j| j.album.clone()).or(cue_sheet.title()), @@ -164,36 +164,30 @@ impl Song { impl Ord for Song { fn cmp(&self, other: &Self) -> Ordering { match (&self.album, &other.album) { - (Some(album_a), Some(album_b)) if album_a == album_b => { - match self.disc_number.cmp(&other.disc_number) { - Ordering::Equal => { - match (&self.track, &other.track) { - (Some(a), Some(b)) => a.cmp(b), - (Some(_), None) => Ordering::Greater, - (None, Some(_)) => Ordering::Less, - _ => self.title.cmp(&other.title), - } - } - o => o - } - }, - (Some(album_a), Some(album_b)) if album_a != album_b => { - match (self.year, other.year) { - (Some(ref year_a), Some(ref year_b)) => { - if year_a != year_b { - year_a.cmp(year_b) - } else { - album_a.cmp(album_b) - } - }, + (Some(album_a), Some(album_b)) if album_a == album_b => match self.disc_number.cmp(&other.disc_number) { + Ordering::Equal => match (&self.track, &other.track) { + (Some(a), Some(b)) => a.cmp(b), (Some(_), None) => Ordering::Greater, (None, Some(_)) => Ordering::Less, - _ => album_a.cmp(album_b) + _ => self.title.cmp(&other.title), + }, + o => o, + }, + (Some(album_a), Some(album_b)) if album_a != album_b => match (self.year, other.year) { + (Some(ref year_a), Some(ref year_b)) => { + if year_a != year_b { + year_a.cmp(year_b) + } else { + album_a.cmp(album_b) + } } + (Some(_), None) => Ordering::Greater, + (None, Some(_)) => Ordering::Less, + _ => album_a.cmp(album_b), }, (Some(_), None) => Ordering::Greater, (None, Some(_)) => Ordering::Less, - _ => self.title.cmp(&other.title) + _ => self.title.cmp(&other.title), } } } diff --git a/src/ui/currently_playing.rs b/src/ui/currently_playing.rs index f1fb611..236c0cf 100644 --- a/src/ui/currently_playing.rs +++ b/src/ui/currently_playing.rs @@ -4,9 +4,9 @@ use log::error; use ratatui::{ layout::{Constraint, Layout}, prelude::*, - style::{Style}, - widgets::{Block, Borders, Gauge}, + style::Style, text::Line, + widgets::{Block, Borders, Gauge}, }; use crate::{ @@ -139,7 +139,8 @@ impl Widget for CurrentlyPlaying { playing_gauge.render(area_bottom, buf); } - let [_, area_bottom_right] = Layout::horizontal([Constraint::Fill(1), Constraint::Length(6)]).areas(area_bottom); + let [_, area_bottom_right] = + Layout::horizontal([Constraint::Fill(1), Constraint::Length(6)]).areas(area_bottom); if self.is_paused { Line::from("PAUSED") diff --git a/src/ui/top_bar.rs b/src/ui/top_bar.rs index dbaeadf..ab6d414 100644 --- a/src/ui/top_bar.rs +++ b/src/ui/top_bar.rs @@ -2,7 +2,7 @@ use chrono::prelude::*; use ratatui::{ prelude::*, style::{Modifier, Style}, - text::{Span, Line}, + text::{Line, Span}, widgets::{Block, Tabs}, }; @@ -43,7 +43,8 @@ impl<'a> TopBar<'a> { impl<'a> Widget for TopBar<'a> { fn render(self, area: Rect, buf: &mut Buffer) { - let tab_titles: Vec = self.tab_titles + let tab_titles: Vec = self + .tab_titles .iter() .map(|t| { Line::from(Span::styled(