diff --git a/TODO.md b/TODO.md index aaa048f..5b758c3 100644 --- a/TODO.md +++ b/TODO.md @@ -6,4 +6,4 @@ - https://github.com/rusqlite/rusqlite - https://docs.rs/rusqlite/0.29.0/rusqlite/backup/index.html - [ ] Restore SQLite backup -- [ ] Update backup file to Google Drive +- [x] Update backup file to ~Google Drive~ **Cloudflare R2** diff --git a/backup.db b/backup.db deleted file mode 100644 index b006ca8..0000000 Binary files a/backup.db and /dev/null differ diff --git a/src/argument.rs b/src/argument.rs index 7d81596..e80bc83 100644 --- a/src/argument.rs +++ b/src/argument.rs @@ -1,18 +1,20 @@ -use anyhow::Result; +use anyhow::{bail, Result}; + +use crate::errors::SqliteBackupError; pub struct Argument { pub source_path: String, } impl Argument { - pub fn build(args: &[String]) -> Result { + pub fn build(args: &[String]) -> Result { if let Some(source_path) = args.get(1) { let argument = Self { source_path: source_path.clone(), }; Ok(argument) } else { - Err("No source path provided.") + bail!(SqliteBackupError::NoSourceFileError); } } } diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..5038877 --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,20 @@ +use std::{error::Error, fmt::Display}; + +#[derive(Debug)] +pub enum SqliteBackupError { + SourceFileError(String), + NoSourceFileError, +} + +impl Error for SqliteBackupError {} + +impl Display for SqliteBackupError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + SqliteBackupError::SourceFileError(source) => { + write!(f, "Source file error: {}", source) + } + SqliteBackupError::NoSourceFileError => write!(f, "No source path provided."), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 95669dc..e460f9d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,12 @@ pub mod argument; pub mod config; +pub mod errors; pub mod uploader; -use std::time::Duration; +use std::{ffi::OsStr, path::Path, time::Duration}; -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; +use errors::SqliteBackupError; use rusqlite::{ backup::{self, Progress}, Connection, @@ -14,6 +16,44 @@ pub trait Backup { fn backup(&self) -> Result<()>; } +pub struct SqliteSourceFile<'a> { + pub path: &'a Path, + pub filename: &'a str, + pub db_name: &'a str, + pub db_extension: &'a str, +} + +impl<'a> SqliteSourceFile<'a> { + pub fn from(src_path: &'a str) -> Result { + let path = Path::new(src_path); + let filename = convert_os_str_result_to_str(path.file_name())?; + let db_name = convert_os_str_result_to_str(path.file_stem())?; + let db_extension = convert_os_str_result_to_str(path.extension())?; + + Ok(Self { + path, + filename, + db_name, + db_extension, + }) + } +} + +fn convert_os_str_result_to_str(result: Option<&OsStr>) -> Result<&str> { + if result.is_none() { + bail!(SqliteBackupError::SourceFileError( + "failed to parse source file".to_string() + )); + } + if let Some(s) = result.unwrap().to_str() { + return Ok(s); + } + + bail!(SqliteBackupError::SourceFileError( + "failed to convert source file to str".to_string() + )); +} + pub struct SqliteBackup { src_conn: rusqlite::Connection, dest: String, diff --git a/src/main.rs b/src/main.rs index 5ee1c7f..615ac46 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,37 +4,33 @@ use sqlite_backup::{ argument, config::Config, uploader::{R2Uploader, Uploader}, - Backup, SqliteBackup, + Backup, SqliteBackup, SqliteSourceFile, }; -use std::{env, path::Path}; +use std::env; #[tokio::main] async fn main() -> Result<()> { + let cfg = Config::load().context("load env vars")?; let args = env::args().collect::>(); match argument::Argument::build(&args) { - Ok(arg) => run(&arg).await?, + Ok(arg) => run(&arg, &cfg).await?, Err(err) => eprintln!("Application Error: {}", err), } + println!("Done"); + Ok(()) } -async fn run(arg: &argument::Argument) -> Result<()> { - // load config/env - let config = Config::load().context("load env vars")?; - - // tempfile - // TODO: Remove unwrap - let src_path = Path::new(arg.source_path.as_str()); - let file_name = src_path.file_name().unwrap(); - let db_name = src_path.file_stem().unwrap().to_str().unwrap(); - let db_extension = src_path.extension().unwrap().to_str().unwrap(); +async fn run(arg: &argument::Argument, cfg: &Config) -> Result<()> { + // create temp dir let tmp_dir = tempfile::tempdir()?; - let dest = tmp_dir.path().join(file_name); // backup data - let src_conn = Connection::open(arg.source_path.clone()).context("create source connection")?; + let src_file = SqliteSourceFile::from(arg.source_path.as_str()).context("parse source path")?; + let src_conn = Connection::open(src_file.path).context("create source connection")?; + let dest = tmp_dir.path().join(src_file.filename); SqliteBackup::new(src_conn, dest.display().to_string(), |p| { println!( "---Progress---- pagecount: {}, remaining: {}", @@ -45,8 +41,14 @@ async fn run(arg: &argument::Argument) -> Result<()> { .context("backup source to destination")?; // upload - let uploader = R2Uploader::new(&config).await; - uploader.upload_object(dest, db_name, db_extension).await?; + let uploader = R2Uploader::new(cfg).await; + uploader + .upload_object( + dest, + format!("sqlite__{}", src_file.db_name).as_str(), + src_file.db_extension, + ) + .await?; // close temp dir tmp_dir.close()?;