Add search feature using grep.
This commit is contained in:
53
src/lib.rs
53
src/lib.rs
@@ -10,6 +10,7 @@ extern crate git2;
|
|||||||
extern crate regex;
|
extern crate regex;
|
||||||
extern crate pathdiff;
|
extern crate pathdiff;
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
|
extern crate grep;
|
||||||
#[macro_use] extern crate toml;
|
#[macro_use] extern crate toml;
|
||||||
|
|
||||||
use std::fs;
|
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<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Searcher<'a> {
|
||||||
|
pub fn new(historian: &Historian) -> Searcher {
|
||||||
|
Searcher {
|
||||||
|
historian
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn search(&self, root: &Page, query: &str) -> Vec<SearchResult> {
|
||||||
|
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<SearchResult> = vec![];
|
||||||
|
self.do_search(root, &mut results, &mut searcher, &matcher);
|
||||||
|
results
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_search(&self, root: &Page, results: &mut Vec<SearchResult>, 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<String> = 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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
41
src/main.rs
41
src/main.rs
@@ -1,7 +1,7 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
extern crate historian;
|
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;
|
#[macro_use] extern crate rocket;
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
@@ -71,6 +71,17 @@ async fn page<'r>(
|
|||||||
.render()))
|
.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
|
/// Path to templates
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
template_path: Option<String>
|
template_path: Option<String>,
|
||||||
|
|
||||||
|
/// Search the wiki
|
||||||
|
#[arg(long)]
|
||||||
|
search: Option<String>,
|
||||||
|
|
||||||
|
/// Search root
|
||||||
|
#[arg(long)]
|
||||||
|
search_root: Option<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_tree(historian: &Historian, page: &Page, prefix: &str) {
|
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]
|
#[rocket::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
@@ -239,6 +266,16 @@ async fn main() {
|
|||||||
return;
|
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()
|
rocket::build()
|
||||||
.manage(historian)
|
.manage(historian)
|
||||||
.manage(renderer)
|
.manage(renderer)
|
||||||
|
|||||||
8
templates/search.css
Normal file
8
templates/search.css
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#results {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#results li {
|
||||||
|
border-bottom: 1px solid;
|
||||||
|
}
|
||||||
21
templates/search.html
Normal file
21
templates/search.html
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block styles %}
|
||||||
|
{{ super() }}
|
||||||
|
<link rel="StyleSheet" href="/search.css" type="text/css" media="screen" />
|
||||||
|
{% endblock styles %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<ul id="results">
|
||||||
|
{% for result in results %}
|
||||||
|
<li>
|
||||||
|
<h2><a href="{{ relative_root | safe }}{{ result.page.url }}">{{ result.page.full_name }}</a></h2>
|
||||||
|
{% for line in result.matches %}
|
||||||
|
<p>{{ line | safe }}</p>
|
||||||
|
{% endfor %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% endblock content %}
|
||||||
Reference in New Issue
Block a user