From 7718ac3ee5eb3f238ec31fdec3c6770464e3596a Mon Sep 17 00:00:00 2001 From: Psychotoxical <171614930+Psychotoxical@users.noreply.github.com> Date: Sat, 9 May 2026 13:06:28 +0200 Subject: [PATCH] refactor: introduce cargo workspace + psysonic-core crate (M1/7) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First milestone of the workspace crate split. Sets up the workspace skeleton and extracts shared logging + cross-crate port traits into a new psysonic-core crate. src-tauri/Cargo.toml now defines [workspace]; top package becomes the workspace root. crates/psysonic-core/ new lib crate, no Tauri-handler code: src/logging.rs full logging facade (was src/logging.rs) src/ports.rs PlaybackQuery + AnalysisOrchestrator trait declarations (no impls yet) The top crate re-exports `psysonic_core::logging` and the `app_eprintln!` / `app_deprintln!` macros so every existing `crate::logging::*` and `crate::app_eprintln!` callsite keeps working unchanged. Port traits intentionally take `tauri::AppHandle` — psysonic-core is a workspace-internal crate, so depending on Tauri here is a feature, not a leak. Implementers will register themselves as `Arc` / `Arc` Tauri State in M2/M3. Behaviour preserving. Cargo check + clippy --workspace clean. --- src-tauri/Cargo.lock | 10 ++++ src-tauri/Cargo.toml | 16 ++++-- src-tauri/crates/psysonic-core/Cargo.toml | 13 +++++ src-tauri/crates/psysonic-core/src/lib.rs | 8 +++ .../{ => crates/psysonic-core}/src/logging.rs | 9 +++- src-tauri/crates/psysonic-core/src/ports.rs | 49 +++++++++++++++++++ src-tauri/src/lib.rs | 4 +- 7 files changed, 104 insertions(+), 5 deletions(-) create mode 100644 src-tauri/crates/psysonic-core/Cargo.toml create mode 100644 src-tauri/crates/psysonic-core/src/lib.rs rename src-tauri/{ => crates/psysonic-core}/src/logging.rs (94%) create mode 100644 src-tauri/crates/psysonic-core/src/ports.rs diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 907f30ad..db4d0fe8 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -3608,6 +3608,7 @@ dependencies = [ "libc", "lofty", "md5", + "psysonic-core", "reqwest", "ringbuf", "rodio", @@ -3637,6 +3638,15 @@ dependencies = [ "zbus 5.15.0", ] +[[package]] +name = "psysonic-core" +version = "1.46.0-dev" +dependencies = [ + "libc", + "serde", + "tauri", +] + [[package]] name = "pxfm" version = "0.1.29" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 951b351f..9fa0fbee 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,13 +1,22 @@ +[workspace] +members = ["crates/*"] +resolver = "2" + +[workspace.package] +version = "1.46.0-dev" +edition = "2021" +rust-version = "1.89" + [package] name = "psysonic" -version = "1.46.0-dev" +version.workspace = true description = "Psysonic Desktop Music Player" authors = [] license = "" repository = "" default-run = "psysonic" -edition = "2021" -rust-version = "1.89" +edition.workspace = true +rust-version.workspace = true [lib] name = "psysonic_lib" @@ -21,6 +30,7 @@ path = "src/main.rs" tauri-build = { version = "2", features = [] } [dependencies] +psysonic-core = { path = "crates/psysonic-core" } tauri = { version = "2", features = ["tray-icon", "image-png"] } tauri-plugin-single-instance = "2" tauri-plugin-shell = "2" diff --git a/src-tauri/crates/psysonic-core/Cargo.toml b/src-tauri/crates/psysonic-core/Cargo.toml new file mode 100644 index 00000000..201f7c64 --- /dev/null +++ b/src-tauri/crates/psysonic-core/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "psysonic-core" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +publish = false + +[dependencies] +tauri = { version = "2" } +serde = { version = "1", features = ["derive"] } + +[target.'cfg(unix)'.dependencies] +libc = "0.2" diff --git a/src-tauri/crates/psysonic-core/src/lib.rs b/src-tauri/crates/psysonic-core/src/lib.rs new file mode 100644 index 00000000..3f47b202 --- /dev/null +++ b/src-tauri/crates/psysonic-core/src/lib.rs @@ -0,0 +1,8 @@ +//! `psysonic-core` — workspace-internal shared primitives. +//! +//! Hosts the runtime logging facade (with `app_eprintln!` / `app_deprintln!` +//! macros) and the cross-crate port traits used to break dependency cycles +//! between `psysonic-audio`, `psysonic-analysis`, and other domain crates. + +pub mod logging; +pub mod ports; diff --git a/src-tauri/src/logging.rs b/src-tauri/crates/psysonic-core/src/logging.rs similarity index 94% rename from src-tauri/src/logging.rs rename to src-tauri/crates/psysonic-core/src/logging.rs index 78fbb040..db73585e 100644 --- a/src-tauri/src/logging.rs +++ b/src-tauri/crates/psysonic-core/src/logging.rs @@ -1,3 +1,10 @@ +//! Runtime logging facade. +//! +//! Provides level-gated `eprintln!` macros (`app_eprintln!` / `app_deprintln!`) +//! that also append to a bounded in-memory ring buffer and a CLI-readable +//! per-runtime log file. Live mode toggling at runtime via +//! `set_logging_mode_from_str("off"|"normal"|"debug")`. + #[cfg(unix)] use libc; use std::collections::VecDeque; @@ -98,7 +105,7 @@ pub fn export_logs_to_file(path: &str) -> Result { Ok(lines) } -pub(crate) fn log_timestamp_local() -> String { +pub fn log_timestamp_local() -> String { let now = ::std::time::SystemTime::now() .duration_since(::std::time::UNIX_EPOCH) .unwrap_or_default(); diff --git a/src-tauri/crates/psysonic-core/src/ports.rs b/src-tauri/crates/psysonic-core/src/ports.rs new file mode 100644 index 00000000..424f1da2 --- /dev/null +++ b/src-tauri/crates/psysonic-core/src/ports.rs @@ -0,0 +1,49 @@ +//! Cross-crate port traits. +//! +//! These traits exist purely to break dependency cycles that would otherwise +//! force `psysonic-audio` ↔ `psysonic-analysis` to depend on each other in +//! both directions. Implementers register themselves as Tauri State on app +//! bootstrap; consumers look them up via `app.try_state::>()`. +//! +//! Layout: +//! - `AnalysisOrchestrator` — implemented in `psysonic-analysis`, called from +//! `psysonic-audio` to enqueue full-buffer loudness/waveform analysis. +//! - `PlaybackQuery` — implemented in `psysonic-audio` on `AudioEngine`, +//! called from `psysonic-analysis` to ask "is this track currently playing?". + +use std::future::Future; +use std::pin::Pin; + +use tauri::AppHandle; + +/// Read-only queries about the live playback session. Implemented on +/// `AudioEngine` in `psysonic-audio`. +/// +/// Registered as `Arc` in Tauri State so non-audio crates +/// can query without taking a hard dep on the audio crate. +pub trait PlaybackQuery: Send + Sync + 'static { + /// `true` if `track_id` is the track currently being decoded/played. + fn is_track_currently_playing(&self, track_id: &str) -> bool; +} + +/// Triggers full-buffer analysis on already-captured track bytes. Implemented +/// on `AnalysisRuntime` in `psysonic-analysis`. +/// +/// Registered as `Arc` in Tauri State so audio +/// callsites (`stream::ranged_http`, `stream::track_stream`, `helpers`, +/// `play_input`, `preload_commands`) can submit without depending on +/// `psysonic-analysis`. +pub trait AnalysisOrchestrator: Send + Sync + 'static { + /// Enqueue a CPU-seed analysis pass for `track_id` over the given byte + /// buffer. `high_priority` mirrors the HTTP-backfill head-insertion + /// behaviour for the currently playing track. + /// + /// Errors are stringified — audio callers only care about pass/fail. + fn submit_cpu_seed<'a>( + &'a self, + app: AppHandle, + track_id: String, + bytes: Vec, + high_priority: bool, + ) -> Pin> + Send + 'a>>; +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index bdac1624..3ba44831 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -6,8 +6,10 @@ mod analysis_cache; mod analysis_runtime; pub mod cli; mod discord; -pub(crate) mod logging; mod lib_commands; + +pub use psysonic_core::logging; +pub use psysonic_core::{app_eprintln, app_deprintln}; #[cfg(target_os = "windows")] mod taskbar_win; mod tray_runtime;