From 0f111dc8f998b7882776009b42f2b08cbee4b7f5 Mon Sep 17 00:00:00 2001 From: jakka Date: Wed, 11 Jun 2025 22:07:55 +0300 Subject: cleaned flac code. started writing db logic --- src/db.rs | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) (limited to 'src/db.rs') diff --git a/src/db.rs b/src/db.rs index 8b13789..e42d235 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1 +1,146 @@ +use anyhow::{Ok, Result, anyhow}; +use directories::BaseDirs; +use futures_util::StreamExt; +use libsql::{Builder, Connection, params}; +use std::{ + ffi::OsStr, + fmt::Display, + path::{Path, absolute}, +}; +use crate::flac; + +pub async fn open_db() -> Result { + let conn = if let Some(base_dir) = BaseDirs::new() { + let db_name = Path::new(base_dir.data_dir()).join("reencoder.db"); + Builder::new_local(db_name).build().await?.connect()? + } else { + return Err(anyhow!("Failed to locate data directory")); + }; + + conn.execute("CREATE TABLE IF NOT EXISTS flacs (path TEXT PRIMARY KEY, vendor TEXT, toencode BOOLEAN NOT NULL)", ()).await?; + + Ok(conn) +} + +#[derive(Debug)] +enum Errors { + EmptyQuery, +} + +impl Display for Errors { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Errors::EmptyQuery => write!(f, "Empty query"), + } + } +} + +trait Reencoder { + async fn insert_file(&self, filename: &impl AsRef) -> Result<()>; + async fn update_file(&self, filename: &impl AsRef) -> Result<()>; + async fn get_files_toencode(&self) -> Result>; +} + +impl Reencoder for Connection { + async fn insert_file(&self, filename: &impl AsRef) -> Result<()> { + let file = Path::new(filename); + let vendor = flac::get_vendor(file); + let toencode = !matches!(vendor.as_str(), flac::CURRENT_VENDOR); + + let abs_filename = absolute(file)?; + + self.execute( + "INSERT INTO flacs (path, vendor, toencode) VALUES (?1, ?2, ?3)", + params![abs_filename.to_str().unwrap(), vendor, toencode], + ) + .await?; + + Ok(()) + } + + async fn update_file(&self, filename: &impl AsRef) -> Result<()> { + let abs_filename = absolute(Path::new(filename))?; + + self.execute( + "REPLACE INTO flacs (path, vendor, toencode) VALUES (?1, ?2, ?3)", + params![abs_filename.to_str().unwrap(), flac::CURRENT_VENDOR, false], + ) + .await?; + + Ok(()) + } + + async fn get_files_toencode(&self) -> Result> { + let rows = self + .query("SELECT path FROM flacs WHERE toencode", ()) + .await?; + if rows.column_count() == 0 { + return Err(anyhow!(Errors::EmptyQuery)); + }; + + let filenames = rows + .into_stream() + .map(|row| row.unwrap().get_str(0).unwrap().to_string()) + .collect::>() + .await; + + Ok(filenames) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + async fn dummy_db(name: impl AsRef) -> Connection { + let conn = Builder::new_local(name) + .build() + .await + .unwrap() + .connect() + .unwrap(); + conn.execute("CREATE TABLE IF NOT EXISTS flacs (path TEXT PRIMARY KEY, vendor TEXT, toencode BOOLEAN NOT NULL)", ()).await.unwrap(); + conn + } + + #[tokio::test] + async fn check_localfiles() { + let dbname = String::from("temp1.db"); + let filenames = ["16bit.flac", "24bit.flac", "32bit.flac"]; + let conn = dummy_db(&dbname).await; + for file in filenames { + let _ = conn.insert_file(&file.to_string()).await; + } + let returned = conn.get_files_toencode().await.unwrap(); + std::fs::remove_file(dbname).unwrap(); + assert!(returned.is_empty()) + } + + #[tokio::test] + async fn check_update() { + let dbname = String::from("temp2.db"); + let filenames = ["16bit.flac", "24bit.flac", "32bit.flac"]; + let conn = dummy_db(&dbname).await; + for file in filenames { + let _ = conn.insert_file(&file.to_string()).await; + } + + let _ = conn + .execute( + "REPLACE INTO flacs (path, vendor, toencode) VALUES (?1, ?2, ?3)", + params![ + absolute(Path::new("16bit.flac")).unwrap().to_str(), + "", + true + ], + ) + .await; + + conn.update_file(&"16bit.flac".to_string()).await.unwrap(); + + let returned = conn.get_files_toencode().await.unwrap(); + std::fs::remove_file(dbname).unwrap(); + assert!(returned.is_empty()) + } +} -- cgit v1.3.1