From 6f6a552de53c30312d8501e4eb63ab74ecfb3366 Mon Sep 17 00:00:00 2001 From: Tal <83217276+talwat@users.noreply.github.com> Date: Sat, 3 Jan 2026 13:25:21 +0100 Subject: [PATCH] chore: eliminate public statics --- src/audio.rs | 2 ++ src/{download.rs => downloader.rs} | 44 ++++++++++++++++++++++++------ src/main.rs | 3 +- src/player.rs | 13 +++++---- src/scrapers.rs | 1 + src/tasks.rs | 4 +-- src/tests/tracks.rs | 4 +-- src/tests/ui.rs | 6 ++-- src/tracks/list.rs | 13 ++++----- src/ui.rs | 2 ++ src/ui/environment.rs | 2 ++ src/ui/interface.rs | 2 ++ src/ui/interface/components.rs | 2 +- 13 files changed, 67 insertions(+), 31 deletions(-) rename src/{download.rs => downloader.rs} (76%) diff --git a/src/audio.rs b/src/audio.rs index c202ab6..6774fdc 100644 --- a/src/audio.rs +++ b/src/audio.rs @@ -1,3 +1,5 @@ +//! Some simple audio related utilities. + pub mod waiter; /// This gets the output stream while also shutting up alsa with [libc]. diff --git a/src/download.rs b/src/downloader.rs similarity index 76% rename from src/download.rs rename to src/downloader.rs index 906aa36..255dc3b 100644 --- a/src/download.rs +++ b/src/downloader.rs @@ -1,3 +1,5 @@ +//! All of the logic and state relating to the downloader. + use std::{ sync::atomic::{self, AtomicBool, AtomicU8}, time::Duration, @@ -13,14 +15,40 @@ use tokio::sync::mpsc; /// indicate to the UI that a download is in progress. static LOADING: AtomicBool = AtomicBool::new(false); -/// Global download progress in the range 0..=100 updated atomically. +/// Global download progress as an integer updated atomically. /// -/// The UI can read this `AtomicU8` to render a global progress indicator -/// when there isn't an immediately queued track available. -pub(crate) static PROGRESS: AtomicU8 = AtomicU8::new(0); +/// This is just a [`AtomicU8`] from 0 to 255, really representing +/// a progress percentage as just a simple integer. For instance, +/// 0.5 would be represented here as 127. +static PROGRESS: AtomicU8 = AtomicU8::new(0); -/// A convenient alias for the progress `AtomicU8` pointer type. -pub type Progress = &'static AtomicU8; +/// A convenient wrapper for the global progress. This is updated by the downloader, +/// and then accessed by the UI to display progress when there isn't an available +/// queued track. +#[derive(Clone, Copy, Debug)] +pub struct Progress(&'static AtomicU8); + +impl Progress { + /// Creates a new handle to the global progress. + pub fn new() -> Self { + Self(&PROGRESS) + } + + /// Sets the global progress. + /// + /// `value` must be between 0 and 1. + pub fn set(&self, value: f32) { + self.0.store( + (value * f32::from(u8::MAX)).round() as u8, + atomic::Ordering::Relaxed, + ); + } + + /// Returns the global progress as a [`f32`] between 0 and 1. + pub fn get(&self) -> f32 { + f32::from(self.0.load(atomic::Ordering::Relaxed)) / f32::from(u8::MAX) + } +} /// The downloader, which has all of the state necessary /// to download tracks and add them to the queue. @@ -57,7 +85,7 @@ impl Downloader { loop { let result = self .tracks - .random(&self.client, &PROGRESS, &mut self.rng) + .random(&self.client, Progress::new(), &mut self.rng) .await; match result { @@ -106,7 +134,7 @@ impl Handle { pub fn track(&mut self) -> Output { self.queue.try_recv().map_or_else(|_| { LOADING.store(true, atomic::Ordering::Relaxed); - Output::Loading(Some(&PROGRESS)) + Output::Loading(Some(Progress::new())) }, Output::Queued, ) } diff --git a/src/main.rs b/src/main.rs index 32eb676..f71fc78 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,12 @@ //! An extremely simple lofi player. + use crate::player::Player; use clap::{Parser, Subcommand}; use std::path::PathBuf; pub mod audio; pub mod bookmark; -pub mod download; +pub mod downloader; pub mod error; pub mod message; pub mod player; diff --git a/src/player.rs b/src/player.rs index 6f32744..f206730 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,11 +1,12 @@ -use std::sync::Arc; +//! The player, which contains all of the core logic behind the music player. +use std::sync::Arc; use tokio::sync::mpsc::{self, Receiver}; use crate::{ audio::waiter, bookmark::Bookmarks, - download, + downloader, tracks::{self, List}, ui, volume::PersistentVolume, @@ -20,7 +21,7 @@ use crate::{ pub enum Current { /// Waiting for a track to arrive. The optional `Progress` is used to /// indicate global download progress when present. - Loading(Option), + Loading(Option), /// A decoded track that can be played; contains the track `Info`. Track(tracks::Info), @@ -52,7 +53,7 @@ pub struct Player { current: Current, /// Background downloader that fills the internal queue. - downloader: download::Handle, + downloader: downloader::Handle, /// Receiver for incoming `Message` commands. rx: Receiver, @@ -158,10 +159,10 @@ impl Player { self.sink.stop(); match self.downloader.track() { - download::Output::Loading(progress) => { + downloader::Output::Loading(progress) => { self.set_current(Current::Loading(progress))?; } - download::Output::Queued(queued) => self.play(queued)?, + downloader::Output::Queued(queued) => self.play(queued)?, } } Message::Play => { diff --git a/src/scrapers.rs b/src/scrapers.rs index f051d13..fe6575a 100644 --- a/src/scrapers.rs +++ b/src/scrapers.rs @@ -1,3 +1,4 @@ +//! Built in web scraping, which isn't guaranteed to have a unified UI. #![allow(clippy::all)] use std::path::{Path, PathBuf}; diff --git a/src/tasks.rs b/src/tasks.rs index a8afbe0..61926a6 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -1,7 +1,7 @@ //! Task management. //! -//! This file aims to abstract a lot of annoying Rust async logic, which may be subject to change. -//! For those who are not intimately familiar with async rust, this will be very confusing. +//! This file aims to abstract a lot of potentially annoying Rust async logic, which may be +//! subject to change. use futures_util::TryFutureExt; use std::future::Future; diff --git a/src/tests/tracks.rs b/src/tests/tracks.rs index f280a65..4f17326 100644 --- a/src/tests/tracks.rs +++ b/src/tests/tracks.rs @@ -108,7 +108,7 @@ mod decoded { #[cfg(test)] mod list { - use crate::{download::PROGRESS, tracks::List}; + use crate::{downloader::Progress, tracks::List}; use reqwest::Client; #[test] @@ -172,7 +172,7 @@ mod list { let client = Client::new(); let track = list - .random(&client, &PROGRESS, &mut fastrand::Rng::new()) + .random(&client, Progress::new(), &mut fastrand::Rng::new()) .await .unwrap(); assert_eq!(track.display, "Apple Juice"); diff --git a/src/tests/ui.rs b/src/tests/ui.rs index 545a7da..cce2f32 100644 --- a/src/tests/ui.rs +++ b/src/tests/ui.rs @@ -125,7 +125,7 @@ mod interface { use tokio::time::Instant; use crate::{ - download::PROGRESS, + downloader::Progress, player::Current, tracks, ui::{Interface, State}, @@ -175,9 +175,9 @@ mod interface { #[tokio::test] async fn progress() { let sink = Arc::new(rodio::Sink::new().0); - PROGRESS.store(50, std::sync::atomic::Ordering::Relaxed); + Progress::new().set(0.5); let mut state = State::initial(sink, String::from("test")); - state.current = Current::Loading(Some(&PROGRESS)); + state.current = Current::Loading(Some(Progress::new())); let menu = Interface::default().menu(&mut state); diff --git a/src/tracks/list.rs b/src/tracks/list.rs index 2baf5bc..6140cd7 100644 --- a/src/tracks/list.rs +++ b/src/tracks/list.rs @@ -1,10 +1,7 @@ //! The module containing all of the logic behind track lists, //! as well as obtaining track names & downloading the raw audio data -use std::{ - cmp::min, - sync::atomic::{AtomicU8, Ordering}, -}; +use std::cmp::min; use bytes::{BufMut as _, Bytes, BytesMut}; use futures_util::StreamExt as _; @@ -13,6 +10,7 @@ use tokio::fs; use crate::{ data_dir, + downloader::Progress, tracks::{ self, error::{self, WithTrackContext as _}, @@ -70,7 +68,7 @@ impl List { &self, track: &str, client: &Client, - progress: Option<&AtomicU8>, + progress: Option, ) -> tracks::Result<(Bytes, String)> { // If the track has a protocol, then we should ignore the base for it. let path = if track.contains("://") { @@ -114,8 +112,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 f64) / (total as f64) * 100.0).round() as u8; - progress.store(rounded, Ordering::Relaxed); + progress.set(downloaded as f32 / total as f32); bytes.put(chunk); } @@ -133,7 +130,7 @@ impl List { pub async fn random( &self, client: &Client, - progress: &AtomicU8, + progress: Progress, rng: &mut fastrand::Rng, ) -> tracks::Result { let (path, display) = self.random_path(rng); diff --git a/src/ui.rs b/src/ui.rs index 3c17369..3deaf72 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,3 +1,5 @@ +//! Everything which has to do with the user interface, including both input and output. + use std::sync::Arc; use crate::player::Current; diff --git a/src/ui/environment.rs b/src/ui/environment.rs index 152b7e6..bd3d669 100644 --- a/src/ui/environment.rs +++ b/src/ui/environment.rs @@ -1,3 +1,5 @@ +//! Contains the initialization and other handling of the terminal environment. + use std::{io::stdout, panic}; use crossterm::{ diff --git a/src/ui/interface.rs b/src/ui/interface.rs index 306a725..02a6e44 100644 --- a/src/ui/interface.rs +++ b/src/ui/interface.rs @@ -1,3 +1,5 @@ +//! Responsible for the actual graphical interface of lowfi. + use crate::{ ui::{self, State}, Args, diff --git a/src/ui/interface/components.rs b/src/ui/interface/components.rs index 7ce747d..6dc14c1 100644 --- a/src/ui/interface/components.rs +++ b/src/ui/interface/components.rs @@ -111,7 +111,7 @@ impl ActionBar { pub fn action(state: &ui::State, width: usize) -> String { let action = match state.current.clone() { Current::Loading(progress) => { - ActionBar::Loading(progress.map(|x| x.load(std::sync::atomic::Ordering::Relaxed))) + ActionBar::Loading(progress.map(|x| (x.get() * 100.0).round() as u8)) } Current::Track(info) => { if state.sink.volume() < 0.01 {