Skip to content

Commit 0a12f44

Browse files
committed
refactor: eliminate panicky .unwrap() calls, add crate docs, migrate eprintln! to tracing
High priority - Replace 11 .unwrap() calls in main.rs artwork HTTP handler with a response-build helper closure (.expect instead of panic) - Replace 9 .unwrap() calls in header.rs sort-state icon_class with if-let pattern - Replace .unwrap() on Mutex/RwLock locks across discord-presence, i18n, logging, android/macos systemint, and scrobble crates with unwrap_or_else(|e| e.into_inner()) or if-let-Ok - Replace .unwrap() on SystemTime::duration_since with unwrap_or_default in scrobble lastfm/librefm/musicbrainz - Safer radio registry URL fallback (filter + unwrap_or) Medium priority - Add //! crate-level doc comments to all 13 workspace lib.rs members - Replace eprintln! with tracing::debug! (crates/utils/src/lyrics.rs, crates/server/src/ytmusic/player.rs) Low priority - Remove unused async-recursion = "1.0" workspace dependency
1 parent ca55400 commit 0a12f44

25 files changed

Lines changed: 154 additions & 100 deletions

File tree

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ wasm-bindgen = "0.2"
3535
serde = { version = "1.0", features = ["derive"] }
3636
serde_json = "1.0"
3737
tokio = { version = "1.0", features = ["rt", "time", "sync", "macros", "io-util"] }
38-
async-recursion = "1.0"
3938
discord-rich-presence = "1.1.0"
4039
lofty = "0.24.0"
4140
image = { version = "0.25", default-features = false, features = ["jpeg", "png", "webp"] }

crates/components/src/header.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ fn icon_class(
77
sort_state: &Option<Signal<Option<(SortField, showcase::SortDirection)>>>,
88
field: SortField,
99
) -> String {
10-
if sort_state.is_some() {
11-
showcase::sort_icon(*sort_state.unwrap().read(), field).to_string()
10+
if let Some(s) = sort_state {
11+
showcase::sort_icon(*s.read(), field).to_string()
1212
} else {
13-
"".to_string()
13+
String::new()
1414
}
1515
}
1616

crates/components/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
//! Reusable Dioxus UI components for the Kopuz music player: modern/normal list views,
2+
//! navigation controller, titlebar, sidebar, bottombar, and shared UI primitives.
3+
14
pub mod modern;
25
pub mod navigation_controller;
36
pub mod normal;

crates/config/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
//! Configuration management for Kopuz: loads, saves, and migrates user settings
2+
//! (audio, theme, media servers, shortcuts) from a JSON config file.
3+
14
use serde::{Deserialize, Deserializer, Serialize};
25
use std::collections::HashMap;
36
use std::fs;

crates/discord-presence/src/lib.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
//! Discord Rich Presence integration for Kopuz: publishes now-playing state
2+
//! (track, artist, album art) to the Discord client via RPC.
3+
14
pub mod cover_art;
25

36
#[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))]
@@ -27,7 +30,7 @@ impl Presence {
2730
}
2831

2932
pub fn disconnect(&self) -> Result<(), Box<dyn std::error::Error>> {
30-
self.client.lock().unwrap().close()?;
33+
self.client.lock().unwrap_or_else(|e| e.into_inner()).close()?;
3134
Ok(())
3235
}
3336

@@ -65,7 +68,7 @@ impl Presence {
6568
activity = activity.assets(assets);
6669
}
6770

68-
self.client.lock().unwrap().set_activity(activity)?;
71+
self.client.lock().unwrap_or_else(|e| e.into_inner()).set_activity(activity)?;
6972
Ok(())
7073
}
7174

@@ -88,12 +91,12 @@ impl Presence {
8891
activity = activity.assets(assets);
8992
}
9093

91-
self.client.lock().unwrap().set_activity(activity)?;
94+
self.client.lock().unwrap_or_else(|e| e.into_inner()).set_activity(activity)?;
9295
Ok(())
9396
}
9497

9598
pub fn clear_activity(&self) -> Result<(), Box<dyn std::error::Error>> {
96-
self.client.lock().unwrap().clear_activity()?;
99+
self.client.lock().unwrap_or_else(|e| e.into_inner()).clear_activity()?;
97100
Ok(())
98101
}
99102
}

crates/hooks/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
//! Dioxus hooks for Kopuz: player controller, library item management,
2+
//! search data, and async player task orchestration.
3+
14
pub mod use_library_items;
25
pub mod use_player_controller;
36
pub mod use_player_task;

