diff --git a/src/main.rs b/src/main.rs index dc240dc..a493ac0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,9 @@ use std::*; -use rusqlite::{Connection, OpenFlags}; - mod modules; use modules::config::{Config, read_config}; -use crate::modules::source::{FileDatabase, WordSource}; +use crate::modules::source::{FileDatabase, Word, WordSource}; const DEFAULT_CONFIG_PATH: &'static str = "./src/assets/wordsmith_default.conf"; @@ -32,51 +30,33 @@ fn main() { let config = read_config(DEFAULT_CONFIG_PATH, default_config); // TODO: read config from param - // TODO: CREATE FILE.db IF DOES NOT EXISTS - // TODO: CREATE TABLE IF DOES NOT EXISTS - let connection = Connection::open(&config.database_path).unwrap(); - let word_source = FileDatabase { - connection: &connection, - }; + let word_source_result = FileDatabase::new(&config.database_path); - // NOTE: testing init - // connection - // .execute( - // "CREATE TABLE word ( - // id INT AUTO_INCREMENT PRIMARY KEY, - // key TEXT NOT NULL, - // description TEXT NOT NULL - // )", - // (), // empty list of parameters. - // ) - // .unwrap(); - connection - .execute( - "INSERT INTO word (key, description) VALUES (?1, ?2)", - ("test", "a description"), - ) - .unwrap(); - connection - .execute( - "INSERT INTO word (key, description) VALUES (?1, ?2)", - ("another", "a description"), - ) - .unwrap(); - connection - .execute( - "INSERT INTO word (key, description) VALUES (?1, ?2)", - ("test yet", "a description"), - ) - .unwrap(); - connection - .execute( - "INSERT INTO word (key, description) VALUES (?1, ?2)", - ("yes yes", "a description"), - ) - .unwrap(); - - if let Ok(w_list) = word_source.find_by_word("test") { - println!("Words! {:?}", w_list); + match word_source_result { + Ok(word_source) => { + if let Ok(w_list) = word_source.find_by_word("test") { + println!("Words! {:?}", w_list); + } + let _ = word_source.edit_word(Word("test".to_string(), "This is a test".to_string())); + let _ = word_source.edit_word(Word("test2".to_string(), "This is a test".to_string())); + let _ = word_source.insert_word(Word("test".to_string(), "This is a test".to_string())); + let _ = + word_source.insert_word(Word("test3".to_string(), "This is a test".to_string())); + if let Ok(w_list) = word_source.find_by_word("test") { + println!("Words! {:?}", w_list); + } + let _ = word_source.remove_word("test2"); + if let Ok(w_list) = word_source.find_by_word("test") { + println!("Words! {:?}", w_list); + } + let _ = word_source.remove_word("test"); + if let Ok(w_list) = word_source.find_by_word("test") { + println!("Words! {:?}", w_list); + } + } + Err(word_source_error) => { + println!("!! {0}", word_source_error); + } } println!("Hello, world! {:?}", config); diff --git a/src/modules/source/mod.rs b/src/modules/source/mod.rs index e050ed8..a929a38 100644 --- a/src/modules/source/mod.rs +++ b/src/modules/source/mod.rs @@ -1,30 +1,65 @@ -use rusqlite::Connection; +use rusqlite::{Connection, params}; #[derive(Clone, Debug)] -pub struct Word(String, String); +pub struct Word(pub String, pub String); -pub struct FileDatabase<'a> { - pub connection: &'a Connection, +pub struct FileDatabase { + connection: Connection, +} + +impl FileDatabase { + pub fn new(path: &str) -> Result { + let connection_result = Connection::open(path); + match connection_result { + Ok(connection) => { + let file_database = Self { + connection: connection, + }; + file_database.init(); + return Ok(file_database); + } + Err(error) => { + if let Some(error_code) = error.sqlite_error_code() { + return Err(format!("{:?}", error_code)); + } + } + } + + return Err(format!("Error while accessing the database file.")); + } + + fn init(&self) { + self.connection + .execute( + "CREATE TABLE IF NOT EXISTS word ( + key TEXT PRIMARY KEY, + description TEXT NOT NULL + )", + (), // empty list of parameters. + ) + .unwrap(); + } } pub trait WordSource { fn find_by_word(&self, word: &str) -> Result, ()>; - fn insert_word(&mut self, word: Word) -> Result<(), ()>; - fn remove_word(&self, word_id: i32) -> Result<(), ()>; - fn edit_word(&mut self, word: Word) -> Result<(), ()>; + fn insert_word(&self, word: Word) -> Result<(), ()>; + fn remove_word(&self, word_key: &str) -> Result<(), ()>; + fn edit_word(&self, word: Word) -> Result; fn get_all_words(&self) -> Vec; + fn normalize_word_key(word_key: &str) -> String; } fn fuzzy_compare(a: &str, b: &str) -> bool { - if a.eq(b) { + if a.eq(b) || a.contains(b) || b.contains(a) { return true; } false } -impl<'a> WordSource for FileDatabase<'a> { +impl WordSource for FileDatabase { fn find_by_word(&self, word: &str) -> Result, ()> { - let statement = self.connection.prepare("select key, description from word"); + let statement = self.connection.prepare("SELECT key, description FROM word"); let mut word_list: Vec = vec![]; if let Ok(mut query) = statement { @@ -32,7 +67,7 @@ impl<'a> WordSource for FileDatabase<'a> { while let Some(row) = rows.next().unwrap() { let found_word: String = row.get(0).unwrap(); if fuzzy_compare(word, &found_word) { - let found_word_description: String = row.get(0).unwrap(); + let found_word_description: String = row.get(1).unwrap(); word_list.push(Word(found_word, found_word_description)); } } @@ -42,23 +77,47 @@ impl<'a> WordSource for FileDatabase<'a> { Ok(word_list) } - fn insert_word(&mut self, word: Word) -> Result<(), ()> { + fn insert_word(&self, word: Word) -> Result<(), ()> { let result = self.connection.execute( "INSERT INTO word (key, description) VALUES (?1, ?2)", - (word.0, word.1), + (&Self::normalize_word_key(&word.0), &word.1), ); - if let Ok(_) = result { - return Ok(()); + if let Ok(count) = result { + if count > 0 { + return Ok(()); + } } Err(()) } - fn edit_word(&mut self, word: Word) -> Result<(), ()> { - Ok(()) + fn edit_word(&self, word: Word) -> Result { + let result = self.connection.execute( + "UPDATE word SET key = ?1, description = ?2 WHERE key = ?1", + (&Self::normalize_word_key(&word.0), &word.1), + ); + match result { + Ok(_) => { + return Ok(word); + } + Err(_) => return Err(()), + } } - fn remove_word(&self, word_id: i32) -> Result<(), ()> { - Ok(()) + fn remove_word(&self, word_key: &str) -> Result<(), ()> { + let result = self.connection.execute( + "DELETE FROM word WHERE key = ?1", + params![Self::normalize_word_key(word_key)], + ); + match result { + Ok(response) => { + if response > 0 { + return Ok(()); + } else { + return Err(()); + } + } + Err(_) => return Err(()), + } } fn get_all_words(&self) -> Vec { @@ -70,11 +129,17 @@ impl<'a> WordSource for FileDatabase<'a> { let mut rows = query.query([]).unwrap(); while let Some(row) = rows.next().unwrap() { let found_word: String = row.get(0).unwrap(); - let found_word_description: String = row.get(0).unwrap(); + let found_word_description: String = row.get(1).unwrap(); word_list.push(Word(found_word, found_word_description)); } } word_list } + + fn normalize_word_key(word_key: &str) -> String { + // TODO: real normalization, not just lowercase, but keep in mind it must be kept as the + // final and real word -> Maybe full refactor to include a word id + word_key.to_lowercase().to_string() + } }