Change book source root depending on language
This commit is contained in:
parent
e4b443cd4a
commit
24e6d6b605
|
@ -29,6 +29,7 @@ use crate::renderer::{CmdRenderer, HtmlHandlebars, MarkdownRenderer, RenderConte
|
|||
use crate::utils;
|
||||
|
||||
use crate::config::{Config, RustEdition};
|
||||
use crate::build_opts::BuildOpts;
|
||||
|
||||
/// The object used to manage and build a book.
|
||||
pub struct MDBook {
|
||||
|
@ -38,6 +39,10 @@ pub struct MDBook {
|
|||
pub config: Config,
|
||||
/// A representation of the book's contents in memory.
|
||||
pub book: Book,
|
||||
/// Build options passed from frontend.
|
||||
pub build_opts: BuildOpts,
|
||||
|
||||
/// List of renderers to be run on the book.
|
||||
renderers: Vec<Box<dyn Renderer>>,
|
||||
|
||||
/// List of pre-processors to be run on the book.
|
||||
|
@ -47,6 +52,12 @@ pub struct MDBook {
|
|||
impl MDBook {
|
||||
/// Load a book from its root directory on disk.
|
||||
pub fn load<P: Into<PathBuf>>(book_root: P) -> Result<MDBook> {
|
||||
MDBook::load_with_build_opts(book_root, BuildOpts::default())
|
||||
}
|
||||
|
||||
/// Load a book from its root directory on disk, passing in options from the
|
||||
/// frontend.
|
||||
pub fn load_with_build_opts<P: Into<PathBuf>>(book_root: P, build_opts: BuildOpts) -> Result<MDBook> {
|
||||
let book_root = book_root.into();
|
||||
let config_location = book_root.join("book.toml");
|
||||
|
||||
|
@ -75,11 +86,11 @@ impl MDBook {
|
|||
}
|
||||
}
|
||||
|
||||
MDBook::load_with_config(book_root, config)
|
||||
MDBook::load_with_config(book_root, config, build_opts)
|
||||
}
|
||||
|
||||
/// Load a book from its root directory using a custom `Config`.
|
||||
pub fn load_with_config<P: Into<PathBuf>>(book_root: P, config: Config) -> Result<MDBook> {
|
||||
/// Load a book from its root directory using a custom config.
|
||||
pub fn load_with_config<P: Into<PathBuf>>(book_root: P, config: Config, build_opts: BuildOpts) -> Result<MDBook> {
|
||||
let root = book_root.into();
|
||||
|
||||
let src_dir = root.join(&config.book.src);
|
||||
|
@ -92,6 +103,7 @@ impl MDBook {
|
|||
root,
|
||||
config,
|
||||
book,
|
||||
build_opts,
|
||||
renderers,
|
||||
preprocessors,
|
||||
})
|
||||
|
@ -102,6 +114,7 @@ impl MDBook {
|
|||
book_root: P,
|
||||
config: Config,
|
||||
summary: Summary,
|
||||
build_opts: BuildOpts,
|
||||
) -> Result<MDBook> {
|
||||
let root = book_root.into();
|
||||
|
||||
|
@ -115,6 +128,7 @@ impl MDBook {
|
|||
root,
|
||||
config,
|
||||
book,
|
||||
build_opts,
|
||||
renderers,
|
||||
preprocessors,
|
||||
})
|
||||
|
@ -185,6 +199,7 @@ impl MDBook {
|
|||
let mut preprocessed_book = self.book.clone();
|
||||
let preprocess_ctx = PreprocessorContext::new(
|
||||
self.root.clone(),
|
||||
self.build_opts.clone(),
|
||||
self.config.clone(),
|
||||
renderer.name().to_string(),
|
||||
);
|
||||
|
@ -201,7 +216,8 @@ impl MDBook {
|
|||
|
||||
let mut render_context = RenderContext::new(
|
||||
self.root.clone(),
|
||||
preprocessed_book,
|
||||
preprocessed_book.clone(),
|
||||
self.build_opts.clone(),
|
||||
self.config.clone(),
|
||||
build_dir,
|
||||
);
|
||||
|
@ -241,7 +257,7 @@ impl MDBook {
|
|||
|
||||
// FIXME: Is "test" the proper renderer name to use here?
|
||||
let preprocess_context =
|
||||
PreprocessorContext::new(self.root.clone(), self.config.clone(), "test".to_string());
|
||||
PreprocessorContext::new(self.root.clone(), self.build_opts.clone(), self.config.clone(), "test".to_string());
|
||||
|
||||
let book = LinkPreprocessor::new().run(&preprocess_context, self.book.clone())?;
|
||||
// Index Preprocessor is disabled so that chapter paths continue to point to the
|
||||
|
@ -336,7 +352,8 @@ impl MDBook {
|
|||
|
||||
/// Get the directory containing this book's source files.
|
||||
pub fn source_dir(&self) -> PathBuf {
|
||||
self.root.join(&self.config.book.src)
|
||||
let src = self.config.get_localized_src_path(self.build_opts.language_ident.as_ref()).unwrap();
|
||||
self.root.join(src)
|
||||
}
|
||||
|
||||
/// Get the directory containing the theme resources for the book.
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
//! Build options.
|
||||
|
||||
/// Build options passed from the frontend to control how the book is built.
|
||||
/// Separate from `Config`, which is global to all book languages.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
|
||||
#[serde(default, rename_all = "kebab-case")]
|
||||
pub struct BuildOpts {
|
||||
/// Language of the book to render.
|
||||
pub language_ident: Option<String>,
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{get_book_dir, open};
|
||||
use crate::{get_book_dir, get_build_opts, open};
|
||||
use clap::{App, ArgMatches, SubCommand};
|
||||
use mdbook::errors::Result;
|
||||
use mdbook::MDBook;
|
||||
|
@ -22,7 +22,8 @@ pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
|
|||
// Build command implementation
|
||||
pub fn execute(args: &ArgMatches) -> Result<()> {
|
||||
let book_dir = get_book_dir(args);
|
||||
let mut book = MDBook::load(&book_dir)?;
|
||||
let opts = get_build_opts(args);
|
||||
let mut book = MDBook::load_with_build_opts(&book_dir, opts)?;
|
||||
|
||||
if let Some(dest_dir) = args.value_of("dest-dir") {
|
||||
book.config.build.build_dir = dest_dir.into();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::get_book_dir;
|
||||
use crate::{get_book_dir, get_build_opts};
|
||||
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||
use mdbook::errors::Result;
|
||||
use mdbook::MDBook;
|
||||
|
@ -34,7 +34,8 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
|
|||
.map(std::iter::Iterator::collect)
|
||||
.unwrap_or_default();
|
||||
let book_dir = get_book_dir(args);
|
||||
let mut book = MDBook::load(&book_dir)?;
|
||||
let build_opts = get_build_opts(args);
|
||||
let mut book = MDBook::load_with_build_opts(&book_dir, build_opts)?;
|
||||
|
||||
if let Some(dest_dir) = args.value_of("dest-dir") {
|
||||
book.config.build.build_dir = dest_dir.into();
|
||||
|
|
|
@ -252,6 +252,31 @@ impl Config {
|
|||
self.get(&key).and_then(Value::as_table)
|
||||
}
|
||||
|
||||
/// Get the source directory of a localized book corresponding to language ident `index`.
|
||||
pub fn get_localized_src_path<I: AsRef<str>>(&self, index: Option<I>) -> Option<PathBuf> {
|
||||
match self.language.default_language() {
|
||||
Some(default) => match index {
|
||||
// Make sure that the language we passed was actually
|
||||
// declared in the config, and return `None` if not.
|
||||
Some(lang_ident) => self.language.0.get(lang_ident.as_ref()).map(|_| {
|
||||
let mut buf = PathBuf::new();
|
||||
buf.push(self.book.src.clone());
|
||||
buf.push(lang_ident.as_ref());
|
||||
buf
|
||||
}),
|
||||
// Use the default specified in book.toml.
|
||||
None => Some(PathBuf::from(default))
|
||||
}
|
||||
|
||||
// No default language was configured in book.toml. Preserve
|
||||
// backwards compatibility by just returning `src`.
|
||||
None => match index {
|
||||
Some(_) => None,
|
||||
None => Some(self.book.src.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn from_legacy(mut table: Value) -> Config {
|
||||
let mut cfg = Config::default();
|
||||
|
||||
|
@ -354,7 +379,6 @@ impl<'de> Deserialize<'de> for Config {
|
|||
.count();
|
||||
|
||||
if default_languages != 1 {
|
||||
use serde::de::Error;
|
||||
return Err(D::Error::custom(
|
||||
"If languages are specified, exactly one must be set as 'default'"
|
||||
));
|
||||
|
@ -727,10 +751,10 @@ pub struct Language {
|
|||
|
||||
impl LanguageConfig {
|
||||
/// Returns the default language specified in the config.
|
||||
pub fn default_language(&self) -> Option<&Language> {
|
||||
pub fn default_language(&self) -> Option<&String> {
|
||||
self.0.iter()
|
||||
.find(|(_, lang)| lang.default)
|
||||
.map(|(_, lang)| lang)
|
||||
.map(|(lang_ident, _)| lang_ident)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -98,6 +98,7 @@ extern crate serde_json;
|
|||
extern crate pretty_assertions;
|
||||
|
||||
pub mod book;
|
||||
pub mod build_opts;
|
||||
pub mod config;
|
||||
pub mod preprocess;
|
||||
pub mod renderer;
|
||||
|
|
|
@ -9,6 +9,7 @@ use clap::{App, AppSettings, Arg, ArgMatches, Shell, SubCommand};
|
|||
use env_logger::Builder;
|
||||
use log::LevelFilter;
|
||||
use mdbook::utils;
|
||||
use mdbook::build_opts::BuildOpts;
|
||||
use std::env;
|
||||
use std::ffi::OsStr;
|
||||
use std::io::Write;
|
||||
|
@ -131,6 +132,14 @@ fn get_book_dir(args: &ArgMatches) -> PathBuf {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_build_opts(args: &ArgMatches) -> BuildOpts {
|
||||
let language = args.value_of("language");
|
||||
|
||||
BuildOpts {
|
||||
language_ident: language.map(String::from)
|
||||
}
|
||||
}
|
||||
|
||||
fn open<P: AsRef<OsStr>>(path: P) {
|
||||
info!("Opening web browser");
|
||||
if let Err(e) = open::that(path) {
|
||||
|
|
|
@ -179,6 +179,7 @@ impl Preprocessor for CmdPreprocessor {
|
|||
mod tests {
|
||||
use super::*;
|
||||
use crate::MDBook;
|
||||
use crate::build_opts::BuildOpts;
|
||||
use std::path::Path;
|
||||
|
||||
fn guide() -> MDBook {
|
||||
|
@ -192,6 +193,7 @@ mod tests {
|
|||
let md = guide();
|
||||
let ctx = PreprocessorContext::new(
|
||||
md.root.clone(),
|
||||
BuildOpts::default(),
|
||||
md.config.clone(),
|
||||
"some-renderer".to_string(),
|
||||
);
|
||||
|
|
|
@ -26,7 +26,7 @@ impl Preprocessor for IndexPreprocessor {
|
|||
}
|
||||
|
||||
fn run(&self, ctx: &PreprocessorContext, mut book: Book) -> Result<Book> {
|
||||
let source_dir = ctx.root.join(&ctx.config.book.src);
|
||||
let source_dir = ctx.source_dir();
|
||||
book.for_each_mut(|section: &mut BookItem| {
|
||||
if let BookItem::Chapter(ref mut ch) = *section {
|
||||
if let Some(ref mut path) = ch.path {
|
||||
|
|
|
@ -42,7 +42,7 @@ impl Preprocessor for LinkPreprocessor {
|
|||
}
|
||||
|
||||
fn run(&self, ctx: &PreprocessorContext, mut book: Book) -> Result<Book> {
|
||||
let src_dir = ctx.root.join(&ctx.config.book.src);
|
||||
let src_dir = ctx.source_dir();
|
||||
|
||||
book.for_each_mut(|section: &mut BookItem| {
|
||||
if let BookItem::Chapter(ref mut ch) = *section {
|
||||
|
|
|
@ -10,6 +10,7 @@ mod links;
|
|||
|
||||
use crate::book::Book;
|
||||
use crate::config::Config;
|
||||
use crate::build_opts::BuildOpts;
|
||||
use crate::errors::*;
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
@ -22,6 +23,8 @@ use std::path::PathBuf;
|
|||
pub struct PreprocessorContext {
|
||||
/// The location of the book directory on disk.
|
||||
pub root: PathBuf,
|
||||
/// The build options passed from the frontend.
|
||||
pub build_opts: BuildOpts,
|
||||
/// The book configuration (`book.toml`).
|
||||
pub config: Config,
|
||||
/// The `Renderer` this preprocessor is being used with.
|
||||
|
@ -36,9 +39,10 @@ pub struct PreprocessorContext {
|
|||
|
||||
impl PreprocessorContext {
|
||||
/// Create a new `PreprocessorContext`.
|
||||
pub(crate) fn new(root: PathBuf, config: Config, renderer: String) -> Self {
|
||||
pub(crate) fn new(root: PathBuf, build_opts: BuildOpts, config: Config, renderer: String) -> Self {
|
||||
PreprocessorContext {
|
||||
root,
|
||||
build_opts,
|
||||
config,
|
||||
renderer,
|
||||
mdbook_version: crate::MDBOOK_VERSION.to_string(),
|
||||
|
@ -46,6 +50,12 @@ impl PreprocessorContext {
|
|||
__non_exhaustive: (),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the directory containing this book's source files.
|
||||
pub fn source_dir(&self) -> PathBuf {
|
||||
let src = self.config.get_localized_src_path(self.build_opts.language_ident.as_ref()).unwrap();
|
||||
self.root.join(src)
|
||||
}
|
||||
}
|
||||
|
||||
/// An operation which is run immediately after loading a book into memory and
|
||||
|
|
|
@ -460,7 +460,7 @@ impl Renderer for HtmlHandlebars {
|
|||
fn render(&self, ctx: &RenderContext) -> Result<()> {
|
||||
let book_config = &ctx.config.book;
|
||||
let html_config = ctx.config.html_config().unwrap_or_default();
|
||||
let src_dir = ctx.root.join(&ctx.config.book.src);
|
||||
let src_dir = ctx.source_dir();
|
||||
let destination = &ctx.destination;
|
||||
let book = &ctx.book;
|
||||
let build_dir = ctx.root.join(&ctx.config.build.build_dir);
|
||||
|
|
|
@ -26,6 +26,7 @@ use std::process::{Command, Stdio};
|
|||
|
||||
use crate::book::Book;
|
||||
use crate::config::Config;
|
||||
use crate::build_opts::BuildOpts;
|
||||
use crate::errors::*;
|
||||
use toml::Value;
|
||||
|
||||
|
@ -58,6 +59,8 @@ pub struct RenderContext {
|
|||
pub root: PathBuf,
|
||||
/// A loaded representation of the book itself.
|
||||
pub book: Book,
|
||||
/// The build options passed from the frontend.
|
||||
pub build_opts: BuildOpts,
|
||||
/// The loaded configuration file.
|
||||
pub config: Config,
|
||||
/// Where the renderer *must* put any build artefacts generated. To allow
|
||||
|
@ -72,13 +75,14 @@ pub struct RenderContext {
|
|||
|
||||
impl RenderContext {
|
||||
/// Create a new `RenderContext`.
|
||||
pub fn new<P, Q>(root: P, book: Book, config: Config, destination: Q) -> RenderContext
|
||||
pub fn new<P, Q>(root: P, book: Book, build_opts: BuildOpts, config: Config, destination: Q) -> RenderContext
|
||||
where
|
||||
P: Into<PathBuf>,
|
||||
Q: Into<PathBuf>,
|
||||
{
|
||||
RenderContext {
|
||||
book,
|
||||
build_opts,
|
||||
config,
|
||||
version: crate::MDBOOK_VERSION.to_string(),
|
||||
root: root.into(),
|
||||
|
@ -90,7 +94,8 @@ impl RenderContext {
|
|||
|
||||
/// Get the source directory's (absolute) path on disk.
|
||||
pub fn source_dir(&self) -> PathBuf {
|
||||
self.root.join(&self.config.book.src)
|
||||
let src = self.config.get_localized_src_path(self.build_opts.language_ident.as_ref()).unwrap();
|
||||
self.root.join(src)
|
||||
}
|
||||
|
||||
/// Load a `RenderContext` from its JSON representation.
|
||||
|
|
|
@ -2,6 +2,7 @@ mod dummy_book;
|
|||
|
||||
use crate::dummy_book::DummyBook;
|
||||
use mdbook::book::Book;
|
||||
use mdbook::build_opts::BuildOpts;
|
||||
use mdbook::config::Config;
|
||||
use mdbook::errors::*;
|
||||
use mdbook::preprocess::{Preprocessor, PreprocessorContext};
|
||||
|
@ -48,8 +49,9 @@ fn mdbook_runs_preprocessors() {
|
|||
|
||||
let temp = DummyBook::new().build().unwrap();
|
||||
let cfg = Config::default();
|
||||
let build_opts = BuildOpts::default();
|
||||
|
||||
let mut book = MDBook::load_with_config(temp.path(), cfg).unwrap();
|
||||
let mut book = MDBook::load_with_config(temp.path(), cfg, build_opts).unwrap();
|
||||
book.with_preprocessor(Spy(Arc::clone(&spy)));
|
||||
book.build().unwrap();
|
||||
|
||||
|
@ -68,8 +70,9 @@ fn mdbook_runs_renderers() {
|
|||
|
||||
let temp = DummyBook::new().build().unwrap();
|
||||
let cfg = Config::default();
|
||||
let build_opts = BuildOpts::default();
|
||||
|
||||
let mut book = MDBook::load_with_config(temp.path(), cfg).unwrap();
|
||||
let mut book = MDBook::load_with_config(temp.path(), cfg, build_opts).unwrap();
|
||||
book.with_renderer(Spy(Arc::clone(&spy)));
|
||||
book.build().unwrap();
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ use crate::dummy_book::{assert_contains_strings, assert_doesnt_contain_strings,
|
|||
|
||||
use anyhow::Context;
|
||||
use mdbook::config::Config;
|
||||
use mdbook::build_opts::BuildOpts;
|
||||
use mdbook::errors::*;
|
||||
use mdbook::utils::fs::write_file;
|
||||
use mdbook::MDBook;
|
||||
|
@ -326,8 +327,9 @@ fn failure_on_missing_file() {
|
|||
|
||||
let mut cfg = Config::default();
|
||||
cfg.build.create_missing = false;
|
||||
let build_opts = BuildOpts::default();
|
||||
|
||||
let got = MDBook::load_with_config(temp.path(), cfg);
|
||||
let got = MDBook::load_with_config(temp.path(), cfg, build_opts);
|
||||
assert!(got.is_err());
|
||||
}
|
||||
|
||||
|
@ -339,9 +341,10 @@ fn create_missing_file_with_config() {
|
|||
|
||||
let mut cfg = Config::default();
|
||||
cfg.build.create_missing = true;
|
||||
let build_opts = BuildOpts::default();
|
||||
|
||||
assert!(!temp.path().join("src").join("intro.md").exists());
|
||||
let _md = MDBook::load_with_config(temp.path(), cfg).unwrap();
|
||||
let _md = MDBook::load_with_config(temp.path(), cfg, build_opts).unwrap();
|
||||
assert!(temp.path().join("src").join("intro.md").exists());
|
||||
}
|
||||
|
||||
|
@ -429,7 +432,8 @@ fn by_default_mdbook_use_index_preprocessor_to_convert_readme_to_index() {
|
|||
let mut cfg = Config::default();
|
||||
cfg.set("book.src", "src2")
|
||||
.expect("Couldn't set config.book.src to \"src2\".");
|
||||
let md = MDBook::load_with_config(temp.path(), cfg).unwrap();
|
||||
let build_opts = BuildOpts::default();
|
||||
let md = MDBook::load_with_config(temp.path(), cfg, build_opts).unwrap();
|
||||
md.build().unwrap();
|
||||
|
||||
let first_index = temp.path().join("book").join("first").join("index.html");
|
||||
|
|
Loading…
Reference in New Issue