aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan Reiner <nathan@nathanreiner.xyz>2023-07-26 21:19:59 +0200
committerNathan Reiner <nathan@nathanreiner.xyz>2023-07-26 21:19:59 +0200
commit61ac9375b4a35878576ac2727c5210cd9fc51a92 (patch)
tree5e147ad0c60b35a83baab3f84143d3c40a09cdcd
parent7679fcc3a0c4fadea00a1a320938851b1518028d (diff)
add gtk3 gui
-rw-r--r--src/gui/generate.rs154
-rw-r--r--src/gui/icon.svg67
-rw-r--r--src/gui/load.rs63
-rw-r--r--src/gui/mod.rs22
-rw-r--r--src/gui/search.rs133
-rw-r--r--src/gui/state.rs38
-rw-r--r--src/gui/style.css18
-rw-r--r--src/gui/welcome.rs51
-rw-r--r--src/index.rs21
-rw-r--r--src/main.rs5
10 files changed, 544 insertions, 28 deletions
diff --git a/src/gui/generate.rs b/src/gui/generate.rs
index 9fe9e3f..a7eb9e5 100644
--- a/src/gui/generate.rs
+++ b/src/gui/generate.rs
@@ -1,6 +1,13 @@
use gtk::prelude::*;
+use gtk::glib;
use super::state::{View, ViewManager};
+use std::sync::Arc;
+use std::sync::Mutex;
use std::rc::Rc;
+use std::thread;
+
+use crate::index::Index;
+use crate::index::GenState;
pub struct Generate {
vm : Rc<ViewManager>
@@ -15,16 +22,149 @@ impl View for Generate {
self.vm = vm
}
- fn make_current(&self) -> gtk::Box {
- let center = gtk::Box::new(gtk::Orientation::Vertical, 10);
- let main = gtk::Box::new(gtk::Orientation::Horizontal, 10);
- let label = gtk::Label::new(Some("Generate"));
+ fn make_current(&self) -> Option<gtk::Box> {
+ let target_dir = Arc::new(Mutex::new(String::new()));
+ let index_file = Arc::new(Mutex::new(String::new()));
+ let (index_tx, index_rx) = std::sync::mpsc::channel();
+ let load_next = Rc::new(Mutex::new(false));
+
+ let splash = gtk::Window::new(gtk::WindowType::Popup);
+ splash.set_type_hint(gtk::gdk::WindowTypeHint::Splashscreen);
+ splash.set_decorated(false);
+ splash.set_position(gtk::WindowPosition::Center);
+ splash.set_resizable(false);
+ splash.set_size_request(600, 200);
+
+ let main = gtk::Box::new(gtk::Orientation::Vertical, 10);
+ main.set_widget_name("generate");
+ let title = gtk::Label::new(Some("Generate Index"));
+ title.set_widget_name("title");
+
+ let step = gtk::Label::new(Some("Choose A Target Directory"));
+ step.set_halign(gtk::Align::Start);
+ let btn = gtk::Button::with_label("Choose");
+ btn.set_sensitive(false);
+ let header = gtk::Box::new(gtk::Orientation::Horizontal, 0);
+
+ let pick = gtk::FileChooserWidget::builder()
+ .create_folders(false)
+ .action(gtk::FileChooserAction::SelectFolder)
+ .build();
+
+ header.pack_start(&step, true, true, 10);
+ header.pack_start(&btn, false, false, 10);
+
+ main.pack_start(&title, false, false, 0);
+ main.pack_start(&header, false, false, 0);
+ main.pack_start(&pick, true, true, 0);
+
+ splash.add(&main);
+
+ pick.connect_selection_changed(glib::clone!(@weak btn => move |pick| {
+ if pick.file().is_some() || pick.current_name().is_some() {
+ btn.set_sensitive(true);
+ } else {
+ btn.set_sensitive(false);
+ }
+ }));
+
+ {
+ let target_dir = Arc::clone(&target_dir);
+ let index_file = Arc::clone(&index_file);
+ let load_next = Rc::clone(&load_next);
+ btn.connect_clicked(glib::clone!(
+ @weak splash,
+ @weak header,
+ @weak step,
+ @weak pick,
+ @weak main => move |btn| {
+ let td = { target_dir.lock().unwrap().clone() };
+
+ if td.is_empty() {
+ let uri = pick.uri().unwrap();
+ let path = uri.as_str().trim_start_matches("file://");
+ let mut target_dir = target_dir.lock().unwrap();
+ *target_dir = path.to_string();
+ step.set_text("Create A Index File");
+ btn.set_label("Create");
+ pick.set_action(gtk::FileChooserAction::Save);
+ } else {
+ let mut path = pick.current_folder().unwrap().to_str().unwrap().to_string();
+ path += "/";
+ path += pick.current_name().unwrap().to_string().as_str();
+ *index_file.lock().unwrap() = path;
+
+ main.remove(&header);
+ main.remove(&pick);
+
+ let progress = gtk::ProgressBar::builder()
+ .ellipsize(gtk::pango::EllipsizeMode::Middle)
+ .show_text(true)
+ .text("Generating")
+ .build();
+
+ progress.set_margin(20);
+ main.pack_start(&progress, true, false, 0);
+
+ let (tx, rx) = glib::MainContext::channel(glib::Priority::default());
+ {
+ let target_dir = Arc::clone(&target_dir);
+ let index_file = Arc::clone(&index_file);
+ let index_tx = index_tx.clone();
+ thread::spawn(move || {
+ let path = target_dir.lock().unwrap().to_string();
+ let idx = Index::generate(path.as_str(), |s, p| {
+ let text = match s {
+ GenState::Fetching => { "Fetching" }
+ GenState::Parsing => { "Parsing" }
+ GenState::Merging => { "Merging" }
+ };
+ tx.send(Some((text, p))).ok();
+ });
+ tx.send(Some(("Saving", 100))).ok();
+ idx.save(index_file.lock().unwrap().to_string());
+ tx.send(None).ok();
+ index_tx.send(idx)
+ });
+ }
+
+ let load_next = Rc::clone(&load_next);
+ rx.attach(None, glib::clone!(
+ @weak splash,
+ @weak progress,
+ @weak main => @default-return glib::Continue(false), move |value| match value {
+ Some((s, p)) => {
+ progress.pulse();
+ progress.set_fraction(f64::from(p) / 100.0);
+ progress.set_text(Some(s));
+ glib::Continue(true)
+ }
+ None => {
+ splash.close();
+ *load_next.lock().unwrap() = true;
+ glib::Continue(false)
+ }
+ }));
+
+ main.show_all();
+ }
+ }));
+ }
- main.pack_start(&label, false, false, 0);
+ let vm = Rc::clone(&self.vm);
+ splash.connect_hide(move |_| {
+ if *load_next.lock().unwrap() {
+ let idx = index_rx.recv().unwrap();
+ vm.set_index(idx);
+ vm.set_current_view("search");
+ } else if vm.get_current_view() == "generate" {
+ vm.set_current_view("welcome")
+ }
+ });
- center.pack_start(&main, true, false, 0);
+ splash.show_all();
- center
+ None
}
}
diff --git a/src/gui/icon.svg b/src/gui/icon.svg
new file mode 100644
index 0000000..0dd19c0
--- /dev/null
+++ b/src/gui/icon.svg
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="48"
+ height="48"
+ viewBox="0 0 12.7 12.7"
+ version="1.1"
+ id="svg5"
+ inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
+ sodipodi:docname="icon.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview7"
+ pagecolor="#505050"
+ bordercolor="#eeeeee"
+ borderopacity="1"
+ inkscape:showpageshadow="0"
+ inkscape:pageopacity="0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#505050"
+ inkscape:document-units="mm"
+ showgrid="false"
+ inkscape:zoom="3.6183075"
+ inkscape:cx="34.961098"
+ inkscape:cy="19.622434"
+ inkscape:window-width="1894"
+ inkscape:window-height="1011"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1" />
+ <defs
+ id="defs2" />
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <circle
+ style="fill:#244c66;fill-opacity:1;stroke:none;stroke-width:0.758009;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
+ id="circle7365-3-6"
+ cx="6.3499999"
+ cy="6.3499999"
+ r="6.0858917" />
+ <circle
+ style="fill:#407d6c;fill-opacity:1;stroke:none;stroke-width:0.605315;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
+ id="path4064-5"
+ cx="5.1240602"
+ cy="6.6535206"
+ r="4.859952" />
+ <circle
+ style="fill:#7db290;fill-opacity:1;stroke:none;stroke-width:0.473211;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
+ id="path4064"
+ cx="4.6072431"
+ cy="7.651989"
+ r="3.7993114" />
+ <circle
+ style="fill:#b5c7cc;fill-opacity:1;stroke:none;stroke-width:0.336748;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
+ id="circle7365"
+ cx="4.4472618"
+ cy="8.6169968"
+ r="2.7036855" />
+ </g>
+</svg>
diff --git a/src/gui/load.rs b/src/gui/load.rs
new file mode 100644
index 0000000..e075d0f
--- /dev/null
+++ b/src/gui/load.rs
@@ -0,0 +1,63 @@
+use gtk::glib;
+use gtk::prelude::*;
+use super::state::{View, ViewManager};
+use std::rc::Rc;
+
+use crate::index::Index;
+
+pub struct Load {
+ vm : Rc<ViewManager>
+}
+
+impl View for Load {
+ fn name(&self) -> &str {
+ "load"
+ }
+
+ fn set_vm(&mut self, vm : Rc<ViewManager>) {
+ self.vm = vm
+ }
+
+ fn make_current(&self) -> Option<gtk::Box> {
+ let vm = Rc::clone(&self.vm);
+ glib::MainContext::default().spawn_local(async move {
+ let dialog = gtk::FileChooserDialog::builder()
+ .action(gtk::FileChooserAction::Open)
+ .transient_for(vm.get_window())
+ .title("Open Index File")
+ .build();
+
+ dialog.add_button("Choose", gtk::ResponseType::Accept);
+
+ let _ = dialog.run_future().await;
+ dialog.close();
+ dialog.hide();
+
+ if let Some(uri) = dialog.uri() {
+ let splash = gtk::Window::new(gtk::WindowType::Popup);
+ splash.set_type_hint(gtk::gdk::WindowTypeHint::Splashscreen);
+ splash.set_decorated(false);
+ splash.set_position(gtk::WindowPosition::Center);
+ splash.set_resizable(false);
+ splash.set_size_request(300, 200);
+ splash.add(&gtk::Label::new(Some("Loading")));
+ splash.show_all();
+ let path = uri.trim_start_matches("file://").to_string();
+ let index = Index::from_file(&path);
+ vm.set_index(index);
+ vm.set_current_view("search");
+ splash.close();
+ } else {
+ vm.set_current_view("welcome");
+ }
+ });
+
+ None
+ }
+}
+
+impl Load {
+ pub fn new() -> Self {
+ Self { vm : Rc::new(ViewManager::empty()) }
+ }
+}
diff --git a/src/gui/mod.rs b/src/gui/mod.rs
index 68f8ab3..2701e5c 100644
--- a/src/gui/mod.rs
+++ b/src/gui/mod.rs
@@ -3,12 +3,15 @@ use std::rc::Rc;
use gtk::prelude::*;
use gtk::Application;
+use gtk::gdk;
use self::state::View;
mod state;
mod welcome;
mod generate;
+mod search;
+mod load;
pub fn run() {
let app = Application::builder()
@@ -16,6 +19,16 @@ pub fn run() {
.build();
app.connect_activate(|app| {
+ let provider = gtk::CssProvider::new();
+ let style = include_bytes!("style.css");
+ provider.load_from_data(style).expect("failed to load default style");
+
+ gtk::StyleContext::add_provider_for_screen(
+ &gdk::Screen::default().expect("error initializing gtk css provider"),
+ &provider,
+ gtk::STYLE_PROVIDER_PRIORITY_APPLICATION
+ );
+
let window = gtk::ApplicationWindow::builder()
.application(app)
.default_width(800)
@@ -23,17 +36,24 @@ pub fn run() {
.title("Index Search")
.build();
+ window.set_widget_name("mainwindow");
+
let mut vm = state::ViewManager::new(window);
let welcome = Rc::new(Mutex::new(welcome::Welcome::new()));
vm.add_view(welcome.clone());
let generate = Rc::new(Mutex::new(generate::Generate::new()));
vm.add_view(generate.clone());
+ let load = Rc::new(Mutex::new(load::Load::new()));
+ vm.add_view(load.clone());
+ let search = Rc::new(Mutex::new(search::Search::new()));
+ vm.add_view(search.clone());
let vm = Rc::new(vm);
welcome.lock().unwrap().set_vm(Rc::clone(&vm));
generate.lock().unwrap().set_vm(Rc::clone(&vm));
+ load.lock().unwrap().set_vm(Rc::clone(&vm));
+ search.lock().unwrap().set_vm(Rc::clone(&vm));
vm.set_current_view("welcome");
- vm.get_window().show_all();
});
app.run();
diff --git a/src/gui/search.rs b/src/gui/search.rs
new file mode 100644
index 0000000..a1c6ff3
--- /dev/null
+++ b/src/gui/search.rs
@@ -0,0 +1,133 @@
+use gtk::gdk::keys::constants::Return as RETURN;
+use gtk::glib;
+use gtk::prelude::*;
+use super::state::{View, ViewManager};
+use std::rc::Rc;
+use std::thread;
+
+pub struct Search {
+ vm : Rc<ViewManager>
+}
+
+impl View for Search {
+ fn name(&self) -> &str {
+ "search"
+ }
+
+ fn set_vm(&mut self, vm : Rc<ViewManager>) {
+ self.vm = vm
+ }
+
+ fn make_current(&self) -> Option<gtk::Box> {
+ let main = gtk::Box::new(gtk::Orientation::Vertical, 0);
+ let scroll = gtk::ScrolledWindow::new(gtk::Adjustment::NONE, gtk::Adjustment::NONE);
+ let search = gtk::SearchEntry::new();
+ let progress = gtk::ProgressBar::new();
+ search.set_widget_name("search_entry");
+ main.pack_start(&search, false, false, 0);
+ main.pack_start(&progress, false, false, 0);
+ let list = gtk::ListBox::new();
+ scroll.add(&list);
+
+ let empty_container = gtk::Box::new(gtk::Orientation::Vertical, 0);
+
+ let bt = include_bytes!("icon.svg");
+ let loader = gtk::gdk_pixbuf::PixbufLoader::new();
+ loader.write(bt).ok();
+ loader.set_size(200, 200);
+ loader.close().ok();
+ let pixbuf = loader.pixbuf().unwrap();
+ let image = gtk::Image::from_pixbuf(Some(&pixbuf));
+
+ empty_container.pack_start(&image, true, true, 0);
+ main.pack_start(&empty_container, true, true, 0);
+
+ let vm = Rc::clone(&self.vm);
+ search.connect_key_press_event(glib::clone!(
+ @strong scroll,
+ @strong list,
+ @weak progress,
+ @weak main,
+ @weak empty_container => @default-return gtk::Inhibit(false), move |entry, event| {
+ match event.keyval() {
+ RETURN => {
+ let query = entry.text().to_string();
+
+ if query.is_empty() {
+ main.remove(&scroll);
+ main.remove(&empty_container);
+ main.pack_start(&empty_container, true, true, 0);
+ return gtk::Inhibit(false);
+ }
+
+ let (tx, rx) = glib::MainContext::channel(glib::Priority::default());
+ let (status_tx, status_rx) = glib::MainContext::channel(glib::Priority::default());
+
+ let index = vm.get_index();
+ thread::spawn(move || {
+ let searchvec = crate::splitter::split_to_words(query);
+ let results = index.lock().unwrap().search(searchvec, |p| {
+ status_tx.send(p).ok();
+ });
+ tx.send(results).ok();
+ });
+
+ status_rx.attach(None, glib::clone!(
+ @weak progress => @default-return glib::Continue(true), move |p| {
+ progress.set_fraction(f64::from(p) / 100.0);
+
+ if p == 100 {
+ glib::Continue(false)
+ } else {
+ glib::Continue(true)
+ }
+ }));
+
+ rx.attach(None, glib::clone!(
+ @weak progress,
+ @weak list,
+ @weak scroll => @default-return glib::Continue(false), move |results| {
+ main.remove(&scroll);
+ main.remove(&empty_container);
+
+ progress.set_fraction(0.0);
+
+ if results.is_empty() {
+ main.pack_start(&empty_container, true, true, 0);
+ return glib::Continue(false);
+ } else {
+ for child in list.children() {
+ list.remove(&child);
+ }
+
+ for result in results.iter().rev().take(1000) {
+ let entry = gtk::Box::new(gtk::Orientation::Horizontal, 0);
+ entry.set_margin(10);
+ let path_label = gtk::Label::new(Some(&result.path));
+ let prio_label = gtk::Label::new(Some(&result.priority.to_string()));
+ entry.pack_start(&prio_label, false, false, 10);
+ entry.pack_start(&path_label, true, true, 10);
+ list.prepend(&entry)
+ }
+
+ main.pack_start(&scroll, true, true, 0);
+ }
+ main.show_all();
+
+ glib::Continue(false)
+ }));
+ }
+ _ => {}
+ }
+ gtk::Inhibit(false)
+ }));
+
+ Some(main)
+ }
+}
+
+impl Search {
+ pub fn new() -> Self {
+ Self { vm : Rc::new(ViewManager::empty()) }
+ }
+}
diff --git a/src/gui/state.rs b/src/gui/state.rs
index 7b9542a..be41f86 100644
--- a/src/gui/state.rs
+++ b/src/gui/state.rs
@@ -1,27 +1,31 @@
use gtk::prelude::*;
use std::collections::HashMap;
-use std::rc::Rc;
use std::sync::Mutex;
+use crate::index::Index;
+use std::rc::Rc;
+use std::sync::Arc;
pub trait View {
fn name(&self) -> &str;
fn set_vm(&mut self, vm : Rc<ViewManager>);
- fn make_current(&self) -> gtk::Box;
+ fn make_current(&self) -> Option<gtk::Box>;
}
#[derive(Default)]
pub struct ViewManager {
views : HashMap<String, Rc<Mutex<dyn View>>>,
- pub current : String,
- pub window : Option<gtk::ApplicationWindow>
+ pub current : Mutex<String>,
+ pub window : Option<gtk::ApplicationWindow>,
+ index : Arc<Mutex<Index>>
}
impl ViewManager {
pub fn new(window : gtk::ApplicationWindow) -> Self {
Self {
views : HashMap::new(),
- current : String::new(),
+ current : Mutex::new(String::new()),
window : Some(window),
+ index : Arc::new(Mutex::new(Index::default()))
}
}
@@ -41,11 +45,31 @@ impl ViewManager {
for child in window.children() {
window.remove(&child)
}
- window.add(&b);
- window.show_all()
+ {
+ *(self.current.lock().unwrap()) = name.to_string();
+ }
+ if let Some(b) = b {
+ window.add(&b);
+ window.show_all();
+ } else {
+ window.hide();
+ }
+ }
+
+ pub fn get_current_view(&self) -> String {
+ self.current.lock().unwrap().to_string()
}
pub fn get_window(&self) -> &gtk::ApplicationWindow {
self.window.as_ref().unwrap()
}
+
+ pub fn get_index(&self) -> Arc<Mutex<Index>> {
+ Arc::clone(&self.index)
+ }
+
+ pub fn set_index(&self, index : Index) {
+ let mut idx = self.index.lock().unwrap();
+ *idx = index;
+ }
}
diff --git a/src/gui/style.css b/src/gui/style.css
new file mode 100644
index 0000000..a25a28c
--- /dev/null
+++ b/src/gui/style.css
@@ -0,0 +1,18 @@
+#generate #title {
+ font-size: 2em;
+ background-color: #282828;
+ padding: 20px;
+}
+
+#search_entry {
+ background-color: #282828;
+ border-radius: 0px;
+ padding: 10px;
+ border: none;
+}
+
+
+#search_entry:focus {
+ border: none;
+ box-shadow: none;
+}
diff --git a/src/gui/welcome.rs b/src/gui/welcome.rs
index 3dac0dd..7c7d02f 100644
--- a/src/gui/welcome.rs
+++ b/src/gui/welcome.rs
@@ -1,3 +1,4 @@
+use gtk::glib;
use gtk::prelude::*;
use super::state::{View, ViewManager};
use std::rc::Rc;
@@ -15,25 +16,61 @@ impl View for Welcome {
self.vm = vm
}
- fn make_current(&self) -> gtk::Box {
+ fn make_current(&self) -> Option<gtk::Box> {
+ let splash = gtk::Window::new(gtk::WindowType::Popup);
+ splash.set_type_hint(gtk::gdk::WindowTypeHint::Splashscreen);
+ splash.set_decorated(false);
+ splash.set_position(gtk::WindowPosition::Center);
+ splash.set_resizable(false);
+ splash.set_size_request(300, 200);
let center = gtk::Box::new(gtk::Orientation::Vertical, 10);
let main = gtk::Box::new(gtk::Orientation::Horizontal, 10);
let gen_button = gtk::Button::with_label("Generate");
- let merge_button = gtk::Button::with_label("Merge");
let load_button = gtk::Button::with_label("Load");
- main.pack_start(&gen_button, true, false, 10);
- main.pack_start(&merge_button, true, false, 10);
- main.pack_start(&load_button, true, false, 10);
+ main.pack_start(&gen_button, true, true, 0);
+ main.pack_start(&load_button, true, true, 0);
+ main.set_height_request(50);
+ main.set_homogeneous(true);
+
+ let bt = include_bytes!("icon.svg");
+ let loader = gtk::gdk_pixbuf::PixbufLoader::new();
+ loader.write(bt).ok();
+ loader.set_size(100, 100);
+ loader.close().ok();
+ let pixbuf = loader.pixbuf().unwrap();
+ let image = gtk::Image::from_pixbuf(Some(&pixbuf));
+
+ center.pack_start(&image, true, true, 0);
center.pack_start(&main, true, false, 0);
+ self.vm.get_window().set_type_hint(gtk::gdk::WindowTypeHint::Splashscreen);
+
let vm = Rc::clone(&self.vm);
- gen_button.connect_clicked(move |_| {
+ gen_button.connect_clicked(glib::clone!(@weak splash => move |_| {
vm.set_current_view("generate");
+ splash.close();
+ }));
+
+ let vm = Rc::clone(&self.vm);
+ load_button.connect_clicked(glib::clone!(@weak splash => move |_| {
+ vm.set_current_view("load");
+ splash.close();
+ }));
+
+ let vm = Rc::clone(&self.vm);
+ splash.connect_hide(move |_| {
+ if vm.get_current_view() == "welcome" {
+ vm.get_window().application().unwrap().quit();
+ }
});
- center
+ center.set_margin(10);
+ splash.add(&center);
+ splash.show_all();
+
+ None
}
}
diff --git a/src/index.rs b/src/index.rs
index cc86f0c..be23e0e 100644
--- a/src/index.rs
+++ b/src/index.rs
@@ -16,7 +16,7 @@ use crate::vector;
/// or read from a file.
#[derive(Clone, Debug)]
pub struct Index {
- filecache : Vec<FileCache>,
+ pub filecache : Vec<FileCache>,
}
impl Default for Index {
@@ -69,7 +69,7 @@ impl Index {
if content.is_empty() {
result_tx.send(FileCache {
- path: "".to_string(),
+ path: "[is_empty]".to_string(),
vector : FileVector::default()
}).ok();
continue;
@@ -78,7 +78,7 @@ impl Index {
let words : Vec<String> = splitter::split_to_words(content);
let fv = FileVector::from_words(words);
result_tx.send(FileCache {
- path: "".to_string(),
+ path: path.to_string(),
vector : fv
}).ok();
}
@@ -128,7 +128,7 @@ impl Index {
Self { filecache }
}
- pub fn search(&self, search_args : Vec<String>) -> Vec<SearchResult> {
+ pub fn search(&self, search_args : Vec<String>, callback : impl Fn(u8)) -> Vec<SearchResult> {
let mut v : HashMap<Indexer, Count> = HashMap::new();
let mut opt : HashMap<Indexer, Count> = HashMap::new();
@@ -149,13 +149,20 @@ impl Index {
let mut results : Vec<SearchResult> = Vec::new();
- for filecache in self.filecache.iter() {
+ let mut last_p = 0;
+ for (i, filecache) in self.filecache.iter().enumerate() {
let mut r = SearchResult { priority : 0, path : filecache.path.clone() };
r.priority = vector::match_vector(&v, &filecache.vector);
if r.priority > 0 {
r.priority += vector::scalar_product(&opt, &filecache.vector);
results.push(r);
}
+
+ let p = i * 100 / self.filecache.len();
+ if last_p < p {
+ callback(p as u8);
+ last_p = p;
+ }
}
results.sort_by(|a, b| b.priority.cmp(&a.priority));
results
@@ -170,4 +177,8 @@ impl Index {
pub fn num_files(&self) -> usize {
self.filecache.len()
}
+
+ pub fn import(&mut self, index : Index) {
+ self.filecache = index.filecache;
+ }
}
diff --git a/src/main.rs b/src/main.rs
index 1a8a7c9..20697f7 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -49,7 +49,10 @@ fn main() {
let searchvec = splitter::split_to_words(search);
let idx = Index::from_file(&file);
- let results = idx.search(searchvec);
+ let results = idx.search(searchvec, |p| {
+ eprint!("\r\x1b[2K{}% searched", p);
+ });
+ eprint!("\r\x1b[2K");
for result in results {
println!("{}", result.path);
}