From d531f8909d3ee47ea11a7a0dc60ea70819c16032 Mon Sep 17 00:00:00 2001 From: jakka Date: Tue, 30 Sep 2025 17:00:59 +0300 Subject: removed useless trait, reorganized code, isolated modules --- src/db.rs | 203 +++++++++++++++++++++++++++-------------------------------- src/files.rs | 50 ++++++++------- src/flac.rs | 4 +- src/main.rs | 7 +-- 4 files changed, 125 insertions(+), 139 deletions(-) diff --git a/src/db.rs b/src/db.rs index 809e5e0..ba14c1a 100644 --- a/src/db.rs +++ b/src/db.rs @@ -20,126 +20,109 @@ const DEDUPE_DB: &str = "DELETE FROM flacs WHERE rowid NOT IN (SELECT MAX(rowid) FROM flacs GROUP BY path)"; const GET_MODTIME: &str = "SELECT modtime FROM flacs WHERE path = ?1"; -pub trait Database { - type Conn; - fn new(path: Option<&PathBuf>) -> Result; - fn insert_file(&self, filename: &Path) -> Result<()>; - fn update_file(&self, filename: &Path) -> Result<()>; - fn check_file(&self, filename: &Path) -> Result; - fn init_clean_files(&self) -> Result, rusqlite::Error>; - fn remove_file(&self, filename: &Path) -> Result<()>; - fn get_toencode_files(&self) -> Result, rusqlite::Error>; - fn get_toencode_number(&self) -> Result; - fn get_modtime(&self, file: &Path) -> Result; - fn vacuum(&self) -> Result<()>; +pub(crate) fn init_connection(path: Option<&PathBuf>) -> Result { + let conn = if let Some(file) = path { + Connection::open(file)? + } else if let Some(base_dir) = BaseDirs::new() { + let file = Path::new(base_dir.data_dir()).join("reencoder.db"); + Connection::open(file)? + } else { + return Err(anyhow!("Failed to locate data directory")); + }; + conn.execute(TABLE_CREATE, ())?; + Ok(conn) } -impl Database for Connection { - type Conn = Connection; - fn new(path: Option<&PathBuf>) -> Result { - let conn = if let Some(file) = path { - Connection::open(file)? - } else if let Some(base_dir) = BaseDirs::new() { - let file = Path::new(base_dir.data_dir()).join("reencoder.db"); - Connection::open(file)? - } else { - return Err(anyhow!("Failed to locate data directory")); - }; - conn.execute(TABLE_CREATE, ())?; - Ok(conn) - } - - fn insert_file(&self, filename: &Path) -> Result<()> { - let toencode = !matches!(get_vendor(filename)?.as_str(), CURRENT_VENDOR); +pub(crate) fn insert_file(conn: &Connection, filename: &Path) -> Result<()> { + let toencode = !matches!(get_vendor(filename)?.as_str(), CURRENT_VENDOR); - let modtime = filename - .metadata()? - .modified()? - .duration_since(UNIX_EPOCH)? - .as_secs(); + let modtime = filename + .metadata()? + .modified()? + .duration_since(UNIX_EPOCH)? + .as_secs(); - self.execute( - ADD_ITEM, - params![filename.to_str().unwrap(), toencode, modtime], - )?; + conn.execute( + ADD_ITEM, + params![filename.to_str().unwrap(), toencode, modtime], + )?; - Ok(()) - } + Ok(()) +} - fn update_file(&self, filename: &Path) -> Result<()> { - let modtime = filename - .metadata()? - .modified()? - .duration_since(UNIX_EPOCH)? - .as_secs(); +pub(crate) fn update_file(conn: &Connection, filename: &Path) -> Result<()> { + let modtime = filename + .metadata()? + .modified()? + .duration_since(UNIX_EPOCH)? + .as_secs(); - self.execute( - UPDATE_ITEM, - params![filename.to_str().unwrap(), false, modtime], - )?; + conn.execute( + UPDATE_ITEM, + params![filename.to_str().unwrap(), false, modtime], + )?; - Ok(()) - } + Ok(()) +} - fn check_file(&self, filename: &Path) -> Result { - if self.query_one(CHECK_FILE, params!(filename.to_str().unwrap()), |row| { - let num: bool = row.get(0)?; - Ok(num) - })? { - Ok(true) - } else { - Ok(false) - } +pub(crate) fn check_file(conn: &Connection, filename: &Path) -> Result { + if conn.query_one(CHECK_FILE, params!(filename.to_str().unwrap()), |row| { + let num: bool = row.get(0)?; + Ok(num) + })? { + Ok(true) + } else { + Ok(false) } +} - fn init_clean_files(&self) -> Result, rusqlite::Error> { - self.execute(DEDUPE_DB, ())?; - let mut stmt = self.prepare(FETCH_FILES)?; - let mut rows = stmt.query(())?; - let mut files = Vec::new(); - while let Ok(Some(row)) = rows.next() { - let path: String = row.get(0)?; - files.push(PathBuf::from(path)); - } - Ok(files) +pub(crate) fn init_clean_files(conn: &Connection) -> Result, rusqlite::Error> { + conn.execute(DEDUPE_DB, ())?; + let mut stmt = conn.prepare(FETCH_FILES)?; + let mut rows = stmt.query(())?; + let mut files = Vec::new(); + while let Ok(Some(row)) = rows.next() { + let path: String = row.get(0)?; + files.push(PathBuf::from(path)); } + Ok(files) +} - fn remove_file(&self, filename: &Path) -> Result<()> { - self.execute(REMOVE_FILE, params!(filename.to_str().unwrap()))?; - Ok(()) - } +pub(crate) fn remove_file(conn: &Connection, filename: &Path) -> Result<()> { + conn.execute(REMOVE_FILE, params!(filename.to_str().unwrap()))?; + Ok(()) +} - fn get_toencode_files(&self) -> Result, rusqlite::Error> { - let mut stmt = self.prepare(TOENCODE_PATHS)?; - let mut rows = stmt.query(())?; - let mut files: Vec = Vec::new(); - while let Ok(Some(row)) = rows.next() { - let path: String = row.get(0)?; - files.push(PathBuf::from(path)); - } - Ok(files) +pub(crate) fn get_toencode_files(conn: &Connection) -> Result, rusqlite::Error> { + let mut stmt = conn.prepare(TOENCODE_PATHS)?; + let mut rows = stmt.query(())?; + let mut files: Vec = Vec::new(); + while let Ok(Some(row)) = rows.next() { + let path: String = row.get(0)?; + files.push(PathBuf::from(path)); } + Ok(files) +} - fn get_toencode_number(&self) -> Result { - self.query_one(TOENCODE_NUMBER, (), |row| { - let num: u64 = row.get(0)?; - Ok(num) - }) - } +pub(crate) fn get_toencode_number(conn: &Connection) -> Result { + conn.query_one(TOENCODE_NUMBER, (), |row| { + let num: u64 = row.get(0)?; + Ok(num) + }) +} - fn get_modtime(&self, file: &Path) -> Result { - Ok( - self.query_one(GET_MODTIME, params![file.to_str().unwrap()], |row| { - let modtime: u64 = row.get(0)?; - Ok(modtime) - })?, - ) - } +pub(crate) fn get_modtime(conn: &Connection, file: &Path) -> Result { + Ok( + conn.query_one(GET_MODTIME, params![file.to_str().unwrap()], |row| { + let modtime: u64 = row.get(0)?; + Ok(modtime) + })?, + ) +} - fn vacuum(&self) -> Result<()> { - self.execute("VACUUM", ())?; - Ok(()) - } +pub(crate) fn vacuum(conn: &Connection) -> Result<()> { + conn.execute("VACUUM", ())?; + Ok(()) } #[cfg(test)] @@ -156,10 +139,10 @@ mod tests { "./samples/32bit.flac", ]; let mut counter = 0; - let conn = Connection::new(Some(&dbname)).unwrap(); + let conn = init_connection(Some(&dbname)).unwrap(); for file in filenames { let filename = PathBuf::from(file); - conn.insert_file(&filename).unwrap(); + insert_file(&conn, &filename).unwrap(); } let mut stmt = conn.prepare(TOENCODE_PATHS).unwrap(); let mut returned = stmt.query(()).unwrap(); @@ -179,10 +162,9 @@ mod tests { "./samples/24bit.flac", "./samples/32bit.flac", ]; - let conn = Connection::new(Some(&dbname)).unwrap(); + let conn = init_connection(Some(&dbname)).unwrap(); for file in filenames { - conn.insert_file(&Path::new(file).canonicalize().unwrap()) - .unwrap(); + insert_file(&conn, &Path::new(file).canonicalize().unwrap()).unwrap(); } conn.execute( @@ -199,8 +181,11 @@ mod tests { ) .unwrap(); - conn.update_file(&Path::new("./samples/16bit.flac").canonicalize().unwrap()) - .unwrap(); + update_file( + &conn, + &Path::new("./samples/16bit.flac").canonicalize().unwrap(), + ) + .unwrap(); let mut stmt = conn.prepare(TOENCODE_PATHS).unwrap(); let mut returned = stmt.query(()).unwrap(); diff --git a/src/files.rs b/src/files.rs index afb42d7..3b00de1 100644 --- a/src/files.rs +++ b/src/files.rs @@ -1,3 +1,5 @@ +use crate::db; +use crate::flac::handle_encode; use anyhow::{Result, anyhow}; #[cfg(not(test))] use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle}; @@ -15,8 +17,6 @@ use std::{ }; use walkdir::WalkDir; -use crate::{db::Database, flac::handle_encode}; - #[cfg(not(test))] const BAR_TEMPLATE: &str = "{msg:<} [{wide_bar:.green/cyan}] Elapsed: {elapsed} {pos:>7}/{len:7}"; #[cfg(not(test))] @@ -51,25 +51,25 @@ impl Display for FileError { impl Error for FileError {} fn handle_file(file: &Path, conn: &Connection) -> Result<()> { - if conn.check_file(file)? { + if db::check_file(conn, file)? { let modtime = file .metadata()? .modified()? .duration_since(UNIX_EPOCH)? .as_secs(); - let db_modtime = conn.get_modtime(file)?; + let db_modtime = db::get_modtime(conn, file)?; if modtime != db_modtime { - conn.update_file(file)?; + db::update_file(conn, file)?; } return Ok(()); } - conn.insert_file(file)?; + db::insert_file(conn, file)?; Ok(()) } -pub fn index_files_recursively( +pub(crate) fn index_files_recursively( path: &Path, conn: &Connection, handler: Arc, @@ -129,16 +129,20 @@ pub fn index_files_recursively( Ok(()) } -pub fn reencode_files(conn: Connection, handler: Arc, threads: usize) -> Result<()> { +pub(crate) fn reencode_files( + conn: Connection, + handler: Arc, + threads: usize, +) -> Result<()> { #[cfg(not(test))] let bar = ProgressBar::with_draw_target( - Some(conn.get_toencode_number()?), + Some(db::get_toencode_number(&conn)?), ProgressDrawTarget::stdout_with_hz(60), ) .with_style(ProgressStyle::with_template(BAR_TEMPLATE)?.progress_chars("#>-")) .with_message("Reencoding"); - let mut files = conn.get_toencode_files()?.into_iter(); + let mut files = db::get_toencode_files(&conn)?.into_iter(); let lock = Arc::new(Mutex::new(conn)); @@ -170,7 +174,7 @@ pub fn reencode_files(conn: Connection, handler: Arc, threads: usize match handle_encode(&file, handler) { Err(error) => eprintln!("{}", FileError::new(&file, error)), Ok(false) => { - if let Err(error) = lock.lock().unwrap().update_file(&file) { + if let Err(error) = db::update_file(&lock.lock().unwrap(), &file) { eprintln!("{}", FileError::new(&file, error)); } #[cfg(not(test))] @@ -194,8 +198,8 @@ pub fn reencode_files(conn: Connection, handler: Arc, threads: usize Ok(()) } -pub fn clean_files(conn: &Connection, handler: Arc) -> Result<()> { - let files = conn.init_clean_files()?; +pub(crate) fn clean_files(conn: &Connection, handler: Arc) -> Result<()> { + let files = db::init_clean_files(conn)?; #[cfg(not(test))] let spinner = ProgressBar::with_draw_target(None, ProgressDrawTarget::stdout_with_hz(60)) @@ -206,7 +210,7 @@ pub fn clean_files(conn: &Connection, handler: Arc) -> Result<()> { files.iter().for_each(|file| { #[allow(clippy::collapsible_if)] if handler.load(Ordering::SeqCst) && !file.exists() { - if let Err(error) = conn.remove_file(file) { + if let Err(error) = db::remove_file(conn, file) { eprintln!("{}", FileError::new(file, error)) }; #[cfg(not(test))] @@ -216,7 +220,7 @@ pub fn clean_files(conn: &Connection, handler: Arc) -> Result<()> { #[cfg(not(test))] spinner.finish(); - conn.vacuum()?; + db::vacuum(conn)?; Ok(()) } @@ -229,7 +233,7 @@ mod tests { fn test_index_lots_of_files() { let dbname = PathBuf::from("temp3.db"); let handler = Arc::new(AtomicBool::new(true)); - let conn = Connection::new(Some(&dbname)).unwrap(); + let conn = db::init_connection(Some(&dbname)).unwrap(); index_files_recursively(Path::new("./testfiles"), &conn, handler).unwrap(); std::fs::remove_file(dbname).unwrap(); } @@ -238,7 +242,7 @@ mod tests { fn test_clean_files() { let dbname = PathBuf::from("temp4.db"); let handler = Arc::new(AtomicBool::new(true)); - let conn = Connection::new(Some(&dbname)).unwrap(); + let conn = db::init_connection(Some(&dbname)).unwrap(); let filenames = [ "./samples/16bit.flac", "./samples/24bit.flac", @@ -248,13 +252,13 @@ mod tests { std::fs::copy("./samples/32bit.flac", "./samples/nonexisting.flac").unwrap(); for file in filenames { let filename = PathBuf::from(file); - conn.insert_file(&filename).unwrap(); + db::insert_file(&conn, &filename).unwrap(); } std::fs::remove_file("./samples/nonexisting.flac").unwrap(); clean_files(&conn, handler).unwrap(); - let counter = conn.init_clean_files().unwrap().len(); + let counter = db::init_clean_files(&conn).unwrap().len(); std::fs::remove_file(dbname).unwrap(); assert!(counter == 3) } @@ -263,13 +267,13 @@ mod tests { fn test_reencode_lots_of_files() { let dbname = PathBuf::from("temp5.db"); let handler = Arc::new(AtomicBool::new(true)); - let conn = Connection::new(Some(&dbname)).unwrap(); + let conn = db::init_connection(Some(&dbname)).unwrap(); let temp = handler.clone(); index_files_recursively(Path::new("./testfiles"), &conn, temp).unwrap(); - println!("\n{}", conn.get_toencode_number().unwrap()); + println!("\n{}", db::get_toencode_number(&conn).unwrap()); reencode_files(conn, handler, 4).unwrap(); - let conn = Connection::new(Some(&dbname)).unwrap(); - println!("\n{}", conn.get_toencode_number().unwrap()); + let conn = db::init_connection(Some(&dbname)).unwrap(); + println!("\n{}", db::get_toencode_number(&conn).unwrap()); std::fs::remove_file(dbname).unwrap(); } } diff --git a/src/flac.rs b/src/flac.rs index e882cd7..eb603d7 100644 --- a/src/flac.rs +++ b/src/flac.rs @@ -141,7 +141,7 @@ fn encode_file(filename: &Path, handler: Arc) -> Result { Ok(false) } -pub fn handle_encode(filename: &Path, handler: Arc) -> Result { +pub(crate) fn handle_encode(filename: &Path, handler: Arc) -> Result { match encode_file(filename, handler) { Err(error) => { let _ = std::fs::remove_file(filename.with_extension("tmp")); @@ -151,7 +151,7 @@ pub fn handle_encode(filename: &Path, handler: Arc) -> Result } } -pub fn get_vendor(file: &Path) -> Result { +pub(crate) fn get_vendor(file: &Path) -> Result { let blocklist = metadata::BlockList::open(file)?; if let Some(data) = blocklist.get::() { Ok(data.vendor_string.to_owned()) diff --git a/src/main.rs b/src/main.rs index d1e4562..de3da62 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,6 @@ use anyhow::Result; use clap::{Arg, ArgAction, Command, ValueHint, command, value_parser}; use clap_complete::{Generator, Shell, generate}; use console::style; -use rusqlite::Connection; use std::{ path::PathBuf, sync::{ @@ -14,8 +13,6 @@ use std::{ }, }; -use crate::db::Database; - fn build_cli() -> Command { command!() .arg( @@ -93,12 +90,12 @@ fn main() -> Result<()> { r.store(false, Ordering::SeqCst); })?; - let conn = Connection::new(args.get_one::("db"))?; + let conn = db::init_connection(args.get_one::("db"))?; let path = args.get_one::("path"); if path.is_none() && !args.get_flag("clean") && !args.get_flag("doit") { - let count = conn.get_toencode_number()?; + let count = db::get_toencode_number(&conn)?; println!("Files to reencode:\t{}", style(count).green()); return Ok(()); } -- cgit v1.3.1