From d347d89dfeef54d648ffdea054897617c0016509 Mon Sep 17 00:00:00 2001 From: salmonik Date: Sat, 6 Jun 2026 19:48:22 +0300 Subject: correctly working auto completion for db --- src/daemon.rs | 113 ++++++++++++++++------------------------------------------ 1 file changed, 31 insertions(+), 82 deletions(-) (limited to 'src/daemon.rs') diff --git a/src/daemon.rs b/src/daemon.rs index d34305a..5ad6654 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -18,14 +18,20 @@ use std::sync::{Arc, Mutex, RwLock}; use std::thread; use std::time::{Duration, Instant, SystemTime}; -struct ScrobbleState { - track: Option, - start_time: Option, - timestamp: Option, +pub struct ScrobbleState { + pub track: Option, + pub start_time: Option, + pub timestamp: Option, +} + +impl Default for ScrobbleState { + fn default() -> Self { + Self::new() + } } impl ScrobbleState { - fn new() -> Self { + pub fn new() -> Self { ScrobbleState { track: None, start_time: None, @@ -33,7 +39,7 @@ impl ScrobbleState { } } - fn start(&mut self, track: Track) { + pub fn start(&mut self, track: Track) { if track.is_stream { crate::debug_eprintln(format_args!( "scrobble: not tracking stream '{}'", @@ -57,7 +63,7 @@ impl ScrobbleState { self.timestamp = Some(ts); } - fn should_scrobble(&self) -> bool { + pub fn should_scrobble(&self) -> bool { let (Some(track), Some(start)) = (&self.track, self.start_time) else { crate::debug_eprintln(format_args!( "scrobble: should_scrobble false — no track or start_time" @@ -78,7 +84,7 @@ impl ScrobbleState { result } - fn should_scrobble_with_params(elapsed: u64, duration_secs: Option) -> bool { + pub fn should_scrobble_with_params(elapsed: u64, duration_secs: Option) -> bool { if elapsed < 30 { return false; } @@ -89,86 +95,13 @@ impl ScrobbleState { elapsed >= threshold } - fn clear(&mut self) { + pub fn clear(&mut self) { self.track = None; self.start_time = None; self.timestamp = None; } } -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_scrobble_state_new() { - let s = ScrobbleState::new(); - assert!(s.track.is_none()); - assert!(s.start_time.is_none()); - assert!(s.timestamp.is_none()); - } - - #[test] - fn test_scrobble_state_clear() { - let mut s = ScrobbleState::new(); - let t = Track { - path: PathBuf::from("/test/song.mp3"), - title: Some("Test".into()), - artist: Some("Artist".into()), - album: None, - duration_secs: Some(200.0), - is_stream: false, - }; - s.start(t); - assert!(s.track.is_some()); - s.clear(); - assert!(s.track.is_none()); - assert!(s.start_time.is_none()); - assert!(s.timestamp.is_none()); - } - - #[test] - fn test_should_scrobble_without_track_returns_false() { - assert!(!ScrobbleState::new().should_scrobble()); - } - - #[test] - fn test_should_scrobble_threshold_below_30_seconds() { - assert!(!ScrobbleState::should_scrobble_with_params(29, Some(200.0))); - } - - #[test] - fn test_should_scrobble_threshold_half_duration() { - assert!(ScrobbleState::should_scrobble_with_params(100, Some(200.0))); - } - - #[test] - fn test_should_scrobble_threshold_below_half_duration() { - assert!(!ScrobbleState::should_scrobble_with_params(80, Some(200.0))); - } - - #[test] - fn test_should_scrobble_threshold_capped_at_4min() { - assert!(ScrobbleState::should_scrobble_with_params(240, Some(600.0))); - assert!(!ScrobbleState::should_scrobble_with_params( - 239, - Some(600.0) - )); - } - - #[test] - fn test_should_scrobble_no_duration_requires_4min() { - assert!(ScrobbleState::should_scrobble_with_params(240, None)); - assert!(!ScrobbleState::should_scrobble_with_params(239, None)); - } - - #[test] - fn test_should_scrobble_short_song() { - assert!(ScrobbleState::should_scrobble_with_params(30, Some(60.0))); - assert!(!ScrobbleState::should_scrobble_with_params(15, Some(60.0))); - } -} - fn try_scrobble_current(scrobbler: &RwLock, state: &mut ScrobbleState) { if let Some(ref track) = state.track && track.is_stream @@ -875,6 +808,22 @@ fn process_command( // Strip trailing slash so autocompleted Artist/Album/ resolves cleanly let path_str = path_str.trim_end_matches('/').to_string(); + // Unescape backslash sequences (e.g., \! → !) so shell escaping works + let path_str = { + let mut unescaped = String::with_capacity(path_str.len()); + let mut chars = path_str.chars(); + while let Some(c) = chars.next() { + if c == '\\' { + if let Some(next) = chars.next() { + unescaped.push(next); + } + } else { + unescaped.push(c); + } + } + unescaped + }; + if path_str.is_empty() { return "Usage: play [now|next|last] \n".to_string(); } -- cgit v1.2.3