From 6269636e5a71629fd5599fe67aac4b16ead44e87 Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Sun, 25 Feb 2024 16:17:21 +0100
Subject: [PATCH] feat: Add favicon support; improvements & bugfixes
---
Cargo.lock | 154 +++++++++++++++++++++++++++++++++
Cargo.toml | 3 +
src/engines/bing.rs | 11 ++-
src/engines/brave.rs | 11 ++-
src/engines/duckduckgo.rs | 7 +-
src/engines/engine_base.rs | 126 +++++++++++++++++++++++++--
src/main.rs | 86 +++++++++---------
src/public/css/style.css | 150 +++++++++++++++++++++++++++-----
src/public/html/beginning.html | 28 +++---
src/public/html/frontpage.html | 19 ++++
src/public/html/result.html | 31 +++++++
src/rocket_requests.rs | 17 ++++
src/static_files.rs | 56 +++++++++---
13 files changed, 597 insertions(+), 102 deletions(-)
create mode 100644 src/public/html/frontpage.html
create mode 100644 src/public/html/result.html
create mode 100644 src/rocket_requests.rs
diff --git a/Cargo.lock b/Cargo.lock
index 87076ca..91a529e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -17,6 +17,19 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+[[package]]
+name = "ahash"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d713b3834d76b85304d4d525563c1276e2e30dc97cc67bfb4585a4a29fc2c89f"
+dependencies = [
+ "cfg-if",
+ "getrandom",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
[[package]]
name = "aho-corasick"
version = "1.1.2"
@@ -26,6 +39,21 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "async-stream"
version = "0.3.5"
@@ -210,6 +238,20 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+[[package]]
+name = "chrono"
+version = "0.4.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "wasm-bindgen",
+ "windows-targets 0.52.0",
+]
+
[[package]]
name = "clang-sys"
version = "1.7.0"
@@ -637,6 +679,29 @@ dependencies = [
"tokio-native-tls",
]
+[[package]]
+name = "iana-time-zone"
+version = "0.1.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
[[package]]
name = "idna"
version = "0.5.0"
@@ -905,6 +970,15 @@ dependencies = [
"winapi",
]
+[[package]]
+name = "num-traits"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
+dependencies = [
+ "autocfg",
+]
+
[[package]]
name = "num_cpus"
version = "1.16.0"
@@ -1038,6 +1112,48 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+[[package]]
+name = "phf"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
+dependencies = [
+ "phf_macros",
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
+dependencies = [
+ "phf_shared",
+ "rand",
+]
+
+[[package]]
+name = "phf_macros"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
+dependencies = [
+ "phf_generator",
+ "phf_shared",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
+dependencies = [
+ "siphasher",
+]
+
[[package]]
name = "pin-project-lite"
version = "0.2.13"
@@ -1546,6 +1662,12 @@ dependencies = [
"libc",
]
+[[package]]
+name = "siphasher"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
+
[[package]]
name = "slab"
version = "0.4.9"
@@ -1637,14 +1759,17 @@ dependencies = [
name = "tcp_test"
version = "0.1.0"
dependencies = [
+ "ahash",
"async-trait",
"bytes",
+ "chrono",
"futures",
"html-escape",
"lazy-regex",
"lazy_static",
"log",
"mio",
+ "phf",
"regex",
"reqwest",
"rocket",
@@ -2144,6 +2269,15 @@ dependencies = [
"windows-targets 0.48.5",
]
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets 0.52.0",
+]
+
[[package]]
name = "windows-sys"
version = "0.48.0"
@@ -2304,6 +2438,26 @@ dependencies = [
"is-terminal",
]
+[[package]]
+name = "zerocopy"
+version = "0.7.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "zeroize"
version = "1.7.0"
diff --git a/Cargo.toml b/Cargo.toml
index 44825fd..91618dc 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,14 +6,17 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+ahash = "0.8.9"
async-trait = "0.1.77"
bytes = "1.5.0"
+chrono = "0.4.34"
futures = "0.3.30"
html-escape = "0.2.13"
lazy-regex = "3.1.0"
lazy_static = "1.4.0"
log = "0.4.20"
mio = { version = "0.8.10", features = ["net", "os-poll", ] }
+phf = { version = "0.11.2", features = ["macros"] }
regex = "1.10.3"
reqwest = { version = "0.11.23", features = ["stream"] }
rocket = "0.5.0"
diff --git a/src/engines/bing.rs b/src/engines/bing.rs
index ade3009..cd5d185 100644
--- a/src/engines/bing.rs
+++ b/src/engines/bing.rs
@@ -14,9 +14,11 @@ pub mod bing {
lazy_static! {
static ref RESULTS_START: Regex = Regex::new(r#"id="b_results""#).unwrap();
- static ref SINGLE_RESULT: Regex = Regex::new(r#"
(?P.+?).*?(((?P.*?).*?"#).unwrap();
+ static ref SINGLE_RESULT: Regex = Regex::new(r#".*?
.+?)")?.*?(?P.+?).*?(((?P.*?).*?"#).unwrap();
}
+ const DATE_FORMAT: &str = "%b %d, %Y";
+
#[derive(Clone, Debug)]
pub struct Bing {
positions: EnginePositions,
@@ -24,8 +26,11 @@ pub mod bing {
impl EngineBase for Bing {
fn parse_next<'a>(&mut self) -> Option {
- self.positions
- .handle_block_using_default_method(&SINGLE_RESULT, SearchEngine::Bing)
+ self.positions.handle_block_using_default_method(
+ &SINGLE_RESULT,
+ SearchEngine::Bing,
+ Some(DATE_FORMAT),
+ )
}
fn push_packet<'a>(&mut self, packet: impl Iterator- ) {
diff --git a/src/engines/brave.rs b/src/engines/brave.rs
index ac72a35..4b573d5 100644
--- a/src/engines/brave.rs
+++ b/src/engines/brave.rs
@@ -14,9 +14,11 @@ pub mod brave {
lazy_static! {
static ref RESULTS_START: Regex = Regex::new(r#"(?P.+?)
.+?(?P.+?)
"#).unwrap();
+ static ref SINGLE_RESULT: Regex = Regex::new(r#".+?(?:(?P.+?) - )?(?P.+?)
.*?.*?"#).unwrap();
}
+ const DATE_FORMAT: &str = "%m %d, %Y";
+
#[derive(Clone, Debug)]
pub struct Brave {
positions: EnginePositions,
@@ -24,8 +26,11 @@ pub mod brave {
impl EngineBase for Brave {
fn parse_next<'a>(&mut self) -> Option {
- self.positions
- .handle_block_using_default_method(&SINGLE_RESULT, SearchEngine::Brave)
+ self.positions.handle_block_using_default_method(
+ &SINGLE_RESULT,
+ SearchEngine::Brave,
+ Some(DATE_FORMAT),
+ )
}
fn push_packet<'a>(&mut self, packet: impl Iterator- ) {
diff --git a/src/engines/duckduckgo.rs b/src/engines/duckduckgo.rs
index b0efac4..b98fa5d 100644
--- a/src/engines/duckduckgo.rs
+++ b/src/engines/duckduckgo.rs
@@ -25,8 +25,11 @@ pub mod duckduckgo {
impl EngineBase for DuckDuckGo {
fn parse_next<'a>(&mut self) -> Option {
- self.positions
- .handle_block_using_default_method(&SINGLE_RESULT, SearchEngine::DuckDuckGo)
+ self.positions.handle_block_using_default_method(
+ &SINGLE_RESULT,
+ SearchEngine::DuckDuckGo,
+ None,
+ )
}
fn push_packet<'a>(&mut self, packet: impl Iterator
- ) {
diff --git a/src/engines/engine_base.rs b/src/engines/engine_base.rs
index 7412646..b6c1fec 100644
--- a/src/engines/engine_base.rs
+++ b/src/engines/engine_base.rs
@@ -1,9 +1,16 @@
pub mod engine_base {
use core::fmt;
- use std::{fmt::Debug, fmt::Display, sync::Arc};
+ use std::{
+ fmt::{Debug, Display},
+ hash::Hash,
+ ops::Sub,
+ sync::Arc,
+ };
+ use chrono::{DateTime, TimeDelta, TimeZone, Utc};
use futures::{lock::Mutex, Future, StreamExt};
use lazy_static::lazy_static;
+ use phf::phf_map;
use regex::Regex;
use reqwest::{Error, Response};
use rustc_hash::FxHashMap;
@@ -16,6 +23,9 @@ pub mod engine_base {
static ref STRIP: Regex = Regex::new(r"[\s\n]+").unwrap();
static ref STRIP_HTML_TAGS: Regex =
Regex::new(r#"<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>"#).unwrap();
+ static ref RELATIVE_DATETIME_PARSER: Regex =
+ Regex::new(r#"(?P\d+) (?Psecond|minute|hour|day|week|month|year)s? ago"#)
+ .unwrap();
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
@@ -35,21 +45,34 @@ pub mod engine_base {
}
}
- #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
+ #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+ pub struct SearchResultDate {
+ pub date: DateTime,
+ // true if original date wasn't available and only
+ // a relative time such as "2 hours ago" was provided
+ pub is_relative: bool,
+ }
+
+ #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct SearchResult {
pub title: String,
pub url: String,
pub description: String,
pub engine: SearchEngine,
+ pub image_url: Option,
+ pub date: Option,
+ }
+
+ impl Hash for SearchResult {
+ fn hash(&self, state: &mut H) {
+ self.url.hash(state);
+ }
}
impl SearchResult {
pub fn get_html_id(&self) -> String {
- format!(
- "html-id-{}-{}",
- hash_string(&self.url),
- self.url[self.url.len() - 5..].to_string(),
- )
+ // IDs must start with a letter, so we add an "h" (html ID) to the beginning
+ format!("h{:X}", hash_string(&self.url),)
}
}
@@ -77,12 +100,26 @@ pub mod engine_base {
let url = req.url().clone();
let mut stream = req.bytes_stream();
+ let mut debug_has_fetched_once = false;
+ let mut debug_content = Vec::new();
+ if cfg!(debug_assertions) {
+ println!("Requesting: {}", url);
+ }
+
while let Some(chunk) = stream.next().await {
let buffer = chunk.unwrap();
self.push_packet(buffer.iter());
+ if cfg!(debug_assertions) {
+ debug_content.extend(buffer);
+ }
+
while let Some(result) = self.parse_next() {
+ if cfg!(debug_assertions) {
+ debug_has_fetched_once = true;
+ }
+
if tx.send(result).await.is_err() {
return Err(());
}
@@ -90,11 +127,25 @@ pub mod engine_base {
}
while let Some(result) = self.parse_next() {
+ if cfg!(debug_assertions) {
+ debug_has_fetched_once = true;
+ }
+
if tx.send(result).await.is_err() {
return Err(());
}
}
+ if cfg!(debug_assertions) {
+ if debug_has_fetched_once {
+ println!("Finished fetching: {}", url);
+ } else {
+ println!("{}", "==============");
+ println!("No results for: {}", url);
+ println!("{}", String::from_utf8_lossy(&debug_content));
+ }
+ }
+
Ok(())
}
}
@@ -105,6 +156,16 @@ pub mod engine_base {
pub started: bool,
}
+ static UNIT_VALUES_MAP: phf::Map<&'static str, u32> = phf_map! {
+ "second" => 1,
+ "minute" => 60,
+ "hour" => 60 * 60,
+ "day" => 60 * 60 * 24,
+ "week" => 60 * 60 * 24 * 7,
+ "month" => 60 * 60 * 30,
+ "year" => 60 * 60 * 365,
+ };
+
impl EnginePositions {
pub fn new() -> Self {
EnginePositions {
@@ -113,6 +174,25 @@ pub mod engine_base {
}
}
+ pub fn parse_date(date: &str) -> Option> {
+ let raw_date_stripped = date.split_whitespace().collect::>().join(" ");
+
+ if let Some(capture) = RELATIVE_DATETIME_PARSER.captures(&raw_date_stripped) {
+ let now = Utc::now();
+ let amount = capture.name("amount")?.as_str().parse::().ok()?;
+ let unit = capture.name("unit")?.as_str();
+
+ let multiplier = UNIT_VALUES_MAP.get(&unit)?.clone() as i64;
+ let seconds_elapsed = amount * multiplier;
+
+ let publish_date = now - TimeDelta::seconds(seconds_elapsed);
+
+ Some(publish_date)
+ } else {
+ None
+ }
+ }
+
pub fn slice_remaining_block(&mut self, start_position: &usize) {
let previous_block_bytes = self.previous_block.as_bytes().to_vec();
let remaining_bytes = previous_block_bytes[*start_position..].to_vec();
@@ -142,6 +222,7 @@ pub mod engine_base {
&mut self,
single_result_regex: &Regex,
engine: SearchEngine,
+ date_format: Option<&str>,
) -> Option {
if self.started {
if let Some(capture) = single_result_regex.captures(&self.previous_block.to_owned())
@@ -157,12 +238,43 @@ pub mod engine_base {
let url = decode(capture.name("url").unwrap().as_str())
.unwrap()
.into_owned();
+ let image = match capture.name("image") {
+ Some(image) => Some(image.as_str().to_string()),
+ None => None,
+ };
+
+ let mut publish_date: Option = None;
+
+ if date_format.is_some() {
+ let date = capture.name("date");
+ publish_date = match date {
+ Some(date) => {
+ match DateTime::parse_from_str(date.as_str(), date_format.unwrap())
+ {
+ Ok(parsed_date) => Some(SearchResultDate {
+ date: parsed_date.to_utc(),
+ is_relative: false,
+ }),
+ Err(_) => match EnginePositions::parse_date(&date.as_str()) {
+ Some(parsed_date) => Some(SearchResultDate {
+ date: parsed_date.to_utc(),
+ is_relative: true,
+ }),
+ None => None,
+ },
+ }
+ }
+ None => None,
+ };
+ }
let result = SearchResult {
title,
description,
url,
engine,
+ image_url: image,
+ date: publish_date,
};
let end_position = capture.get(0).unwrap().end();
diff --git a/src/main.rs b/src/main.rs
index e19d236..2089166 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,15 +1,18 @@
use std::str;
+use ahash::AHashSet;
use engines::bing::bing::Bing;
use engines::brave::brave::Brave;
use engines::duckduckgo::duckduckgo::DuckDuckGo;
use engines::engine_base::engine_base::SearchResult;
use lazy_static::lazy_static;
-use regex::Regex;
+use rocket::form::Form;
use rocket::response::content::{RawCss, RawHtml};
use rocket::response::stream::TextStream;
use rocket::time::Instant;
-use static_files::static_files::{render_beginning_html, render_finished_css};
+use static_files::static_files::{
+ render_beginning_html, render_finished_css, render_result, render_result_engine_visibility,
+};
use tokio::sync::mpsc;
use crate::static_files::static_files::read_file_contents;
@@ -25,26 +28,8 @@ pub mod utils;
extern crate rocket;
lazy_static! {
- static ref HTML_BEGINNING: String =
- read_file_contents("./src/public/html/beginning.html").unwrap();
- static ref SET_VALUE_REPLACE: Regex = Regex::new(r#"\{\% search_value \%\}"#).unwrap();
static ref HTML_END: String = read_file_contents("./src/public/html/end.html").unwrap();
static ref TAILWIND_CSS: String = read_file_contents("./src/public/css/style.css").unwrap();
- static ref FINISHED_CSS: String = read_file_contents("./src/public/css/finished.css").unwrap();
- static ref FINISHED_NAME_REPLACE: Regex = Regex::new(r#"(__engine__)"#).unwrap();
- static ref FINISHED_TIME_REPLACE: Regex = Regex::new(r#"{% time %}"#).unwrap();
-}
-
-#[get("/search")]
-fn search_get() -> &'static str {
- "
-
-
-
- "
}
#[get("/style.css")]
@@ -52,8 +37,29 @@ fn get_tailwindcss() -> RawCss<&'static str> {
RawCss(&TAILWIND_CSS)
}
-#[get("/searchquery?")]
-async fn hello<'a>(query: &str) -> RawHtml {
+#[get("/")]
+async fn search_get() -> RawHtml<&'static str> {
+ RawHtml(include_str!("./public/html/frontpage.html"))
+}
+
+#[derive(FromForm)]
+struct Body {
+ query: String,
+}
+
+macro_rules! search {
+ ($engine:ident,$query_ref:expr,$tx_ref:expr) => {{
+ tokio::spawn(async move {
+ let mut engine = $engine::new();
+
+ engine.search($query_ref, $tx_ref).await
+ })
+ }};
+}
+
+#[post("/", data = "")]
+async fn search_post<'a>(body: Form) -> RawHtml {
+ let query = &body.query;
let query_brave = query.to_owned().clone();
let query_duckduckgo = query.to_owned().clone();
let query_bing = query.to_owned().clone();
@@ -72,31 +78,25 @@ async fn hello<'a>(query: &str) -> RawHtml {
let now = Instant::now();
- let brave_task = tokio::spawn(async move {
- let mut brave = Brave::new();
-
- brave.search(&query_brave, tx_brave).await;
- });
-
- let duckduckgo_task = tokio::spawn(async move {
- let mut duckduckgo = DuckDuckGo::new();
-
- duckduckgo.search(&query_duckduckgo, tx_duckduckgo).await;
- });
-
- let bing_task = tokio::spawn(async move {
- let mut bing = Bing::new();
-
- bing.search(&query_bing, tx_bing).await;
- });
+ let brave_task = search!(Brave, &query_brave, tx_brave);
+ let bing_task = search!(Bing, &query_bing, tx_bing);
+ let duckduckgo_task = search!(DuckDuckGo, &query_duckduckgo, tx_duckduckgo);
let beginning_html = render_beginning_html(&query);
+ let mut results: AHashSet = AHashSet::new();
+
RawHtml(TextStream! {
yield beginning_html;
while !brave_task.is_finished() || !duckduckgo_task.is_finished() || !bing_task.is_finished() {
while let Some(result) = rx.recv().await {
+ if results.contains(&result.url) {
+ yield render_result_engine_visibility(&result.get_html_id(), &result.engine);
+
+ continue;
+ }
+
if !first_result_yielded {
let diff = first_result_start.elapsed().whole_milliseconds();
first_result_yielded = true;
@@ -123,9 +123,10 @@ async fn hello<'a>(query: &str) -> RawHtml {
yield render_finished_css("duckduckgo", now.elapsed().whole_milliseconds());
}
- let text = format!("
{}
{}
{}", &result.title, &result.description, &result.engine.to_string());
+ yield render_result(&result);
+ yield render_result_engine_visibility(&result.get_html_id(), &result.engine);
- yield text.to_string();
+ results.insert(result.url.to_string());
}
}
@@ -151,7 +152,6 @@ async fn hello<'a>(query: &str) -> RawHtml {
#[launch]
async fn rocket() -> _ {
rocket::build()
- .mount("/", routes![hello])
- .mount("/", routes![search_get])
+ .mount("/", routes![search_post, search_get])
.mount("/", routes![get_tailwindcss])
}
diff --git a/src/public/css/style.css b/src/public/css/style.css
index d60513e..797af59 100644
--- a/src/public/css/style.css
+++ b/src/public/css/style.css
@@ -108,30 +108,12 @@ header {
height: 2em;
padding: 0.5em;
border-radius: 50%;
-}
-
-li {
- list-style: none;
- background: #222;
- padding: 1em;
- margin: 1em 0;
- border-radius: 0.5em;
- animation: moveIn 0.5s;
-}
-
-li h1 {
- color: #fff;
text-decoration: none;
- font-size: 1.2rem;
-}
-
-li p {
- color: #aaa;
- line-height: 1.5;
- font-size: 0.9rem;
}
li.fake {
+ background: #222;
+ border-radius: 0.5em;
height: 6em;
animation: fakeShimmer 0.8s infinite linear;
}
@@ -178,3 +160,131 @@ li.fake.small {
opacity: 1;
}
}
+
+#front-search {
+ min-width: 20em;
+ font-size: 1.25rem;
+ padding: 1em 1.6em;
+ border: none;
+ border-radius: 10em;
+ background: #555;
+ color: #fff;
+}
+
+#frontage {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.result {
+ list-style: none;
+ margin: 1em 0;
+ animation: moveIn 0.5s;
+}
+
+.result article {
+ display: flex;
+ align-items: center;
+ gap: 1em;
+}
+
+.result .content {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5em;
+ justify-content: start;
+}
+
+.result a {
+ padding: 1em;
+ text-decoration: none;
+ display: flex;
+ flex-direction: column;
+ gap: 0.5em;
+
+ background: #222;
+ border-radius: 0.5em;
+}
+
+.result .url {
+ /* Show max of 1 line */
+ display: -webkit-box;
+ -webkit-line-clamp: 1;
+ line-clamp: 1;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+}
+
+.result a:visited {
+ background: #444;
+ color: red;
+}
+
+.result h3 {
+ color: #fff;
+ text-decoration: none;
+ font-size: 1.2rem;
+}
+
+.result p {
+ color: #aaa;
+ line-height: 1.5;
+ font-size: 0.9rem;
+}
+
+.result small {
+ color: #888;
+}
+
+.result .search-engines {
+ display: flex;
+ gap: 1em;
+ margin-top: 1em;
+}
+
+.result .search-engines>li {
+ list-style: none;
+ opacity: 0;
+ color: #aaa;
+}
+
+.result .image {
+ display: flex;
+ justify-content: center;
+
+ background: #333;
+ border-radius: .5em;
+ width: 3em;
+ height: 3em;
+}
+
+.result .image .round {
+ width: 2em;
+ height: 2em;
+ margin: .5em;
+ border-radius: .5em;
+ overflow: hidden;
+}
+
+.result .image img {
+ width: 100%;
+ height: 100%;
+ position: relative;
+
+ /* Hide broken image icon */
+ -moz-force-broken-image-icon: 0;
+}
+
+.result .image img::after {
+ content: " ";
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ display: block;
+ background-size: contain;
+ background-repeat: no-repeat;
+ background-color: #333;
+}
diff --git a/src/public/html/beginning.html b/src/public/html/beginning.html
index bb78c25..bfffc0b 100644
--- a/src/public/html/beginning.html
+++ b/src/public/html/beginning.html
@@ -10,12 +10,14 @@
-
@@ -225,13 +227,13 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/src/public/html/frontpage.html b/src/public/html/frontpage.html
new file mode 100644
index 0000000..21c0da2
--- /dev/null
+++ b/src/public/html/frontpage.html
@@ -0,0 +1,19 @@
+
+
+
+
+ tifsep
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/public/html/result.html b/src/public/html/result.html
new file mode 100644
index 0000000..bb5af93
--- /dev/null
+++ b/src/public/html/result.html
@@ -0,0 +1,31 @@
+-
+
+
+
+
+
+

+
+
+
+
{% url %}
+
{% title %}
+
{% description %}
+
+
+
+ - Bing
+ - DuckDuckGo
+ - Brave
+
+ {% date %}
+
+
+
diff --git a/src/rocket_requests.rs b/src/rocket_requests.rs
new file mode 100644
index 0000000..ed41344
--- /dev/null
+++ b/src/rocket_requests.rs
@@ -0,0 +1,17 @@
+struct UserAgent(String);
+
+// impl<'a, 'r> FromRequest<'a, 'r> for Token {
+// type Error = Infallible;
+//
+// fn from_request(request: &'a Request<'r>) -> request::Outcome {
+// let token = request.headers().get_one("token");
+// match token {
+// Some(token) => {
+// // check validity
+// Outcome::Success(Token(token.to_string()))
+// },
+// // token does not exist
+// None => Outcome::Failure(Status::Unauthorized)
+// }
+// }
+// }
diff --git a/src/static_files.rs b/src/static_files.rs
index 5b71e6a..cb32796 100644
--- a/src/static_files.rs
+++ b/src/static_files.rs
@@ -1,10 +1,18 @@
pub mod static_files {
- use lazy_static::lazy_static;
use std::{
+ fmt::Debug,
fs::File,
+ hash::Hash,
io::{Error, Read},
};
+ use reqwest::Url;
+
+ use crate::{
+ engines::engine_base::engine_base::{SearchEngine, SearchResult},
+ utils::utils::hash_string,
+ };
+
pub fn read_file_contents(path: &str) -> Result {
let mut contents = String::new();
@@ -15,11 +23,7 @@ pub mod static_files {
Ok(contents)
}
- lazy_static! {
- static ref HTML_BEGINNING: String =
- read_file_contents("./src/public/html/beginning.html").unwrap();
- }
-
+ const HTML_BEGINNING: &str = include_str!("./public/html/beginning.html");
const HTML_BEGINNING_QUERY_REPLACE: &str = r#"{% search_value %}"#;
pub fn render_beginning_html(query: &str) -> String {
@@ -29,17 +33,47 @@ pub mod static_files {
)
}
- lazy_static! {
- static ref FINISHED_CSS: String =
- read_file_contents("./src/public/css/finished.css").unwrap();
- }
+ const FINISHED_CSS: &str = include_str!("./public/css/finished.css");
pub fn render_finished_css(engine: &str, time: i128) -> String {
format!(
"",
FINISHED_CSS
.replace("__engine__", engine)
- .replace("{% time %}", &format!("{}ms", &time.to_string()))
+ .replace("{% time %}", &time.to_string())
+ )
+ }
+
+ const HTML_RESULT: &str = include_str!("./public/html/result.html");
+
+ pub fn render_result(result: &SearchResult) -> String {
+ HTML_RESULT
+ .replace("{% title %}", &result.title)
+ .replace("{% url %}", &result.url)
+ .replace(
+ "{% url_host %}",
+ Url::parse(&result.url).unwrap().host_str().unwrap(),
+ )
+ .replace("{% description %}", &result.description)
+ .replace("__ID__", &result.get_html_id())
+ .replace(
+ "{% image_url %}",
+ &result.image_url.clone().unwrap_or("".to_string()),
+ )
+ .replace(
+ "{% date %}",
+ &(match &result.date {
+ Some(date_info) => date_info.date.format("%d. %B %Y").to_string(),
+ None => "".to_string(),
+ }),
+ )
+ }
+
+ pub fn render_result_engine_visibility(id: &str, engine: &SearchEngine) -> String {
+ format!(
+ "",
+ id,
+ engine.to_string().to_lowercase()
)
}
}