1use super::MDBook;
4use anyhow::{Context, Result};
5use mdbook_core::config::Config;
6use mdbook_core::utils::fs;
7use mdbook_html::theme::Theme;
8use std::path::PathBuf;
9use tracing::{debug, error, info, trace};
10
11#[derive(Debug, Clone, PartialEq)]
13pub struct BookBuilder {
14 root: PathBuf,
15 create_gitignore: bool,
16 config: Config,
17 copy_theme: bool,
18}
19
20impl BookBuilder {
21 pub fn new<P: Into<PathBuf>>(root: P) -> BookBuilder {
24 BookBuilder {
25 root: root.into(),
26 create_gitignore: false,
27 config: Config::default(),
28 copy_theme: false,
29 }
30 }
31
32 pub fn with_config(&mut self, cfg: Config) -> &mut BookBuilder {
34 self.config = cfg;
35 self
36 }
37
38 pub fn config(&self) -> &Config {
40 &self.config
41 }
42
43 pub fn copy_theme(&mut self, copy: bool) -> &mut BookBuilder {
46 self.copy_theme = copy;
47 self
48 }
49
50 pub fn create_gitignore(&mut self, create: bool) -> &mut BookBuilder {
52 self.create_gitignore = create;
53 self
54 }
55
56 pub fn build(&self) -> Result<MDBook> {
65 info!("Creating a new book with stub content");
66
67 self.create_directory_structure()
68 .with_context(|| "Unable to create directory structure")?;
69
70 self.create_stub_files()
71 .with_context(|| "Unable to create stub files")?;
72
73 if self.create_gitignore {
74 self.build_gitignore()
75 .with_context(|| "Unable to create .gitignore")?;
76 }
77
78 if self.copy_theme {
79 self.copy_across_theme()
80 .with_context(|| "Unable to copy across the theme")?;
81 }
82
83 self.write_book_toml()?;
84
85 match MDBook::load(&self.root) {
86 Ok(book) => Ok(book),
87 Err(e) => {
88 error!("{}", e);
89
90 panic!(
91 "The BookBuilder should always create a valid book. If you are seeing this it \
92 is a bug and should be reported."
93 );
94 }
95 }
96 }
97
98 fn write_book_toml(&self) -> Result<()> {
99 debug!("Writing book.toml");
100 let book_toml = self.root.join("book.toml");
101 let cfg =
102 toml::to_string(&self.config).with_context(|| "Unable to serialize the config")?;
103
104 fs::write(&book_toml, cfg)?;
105 Ok(())
106 }
107
108 fn copy_across_theme(&self) -> Result<()> {
109 debug!("Copying theme");
110
111 let html_config = self.config.html_config().unwrap_or_default();
112 Theme::copy_theme(&html_config, &self.root)?;
113 Ok(())
114 }
115
116 fn build_gitignore(&self) -> Result<()> {
117 fs::write(
118 self.root.join(".gitignore"),
119 format!("{}", self.config.build.build_dir.display()),
120 )?;
121 Ok(())
122 }
123
124 fn create_stub_files(&self) -> Result<()> {
125 debug!("Creating example book contents");
126 let src_dir = self.root.join(&self.config.book.src);
127
128 let summary = src_dir.join("SUMMARY.md");
129 if !summary.exists() {
130 trace!("No summary found creating stub summary and chapter_1.md.");
131 fs::write(
132 summary,
133 "# Summary\n\
134 \n\
135 - [Chapter 1](./chapter_1.md)\n",
136 )?;
137
138 fs::write(src_dir.join("chapter_1.md"), "# Chapter 1\n")?;
139 } else {
140 trace!("Existing summary found, no need to create stub files.");
141 }
142 Ok(())
143 }
144
145 fn create_directory_structure(&self) -> Result<()> {
146 debug!("Creating directory tree");
147 fs::create_dir_all(&self.root)?;
148
149 let src = self.root.join(&self.config.book.src);
150 fs::create_dir_all(src)?;
151
152 let build = self.root.join(&self.config.build.build_dir);
153 fs::create_dir_all(build)?;
154
155 Ok(())
156 }
157}