crates/i18n/src/lib.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
//! Internationalization engine for Kopuz: loads Fluent (.ftl) locale files,
2+
//! provides a global `t()` macro for translatable UI strings.
3+
14
use fluent_bundle::concurrent::FluentBundle;
25
use fluent_bundle::{FluentArgs, FluentResource, FluentValue};
36
use std::borrow::Cow;
@@ -79,11 +82,13 @@ pub fn init(lang: &str) {
7982
}
8083

8184
pub fn set_locale(lang: &str) {
82-
*state().write().unwrap() = I18nState::new(lang);
85+
if let Ok(mut w) = state().write() {
86+
*w = I18nState::new(lang);
87+
}
8388
}
8489

8590
pub fn t(key: &str) -> String {
86-
state().read().unwrap().translate(key, None)
91+
state().read().unwrap_or_else(|e| e.into_inner()).translate(key, None)
8792
}
8893

8994
pub fn t_with(key: &str, args: &[(&str, String)]) -> String {
@@ -94,7 +99,7 @@ pub fn t_with(key: &str, args: &[(&str, String)]) -> String {
9499
FluentValue::String(Cow::Owned(v.clone())),
95100
);
96101
}
97-
state().read().unwrap().translate(key, Some(&fluent_args))
102+
state().read().unwrap_or_else(|e| e.into_inner()).translate(key, Some(&fluent_args))
98103
}
99104

100105
pub fn available_languages() -> &'static [(&'static str, &'static str)] {

crates/kopuz/src/logging.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ pub fn init(log_dir: &Path, config_tracing_enabled: bool) {
150150
};
151151

152152
let trace_enabled = chrome_guard.is_some();
153-
*GUARDS.lock().unwrap() = Some(LogGuards {
153+
*GUARDS.lock().unwrap_or_else(|e| e.into_inner()) = Some(LogGuards {
154154
_file: file_guard,
155155
_chrome: chrome_guard,
156156
});

crates/kopuz/src/main.rs

Lines changed: 79 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,22 @@ fn main() {
563563
.unwrap_or_else(|| std::path::PathBuf::from("./cache/webview"));
564564
let _ = std::fs::create_dir_all(&webview_data_dir);
565565

566+
fn resp(status: u16, headers: &[(&str, &str)], body: Vec<u8>) -> http::Response<std::borrow::Cow<'static, [u8]>> {
567+
let mut b = http::Response::builder().status(status);
568+
b = b.header("Access-Control-Allow-Origin", "*");
569+
for (k, v) in headers {
570+
b = b.header(*k, *v);
571+
}
572+
b.body(std::borrow::Cow::from(body))
573+
.unwrap_or_else(|_| {
574+
http::Response::builder()
575+
.status(500)
576+
.header("Access-Control-Allow-Origin", "*")
577+
.body(std::borrow::Cow::from(Vec::new()))
578+
.expect("static fallback response")
579+
})
580+
}
581+
566582
let config = dioxus::desktop::Config::new()
567583
.with_custom_head(
568584
"<style>html,body{background:#000;margin:0;padding:0}body{opacity:0}</style>"
@@ -597,12 +613,11 @@ fn main() {
597613
let high_quality = query.split('&').any(|kv| kv == "hq=1");
598614

599615
if file_path.is_empty() {
600-
responder.respond(
601-
http::Response::builder()
602-
.status(400)
603-
.body(std::borrow::Cow::from(Vec::new()))
604-
.unwrap(),
605-
);
616+
responder.respond(resp(
617+
400,
618+
&[],
619+
Vec::new(),
620+
));
606621
return;
607622
}
608623

@@ -625,16 +640,13 @@ fn main() {
625640
if hq_path.exists()
626641
&& let Ok(b) = tokio::fs::read(&hq_path).await
627642
{
628-
responder.respond(
629-
http::Response::builder()
630-
.header("Content-Type", "image/jpeg")
631-
.header("Access-Control-Allow-Origin", "*")
632-
.header("Cache-Control", "public, max-age=31536000")
633-
.body(std::borrow::Cow::from(b))
634-
.unwrap(),
635-
);
636-
return;
637-
}
643+
responder.respond(resp(
644+
200,
645+
&[("Content-Type", "image/jpeg"), ("Cache-Control", "public, max-age=31536000")],
646+
b,
647+
));
648+
return;
649+
}
638650
match tokio::fs::read(&file_path).await {
639651
Ok(raw) => {
640652
let file_path_clone = file_path.clone();
@@ -653,31 +665,23 @@ fn main() {
653665
})
654666
.await;
655667
match result {
656-
Ok((bytes, mime)) => responder.respond(
657-
http::Response::builder()
658-
.header("Content-Type", mime)
659-
.header("Access-Control-Allow-Origin", "*")
660-
.header(
661-
"Cache-Control",
662-
"public, max-age=31536000",
663-
)
664-
.body(std::borrow::Cow::from(bytes))
665-
.unwrap(),
666-
),
667-
Err(_) => responder.respond(
668-
http::Response::builder()
669-
.status(500)
670-
.body(std::borrow::Cow::from(Vec::new()))
671-
.unwrap(),
672-
),
668+
Ok((bytes, mime)) => responder.respond(resp(
669+
200,
670+
&[("Content-Type", mime), ("Cache-Control", "public, max-age=31536000")],
671+
bytes,
672+
)),
673+
Err(_) => responder.respond(resp(
674+
500,
675+
&[],
676+
Vec::new(),
677+
)),
673678
}
674679
}
675-
Err(_) => responder.respond(
676-
http::Response::builder()
677-
.status(404)
678-
.body(std::borrow::Cow::from(Vec::new()))
679-
.unwrap(),
680-
),
680+
Err(_) => responder.respond(resp(
681+
404,
682+
&[],
683+
Vec::new(),
684+
)),
681685
}
682686
return;
683687
}
@@ -699,12 +703,11 @@ fn main() {
699703
},
700704
),
701705
Err(_) => {
702-
responder.respond(
703-
http::Response::builder()
704-
.status(404)
705-
.body(std::borrow::Cow::from(Vec::new()))
706-
.unwrap(),
707-
);
706+
responder.respond(resp(
707+
404,
708+
&[],
709+
Vec::new(),
710+
));
708711
return;
709712
}
710713
}
@@ -732,37 +735,32 @@ fn main() {
732735
},
733736
),
734737
Err(_) => {
735-
responder.respond(
736-
http::Response::builder()
737-
.status(500)
738-
.body(std::borrow::Cow::from(Vec::new()))
739-
.unwrap(),
740-
);
738+
responder.respond(resp(
739+
500,
740+
&[],
741+
Vec::new(),
742+
));
741743
return;
742744
}
743745
}
744746
}
745747
Err(e) => {
746748
tracing::warn!("[artwork] not found {}: {}", file_path, e);
747-
responder.respond(
748-
http::Response::builder()
749-
.status(404)
750-
.body(std::borrow::Cow::from(Vec::new()))
751-
.unwrap(),
752-
);
749+
responder.respond(resp(
750+
404,
751+
&[],
752+
Vec::new(),
753+
));
753754
return;
754755
}
755756
}
756757
};
757758

