use rusqlite::{Connection, params}; #[derive(Clone, Debug)] pub struct Word { pub key: String, pub description: String, pub lang: String, } 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, lang VARCHAR(2) NOT NULL )", (), // empty list of parameters. ) .unwrap(); } } pub trait WordSource { fn get_all_words(&self) -> Vec; fn find_by_word(&self, word: &str) -> Vec; fn find_by_lang(&self, lang: &str) -> Vec; fn find_by_word_and_lang(&self, word: &str, lang: &str) -> Vec; fn insert_word(&self, word: Word) -> Result<(), ()>; fn remove_word(&self, word_key: &str) -> Result<(), ()>; fn edit_word(&self, word: Word, key: &str) -> Result; fn normalize_word_key(word_key: &str) -> String; fn normalize_lang_key(word_key: &str) -> String; } // TODO: improve fuzzy fn fuzzy_compare(a: &str, b: &str) -> bool { if a.eq(b) || a.contains(b) || b.contains(a) { return true; } false } impl WordSource for FileDatabase { fn get_all_words(&self) -> Vec { let statement = self .connection .prepare("select key, description, lang from word ORDER BY key ASC"); let mut word_list: Vec = vec![]; // TODO: make this pretty with functional programming if let Ok(mut query) = statement { 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(1).unwrap(); let found_word_lang: String = row.get(2).unwrap(); word_list.push(Word { key: found_word, description: found_word_description, lang: found_word_lang, }); } } word_list } fn find_by_word(&self, word: &str) -> Vec { let statement = self .connection .prepare("SELECT key, description, lang FROM word ORDER BY key ASC"); let mut word_list: Vec = vec![]; if let Ok(mut query) = statement { let mut rows = query.query([]).unwrap(); 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(1).unwrap(); let found_word_lang: String = row.get(2).unwrap(); word_list.push(Word { key: found_word, description: found_word_description, lang: found_word_lang, }); } } } word_list } fn find_by_lang(&self, lang: &str) -> Vec { let statement = self .connection .prepare("select key, description, lang from word where lang = ?1 ORDER BY key ASC"); let mut word_list: Vec = vec![]; // TODO: make this pretty with functional programming if let Ok(mut query) = statement { let mut rows = query.query([lang]).unwrap(); while let Some(row) = rows.next().unwrap() { let found_word: String = row.get(0).unwrap(); let found_word_description: String = row.get(1).unwrap(); let found_word_lang: String = row.get(2).unwrap(); word_list.push(Word { key: found_word, description: found_word_description, lang: found_word_lang, }); } } word_list } fn find_by_word_and_lang(&self, word: &str, lang: &str) -> Vec { let statement = self .connection .prepare("SELECT key, description, lang FROM word where lang = ?1 ORDER BY key ASC"); let mut word_list: Vec = vec![]; if let Ok(mut query) = statement { let mut rows = query.query([lang]).unwrap(); 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(1).unwrap(); let found_word_lang: String = row.get(2).unwrap(); word_list.push(Word { key: found_word, description: found_word_description, lang: found_word_lang, }); } } } word_list } fn insert_word(&self, word: Word) -> Result<(), ()> { let result = self.connection.execute( "INSERT INTO word (key, description, lang) VALUES (?1, ?2, ?3)", ( &Self::normalize_word_key(&word.key), &word.description, &Self::normalize_lang_key(&word.lang), ), ); if let Ok(count) = result { if count > 0 { return Ok(()); } } Err(()) } fn edit_word(&self, word: Word, key: &str) -> Result { let result = self.connection.execute( "UPDATE word SET key = ?1, description = ?2, lang = ?3 WHERE key = ?4", ( &Self::normalize_word_key(&word.key), &word.description, &Self::normalize_lang_key(&word.lang), key, ), ); match result { Ok(_) => { return Ok(word); } Err(_) => return Err(()), } } 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 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() } fn normalize_lang_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() } }