//! This crate generates HTML templates for crates.rs
//!
//! Most template require their own type of struct that does
//! some lightweight conversion from data model/APIs,
//! because the template engine Ructe doesn't support
//! complex expressions in the templates.
mod cat_page;
mod crate_page;
mod download_graph;
mod home_page;
mod iter;
mod not_found_page;
mod search_page;
mod urler;
use render_readme::Markup;
pub use crate::not_found_page::*;
pub use crate::search_page::*;
use crate::crate_page::*;
use crate::urler::Urler;
use categories::Category;
use chrono::prelude::*;
use failure;
use failure::ResultExt;
use kitchen_sink::KitchenSink;
use kitchen_sink::{stopped, KitchenSinkErr};
use render_readme::Renderer;
use rich_crate::RichCrate;
use rich_crate::RichCrateVersion;
use std::io::Write;
include!(concat!(env!("OUT_DIR"), "/templates.rs"));
/// Metadata used in the base template, mostly for ``
pub struct Page {
title: String,
description: Option,
item_name: Option,
item_description: Option,
keywords: Option,
created: Option,
alternate: Option,
alternate_type: Option<&'static str>,
canonical: Option,
noindex: bool,
search_meta: bool,
critical_css_data: Option<&'static str>,
}
impl Page {
pub fn site_twitter_handle(&self) -> &str {
"@CratesRS"
}
pub fn critical_css(&self) -> templates::Html<&'static str> {
let data = self.critical_css_data.unwrap_or(include_str!("../../style/public/critical.css"));
templates::Html(data)
}
}
/// See `cat_page.rs.html`
pub fn render_category(out: &mut dyn Write, cat: &Category, crates: &KitchenSink, renderer: &Renderer) -> Result<(), failure::Error> {
let urler = Urler::new(None);
let page = cat_page::CatPage::new(cat, crates, renderer).context("can't prepare rendering of category page")?;
templates::cat_page(out, &page, &urler)?;
Ok(())
}
/// See `homepage.rs.html`
pub fn render_homepage(out: &mut dyn Write, crates: &KitchenSink) -> Result<(), failure::Error> {
let urler = Urler::new(None);
templates::homepage(out, &home_page::HomePage::new(crates)?, &urler)?;
Ok(())
}
/// See `atom.rs.html`
pub fn render_feed(out: &mut dyn Write, crates: &KitchenSink) -> Result<(), failure::Error> {
let urler = Urler::new(None);
templates::atom(out, &home_page::HomePage::new(crates)?, &urler)?;
Ok(())
}
pub fn render_sitemap(sitemap: &mut impl Write, crates: &KitchenSink) -> Result<(), failure::Error> {
let all_crates = crates.sitemap_crates()?;
sitemap.write_all(br#""#)?;
let now = Utc::now().timestamp();
for (origin, rank, lastmod) in all_crates {
let age = now - lastmod;
write!(
sitemap,
r#"
{freq}{pri:0.1}{date}https://lib.rs/crates/{name}"#,
name = origin.short_crate_name(),
date = Utc.timestamp(lastmod, 0).to_rfc3339(),
pri = (rank * 2.).min(1.),
freq = match age {
x if x > 3600 * 24 * 30 * 18 => "yearly",
x if x > 3600 * 24 * 60 => "monthly",
x if x > 3600 * 24 * 7 => "weekly",
_ => "daily",
},
)?;
}
sitemap.write_all(b"\n\n")?;
Ok(())
}
/// See `crate_page.rs.html`
pub fn render_crate_page(out: &mut dyn Write, all: &RichCrate, ver: &RichCrateVersion, kitchen_sink: &KitchenSink, renderer: &Renderer) -> Result {
if stopped() {
Err(KitchenSinkErr::Stopped)?;
}
let urler = Urler::new(Some(ver.short_name().to_string()));
let c = CratePage::new(all, ver, kitchen_sink, renderer).context("New crate page")?;
templates::crate_page(out, &urler, &c).context("crate page io")?;
Ok(c.page_title())
}
/// See `crate_page.rs.html`
pub fn render_static_page(out: &mut dyn Write, title: String, page: &Markup, renderer: &Renderer) -> Result<(), failure::Error> {
if stopped() {
Err(KitchenSinkErr::Stopped)?;
}
let (html, warnings) = renderer.page(page, Some(("https://lib.rs", "https://lib.rs")), false, None);
if !warnings.is_empty() {
eprintln!("static: {:?}", warnings);
}
templates::static_page(out, &Page {
title,
alternate: None,
alternate_type: None,
canonical: None,
critical_css_data: None,
created: None,
description: None,
item_description: None,
item_name: None,
keywords: None,
noindex: false,
search_meta: true,
}, templates::Html(html))?;
Ok(())
}
/// Ructe doesn't like complex expressions…
trait MyAsStr {
fn as_str(&self) -> Option<&str>;
}
impl> MyAsStr for Option {
fn as_str(&self) -> Option<&str> {
self.as_ref().map(|s| s.as_ref())
}
}
pub(crate) fn date_now() -> String {
Utc::now().format("%Y-%m-%d").to_string()
}