Skip to content

Commit 84fe0cb

Browse files
authored
Merge pull request #841 from pietroalbini/remote-template-data-static
Remove TEMPLATE_DATA static
2 parents 5b2fdf4 + 2b4091e commit 84fe0cb

File tree

8 files changed

+110
-77
lines changed

8 files changed

+110
-77
lines changed

src/bin/cratesfyi.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,11 @@ impl CommandLine {
9090
socket_addr,
9191
reload_templates,
9292
} => {
93-
Server::start(Some(&socket_addr), reload_templates, config);
93+
Server::start(Some(&socket_addr), reload_templates, config)?;
94+
}
95+
Self::Daemon { foreground } => {
96+
cratesfyi::utils::start_daemon(!foreground, config)?;
9497
}
95-
Self::Daemon { foreground } => cratesfyi::utils::start_daemon(!foreground, config),
9698
Self::Database { subcommand } => subcommand.handle_args(),
9799
Self::Queue { subcommand } => subcommand.handle_args(),
98100
}

src/test/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,8 @@ pub(crate) struct TestFrontend {
212212
impl TestFrontend {
213213
fn new(db: &TestDatabase, config: Arc<Config>) -> Self {
214214
Self {
215-
server: Server::start_test(db.conn.clone(), config),
215+
server: Server::start_test(db.conn.clone(), config)
216+
.expect("failed to start the server"),
216217
client: Client::new(),
217218
}
218219
}

src/utils/daemon.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::{
88
Config, DocBuilder, DocBuilderOptions,
99
};
1010
use chrono::{Timelike, Utc};
11+
use failure::Error;
1112
use log::{debug, error, info, warn};
1213
use std::panic::{catch_unwind, AssertUnwindSafe};
1314
use std::path::PathBuf;
@@ -18,7 +19,7 @@ use std::{env, thread};
1819
#[cfg(not(target_os = "windows"))]
1920
use ::{libc::fork, std::fs::File, std::io::Write, std::process::exit};
2021

21-
pub fn start_daemon(background: bool, config: Arc<Config>) {
22+
pub fn start_daemon(background: bool, config: Arc<Config>) -> Result<(), Error> {
2223
const CRATE_VARIABLES: [&str; 3] = [
2324
"CRATESFYI_PREFIX",
2425
"CRATESFYI_GITHUB_USERNAME",
@@ -250,7 +251,8 @@ pub fn start_daemon(background: bool, config: Arc<Config>) {
250251
// at least start web server
251252
info!("Starting web server");
252253

253-
crate::Server::start(None, false, config);
254+
crate::Server::start(None, false, config)?;
255+
Ok(())
254256
}
255257

256258
fn opts() -> DocBuilderOptions {

src/web/mod.rs

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,11 @@ mod rustdoc;
5555
mod sitemap;
5656
mod source;
5757

58+
use self::page::TemplateData;
5859
use self::pool::Pool;
5960
use crate::config::Config;
6061
use chrono::{DateTime, Utc};
62+
use failure::Error;
6163
use handlebars_iron::{DirectorySource, HandlebarsEngine, SourceError};
6264
use iron::headers::{CacheControl, CacheDirective, ContentType, Expires, HttpDate};
6365
use iron::modifiers::Redirect;
@@ -112,8 +114,12 @@ impl CratesfyiHandler {
112114
chain
113115
}
114116

115-
fn new(pool: Pool, config: Arc<Config>) -> CratesfyiHandler {
116-
let inject_extensions = InjectExtensions { pool, config };
117+
fn new(pool: Pool, config: Arc<Config>, template_data: Arc<TemplateData>) -> CratesfyiHandler {
118+
let inject_extensions = InjectExtensions {
119+
pool,
120+
config,
121+
template_data,
122+
};
117123

118124
let routes = routes::build_routes();
119125
let blacklisted_prefixes = routes.page_prefixes();
@@ -215,12 +221,15 @@ impl Handler for CratesfyiHandler {
215221
struct InjectExtensions {
216222
pool: Pool,
217223
config: Arc<Config>,
224+
template_data: Arc<TemplateData>,
218225
}
219226

220227
impl BeforeMiddleware for InjectExtensions {
221228
fn before(&self, req: &mut Request) -> IronResult<()> {
222229
req.extensions.insert::<Pool>(self.pool.clone());
223230
req.extensions.insert::<Config>(self.config.clone());
231+
req.extensions
232+
.insert::<TemplateData>(self.template_data.clone());
224233

225234
Ok(())
226235
}
@@ -383,24 +392,46 @@ pub struct Server {
383392
}
384393

385394
impl Server {
386-
pub fn start(addr: Option<&str>, reload_templates: bool, config: Arc<Config>) -> Self {
395+
pub fn start(
396+
addr: Option<&str>,
397+
reload_templates: bool,
398+
config: Arc<Config>,
399+
) -> Result<Self, Error> {
387400
// Initialize templates
388-
let _: &page::TemplateData = &*page::TEMPLATE_DATA;
401+
let template_data = Arc::new(TemplateData::new()?);
389402
if reload_templates {
390-
page::TemplateData::start_template_reloading();
403+
TemplateData::start_template_reloading(template_data.clone());
391404
}
392405

393-
let server = Self::start_inner(addr.unwrap_or(DEFAULT_BIND), Pool::new(), config);
406+
let server = Self::start_inner(
407+
addr.unwrap_or(DEFAULT_BIND),
408+
Pool::new(),
409+
config,
410+
template_data,
411+
);
394412
info!("Running docs.rs web server on http://{}", server.addr());
395-
server
413+
Ok(server)
396414
}
397415

398416
#[cfg(test)]
399-
pub(crate) fn start_test(conn: Arc<Mutex<Connection>>, config: Arc<Config>) -> Self {
400-
Self::start_inner("127.0.0.1:0", Pool::new_simple(conn.clone()), config)
417+
pub(crate) fn start_test(
418+
conn: Arc<Mutex<Connection>>,
419+
config: Arc<Config>,
420+
) -> Result<Self, Error> {
421+
Ok(Self::start_inner(
422+
"127.0.0.1:0",
423+
Pool::new_simple(conn.clone()),
424+
config,
425+
Arc::new(TemplateData::new()?),
426+
))
401427
}
402428

403-
fn start_inner(addr: &str, pool: Pool, config: Arc<Config>) -> Self {
429+
fn start_inner(
430+
addr: &str,
431+
pool: Pool,
432+
config: Arc<Config>,
433+
template_data: Arc<TemplateData>,
434+
) -> Self {
404435
// poke all the metrics counters to instantiate and register them
405436
metrics::TOTAL_BUILDS.inc_by(0);
406437
metrics::SUCCESSFUL_BUILDS.inc_by(0);
@@ -409,7 +440,7 @@ impl Server {
409440
metrics::UPLOADED_FILES_TOTAL.inc_by(0);
410441
metrics::FAILED_DB_CONNECTIONS.inc_by(0);
411442

412-
let cratesfyi = CratesfyiHandler::new(pool, config);
443+
let cratesfyi = CratesfyiHandler::new(pool, config, template_data);
413444
let inner = Iron::new(cratesfyi)
414445
.http(addr)
415446
.unwrap_or_else(|_| panic!("Failed to bind to socket on {}", addr));

src/web/page/mod.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,6 @@ pub(crate) use web_page::WebPage;
88

99
use serde::Serialize;
1010

11-
lazy_static::lazy_static! {
12-
/// Holds all data relevant to templating
13-
pub(crate) static ref TEMPLATE_DATA: TemplateData = TemplateData::new().expect("Failed to load template data");
14-
}
15-
1611
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize)]
1712
pub(crate) struct GlobalAlert {
1813
pub(crate) url: &'static str,

src/web/page/templates.rs

Lines changed: 48 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,36 @@
1-
use super::TEMPLATE_DATA;
21
use crate::error::Result;
32
use arc_swap::ArcSwap;
43
use chrono::{DateTime, Utc};
4+
use notify::{watcher, RecursiveMode, Watcher};
55
use serde_json::Value;
66
use std::collections::HashMap;
7+
use std::sync::{mpsc::channel, Arc};
8+
use std::thread;
9+
use std::time::Duration;
710
use tera::{Result as TeraResult, Tera};
811

912
/// Holds all data relevant to templating
10-
///
11-
/// Most data is stored as a pre-serialized `Value` so that we don't have to
12-
/// re-serialize them every time they're needed. The values themselves are exposed
13-
/// to templates via custom functions
13+
#[derive(Debug)]
1414
pub(crate) struct TemplateData {
1515
/// The actual templates, stored in an `ArcSwap` so that they're hot-swappable
1616
// TODO: Conditional compilation so it's not always wrapped, the `ArcSwap` is unneeded overhead for prod
1717
pub templates: ArcSwap<Tera>,
18-
/// The current global alert, serialized into a json value
19-
global_alert: Value,
20-
/// The version of docs.rs, serialized into a json value
21-
docsrs_version: Value,
22-
/// The current resource suffix of rustc, serialized into a json value
23-
resource_suffix: Value,
2418
}
2519

2620
impl TemplateData {
27-
pub fn new() -> Result<Self> {
21+
pub(crate) fn new() -> Result<Self> {
2822
log::trace!("Loading templates");
2923

3024
let data = Self {
3125
templates: ArcSwap::from_pointee(load_templates()?),
32-
global_alert: serde_json::to_value(crate::GLOBAL_ALERT)?,
33-
docsrs_version: Value::String(crate::BUILD_VERSION.to_owned()),
34-
resource_suffix: Value::String(load_rustc_resource_suffix().unwrap_or_else(|err| {
35-
log::error!("Failed to load rustc resource suffix: {:?}", err);
36-
String::from("???")
37-
})),
3826
};
3927

4028
log::trace!("Finished loading templates");
4129

4230
Ok(data)
4331
}
4432

45-
pub fn start_template_reloading() {
46-
use notify::{watcher, RecursiveMode, Watcher};
47-
use std::{
48-
sync::{mpsc::channel, Arc},
49-
thread,
50-
time::Duration,
51-
};
52-
33+
pub(crate) fn start_template_reloading(template_data: Arc<TemplateData>) {
5334
let (tx, rx) = channel();
5435
// Set a 2 second event debounce for the watcher
5536
let mut watcher = watcher(tx, Duration::from_secs(2)).unwrap();
@@ -67,7 +48,7 @@ impl TemplateData {
6748
match load_templates() {
6849
Ok(templates) => {
6950
log::info!("Reloaded templates");
70-
super::TEMPLATE_DATA.templates.swap(Arc::new(templates));
51+
template_data.templates.swap(Arc::new(templates));
7152
}
7253

7354
Err(err) => log::error!("Error reloading templates: {:?}", err),
@@ -77,6 +58,10 @@ impl TemplateData {
7758
}
7859
}
7960

61+
impl iron::typemap::Key for TemplateData {
62+
type Value = std::sync::Arc<TemplateData>;
63+
}
64+
8065
// TODO: Is there a reason this isn't fatal? If the rustc version is incorrect (Or "???" as used by default), then
8166
// all pages will be served *really* weird because they'll lack all CSS
8267
fn load_rustc_resource_suffix() -> Result<String> {
@@ -99,13 +84,31 @@ fn load_rustc_resource_suffix() -> Result<String> {
9984
failure::bail!("failed to parse the rustc version");
10085
}
10186

102-
pub(super) fn load_templates() -> TeraResult<Tera> {
87+
pub(super) fn load_templates() -> Result<Tera> {
10388
let mut tera = Tera::new("tera-templates/**/*")?;
10489

105-
// Custom functions
106-
tera.register_function("global_alert", global_alert);
107-
tera.register_function("docsrs_version", docsrs_version);
108-
tera.register_function("rustc_resource_suffix", rustc_resource_suffix);
90+
// This function will return any global alert, if present.
91+
ReturnValue::add_function_to(
92+
&mut tera,
93+
"global_alert",
94+
serde_json::to_value(crate::GLOBAL_ALERT)?,
95+
);
96+
// This function will return the current version of docs.rs.
97+
ReturnValue::add_function_to(
98+
&mut tera,
99+
"docsrs_version",
100+
Value::String(crate::BUILD_VERSION.into()),
101+
);
102+
// This function will return the resource suffix of the latest nightly used to build
103+
// documentation on docs.rs, or ??? if no resource suffix was found.
104+
ReturnValue::add_function_to(
105+
&mut tera,
106+
"rustc_resource_suffix",
107+
Value::String(load_rustc_resource_suffix().unwrap_or_else(|err| {
108+
log::error!("Failed to load rustc resource suffix: {:?}", err);
109+
String::from("???")
110+
})),
111+
);
109112

110113
// Custom filters
111114
tera.register_filter("timeformat", timeformat);
@@ -115,28 +118,23 @@ pub(super) fn load_templates() -> TeraResult<Tera> {
115118
Ok(tera)
116119
}
117120

118-
/// Returns an `Option<GlobalAlert>` in json form for templates
119-
fn global_alert(args: &HashMap<String, Value>) -> TeraResult<Value> {
120-
debug_assert!(args.is_empty(), "global_alert takes no args");
121-
122-
Ok(TEMPLATE_DATA.global_alert.clone())
121+
/// Simple function that returns the pre-defined value.
122+
struct ReturnValue {
123+
name: &'static str,
124+
value: Value,
123125
}
124126

125-
/// Returns the version of docs.rs, takes the `safe` parameter which can be `true` to get a url-safe version
126-
fn docsrs_version(args: &HashMap<String, Value>) -> TeraResult<Value> {
127-
debug_assert!(
128-
args.is_empty(),
129-
"docsrs_version only takes no args, to get a safe version use `docsrs_version() | slugify`",
130-
);
131-
132-
Ok(TEMPLATE_DATA.docsrs_version.clone())
127+
impl ReturnValue {
128+
fn add_function_to(tera: &mut Tera, name: &'static str, value: Value) {
129+
tera.register_function(name, Self { name, value })
130+
}
133131
}
134132

135-
/// Returns the current rustc resource suffix
136-
fn rustc_resource_suffix(args: &HashMap<String, Value>) -> TeraResult<Value> {
137-
debug_assert!(args.is_empty(), "rustc_resource_suffix takes no args");
138-
139-
Ok(TEMPLATE_DATA.resource_suffix.clone())
133+
impl tera::Function for ReturnValue {
134+
fn call(&self, args: &HashMap<String, Value>) -> TeraResult<Value> {
135+
debug_assert!(args.is_empty(), format!("{} takes no args", self.name));
136+
Ok(self.value.clone())
137+
}
140138
}
141139

142140
/// Prettily format a timestamp

src/web/page/web_page.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use super::TEMPLATE_DATA;
2-
use iron::{headers::ContentType, response::Response, status::Status, IronResult};
1+
use super::TemplateData;
2+
use iron::{headers::ContentType, response::Response, status::Status, IronResult, Request};
33
use serde::Serialize;
44
use tera::Context;
55

@@ -28,9 +28,13 @@ macro_rules! impl_webpage {
2828
pub trait WebPage: Serialize + Sized {
2929
/// Turn the current instance into a `Response`, ready to be served
3030
// TODO: We could cache similar pages using the `&Context`
31-
fn into_response(self) -> IronResult<Response> {
31+
fn into_response(self, req: &Request) -> IronResult<Response> {
3232
let ctx = Context::from_serialize(&self).unwrap();
33-
let rendered = TEMPLATE_DATA
33+
34+
let rendered = req
35+
.extensions
36+
.get::<TemplateData>()
37+
.expect("missing TemplateData from the request extensions")
3438
.templates
3539
.load()
3640
.render(Self::TEMPLATE, &ctx)

src/web/sitemap.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub fn sitemap_handler(req: &mut Request) -> IronResult<Response> {
4949
})
5050
.collect::<Vec<(String, String)>>();
5151

52-
SitemapXml { releases }.into_response()
52+
SitemapXml { releases }.into_response(req)
5353
}
5454

5555
pub fn robots_txt_handler(_: &mut Request) -> IronResult<Response> {
@@ -83,5 +83,5 @@ pub fn about_handler(req: &mut Request) -> IronResult<Response> {
8383
rustc_version,
8484
limits: Limits::default(),
8585
}
86-
.into_response()
86+
.into_response(req)
8787
}

0 commit comments

Comments
 (0)