diff --git a/Cargo.lock b/Cargo.lock index 7afd9d3..a329de2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,6 +9,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "anyhow" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" + [[package]] name = "atty" version = "0.2.14" @@ -310,6 +316,20 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "handlebars" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8ae96a0e0dacf151557ccba95a7a80889f8e74a784484377739628fcdb3996" +dependencies = [ + "log 0.4.8", + "pest", + "pest_derive", + "quick-error", + "serde", + "serde_json", +] + [[package]] name = "headers" version = "0.3.2" @@ -488,6 +508,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + [[package]] name = "matches" version = "0.1.8" @@ -615,6 +641,49 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +dependencies = [ + "maplit", + "pest", + "sha-1", +] + [[package]] name = "pfacts" version = "0.1.0" @@ -718,10 +787,13 @@ dependencies = [ name = "printerfacts" version = "0.1.0" dependencies = [ + "anyhow", + "handlebars", "log 0.4.8", "pfacts", "pretty_env_logger", "rand 0.7.3", + "serde", "tokio", "warp", ] @@ -974,6 +1046,9 @@ name = "serde" version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c" +dependencies = [ + "serde_derive", +] [[package]] name = "serde_derive" @@ -1194,6 +1269,12 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + [[package]] name = "unicase" version = "1.4.2" diff --git a/Cargo.toml b/Cargo.toml index d4535d4..222f01a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,9 +7,12 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1" +handlebars = "3.2.1" log = "0" pfacts = "0.1.0" pretty_env_logger = "0" rand = "0" +serde = { version = "1", features = ["derive"] } tokio = { version = "0.2", features = ["macros"] } warp = "0.2" diff --git a/default.nix b/default.nix index c83abf5..846ba81 100644 --- a/default.nix +++ b/default.nix @@ -14,6 +14,7 @@ in pkgs.dockerTools.buildLayeredImage { config = { Cmd = [ "${printerfacts}/bin/printerfacts" ]; + Env = [ "RUST_LOG=info" ]; WorkingDir = "/"; }; } diff --git a/nix/sources.json b/nix/sources.json index e8b854e..14385e5 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -17,6 +17,18 @@ "url": "https://github.com/justinwoo/easy-dhall-nix/archive/27edaa0c769b9c876f11c50c9ab542061d07def5.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, + "gruvbox-css": { + "branch": "master", + "description": "My minimal Gruvbox CSS file I've been keeping multiple places", + "homepage": null, + "owner": "Xe", + "repo": "gruvbox-css", + "rev": "6e1841c94190a1e06e63a2596767e66c35671320", + "sha256": "0s7whnin63558i70wp3by3rydsdx0qdzh5553km1s29w81npmgj6", + "type": "tarball", + "url": "https://github.com/Xe/gruvbox-css/archive/6e1841c94190a1e06e63a2596767e66c35671320.tar.gz", + "url_template": "https://github.com///archive/.tar.gz" + }, "naersk": { "branch": "master", "description": "Build rust crates in Nix. No configuration, no code generation, no IFD. Sandbox friendly.", diff --git a/printerfacts.nix b/printerfacts.nix index 6d0b5ee..830d384 100644 --- a/printerfacts.nix +++ b/printerfacts.nix @@ -5,9 +5,22 @@ let (path: type: type != "directory" || builtins.baseNameOf path != "target") dir; naersk = pkgs.callPackage sources.naersk { }; + gruvbox-css = pkgs.callPackage sources.gruvbox-css { }; src = srcNoTarget ./.; pfacts = naersk.buildPackage { inherit src; remapPathPrefix = true; }; -in pfacts +in pkgs.stdenv.mkDerivation { + inherit (pfacts) name; + inherit src; + phases = "installPhase"; + + installPhase = '' + mkdir -p $out/static + + cp -rf $src/templates $out/templates + cp -rf ${pfacts}/bin $out/bin + cp -rf ${gruvbox-css}/gruvbox.css $out/static/gruvbox.css + ''; +} diff --git a/src/main.rs b/src/main.rs index 60e43fa..e835bab 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,25 +1,96 @@ -use std::convert::Infallible; +use handlebars::Handlebars; use pfacts::Facts; use rand::prelude::*; +use serde::Serialize; +use std::{convert::Infallible, sync::Arc}; use warp::Filter; async fn give_fact(facts: Facts) -> Result { Ok(facts.choose(&mut thread_rng()).unwrap().clone()) } +#[derive(Serialize)] +struct TemplateContext { + title: &'static str, + fact: Option, + // This key tells handlebars which template is the parent. + parent: &'static str, +} + +struct WithTemplate { + name: &'static str, + value: T, +} + +fn render(template: WithTemplate, hbs: Arc) -> impl warp::Reply +where + T: Serialize, +{ + let render = hbs + .render(template.name, &template.value) + .unwrap_or_else(|err| err.to_string()); + warp::reply::html(render) +} + #[tokio::main] -async fn main() { +async fn main() -> anyhow::Result<()> { pretty_env_logger::init(); let facts = pfacts::make(); + let mut hb = Handlebars::new(); - let mw = warp::any().map(move || facts.clone()); + hb.register_template_file("layout", "./templates/layout.hbs")?; + hb.register_template_file("footer", "./templates/footer.hbs")?; + hb.register_template_file("index", "./templates/index.hbs")?; + hb.register_template_file("error/404", "./templates/error/404.hbs")?; - let fact_handler = warp::any() - .and(mw) + let fact = { + let facts = facts.clone(); + warp::any().map(move || facts.clone()) + }; + let hb = Arc::new(hb); + let handlebars = move |with_template| render(with_template, hb.clone()); + + let files = warp::path("static").and(warp::fs::dir("./static")); + + let fact_handler = warp::get() + .and(warp::path("fact")) + .and(fact) .and_then(give_fact); + let index_handler = warp::get() + .and(warp::path::end()) + .map(move || WithTemplate { + name: "index", + value: TemplateContext { + title: "Printer Facts", + fact: { + let ref facts = facts.clone(); + Some(facts.choose(&mut thread_rng()).unwrap().clone()) + }, + parent: "layout", + }, + }) + .map(handlebars.clone()); + + let not_found_handler = warp::any() + .map(move || WithTemplate { + name: "error/404", + value: TemplateContext { + title: "Not Found", + fact: None, + parent: "layout", + }, + }) + .map(handlebars.clone()); + log::info!("listening on port 5000"); - warp::serve(fact_handler) - .run(([0, 0, 0, 0], 5000)) - .await; + warp::serve( + fact_handler + .or(index_handler) + .or(files) + .or(not_found_handler), + ) + .run(([0, 0, 0, 0], 5000)) + .await; + Ok(()) } diff --git a/static/.gitignore b/static/.gitignore new file mode 100644 index 0000000..bc5370d --- /dev/null +++ b/static/.gitignore @@ -0,0 +1 @@ +gruvbox.css diff --git a/templates/error/404.hbs b/templates/error/404.hbs new file mode 100644 index 0000000..542c039 --- /dev/null +++ b/templates/error/404.hbs @@ -0,0 +1,6 @@ +{{#*inline "page"}} +

This page does not exist!

+ +Go home +{{/inline}} +{{~> (parent)~}} diff --git a/templates/footer.hbs b/templates/footer.hbs new file mode 100644 index 0000000..1c49f2a --- /dev/null +++ b/templates/footer.hbs @@ -0,0 +1,3 @@ + diff --git a/templates/index.hbs b/templates/index.hbs new file mode 100644 index 0000000..2c8cd7c --- /dev/null +++ b/templates/index.hbs @@ -0,0 +1,10 @@ +{{#*inline "page"}} + +

+

+ {{ fact }} +
+

+ +{{/inline}} +{{~> (parent)~}} diff --git a/templates/layout.hbs b/templates/layout.hbs new file mode 100644 index 0000000..46c19d7 --- /dev/null +++ b/templates/layout.hbs @@ -0,0 +1,17 @@ + + + + {{ title }} + + + + +
+

{{ title }}

+ + {{~> page}} + + {{> footer }} +
+ +