simsearch 0.4.0

A small in-memory fuzzy search index for embedded autocomplete and search suggestions.
Documentation
use std::fs::File;

use inquire::ui::{Color, RenderConfig, StyleSheet, Styled};
use inquire::{
    CustomUserError, Text,
    autocompletion::{Autocomplete, Replacement},
};

use simsearch::Index;

fn main() {
    inquire::set_global_render_config(get_render_config());

    Text::new("Search for a book:")
        .with_autocomplete(BookSearcher::load())
        .with_help_message("Try typing 'old man'")
        .with_page_size(10)
        .prompt()
        .ok();
}

#[derive(Clone)]
pub struct BookSearcher {
    engine: Index<String>,
}

impl BookSearcher {
    pub fn load() -> Self {
        let mut file = File::open("./books.json").unwrap();
        let json: serde_json::Value = serde_json::from_reader(&mut file).unwrap();
        let books = json
            .as_array()
            .unwrap()
            .iter()
            .map(|v| v.as_str().unwrap().to_string())
            .collect::<Vec<_>>();
        let mut engine = Index::new();

        for title in books {
            engine.insert(title.clone(), &title);
        }

        BookSearcher { engine }
    }
}

impl Autocomplete for BookSearcher {
    fn get_suggestions(&mut self, input: &str) -> Result<Vec<String>, CustomUserError> {
        Ok(self
            .engine
            .search(input)
            .into_iter()
            .map(|hit| format!("{:.3}  {}", hit.score, hit.id))
            .collect())
    }

    fn get_completion(
        &mut self,
        _: &str,
        suggestion: Option<String>,
    ) -> Result<Replacement, CustomUserError> {
        Ok(suggestion.and_then(|suggestion| {
            suggestion
                .split_once("  ")
                .map(|(_, title)| title.to_string())
        }))
    }
}

fn get_render_config() -> RenderConfig<'static> {
    RenderConfig {
        prompt_prefix: Styled::new(">").with_fg(Color::LightRed),
        highlighted_option_prefix: Styled::new("*").with_fg(Color::LightYellow),
        option: StyleSheet::new().with_fg(Color::DarkBlue),
        help_message: StyleSheet::new().with_fg(Color::LightYellow),
        ..Default::default()
    }
}