Skip to content

Commit 2d17b29

Browse files
pietroalbiniXAMPPRocky
authored andcommitted
blacksmith: cache the manifests and targets for an hour (#293)
Before this, the preprocessor fetched the channel manifests to figure out the stable version and the available targets every single build. This was annoying especially when running `mdbook serve`, which rebuilds the content automatically every time a file changes on disk. This caches that information in an ignored .blacksmith-cache.json file, with a TTL of an hour. Within the hour the old data will be reused, and after that amount of time new data will be fetched to refresh the cache. This shouldn't affect CI or other production builds, as the repository as a whole isn't cached between builds.
1 parent acceaef commit 2d17b29

File tree

3 files changed

+68
-15
lines changed

3 files changed

+68
-15
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
book
2+
.blacksmith-cache.json

blacksmith/src/lib.rs

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::{
55
fmt::Write,
66
io::{BufRead, BufReader},
77
path::Path,
8+
time::SystemTime,
89
};
910

1011
use mdbook::{
@@ -21,6 +22,7 @@ const RUSTUP_URLS: &str =
2122
/// A representation of a rust target platform. `stable`, `beta`, and `nightly`
2223
/// represent whether the platform is available on that channel. `stable` also
2324
/// carries the specific stable compiler version.
25+
#[derive(serde::Serialize, serde::Deserialize)]
2426
pub struct Platform {
2527
stable: Option<String>,
2628
beta: bool,
@@ -38,8 +40,9 @@ impl Default for Platform {
3840
}
3941

4042
/// `Blacksmith` builds dynamic tables and lists to display in the Rust Forge.
41-
#[derive(Default)]
43+
#[derive(Default, serde::Serialize, serde::Deserialize)]
4244
pub struct Blacksmith {
45+
last_update: Option<u64>,
4346
rustup: Vec<String>,
4447
stable_version: Option<String>,
4548
platforms: BTreeMap<String, Platform>,
@@ -51,17 +54,30 @@ impl Blacksmith {
5154
Self::default()
5255
}
5356

57+
/// Check if the data in this `Blacksmith` instance was not updated within the TTL.
58+
pub fn is_stale(&self, ttl: u64) -> bool {
59+
if let (Some(last_update), Some(now)) = (self.last_update, unix_time()) {
60+
last_update + ttl < now
61+
} else {
62+
true
63+
}
64+
}
65+
5466
/// Populates a `Blacksmith` instance with data gathered from Rust's CI and
5567
/// distribution channels.
56-
pub fn init(mut self) -> Result<Self, Box<dyn std::error::Error>> {
68+
pub fn init() -> Result<Self, Box<dyn std::error::Error>> {
69+
let mut blacksmith = Self::new();
70+
5771
let rustup_url_regex =
5872
regex::Regex::new(r"^rustup/dist/([^/]+)/rustup-init(?:\.exe)?$").unwrap();
5973
for line in BufReader::new(reqwest::get(RUSTUP_URLS)?).lines() {
6074
if let Some(m) = rustup_url_regex.captures(&(line?)) {
61-
self.rustup.push(m.get(1).unwrap().as_str().to_string());
75+
blacksmith
76+
.rustup
77+
.push(m.get(1).unwrap().as_str().to_string());
6278
}
6379
}
64-
log::info!("Found {} targets for rustup", self.rustup.len());
80+
log::info!("Found {} targets for rustup", blacksmith.rustup.len());
6581

6682
for &channel_name in CHANNELS {
6783
let channel_url = format!("{}{}.toml", CHANNEL_URL_PREFIX, channel_name);
@@ -90,11 +106,11 @@ impl Blacksmith {
90106
.collect::<Vec<_>>();
91107

92108
if channel_name == "stable" {
93-
self.stable_version = Some(vers.clone());
109+
blacksmith.stable_version = Some(vers.clone());
94110
}
95111

96112
for platform in platforms {
97-
let entry = self
113+
let entry = blacksmith
98114
.platforms
99115
.entry(platform)
100116
.or_insert_with(Platform::default);
@@ -108,7 +124,8 @@ impl Blacksmith {
108124
}
109125
}
110126

111-
Ok(self)
127+
blacksmith.last_update = unix_time();
128+
Ok(blacksmith)
112129
}
113130

114131
fn generate_redirects(&self, ctx: &PreprocessorContext) {
@@ -299,6 +316,13 @@ fn generate_standalone_links(base: &str, stem: &str, name: &str, extension: &str
299316
)
300317
}
301318

319+
fn unix_time() -> Option<u64> {
320+
SystemTime::now()
321+
.duration_since(SystemTime::UNIX_EPOCH)
322+
.ok()
323+
.map(|d| d.as_secs())
324+
}
325+
302326
impl Preprocessor for Blacksmith {
303327
fn name(&self) -> &str {
304328
"blacksmith"

blacksmith/src/main.rs

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1-
use std::{env, io, process};
1+
use std::{env, io, path::Path, process};
22

33
use clap::{clap_app, ArgMatches};
4-
use mdbook::{errors::Error, preprocess::{CmdPreprocessor, Preprocessor}};
4+
use mdbook::{
5+
errors::Error,
6+
preprocess::{CmdPreprocessor, Preprocessor},
7+
};
58

69
use mdbook_blacksmith::Blacksmith;
710

11+
const CACHE_FILE: &str = ".blacksmith-cache.json";
12+
const CACHE_TTL_SECONDS: u64 = 3600; // 1 hour
13+
814
fn main() {
915
// If RUST_LOG is present use that, else default to info level printing.
1016
if env::var("RUST_LOG").is_ok() {
@@ -21,7 +27,8 @@ fn main() {
2127
(about: "Check whether a renderer is supported by this preprocessor")
2228
(@arg renderer: +takes_value +required)
2329
)
24-
).get_matches();
30+
)
31+
.get_matches();
2532

2633
macro_rules! log_unwrap {
2734
($result:expr) => {
@@ -32,15 +39,37 @@ fn main() {
3239
process::exit(1);
3340
}
3441
}
35-
}
42+
};
3643
}
3744

38-
let blacksmith = Blacksmith::new();
45+
let cache_file = Path::new(CACHE_FILE);
46+
let mut blacksmith = if cache_file.is_file() {
47+
log_unwrap!(serde_json::from_slice(&log_unwrap!(std::fs::read(
48+
&cache_file
49+
))))
50+
} else {
51+
Blacksmith::new()
52+
};
3953

4054
if let Some(sub_args) = matches.subcommand_matches("supports") {
4155
handle_supports(&blacksmith, sub_args);
42-
} else {
43-
log_unwrap!(handle_preprocessing(&log_unwrap!(blacksmith.init())))
56+
} else {
57+
let mut update_cache = false;
58+
if blacksmith.is_stale(CACHE_TTL_SECONDS) {
59+
blacksmith = log_unwrap!(Blacksmith::init());
60+
update_cache = true;
61+
} else {
62+
log::info!("Using cached data in {}", cache_file.display());
63+
}
64+
log_unwrap!(handle_preprocessing(&blacksmith));
65+
66+
if update_cache {
67+
log::info!("Storing the cache in {}", cache_file.display());
68+
log_unwrap!(std::fs::write(
69+
&cache_file,
70+
&log_unwrap!(serde_json::to_vec(&blacksmith))
71+
));
72+
}
4473
}
4574
}
4675

@@ -76,4 +105,3 @@ fn handle_supports(pre: &Blacksmith, sub_args: &ArgMatches) -> ! {
76105
process::exit(1);
77106
}
78107
}
79-

0 commit comments

Comments
 (0)