playlist-check/src/main.rs

84 lines
2.4 KiB
Rust
Raw Normal View History

use anyhow::Result;
use clap::{Parser, Subcommand};
use m3u::Entry;
use std::collections::HashSet;
/// Check if files in a playlist actually exist.
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
/// Check mode.
#[command(subcommand)]
mode: Mode,
/// Playlist file name.
input: String,
}
#[derive(Subcommand, Clone, Debug)]
enum Mode {
#[command()]
Files,
#[command()]
Directories {
#[arg(long, short)]
base_directory: String,
#[arg(long, short, default_value_t = false)]
relative: bool,
},
}
fn get_directories(base_directory: &str, is_relative: bool) -> Result<HashSet<String>> {
let mut result = HashSet::<String>::new();
for entry in std::fs::read_dir(base_directory)? {
let entry = entry?;
if entry.metadata()?.is_dir() {
let p = if is_relative {
entry.file_name().to_string_lossy().to_string()
} else {
entry.path().display().to_string()
};
result.insert(p);
}
}
Ok(result)
}
fn main() -> Result<()> {
let args = Args::parse();
let mut reader = m3u::Reader::open_ext(&args.input)?;
match args.mode {
Mode::Files => {
for entry in reader.entry_exts() {
if let Entry::Path(p) = entry?.entry {
if std::fs::metadata(&p).is_err() {
println!("{}", p.display());
}
}
}
}
Mode::Directories {
base_directory,
relative,
} => {
let mut playlist_directories = HashSet::<String>::new();
for entry in reader.entry_exts() {
if let Entry::Path(p) = entry?.entry {
if let Some(dir) = p.parent() {
playlist_directories.insert(dir.display().to_string());
}
}
}
let actual_directories = get_directories(&base_directory, relative)?;
let mut missing_directories = actual_directories
.difference(&playlist_directories)
.into_iter()
.collect::<Vec<_>>();
missing_directories.sort_unstable();
for d in missing_directories {
println!("{}", d);
}
}
}
Ok(())
}