diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/db.rs | 39 | ||||
| -rw-r--r-- | src/files.rs | 62 | ||||
| -rw-r--r-- | src/main.rs | 5 |
3 files changed, 63 insertions, 43 deletions
@@ -3,7 +3,7 @@ use directories::BaseDirs; use futures_util::Stream; use libsql::{Builder, Connection, params}; use std::{ - path::{Path, PathBuf}, + path::Path, time::{Duration, UNIX_EPOCH}, }; @@ -26,10 +26,14 @@ pub struct Database(pub Connection); impl Database { pub async fn new(path: impl AsRef<Path>) -> Result<Self> { - let conn = Builder::new_local(path).build().await?.connect()?; - conn.execute(TABLE_CREATE, ()).await?; + if path.as_ref().is_file() { + let conn = Builder::new_local(path).build().await?.connect()?; + conn.execute(TABLE_CREATE, ()).await?; - Ok(Database(conn)) + Ok(Database(conn)) + } else { + Err(anyhow!("Not a file")) + } } pub async fn insert_file(&self, filename: impl AsRef<Path>) -> Result<()> { @@ -102,26 +106,17 @@ impl Database { } } - pub async fn clean_files(&self) -> Result<()> { - let mut tasks = tokio::task::JoinSet::new(); + pub async fn init_clean_files( + &self, + ) -> Result<impl Stream<Item = libsql::Result<libsql::Row>>> { self.0.execute(DEDUPE_DB, ()).await?; - let mut query_res = self.0.query(FETCH_FILES, ()).await?; - while let Ok(Some(row)) = query_res.next().await { - let path = PathBuf::from(row.get_str(0)?); - let conn = self.0.clone(); - tasks.spawn(async move { - if !path.exists() { - let _ = conn - .execute(REMOVE_FILE, params!(path.to_str().unwrap())) - .await; - } - }); - } - - tasks.join_all().await; - - self.0.execute("VACUUM", ()).await?; + Ok(self.0.query(FETCH_FILES, ()).await?.into_stream()) + } + pub async fn remove_file(&self, filename: impl AsRef<Path>) -> Result<()> { + self.0 + .execute(REMOVE_FILE, params!(filename.as_ref().to_str().unwrap())) + .await?; Ok(()) } diff --git a/src/files.rs b/src/files.rs index bbcc0c4..52b4058 100644 --- a/src/files.rs +++ b/src/files.rs @@ -1,7 +1,6 @@ use anyhow::{Result, anyhow}; use futures_util::StreamExt; -#[cfg(not(test))] -use indicatif::{ProgressBar, ProgressStyle}; +use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle}; use pin_utils::pin_mut; use std::{ error::Error, @@ -15,8 +14,8 @@ use walkdir::WalkDir; use crate::{db::Database, flac::encode_file}; -#[cfg(not(test))] -const BAR_TEMPLATE: &str = "{msg} [{wide_bar:.green/cyan}] Elapsed: {elapsed} {pos:>7}/{len:7}"; +const BAR_TEMPLATE: &str = "{msg:<} [{wide_bar:.green/cyan}] Elapsed: {elapsed} {pos:>7}/{len:7}"; +const SPINNER_TEMPLATE: &str = "Removed from db: {pos:.green}"; #[derive(Debug)] pub struct FileError { @@ -86,8 +85,7 @@ pub async fn index_files_recursively( let mut tasks: JoinSet<Result<(), anyhow::Error>> = JoinSet::new(); - #[cfg(not(test))] - let bar = ProgressBar::new(0) + let bar = ProgressBar::with_draw_target(Some(0), ProgressDrawTarget::stdout_with_hz(60)) .with_style(ProgressStyle::with_template(BAR_TEMPLATE)?.progress_chars("#>-")) .with_message("Indexing"); @@ -99,15 +97,13 @@ pub async fn index_files_recursively( if let Some(ext) = path.extension() { if ext == "flac" { let newconn = conn.clone(); - #[cfg(not(test))] + let newbar = bar.clone(); tasks.spawn(async move { handle_file(path, newconn).await?; - #[cfg(not(test))] newbar.inc(1); Ok(()) }); - #[cfg(not(test))] bar.inc_length(1); } } @@ -127,7 +123,6 @@ pub async fn index_files_recursively( } } - #[cfg(not(test))] bar.abandon_with_message("Indexing aborted"); return Ok(()) }, @@ -140,7 +135,6 @@ pub async fn index_files_recursively( } } - #[cfg(not(test))] bar.finish_with_message("Finished indexing"); Ok(()) } @@ -151,17 +145,18 @@ pub async fn reencode_files(conn: &Database, canceltoken: CancellationToken) -> let mut tasks = JoinSet::new(); - #[cfg(not(test))] - let bar = ProgressBar::new(conn.get_toencode_number().await?) - .with_style(ProgressStyle::with_template(BAR_TEMPLATE)?.progress_chars("#>-")) - .with_message("Reencoding"); + let bar = ProgressBar::with_draw_target( + Some(conn.get_toencode_number().await?), + ProgressDrawTarget::stdout_with_hz(60), + ) + .with_style(ProgressStyle::with_template(BAR_TEMPLATE)?.progress_chars("#>-")) + .with_message("Indexing"); while let Some(Ok(row)) = stream.next().await { if let Some(file) = row.get_value(0)?.as_text() { let filename = Path::new(file).canonicalize()?; if filename.exists() { let newconn = conn.clone(); - #[cfg(not(test))] let newbar = bar.clone(); tasks.spawn(async move { let file = filename.clone(); @@ -174,7 +169,6 @@ pub async fn reencode_files(conn: &Database, canceltoken: CancellationToken) -> if let Err(error) = newconn.update_file(&filename).await { return Err(anyhow!(FileError::new(&filename, error))); }; - #[cfg(not(test))] newbar.inc(1); Ok(()) }); @@ -194,7 +188,6 @@ pub async fn reencode_files(conn: &Database, canceltoken: CancellationToken) -> } } - #[cfg(not(test))] bar.abandon_with_message("Reencoding aborted"); return Ok(()) }, @@ -207,12 +200,43 @@ pub async fn reencode_files(conn: &Database, canceltoken: CancellationToken) -> } } - #[cfg(not(test))] bar.finish_with_message("Finished encoding"); Ok(()) } +pub async fn clean_files(conn: &Database) -> Result<()> { + let mut tasks: JoinSet<std::result::Result<(), anyhow::Error>> = JoinSet::new(); + + let query_res = conn.init_clean_files().await?; + pin_mut!(query_res); + + let spinner = ProgressBar::with_draw_target(None, ProgressDrawTarget::stdout_with_hz(60)) + .with_style(ProgressStyle::with_template(SPINNER_TEMPLATE)?); + + while let Some(Ok(row)) = query_res.next().await { + let path = PathBuf::from(row.get_str(0)?); + let newconn = conn.clone(); + + let newspinner = spinner.clone(); + tasks.spawn(async move { + if !path.exists() { + newconn.remove_file(path).await?; + newspinner.inc(1); + } + Ok(()) + }); + } + + tasks.join_all().await; + + spinner.finish(); + + conn.0.execute("VACUUM", ()).await?; + + Ok(()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/main.rs b/src/main.rs index 1dd6f1b..9deb7c6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ mod flac; use anyhow::Result; use clap::{Arg, ArgAction, Command, ValueHint, command, value_parser}; use clap_complete::{Generator, Shell, generate}; +use console::style; use std::path::PathBuf; use tokio_util::sync::CancellationToken; @@ -101,7 +102,7 @@ fn main() -> Result<()> { if path.is_none() && !args.get_flag("clean") && !args.get_flag("doit") { let count = conn.get_toencode_number().await?; - println!("Files to reencode:\t{count}"); + println!("Files to reencode:\t{}", style(count).green()); return Ok(()); } @@ -115,7 +116,7 @@ fn main() -> Result<()> { } if args.get_flag("clean") { - conn.clean_files().await?; + files::clean_files(&conn).await?; } if canceltoken.is_cancelled() { |
