Skip to content

Commit

Permalink
Revert "Deleted Handlebars and renamed tera-templates to templates"
Browse files Browse the repository at this point in the history
This reverts commit 55b85cf.
  • Loading branch information
Joshua Nelson committed Jul 7, 2020
1 parent a51926f commit 6fb9711
Show file tree
Hide file tree
Showing 31 changed files with 465 additions and 5 deletions.
110 changes: 110 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ serde_json = "1.0"
# iron dependencies
iron = "0.5"
router = "0.5"
handlebars-iron = "0.25"
params = "0.8"
staticfile = { version = "0.4", features = [ "cache" ] }
tempfile = "3.1.0"
Expand Down
1 change: 1 addition & 0 deletions dockerfiles/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ RUN mkdir -p /opt/docsrs/prefix
COPY --from=build /build/target/release/cratesfyi /usr/local/bin
COPY static /opt/docsrs/prefix/public_html
COPY templates /opt/docsrs/templates
COPY tera-templates /opt/docsrs/tera-templates
COPY dockerfiles/entrypoint.sh /opt/docsrs/

WORKDIR /opt/docsrs
Expand Down
28 changes: 25 additions & 3 deletions src/web/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ use crate::{config::Config, db::Pool, impl_webpage, BuildQueue};
use chrono::{DateTime, Utc};
use extensions::InjectExtensions;
use failure::Error;
use handlebars_iron::{DirectorySource, HandlebarsEngine, SourceError};
use iron::{
self,
headers::{CacheControl, CacheDirective, ContentType, Expires, HttpDate},
Expand All @@ -103,6 +104,17 @@ const OPENSEARCH_XML: &[u8] = include_bytes!("opensearch.xml");

const DEFAULT_BIND: &str = "0.0.0.0:3000";

fn handlebars_engine() -> Result<HandlebarsEngine, SourceError> {
// TODO: Use DocBuilderOptions for paths
let mut hbse = HandlebarsEngine::new();
hbse.add(Box::new(DirectorySource::new("./templates", ".hbs")));

// load templates
hbse.reload()?;

Ok(hbse)
}

struct CratesfyiHandler {
shared_resource_handler: Box<dyn Handler>,
router_handler: Box<dyn Handler>,
Expand All @@ -113,9 +125,11 @@ struct CratesfyiHandler {

impl CratesfyiHandler {
fn chain<H: Handler>(inject_extensions: InjectExtensions, base: H) -> Chain {
let hbse = handlebars_engine().expect("Failed to load handlebar templates");

let mut chain = Chain::new(base);
chain.link_before(inject_extensions);

chain.link_after(hbse);
chain
}

Expand Down Expand Up @@ -570,7 +584,7 @@ fn ico_handler(req: &mut Request) -> IronResult<Response> {
}

/// MetaData used in header
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
pub(crate) struct MetaData {
pub(crate) name: String,
pub(crate) version: String,
Expand Down Expand Up @@ -638,7 +652,10 @@ impl_webpage! {
#[cfg(test)]
mod test {
use super::*;
use crate::{test::*, web::match_version};
use crate::{
test::*,
web::{handlebars_engine, match_version},
};
use kuchiki::traits::TendrilSink;
use serde_json::json;

Expand Down Expand Up @@ -847,6 +864,11 @@ mod test {
});
}

#[test]
fn test_templates_are_valid() {
handlebars_engine().expect("Failed to load handlebar templates");
}

#[test]
fn serialize_metadata() {
let mut metadata = MetaData {
Expand Down
190 changes: 190 additions & 0 deletions src/web/page/handlebars.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
//! Generic page struct
use handlebars_iron::Template;
use iron::response::Response;
use iron::{status, IronResult, Set};
use once_cell::sync::Lazy;
use serde::{
ser::{SerializeStruct, Serializer},
Serialize,
};
use serde_json::Value;
use std::collections::BTreeMap;

static RUSTC_RESOURCE_SUFFIX: Lazy<String> =
Lazy::new(|| load_rustc_resource_suffix().unwrap_or_else(|_| "???".into()));

fn load_rustc_resource_suffix() -> Result<String, failure::Error> {
// New instances of the configuration or the connection pool shouldn't be created inside the
// application, but we're removing handlebars so this is not going to be a problem in the long
// term. To avoid wasting resources, the pool is hardcoded to only keep one connection alive.
let mut config = crate::Config::from_env()?;
config.max_pool_size = 1;
config.min_pool_idle = 1;
let pool = crate::db::Pool::new(&config)?;
let conn = pool.get()?;

let res = conn.query(
"SELECT value FROM config WHERE name = 'rustc_version';",
&[],
)?;
if res.is_empty() {
failure::bail!("missing rustc version");
}

if let Some(Ok(vers)) = res.get(0).get_opt::<_, Value>("value") {
if let Some(vers_str) = vers.as_str() {
return Ok(crate::utils::parse_rustc_version(vers_str)?);
}
}

failure::bail!("failed to parse the rustc version");
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Page<T: Serialize> {
title: Option<String>,
content: T,
status: status::Status,
varss: BTreeMap<String, String>,
varsb: BTreeMap<String, bool>,
varsi: BTreeMap<String, i64>,
rustc_resource_suffix: &'static str,
}

impl<T: Serialize> Page<T> {
pub fn new(content: T) -> Page<T> {
Page {
title: None,
content,
status: status::Ok,
varss: BTreeMap::new(),
varsb: BTreeMap::new(),
varsi: BTreeMap::new(),
rustc_resource_suffix: &RUSTC_RESOURCE_SUFFIX,
}
}

/// Sets a string variable
pub fn set(mut self, var: &str, val: &str) -> Page<T> {
self.varss.insert(var.to_owned(), val.to_owned());
self
}

/// Sets a boolean variable
pub fn set_bool(mut self, var: &str, val: bool) -> Page<T> {
self.varsb.insert(var.to_owned(), val);
self
}

/// Sets a boolean variable to true
pub fn set_true(mut self, var: &str) -> Page<T> {
self.varsb.insert(var.to_owned(), true);
self
}

/// Sets title of page
pub fn title(mut self, title: &str) -> Page<T> {
self.title = Some(title.to_owned());
self
}

#[allow(clippy::wrong_self_convention)]
pub fn to_resp(self, template: &str) -> IronResult<Response> {
let mut resp = Response::new();
let status = self.status;
let temp = Template::new(template, self);
resp.set_mut(temp).set_mut(status);

Ok(resp)
}
}

impl<T: Serialize> Serialize for Page<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// Make sure that the length parameter passed to serde is correct by
// adding the someness of the global alert to the total. `true`
// is 1 and `false` is 0, so it increments if the value is some (and therefore
// needs to be serialized)
let mut state = serializer.serialize_struct(
"Page",
8 + crate::GLOBAL_ALERT.is_some() as usize + self.title.is_some() as usize,
)?;

if let Some(ref title) = self.title {
state.serialize_field("title", title)?;
}

state.serialize_field("has_global_alert", &crate::GLOBAL_ALERT.is_some())?;
if let Some(ref global_alert) = crate::GLOBAL_ALERT {
state.serialize_field("global_alert", global_alert)?;
}

state.serialize_field("content", &self.content)?;
state.serialize_field("rustc_resource_suffix", self.rustc_resource_suffix)?;
state.serialize_field("cratesfyi_version", crate::BUILD_VERSION)?;
state.serialize_field(
"cratesfyi_version_safe",
&build_version_safe(crate::BUILD_VERSION),
)?;
state.serialize_field("varss", &self.varss)?;
state.serialize_field("varsb", &self.varsb)?;
state.serialize_field("varsi", &self.varsi)?;

state.end()
}
}

fn build_version_safe(version: &str) -> String {
version.replace(" ", "-").replace("(", "").replace(")", "")
}

#[cfg(test)]
mod tests {
use super::*;
use crate::web::releases;
use iron::Url;

#[test]
fn load_page_from_releases() {
crate::test::wrapper(|env| {
let db = env.db();
db.fake_release().name("foo").version("0.1.0").create()?;
let packages = releases::get_releases(&db.conn(), 1, 1, releases::Order::ReleaseTime);

let mut varsb = BTreeMap::new();
varsb.insert("show_search_form".into(), true);
varsb.insert("hide_package_navigation".into(), true);

let correct_page = Page {
title: None,
content: packages.clone(),
status: status::Status::Ok,
varss: BTreeMap::new(),
varsb,
varsi: BTreeMap::new(),
rustc_resource_suffix: &RUSTC_RESOURCE_SUFFIX,
};

let page = Page::new(packages)
.set_true("show_search_form")
.set_true("hide_package_navigation");

assert_eq!(page, correct_page);

Ok(())
})
}

#[test]
fn build_version_url_safe() {
let safe = format!(
"https://docs.rs/builds/{}",
build_version_safe(crate::BUILD_VERSION)
);
assert!(Url::parse(&safe).is_ok());
}
}
2 changes: 2 additions & 0 deletions src/web/page/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
mod handlebars;
mod templates;
mod web_page;

pub use handlebars::*;
pub(crate) use templates::TemplateData;
pub(crate) use web_page::WebPage;

Expand Down
4 changes: 2 additions & 2 deletions src/web/page/templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use std::{
use tera::{Result as TeraResult, Tera};
use walkdir::WalkDir;

const TEMPLATES_DIRECTORY: &str = "templates";
const TEMPLATES_DIRECTORY: &str = "tera-templates";

/// Holds all data relevant to templating
#[derive(Debug)]
Expand Down Expand Up @@ -45,7 +45,7 @@ impl TemplateData {
let mut watcher = watcher(tx, Duration::from_secs(2)).unwrap();

watcher
.watch(TEMPLATES_DIRECTORY, RecursiveMode::Recursive)
.watch("tera-templates", RecursiveMode::Recursive)
.unwrap();

thread::spawn(move || {
Expand Down
5 changes: 5 additions & 0 deletions templates/footer.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{{#if varsb.javascript_highlightjs}}<script type="text/javascript" charset="utf-8">hljs.initHighlighting();</script>{{/if}}
<script type="text/javascript" src="/menu.js?{{cratesfyi_version_safe}}"></script>
<script type="text/javascript" src="/index.js?{{cratesfyi_version_safe}}"></script>
</body>
</html>
27 changes: 27 additions & 0 deletions templates/header.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="generator" content="docs.rs {{cratesfyi_version}}">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/pure/0.6.0/pure-min.css" type="text/css" media="all" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/pure/0.6.0/grids-responsive-min.css" type="text/css" media="all" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css" type="text/css" media="all" />
<link rel="stylesheet" href="/normalize-{{rustc_resource_suffix}}.css" type="text/css" media="all" />
<link rel="stylesheet" href="/rustdoc-{{rustc_resource_suffix}}.css" type="text/css" media="all" />
<link rel="stylesheet" href="/light-{{rustc_resource_suffix}}.css" type="text/css" media="all" />
<link rel="stylesheet" href="/style.css?{{cratesfyi_version_safe}}" type="text/css" media="all" />
<link rel="search" href="/opensearch.xml" type="application/opensearchdescription+xml" title="Docs.rs">
{{#if varsb.javascript_highlightjs}}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.4.0/styles/github.min.css" type="text/css" media="all" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.4.0/highlight.min.js" type="text/javascript" charset="utf-8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.4.0/languages/rust.min.js" type="text/javascript" charset="utf-8"></script>
{{/if}}
{{#if varsb.javascript_highchartjs}}
<script src="https://cdnjs.cloudflare.com/ajax/libs/highcharts/4.2.5/highcharts.js" type="text/javascript" charset="utf-8"></script>
{{/if}}
<title>{{#if title}}{{title}} - {{/if}}{{#if content.metadata.name}}{{content.metadata.name}} {{content.metadata.version}} - {{/if}}Docs.rs</title>
</head>
<body>

{{> navigation}}
Loading

0 comments on commit 6fb9711

Please sign in to comment.