From be810c65933b194fbf47612a2a05818f4040af16 Mon Sep 17 00:00:00 2001 From: ducdetronquito Date: Fri, 2 Feb 2024 14:29:49 +0100 Subject: [PATCH] refactor: Handle SQLite storage in a dedicated SqliteBirthdayStorage struct --- src/birthday_store.rs | 122 ++++++++++++++++++++---------------------- src/lib.rs | 38 +++++++++++-- 2 files changed, 90 insertions(+), 70 deletions(-) diff --git a/src/birthday_store.rs b/src/birthday_store.rs index a3efaa5..97147ea 100644 --- a/src/birthday_store.rs +++ b/src/birthday_store.rs @@ -1,81 +1,73 @@ -use std::path::{Path, PathBuf}; +use std::path::Path; use anyhow::Result; use chrono::{NaiveDate, NaiveDateTime}; -use directories::ProjectDirs; use rusqlite::Connection; use crate::Birthday; -pub fn add(name: String, date: NaiveDate) -> Result<()> { - let db = get_db()?; - let timestamp = to_timestamp(date); - db.execute( - "INSERT INTO birthdays(name, date_timestamp) VALUES(?1, ?2)", - (name, timestamp), - )?; - Ok(()) +pub struct SqliteBirthdayStore { + conn: Connection, } -fn get_db_path() -> Result { - let mut data_dir = match std::env::var("BIRTHDAY_DATA") { - Ok(path) => PathBuf::from(path), - Err(_) => match ProjectDirs::from("", "", "birthday") { - Some(project_dirs) => project_dirs.data_dir().to_owned(), - None => Path::new("./").to_owned(), - }, - }; - - std::fs::create_dir_all(&data_dir)?; - data_dir.push("test.db"); - Ok(data_dir) -} +impl SqliteBirthdayStore { + pub fn open(data_dir: &Path, store_name: String) -> Result { + let mut db_path = data_dir.to_owned(); + db_path.push(store_name + ".db"); + let conn = Connection::open(db_path)?; + conn.execute( + "CREATE TABLE IF NOT EXISTS birthdays ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + date_timestamp INTEGER NOT NULL + ) STRICT", + (), + )?; + Ok(SqliteBirthdayStore { conn }) + } -pub fn get_all() -> Result> { - let db = get_db()?; - let mut statement = db.prepare("SELECT id, name, date_timestamp FROM birthdays")?; - let birthday_iter = statement.query_map([], |row| { - let id = row.get(0)?; - let name = row.get(1)?; - let timestamp = row.get(2)?; - let date = from_timestamp(timestamp); - Ok(Birthday { id, name, date }) - })?; - let birthdays = birthday_iter.collect::, rusqlite::Error>>()?; - Ok(birthdays) -} + pub fn add(&self, name: String, date: NaiveDate) -> Result<()> { + let timestamp = to_timestamp(date); + self.conn.execute( + "INSERT INTO birthdays(name, date_timestamp) VALUES(?1, ?2)", + (name, timestamp), + )?; + Ok(()) + } -pub fn remove(id: i32) -> Result> { - let db = get_db()?; - let mut statement = - db.prepare("DELETE FROM birthdays WHERE id = :id RETURNING id, name, date_timestamp")?; - let birthday_iter = statement.query_map(&[(":id", id.to_string().as_str())], |row| { - let id = row.get(0)?; - let name = row.get(1)?; - let timestamp = row.get(2)?; - let date = from_timestamp(timestamp); - Ok(Birthday { id, name, date }) - })?; - let birthdays = birthday_iter.collect::, rusqlite::Error>>()?; - if birthdays.is_empty() { - Ok(None) - } else { - Ok(Some(birthdays[0].clone())) + pub fn get_all(&self) -> Result> { + let mut statement = self + .conn + .prepare("SELECT id, name, date_timestamp FROM birthdays")?; + let birthday_iter = statement.query_map([], |row| { + let id = row.get(0)?; + let name = row.get(1)?; + let timestamp = row.get(2)?; + let date = from_timestamp(timestamp); + Ok(Birthday { id, name, date }) + })?; + let birthdays = birthday_iter.collect::, rusqlite::Error>>()?; + Ok(birthdays) } -} -fn get_db() -> Result { - let db_path = get_db_path()?; - let db = Connection::open(db_path)?; - db.execute( - "CREATE TABLE IF NOT EXISTS birthdays ( - id INTEGER PRIMARY KEY, - name TEXT NOT NULL, - date_timestamp INTEGER NOT NULL - ) STRICT", - (), - )?; - Ok(db) + pub fn remove(&self, id: i32) -> Result> { + let mut statement = self + .conn + .prepare("DELETE FROM birthdays WHERE id = :id RETURNING id, name, date_timestamp")?; + let birthday_iter = statement.query_map(&[(":id", id.to_string().as_str())], |row| { + let id = row.get(0)?; + let name = row.get(1)?; + let timestamp = row.get(2)?; + let date = from_timestamp(timestamp); + Ok(Birthday { id, name, date }) + })?; + let birthdays = birthday_iter.collect::, rusqlite::Error>>()?; + if birthdays.is_empty() { + Ok(None) + } else { + Ok(Some(birthdays[0].clone())) + } + } } fn to_timestamp(date: NaiveDate) -> i64 { diff --git a/src/lib.rs b/src/lib.rs index 1d135c3..353282c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,20 +1,46 @@ mod birthday; mod birthday_store; +use std::path::{Path, PathBuf}; + use anyhow::{bail, Result}; pub use birthday::Birthday; +use birthday_store::SqliteBirthdayStore; use chrono::{Datelike, NaiveDate}; +use directories::ProjectDirs; + +fn get_db_path() -> Result { + let mut data_dir = match std::env::var("BIRTHDAY_DATA") { + Ok(path) => PathBuf::from(path), + Err(_) => match ProjectDirs::from("", "", "birthday") { + Some(project_dirs) => project_dirs.data_dir().to_owned(), + None => Path::new("./").to_owned(), + }, + }; + + std::fs::create_dir_all(&data_dir)?; + data_dir.push("test.db"); + Ok(data_dir) +} + +fn open_store() -> Result { + let db_path = get_db_path()?; + SqliteBirthdayStore::open(db_path.as_path(), "birthday".to_string()) +} pub fn add(name: String, day: u32, month: u32, year: i32) -> Result<()> { + let store = open_store()?; let birthdate = NaiveDate::from_ymd_opt(year, month, day).expect("Invalid date"); - birthday_store::add(name, birthdate) + store.add(name, birthdate) } pub fn get_all() -> Result> { - birthday_store::get_all() + let store = open_store()?; + store.get_all() } pub fn get_next(today: NaiveDate) -> Result> { - let mut birthdays = birthday_store::get_all()?; + let store = open_store()?; + let mut birthdays = store.get_all()?; birthdays.sort_by_key(|birthday| birthday.next(today)); Ok(birthdays.into_iter().next()) } @@ -25,7 +51,8 @@ pub fn search( month: Option, day: Option, ) -> Result> { - let mut birthdays = birthday_store::get_all()?; + let store = open_store()?; + let mut birthdays = store.get_all()?; if let Some(name) = name { birthdays.retain(|birthday| birthday.name.contains(&name)); @@ -53,5 +80,6 @@ pub fn search( } pub fn forget(id: i32) -> Result> { - birthday_store::remove(id) + let store = open_store()?; + store.remove(id) }