extern crate actix_web; extern crate serde; #[macro_use] extern crate serde_derive; extern crate grep; extern crate time; use actix_web::{fs, fs::NamedFile, http, server, App, HttpRequest, HttpResponse, Query}; use std::error::Error; use grep::regex::RegexMatcherBuilder; use grep::searcher::sinks::Lossy; use grep::searcher::{BinaryDetection, SearcherBuilder}; use std::env; use std::fs::File; fn main() { server::new(|| { App::new() .route("/service/search", http::Method::GET, search) .resource("/favicon.ico", |r| r.f(favicon)) .resource("/", |r| r.f(index)) .handler( "/static", fs::StaticFiles::new(front_end_dir()) .unwrap() // .index_file("index.html"), ) .finish() }).bind("0.0.0.0:8080") .unwrap() .run(); } fn index(_req: &HttpRequest) -> Result { Ok(NamedFile::open(front_end_dir() + "/index.html")?) } fn favicon(_req: &HttpRequest) -> Result { Ok(NamedFile::open(front_end_dir() + "/favicon.ico")?) } fn front_end_dir() -> String { env::var("TORRENTS_CSV_FRONT_END_DIR").unwrap_or("../ui/dist".to_string()) } fn torrents_csv_file() -> String { env::var("TORRENTS_CSV_FILE").unwrap_or("../../torrents.csv".to_string()) } #[derive(Deserialize)] struct SearchQuery { q: String, page: Option, size: Option, } fn search(query: Query) -> HttpResponse { HttpResponse::Ok() .header("Access-Control-Allow-Origin", "*") .content_type("text/csv") .body(ripgrep(query)) } fn ripgrep(query: Query) -> String { let page = query.page.unwrap_or(1); let size = query.size.unwrap_or(10); let offset = size * (page - 1); let csv_file = File::open(torrents_csv_file()); println!("query = {} , page = {}, size = {}", query.q, page, size); let results = search_file(csv_file.unwrap(), &query.q).unwrap(); let last: usize = if offset + size < results.len() { offset + size } else { results.len() }; results[offset..last].join("") } fn search_file(file: File, query: &str) -> Result, Box> { let pattern = query.replace(" ", ".*"); let matcher = RegexMatcherBuilder::new() .case_insensitive(true) .build(&pattern) .unwrap(); let mut matches: Vec = vec![]; let mut searcher = SearcherBuilder::new() .binary_detection(BinaryDetection::quit(b'\x00')) // .line_number(false) .build(); searcher.search_file( &matcher, &file, Lossy(|_lnum, line| { matches.push(line.to_string()); Ok(true) }), )?; Ok(matches) } #[cfg(test)] mod tests { use std::fs::File; use time::PreciseTime; #[test] fn test() { let start = PreciseTime::now(); let results = super::search_file(File::open(super::torrents_csv_file()).unwrap(), "sherlock").unwrap(); assert!(results.len() > 2); let end = PreciseTime::now(); println!("Query took {} seconds.", start.to(end)); } }