use common::pattern::PatternMatch;
use input::document::{CompiledDocument, CompiledDocumentBatch, DocumentBatch, DocumentReferenceBatch, DocumentReference, Document};
use output::output::{Output, OutputBatch};
use query::query::{CompiledQuery, CompiledQueryGroup};
use std::collections::{HashMap, HashSet};
use common::compilation::CompilableTo;
use common::retrieve::load_document;
use std::sync::mpsc;
use std::thread;
pub trait Scanner: Clone + Send {
fn scan_batch(&self, documents: &CompiledDocumentBatch) -> OutputBatch;
fn scan_single(&self, document: &CompiledDocument) -> OutputBatch;
fn scan_concurrently(
&self,
batches: mpsc::Receiver<DocumentReferenceBatch>,
threads: u8,
) -> mpsc::Receiver<OutputBatch>;
}
impl Scanner for CompiledQuery {
fn scan_single(&self, document: &CompiledDocument) -> OutputBatch {
let placeholder_string_no_url = String::from("");
let url = match &document.url {
Some(value) => &value,
None => &placeholder_string_no_url, };
if !(&self.scope.pattern.quick_check(url)) {
return OutputBatch::from(vec![]); }
let input = document.content(self.scope.content);
let mut matches: HashMap<&String, bool> = HashMap::new();
let mut match_results: Vec<PatternMatch> = Vec::new();
for trigger in &self.triggers {
let does_match = trigger.quick_check(&input);
if does_match {
match_results.push(match trigger.full_check(&input) {
Some(value) => value,
None => return OutputBatch::from(vec![]), });
}
matches.insert(&trigger.id, does_match);
}
if match self.threshold.evaluate(&matches) {
Ok(evaluation) => evaluation,
Err(_) => return OutputBatch::from(vec![]), } {
return OutputBatch::from(vec![Output::new(&document, &self, match_results, None)]);
} else {
return OutputBatch::from(vec![]);
}
}
fn scan_batch(&self, documents: &CompiledDocumentBatch) -> OutputBatch {
let mut outputs: Vec<Output> = Vec::new();
for document in &documents.documents {
let output_batch = self.scan_single(document);
outputs.extend(output_batch.outputs);
}
OutputBatch::from(outputs)
}
fn scan_concurrently(
&self,
batches: mpsc::Receiver<DocumentReferenceBatch>,
threads: u8,
) -> mpsc::Receiver<OutputBatch> {
let query_group = CompiledQueryGroup::from(self.clone());
query_group.scan_concurrently(batches, threads)
}
}
impl Scanner for CompiledQueryGroup {
fn scan_single(&self, document: &CompiledDocument) -> OutputBatch {
let mut output_batch = OutputBatch::new();
let to_feed = document.content(self.regex_feed);
let matches: Vec<_> = self.regex_collected.matches(to_feed).into_iter().collect();
let mut queries_to_run: HashSet<usize> = HashSet::new();
for match_item in matches {
queries_to_run.insert(match self.regex_collected_query_index.get(match_item) {
Some(index) => *index,
None => return OutputBatch::from(vec![]), });
}
for query_index in queries_to_run {
let query = match self.queries.get(query_index) {
Some(value) => value,
None => return OutputBatch::from(vec![]), };
output_batch.merge_with(query.scan_single(document));
}
for query in &self.always_run_queries {
output_batch.merge_with(query.scan_single(document));
}
output_batch
}
fn scan_batch(&self, documents: &CompiledDocumentBatch) -> OutputBatch {
let mut output_batch = OutputBatch::from(vec![]);
for document in &documents.documents {
output_batch.merge_with(self.scan_single(document));
}
output_batch
}
fn scan_concurrently(
&self,
batches: mpsc::Receiver<DocumentReferenceBatch>,
threads: u8,
) -> mpsc::Receiver<OutputBatch> {
let (ultimate_transmitter, ultimate_receiver) = mpsc::channel::<OutputBatch>();
let cloned_self = self.clone();
thread::spawn(move || {
let (tx_requests, rx_requests) = mpsc::channel::<thread::ThreadId>();
let mut handles: Vec<thread::JoinHandle<_>> = Vec::new();
let mut outgoing: HashMap<thread::ThreadId, mpsc::Sender<DocumentReferenceBatch>> =
HashMap::new();
for _ in 0..threads {
let (tx_inputs, rx_inputs) = mpsc::channel::<DocumentReferenceBatch>();
let tx_request_documents = tx_requests.clone();
let tx_send_output = ultimate_transmitter.clone();
let supercloned_self = cloned_self.clone(); let handle = thread::spawn(move || {
let id = thread::current().id();
loop {
match tx_request_documents.send(id) {
Ok(_) => (),
Err(_) => break,
};
let batch = match rx_inputs.recv() {
Ok(values) => values,
Err(_) => break, };
let mut documents: Vec<Document> = Vec::new();
for document_reference in batch.documents {
documents.push(match document_reference {
DocumentReference::Populated(document) => document,
DocumentReference::Unpopulated(path) => {
match load_document(&path) {
Ok(document) => {
document
},
Err(_issue) => {
continue;
}, }
}
});
}
let populated_batch = DocumentBatch::from(documents);
let compiled_batch = match populated_batch.compile() {
Ok(value) => value,
Err(_) => continue, };
let outputs = supercloned_self.scan_batch(&compiled_batch);
match tx_send_output.send(outputs) {
Ok(_) => (),
Err(_) => break, };
}
drop(tx_send_output);
});
outgoing.insert(handle.thread().id(), tx_inputs);
handles.push(handle);
}
loop {
let request = match rx_requests.recv() {
Ok(request) => request,
Err(_error) => break,
};
let batch_to_send = match batches.recv() {
Ok(batch) => batch,
Err(_) => {
drop(rx_requests);
break;
}, };
match outgoing.get(&request) {
Some(channel) => {
match channel.send(batch_to_send) {
Ok(_) => (),
Err(_) => continue };
}
None => break, }
}
for outgoing_sender in outgoing.values() {
drop(outgoing_sender);
}
});
ultimate_receiver
}
}