Skip to content

Commit

Permalink
Redirect to a 404 page when serving translated
Browse files Browse the repository at this point in the history
We can't redirect in warp based on the URL, so redirect to the default
language's 404 page instead.

See: seanmonstar/warp#171
  • Loading branch information
Ruin0x11 authored and rzerres committed Sep 14, 2021
1 parent b21852e commit a133d2b
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 47 deletions.
30 changes: 19 additions & 11 deletions src/book/book.rs
Expand Up @@ -18,27 +18,37 @@ pub fn load_book<P: AsRef<Path>>(
if cfg.language.has_localized_dir_structure() {
match build_opts.language_ident {
// Build a single book's translation.
Some(_) => Ok(LoadedBook::Single(load_single_book_translation(&root_dir, cfg, &build_opts.language_ident)?)),
Some(_) => Ok(LoadedBook::Single(load_single_book_translation(
&root_dir,
cfg,
&build_opts.language_ident,
)?)),
// Build all available translations at once.
None => {
let mut translations = HashMap::new();
for (lang_ident, _) in cfg.language.0.iter() {
let book = load_single_book_translation(&root_dir, cfg, &Some(lang_ident.clone()))?;
let book =
load_single_book_translation(&root_dir, cfg, &Some(lang_ident.clone()))?;
translations.insert(lang_ident.clone(), book);
}
Ok(LoadedBook::Localized(LocalizedBooks(translations)))
}
}
} else {
Ok(LoadedBook::Single(load_single_book_translation(&root_dir, cfg, &None)?))
Ok(LoadedBook::Single(load_single_book_translation(
&root_dir, cfg, &None,
)?))
}
}

fn load_single_book_translation<P: AsRef<Path>>(root_dir: P, cfg: &Config, language_ident: &Option<String>) -> Result<Book> {
let localized_src_dir = root_dir.as_ref().join(
cfg.get_localized_src_path(build_opts.language_ident.as_ref())
.unwrap(),
);
fn load_single_book_translation<P: AsRef<Path>>(
root_dir: P,
cfg: &Config,
language_ident: &Option<String>,
) -> Result<Book> {
let localized_src_dir = root_dir
.as_ref()
.join(cfg.get_localized_src_path(language_ident.as_ref()).unwrap());
let fallback_src_dir = root_dir.as_ref().join(cfg.get_fallback_src_path());

let summary_md = localized_src_dir.join("SUMMARY.md");
Expand Down Expand Up @@ -182,9 +192,7 @@ impl LocalizedBooks {
items.extend(book.iter().items);
}

BookItems {
items: items
}
BookItems { items: items }
}

/// Recursively apply a closure to each item in the book, allowing you to
Expand Down
32 changes: 22 additions & 10 deletions src/book/mod.rs
Expand Up @@ -10,15 +10,15 @@ mod book;
mod init;
mod summary;

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

use std::collections::HashMap;
use std::io::Write;
use std::path::PathBuf;
use std::process::Command;
use std::string::ToString;
use std::collections::HashMap;
use tempfile::Builder as TempFileBuilder;
use tempfile::TempDir;
use toml::Value;
Expand Down Expand Up @@ -131,8 +131,12 @@ impl MDBook {
.unwrap(),
);
let fallback_src_dir = root.join(config.get_fallback_src_path());
let book =
LoadedBook::Single(book::load_book_from_disk(&summary, localized_src_dir, fallback_src_dir, &config)?);
let book = LoadedBook::Single(book::load_book_from_disk(
&summary,
localized_src_dir,
fallback_src_dir,
&config,
)?);

let renderers = determine_renderers(&config);
let preprocessors = determine_preprocessors(&config)?;
Expand Down Expand Up @@ -207,7 +211,12 @@ impl MDBook {
Ok(())
}

fn preprocess(&self, preprocess_ctx: &PreprocessorContext, renderer: &dyn Renderer, book: Book) -> Result<Book> {
fn preprocess(
&self,
preprocess_ctx: &PreprocessorContext,
renderer: &dyn Renderer,
book: Book,
) -> Result<Book> {
let mut preprocessed_book = book;
for preprocessor in &self.preprocessors {
if preprocessor_should_run(&**preprocessor, renderer, &self.config) {
Expand All @@ -232,13 +241,16 @@ impl MDBook {
let mut new_books = HashMap::new();

for (ident, book) in books.0.iter() {
let preprocessed_book = self.preprocess(&preprocess_ctx, renderer, book.clone())?;
let preprocessed_book =
self.preprocess(&preprocess_ctx, renderer, book.clone())?;
new_books.insert(ident.clone(), preprocessed_book);
}

LoadedBook::Localized(LocalizedBooks(new_books))
},
LoadedBook::Single(ref book) => LoadedBook::Single(self.preprocess(&preprocess_ctx, renderer, book.clone())?),
}
LoadedBook::Single(ref book) => {
LoadedBook::Single(self.preprocess(&preprocess_ctx, renderer, book.clone())?)
}
};

info!("Running the {} backend", renderer.name());
Expand Down Expand Up @@ -364,8 +376,8 @@ impl MDBook {
for (_, book) in books.0.iter() {
self.test_book(book, &temp_dir, &library_args)?;
}
},
LoadedBook::Single(ref book) => self.test_book(&book, &temp_dir, &library_args)?
}
LoadedBook::Single(ref book) => self.test_book(&book, &temp_dir, &library_args)?,
}

Ok(())
Expand Down
33 changes: 24 additions & 9 deletions src/cmd/serve.rs
Expand Up @@ -177,18 +177,33 @@ async fn serve(
});
// A warp Filter that serves from the filesystem.
let book_route = warp::fs::dir(build_dir.clone());
// The fallback route for 404 errors
let fallback_route = warp::fs::file(build_dir.join(file_404))
.map(|reply| warp::reply::with_status(reply, warp::http::StatusCode::NOT_FOUND));
if let Some(lang_ident) = language {
// Redirect root to the default translation directory, if serving a localized book.
// BUG: This can't be `/{lang_ident}`, or the static assets won't get loaded.
let index_for_language = format!("/{}/index.html", lang_ident).parse::<Uri>().unwrap();
let redirect_to_index = warp::path::end().map(move || warp::redirect(index_for_language.clone()));
let routes = livereload.or(redirect_to_index).or(book_route).or(fallback_route);
// NOTE: This can't be `/{lang_ident}`, or the static assets won't get loaded.
// BUG: Redirects get cached if you change the --language parameter,
// meaning you'll get a 404 unless you disable cache in the developer tools.
let index_for_language = format!("/{}/index.html", lang_ident)
.parse::<Uri>()
.unwrap();
let redirect_to_index =
warp::path::end().map(move || warp::redirect(index_for_language.clone()));

// BUG: It is not possible to conditionally redirect to the correct 404
// page depending on the URL in warp, so just redirect to the one in the
// default language.
// See: https://github.com/seanmonstar/warp/issues/171
let fallback_route = warp::fs::file(build_dir.join(lang_ident).join(file_404))
.map(|reply| warp::reply::with_status(reply, warp::http::StatusCode::NOT_FOUND));

let routes = livereload
.or(redirect_to_index)
.or(book_route)
.or(fallback_route);
warp::serve(routes).run(address).await;
}
else {
} else {
// The fallback route for 404 errors
let fallback_route = warp::fs::file(build_dir.join(file_404))
.map(|reply| warp::reply::with_status(reply, warp::http::StatusCode::NOT_FOUND));
let routes = livereload.or(book_route).or(fallback_route);
warp::serve(routes).run(address).await;
};
Expand Down
3 changes: 2 additions & 1 deletion src/preprocess/cmd.rs
Expand Up @@ -199,7 +199,8 @@ mod tests {
);

