diff --git a/Cargo.toml b/Cargo.toml index afa11fb..65cd545 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,3 +60,17 @@ indicatif = { version = "0.18.0", optional = true } [target.'cfg(target_os = "linux")'.dependencies] libc = "0.2.167" +[lints.clippy] +all = { level = "warn", priority = -1 } +pedantic = { level = "warn", priority = -1 } +nursery = { level = "warn", priority = -1 } + +unwrap_in_result = "warn" +missing_docs_in_private_items = "warn" + +missing_errors_doc = "allow" +missing_panics_doc = "allow" +must_use_candidate = "allow" +cast_precision_loss = "allow" +cast_sign_loss = "allow" +cast_possible_truncation = "allow" \ No newline at end of file diff --git a/src/audio.rs b/src/audio.rs index 3818419..7d6808f 100644 --- a/src/audio.rs +++ b/src/audio.rs @@ -25,7 +25,7 @@ pub fn silent_get_output_stream() -> eyre::Result eyre::Result Result { + pub fn bookmark(&mut self, track: &tracks::Info) -> Result { let entry = track.to_entry(); let idx = self.entries.iter().position(|x| **x == entry); diff --git a/src/download.rs b/src/download.rs index 83fda8f..cbb1e0c 100644 --- a/src/download.rs +++ b/src/download.rs @@ -15,6 +15,8 @@ static LOADING: AtomicBool = AtomicBool::new(false); pub(crate) static PROGRESS: AtomicU8 = AtomicU8::new(0); pub type Progress = &'static AtomicU8; +/// The downloader, which has all of the state necessary +/// to download tracks and add them to the queue. pub struct Downloader { queue: Sender, tx: Sender, @@ -24,7 +26,10 @@ pub struct Downloader { } impl Downloader { - pub async fn init(size: usize, tracks: tracks::List, tx: Sender) -> Handle { + /// Initializes the downloader with a track list. + /// + /// `tx` specifies the [`Sender`] to be notified with [`crate::Message::Loaded`]. + pub fn init(size: usize, tracks: tracks::List, tx: Sender) -> Handle { let client = Client::new(); let (qtx, qrx) = mpsc::channel(size - 1); @@ -64,25 +69,30 @@ impl Downloader { } } } + +/// Downloader handle, responsible for managing +/// the downloader task and internal buffer. pub struct Handle { queue: Receiver, handle: JoinHandle>, } +/// The output when a track is requested from the downloader. pub enum Output { Loading(Option), Queued(tracks::Queued), } impl Handle { - pub async fn track(&mut self) -> Output { - match self.queue.try_recv() { - Ok(queued) => Output::Queued(queued), - Err(_) => { + /// Gets either a queued track, or a progress report, + /// depending on the state of the internal download buffer. + #[rustfmt::skip] + pub fn track(&mut self) -> Output { + self.queue.try_recv().map_or_else(|_| { LOADING.store(true, atomic::Ordering::Relaxed); Output::Loading(Some(&PROGRESS)) - } - } + }, Output::Queued, + ) } } diff --git a/src/main.rs b/src/main.rs index 6ac0cf0..758e678 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,4 @@ //! An extremely simple lofi player. -#![warn(clippy::all, clippy::pedantic, clippy::nursery)] - pub mod error; use std::path::PathBuf; @@ -98,23 +96,21 @@ pub fn data_dir() -> crate::Result { async fn main() -> eyre::Result<()> { let args = Args::parse(); - if let Some(command) = args.command { + #[cfg(feature = "scrape")] + if let Some(command) = &args.command { match command { - #[cfg(feature = "scrape")] Commands::Scrape { source } => match source { Source::Archive => scrapers::archive::scrape().await?, Source::Lofigirl => scrapers::lofigirl::scrape().await?, Source::Chillhop => scrapers::chillhop::scrape().await?, }, } - } else { - let player = Player::init(args).await?; - let environment = player.environment(); - let result = player.run().await; + } - environment.cleanup(result.is_ok())?; - result?; - }; + let player = Player::init(args).await?; + let environment = player.environment(); + let result = player.run().await; - Ok(()) + environment.cleanup(result.is_ok())?; + Ok(result?) } diff --git a/src/player.rs b/src/player.rs index 21fd3ab..bb3aeee 100644 --- a/src/player.rs +++ b/src/player.rs @@ -23,13 +23,13 @@ pub enum Current { impl Default for Current { fn default() -> Self { - Current::Loading(None) + Self::Loading(None) } } impl Current { - pub fn loading(&self) -> bool { - return matches!(self, Current::Loading(_)); + pub const fn loading(&self) -> bool { + matches!(self, Self::Loading(_)) } } @@ -53,25 +53,25 @@ impl Drop for Player { } impl Player { - pub fn environment(&self) -> ui::Environment { + pub const fn environment(&self) -> ui::Environment { self.ui.environment } - pub async fn set_current(&mut self, current: Current) -> crate::Result<()> { + pub fn set_current(&mut self, current: Current) -> crate::Result<()> { self.current = current.clone(); - self.update(ui::Update::Track(current)).await?; + self.update(ui::Update::Track(current))?; let Current::Track(track) = &self.current else { return Ok(()); }; - let bookmarked = self.bookmarks.bookmarked(&track); - self.update(ui::Update::Bookmarked(bookmarked)).await?; + let bookmarked = self.bookmarks.bookmarked(track); + self.update(ui::Update::Bookmarked(bookmarked))?; Ok(()) } - pub async fn update(&mut self, update: ui::Update) -> crate::Result<()> { + pub fn update(&mut self, update: ui::Update) -> crate::Result<()> { self.broadcast.send(update)?; Ok(()) } @@ -87,21 +87,19 @@ impl Player { let (tx, rx) = mpsc::channel(8); tx.send(Message::Init).await?; let (utx, urx) = broadcast::channel(8); - let current = Current::Loading(None); let list = List::load(args.track_list.as_ref()).await?; - let state = - ui::State::initial(sink.clone(), args.width, current.clone(), list.name.clone()); + let state = ui::State::initial(Arc::clone(&sink), args.width, list.name.clone()); let volume = PersistentVolume::load().await?; sink.set_volume(volume.float()); Ok(Self { ui: ui::Handle::init(tx.clone(), urx, state, &args).await?, - downloader: Downloader::init(args.buffer_size as usize, list, tx.clone()).await, - waiter: waiter::Handle::new(sink.clone(), tx.clone()), + downloader: Downloader::init(args.buffer_size as usize, list, tx.clone()), + waiter: waiter::Handle::new(Arc::clone(&sink), tx.clone()), bookmarks: Bookmarks::load().await?, - current, + current: Current::default(), broadcast: utx, rx, sink, @@ -112,15 +110,15 @@ impl Player { pub async fn close(&self) -> crate::Result<()> { self.bookmarks.save().await?; - PersistentVolume::save(self.sink.volume() as f32).await?; + PersistentVolume::save(self.sink.volume()).await?; Ok(()) } - pub async fn play(&mut self, queued: tracks::Queued) -> crate::Result<()> { + pub fn play(&mut self, queued: tracks::Queued) -> crate::Result<()> { let decoded = queued.decode()?; self.sink.append(decoded.data); - self.set_current(Current::Track(decoded.info)).await?; + self.set_current(Current::Track(decoded.info))?; self.waiter.notify(); Ok(()) @@ -135,12 +133,12 @@ impl Player { } self.sink.stop(); - match self.downloader.track().await { + match self.downloader.track() { download::Output::Loading(progress) => { - self.set_current(Current::Loading(progress)).await?; + self.set_current(Current::Loading(progress))?; } download::Output::Queued(queued) => { - self.play(queued).await?; + self.play(queued)?; } }; } @@ -160,19 +158,19 @@ impl Player { Message::ChangeVolume(change) => { self.sink .set_volume((self.sink.volume() + change).clamp(0.0, 1.0)); - self.update(ui::Update::Volume).await?; + self.update(ui::Update::Volume)?; } Message::SetVolume(set) => { self.sink.set_volume(set.clamp(0.0, 1.0)); - self.update(ui::Update::Volume).await?; + self.update(ui::Update::Volume)?; } Message::Bookmark => { let Current::Track(current) = &self.current else { continue; }; - let bookmarked = self.bookmarks.bookmark(current).await?; - self.update(ui::Update::Bookmarked(bookmarked)).await?; + let bookmarked = self.bookmarks.bookmark(current)?; + self.update(ui::Update::Bookmarked(bookmarked))?; } Message::Quit => break, } diff --git a/src/tests/bookmark.rs b/src/tests/bookmark.rs index 0059693..8637b9a 100644 --- a/src/tests/bookmark.rs +++ b/src/tests/bookmark.rs @@ -11,8 +11,8 @@ mod bookmark { } } - #[tokio::test] - async fn toggle_and_check() { + #[test] + fn toggle_and_check() { let mut bm = Bookmarks { entries: vec![] }; let info = test_info("p.mp3", "Nice Track"); @@ -20,37 +20,37 @@ mod bookmark { assert!(!bm.bookmarked(&info)); // bookmark it - let added = bm.bookmark(&info).await.unwrap(); + let added = bm.bookmark(&info).unwrap(); assert!(added); assert!(bm.bookmarked(&info)); // un-bookmark it - let removed = bm.bookmark(&info).await.unwrap(); + let removed = bm.bookmark(&info).unwrap(); assert!(!removed); assert!(!bm.bookmarked(&info)); } - #[tokio::test] - async fn multiple_bookmarks() { + #[test] + fn multiple_bookmarks() { let mut bm = Bookmarks { entries: vec![] }; let info1 = test_info("track1.mp3", "Track One"); let info2 = test_info("track2.mp3", "Track Two"); - bm.bookmark(&info1).await.unwrap(); - bm.bookmark(&info2).await.unwrap(); + bm.bookmark(&info1).unwrap(); + bm.bookmark(&info2).unwrap(); assert!(bm.bookmarked(&info1)); assert!(bm.bookmarked(&info2)); assert_eq!(bm.entries.len(), 2); } - #[tokio::test] - async fn duplicate_bookmark_removes() { + #[test] + fn duplicate_bookmark_removes() { let mut bm = Bookmarks { entries: vec![] }; let info = test_info("x.mp3", "X"); - bm.bookmark(&info).await.unwrap(); - let is_added = bm.bookmark(&info).await.unwrap(); + bm.bookmark(&info).unwrap(); + let is_added = bm.bookmark(&info).unwrap(); assert!(!is_added); assert!(bm.entries.is_empty()); diff --git a/src/tests/ui.rs b/src/tests/ui.rs index 246ced4..85d39e7 100644 --- a/src/tests/ui.rs +++ b/src/tests/ui.rs @@ -79,7 +79,7 @@ mod window { #[test] fn simple() { - let mut w = Window::new(3, false); + let w = Window::new(3, false); let (render, height) = w.render(vec![String::from("abc")], false, true).unwrap(); const MIDDLE: &str = "─────"; @@ -89,7 +89,7 @@ mod window { #[test] fn spaced() { - let mut w = Window::new(3, false); + let w = Window::new(3, false); let (render, height) = w .render( vec![String::from("abc"), String::from(" b"), String::from("c")], @@ -137,7 +137,7 @@ mod interface { #[test] fn loading() { let sink = Arc::new(rodio::Sink::new().0); - let mut state = State::initial(sink, 3, Current::Loading(None), String::from("test")); + let mut state = State::initial(sink, 3, String::from("test")); let menu = interface::menu(&mut state, Params::default()); assert_eq!(menu[0], "loading "); @@ -157,7 +157,7 @@ mod interface { fn volume() { let sink = Arc::new(rodio::Sink::new().0); sink.set_volume(0.5); - let mut state = State::initial(sink, 3, Current::Loading(None), String::from("test")); + let mut state = State::initial(sink, 3, String::from("test")); state.timer = Some(Instant::now()); let menu = interface::menu(&mut state, Params::default()); @@ -179,12 +179,9 @@ mod interface { fn progress() { let sink = Arc::new(rodio::Sink::new().0); PROGRESS.store(50, std::sync::atomic::Ordering::Relaxed); - let mut state = State::initial( - sink, - 3, - Current::Loading(Some(&PROGRESS)), - String::from("test"), - ); + let mut state = State::initial(sink, 3, String::from("test")); + state.current = Current::Loading(Some(&PROGRESS)); + let menu = interface::menu(&mut state, Params::default()); assert_eq!(menu[0], format!("loading {} ", "50%".bold())); @@ -210,8 +207,8 @@ mod interface { duration: Some(Duration::from_secs(8)), }; - let current = Current::Track(track.clone()); - let mut state = State::initial(sink, 3, current, String::from("test")); + let mut state = State::initial(sink, 3, String::from("test")); + state.current = Current::Track(track.clone()); let menu = interface::menu(&mut state, Params::default()); assert_eq!( diff --git a/src/tracks.rs b/src/tracks.rs index 9b115e9..c2fa33f 100644 --- a/src/tracks.rs +++ b/src/tracks.rs @@ -2,24 +2,18 @@ //! of tracks, as well as downloading them & finding new ones. //! //! There are several structs which represent the different stages -//! that go on in downloading and playing tracks. The proccess for fetching tracks, -//! and what structs are relevant in each step, are as follows. +//! that go on in downloading and playing tracks. When first queued, +//! the downloader will return a [`Queued`] track. //! -//! First Stage, when a track is initially fetched. -//! 1. Raw entry selected from track list. -//! 2. Raw entry split into path & display name. -//! 3. Track data fetched, and [`QueuedTrack`] is created which includes a [`TrackName`] that may be raw. -//! -//! Second Stage, when a track is played. -//! 1. Track data is decoded. -//! 2. [`Info`] created from decoded data. -//! 3. [`Decoded`] made from [`Info`] and the original decoded data. +//! Then, when it's time to play the track, it is decoded into +//! a [`Decoded`] track, which includes all the information +//! in the form of [`Info`]. use std::{fmt::Debug, io::Cursor, time::Duration}; use bytes::Bytes; use rodio::{Decoder, Source as _}; -use unicode_segmentation::UnicodeSegmentation; +use unicode_segmentation::UnicodeSegmentation as _; pub mod list; pub use list::List; @@ -27,7 +21,7 @@ pub mod error; pub mod format; pub use error::{Error, Result}; -use crate::tracks::error::WithTrackContext; +use crate::tracks::error::WithTrackContext as _; /// Just a shorthand for a decoded [Bytes]. pub type DecodedData = Decoder>; @@ -35,7 +29,7 @@ pub type DecodedData = Decoder>; /// Tracks which are still waiting in the queue, and can't be played yet. /// /// This means that only the data & track name are included. -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] pub struct Queued { /// Display name of the track. pub display: String, @@ -66,6 +60,7 @@ impl Queued { Decoded::new(self) } + /// Creates a new queued track. pub fn new(path: String, data: Bytes, display: Option) -> Result { let display = match display { None => self::format::name(&path)?, @@ -73,8 +68,8 @@ impl Queued { }; Ok(Self { - path, display, + path, data, }) } @@ -122,7 +117,7 @@ impl Info { } } -/// This struct is seperate from [Track] since it is generated lazily from +/// This struct is separate from [Track] since it is generated lazily from /// a track, and not when the track is first downloaded. pub struct Decoded { /// Has both the formatted name and some information from the decoded data. @@ -138,7 +133,7 @@ impl Decoded { pub fn new(track: Queued) -> Result { let (path, display) = (track.path.clone(), track.display.clone()); let data = Decoder::builder() - .with_byte_len(track.data.len().try_into().unwrap()) + .with_byte_len(track.data.len().try_into()?) .with_data(Cursor::new(track.data)) .build() .track(track.display)?; diff --git a/src/tracks/error.rs b/src/tracks/error.rs index 0eb885e..4e712d4 100644 --- a/src/tracks/error.rs +++ b/src/tracks/error.rs @@ -19,6 +19,9 @@ pub enum Kind { #[error("unable to fetch data: {0}")] Request(#[from] reqwest::Error), + + #[error("couldn't handle integer track length: {0}")] + Integer(#[from] std::num::TryFromIntError), } #[derive(Debug, thiserror::Error)] diff --git a/src/tracks/format.rs b/src/tracks/format.rs index b8920fb..9340a38 100644 --- a/src/tracks/format.rs +++ b/src/tracks/format.rs @@ -1,10 +1,10 @@ -use convert_case::{Case, Casing}; +use convert_case::{Case, Casing as _}; use lazy_static::lazy_static; use regex::Regex; use std::path::Path; use url::form_urlencoded; -use super::error::WithTrackContext; +use super::error::WithTrackContext as _; lazy_static! { static ref MASTER_PATTERNS: [Regex; 5] = [ @@ -84,7 +84,7 @@ pub fn name(name: &str) -> super::Result { // If the entire name of the track is a number, then just return it. if skip == name.len() { - Ok(name.trim().to_string()) + Ok(name.trim().to_owned()) } else { // We've already checked before that the bound is at an ASCII digit. #[allow(clippy::string_slice)] diff --git a/src/tracks/list.rs b/src/tracks/list.rs index 13c6502..70912ae 100644 --- a/src/tracks/list.rs +++ b/src/tracks/list.rs @@ -6,8 +6,8 @@ use std::{ sync::atomic::{AtomicU8, Ordering}, }; -use bytes::{BufMut, Bytes, BytesMut}; -use futures::StreamExt; +use bytes::{BufMut as _, Bytes, BytesMut}; +use futures::StreamExt as _; use reqwest::Client; use tokio::fs; @@ -15,7 +15,7 @@ use crate::{ data_dir, tracks::{ self, - error::{self, WithTrackContext}, + error::{self, WithTrackContext as _}, }, }; @@ -114,7 +114,7 @@ impl List { while let Some(item) = stream.next().await { let chunk = item.track(track)?; downloaded = min(downloaded + (chunk.len() as u64), total); - let rounded = ((downloaded as f32) / (total as f32) * 100.0).round() as u8; + let rounded = ((downloaded as f64) / (total as f64) * 100.0).round() as u8; progress.store(rounded, Ordering::Relaxed); bytes.put(chunk); diff --git a/src/ui.rs b/src/ui.rs index dffa53a..6d26d6f 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -22,8 +22,8 @@ pub mod mpris; type Result = std::result::Result; -/// The error type for the UI, which is used to handle errors that occur -/// while drawing the UI or handling input. +/// The error type for the UI, which is used to handle errors +/// that occur while drawing the UI or handling input. #[derive(Debug, thiserror::Error)] pub enum Error { #[error("unable to convert number")] @@ -36,7 +36,7 @@ pub enum Error { CrateSend(#[from] tokio::sync::mpsc::error::SendError), #[error("sharing state between backend and frontend failed")] - UiSend(#[from] tokio::sync::broadcast::error::SendError), + Send(#[from] tokio::sync::broadcast::error::SendError), #[cfg(feature = "mpris")] #[error("mpris bus error")] @@ -47,6 +47,11 @@ pub enum Error { Fdo(#[from] mpris_server::zbus::fdo::Error), } +/// The UI state, which is all of the information that +/// the user interface needs to display to the user. +/// +/// It should be noted that this is also used by MPRIS to keep +/// track of state. #[derive(Clone)] pub struct State { pub sink: Arc, @@ -60,19 +65,26 @@ pub struct State { } impl State { - pub fn initial(sink: Arc, width: usize, current: Current, list: String) -> Self { + /// Creates an initial UI state. + pub fn initial(sink: Arc, width: usize, list: String) -> Self { let width = 21 + width.min(32) * 2; Self { width, sink, - current, list, + current: Current::default(), bookmarked: false, timer: None, } } } +/// A UI update sent out by the main player thread, which may +/// not be immediately applied by the UI. +/// +/// This corresponds to user actions, like bookmarking a track, +/// skipping, or changing the volume. The difference is that it also +/// contains the new information about the track. #[derive(Debug, Clone)] pub enum Update { Track(Current), @@ -81,12 +93,16 @@ pub enum Update { Quit, } +/// Just a simple wrapper for the two primary tasks that the UI +/// requires to function. #[derive(Debug)] struct Tasks { render: JoinHandle>, input: JoinHandle>, } +/// The UI handle for controlling the state of the UI, as well as +/// updating MPRIS information and other small interfacing tasks. pub struct Handle { tasks: Tasks, pub environment: Environment, @@ -102,6 +118,14 @@ impl Drop for Handle { } impl Handle { + /// The main UI process, which will both render the UI to the terminal + /// and also update state. + /// + /// It does both of these things at a fixed interval, due to things + /// like the track duration changing too frequently. + /// + /// `rx` is the receiver for state updates, `state` the initial state, + /// and `params` specifies aesthetic options that are specified by the user. async fn ui( mut rx: broadcast::Receiver, mut state: State, @@ -111,8 +135,6 @@ impl Handle { let mut window = Window::new(state.width, params.borderless); loop { - interface::draw(&mut state, &mut window, params)?; - if let Ok(message) = rx.try_recv() { match message { Update::Track(track) => state.current = track, @@ -122,12 +144,15 @@ impl Handle { } }; + interface::draw(&mut state, &mut window, params)?; interval.tick().await; } Ok(()) } + /// Initializes the UI itself, along with all of the tasks that are related to it. + #[allow(clippy::unused_async)] pub async fn init( tx: Sender, updater: broadcast::Receiver, diff --git a/src/ui/environment.rs b/src/ui/environment.rs index 13b5851..c844b29 100644 --- a/src/ui/environment.rs +++ b/src/ui/environment.rs @@ -45,7 +45,7 @@ impl Environment { panic::set_hook(Box::new(move |info| { let _ = environment.cleanup(false); - eprintln!("panic: {}", info); + eprintln!("panic: {info}"); })); Ok(environment) diff --git a/src/ui/interface.rs b/src/ui/interface.rs index 5f512e2..1e60776 100644 --- a/src/ui/interface.rs +++ b/src/ui/interface.rs @@ -26,7 +26,7 @@ impl From<&Args> for Params { } pub(crate) fn menu(state: &mut ui::State, params: Params) -> Vec { - let action = components::action(&state, state.width); + let action = components::action(state, state.width); let middle = match state.timer { Some(timer) => { @@ -38,7 +38,7 @@ pub(crate) fn menu(state: &mut ui::State, params: Params) -> Vec { components::audio_bar(state.width - 17, volume, &percentage) } - None => components::progress_bar(&state, state.width - 16), + None => components::progress_bar(state, state.width - 16), }; let controls = components::controls(state.width); diff --git a/src/ui/mpris.rs b/src/ui/mpris.rs index d26e108..761b461 100644 --- a/src/ui/mpris.rs +++ b/src/ui/mpris.rs @@ -263,8 +263,8 @@ pub struct Server { /// The inner MPRIS server. inner: mpris_server::Server, - /// Broadcast reciever. - reciever: broadcast::Receiver, + /// Broadcast receiver. + receiver: broadcast::Receiver, } impl Server { @@ -273,16 +273,17 @@ impl Server { &mut self, properties: impl IntoIterator + Send + Sync, ) -> ui::Result<()> { - while let Ok(update) = self.reciever.try_recv() { + while let Ok(update) = self.receiver.try_recv() { if let Update::Track(current) = update { self.player().current.swap(Arc::new(current)); } } - self.inner.properties_changed(properties).await?; + self.inner.properties_changed(properties).await?; Ok(()) } + /// Updates the volume with the latest information. pub async fn update_volume(&mut self) -> ui::Result<()> { self.changed(vec![Property::Volume(self.player().sink.volume().into())]) .await?; @@ -290,7 +291,7 @@ impl Server { Ok(()) } - /// Shorthand to emit a `PropertiesChanged` signal, specifically about playback. + /// Updates the playback with the latest information. pub async fn update_playback(&mut self) -> ui::Result<()> { let status = self.player().playback_status().await?; self.changed(vec![Property::PlaybackStatus(status)]).await?; @@ -298,6 +299,7 @@ impl Server { Ok(()) } + /// Updates the current track data with the current information. pub async fn update_metadata(&mut self) -> ui::Result<()> { let metadata = self.player().metadata().await?; self.changed(vec![Property::Metadata(metadata)]).await?; @@ -314,7 +316,7 @@ impl Server { pub async fn new( state: ui::State, sender: mpsc::Sender, - reciever: broadcast::Receiver, + receiver: broadcast::Receiver, ) -> ui::Result { let suffix = if env::var("LOWFI_FIXED_MPRIS_NAME").is_ok_and(|x| x == "1") { String::from("lowfi") @@ -335,7 +337,7 @@ impl Server { Ok(Self { inner: server, - reciever, + receiver, }) } } diff --git a/src/ui/window.rs b/src/ui/window.rs index 8412ddb..c80f645 100644 --- a/src/ui/window.rs +++ b/src/ui/window.rs @@ -2,11 +2,11 @@ use std::io::{stdout, Stdout}; use crossterm::{ cursor::{MoveToColumn, MoveUp}, - style::{Print, Stylize}, + style::{Print, Stylize as _}, terminal::{Clear, ClearType}, }; -use std::fmt::Write; -use unicode_segmentation::UnicodeSegmentation; +use std::fmt::Write as _; +use unicode_segmentation::UnicodeSegmentation as _; /// Represents an abstraction for drawing the actual lowfi window itself. /// @@ -52,7 +52,7 @@ impl Window { } pub(crate) fn render( - &mut self, + &self, content: Vec, space: bool, testing: bool, diff --git a/src/volume.rs b/src/volume.rs index c6c3d0b..89119b5 100644 --- a/src/volume.rs +++ b/src/volume.rs @@ -1,8 +1,11 @@ +//! Persistent volume management. use std::{num::ParseIntError, path::PathBuf}; use tokio::fs; +/// Shorthand for a [`Result`] with a persistent volume error. type Result = std::result::Result; +/// Errors which occur when loading/unloading persistent volume. #[derive(Debug, thiserror::Error)] pub enum Error { #[error("couldn't find config directory")] @@ -27,7 +30,7 @@ impl PersistentVolume { /// Retrieves the config directory. async fn config() -> Result { let config = dirs::config_dir() - .ok_or_else(|| Error::Directory)? + .ok_or(Error::Directory)? .join(PathBuf::from("lowfi")); if !config.exists() {