Formatting

This commit is contained in:
Connor Johnstone
2026-03-18 15:36:42 -04:00
parent 3159ee51ad
commit a2152cbf8d
6 changed files with 55 additions and 34 deletions

View File

@@ -7,7 +7,10 @@ use shanty_db::Database;
use shanty_org::{DEFAULT_FORMAT, OrgConfig, organize_from_db, organize_from_directory};
#[derive(Parser)]
#[command(name = "shanty-org", about = "Organize music files into a clean directory structure")]
#[command(
name = "shanty-org",
about = "Organize music files into a clean directory structure"
)]
struct Cli {
/// Source directory of music files (standalone mode, reads tags from files).
#[arg(long)]

View File

@@ -51,9 +51,7 @@ impl TrackMetadata {
.unwrap_or("")
.to_string();
let tagged_file = Probe::open(path)?
.options(ParseOptions::default())
.read()?;
let tagged_file = Probe::open(path)?.options(ParseOptions::default()).read()?;
let tag = tagged_file
.primary_tag()

View File

@@ -56,10 +56,7 @@ fn resolve_target(target: &Path) -> PathBuf {
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("file");
let ext = target
.extension()
.and_then(|s| s.to_str())
.unwrap_or("");
let ext = target.extension().and_then(|s| s.to_str()).unwrap_or("");
let parent = target.parent().unwrap_or(Path::new("."));
for i in 2..=999 {
@@ -235,10 +232,7 @@ pub async fn organize_from_db(
}
/// Organize music files from a source directory (standalone, no database).
pub async fn organize_from_directory(
source_dir: &Path,
config: &OrgConfig,
) -> OrgResult<OrgStats> {
pub async fn organize_from_directory(source_dir: &Path, config: &OrgConfig) -> OrgResult<OrgStats> {
let mut stats = OrgStats::default();
let source_root = source_dir
.canonicalize()
@@ -344,12 +338,11 @@ fn cleanup_empty_dirs_recursive(root: &Path) {
if dir == root {
continue;
}
if let Ok(mut entries) = std::fs::read_dir(&dir) {
if entries.next().is_none() {
if std::fs::remove_dir(&dir).is_ok() {
tracing::debug!(path = %dir.display(), "removed empty directory");
}
}
if let Ok(mut entries) = std::fs::read_dir(&dir)
&& entries.next().is_none()
&& std::fs::remove_dir(&dir).is_ok()
{
tracing::debug!(path = %dir.display(), "removed empty directory");
}
}
}

View File

@@ -9,7 +9,9 @@ pub fn sanitize_component(s: &str) -> String {
.collect();
// Trim leading/trailing dots and spaces (problematic on Windows and some Linux tools)
result = result.trim_matches(|c: char| c == '.' || c == ' ').to_string();
result = result
.trim_matches(|c: char| c == '.' || c == ' ')
.to_string();
// Collapse consecutive underscores
while result.contains("__") {

View File

@@ -49,7 +49,7 @@ pub fn render(template: &str, meta: &TrackMetadata) -> String {
// Split template by "/" so we can sanitize each path component individually.
// The extension is special — it's part of the filename, not sanitized separately.
let result = template
template
.replace("{artist}", &sanitize_component(artist))
.replace("{album_artist}", &sanitize_component(album_artist))
.replace("{album}", &sanitize_component(album))
@@ -58,9 +58,7 @@ pub fn render(template: &str, meta: &TrackMetadata) -> String {
.replace("{disc_number}", &disc_number)
.replace("{year}", &year)
.replace("{genre}", &sanitize_component(genre))
.replace("{ext}", ext);
result
.replace("{ext}", ext)
}
#[cfg(test)]
@@ -85,14 +83,23 @@ mod tests {
fn test_render_default_format() {
let meta = test_meta();
let result = render(DEFAULT_FORMAT, &meta);
assert_eq!(result, "Pink Floyd/The Dark Side of the Moon/03 - Time.flac");
assert_eq!(
result,
"Pink Floyd/The Dark Side of the Moon/03 - Time.flac"
);
}
#[test]
fn test_render_custom_format() {
let meta = test_meta();
let result = render("{artist} - {album}/{disc_number}-{track_number} {title}.{ext}", &meta);
assert_eq!(result, "Pink Floyd - The Dark Side of the Moon/1-03 Time.flac");
let result = render(
"{artist} - {album}/{disc_number}-{track_number} {title}.{ext}",
&meta,
);
assert_eq!(
result,
"Pink Floyd - The Dark Side of the Moon/1-03 Time.flac"
);
}
#[test]
@@ -102,7 +109,10 @@ mod tests {
..Default::default()
};
let result = render(DEFAULT_FORMAT, &meta);
assert_eq!(result, "Unknown Artist/Unknown Album/00 - Unknown Title.mp3");
assert_eq!(
result,
"Unknown Artist/Unknown Album/00 - Unknown Title.mp3"
);
}
#[test]

View File

@@ -87,8 +87,16 @@ async fn test_organize_from_directory() {
let money_path = target
.path()
.join("Pink Floyd/The Dark Side of the Moon/06 - Money.mp3");
assert!(time_path.exists(), "Time should exist at {}", time_path.display());
assert!(money_path.exists(), "Money should exist at {}", money_path.display());
assert!(
time_path.exists(),
"Time should exist at {}",
time_path.display()
);
assert!(
money_path.exists(),
"Money should exist at {}",
money_path.display()
);
// Source files should be gone (moved, not copied)
assert!(!source.path().join("song1.mp3").exists());
@@ -124,7 +132,12 @@ async fn test_organize_copy_mode() {
// Source should still exist (copy mode)
assert!(source.path().join("song.mp3").exists());
// Target should exist too
assert!(target.path().join("Pink Floyd/DSOTM/01 - Time.mp3").exists());
assert!(
target
.path()
.join("Pink Floyd/DSOTM/01 - Time.mp3")
.exists()
);
}
#[tokio::test]
@@ -237,8 +250,10 @@ async fn test_organize_missing_metadata() {
assert_eq!(stats.files_organized, 1);
// Should use "Unknown" fallbacks
assert!(target
.path()
.join("Unknown Artist/Unknown Album/00 - Unknown Title.mp3")
.exists());
assert!(
target
.path()
.join("Unknown Artist/Unknown Album/00 - Unknown Title.mp3")
.exists()
);
}