Skip to content

Commit 282fdaa

Browse files
committed
Redirect to a 404 page when serving translated
We can't redirect in warp based on the URL, so redirect to the default language's 404 page instead. See: seanmonstar/warp#171
1 parent 85ab4d3 commit 282fdaa

File tree

8 files changed

+138
-69
lines changed

8 files changed

+138
-69
lines changed

src/book/book.rs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,37 @@ pub fn load_book<P: AsRef<Path>>(
1818
if cfg.language.has_localized_dir_structure() {
1919
match build_opts.language_ident {
2020
// Build a single book's translation.
21-
Some(_) => Ok(LoadedBook::Single(load_single_book_translation(&root_dir, cfg, &build_opts.language_ident)?)),
21+
Some(_) => Ok(LoadedBook::Single(load_single_book_translation(
22+
&root_dir,
23+
cfg,
24+
&build_opts.language_ident,
25+
)?)),
2226
// Build all available translations at once.
2327
None => {
2428
let mut translations = HashMap::new();
2529
for (lang_ident, _) in cfg.language.0.iter() {
26-
let book = load_single_book_translation(&root_dir, cfg, &Some(lang_ident.clone()))?;
30+
let book =
31+
load_single_book_translation(&root_dir, cfg, &Some(lang_ident.clone()))?;
2732
translations.insert(lang_ident.clone(), book);
2833
}
2934
Ok(LoadedBook::Localized(LocalizedBooks(translations)))
3035
}
3136
}
3237
} else {
33-
Ok(LoadedBook::Single(load_single_book_translation(&root_dir, cfg, &None)?))
38+
Ok(LoadedBook::Single(load_single_book_translation(
39+
&root_dir, cfg, &None,
40+
)?))
3441
}
3542
}
3643

37-
fn load_single_book_translation<P: AsRef<Path>>(root_dir: P, cfg: &Config, language_ident: &Option<String>) -> Result<Book> {
38-
let localized_src_dir = root_dir.as_ref().join(
39-
cfg.get_localized_src_path(language_ident.as_ref())
40-
.unwrap(),
41-
);
44+
fn load_single_book_translation<P: AsRef<Path>>(
45+
root_dir: P,
46+
cfg: &Config,
47+
language_ident: &Option<String>,
48+
) -> Result<Book> {
49+
let localized_src_dir = root_dir
50+
.as_ref()
51+
.join(cfg.get_localized_src_path(language_ident.as_ref()).unwrap());
4252
let fallback_src_dir = root_dir.as_ref().join(cfg.get_fallback_src_path());
4353

4454
let summary_md = localized_src_dir.join("SUMMARY.md");
@@ -172,9 +182,7 @@ impl LocalizedBooks {
172182
items.extend(book.iter().items);
173183
}
174184

175-
BookItems {
176-
items: items
177-
}
185+
BookItems { items: items }
178186
}
179187

180188
/// Recursively apply a closure to each item in the book, allowing you to
@@ -239,7 +247,7 @@ impl LoadedBook {
239247
pub fn first(&self) -> &Book {
240248
match self {
241249
LoadedBook::Localized(books) => books.0.iter().next().unwrap().1,
242-
LoadedBook::Single(book) => &book
250+
LoadedBook::Single(book) => &book,
243251
}
244252
}
245253
}
@@ -617,7 +625,7 @@ more text.
617625
Vec::new(),
618626
&cfg,
619627
)
620-
.unwrap();
628+
.unwrap();
621629
assert_eq!(got, should_be);
622630
}
623631

src/book/mod.rs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ mod book;
1010
mod init;
1111
mod summary;
1212

13-
pub use self::book::{load_book, BookItem, BookItems, Chapter, Book, LocalizedBooks, LoadedBook};
13+
pub use self::book::{load_book, Book, BookItem, BookItems, Chapter, LoadedBook, LocalizedBooks};
1414
pub use self::init::BookBuilder;
1515
pub use self::summary::{parse_summary, Link, SectionNumber, Summary, SummaryItem};
1616

17+
use std::collections::HashMap;
1718
use std::io::Write;
1819
use std::path::PathBuf;
1920
use std::process::Command;
2021
use std::string::ToString;
21-
use std::collections::HashMap;
2222
use tempfile::Builder as TempFileBuilder;
2323
use tempfile::TempDir;
2424
use toml::Value;
@@ -133,8 +133,12 @@ impl MDBook {
133133
.unwrap(),
134134
);
135135
let fallback_src_dir = root.join(config.get_fallback_src_path());
136-
let book =
137-
LoadedBook::Single(book::load_book_from_disk(&summary, localized_src_dir, fallback_src_dir, &config)?);
136+
let book = LoadedBook::Single(book::load_book_from_disk(
137+
&summary,
138+
localized_src_dir,
139+
fallback_src_dir,
140+
&config,
141+
)?);
138142