758-
responder.respond(
759-
http::Response::builder()
760-
.header("Content-Type", mime)
761-
.header("Access-Control-Allow-Origin", "*")
762-
.header("Cache-Control", "public, max-age=31536000")
763-
.body(std::borrow::Cow::from(bytes))
764-
.unwrap(),
765-
);
759+
responder.respond(resp(
760+
200,
761+
&[("Content-Type", mime), ("Cache-Control", "public, max-age=31536000")],
762+
bytes,
763+
));
766764
}
767765
.instrument(tracing::info_span!("artwork.serve")),
768766
);
@@ -829,23 +827,33 @@ fn main() {
829827
}
830828
});
831829

830+
fn err_resp(status: u16) -> http::Response<std::borrow::Cow<'static, [u8]>> {
831+
http::Response::builder()
832+
.status(status)
833+
.header("Access-Control-Allow-Origin", "*")
834+
.body(std::borrow::Cow::from(Vec::new()))
835+
.unwrap_or_else(|_| {
836+
http::Response::builder()
837+
.status(500)
838+
.header("Access-Control-Allow-Origin", "*")
839+
.body(std::borrow::Cow::from(Vec::new()))
840+
.expect("static fallback response")
841+
})
842+
}
843+
832844
match read_result {
833845
Ok(bytes) => http::Response::builder()
834846
.header("Content-Type", mime)
835847
.header("Access-Control-Allow-Origin", "*")
836848
.body(std::borrow::Cow::from(bytes))
837-
.unwrap(),
849+
.unwrap_or_else(|_| err_resp(500)),
838850
Err(e) => {
839851
let status = if e.kind() == std::io::ErrorKind::NotFound {
840852
404
841853
} else {
842854
500
843855
};
844-
http::Response::builder()
845-
.status(status)
846-
.header("Access-Control-Allow-Origin", "*")
847-
.body(std::borrow::Cow::from(Vec::new()))
848-
.unwrap()
856+
err_resp(status)
849857
}
850858
}
851859
});

crates/kopuz_route/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
//! Route definitions for the Kopuz Dioxus application: enum of all navigable
2+
//! screens (Home, Discover, Album, Artist, Playlist, Settings, etc.).
3+
14
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
25
pub enum Route {
36
Home,

0 commit comments

Comments
 (0)