From 82c82ae46a5f722f1b23923e1af1558b9e69b3aa Mon Sep 17 00:00:00 2001 From: Connor Johnstone Date: Wed, 18 Mar 2026 15:37:30 -0400 Subject: [PATCH] Formatting and CI --- .gitea/workflows/ci.yml | 21 ++++++++++++ Cargo.lock | 2 ++ justfile | 26 ++++++++++++++ rust-toolchain.toml | 2 ++ shanty-config/Cargo.toml | 4 +++ shanty-config/src/lib.rs | 48 ++++++++++++++++++-------- shanty-config/tests/integration.rs | 55 ++++++++++++++++++++++++++++++ shanty-db | 2 +- shanty-dl | 2 +- shanty-index | 2 +- shanty-org | 2 +- shanty-search | 2 +- shanty-tag | 2 +- shanty-watch | 2 +- shanty-web | 2 +- src/main.rs | 7 ++-- 16 files changed, 154 insertions(+), 27 deletions(-) create mode 100644 .gitea/workflows/ci.yml create mode 100644 justfile create mode 100644 rust-toolchain.toml create mode 100644 shanty-config/tests/integration.rs diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..5f7b86b --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,21 @@ +name: CI + +on: [push, pull_request] + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + + - uses: dtolnay/rust-toolchain@stable + + - run: cargo fmt --check + + - run: cargo clippy --workspace -- -D warnings + + - run: cargo build --workspace + + - run: cargo test --workspace diff --git a/Cargo.lock b/Cargo.lock index 1009e1f..1b7d67b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2934,7 +2934,9 @@ dependencies = [ "dirs", "serde", "serde_yaml", + "tempfile", "tracing", + "tracing-subscriber", ] [[package]] diff --git a/justfile b/justfile new file mode 100644 index 0000000..b3e9705 --- /dev/null +++ b/justfile @@ -0,0 +1,26 @@ +default: + @just --list + +build: + cargo build --workspace + +test: + cargo test --workspace + +lint: + cargo clippy --workspace -- -D warnings + +fmt: + cargo fmt + +check: fmt lint test + +run: + cargo run --bin shanty + +frontend: + cd shanty-web/frontend && trunk build + +dev: frontend run + +ci: check build diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..292fe49 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "stable" diff --git a/shanty-config/Cargo.toml b/shanty-config/Cargo.toml index f0519e3..bd69d57 100644 --- a/shanty-config/Cargo.toml +++ b/shanty-config/Cargo.toml @@ -9,3 +9,7 @@ serde = { workspace = true } serde_yaml = "0.9" dirs = "6" tracing = { workspace = true } + +[dev-dependencies] +tempfile = "3" +tracing-subscriber = { workspace = true } diff --git a/shanty-config/src/lib.rs b/shanty-config/src/lib.rs index e17c93b..5e9c1ea 100644 --- a/shanty-config/src/lib.rs +++ b/shanty-config/src/lib.rs @@ -164,15 +164,33 @@ fn default_organization_format() -> String { "{artist}/{album}/{track_number} - {title}.{ext}".to_string() } -fn default_port() -> u16 { 8085 } -fn default_bind() -> String { "0.0.0.0".to_string() } -fn default_confidence() -> f64 { 0.8 } -fn default_format() -> String { "opus".to_string() } -fn default_search_source() -> String { "ytmusic".to_string() } -fn default_true() -> bool { true } -fn default_rate_limit() -> u32 { 450 } -fn default_rate_limit_auth() -> u32 { 1800 } -fn default_concurrency() -> usize { 4 } +fn default_port() -> u16 { + 8085 +} +fn default_bind() -> String { + "0.0.0.0".to_string() +} +fn default_confidence() -> f64 { + 0.8 +} +fn default_format() -> String { + "opus".to_string() +} +fn default_search_source() -> String { + "ytmusic".to_string() +} +fn default_true() -> bool { + true +} +fn default_rate_limit() -> u32 { + 450 +} +fn default_rate_limit_auth() -> u32 { + 1800 +} +fn default_concurrency() -> usize { + 4 +} // --- Loading and Saving --- @@ -223,8 +241,8 @@ impl AppConfig { std::fs::create_dir_all(parent) .map_err(|e| format!("failed to create config directory: {e}"))?; } - let yaml = serde_yaml::to_string(self) - .map_err(|e| format!("failed to serialize config: {e}"))?; + let yaml = + serde_yaml::to_string(self).map_err(|e| format!("failed to serialize config: {e}"))?; std::fs::write(&config_path, yaml) .map_err(|e| format!("failed to write config to {}: {e}", config_path.display()))?; tracing::info!(path = %config_path.display(), "config saved"); @@ -241,10 +259,10 @@ impl AppConfig { if let Ok(v) = std::env::var("SHANTY_DOWNLOAD_PATH") { config.download_path = PathBuf::from(v); } - if let Ok(v) = std::env::var("SHANTY_WEB_PORT") { - if let Ok(port) = v.parse() { - config.web.port = port; - } + if let Ok(v) = std::env::var("SHANTY_WEB_PORT") + && let Ok(port) = v.parse() + { + config.web.port = port; } if let Ok(v) = std::env::var("SHANTY_WEB_BIND") { config.web.bind = v; diff --git a/shanty-config/tests/integration.rs b/shanty-config/tests/integration.rs new file mode 100644 index 0000000..e294a59 --- /dev/null +++ b/shanty-config/tests/integration.rs @@ -0,0 +1,55 @@ +use shanty_config::AppConfig; + +#[test] +fn test_default_config() { + let config = AppConfig::default(); + assert_eq!(config.web.port, 8085); + assert_eq!(config.web.bind, "0.0.0.0"); + assert_eq!(config.tagging.confidence, 0.8); + assert!(config.tagging.write_tags); + assert!(!config.tagging.auto_tag); + assert_eq!(config.download.format, "opus"); + assert_eq!(config.download.search_source, "ytmusic"); + assert_eq!(config.download.rate_limit, 450); + assert_eq!(config.download.rate_limit_auth, 1800); + assert_eq!(config.indexing.concurrency, 4); + assert!(config.allowed_secondary_types.is_empty()); +} + +#[test] +fn test_load_missing_file() { + // Loading from a nonexistent path should return defaults + let config = AppConfig::load(Some("/tmp/shanty-test-nonexistent-config.yaml")); + assert_eq!(config.web.port, 8085); +} + +#[test] +fn test_save_and_load_roundtrip() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("config.yaml"); + let path_str = path.to_string_lossy().to_string(); + + let mut config = AppConfig::default(); + config.web.port = 9999; + config.tagging.confidence = 0.95; + config.download.format = "flac".to_string(); + config.indexing.concurrency = 8; + + config.save(Some(&path_str)).unwrap(); + + let loaded = AppConfig::load(Some(&path_str)); + assert_eq!(loaded.web.port, 9999); + assert_eq!(loaded.tagging.confidence, 0.95); + assert_eq!(loaded.download.format, "flac"); + assert_eq!(loaded.indexing.concurrency, 8); +} + +#[test] +fn test_config_path_resolution() { + let path = AppConfig::config_path(Some("/custom/path.yaml")); + assert_eq!(path.to_string_lossy(), "/custom/path.yaml"); + + // Default path should end with config.yaml + let default = AppConfig::config_path(None); + assert!(default.to_string_lossy().ends_with("config.yaml")); +} diff --git a/shanty-db b/shanty-db index 37410ce..c645260 160000 --- a/shanty-db +++ b/shanty-db @@ -1 +1 @@ -Subproject commit 37410ce216f25fbc4ae0f12dd8caabd48f0b4497 +Subproject commit c6452609d6865835567c80d8b92daf265081069b diff --git a/shanty-dl b/shanty-dl index a57df38..2592651 160000 --- a/shanty-dl +++ b/shanty-dl @@ -1 +1 @@ -Subproject commit a57df38eb17c041e5a390c67999a97c42bf0cefc +Subproject commit 2592651c9a26739a8290d778646ccbfde84113c4 diff --git a/shanty-index b/shanty-index index 1d1674e..3494de1 160000 --- a/shanty-index +++ b/shanty-index @@ -1 +1 @@ -Subproject commit 1d1674e3a135cfc475f74fb57094d02ce061e3b9 +Subproject commit 3494de113366b1b54e6d74c45ab17b52668e647a diff --git a/shanty-org b/shanty-org index 3159ee5..a2152cb 160000 --- a/shanty-org +++ b/shanty-org @@ -1 +1 @@ -Subproject commit 3159ee51adca363e4f2a38a6c4a4052839fe21bc +Subproject commit a2152cbf8d95aa0f5d8e2c5d1d5da4da9abf9d61 diff --git a/shanty-search b/shanty-search index d09557d..d358b79 160000 --- a/shanty-search +++ b/shanty-search @@ -1 +1 @@ -Subproject commit d09557d953ee918fb83d952149f77a564b5631f3 +Subproject commit d358b79a6bd86dd722d879731b0748e66f49912c diff --git a/shanty-tag b/shanty-tag index 4400cbc..5957d69 160000 --- a/shanty-tag +++ b/shanty-tag @@ -1 +1 @@ -Subproject commit 4400cbc1cb006117a5aacf45c4094396d394770c +Subproject commit 5957d69e7dc2ec5d6c35b335544d28d87a0a8831 diff --git a/shanty-watch b/shanty-watch index 3d01fa8..0b33678 160000 --- a/shanty-watch +++ b/shanty-watch @@ -1 +1 @@ -Subproject commit 3d01fa85c9575683204a9a875d9ff7bb7711c17c +Subproject commit 0b336789da347901dd949806d2b3c7cf91a07042 diff --git a/shanty-web b/shanty-web index 1f36374..93392db 160000 --- a/shanty-web +++ b/shanty-web @@ -1 +1 @@ -Subproject commit 1f3637439486bfae8e0a794a5e7eb818926f1e8e +Subproject commit 93392db27c81fb4c7488fcb755a05baa317af279 diff --git a/src/main.rs b/src/main.rs index 01ca946..27e4eeb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ use actix_cors::Cors; -use actix_web::{web, App, HttpServer}; +use actix_web::{App, HttpServer, web}; use clap::Parser; use tracing_actix_web::TracingLogger; use tracing_subscriber::EnvFilter; @@ -79,9 +79,8 @@ async fn main() -> anyhow::Result<()> { static_dir } else { // Check next to shanty-web crate root (for development) - let dev_path = std::path::PathBuf::from( - concat!(env!("CARGO_MANIFEST_DIR"), "/shanty-web/static"), - ); + let dev_path = + std::path::PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/shanty-web/static")); if dev_path.is_dir() { dev_path } else {