use common::compilation::CompilableTo;
use common::validation::Issue;
use query::scope::ScopeContent;
use regex::Regex;
use url::Url;
use lazy_static::lazy_static;
use htmlescape::decode_html;
lazy_static! {
static ref HTML_REGEX: Regex = Regex::new(r"<(.*?)>").unwrap();
static ref SPACE_REGEX: Regex = Regex::new(r"\s{2,}").unwrap();
}
#[derive(Clone)]
pub struct Document {
pub url: Option<String>,
pub data: Vec<u8>,
pub mime: Option<String>,
}
pub enum DocumentReference {
Populated(Document),
Unpopulated(String),
}
pub struct DocumentReferenceBatch {
pub documents: Vec<DocumentReference>,
}
pub struct CompiledDocument {
pub url: Option<String>,
pub raw: String,
pub mime: Option<String>,
pub text: String,
pub domain: Option<String>,
}
pub struct DocumentBatch {
pub documents: Vec<Document>,
}
pub struct CompiledDocumentBatch {
pub documents: Vec<CompiledDocument>,
}
enum DocumentKind {
Html,
Unknown,
}
impl Document {
fn detect_document_kind(&self) -> DocumentKind {
let mut is_html = match &self.mime {
Some(value) => value.eq("text/html"),
None => false,
};
match &self.url {
Some(value) => {
if value.ends_with(".html") {
is_html = true;
}
}
None => (),
};
if is_html {
return DocumentKind::Html;
}
DocumentKind::Unknown
}
pub fn domain(&self) -> Option<String> {
let own_url = match &self.url {
Some(value) => value,
None => return None,
};
let parsed_url = match Url::parse(own_url.as_str()) {
Ok(url) => url,
Err(_) => return None,
};
match parsed_url.host_str() {
Some(value) => Some(String::from(value)),
None => None,
}
}
fn raw(&self) -> String {
String::from_utf8_lossy(self.data.as_slice()).into_owned()
}
fn extract_document_text(&self) -> String {
match &self.detect_document_kind() {
DocumentKind::Html => {
let extracted = String::from(SPACE_REGEX.replace_all(&HTML_REGEX.replace_all(&self.raw(), " "), " "));
match decode_html(extracted.as_str()) {
Ok(value) => value,
Err(_) => extracted
}
},
DocumentKind::Unknown => self.raw(),
}
}
}
impl CompilableTo<CompiledDocument> for Document {
fn compile(&self) -> Result<CompiledDocument, Issue> {
let text = self.extract_document_text();
let domain = self.domain();
let raw = self.raw();
Ok(CompiledDocument {
url: self.url.clone(),
raw: raw,
mime: self.mime.clone(),
text: text,
domain: domain,
})
}
}
impl CompilableTo<CompiledDocumentBatch> for DocumentBatch {
fn compile(&self) -> Result<CompiledDocumentBatch, Issue> {
let mut compiled_documents: Vec<CompiledDocument> = Vec::new();
for document in &self.documents {
let compiled_document = match document.compile() {
Ok(value) => value,
Err(_error) => continue, };
compiled_documents.push(compiled_document);
}
Ok(CompiledDocumentBatch {
documents: compiled_documents,
})
}
}
impl CompiledDocument {
pub fn content(&self, content: ScopeContent) -> &String {
match content {
ScopeContent::Raw => &self.raw,
ScopeContent::Text => &self.text,
}
}
}
impl From<Vec<Document>> for DocumentBatch {
fn from(docs: Vec<Document>) -> DocumentBatch {
DocumentBatch { documents: docs }
}
}
impl From<Vec<DocumentReference>> for DocumentReferenceBatch {
fn from(docs: Vec<DocumentReference>) -> DocumentReferenceBatch {
DocumentReferenceBatch { documents: docs }
}
}