1 // Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Implementation of the `build` subcommand, used to compile a book.
14 use std
::fs
::{self, File}
;
15 use std
::io
::prelude
::*;
16 use std
::io
::{self, BufWriter}
;
17 use std
::path
::{Path, PathBuf}
;
18 use rustc_back
::tempdir
::TempDir
;
20 use subcommand
::Subcommand
;
22 use error
::{err, CliResult, CommandResult}
;
24 use book
::{Book, BookItem}
;
30 pub fn parse_cmd(name
: &str) -> Option
<Box
<Subcommand
>> {
38 fn write_toc(book
: &Book
, current_page
: &BookItem
, out
: &mut Write
) -> io
::Result
<()> {
39 fn walk_items(items
: &[BookItem
],
41 current_page
: &BookItem
,
42 out
: &mut Write
) -> io
::Result
<()> {
43 for (i
, item
) in items
.iter().enumerate() {
44 walk_item(item
, &format
!("{}{}.", section
, i
+ 1)[..], current_page
, out
)?
;
48 fn walk_item(item
: &BookItem
,
50 current_page
: &BookItem
,
51 out
: &mut Write
) -> io
::Result
<()> {
52 let class_string
= if item
.path
== current_page
.path
{
58 writeln
!(out
, "<li><a {} href='{}'><b>{}</b> {}</a>",
60 current_page
.path_to_root
.join(&item
.path
).with_extension("html").display(),
63 if !item
.children
.is_empty() {
64 writeln
!(out
, "<ul class='section'>")?
;
65 let _
= walk_items(&item
.children
[..], section
, current_page
, out
);
66 writeln
!(out
, "</ul>")?
;
68 writeln
!(out
, "</li>")?
;
73 writeln
!(out
, "<div id='toc' class='mobile-hidden'>")?
;
74 writeln
!(out
, "<ul class='chapter'>")?
;
75 walk_items(&book
.chapters
[..], "", ¤t_page
, out
)?
;
76 writeln
!(out
, "</ul>")?
;
77 writeln
!(out
, "</div>")?
;
82 fn render(book
: &Book
, tgt
: &Path
) -> CliResult
<()> {
83 let tmp
= TempDir
::new("rustbook")?
;
85 for (_section
, item
) in book
.iter() {
86 let out_path
= match item
.path
.parent() {
87 Some(p
) => tgt
.join(p
),
88 None
=> tgt
.to_path_buf(),
92 if env
::args().len() < 3 {
93 src
= env
::current_dir().unwrap().clone();
95 src
= PathBuf
::from(&env
::args().nth(2).unwrap());
97 // preprocess the markdown, rerouting markdown references to html
99 let mut markdown_data
= String
::new();
100 File
::open(&src
.join(&item
.path
)).and_then(|mut f
| {
101 f
.read_to_string(&mut markdown_data
)
103 let preprocessed_path
= tmp
.path().join(item
.path
.file_name().unwrap());
105 let urls
= markdown_data
.replace(".md)", ".html)");
106 File
::create(&preprocessed_path
).and_then(|mut f
| {
107 f
.write_all(urls
.as_bytes())
111 // write the prelude to a temporary HTML file for rustdoc inclusion
112 let prelude
= tmp
.path().join("prelude.html");
114 let mut buffer
= BufWriter
::new(File
::create(&prelude
)?
);
115 writeln
!(&mut buffer
, r
#"
117 <button id="toggle-nav">
118 <span class="sr-only">Toggle navigation</span>
119 <span class="bar"></span>
120 <span class="bar"></span>
121 <span class="bar"></span>
124 let _
= write_toc(book
, &item
, &mut buffer
);
125 writeln
!(&mut buffer
, "<div id='page-wrapper'>")?
;
126 writeln
!(&mut buffer
, "<div id='page'>")?
;
129 // write the postlude to a temporary HTML file for rustdoc inclusion
130 let postlude
= tmp
.path().join("postlude.html");
132 let mut buffer
= BufWriter
::new(File
::create(&postlude
)?
);
133 writeln
!(&mut buffer
, "<script src='rustbook.js'></script>")?
;
134 writeln
!(&mut buffer
, "<script src='playpen.js'></script>")?
;
135 writeln
!(&mut buffer
, "</div></div>")?
;
138 fs
::create_dir_all(&out_path
)?
;
140 let rustdoc_args
: &[String
] = &[
142 preprocessed_path
.display().to_string(),
143 format
!("-o{}", out_path
.display()),
144 format
!("--html-before-content={}", prelude
.display()),
145 format
!("--html-after-content={}", postlude
.display()),
146 format
!("--markdown-playground-url=https://play.rust-lang.org"),
147 format
!("--markdown-css={}", item
.path_to_root
.join("rustbook.css").display()),
148 "--markdown-no-toc".to_string(),
150 let output_result
= rustdoc
::main_args(rustdoc_args
);
151 if output_result
!= 0 {
152 let message
= format
!("Could not execute `rustdoc` with {:?}: {}",
153 rustdoc_args
, output_result
);
154 return Err(err(&message
));
158 // create index.html from the root README
159 fs
::copy(&tgt
.join("README.html"), &tgt
.join("index.html"))?
;
161 // Copy js for playpen
162 let mut playpen
= File
::create(tgt
.join("playpen.js"))?
;
163 let js
= include_bytes
!("../../librustdoc/html/static/playpen.js");
164 playpen
.write_all(js
)?
;
168 impl Subcommand
for Build
{
169 fn parse_args(&mut self, _
: &[String
]) -> CliResult
<()> {
173 fn execute(&mut self, term
: &mut Term
) -> CommandResult
<()> {
174 let cwd
= env
::current_dir().unwrap();
178 if env
::args().len() < 3 {
181 src
= PathBuf
::from(&env
::args().nth(2).unwrap());
184 if env
::args().len() < 4 {
185 tgt
= cwd
.join("_book");
187 tgt
= PathBuf
::from(&env
::args().nth(3).unwrap());
190 // `_book` directory may already exist from previous runs. Check and
191 // delete it if it exists.
192 for entry
in fs
::read_dir(&cwd
)?
{
193 let path
= entry?
.path();
194 if path
== tgt { fs::remove_dir_all(&tgt)? }
196 fs
::create_dir(&tgt
)?
;
199 let css
= include_bytes
!("static/rustbook.css");
200 let js
= include_bytes
!("static/rustbook.js");
202 let mut css_file
= File
::create(tgt
.join("rustbook.css"))?
;
203 css_file
.write_all(css
)?
;
205 let mut js_file
= File
::create(tgt
.join("rustbook.js"))?
;
206 js_file
.write_all(js
)?
;
209 let mut summary
= File
::open(&src
.join("SUMMARY.md"))?
;
210 match book
::parse_summary(&mut summary
, &src
) {
212 // execute rustdoc on the whole book
216 let n
= errors
.len();
218 term
.err(&format
!("error: {}", err
)[..]);
221 Err(err(&format
!("{} errors occurred", n
)))