From 3e5d3fa5552f6ce58d7c9619a3a979c774f705ed Mon Sep 17 00:00:00 2001 From: Daniel Heras Quesada Date: Sun, 2 Aug 2026 18:04:04 +0200 Subject: [PATCH] WIP: basic wordlist rendering --- src/modules/source/mod.rs | 116 ++++++++++++++++++-------------------- src/modules/ui/app.rs | 54 ++++++++++++++---- 2 files changed, 99 insertions(+), 71 deletions(-) diff --git a/src/modules/source/mod.rs b/src/modules/source/mod.rs index 007d3ae..e72118c 100644 --- a/src/modules/source/mod.rs +++ b/src/modules/source/mod.rs @@ -47,13 +47,13 @@ impl FileDatabase { } pub trait WordSource { - fn find_by_word(&self, word: &str) -> Result, ()>; - fn find_by_word_and_lang(&self, word: &str, lang: &str) -> Result, ()>; + 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) -> Result; - fn get_all_words(&self) -> Vec; - fn get_all_words_by_lang(&self, lang: &str) -> Vec; fn normalize_word_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 { - fn find_by_word(&self, word: &str) -> Result, ()> { + fn get_all_words(&self) -> Vec { + let statement = self + .connection + .prepare("select key, description, lang from word"); + 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"); @@ -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, ()> { + fn find_by_lang(&self, lang: &str) -> Vec { + let statement = self + .connection + .prepare("select key, description, lang from word where lang = ?1"); + 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"); @@ -113,10 +159,8 @@ impl WordSource for FileDatabase { }); } } - } else { - return Err(()); } - Ok(word_list) + word_list } fn insert_word(&self, word: Word) -> Result<(), ()> { @@ -170,54 +214,6 @@ impl WordSource for FileDatabase { } } - fn get_all_words(&self) -> Vec { - let statement = self - .connection - .prepare("select key, description, lang from word"); - 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 get_all_words_by_lang(&self, lang: &str) -> Vec { - let statement = self - .connection - .prepare("select key, description, lang from word where lang = ?1"); - 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 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 diff --git a/src/modules/ui/app.rs b/src/modules/ui/app.rs index 88b299d..6ed03d1 100644 --- a/src/modules/ui/app.rs +++ b/src/modules/ui/app.rs @@ -1,4 +1,4 @@ -use crate::modules::source::FileDatabase; +use crate::modules::source::{FileDatabase, WordSource}; use crossterm::event::{ self, Event::{self, Key}, @@ -10,7 +10,7 @@ use ratatui::{ text::Text, widgets::{Block, Borders, Padding, Paragraph}, }; -use std::io; +use std::{io, ops::Index}; #[derive(PartialEq)] pub enum UiState { @@ -25,7 +25,7 @@ pub enum UiState { pub struct App { state: UiState, file_database: FileDatabase, - scroll_position: u32, + scroll_position: usize, word_filter: Option, lang_filter: Option, exit: bool, @@ -88,7 +88,8 @@ impl App { } }; 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], ); @@ -104,15 +105,31 @@ impl App { 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( Paragraph::new("Word list").block(Block::new().borders(Borders::ALL).title("Entries")), words_layout[0], ); - - frame.render_widget( - Paragraph::new("Selected word description").block(Block::new().borders(Borders::ALL)), - words_layout[1], - ); + let selected_word = word_list.into_iter().nth(self.scroll_position); + if let Some(w) = selected_word { + frame.render_widget( + 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 ") .style(Style::new().fg(Color::DarkGray)); @@ -137,10 +154,20 @@ impl App { 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' { return Err(io::Error::last_os_error()); // FIXME: this is not right } + // State change if c == 'f' || c == '/' { self.state = UiState::FILTERWORD; } @@ -156,8 +183,13 @@ impl App { if c == 'a' { self.state = UiState::ADD; } - // TODO: remove, just for testing - self.lang_filter = Some(format!("{0} >> {1}", c, key.code)); + // Movement + if c == 'j' { + self.scroll_position -= 1; + } + if c == 'k' { + self.scroll_position += 1; + } } } }