Initial Commit

This commit is contained in:
Jai A 2020-05-09 22:11:48 -07:00
commit da19743ba5
12 changed files with 2681 additions and 0 deletions

105
.gitignore vendored Normal file
View File

@ -0,0 +1,105 @@
# Created by https://www.gitignore.io/api/rust,clion
# Edit at https://www.gitignore.io/?templates=rust,clion
### CLion ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### CLion Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
.idea/**/sonarlint/
# SonarQube Plugin
.idea/**/sonarIssues.xml
# Markdown Navigator plugin
.idea/**/markdown-navigator.xml
.idea/**/markdown-navigator/
### Rust ###
# Generated by Cargo
# will have compiled files and executables
/target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# End of https://www.gitignore.io/api/rust,clion

8
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

11
.idea/Fabricate.iml generated Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="CPP_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$/../fabricate">
<sourceFolder url="file://$MODULE_DIR$/../fabricate/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/../fabricate/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/dictionaries/geometrically.xml generated Normal file
View File

@ -0,0 +1,8 @@
<component name="ProjectDictionaryState">
<dictionary name="geometrically">
<words>
<w>serde</w>
<w>tantivy</w>
</words>
</dictionary>
</component>

6
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
</project>

8
.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/../fabricate/.idea/Fabricate.iml" filepath="$PROJECT_DIR$/../fabricate/.idea/Fabricate.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

2302
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

18
Cargo.toml Normal file
View File

@ -0,0 +1,18 @@
[package]
name = "fabricate"
version = "0.1.0"
authors = ["geometrically <jai.a@tuta.io>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
actix-web = "2.0"
actix-rt = "1.1.1"
handlebars = { version = "3.0.0", features = ["dir_source"] }
serde_json = "1.0"
serde_derive = "1.0.107"
serde = "1.0.107"
tantivy = "0.12.0"
tempdir = "0.3.7"

159
src/main.rs Normal file
View File

@ -0,0 +1,159 @@
#[macro_use]
extern crate serde_json;
#[macro_use]
extern crate tantivy;
use actix_web::{web, web::Data, web::Query, App, HttpRequest, HttpResponse, HttpServer, Responder, get, post};
use handlebars::Handlebars;
use serde_derive::Deserialize;
use tantivy::collector::TopDocs;
use tantivy::query::{QueryParser};
use tantivy::schema::*;
use tantivy::{Index, IndexReader};
use tantivy::ReloadPolicy;
use tempdir::TempDir;
#[derive(Deserialize)]
pub struct SearchRequest {
q: Option<String>,
}
#[post("search")]
async fn search_post(Query(info): Query<SearchRequest>, reader: Data<IndexReader>, parser: Data<QueryParser<>>, schema: Data<Schema<>>) -> HttpResponse {
let results = handle_search(Query(info), reader, parser, schema);
let mut data = "{ \"results\": [".to_owned();
for result in &results {
data.push_str(&result);
data.push_str(",");
}
if &results.len() > &(0 as usize) {
data.pop();
}
data.push_str("] }");
HttpResponse::Ok().body(data)
}
#[get("search")]
async fn search(Query(info): Query<SearchRequest>, hb: Data<Handlebars<'_>>, reader: Data<IndexReader>, parser: Data<QueryParser<>>, schema: Data<Schema<>>) -> HttpResponse {
let results = handle_search(Query(info), reader, parser, schema);
let data = json!({
"results": results,
});
let body = hb.render("search", &data).unwrap();
HttpResponse::Ok().body(body)
}
fn handle_search(Query(info): Query<SearchRequest>, reader: Data<IndexReader>, parser: Data<QueryParser<>>, schema: Data<Schema<>>) -> Vec<String>{
let mut search_query : String = "".to_string();
if let Some(q) = info.q {
search_query = q;
}
let searcher = reader.searcher();
let mut results = vec![];
if let Ok(query) = parser.parse_query(&search_query) {
if let Ok(top_docs) = searcher.search(&query, &TopDocs::with_limit(10)) {
for (_score, doc_address) in top_docs {
if let Ok(retrieved_doc) = searcher.doc(doc_address) {
results.push(schema.to_json(&retrieved_doc));
}
}
}
}
return results;
}
#[get("/")]
async fn index(hb: web::Data<Handlebars<'_>>) -> HttpResponse {
let data = json!({
"name": "Handlebars"
});
let body = hb.render("index", &data).unwrap();
HttpResponse::Ok().body(body)
}
#[actix_rt::main]
async fn main() -> tantivy::Result<()> {
//Handlebars
let mut handlebars = Handlebars::new();
handlebars
.register_templates_directory(".html", "./static/templates")
.unwrap();
let handlebars_ref = web::Data::new(handlebars);
//Search
let index_path = TempDir::new("search_index")?;
let mut schema_builder = Schema::builder();
schema_builder.add_text_field("title", TEXT | STORED);
schema_builder.add_text_field("keywords", TEXT | STORED);
schema_builder.add_text_field("description", TEXT | STORED);
schema_builder.add_text_field("body", TEXT);
let schema = schema_builder.build();
let schema_ref = web::Data::new(schema.clone());
let title = schema.get_field("title").unwrap();
let keywords = schema.get_field("keywords").unwrap();
let description = schema.get_field("description").unwrap();
let body = schema.get_field("body").unwrap();
let site_index = Index::create_in_dir(&index_path, schema.clone())?;
let mut index_writer = site_index.writer(50_000_000)?;
index_writer.add_document(doc!(
title => "Magic",
keywords => "Magic Fun Adventure",
description => "A magic mod for magical purposes!",
body => "A cool magic mod made by your mom :)",
));
index_writer.add_document(doc!(
title => "Technology",
keywords => "Technology Fun Adventure",
description => "A tech mod for tech purposes!",
body => "A tech mod made by your mom :)",
));
index_writer.commit()?;
let reader = site_index.reader_builder().reload_policy(ReloadPolicy::OnCommit).try_into()?;
let reader_ref = web::Data::new(reader);
let query_parser = QueryParser::for_index(&site_index, vec![title, body, keywords, description]);
let query_parser_ref = web::Data::new(query_parser);
//Init App
HttpServer::new(move || {
App::new()
.app_data(handlebars_ref.clone())
.app_data(reader_ref.clone())
.app_data(query_parser_ref.clone())
.app_data(schema_ref.clone())
.service(index)
.service(search)
.service(search_post)
})
.bind("127.0.0.1:8000")?
.run()
.await?;
Ok(())
}

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>{{name}} Example</title>
</head>
<body>
<h1>{{name}} example</h1>
<p>This is an example of how to use {{name}} with Actix-Web.</p>
</body>
</html>

View File

@ -0,0 +1,38 @@
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Search</title>
</head>
<body>
<h1>Test Mod Search</h1>
<form>
<label for="search-input">Search for Mods:</label>
<input type="text" id="search-input" oninput="handleSearch()">
</form>
<p>{{results}}</p>
<script>
let input = document.getElementById("search-input");
function handleSearch() {
let safeName = encodeURIComponent(input.value).replace(/%20/g,'+');
let xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
console.log(xmlHttp.responseText);
console.log(JSON.parse(xmlHttp.responseText));
}
}
xmlHttp.open("POST", "search?q=" + safeName, true);
xmlHttp.send(null);
window.history.pushState('Search', 'Search', '/search?q=' + safeName);
}
</script>
</body>
</html>