2018-10-11 21:59:30 +00:00
|
|
|
extern crate actix_web;
|
|
|
|
extern crate serde;
|
|
|
|
#[macro_use]
|
|
|
|
extern crate serde_derive;
|
2018-10-22 18:55:47 +00:00
|
|
|
extern crate grep;
|
|
|
|
extern crate time;
|
2018-10-11 21:59:30 +00:00
|
|
|
|
2018-11-27 21:29:56 +00:00
|
|
|
use actix_web::{fs, fs::NamedFile, http, server, App, HttpRequest, HttpResponse, Query};
|
2018-10-22 18:55:47 +00:00
|
|
|
|
|
|
|
use std::error::Error;
|
|
|
|
|
2018-10-22 22:31:34 +00:00
|
|
|
use grep::regex::RegexMatcherBuilder;
|
2018-10-22 18:55:47 +00:00
|
|
|
use grep::searcher::sinks::Lossy;
|
|
|
|
use grep::searcher::{BinaryDetection, SearcherBuilder};
|
2018-12-02 05:15:54 +00:00
|
|
|
use std::env;
|
2018-10-22 18:55:47 +00:00
|
|
|
use std::fs::File;
|
2018-10-11 21:59:30 +00:00
|
|
|
|
|
|
|
fn main() {
|
2018-11-27 21:29:56 +00:00
|
|
|
|
2018-12-02 05:15:54 +00:00
|
|
|
|
2018-10-22 18:55:47 +00:00
|
|
|
server::new(|| {
|
|
|
|
App::new()
|
|
|
|
.route("/service/search", http::Method::GET, search)
|
2018-11-27 21:29:56 +00:00
|
|
|
.resource("/favicon.ico", |r| r.f(favicon))
|
|
|
|
.resource("/", |r| r.f(index))
|
2018-10-22 18:55:47 +00:00
|
|
|
.handler(
|
2018-11-27 21:29:56 +00:00
|
|
|
"/static",
|
2018-12-02 05:15:54 +00:00
|
|
|
fs::StaticFiles::new(front_end_dir())
|
2018-10-22 18:55:47 +00:00
|
|
|
.unwrap()
|
2018-11-27 21:29:56 +00:00
|
|
|
// .index_file("index.html"),
|
2018-10-22 18:55:47 +00:00
|
|
|
)
|
|
|
|
.finish()
|
2018-12-01 00:14:02 +00:00
|
|
|
}).bind("0.0.0.0:8080")
|
2018-10-22 18:55:47 +00:00
|
|
|
.unwrap()
|
|
|
|
.run();
|
2018-10-11 21:59:30 +00:00
|
|
|
}
|
|
|
|
|
2018-11-27 21:29:56 +00:00
|
|
|
fn index(_req: &HttpRequest) -> Result<NamedFile, actix_web::error::Error> {
|
2018-12-02 05:15:54 +00:00
|
|
|
Ok(NamedFile::open(front_end_dir() + "/index.html")?)
|
2018-11-27 21:29:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn favicon(_req: &HttpRequest) -> Result<NamedFile, actix_web::error::Error> {
|
2018-12-02 05:15:54 +00:00
|
|
|
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())
|
2018-11-27 21:29:56 +00:00
|
|
|
}
|
|
|
|
|
2018-10-11 21:59:30 +00:00
|
|
|
#[derive(Deserialize)]
|
|
|
|
struct SearchQuery {
|
2018-10-22 18:55:47 +00:00
|
|
|
q: String,
|
|
|
|
page: Option<usize>,
|
|
|
|
size: Option<usize>,
|
2018-10-11 21:59:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn search(query: Query<SearchQuery>) -> HttpResponse {
|
2018-10-22 18:55:47 +00:00
|
|
|
HttpResponse::Ok()
|
|
|
|
.header("Access-Control-Allow-Origin", "*")
|
|
|
|
.content_type("text/csv")
|
|
|
|
.body(ripgrep(query))
|
2018-10-11 21:59:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn ripgrep(query: Query<SearchQuery>) -> String {
|
2018-10-22 18:55:47 +00:00
|
|
|
let page = query.page.unwrap_or(1);
|
|
|
|
let size = query.size.unwrap_or(10);
|
2018-10-22 22:31:34 +00:00
|
|
|
let offset = size * (page - 1);
|
2018-12-02 05:15:54 +00:00
|
|
|
let csv_file = File::open(torrents_csv_file());
|
2018-10-22 18:55:47 +00:00
|
|
|
|
2018-10-22 22:31:34 +00:00
|
|
|
println!("query = {} , page = {}, size = {}", query.q, page, size);
|
2018-10-22 18:55:47 +00:00
|
|
|
|
|
|
|
let results = search_file(csv_file.unwrap(), &query.q).unwrap();
|
|
|
|
|
2018-10-22 22:31:34 +00:00
|
|
|
let last: usize = if offset + size < results.len() {
|
|
|
|
offset + size
|
|
|
|
} else {
|
|
|
|
results.len()
|
|
|
|
};
|
|
|
|
|
|
|
|
results[offset..last].join("")
|
2018-10-22 18:55:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn search_file(file: File, query: &str) -> Result<Vec<String>, Box<Error>> {
|
|
|
|
let pattern = query.replace(" ", ".*");
|
|
|
|
|
2018-10-22 22:31:34 +00:00
|
|
|
let matcher = RegexMatcherBuilder::new()
|
|
|
|
.case_insensitive(true)
|
|
|
|
.build(&pattern)
|
|
|
|
.unwrap();
|
|
|
|
|
2018-10-22 18:55:47 +00:00
|
|
|
let mut matches: Vec<String> = 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 =
|
2018-12-02 05:15:54 +00:00
|
|
|
super::search_file(File::open(super::torrents_csv_file()).unwrap(), "sherlock").unwrap();
|
2018-10-22 18:55:47 +00:00
|
|
|
assert!(results.len() > 2);
|
|
|
|
let end = PreciseTime::now();
|
|
|
|
println!("Query took {} seconds.", start.to(end));
|
|
|
|
}
|
2018-10-11 21:59:30 +00:00
|
|
|
}
|