From f3e708922f69941dc717f52764ad7dd03032ec99 Mon Sep 17 00:00:00 2001 From: Gregory Marco Date: Sun, 31 Aug 2025 01:19:08 -0500 Subject: [PATCH] Add search feature using grep. --- src/lib.rs | 53 +++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 41 +++++++++++++++++++++++++++++++-- templates/search.css | 8 +++++++ templates/search.html | 21 +++++++++++++++++ 4 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 templates/search.css create mode 100644 templates/search.html diff --git a/src/lib.rs b/src/lib.rs index 1360b2c..5e36eca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ extern crate git2; extern crate regex; extern crate pathdiff; extern crate chrono; +extern crate grep; #[macro_use] extern crate toml; use std::fs; @@ -724,3 +725,55 @@ impl<'a> Linker<'a> { } } } + +pub struct Searcher<'a> { + historian: &'a Historian +} + +#[derive(Serialize)] +pub struct SearchResult { + pub page: Page, + pub matches: Vec +} + +impl<'a> Searcher<'a> { + pub fn new(historian: &Historian) -> Searcher { + Searcher { + historian + } + } + + pub fn search(&self, root: &Page, query: &str) -> Vec { + let mut searcher = grep::searcher::SearcherBuilder::new().build(); + let matcher = grep::regex::RegexMatcherBuilder::new() + .fixed_strings(true) + .case_insensitive(true) + .build(query) + .unwrap(); + + let mut results: Vec = vec![]; + self.do_search(root, &mut results, &mut searcher, &matcher); + results + } + + fn do_search(&self, root: &Page, results: &mut Vec, searcher: &mut grep::searcher::Searcher, matcher: &grep::regex::RegexMatcher) { + for child in &root.children { + if let Some(child_page) = self.historian.resolve_to_page(&child.full_name) { + let mut matches: Vec = vec![]; + searcher.search_path(matcher, &child.path, grep::searcher::sinks::UTF8(|lnum, line| { + matches.push(line.to_owned()); + Ok(true) + })); + + self.do_search(&child_page, results, searcher, matcher); + + if !matches.is_empty() { + results.push(SearchResult { + page: child_page, + matches + }); + } + } + } + } +} diff --git a/src/main.rs b/src/main.rs index 8c1c390..0b528e8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; extern crate historian; -use historian::{Historian, Page, Edit, PageRenderer, export_wiki, Linker}; +use historian::{Historian, Page, Edit, PageRenderer, export_wiki, Linker, Searcher, SearchResult}; #[macro_use] extern crate rocket; use rocket::serde::json::Json; @@ -71,6 +71,17 @@ async fn page<'r>( .render())) } } + + if key == "q" { + return PageResponder::Page(RawHtml(renderer.template("search.html") + .with_historian(&historian) + .with_page(&page) + .insert("results", &Searcher::new(&historian).search(&page, value)) + .insert("options", &toml! { + dynamic = true + }) + .render())) + } } } @@ -191,7 +202,15 @@ struct Args { /// Path to templates #[arg(long)] - template_path: Option + template_path: Option, + + /// Search the wiki + #[arg(long)] + search: Option, + + /// Search root + #[arg(long)] + search_root: Option } fn print_tree(historian: &Historian, page: &Page, prefix: &str) { @@ -203,6 +222,14 @@ fn print_tree(historian: &Historian, page: &Page, prefix: &str) { } } +fn print_result(result: &SearchResult) { + println!("{}", result.page.full_name); + for line in &result.matches { + println!("{}", line); + } + println!("---"); +} + #[rocket::main] async fn main() { let args = Args::parse(); @@ -239,6 +266,16 @@ async fn main() { return; } + if let Some(search) = args.search { + let searcher = Searcher::new(&historian); + let search_root = args.search_root.as_deref().unwrap_or(""); + let page = historian.resolve_to_page(&search_root).expect("failed to find page"); + for result in searcher.search(&page, &search) { + print_result(&result); + } + return; + } + rocket::build() .manage(historian) .manage(renderer) diff --git a/templates/search.css b/templates/search.css new file mode 100644 index 0000000..370939f --- /dev/null +++ b/templates/search.css @@ -0,0 +1,8 @@ +#results { + list-style-type: none; + padding: 0; +} + +#results li { + border-bottom: 1px solid; +} diff --git a/templates/search.html b/templates/search.html new file mode 100644 index 0000000..a7181da --- /dev/null +++ b/templates/search.html @@ -0,0 +1,21 @@ +{% extends "base.html" %} + +{% block styles %} + {{ super() }} + +{% endblock styles %} + +{% block content %} + +
    +{% for result in results %} +
  • +

    {{ result.page.full_name }}

    + {% for line in result.matches %} +

    {{ line | safe }}

    + {% endfor %} +
  • +{% endfor %} +
+ +{% endblock content %}