aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/dictionary.rs1
-rw-r--r--src/filecache.rs1
-rw-r--r--src/gui/circular.rs421
-rw-r--r--src/gui/easing.rs133
-rw-r--r--src/gui/mod.rs264
-rw-r--r--src/index.rs15
-rw-r--r--src/main.rs4
-rw-r--r--src/searchresult.rs1
-rw-r--r--src/vector.rs1
9 files changed, 840 insertions, 1 deletions
diff --git a/src/dictionary.rs b/src/dictionary.rs
index 94f377d..e651e71 100644
--- a/src/dictionary.rs
+++ b/src/dictionary.rs
@@ -5,6 +5,7 @@ use crate::vector::FileVector;
/// It also provides a function to convert it to
/// a vector and generate a FileVector from a word list
/// with the current directory.
+#[derive(Clone, Debug)]
pub struct Dictionary {
last_index : usize,
data : HashMap<String, u64>,
diff --git a/src/filecache.rs b/src/filecache.rs
index 7faf439..f8d84ec 100644
--- a/src/filecache.rs
+++ b/src/filecache.rs
@@ -1,6 +1,7 @@
use crate::vector::FileVector;
/// Represents one file which was indexed.
+#[derive(Clone, Debug)]
pub struct FileCache {
pub vector : FileVector,
pub path : String,
diff --git a/src/gui/circular.rs b/src/gui/circular.rs
new file mode 100644
index 0000000..3a35e02
--- /dev/null
+++ b/src/gui/circular.rs
@@ -0,0 +1,421 @@
+//! Show a circular progress indicator.
+use iced::advanced::layout;
+use iced::advanced::renderer;
+use iced::advanced::widget::tree::{self, Tree};
+use iced::advanced::{Clipboard, Layout, Renderer, Shell, Widget};
+use iced::event;
+use iced::mouse;
+use iced::time::Instant;
+use iced::widget::canvas;
+use iced::window::{self, RedrawRequest};
+use iced::{
+ Background, Color, Element, Event, Length, Rectangle, Size, Vector,
+};
+
+use super::easing::{self, Easing};
+
+use std::f32::consts::PI;
+use std::time::Duration;
+
+const MIN_RADIANS: f32 = PI / 8.0;
+const WRAP_RADIANS: f32 = 2.0 * PI - PI / 4.0;
+const BASE_ROTATION_SPEED: u32 = u32::MAX / 80;
+
+#[allow(missing_debug_implementations)]
+pub struct Circular<'a, Theme>
+where
+ Theme: StyleSheet,
+{
+ size: f32,
+ bar_height: f32,
+ style: <Theme as StyleSheet>::Style,
+ easing: &'a Easing,
+ cycle_duration: Duration,
+ rotation_duration: Duration,
+}
+
+impl<'a, Theme> Circular<'a, Theme>
+where
+ Theme: StyleSheet,
+{
+ /// Creates a new [`Circular`] with the given content.
+ pub fn new() -> Self {
+ Circular {
+ size: 40.0,
+ bar_height: 4.0,
+ style: <Theme as StyleSheet>::Style::default(),
+ easing: &easing::STANDARD,
+ cycle_duration: Duration::from_millis(600),
+ rotation_duration: Duration::from_secs(2),
+ }
+ }
+
+ /// Sets the size of the [`Circular`].
+ pub fn size(mut self, size: f32) -> Self {
+ self.size = size;
+ self
+ }
+
+ /// Sets the bar height of the [`Circular`].
+ pub fn bar_height(mut self, bar_height: f32) -> Self {
+ self.bar_height = bar_height;
+ self
+ }
+
+ /// Sets the style variant of this [`Circular`].
+ pub fn style(mut self, style: <Theme as StyleSheet>::Style) -> Self {
+ self.style = style;
+ self
+ }
+
+ /// Sets the easing of this [`Circular`].
+ pub fn easing(mut self, easing: &'a Easing) -> Self {
+ self.easing = easing;
+ self
+ }
+
+ /// Sets the cycle duration of this [`Circular`].
+ pub fn cycle_duration(mut self, duration: Duration) -> Self {
+ self.cycle_duration = duration / 2;
+ self
+ }
+
+ /// Sets the base rotation duration of this [`Circular`]. This is the duration that a full
+ /// rotation would take if the cycle rotation were set to 0.0 (no expanding or contracting)
+ pub fn rotation_duration(mut self, duration: Duration) -> Self {
+ self.rotation_duration = duration;
+ self
+ }
+}
+
+impl<'a, Theme> Default for Circular<'a, Theme>
+where
+ Theme: StyleSheet,
+{
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+#[derive(Clone, Copy)]
+enum Animation {
+ Expanding {
+ start: Instant,
+ progress: f32,
+ rotation: u32,
+ last: Instant,
+ },
+ Contracting {
+ start: Instant,
+ progress: f32,
+ rotation: u32,
+ last: Instant,
+ },
+}
+
+impl Default for Animation {
+ fn default() -> Self {
+ Self::Expanding {
+ start: Instant::now(),
+ progress: 0.0,
+ rotation: 0,
+ last: Instant::now(),
+ }
+ }
+}
+
+impl Animation {
+ fn next(&self, additional_rotation: u32, now: Instant) -> Self {
+ match self {
+ Self::Expanding { rotation, .. } => Self::Contracting {
+ start: now,
+ progress: 0.0,
+ rotation: rotation.wrapping_add(additional_rotation),
+ last: now,
+ },
+ Self::Contracting { rotation, .. } => Self::Expanding {
+ start: now,
+ progress: 0.0,
+ rotation: rotation.wrapping_add(
+ BASE_ROTATION_SPEED.wrapping_add(
+ ((WRAP_RADIANS / (2.0 * PI)) * u32::MAX as f32) as u32,
+ ),
+ ),
+ last: now,
+ },
+ }
+ }
+
+ fn start(&self) -> Instant {
+ match self {
+ Self::Expanding { start, .. } | Self::Contracting { start, .. } => {
+ *start
+ }
+ }
+ }
+
+ fn last(&self) -> Instant {
+ match self {
+ Self::Expanding { last, .. } | Self::Contracting { last, .. } => {
+ *last
+ }
+ }
+ }
+
+ fn timed_transition(
+ &self,
+ cycle_duration: Duration,
+ rotation_duration: Duration,
+ now: Instant,
+ ) -> Self {
+ let elapsed = now.duration_since(self.start());
+ let additional_rotation = ((now - self.last()).as_secs_f32()
+ / rotation_duration.as_secs_f32()
+ * (u32::MAX) as f32) as u32;
+
+ match elapsed {
+ elapsed if elapsed > cycle_duration => {
+ self.next(additional_rotation, now)
+ }
+ _ => self.with_elapsed(
+ cycle_duration,
+ additional_rotation,
+ elapsed,
+ now,
+ ),
+ }
+ }
+
+ fn with_elapsed(
+ &self,
+ cycle_duration: Duration,
+ additional_rotation: u32,
+ elapsed: Duration,
+ now: Instant,
+ ) -> Self {
+ let progress = elapsed.as_secs_f32() / cycle_duration.as_secs_f32();
+ match self {
+ Self::Expanding {
+ start, rotation, ..
+ } => Self::Expanding {
+ start: *start,
+ progress,
+ rotation: rotation.wrapping_add(additional_rotation),
+ last: now,
+ },
+ Self::Contracting {
+ start, rotation, ..
+ } => Self::Contracting {
+ start: *start,
+ progress,
+ rotation: rotation.wrapping_add(additional_rotation),
+ last: now,
+ },
+ }
+ }
+
+ fn rotation(&self) -> f32 {
+ match self {
+ Self::Expanding { rotation, .. }
+ | Self::Contracting { rotation, .. } => {
+ *rotation as f32 / u32::MAX as f32
+ }
+ }
+ }
+}
+
+#[derive(Default)]
+struct State {
+ animation: Animation,
+ cache: canvas::Cache,
+}
+
+impl<'a, Message, Theme> Widget<Message, iced::Renderer<Theme>>
+ for Circular<'a, Theme>
+where
+ Message: 'a + Clone,
+ Theme: StyleSheet,
+{
+ fn tag(&self) -> tree::Tag {
+ tree::Tag::of::<State>()
+ }
+
+ fn state(&self) -> tree::State {
+ tree::State::new(State::default())
+ }
+
+ fn width(&self) -> Length {
+ Length::Fixed(self.size)
+ }
+
+ fn height(&self) -> Length {
+ Length::Fixed(self.size)
+ }
+
+ fn layout(
+ &self,
+ _renderer: &iced::Renderer<Theme>,
+ limits: &layout::Limits,
+ ) -> layout::Node {
+ let limits = limits.width(self.size).height(self.size);
+ let size = limits.resolve(Size::ZERO);
+
+ layout::Node::new(size)
+ }
+
+ fn on_event(
+ &mut self,
+ tree: &mut Tree,
+ event: Event,
+ _layout: Layout<'_>,
+ _cursor: mouse::Cursor,
+ _renderer: &iced::Renderer<Theme>,
+ _clipboard: &mut dyn Clipboard,
+ shell: &mut Shell<'_, Message>,
+ ) -> event::Status {
+ const FRAME_RATE: u64 = 60;
+
+ let state = tree.state.downcast_mut::<State>();
+
+ if let Event::Window(window::Event::RedrawRequested(now)) = event {
+ state.animation = state.animation.timed_transition(
+ self.cycle_duration,
+ self.rotation_duration,
+ now,
+ );
+
+ state.cache.clear();
+ shell.request_redraw(RedrawRequest::At(
+ now + Duration::from_millis(1000 / FRAME_RATE),
+ ));
+ }
+
+ event::Status::Ignored
+ }
+
+ fn draw(
+ &self,
+ tree: &Tree,
+ renderer: &mut iced::Renderer<Theme>,
+ theme: &Theme,
+ _style: &renderer::Style,
+ layout: Layout<'_>,
+ _cursor: mouse::Cursor,
+ _viewport: &Rectangle,
+ ) {
+ let state = tree.state.downcast_ref::<State>();
+ let bounds = layout.bounds();
+ let custom_style =
+ <Theme as StyleSheet>::appearance(theme, &self.style);
+
+ let geometry = state.cache.draw(renderer, bounds.size(), |frame| {
+ let track_radius = frame.width() / 2.0 - self.bar_height;
+ let track_path = canvas::Path::circle(frame.center(), track_radius);
+
+ frame.stroke(
+ &track_path,
+ canvas::Stroke::default()
+ .with_color(custom_style.track_color)
+ .with_width(self.bar_height),
+ );
+
+ let mut builder = canvas::path::Builder::new();
+
+ let start = state.animation.rotation() * 2.0 * PI;
+
+ match state.animation {
+ Animation::Expanding { progress, .. } => {
+ builder.arc(canvas::path::Arc {
+ center: frame.center(),
+ radius: track_radius,
+ start_angle: start,
+ end_angle: start
+ + MIN_RADIANS
+ + WRAP_RADIANS * (self.easing.y_at_x(progress)),
+ });
+ }
+ Animation::Contracting { progress, .. } => {
+ builder.arc(canvas::path::Arc {
+ center: frame.center(),
+ radius: track_radius,
+ start_angle: start
+ + WRAP_RADIANS * (self.easing.y_at_x(progress)),
+ end_angle: start + MIN_RADIANS + WRAP_RADIANS,
+ });
+ }
+ }
+
+ let bar_path = builder.build();
+
+ frame.stroke(
+ &bar_path,
+ canvas::Stroke::default()
+ .with_color(custom_style.bar_color)
+ .with_width(self.bar_height),
+ );
+ });
+
+ renderer.with_translation(
+ Vector::new(bounds.x, bounds.y),
+ |renderer| {
+ use iced::advanced::graphics::geometry::Renderer as _;
+
+ renderer.draw(vec![geometry]);
+ },
+ );
+ }
+}
+
+impl<'a, Message, Theme> From<Circular<'a, Theme>>
+ for Element<'a, Message, iced::Renderer<Theme>>
+where
+ Message: Clone + 'a,
+ Theme: StyleSheet + 'a,
+{
+ fn from(circular: Circular<'a, Theme>) -> Self {
+ Self::new(circular)
+ }
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct Appearance {
+ /// The [`Background`] of the progress indicator.
+ pub background: Option<Background>,
+ /// The track [`Color`] of the progress indicator.
+ pub track_color: Color,
+ /// The bar [`Color`] of the progress indicator.
+ pub bar_color: Color,
+}
+
+impl std::default::Default for Appearance {
+ fn default() -> Self {
+ Self {
+ background: None,
+ track_color: Color::TRANSPARENT,
+ bar_color: Color::BLACK,
+ }
+ }
+}
+
+/// A set of rules that dictate the style of an indicator.
+pub trait StyleSheet {
+ /// The supported style of the [`StyleSheet`].
+ type Style: Default;
+
+ /// Produces the active [`Appearance`] of a indicator.
+ fn appearance(&self, style: &Self::Style) -> Appearance;
+}
+
+impl StyleSheet for iced::Theme {
+ type Style = ();
+
+ fn appearance(&self, _style: &Self::Style) -> Appearance {
+ let palette = self.extended_palette();
+
+ Appearance {
+ background: None,
+ track_color: palette.background.weak.color,
+ bar_color: palette.primary.base.color,
+ }
+ }
+}
diff --git a/src/gui/easing.rs b/src/gui/easing.rs
new file mode 100644
index 0000000..665b332
--- /dev/null
+++ b/src/gui/easing.rs
@@ -0,0 +1,133 @@
+use iced::Point;
+
+use lyon_algorithms::measure::PathMeasurements;
+use lyon_algorithms::path::{builder::NoAttributes, path::BuilderImpl, Path};
+use once_cell::sync::Lazy;
+
+pub static EMPHASIZED: Lazy<Easing> = Lazy::new(|| {
+ Easing::builder()
+ .cubic_bezier_to([0.05, 0.0], [0.133333, 0.06], [0.166666, 0.4])
+ .cubic_bezier_to([0.208333, 0.82], [0.25, 1.0], [1.0, 1.0])
+ .build()
+});
+
+pub static EMPHASIZED_DECELERATE: Lazy<Easing> = Lazy::new(|| {
+ Easing::builder()
+ .cubic_bezier_to([0.05, 0.7], [0.1, 1.0], [1.0, 1.0])
+ .build()
+});
+
+pub static EMPHASIZED_ACCELERATE: Lazy<Easing> = Lazy::new(|| {
+ Easing::builder()
+ .cubic_bezier_to([0.3, 0.0], [0.8, 0.15], [1.0, 1.0])
+ .build()
+});
+
+pub static STANDARD: Lazy<Easing> = Lazy::new(|| {
+ Easing::builder()
+ .cubic_bezier_to([0.2, 0.0], [0.0, 1.0], [1.0, 1.0])
+ .build()
+});
+
+pub static STANDARD_DECELERATE: Lazy<Easing> = Lazy::new(|| {
+ Easing::builder()
+ .cubic_bezier_to([0.0, 0.0], [0.0, 1.0], [1.0, 1.0])
+ .build()
+});
+
+pub static STANDARD_ACCELERATE: Lazy<Easing> = Lazy::new(|| {
+ Easing::builder()
+ .cubic_bezier_to([0.3, 0.0], [1.0, 1.0], [1.0, 1.0])
+ .build()
+});
+
+pub struct Easing {
+ path: Path,
+ measurements: PathMeasurements,
+}
+
+impl Easing {
+ pub fn builder() -> Builder {
+ Builder::new()
+ }
+
+ pub fn y_at_x(&self, x: f32) -> f32 {
+ let mut sampler = self.measurements.create_sampler(
+ &self.path,
+ lyon_algorithms::measure::SampleType::Normalized,
+ );
+ let sample = sampler.sample(x);
+
+ sample.position().y
+ }
+}
+
+pub struct Builder(NoAttributes<BuilderImpl>);
+
+impl Builder {
+ pub fn new() -> Self {
+ let mut builder = Path::builder();
+ builder.begin(lyon_algorithms::geom::point(0.0, 0.0));
+
+ Self(builder)
+ }
+
+ /// Adds a line segment. Points must be between 0,0 and 1,1
+ pub fn line_to(mut self, to: impl Into<Point>) -> Self {
+ self.0.line_to(Self::point(to));
+
+ self
+ }
+
+ /// Adds a quadratic bézier curve. Points must be between 0,0 and 1,1
+ pub fn quadratic_bezier_to(
+ mut self,
+ ctrl: impl Into<Point>,
+ to: impl Into<Point>,
+ ) -> Self {
+ self.0
+ .quadratic_bezier_to(Self::point(ctrl), Self::point(to));
+
+ self
+ }
+
+ /// Adds a cubic bézier curve. Points must be between 0,0 and 1,1
+ pub fn cubic_bezier_to(
+ mut self,
+ ctrl1: impl Into<Point>,
+ ctrl2: impl Into<Point>,
+ to: impl Into<Point>,
+ ) -> Self {
+ self.0.cubic_bezier_to(
+ Self::point(ctrl1),
+ Self::point(ctrl2),
+ Self::point(to),
+ );
+
+ self
+ }
+
+ pub fn build(mut self) -> Easing {
+ self.0.line_to(lyon_algorithms::geom::point(1.0, 1.0));
+ self.0.end(false);
+
+ let path = self.0.build();
+ let measurements = PathMeasurements::from_path(&path, 0.0);
+
+ Easing { path, measurements }
+ }
+
+ fn point(p: impl Into<Point>) -> lyon_algorithms::geom::Point<f32> {
+ let p: Point = p.into();
+ lyon_algorithms::geom::point(
+ p.x.min(1.0).max(0.0),
+ p.y.min(1.0).max(0.0),
+ )
+ }
+}
+
+impl Default for Builder {
+ fn default() -> Self {
+ Self::new()
+ }
+}
diff --git a/src/gui/mod.rs b/src/gui/mod.rs
new file mode 100644
index 0000000..0c1b9cc
--- /dev/null
+++ b/src/gui/mod.rs
@@ -0,0 +1,264 @@
+use std::io::Write;
+use std::thread;
+use std::time::Duration;
+
+use iced::theme::Theme;
+use iced::widget::{text, scrollable, container, column, button, text_input, progress_bar};
+use iced::{window, Length};
+use iced::{Application, Element};
+use iced::{Command, Settings};
+use once_cell::sync::Lazy;
+use std::sync::Mutex;
+
+use crate::index::Index;
+use crate::searchresult::SearchResult;
+use crate::splitter;
+
+mod easing;
+mod circular;
+
+static SEARCH_ID: Lazy<text_input::Id> = Lazy::new(text_input::Id::unique);
+static GENERATE_PROGRESS : Lazy<Mutex<u8>> = Lazy::new(|| { Mutex::new(0) });
+
+pub fn run() -> iced::Result {
+ App::run(Settings {
+ window: window::Settings {
+ size: (500, 800),
+ ..window::Settings::default()
+ },
+ ..Settings::default()
+ })
+}
+
+#[derive(Debug)]
+enum App {
+ StartMenu,
+ Generating(GenState),
+ Loading,
+ Search(State),
+}
+
+#[derive(Debug, Default)]
+struct GenState {
+ progress: u8,
+}
+
+#[derive(Debug, Default)]
+struct State {
+ input_value: String,
+ index : Index,
+ results : Vec<SearchResult>
+}
+
+struct SearchState {
+ search : String,
+ index : Index,
+}
+
+impl SearchState {
+ pub async fn search(self) -> Vec<SearchResult> {
+ let search_args = splitter::split_to_words(self.search.clone());
+ self.index.search(search_args)
+ }
+}
+
+#[derive(Clone, Debug)]
+enum Message {
+ Load,
+ Generate,
+ Loaded(Index),
+ GeneratingUpdate(u8),
+ InputChanged(String),
+ SearchSubmit,
+ SearchFinished(Vec<SearchResult>)
+}
+
+async fn load_file() -> Index {
+ let file = rfd::FileDialog::new().add_filter("index", &["idxs"])
+ .set_directory(".")
+ .set_title("Choose Index File")
+ .pick_file();
+ let file = file.unwrap();
+ let file = file.to_str();
+ let file = file.unwrap();
+ Index::from_file(file)
+}
+
+async fn generate() -> Index {
+ let file = rfd::FileDialog::new().add_filter("index", &["idxs"])
+ .set_directory(".")
+ .set_title("Choose Index File")
+ .save_file();
+ let file = file.unwrap();
+ let file = file.to_str();
+ let file = file.unwrap();
+
+ let input = rfd::FileDialog::new()
+ .set_directory(".")
+ .set_title("Choose Directory to Index")
+ .pick_folder();
+ let input = input.unwrap();
+ let input = input.to_str();
+ let input = input.unwrap();
+ Index::generate(input, file, |counter, nof| {
+ let p = ((counter * 100) / nof) as u8;
+ *GENERATE_PROGRESS.lock().unwrap() = p;
+ eprint!("\r\x1b[2K{} of {} files indexed ({}%)", counter, nof, p);
+ std::io::stdout().flush().ok();
+ })
+}
+
+async fn generate_update_timer() -> u8 {
+ thread::sleep(Duration::from_millis(100));
+ let p;
+ {
+ p = *GENERATE_PROGRESS.lock().unwrap();
+ }
+ p
+}
+
+impl Application for App {
+ type Message = Message;
+ type Theme = Theme;
+ type Executor = iced::executor::Default;
+ type Flags = ();
+
+ fn new(_flags: ()) -> (App, Command<Message>) {
+ (
+ App::StartMenu,
+ Command::none()
+ )
+ }
+
+ fn title(&self) -> String {
+ "Index Search".to_string()
+ }
+
+ fn update(&mut self, message: Message) -> Command<Message> {
+ match self {
+ App::StartMenu => {
+ match message {
+ Message::Load => {
+ *self = App::Loading;
+ Command::batch(vec![
+ Command::perform(load_file(), Message::Loaded)
+ ])
+ }
+ Message::Generate => {
+ *self = App::Generating(Default::default());
+ Command::batch(vec![
+ Command::perform(generate(), Message::Loaded),
+ Command::perform(generate_update_timer(), Message::GeneratingUpdate)
+ ])
+ }
+ _ => {
+ Command::none()
+ }
+ }
+ }
+ App::Loading => {
+ match message {
+ Message::Loaded(index) => {
+ *self = App::Search(State { index, ..Default::default() });
+ Command::none()
+ }
+ _ => {
+ Command::none()
+ }
+ }
+ }
+ App::Generating(state) => {
+ match message {
+ Message::Loaded(index) => {
+ *self = App::Search(State { index, ..Default::default() });
+ Command::none()
+ }
+ Message::GeneratingUpdate(p) => {
+ state.progress = p;
+ Command::perform(generate_update_timer(), Message::GeneratingUpdate)
+ }
+ _ => {
+ Command::none()
+ }
+ }
+ }
+ App::Search(state) => {
+ match message {
+ Message::InputChanged(value) => {
+ state.input_value = value;
+ Command::none()
+ }
+ Message::SearchSubmit => {
+ Command::perform(
+ SearchState {
+ search: state.input_value.clone(),
+ index : state.index.clone()
+ }.search(),
+ Message::SearchFinished
+ )
+ }
+ Message::SearchFinished(results) => {
+ state.results = results.clone();
+ Command::none()
+ }
+ _ => {
+ Command::none()
+ }
+ }
+ }
+ }
+ }
+
+ fn view(&self) -> Element<Message> {
+ let container = container(match self {
+ App::StartMenu => {
+ column![
+ button("Open").on_press(Message::Load),
+ button("Generate").on_press(Message::Generate),
+ ].spacing(10).align_items(iced::Alignment::Center)
+ }
+ App::Loading => {
+ column![
+ text("Loading"),
+ circular::Circular::new()
+ ].spacing(10).align_items(iced::Alignment::Center)
+ }
+ App::Generating(state) => {
+ column![
+ text("Generating"),
+ progress_bar(0.0..=100.0, state.progress as f32)
+ ].spacing(10).align_items(iced::Alignment::Center)
+ }
+ App::Search(State { input_value, results, ..}) => {
+ let res : Element<Message> = if results.is_empty() {
+ column![text("There are no results")].spacing(10).into()
+ } else {
+ let lines : Vec<Element<_>> = results.iter().map(|r| text(r.path.clone()).into()).collect();
+ column(lines).spacing(10).into()
+ };
+
+ column![
+ text_input("Search", input_value)
+ .id(SEARCH_ID.clone())
+ .on_submit(Message::SearchSubmit)
+ .on_input(Message::InputChanged)
+ .padding(5),
+ scrollable(res).width(Length::Fill)
+ ].spacing(10)
+ }
+ }).center_x()
+ .padding(10)
+ .width(Length::Fill)
+ .height(Length::Fill);
+
+ match self {
+ App::Search(_) => {
+ container.into()
+ }
+ _ => {
+ container.center_y().into()
+ }
+ }
+ }
+}
+
diff --git a/src/index.rs b/src/index.rs
index 616682d..9f0af1d 100644
--- a/src/index.rs
+++ b/src/index.rs
@@ -1,3 +1,4 @@
+use std::fmt::{Formatter, Debug, Result};
use std::fs::File;
use std::io::{Write, BufReader, BufRead};
use walkdir::*;
@@ -14,12 +15,26 @@ use crate::vector;
/// Represents a Index which is ether generated
/// or read from a file.
+#[derive(Clone, Debug)]
pub struct Index {
dictionary : Dictionary,
filecache : Vec<FileCache>,
}
+impl Default for Index {
+ fn default() -> Self {
+ Self::empty()
+ }
+}
+
impl Index {
+ pub fn empty() -> Self {
+ Self {
+ dictionary : Dictionary::new(),
+ filecache : Vec::new()
+ }
+ }
+
pub fn generate(input_path : &str, index_path : &str, callback : impl Fn(u64, u64)) -> Self {
let mut index_file = File::create(index_path).unwrap();
let mut dict = Dictionary::new();
diff --git a/src/main.rs b/src/main.rs
index 261008b..fc82739 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -7,6 +7,7 @@ pub mod searchresult;
pub mod filecounter;
pub mod extractors;
pub mod index;
+pub mod gui;
use index::Index;
use std::io::*;
@@ -47,6 +48,7 @@ fn main() {
println!("{}", result.path);
}
}
-
+ } else {
+ let _ = gui::run();
}
}
diff --git a/src/searchresult.rs b/src/searchresult.rs
index cc882ff..23e8d02 100644
--- a/src/searchresult.rs
+++ b/src/searchresult.rs
@@ -1,5 +1,6 @@
/// This struct is returned by the index::Index::search
/// function.
+#[derive(Debug, Clone)]
pub struct SearchResult {
pub priority : u64,
pub path : String
diff --git a/src/vector.rs b/src/vector.rs
index 0779e07..87be04b 100644
--- a/src/vector.rs
+++ b/src/vector.rs
@@ -5,6 +5,7 @@ use std::ops::{Deref, DerefMut};
/// It is stored as a HashMap, because we do not
/// have to store the zeros. With that we save a lot
/// of storage.
+#[derive(Clone, Debug)]
pub struct FileVector {
data : HashMap<u64, u64>
}