WIP: basic wordlist rendering
This commit is contained in:
@@ -47,13 +47,13 @@ impl FileDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait WordSource {
|
pub trait WordSource {
|
||||||
fn find_by_word(&self, word: &str) -> Result<Vec<Word>, ()>;
|
fn get_all_words(&self) -> Vec<Word>;
|
||||||
fn find_by_word_and_lang(&self, word: &str, lang: &str) -> Result<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 insert_word(&self, word: Word) -> Result<(), ()>;
|
||||||
fn remove_word(&self, word_key: &str) -> Result<(), ()>;
|
fn remove_word(&self, word_key: &str) -> Result<(), ()>;
|
||||||
fn edit_word(&self, word: Word) -> Result<Word, ()>;
|
fn edit_word(&self, word: Word) -> Result<Word, ()>;
|
||||||
fn get_all_words(&self) -> Vec<Word>;
|
|
||||||
fn get_all_words_by_lang(&self, lang: &str) -> Vec<Word>;
|
|
||||||
fn normalize_word_key(word_key: &str) -> String;
|
fn normalize_word_key(word_key: &str) -> String;
|
||||||
fn normalize_lang_key(word_key: &str) -> String;
|
fn normalize_lang_key(word_key: &str) -> String;
|
||||||
}
|
}
|
||||||
@@ -67,7 +67,31 @@ fn fuzzy_compare(a: &str, b: &str) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WordSource for FileDatabase {
|
impl WordSource for FileDatabase {
|
||||||
fn find_by_word(&self, word: &str) -> Result<Vec<Word>, ()> {
|
fn get_all_words(&self) -> Vec<Word> {
|
||||||
|
let statement = self
|
||||||
|
.connection
|
||||||
|
.prepare("select key, description, lang from word");
|
||||||
|
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
|
let statement = self
|
||||||
.connection
|
.connection
|
||||||
.prepare("SELECT key, description, lang FROM word");
|
.prepare("SELECT key, description, lang FROM word");
|
||||||
@@ -87,13 +111,35 @@ impl WordSource for FileDatabase {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return Err(());
|
|
||||||
}
|
}
|
||||||
Ok(word_list)
|
word_list
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_by_word_and_lang(&self, word: &str, lang: &str) -> Result<Vec<Word>, ()> {
|
fn find_by_lang(&self, lang: &str) -> Vec<Word> {
|
||||||
|
let statement = self
|
||||||
|
.connection
|
||||||
|
.prepare("select key, description, lang from word where lang = ?1");
|
||||||
|
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
|
let statement = self
|
||||||
.connection
|
.connection
|
||||||
.prepare("SELECT key, description, lang FROM word where lang = ?1");
|
.prepare("SELECT key, description, lang FROM word where lang = ?1");
|
||||||
@@ -113,10 +159,8 @@ impl WordSource for FileDatabase {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return Err(());
|
|
||||||
}
|
}
|
||||||
Ok(word_list)
|
word_list
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_word(&self, word: Word) -> Result<(), ()> {
|
fn insert_word(&self, word: Word) -> Result<(), ()> {
|
||||||
@@ -170,54 +214,6 @@ impl WordSource for FileDatabase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_all_words(&self) -> Vec<Word> {
|
|
||||||
let statement = self
|
|
||||||
.connection
|
|
||||||
.prepare("select key, description, lang from word");
|
|
||||||
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 get_all_words_by_lang(&self, lang: &str) -> Vec<Word> {
|
|
||||||
let statement = self
|
|
||||||
.connection
|
|
||||||
.prepare("select key, description, lang from word where lang = ?1");
|
|
||||||
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 normalize_word_key(word_key: &str) -> String {
|
fn normalize_word_key(word_key: &str) -> String {
|
||||||
// TODO: real normalization, not just lowercase, but keep in mind it must be kept as the
|
// 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
|
// final and real word -> Maybe full refactor to include a word id
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::modules::source::FileDatabase;
|
use crate::modules::source::{FileDatabase, WordSource};
|
||||||
use crossterm::event::{
|
use crossterm::event::{
|
||||||
self,
|
self,
|
||||||
Event::{self, Key},
|
Event::{self, Key},
|
||||||
@@ -10,7 +10,7 @@ use ratatui::{
|
|||||||
text::Text,
|
text::Text,
|
||||||
widgets::{Block, Borders, Padding, Paragraph},
|
widgets::{Block, Borders, Padding, Paragraph},
|
||||||
};
|
};
|
||||||
use std::io;
|
use std::{io, ops::Index};
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub enum UiState {
|
pub enum UiState {
|
||||||
@@ -25,7 +25,7 @@ pub enum UiState {
|
|||||||
pub struct App {
|
pub struct App {
|
||||||
state: UiState,
|
state: UiState,
|
||||||
file_database: FileDatabase,
|
file_database: FileDatabase,
|
||||||
scroll_position: u32,
|
scroll_position: usize,
|
||||||
word_filter: Option<String>,
|
word_filter: Option<String>,
|
||||||
lang_filter: Option<String>,
|
lang_filter: Option<String>,
|
||||||
exit: bool,
|
exit: bool,
|
||||||
@@ -88,7 +88,8 @@ impl App {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
frame.render_widget(
|
frame.render_widget(
|
||||||
Paragraph::new(word_filter).block(Block::new().title("Word").borders(Borders::ALL)),
|
Paragraph::new(word_filter.clone())
|
||||||
|
.block(Block::new().title("Word").borders(Borders::ALL)),
|
||||||
filters_layout[0],
|
filters_layout[0],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -104,15 +105,31 @@ impl App {
|
|||||||
filters_layout[1],
|
filters_layout[1],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// FIXME: this is wrong, the list must reside in memory and only be updated when required,
|
||||||
|
// this makes a new call every frame, insane
|
||||||
|
let word_list = {
|
||||||
|
if let Some(w_filter) = &self.word_filter
|
||||||
|
&& let Some(l_filter) = &self.lang_filter
|
||||||
|
{
|
||||||
|
self.file_database
|
||||||
|
.find_by_word_and_lang(&word_filter, &l_filter)
|
||||||
|
} else if let Some(l_filter) = &self.lang_filter {
|
||||||
|
self.file_database.find_by_lang(&l_filter)
|
||||||
|
} else {
|
||||||
|
self.file_database.get_all_words()
|
||||||
|
}
|
||||||
|
};
|
||||||
frame.render_widget(
|
frame.render_widget(
|
||||||
Paragraph::new("Word list").block(Block::new().borders(Borders::ALL).title("Entries")),
|
Paragraph::new("Word list").block(Block::new().borders(Borders::ALL).title("Entries")),
|
||||||
words_layout[0],
|
words_layout[0],
|
||||||
);
|
);
|
||||||
|
let selected_word = word_list.into_iter().nth(self.scroll_position);
|
||||||
frame.render_widget(
|
if let Some(w) = selected_word {
|
||||||
Paragraph::new("Selected word description").block(Block::new().borders(Borders::ALL)),
|
frame.render_widget(
|
||||||
words_layout[1],
|
Paragraph::new(w.description).block(Block::new().borders(Borders::ALL)),
|
||||||
);
|
words_layout[1],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let help_text = Text::raw("(f)ilter | lan(g) | (d)elete | (e)dit | (a)dd | (q)uit ")
|
let help_text = Text::raw("(f)ilter | lan(g) | (d)elete | (e)dit | (a)dd | (q)uit ")
|
||||||
.style(Style::new().fg(Color::DarkGray));
|
.style(Style::new().fg(Color::DarkGray));
|
||||||
@@ -137,10 +154,20 @@ impl App {
|
|||||||
self.word_filter = Some(c.to_string());
|
self.word_filter = Some(c.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
UiState::FILTERLANG => {
|
||||||
|
if let Some(mut prev_lang) = self.lang_filter.clone() {
|
||||||
|
prev_lang.push(c);
|
||||||
|
self.lang_filter = Some(prev_lang);
|
||||||
|
} else {
|
||||||
|
self.lang_filter = Some(c.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
// Quit
|
||||||
if c == 'q' || c == 'x' {
|
if c == 'q' || c == 'x' {
|
||||||
return Err(io::Error::last_os_error()); // FIXME: this is not right
|
return Err(io::Error::last_os_error()); // FIXME: this is not right
|
||||||
}
|
}
|
||||||
|
// State change
|
||||||
if c == 'f' || c == '/' {
|
if c == 'f' || c == '/' {
|
||||||
self.state = UiState::FILTERWORD;
|
self.state = UiState::FILTERWORD;
|
||||||
}
|
}
|
||||||
@@ -156,8 +183,13 @@ impl App {
|
|||||||
if c == 'a' {
|
if c == 'a' {
|
||||||
self.state = UiState::ADD;
|
self.state = UiState::ADD;
|
||||||
}
|
}
|
||||||
// TODO: remove, just for testing
|
// Movement
|
||||||
self.lang_filter = Some(format!("{0} >> {1}", c, key.code));
|
if c == 'j' {
|
||||||
|
self.scroll_position -= 1;
|
||||||
|
}
|
||||||
|
if c == 'k' {
|
||||||
|
self.scroll_position += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user