From dc1ecc81ea2bd388ff7dfd4bf15debe78185b796 Mon Sep 17 00:00:00 2001 From: Tarasov Aleksandr <55220741+arabianq@users.noreply.github.com> Date: Fri, 15 May 2026 19:32:26 +0300 Subject: [PATCH] refactor: replace all rust Result with anyhow::Result (#103) --- Cargo.lock | 1 + Cargo.toml | 1 + src/bin/cli.rs | 9 ++++----- src/bin/daemon.rs | 9 +++++---- src/gui/mod.rs | 14 +++++--------- src/main.rs | 4 ++-- src/types/audio_player.rs | 31 ++++++++++++++---------------- src/types/config.rs | 17 +++++++++-------- src/utils/config.rs | 5 +++-- src/utils/daemon.rs | 7 ++++--- src/utils/gui.rs | 6 +++--- src/utils/pipewire.rs | 40 ++++++++++++++++++++------------------- 12 files changed, 72 insertions(+), 72 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7b93571..f4e2afe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3097,6 +3097,7 @@ checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" name = "pwsp" version = "1.8.1" dependencies = [ + "anyhow", "async-trait", "clap", "dirs", diff --git a/Cargo.toml b/Cargo.toml index 92481c4..511b3a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ clap = { version = "4.6.1", default-features = false, features = [ ] } dirs = "6.0.0" itertools = "0.14.0" +anyhow = "1.0.102" rodio = { git = "https://github.com/arabianq/rodio.git", rev = "1a08f281c352622bd82b87b8731585245802d9cf", default-features = false, features = [ "symphonia-all", diff --git a/src/bin/cli.rs b/src/bin/cli.rs index 88223c3..907fd1d 100644 --- a/src/bin/cli.rs +++ b/src/bin/cli.rs @@ -1,9 +1,10 @@ +use anyhow::{Result, anyhow}; use clap::{Parser, Subcommand}; use pwsp::{ types::socket::Request, utils::daemon::{make_request, wait_for_daemon}, }; -use std::{error::Error, path::PathBuf}; +use std::path::PathBuf; #[derive(Parser, Debug)] #[command(version, about, long_about = None)] @@ -146,7 +147,7 @@ enum SetCommands { } #[tokio::main] -async fn main() -> Result<(), Box> { +async fn main() -> Result<()> { let cli = Cli::parse(); wait_for_daemon().await?; @@ -204,9 +205,7 @@ async fn main() -> Result<(), Box> { }, }; - let response = make_request(request) - .await - .map_err(|e| e as Box)?; + let response = make_request(request).await.map_err(|e| anyhow!(e))?; println!("{} : {}", response.status, response.message); Ok(()) diff --git a/src/bin/daemon.rs b/src/bin/daemon.rs index 2d522f6..78e17ba 100644 --- a/src/bin/daemon.rs +++ b/src/bin/daemon.rs @@ -1,3 +1,4 @@ +use anyhow::{Result, anyhow}; use pwsp::{ types::socket::{MAX_MESSAGE_SIZE, Request, Response}, utils::{ @@ -11,7 +12,7 @@ use pwsp::{ }, }; use std::os::unix::fs::PermissionsExt; -use std::{error::Error, fs, time::Duration}; +use std::{fs, time::Duration}; use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, net::UnixListener, @@ -19,11 +20,11 @@ use tokio::{ }; #[tokio::main] -async fn main() -> Result<(), Box> { +async fn main() -> Result<()> { create_runtime_dir()?; if is_daemon_running()? { - return Err("Another instance is already running.".into()); + return Err(anyhow!("Another instance is already running.")); } get_daemon_config(); // Initialize daemon config @@ -76,7 +77,7 @@ async fn main() -> Result<(), Box> { Ok(()) } -async fn commands_loop(listener: UnixListener) -> Result<(), Box> { +async fn commands_loop(listener: UnixListener) -> Result<()> { loop { let (mut stream, _addr) = listener.accept().await?; diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 1393166..5187a47 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -2,6 +2,7 @@ mod draw; mod input; mod update; +use anyhow::{Result, anyhow}; use eframe::{HardwareAcceleration, NativeOptions, icon_data::from_png_bytes, run_native}; use egui::{Context, FontData, FontDefinitions, FontFamily, FontTweak, Vec2, ViewportBuilder}; use itertools::Itertools; @@ -20,7 +21,6 @@ use pwsp::{ }; use rfd::FileDialog; use std::{ - error::Error, fs, path::{Path, PathBuf}, sync::{Arc, Mutex}, @@ -198,11 +198,7 @@ impl SoundpadGui { } } -fn add_font( - font_name: &str, - font_bytes: &[u8], - fonts: &mut FontDefinitions, -) -> Result<(), Box> { +fn add_font(font_name: &str, font_bytes: &[u8], fonts: &mut FontDefinitions) -> Result<()> { let font_data = FontData::from_owned(font_bytes.to_vec()).tweak(FontTweak { scale: 1.0, hinting_override: Some(true), @@ -227,7 +223,7 @@ fn add_font( Ok(()) } -fn load_system_fonts(fonts: &mut FontDefinitions) -> Result<(), Box> { +fn load_system_fonts(fonts: &mut FontDefinitions) -> Result<()> { let (_, en_sans) = find_for_locale("en", FontStyle::Sans); let (_, en_serif) = find_for_locale("en", FontStyle::Serif); let (_, ja_sans) = find_for_locale("ja", FontStyle::Sans); @@ -246,7 +242,7 @@ fn load_system_fonts(fonts: &mut FontDefinitions) -> Result<(), Box> Ok(()) } -pub async fn run() -> Result<(), Box> { +pub async fn run() -> Result<()> { const ICON: &[u8] = include_bytes!("../../assets/icon.png"); let options = NativeOptions { @@ -283,6 +279,6 @@ pub async fn run() -> Result<(), Box> { } Ok(()) } - Err(e) => Err(e.into()), + Err(e) => Err(anyhow!(e.to_string())), } } diff --git a/src/main.rs b/src/main.rs index 146394a..37445f6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,8 @@ mod gui; -use std::error::Error; +use anyhow::Result; #[tokio::main] -async fn main() -> Result<(), Box> { +async fn main() -> Result<()> { gui::run().await } diff --git a/src/types/audio_player.rs b/src/types/audio_player.rs index 521541f..f3924f4 100644 --- a/src/types/audio_player.rs +++ b/src/types/audio_player.rs @@ -5,6 +5,7 @@ use crate::{ pipewire::{create_link, get_device, link_player_to_virtual_mic}, }, }; +use anyhow::{Result, anyhow}; use rodio::{Decoder, DeviceSinkBuilder, MixerDeviceSink, Player, Source}; use serde::{Deserialize, Serialize}; use std::{ @@ -65,7 +66,7 @@ pub struct AudioPlayer { } impl AudioPlayer { - pub async fn new() -> Result> { + pub async fn new() -> Result { let daemon_config = get_daemon_config(); let default_volume = daemon_config.default_volume.unwrap_or(1.0); @@ -88,7 +89,7 @@ impl AudioPlayer { Ok(audio_player) } - fn ensure_stream(&mut self) -> Result<&MixerDeviceSink, Box> { + fn ensure_stream(&mut self) -> Result<&MixerDeviceSink> { if self.stream_handle.is_none() { let mut sink = DeviceSinkBuilder::open_default_sink()?; sink.log_on_drop(false); @@ -126,7 +127,7 @@ impl AudioPlayer { } } - async fn link_player(&mut self) -> Result<(), Box> { + async fn link_player(&mut self) -> Result<()> { if self.player_link_sender.is_some() { return Ok(()); } @@ -140,7 +141,7 @@ impl AudioPlayer { } } - async fn link_devices(&mut self) -> Result<(), Box> { + async fn link_devices(&mut self) -> Result<()> { self.abort_link_thread(); let input_device; @@ -289,7 +290,7 @@ impl AudioPlayer { 0.0 } - pub fn seek(&mut self, position: f32, id: Option) -> Result<(), Box> { + pub fn seek(&mut self, position: f32, id: Option) -> Result<()> { let position = if position < 0.0 { 0.0 } else { position }; if let Some(id) = id { @@ -305,22 +306,18 @@ impl AudioPlayer { Ok(()) } - pub fn get_duration(&mut self, id: Option) -> Result> { + pub fn get_duration(&mut self, id: Option) -> Result { if let Some(id) = id { if let Some(sound) = self.tracks.get(&id) { - return sound.duration.ok_or("Unknown duration".into()); + return sound.duration.ok_or(anyhow!("Unknown duration")); } } else if let Some(sound) = self.tracks.values().last() { - return sound.duration.ok_or("Unknown duration".into()); + return sound.duration.ok_or(anyhow!("Unknown duration")); } - Err("No track playing".into()) + Err(anyhow!("No track playing")) } - pub async fn play( - &mut self, - file_path: &Path, - concurrent: bool, - ) -> Result> { + pub async fn play(&mut self, file_path: &Path, concurrent: bool) -> Result { let path_buf = file_path.to_path_buf(); let decoder_result = @@ -369,7 +366,7 @@ impl AudioPlayer { Ok(id) } - Err(err) => Err(err as Box), + Err(err) => Err(anyhow!(err)), } } @@ -472,11 +469,11 @@ impl AudioPlayer { } } - pub async fn set_current_input_device(&mut self, name: &str) -> Result<(), Box> { + pub async fn set_current_input_device(&mut self, name: &str) -> Result<()> { let input_device = get_device(name).await?; if input_device.device_type != DeviceType::Input { - return Err("Selected device is not an input device".into()); + return Err(anyhow!("Selected device is not an input device")); } self.input_device_name = Some(name.to_string()); diff --git a/src/types/config.rs b/src/types/config.rs index 482f5a3..301d692 100644 --- a/src/types/config.rs +++ b/src/types/config.rs @@ -1,6 +1,7 @@ use crate::{types::socket::Request, utils::config::get_config_path}; +use anyhow::Result; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, error::Error, fs, path::PathBuf}; +use std::{collections::HashMap, fs, path::PathBuf}; #[derive(Default, Clone, Serialize, Deserialize)] #[serde(default)] @@ -10,7 +11,7 @@ pub struct DaemonConfig { } impl DaemonConfig { - pub fn save_to_file(&self) -> Result<(), Box> { + pub fn save_to_file(&self) -> Result<()> { let config_path = get_config_path()?.join("daemon.json"); if let Some(config_dir) = config_path.parent() @@ -24,7 +25,7 @@ impl DaemonConfig { Ok(()) } - pub fn load_from_file() -> Result> { + pub fn load_from_file() -> Result { let config_path = get_config_path()?.join("daemon.json"); let bytes = fs::read(config_path)?; match serde_json::from_slice::(&bytes) { @@ -65,7 +66,7 @@ impl Default for GuiConfig { } impl GuiConfig { - pub fn save_to_file(&mut self) -> Result<(), Box> { + pub fn save_to_file(&mut self) -> Result<()> { let config_path = get_config_path()?.join("gui.json"); if let Some(config_dir) = config_path.parent() @@ -84,7 +85,7 @@ impl GuiConfig { Ok(()) } - pub fn load_from_file() -> Result> { + pub fn load_from_file() -> Result { let config_path = get_config_path()?.join("gui.json"); let bytes = fs::read(config_path)?; match serde_json::from_slice::(&bytes) { @@ -108,11 +109,11 @@ pub struct HotkeyConfig { } impl HotkeyConfig { - pub fn config_path() -> Result> { + pub fn config_path() -> Result { Ok(get_config_path()?.join("hotkeys.json")) } - pub fn load() -> Result> { + pub fn load() -> Result { let path = Self::config_path()?; if !path.exists() { return Ok(HotkeyConfig::default()); @@ -124,7 +125,7 @@ impl HotkeyConfig { } } - pub fn save(&self) -> Result<(), Box> { + pub fn save(&self) -> Result<()> { let path = Self::config_path()?; if let Some(dir) = path.parent() && !dir.exists() diff --git a/src/utils/config.rs b/src/utils/config.rs index 127cc7a..c937b27 100644 --- a/src/utils/config.rs +++ b/src/utils/config.rs @@ -1,6 +1,7 @@ -use std::{error::Error, path::PathBuf}; +use anyhow::Result; +use std::path::PathBuf; -pub fn get_config_path() -> Result> { +pub fn get_config_path() -> Result { let config_path = dirs::config_dir().expect("Failed to obtain config dir"); Ok(config_path.join("pwsp")) } diff --git a/src/utils/daemon.rs b/src/utils/daemon.rs index 5a6ce94..864d155 100644 --- a/src/utils/daemon.rs +++ b/src/utils/daemon.rs @@ -4,6 +4,7 @@ use crate::types::{ socket::{MAX_MESSAGE_SIZE, Request, Response}, }; +use anyhow::Result; use std::os::unix::fs::PermissionsExt; use std::path::PathBuf; use std::{error::Error, fs}; @@ -40,7 +41,7 @@ pub fn get_runtime_dir() -> PathBuf { dirs::runtime_dir().unwrap_or(PathBuf::from("/run/pwsp")) } -pub fn create_runtime_dir() -> Result<(), Box> { +pub fn create_runtime_dir() -> Result<()> { let runtime_dir = get_runtime_dir(); if !runtime_dir.exists() { fs::create_dir_all(&runtime_dir)?; @@ -50,7 +51,7 @@ pub fn create_runtime_dir() -> Result<(), Box> { Ok(()) } -pub fn is_daemon_running() -> Result> { +pub fn is_daemon_running() -> Result { let lock_file = fs::File::create(get_runtime_dir().join("daemon.lock"))?; match lock_file.try_lock() { Ok(_) => Ok(false), @@ -58,7 +59,7 @@ pub fn is_daemon_running() -> Result> { } } -pub async fn wait_for_daemon() -> Result<(), Box> { +pub async fn wait_for_daemon() -> Result<()> { if is_daemon_running()? { return Ok(()); } diff --git a/src/utils/gui.rs b/src/utils/gui.rs index c289db5..04fe9e8 100644 --- a/src/utils/gui.rs +++ b/src/utils/gui.rs @@ -7,8 +7,8 @@ use crate::{ }, utils::daemon::{is_daemon_running, make_request}, }; +use anyhow::{Result, anyhow}; use std::{ - error::Error, sync::{Arc, Mutex}, time::Instant, }; @@ -22,11 +22,11 @@ pub fn get_gui_config() -> GuiConfig { }) } -pub fn make_request_sync(request: Request) -> Result> { +pub fn make_request_sync(request: Request) -> Result { tokio::task::block_in_place(|| { tokio::runtime::Handle::current() .block_on(make_request(request)) - .map_err(|e| e as Box) + .map_err(|e| anyhow!(e)) }) } diff --git a/src/utils/pipewire.rs b/src/utils/pipewire.rs index cb15a07..add49f9 100644 --- a/src/utils/pipewire.rs +++ b/src/utils/pipewire.rs @@ -1,9 +1,10 @@ use crate::types::pipewire::{AudioDevice, DeviceType, Port, Terminate}; +use anyhow::{Result, anyhow}; use pipewire::{ context::ContextRc, link::Link, main_loop::MainLoopRc, properties::properties, registry::GlobalObject, spa::utils::dict::DictRef, }; -use std::{collections::HashMap, error::Error, thread}; +use std::{collections::HashMap, thread}; use tokio::{ sync::mpsc, time::{Duration, timeout}, @@ -142,7 +143,7 @@ async fn pw_get_global_objects_thread( main_loop.run(); } -pub async fn get_all_devices() -> Result<(Vec, Vec), Box> { +pub async fn get_all_devices() -> Result<(Vec, Vec)> { // Channels to communicate with pipewire thread let (main_sender, mut main_receiver) = mpsc::channel(10); let (pw_sender, pw_receiver) = pipewire::channel::channel(); @@ -155,7 +156,7 @@ pub async fn get_all_devices() -> Result<(Vec, Vec), B // Wait for initialization to complete if let Err(e) = init_receiver.recv()? { - return Err(e.into()); + return Err(anyhow!(e)); } let mut input_devices: HashMap = HashMap::new(); @@ -238,7 +239,7 @@ pub async fn get_all_devices() -> Result<(Vec, Vec), B } } -pub async fn get_device(device_name: &str) -> Result> { +pub async fn get_device(device_name: &str) -> Result { let (input_devices, output_devices) = get_all_devices().await?; input_devices @@ -250,10 +251,10 @@ pub async fn get_device(device_name: &str) -> Result || device.name.contains(device_name) || device.nick.contains(device_name) }) - .ok_or_else(|| "Device not found".into()) + .ok_or_else(|| anyhow!("Device not found")) } -pub fn create_virtual_mic() -> Result, Box> { +pub fn create_virtual_mic() -> Result> { let (pw_sender, pw_receiver) = pipewire::channel::channel::(); let (init_sender, init_receiver) = std::sync::mpsc::sync_channel(0); @@ -305,45 +306,46 @@ pub fn create_virtual_mic() -> Result, Box< }); if let Err(e) = init_receiver.recv()? { - return Err(e.into()); + return Err(anyhow!(e)); } Ok(pw_sender) } -pub async fn link_player_to_virtual_mic() --> Result, Box> { +pub async fn link_player_to_virtual_mic() -> Result> { let pwsp_daemon_output = match get_device("pwsp-daemon").await { Ok(device) => device, Err(_) => { - return Err( - "Could not find alsa_playback.pwsp-daemon device, skipping device linking".into(), - ); + return Err(anyhow!( + "Could not find alsa_playback.pwsp-daemon device, skipping device linking" + )); } }; let pwsp_daemon_input = match get_device("pwsp-virtual-mic").await { Ok(device) => device, Err(_) => { - return Err("Could not find pwsp-virtual-mic device, skipping device linking".into()); + return Err(anyhow!( + "Could not find pwsp-virtual-mic device, skipping device linking" + )); } }; let output_fl = match pwsp_daemon_output.output_fl { Some(port) => port, - None => return Err("Failed to get pwsp-daemon output_fl".into()), + None => return Err(anyhow!("Failed to get pwsp-daemon output_fl")), }; let output_fr = match pwsp_daemon_output.output_fr { Some(port) => port, - None => return Err("Failed to get pwsp-daemon output_fr".into()), + None => return Err(anyhow!("Failed to get pwsp-daemon output_fr")), }; let input_fl = match pwsp_daemon_input.input_fl { Some(port) => port, - None => return Err("Failed to get pwsp-virtual-mic input_fl".into()), + None => return Err(anyhow!("Failed to get pwsp-virtual-mic input_fl")), }; let input_fr = match pwsp_daemon_input.input_fr { Some(port) => port, - None => return Err("Failed to get pwsp-virtual-mic input_fr".into()), + None => return Err(anyhow!("Failed to get pwsp-virtual-mic input_fr")), }; create_link(output_fl, output_fr, input_fl, input_fr) @@ -354,7 +356,7 @@ pub fn create_link( output_fr: Port, input_fl: Port, input_fr: Port, -) -> Result, Box> { +) -> Result> { let (pw_sender, pw_receiver) = pipewire::channel::channel::(); let (init_sender, init_receiver) = std::sync::mpsc::sync_channel(0); @@ -419,7 +421,7 @@ pub fn create_link( }); if let Err(e) = init_receiver.recv()? { - return Err(e.into()); + return Err(anyhow!(e)); } Ok(pw_sender)