139143
let renderers = determine_renderers(&config);
140144
let preprocessors = determine_preprocessors(&config)?;
@@ -223,13 +227,16 @@ impl MDBook {
223227
let mut new_books = HashMap::new();
224228

225229
for (ident, book) in books.0.iter() {
226-
let preprocessed_book = self.preprocess(&preprocess_ctx, renderer, book.clone())?;
230+
let preprocessed_book =
231+
self.preprocess(&preprocess_ctx, renderer, book.clone())?;
227232
new_books.insert(ident.clone(), preprocessed_book);
228233
}
229234

230235
LoadedBook::Localized(LocalizedBooks(new_books))
231-
},
232-
LoadedBook::Single(ref book) => LoadedBook::Single(self.preprocess(&preprocess_ctx, renderer, book.clone())?),
236+
}
237+
LoadedBook::Single(ref book) => {
238+
LoadedBook::Single(self.preprocess(&preprocess_ctx, renderer, book.clone())?)
239+
}
233240
};
234241

235242
let name = renderer.name();

src/cmd/serve.rs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
9292
Some(lang_ident) => Some(lang_ident.clone()),
9393
// If not, it will be at the root.
9494
None => None,
95-
}
95+
},
9696
};
9797

9898
let sockaddr: SocketAddr = address
@@ -177,9 +177,6 @@ async fn serve(
177177
});
178178
// A warp Filter that serves from the filesystem.
179179
let book_route = warp::fs::dir(build_dir.clone());
180-
// The fallback route for 404 errors
181-
let fallback_route = warp::fs::file(build_dir.join(file_404))
182-
.map(|reply| warp::reply::with_status(reply, warp::http::StatusCode::NOT_FOUND));
183180

184181
std::panic::set_hook(Box::new(move |panic_info| {
185182
// exit if serve panics
@@ -189,13 +186,31 @@ async fn serve(
189186

190187
if let Some(lang_ident) = language {
191188
// Redirect root to the default translation directory, if serving a localized book.
192-
// BUG: This can't be `/{lang_ident}`, or the static assets won't get loaded.
193-
let index_for_language = format!("/{}/index.html", lang_ident).parse::<Uri>().unwrap();
194-
let redirect_to_index = warp::path::end().map(move || warp::redirect(index_for_language.clone()));
195-
let routes = livereload.or(redirect_to_index).or(book_route).or(fallback_route);
189+
// NOTE: This can't be `/{lang_ident}`, or the static assets won't get loaded.
190+
// BUG: Redirects get cached if you change the --language parameter,
191+
// meaning you'll get a 404 unless you disable cache in the developer tools.
192+
let index_for_language = format!("/{}/index.html", lang_ident)
193+
.parse::<Uri>()
194+
.unwrap();
195+
let redirect_to_index =
196+
warp::path::end().map(move || warp::redirect(index_for_language.clone()));
197+
198+
// BUG: It is not possible to conditionally redirect to the correct 404
199+
// page depending on the URL in warp, so just redirect to the one in the
200+
// default language.
201+
// See: https://github.com/seanmonstar/warp/issues/171
202+
let fallback_route = warp::fs::file(build_dir.join(lang_ident).join(file_404))
203+
.map(|reply| warp::reply::with_status(reply, warp::http::StatusCode::NOT_FOUND));
204+
205+
let routes = livereload
206+
.or(redirect_to_index)
207+
.or(book_route)
208+
.or(fallback_route);
196209
warp::serve(routes).run(address).await;
197-
}
198-
else {
210+
} else {
211+
// The fallback route for 404 errors
212+
let fallback_route = warp::fs::file(build_dir.join(file_404))
213+
.map(|reply| warp::reply::with_status(reply, warp::http::StatusCode::NOT_FOUND));
199214
let routes = livereload.or(book_route).or(fallback_route);
200215
warp::serve(routes).run(address).await;
201216
};

src/preprocess/cmd.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,8 @@ mod tests {
199199
);
200200

201201
let mut buffer = Vec::new();
202-
cmd.write_input(&mut buffer, &md.book.first(), &ctx).unwrap();
202+
cmd.write_input(&mut buffer, &md.book.first(), &ctx)
203+
.unwrap();
203204

204205
let (got_ctx, got_book) = CmdPreprocessor::parse_input(buffer.as_slice()).unwrap();
205206

src/renderer/html_handlebars/hbs_renderer.rs

Lines changed: 57 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -62,31 +62,49 @@ impl HtmlHandlebars {
6262
let mut path = src_dir.clone();
6363
path.push(lang_ident);
6464
path
65-
},
65+
}
6666
// `src_dir` is where index.html and the other extra files
6767
// are, so use that.
68-
None => src_dir.clone()
68+
None => src_dir.clone(),
6969
};
70-
self.render_book(ctx, &book, src_dir, &extra_file_dir, &ctx.destination, &ctx.config.build.build_dir, html_config, handlebars, theme)?;
70+
self.render_book(
71+
ctx,
72+
&book,
73+
src_dir,
74+
&extra_file_dir,
75+
&ctx.destination,
76+
&ctx.config.build.build_dir,
77+
html_config,
78+
handlebars,
79+
theme,
80+
)?;
7181
}
7282
}
7383

7484
Ok(())
7585
}
7686

