Make mdbook init
output multilingual structure
This commit is contained in:
parent
5e223e074e
commit
a042cfc72b
@ -3,7 +3,7 @@ use std::io::Write;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use super::MDBook;
|
use super::MDBook;
|
||||||
use crate::config::Config;
|
use crate::config::{Config, Language};
|
||||||
use crate::errors::*;
|
use crate::errors::*;
|
||||||
use crate::theme;
|
use crate::theme;
|
||||||
|
|
||||||
@ -14,22 +14,46 @@ pub struct BookBuilder {
|
|||||||
create_gitignore: bool,
|
create_gitignore: bool,
|
||||||
config: Config,
|
config: Config,
|
||||||
copy_theme: bool,
|
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 {
|
impl BookBuilder {
|
||||||
/// Create a new `BookBuilder` which will generate a book in the provided
|
/// Create a new `BookBuilder` which will generate a book in the provided
|
||||||
/// root directory.
|
/// root directory.
|
||||||
pub fn new<P: Into<PathBuf>>(root: P) -> BookBuilder {
|
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 {
|
BookBuilder {
|
||||||
root: root.into(),
|
root: root.into(),
|
||||||
create_gitignore: false,
|
create_gitignore: false,
|
||||||
config: Config::default(),
|
config: cfg,
|
||||||
copy_theme: false,
|
copy_theme: false,
|
||||||
|
language_ident: language_ident
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the [`Config`] to be used.
|
/// Get the output source directory of the builder.
|
||||||
pub fn with_config(&mut self, cfg: Config) -> &mut BookBuilder {
|
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.config = cfg;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -101,8 +125,8 @@ impl BookBuilder {
|
|||||||
|
|
||||||
File::create(book_toml)
|
File::create(book_toml)
|
||||||
.with_context(|| "Couldn't create book.toml")?
|
.with_context(|| "Couldn't create book.toml")?
|
||||||
.write_all(&cfg)
|
.write_all(&cfg)
|
||||||
.with_context(|| "Unable to write config to book.toml")?;
|
.with_context(|| "Unable to write config to book.toml")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,7 +196,7 @@ impl BookBuilder {
|
|||||||
|
|
||||||
fn create_stub_files(&self) -> Result<()> {
|
fn create_stub_files(&self) -> Result<()> {
|
||||||
debug!("Creating example book contents");
|
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");
|
let summary = src_dir.join("SUMMARY.md");
|
||||||
if !summary.exists() {
|
if !summary.exists() {
|
||||||
@ -193,10 +217,10 @@ impl BookBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create_directory_structure(&self) -> Result<()> {
|
fn create_directory_structure(&self) -> Result<()> {
|
||||||
debug!("Creating directory tree");
|
debug!("Creating directory tree at {}", self.root.display());
|
||||||
fs::create_dir_all(&self.root)?;
|
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)?;
|
fs::create_dir_all(&src)?;
|
||||||
|
|
||||||
let build = self.root.join(&self.config.build.build_dir);
|
let build = self.root.join(&self.config.build.build_dir);
|
||||||
|
@ -84,7 +84,7 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
builder.build()?;
|
builder.build()?;
|
||||||
println!("\nAll done, no errors...");
|
println!("\nCreated new book at {}", builder.source_dir().display());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -197,8 +197,8 @@ async fn serve(
|
|||||||
warp::path::end().map(move || warp::redirect(index_for_language.clone()));
|
warp::path::end().map(move || warp::redirect(index_for_language.clone()));
|
||||||
|
|
||||||
// BUG: It is not possible to conditionally redirect to the correct 404
|
// 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
|
// page depending on the URL using warp, so just redirect to the one in
|
||||||
// default language.
|
// the default language.
|
||||||
// See: https://github.com/seanmonstar/warp/issues/171
|
// See: https://github.com/seanmonstar/warp/issues/171
|
||||||
let fallback_route = warp::fs::file(build_dir.join(lang_ident).join(file_404))
|
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));
|
.map(|reply| warp::reply::with_status(reply, warp::http::StatusCode::NOT_FOUND));
|
||||||
|
@ -259,8 +259,8 @@ impl Config {
|
|||||||
// Languages have been specified, assume directory structure with
|
// Languages have been specified, assume directory structure with
|
||||||
// language subfolders.
|
// language subfolders.
|
||||||
Some(ref default) => match index {
|
Some(ref default) => match index {
|
||||||
// Make sure that the language we passed was actually
|
// Make sure that the language we passed was actually declared
|
||||||
// declared in the config, and return `None` if not.
|
// in the config, and return an `Err` if not.
|
||||||
Some(lang_ident) => match self.language.0.get(lang_ident.as_ref()) {
|
Some(lang_ident) => match self.language.0.get(lang_ident.as_ref()) {
|
||||||
Some(_) => Ok(Some(lang_ident.as_ref().into())),
|
Some(_) => Ok(Some(lang_ident.as_ref().into())),
|
||||||
None => Err(anyhow!(
|
None => Err(anyhow!(
|
||||||
@ -272,7 +272,7 @@ impl Config {
|
|||||||
None => Ok(Some(default.to_string())),
|
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 {
|
None => match index {
|
||||||
// We passed in a language from the frontend, but the config
|
// We passed in a language from the frontend, but the config
|
||||||
// offers no languages.
|
// offers no languages.
|
||||||
@ -298,8 +298,8 @@ impl Config {
|
|||||||
Ok(buf)
|
Ok(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// No default language was configured in book.toml. Preserve
|
// No [language] table was declared in book.toml. Preserve backwards
|
||||||
// backwards compatibility by just returning `src`.
|
// compatibility by just returning `src`.
|
||||||
None => Ok(self.book.src.clone()),
|
None => Ok(self.book.src.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -491,6 +491,11 @@ impl Serialize for Config {
|
|||||||
table.insert("rust", rust_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)
|
table.serialize(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -551,7 +556,7 @@ impl Default for BookConfig {
|
|||||||
authors: Vec::new(),
|
authors: Vec::new(),
|
||||||
description: None,
|
description: None,
|
||||||
src: PathBuf::from("src"),
|
src: PathBuf::from("src"),
|
||||||
multilingual: false,
|
multilingual: true,
|
||||||
language: Some(String::from("en")),
|
language: Some(String::from("en")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,21 +53,22 @@ impl HtmlHandlebars {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
LoadedBook::Single(ref book) => {
|
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 {
|
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) => {
|
Some(lang_ident) => {
|
||||||
let mut path = src_dir.clone();
|
let mut path = src_dir.clone();
|
||||||
path.push(lang_ident);
|
path.push(lang_ident);
|
||||||
path
|
path
|
||||||
}
|
}
|
||||||
// `src_dir` is where index.html and the other extra files
|
|
||||||
// are, so use that.
|
|
||||||
None => src_dir.clone(),
|
None => src_dir.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.render_book(
|
self.render_book(
|
||||||
ctx,
|
ctx,
|
||||||
&book,
|
&book,
|
||||||
|
@ -10,7 +10,7 @@ use tempfile::Builder as TempFileBuilder;
|
|||||||
/// are created.
|
/// are created.
|
||||||
#[test]
|
#[test]
|
||||||
fn base_mdbook_init_should_create_default_content() {
|
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();
|
let temp = TempFileBuilder::new().prefix("mdbook").tempdir().unwrap();
|
||||||
for file in &created_files {
|
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();
|
let contents = fs::read_to_string(temp.path().join("book.toml")).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
contents,
|
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 created_files = vec!["intro.md", "first.md", "outro.md"];
|
||||||
|
|
||||||
let temp = TempFileBuilder::new().prefix("mdbook").tempdir().unwrap();
|
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();
|
fs::create_dir_all(src_dir.clone()).unwrap();
|
||||||
static SUMMARY: &str = r#"# Summary
|
static SUMMARY: &str = r#"# Summary
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ fn run_mdbook_init_should_create_content_from_summary() {
|
|||||||
/// files, then call `mdbook init`.
|
/// files, then call `mdbook init`.
|
||||||
#[test]
|
#[test]
|
||||||
fn run_mdbook_init_with_custom_book_and_src_locations() {
|
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();
|
let temp = TempFileBuilder::new().prefix("mdbook").tempdir().unwrap();
|
||||||
for file in &created_files {
|
for file in &created_files {
|
||||||
|
Loading…
Reference in New Issue
Block a user