230 lines
7.6 KiB
Rust
230 lines
7.6 KiB
Rust
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<Self, String> {
|
|
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<Word>;
|
|
fn find_by_word(&self, word: &str) -> Vec<Word>;
|
|
fn find_by_lang(&self, lang: &str) -> Vec<Word>;
|
|
fn find_by_word_and_lang(&self, word: &str, lang: &str) -> Vec<Word>;
|
|
fn insert_word(&self, word: Word) -> Result<(), ()>;
|
|
fn remove_word(&self, word_key: &str) -> Result<(), ()>;
|
|
fn edit_word(&self, word: Word, key: &str) -> Result<Word, ()>;
|
|
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<Word> {
|
|
let statement = self
|
|
.connection
|
|
.prepare("select key, description, lang from word ORDER BY key ASC");
|
|
let mut word_list: Vec<Word> = 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<Word> {
|
|
let statement = self
|
|
.connection
|
|
.prepare("SELECT key, description, lang FROM word ORDER BY key ASC");
|
|
let mut word_list: Vec<Word> = 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<Word> {
|
|
let statement = self
|
|
.connection
|
|
.prepare("select key, description, lang from word where lang = ?1 ORDER BY key ASC");
|
|
let mut word_list: Vec<Word> = 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<Word> {
|
|
let statement = self
|
|
.connection
|
|
.prepare("SELECT key, description, lang FROM word where lang = ?1 ORDER BY key ASC");
|
|
let mut word_list: Vec<Word> = 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<Word, ()> {
|
|
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()
|
|
}
|
|
}
|