77-
fn render_book<'a>(&self,
78-
ctx: &RenderContext,
79-
book: &Book,
80-
src_dir: &PathBuf,
81-
extra_file_dir: &PathBuf,
82-
destination: &PathBuf,
83-
build_dir: &PathBuf,
84-
html_config: &HtmlConfig,
85-
handlebars: &mut Handlebars<'a>,
86-
theme: &Theme,
87+
fn render_book<'a>(
88+
&self,
89+
ctx: &RenderContext,
90+
book: &Book,
91+
src_dir: &PathBuf,
92+
extra_file_dir: &PathBuf,
93+
destination: &PathBuf,
94+
build_dir: &PathBuf,
95+
html_config: &HtmlConfig,
96+
handlebars: &mut Handlebars<'a>,
97+
theme: &Theme,
8798
) -> Result<()> {
8899
let build_dir = ctx.root.join(build_dir);
89-
let mut data = make_data(&ctx.root, &book, &ctx.book, &ctx.config, &html_config, &theme)?;
100+
let mut data = make_data(
101+
&ctx.root,
102+
&book,
103+
&ctx.book,
104+
&ctx.config,
105+
&html_config,
106+
&theme,
107+
)?;
90108

91109
// Print version
92110
let mut print_content = String::new();
@@ -110,7 +128,14 @@ impl HtmlHandlebars {
110128

111129
// Render 404 page
112130
if html_config.input_404 != Some("".to_string()) {
113-
self.render_404(ctx, &html_config, src_dir, destination, handlebars, &mut data)?;
131+
self.render_404(
132+
ctx,
133+
&html_config,
134+
src_dir,
135+
destination,
136+
handlebars,
137+
&mut data,
138+
)?;
114139
}
115140

116141
// Print version
@@ -123,7 +148,8 @@ impl HtmlHandlebars {
123148
debug!("Render template");
124149
let rendered = handlebars.render("index", &data)?;
125150

126-
let rendered = self.post_process(rendered, &html_config.playground, ctx.config.rust.edition);
151+
let rendered =
152+
self.post_process(rendered, &html_config.playground, ctx.config.rust.edition);
127153

128154
utils::fs::write_file(&destination, "print.html", rendered.as_bytes())?;
129155
debug!("Creating print.html ✓");
@@ -147,12 +173,17 @@ impl HtmlHandlebars {
147173
.context("Unable to emit redirects")?;
148174

149175
// Copy all remaining files, avoid a recursive copy from/to the book build dir
150-
utils::fs::copy_files_except_ext(&extra_file_dir, &destination, true, Some(&build_dir), &["md"])?;
176+
utils::fs::copy_files_except_ext(
177+
&extra_file_dir,
178+
&destination,
179+
true,
180+
Some(&build_dir),
181+
&["md"],
182+
)?;
151183

152184
Ok(())
153185
}
154186

155-
156187
fn render_item(
157188
&self,
158189
item: &BookItem,
@@ -229,7 +260,7 @@ impl HtmlHandlebars {
229260
);
230261
if let Some(ref section) = ch.number {
231262
ctx.data
232-
.insert("section".to_owned(), json!(section.to_string()));
263+
.insert("section".to_owned(), json!(section.to_string()));
233264
}
234265

235266
// Render the handlebars template with the data
@@ -461,7 +492,10 @@ impl HtmlHandlebars {
461492
handlebars.register_helper("previous", Box::new(helpers::navigation::previous));
462493
handlebars.register_helper("next", Box::new(helpers::navigation::next));
463494
handlebars.register_helper("theme_option", Box::new(helpers::theme::theme_option));
464-
handlebars.register_helper("language_option", Box::new(helpers::language::language_option));
495+
handlebars.register_helper(
496+
"language_option",
497+
Box::new(helpers::language::language_option),
498+
);
465499
}
466500

467501
/// Copy across any additional CSS and JavaScript files which the book
@@ -568,7 +602,7 @@ impl HtmlHandlebars {
568602
fn maybe_wrong_theme_dir(dir: &Path) -> Result<bool> {
569603
fn entry_is_maybe_book_file(entry: fs::DirEntry) -> Result<bool> {
570604
Ok(entry.file_type()?.is_file()
571-
&& entry.path().extension().map_or(false, |ext| ext == "md"))
605+
&& entry.path().extension().map_or(false, |ext| ext == "md"))
572606
}
573607

574608
if dir.is_dir() {
@@ -842,7 +876,7 @@ fn make_data(
842876
languages.sort();
843877
data.insert("languages".to_owned(), json!(languages));
844878
data.insert("language_config".to_owned(), json!(config.language.clone()));
845-
},
879+
}
846880
LoadedBook::Single(_) => {
847881
data.insert("languages_enabled".to_owned(), json!(false));
848882
}
@@ -1012,7 +1046,7 @@ fn add_playground_pre(
10121046
"\n# #![allow(unused)]\n{}#fn main() {{\n{}#}}",
10131047
attrs, code
10141048
)
1015-
.into()
1049+
.into()
10161050
};
10171051
hide_lines(&content)
10181052
}

0 commit comments

Comments
 (0)