use audex::File;
use std::error::Error;
use std::path::Path;
fn copy_to_temp(original: &str) -> Result<(tempfile::TempDir, String), Box<dyn Error>> {
let src = Path::new(original);
let file_name = src.file_name().ok_or("input path has no file name")?;
let tmp_dir = tempfile::tempdir()?;
let dest = tmp_dir.path().join(file_name);
std::fs::copy(src, &dest)?;
let dest_str = dest.to_string_lossy().into_owned();
Ok((tmp_dir, dest_str))
}
fn run_sync(file_path: &str) -> Result<(), Box<dyn Error>> {
println!("\n--- Sync: Load and set a tag ---");
let mut tagger = File::load(file_path)?;
if !tagger.has_tags() {
tagger.add_tags()?;
}
tagger.set("title", vec!["Example Title".to_string()])?;
tagger.save()?;
let tagger = File::load(file_path)?;
println!("title = {:?}", tagger.get("title"));
println!("\n--- Sync: Remove a tag ---");
let mut tagger = File::load(file_path)?;
tagger.remove("title")?;
tagger.save()?;
let tagger = File::load(file_path)?;
println!("title after remove = {:?}", tagger.get("title"));
println!("\n--- Sync: Set multiple tags ---");
let mut tagger = File::load(file_path)?;
if !tagger.has_tags() {
tagger.add_tags()?;
}
tagger.set("title", vec!["Track One".to_string()])?;
tagger.set("artist", vec!["Some Artist".to_string()])?;
tagger.save()?;
let tagger = File::load(file_path)?;
println!("title = {:?}", tagger.get("title"));
println!("artist = {:?}", tagger.get("artist"));
println!("\n--- Sync: Selective removal ---");
let mut tagger = File::load(file_path)?;
tagger.remove("title")?;
tagger.save()?;
let tagger = File::load(file_path)?;
println!("title = {:?} (removed)", tagger.get("title"));
println!("artist = {:?} (kept)", tagger.get("artist"));
println!("\n--- Sync: Clear all tags ---");
let mut tagger = File::load(file_path)?;
tagger.clear()?;
let tagger = File::load(file_path)?;
println!("remaining keys = {:?}", tagger.keys());
Ok(())
}
#[cfg(feature = "async")]
async fn run_async(file_path: &str) -> Result<(), Box<dyn Error>> {
println!("\n--- Async: Load and set a tag ---");
let mut tagger = File::load_async(file_path).await?;
if !tagger.has_tags() {
tagger.add_tags()?;
}
tagger.set("title", vec!["Example Title".to_string()])?;
tagger.save_async().await?;
let tagger = File::load_async(file_path).await?;
println!("title = {:?}", tagger.get("title"));
println!("\n--- Async: Remove a tag ---");
let mut tagger = File::load_async(file_path).await?;
tagger.remove("title")?;
tagger.save_async().await?;
let tagger = File::load_async(file_path).await?;
println!("title after remove = {:?}", tagger.get("title"));
println!("\n--- Async: Set multiple tags ---");
let mut tagger = File::load_async(file_path).await?;
if !tagger.has_tags() {
tagger.add_tags()?;
}
tagger.set("title", vec!["Track One".to_string()])?;
tagger.set("artist", vec!["Some Artist".to_string()])?;
tagger.save_async().await?;
let tagger = File::load_async(file_path).await?;
println!("title = {:?}", tagger.get("title"));
println!("artist = {:?}", tagger.get("artist"));
println!("\n--- Async: Selective removal ---");
let mut tagger = File::load_async(file_path).await?;
tagger.remove("title")?;
tagger.save_async().await?;
let tagger = File::load_async(file_path).await?;
println!("title = {:?} (removed)", tagger.get("title"));
println!("artist = {:?} (kept)", tagger.get("artist"));
println!("\n--- Async: Clear all tags ---");
let mut tagger = File::load_async(file_path).await?;
tagger.clear_async().await?;
let tagger = File::load_async(file_path).await?;
println!("remaining keys = {:?}", tagger.keys());
Ok(())
}
#[cfg(feature = "async")]
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let args: Vec<String> = std::env::args().collect();
if args.len() < 2 {
eprintln!("Usage: {} <audio_file_path> [mode]", args[0]);
eprintln!();
eprintln!("Modes:");
eprintln!(" sync - Run synchronous operations only");
eprintln!(" async - Run asynchronous operations only");
eprintln!(" both - Run both (default)");
std::process::exit(1);
}
let file_path = &args[1];
let mode = if args.len() > 2 {
args[2].to_lowercase()
} else {
"both".to_string()
};
if mode != "sync" && mode != "async" && mode != "both" {
eprintln!(
"Error: Invalid mode '{}'. Must be 'sync', 'async', or 'both'",
mode
);
std::process::exit(1);
}
if !std::path::Path::new(file_path).exists() {
eprintln!("Error: File not found: {}", file_path);
std::process::exit(1);
}
let (_tmp_dir, tmp_path) = copy_to_temp(file_path)?;
println!("Working on temporary copy: {}", tmp_path);
if mode == "sync" || mode == "both" {
println!("=== Synchronous Operations ===");
run_sync(&tmp_path)?;
}
if mode == "async" || mode == "both" {
println!("\n=== Asynchronous Operations ===");
run_async(&tmp_path).await?;
}
println!("\nDone. Original file was not modified.");
Ok(())
}
#[cfg(not(feature = "async"))]
fn main() -> Result<(), Box<dyn Error>> {
let args: Vec<String> = std::env::args().collect();
if args.len() < 2 {
eprintln!("Usage: {} <audio_file_path> [mode]", args[0]);
eprintln!();
eprintln!("Modes:");
eprintln!(" sync - Run synchronous operations only");
std::process::exit(1);
}
let file_path = &args[1];
if !std::path::Path::new(file_path).exists() {
eprintln!("Error: File not found: {}", file_path);
std::process::exit(1);
}
let (_tmp_dir, tmp_path) = copy_to_temp(file_path)?;
println!("Working on temporary copy: {}", tmp_path);
println!("=== Synchronous Operations ===");
run_sync(&tmp_path)?;
println!("\nDone. Original file was not modified.");
Ok(())
}