Make mdbook init output multilingual structure

This commit is contained in:
Ruin0x11 2020-08-28 11:35:42 -07:00
parent 5e223e074e
commit a042cfc72b
6 changed files with 59 additions and 29 deletions

View File

@ -3,7 +3,7 @@ use std::io::Write;
use std::path::PathBuf;
use super::MDBook;
use crate::config::Config;
use crate::config::{Config, Language};
use crate::errors::*;
use crate::theme;
@ -14,22 +14,46 @@ pub struct BookBuilder {
create_gitignore: bool,
config: Config,
copy_theme: bool,
language_ident: String
}
fn add_default_language(cfg: &mut Config, language_ident: String) {
let language = Language {
name: String::from("English"),
default: true,
title: None,
authors: None,
description: None,
};
cfg.language.0.insert(language_ident, language);
}
impl BookBuilder {
/// Create a new `BookBuilder` which will generate a book in the provided
/// root directory.
pub fn new<P: Into<PathBuf>>(root: P) -> BookBuilder {
let language_ident = String::from("en");
let mut cfg = Config::default();
add_default_language(&mut cfg, language_ident.clone());
BookBuilder {
root: root.into(),
create_gitignore: false,
config: Config::default(),
config: cfg,
copy_theme: false,
language_ident: language_ident
}
}
/// Set the [`Config`] to be used.
pub fn with_config(&mut self, cfg: Config) -> &mut BookBuilder {
/// Get the output source directory of the builder.
pub fn source_dir(&self) -> PathBuf {
let src = self.config.get_localized_src_path(Some(&self.language_ident)).unwrap();
self.root.join(src)
}
/// Set the `Config` to be used.
pub fn with_config(&mut self, mut cfg: Config) -> &mut BookBuilder {
add_default_language(&mut cfg, self.language_ident.clone());
self.config = cfg;
self
}
@ -101,8 +125,8 @@ impl BookBuilder {
File::create(book_toml)
.with_context(|| "Couldn't create book.toml")?
.write_all(&cfg)
.with_context(|| "Unable to write config to book.toml")?;
.write_all(&cfg)
.with_context(|| "Unable to write config to book.toml")?;
Ok(())
}
@ -172,7 +196,7 @@ impl BookBuilder {
fn create_stub_files(&self) -> Result<()> {
debug!("Creating example book contents");
let src_dir = self.root.join(&self.config.book.src);
let src_dir = self.source_dir();
let summary = src_dir.join("SUMMARY.md");
if !summary.exists() {
@ -193,10 +217,10 @@ impl BookBuilder {
}
fn create_directory_structure(&self) -> Result<()> {
debug!("Creating directory tree");
debug!("Creating directory tree at {}", self.root.display());
fs::create_dir_all(&self.root)?;
let src = self.root.join(&self.config.book.src);
let src = self.source_dir();
fs::create_dir_all(&src)?;
let build = self.root.join(&self.config.build.build_dir);

View File

@ -84,7 +84,7 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
}
builder.build()?;
println!("\nAll done, no errors...");
println!("\nCreated new book at {}", builder.source_dir().display());
Ok(())
}

View File

@ -197,8 +197,8 @@ async fn serve(
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.
// page depending on the URL using 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));

View File

@ -259,8 +259,8 @@ impl Config {
// Languages have been specified, assume directory structure with
// language subfolders.
Some(ref default) => match index {
// Make sure that the language we passed was actually
// declared in the config, and return `None` if not.
// Make sure that the language we passed was actually declared
// in the config, and return an `Err` if not.
Some(lang_ident) => match self.language.0.get(lang_ident.as_ref()) {
Some(_) => Ok(Some(lang_ident.as_ref().into())),
None => Err(anyhow!(
@ -272,7 +272,7 @@ impl Config {
None => Ok(Some(default.to_string())),
},
// No default language was configured in book.toml.
// No [language] table was declared in book.toml.
None => match index {
// We passed in a language from the frontend, but the config
// offers no languages.
@ -298,8 +298,8 @@ impl Config {
Ok(buf)
}
// No default language was configured in book.toml. Preserve
// backwards compatibility by just returning `src`.
// No [language] table was declared in book.toml. Preserve backwards
// compatibility by just returning `src`.
None => Ok(self.book.src.clone()),
}
}
@ -491,6 +491,11 @@ impl Serialize for Config {
table.insert("rust", rust_config);
}
if !self.language.0.is_empty() {
let language_config = Value::try_from(&self.language).expect("should always be serializable");
table.insert("language", language_config);
}
table.serialize(s)
}
}
@ -551,7 +556,7 @@ impl Default for BookConfig {
authors: Vec::new(),
description: None,
src: PathBuf::from("src"),
multilingual: false,
multilingual: true,
language: Some(String::from("en")),
}
}

View File

@ -53,21 +53,22 @@ impl HtmlHandlebars {
}
}
LoadedBook::Single(ref book) => {
// `src_dir` points to the root source directory. If this book
// is actually multilingual and we specified a single language
// to build on the command line, then `src_dir` will not be
// pointing at the subdirectory with the specified translation's
// index/summary files. We have to append the language
// identifier to prevent the files from the other translations
// from being copied in the final step.
let extra_file_dir = match &ctx.build_opts.language_ident {
// `src_dir` points to the root source directory, not the
// subdirectory with the translation's index/summary files.
// We have to append the language identifier to prevent the
// files from the other translations from being copied in
// the final step.
Some(lang_ident) => {
let mut path = src_dir.clone();
path.push(lang_ident);
path
}
// `src_dir` is where index.html and the other extra files
// are, so use that.
None => src_dir.clone(),
};
self.render_book(
ctx,
&book,

View File

@ -10,7 +10,7 @@ use tempfile::Builder as TempFileBuilder;
/// are created.
#[test]
fn base_mdbook_init_should_create_default_content() {
let created_files = vec!["book", "src", "src/SUMMARY.md", "src/chapter_1.md"];
let created_files = vec!["book", "src", "src/en", "src/en/SUMMARY.md", "src/en/chapter_1.md"];
let temp = TempFileBuilder::new().prefix("mdbook").tempdir().unwrap();
for file in &created_files {
@ -28,7 +28,7 @@ fn base_mdbook_init_should_create_default_content() {
let contents = fs::read_to_string(temp.path().join("book.toml")).unwrap();
assert_eq!(
contents,
"[book]\nauthors = []\nlanguage = \"en\"\nmultilingual = false\nsrc = \"src\"\n"
"[book]\nauthors = []\nlanguage = \"en\"\nmultilingual = true\nsrc = \"src\"\n[language.en]\ndefault = true\nname = \"English\"\n"
);
}
@ -39,7 +39,7 @@ fn run_mdbook_init_should_create_content_from_summary() {
let created_files = vec!["intro.md", "first.md", "outro.md"];
let temp = TempFileBuilder::new().prefix("mdbook").tempdir().unwrap();
let src_dir = temp.path().join("src");
let src_dir = temp.path().join("src").join("en");
fs::create_dir_all(src_dir.clone()).unwrap();
static SUMMARY: &str = r#"# Summary
@ -66,7 +66,7 @@ fn run_mdbook_init_should_create_content_from_summary() {
/// files, then call `mdbook init`.
#[test]
fn run_mdbook_init_with_custom_book_and_src_locations() {
let created_files = vec!["out", "in", "in/SUMMARY.md", "in/chapter_1.md"];
let created_files = vec!["out", "in", "in/en", "in/en/SUMMARY.md", "in/en/chapter_1.md"];
let temp = TempFileBuilder::new().prefix("mdbook").tempdir().unwrap();
for file in &created_files {