SACAD (Smart Automatic Cover Art Downloader) - A Rust CLI tool to download album cover art.
Provides two binaries: sacad (single cover) and sacad_r (recursive library scan).
- Async runtime: Tokio
- HTTP client: reqwest
- Image processing: image crate, blockhash (perceptual hashing)
- CLI parsing: clap (derive)
# Build
cargo build
cargo build --release
# Check (fast type/lint check)
cargo check
# Run clippy (strict linting enabled in Cargo.toml)
cargo clippy --all-targets --all-features
# Check formatting
cargo +nightly fmt --check -- --config imports_granularity=Crate --config group_imports=StdExternalCrate
# Run tests
cargo test --all-features
# Run single test
cargo test <test_name>
# Run single binary
cargo run --bin sacad -- "artist" "album" 600 cover.jpg
cargo run --bin sacad_r -- /path/to/library 600 cover.jpgsrc/
├── bin/
│ ├── sacad.rs # Single cover download binary
│ └── sacad_r.rs # Recursive library scanner binary
├── source/ # Cover source implementations
├── http/ # HTTP client and caching
├── cl.rs # CLI argument definitions
├── cover.rs # Cover struct and comparison logic
├── extras.rs # Man page and shell completion generation (feature-gated: generate-extras, unix only)
├── lib.rs # Main library API, used by sacad and sacad_r binaries
├── perceptual_hash.rs # Image hashing for similarity
├── tags.rs # Audio file tags handling
└── walk.rs # Library tree walking and stat counters for sacad_r
- Rust 2024 edition, MSRV 1.91 (can be increased as needed)
- Strict Clippy: pedantic + many restriction lints (see
[lints.clippy]in Cargo.toml) - No
unwrap/expect/panicin non-test code; useanyhowfor errors - Imports:
- Place all
usestatements at the top of the file; do not put them inside functions,implblocks, or other inner scopes (the only exception is inside#[cfg(...)]modules such asmod tests, where the imports go at the top of that module) - Group std imports first, then external crates, then local modules
- Never use fully-qualified paths (e.g.,
std::path::Pathorcrate::ui::foo()) in code; always import namespaces viausestatements and refer to symbols by their short name - Import deep
stdnamespaces aggressively (e.g.,use std::path::PathBuf;,use std::collections::HashMap;), except for namespaces likeioorfswhose symbols have very common names that may collide — import those at the module level instead (e.g.,use std::fs;) - For third-party crates, prefer importing at the crate or module level (e.g.,
use anyhow::Context as _;,use clap::Parser;) rather than deeply importing individual symbols, to keep the origin of symbols clear when reading code — only import deeper when needed to avoid very long fully-qualified namespaces
- Place all
- In format strings, never mix positional placeholders (
{}) with named ones; for expression arguments, use named arguments ({id}…id = loc.id) - When formatting paths in error messages or logs, always use debug formatting (
{:?}) rather than.display()to preserve non-UTF-8 safety and show quoting - Prefer
logmacros for logging; nodbg!ortodo! - Prefer
default-features = falsefor dependencies - Do not add
derivetraits unless they are required by the current code (compile errors) or actively used by tests/runtime behavior - In tests:
- Use
use super::*;to import from the parent module - Prefer
unwrap()overexpect()for conciseness - Do not add custom messages to
assert!/assert_eq!/assert_ne!— the test name is sufficient - Prefer full type comparisons with
assert_eq!over selectively checking nested attributes or unpacking; tag types with#[cfg_attr(test, derive(Eq, PartialEq))]if needed - Do not add section-separator comments (e.g.,
// --- Some Section ---) in test modules — test names are descriptive enough
- Use
- Comments:
- Documentation required: every module and item must have a doc comment (
//!or///);missing_docsis warned - Keep comments concise: prefer a short summary over restating implementation details, only mention exceptional cases when they affect behavior, and are not already conveyed by the types used, function signature, or code just below
- Comments do not end with a dot, unless it separates sentences
- When moving or refactoring code, never remove comment lines — preserve all comments and move them along with the code they document
- Documentation required: every module and item must have a doc comment (
- This repository uses the jujutsu VCS. Never use any
jjcommand that modifies the repository. - You can also use read-only git commands for inspecting repository state. Never use any git command that modifies the repository.