let mut buffer = Vec::new();
cmd.write_input(&mut buffer, &md.book.first(), &ctx).unwrap();
cmd.write_input(&mut buffer, &md.book.first(), &ctx)
.unwrap();

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

Expand Down
1 change: 0 additions & 1 deletion src/renderer/html_handlebars/hbs_renderer.rs
Expand Up @@ -152,7 +152,6 @@ impl HtmlHandlebars {
Ok(())
}


fn render_item(
&self,
item: &BookItem,
Expand Down
30 changes: 17 additions & 13 deletions src/renderer/html_handlebars/helpers/language.rs
@@ -1,6 +1,6 @@
use crate::config::LanguageConfig;
use handlebars::{Context, Handlebars, Helper, Output, RenderContext, RenderError};
use std::path::Path;
use crate::config::LanguageConfig;

pub fn language_option(
h: &Helper<'_, '_>,
Expand All @@ -22,10 +22,10 @@ pub fn language_option(

let current_path = rc
.evaluate(ctx, "@root/path")?
.as_json()
.as_str()
.ok_or_else(|| RenderError::new("Type error for `path`, string expected"))?
.replace("\"", "");
.as_json()
.as_str()
.ok_or_else(|| RenderError::new("Type error for `path`, string expected"))?
.replace("\"", "");

let rendered_path = Path::new(&current_path)
.with_extension("html")
Expand All @@ -35,14 +35,15 @@ pub fn language_option(

let path_to_root = rc
.evaluate(ctx, "@root/path_to_root")?
.as_json()
.as_str()
.ok_or_else(|| RenderError::new("Type error for `path_to_root`, string expected"))?
.to_string();
.as_json()
.as_str()
.ok_or_else(|| RenderError::new("Type error for `path_to_root`, string expected"))?
.to_string();

let language = languages.0.get(param).ok_or_else(|| {
RenderError::new(format!("Unknown language identifier '{}'", param))
})?;
let language = languages
.0
.get(param)
.ok_or_else(|| RenderError::new(format!("Unknown language identifier '{}'", param)))?;

let mut href = String::new();
href.push_str(&path_to_root);
Expand All @@ -51,7 +52,10 @@ pub fn language_option(
href.push_str("/");
href.push_str(&rendered_path);

out.write(&format!("<a href=\"{}\"><button role=\"menuitem\" class=\"language\" id=\"light\">", href))?;
out.write(&format!(
"<a href=\"{}\"><button role=\"menuitem\" class=\"language\" id=\"light\">",
href
))?;
out.write(&language.name)?;
out.write("</button></a>")?;

Expand Down
2 changes: 1 addition & 1 deletion src/renderer/html_handlebars/helpers/mod.rs
@@ -1,4 +1,4 @@
pub mod language;
pub mod navigation;
pub mod theme;
pub mod toc;
pub mod language;
2 changes: 1 addition & 1 deletion src/renderer/markdown_renderer.rs
@@ -1,4 +1,4 @@
use crate::book::{BookItem, LoadedBook, Book};
use crate::book::{Book, BookItem, LoadedBook};
use crate::errors::*;
use crate::renderer::{RenderContext, Renderer};
use crate::utils;
Expand Down

0 comments on commit a133d2b

Please sign in to comment.