diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2015-01-08 09:21:57 -0800 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2015-01-08 09:21:57 -0800 |
| commit | cdc75bc34c3a281bf0c49c3a8a31bbe5f8d229d6 (patch) | |
| tree | adede39989a0cf04520bfc90a0b54cefdbfd5e18 /src/rustbook | |
| parent | 2f99a41fe1a27a48e96bc2616ec9faa6de924386 (diff) | |
| parent | 16a6ebd1f60871464c731306aa9007aab30f0dbf (diff) | |
| download | rust-cdc75bc34c3a281bf0c49c3a8a31bbe5f8d229d6.tar.gz rust-cdc75bc34c3a281bf0c49c3a8a31bbe5f8d229d6.zip | |
rollup merge of #19897: steveklabnik/trpl
An updated version of https://github.com/rust-lang/rust/pull/19461 This version vendors aturon/rust-book@731f7bf and builds it when building the docs. This is almost great, except my `make`-foo is poor, so I have my own personal paths in `mk/docs.mk`. How should I best get around that? /cc @brson
Diffstat (limited to 'src/rustbook')
| -rw-r--r-- | src/rustbook/book.rs | 166 | ||||
| -rwxr-xr-x | src/rustbook/build.rs | 191 | ||||
| -rw-r--r-- | src/rustbook/css.rs | 72 | ||||
| -rw-r--r-- | src/rustbook/error.rs | 76 | ||||
| -rw-r--r-- | src/rustbook/help.rs | 46 | ||||
| -rwxr-xr-x | src/rustbook/main.rs | 74 | ||||
| -rw-r--r-- | src/rustbook/serve.rs | 36 | ||||
| -rw-r--r-- | src/rustbook/subcommand.rs | 44 | ||||
| -rw-r--r-- | src/rustbook/term.rs | 31 | ||||
| -rw-r--r-- | src/rustbook/test.rs | 75 |
10 files changed, 811 insertions, 0 deletions
diff --git a/src/rustbook/book.rs b/src/rustbook/book.rs new file mode 100644 index 00000000000..45a864e3378 --- /dev/null +++ b/src/rustbook/book.rs @@ -0,0 +1,166 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Basic data structures for representing a book. + +use std::io::BufferedReader; +use std::iter; +use std::iter::AdditiveIterator; +use regex::Regex; + +pub struct BookItem { + pub title: String, + pub path: Path, + pub path_to_root: Path, + pub children: Vec<BookItem>, +} + +pub struct Book { + pub chapters: Vec<BookItem>, +} + +/// A depth-first iterator over a book. +pub struct BookItems<'a> { + cur_items: &'a [BookItem], + cur_idx: uint, + stack: Vec<(&'a [BookItem], uint)>, +} + +impl<'a> Iterator for BookItems<'a> { + type Item = (String, &'a BookItem); + + fn next(&mut self) -> Option<(String, &'a BookItem)> { + loop { + if self.cur_idx >= self.cur_items.len() { + match self.stack.pop() { + None => return None, + Some((parent_items, parent_idx)) => { + self.cur_items = parent_items; + self.cur_idx = parent_idx + 1; + } + } + } else { + let cur = self.cur_items.get(self.cur_idx).unwrap(); + + let mut section = "".to_string(); + for &(_, idx) in self.stack.iter() { + section.push_str(&(idx + 1).to_string()[]); + section.push('.'); + } + section.push_str(&(self.cur_idx + 1).to_string()[]); + section.push('.'); + + self.stack.push((self.cur_items, self.cur_idx)); + self.cur_items = &cur.children[]; + self.cur_idx = 0; + return Some((section, cur)) + } + } + } +} + +impl Book { + pub fn iter(&self) -> BookItems { + BookItems { + cur_items: &self.chapters[], + cur_idx: 0, + stack: Vec::new(), + } + } +} + +/// Construct a book by parsing a summary (markdown table of contents). +pub fn parse_summary<R: Reader>(input: R, src: &Path) -> Result<Book, Vec<String>> { + fn collapse(stack: &mut Vec<BookItem>, + top_items: &mut Vec<BookItem>, + to_level: uint) { + loop { + if stack.len() < to_level { return } + if stack.len() == 1 { + top_items.push(stack.pop().unwrap()); + return; + } + + let tip = stack.pop().unwrap(); + let last = stack.len() - 1; + stack[last].children.push(tip); + } + } + + let regex = r"(?P<indent>[\t ]*)\*[:space:]*\[(?P<title>.*)\]\((?P<path>.*)\)"; + let item_re = Regex::new(regex).unwrap(); + let mut top_items = vec!(); + let mut stack = vec!(); + let mut errors = vec!(); + + // always include the introduction + top_items.push(BookItem { + title: "Introduction".to_string(), + path: Path::new("README.md"), + path_to_root: Path::new("."), + children: vec!(), + }); + + for line_result in BufferedReader::new(input).lines() { + let line = match line_result { + Ok(line) => line, + Err(err) => { + errors.push(err.desc.to_string()); // FIXME: include detail + return Err(errors); + } + }; + + item_re.captures(&line[]).map(|cap| { + let given_path = cap.name("path"); + let title = cap.name("title").unwrap().to_string(); + + let path_from_root = match src.join(given_path.unwrap()).path_relative_from(src) { + Some(p) => p, + None => { + errors.push(format!("Paths in SUMMARY.md must be relative, \ + but path '{}' for section '{}' is not.", + given_path.unwrap(), title)); + Path::new("") + } + }; + let path_to_root = Path::new(iter::repeat("../") + .take(path_from_root.components().count() - 1) + .collect::<String>()); + let item = BookItem { + title: title, + path: path_from_root, + path_to_root: path_to_root, + children: vec!(), + }; + let level = cap.name("indent").unwrap().chars().map(|c| { + match c { + ' ' => 1u, + '\t' => 4, + _ => unreachable!() + } + }).sum() / 4 + 1; + + if level > stack.len() + 1 { + // FIXME: better error message + errors.push(format!("Section '{}' is indented too many levels.", item.title)); + } else if level <= stack.len() { + collapse(&mut stack, &mut top_items, level); + } + stack.push(item) + }); + } + + if errors.is_empty() { + collapse(&mut stack, &mut top_items, 1); + Ok(Book { chapters: top_items }) + } else { + Err(errors) + } +} diff --git a/src/rustbook/build.rs b/src/rustbook/build.rs new file mode 100755 index 00000000000..db79e0b45e0 --- /dev/null +++ b/src/rustbook/build.rs @@ -0,0 +1,191 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation of the `build` subcommand, used to compile a book. + +use std::os; +use std::io; +use std::io::{fs, File, BufferedWriter, TempDir, IoResult}; + +use subcommand::Subcommand; +use term::Term; +use error::{Error, CliResult, CommandResult}; +use book; +use book::{Book, BookItem}; +use css; + +use regex::Regex; + +use rustdoc; + +struct Build; + +pub fn parse_cmd(name: &str) -> Option<Box<Subcommand>> { + if name == "build" { + Some(box Build as Box<Subcommand>) + } else { + None + } +} + +fn write_toc(book: &Book, path_to_root: &Path, out: &mut Writer) -> IoResult<()> { + fn walk_items(items: &[BookItem], + section: &str, + path_to_root: &Path, + out: &mut Writer) -> IoResult<()> { + for (i, item) in items.iter().enumerate() { + try!(walk_item(item, &format!("{}{}.", section, i + 1)[], path_to_root, out)); + } + Ok(()) + } + fn walk_item(item: &BookItem, + section: &str, + path_to_root: &Path, + out: &mut Writer) -> IoResult<()> { + try!(writeln!(out, "<li><a href='{}'><b>{}</b> {}</a>", + path_to_root.join(item.path.with_extension("html")).display(), + section, + item.title)); + if !item.children.is_empty() { + try!(writeln!(out, "<ul class='section'>")); + let _ = walk_items(&item.children[], section, path_to_root, out); + try!(writeln!(out, "</ul>")); + } + try!(writeln!(out, "</li>")); + + Ok(()) + } + + try!(writeln!(out, "<div id='toc'>")); + try!(writeln!(out, "<ul class='chapter'>")); + try!(walk_items(&book.chapters[], "", path_to_root, out)); + try!(writeln!(out, "</ul>")); + try!(writeln!(out, "</div>")); + + Ok(()) +} + +fn render(book: &Book, tgt: &Path) -> CliResult<()> { + let tmp = TempDir::new("rust-book") + .ok() + // FIXME: lift to Result instead + .expect("could not create temporary directory"); + + for (section, item) in book.iter() { + println!("{} {}", section, item.title); + + let out_path = tgt.join(item.path.dirname()); + + let regex = r"\[(?P<title>[^]]*)\]\((?P<url_stem>[^)]*)\.(?P<ext>md|markdown)\)"; + let md_urls = Regex::new(regex).unwrap(); + + let src; + if os::args().len() < 3 { + src = os::getcwd().unwrap().clone(); + } else { + src = Path::new(os::args()[2].clone()); + } + // preprocess the markdown, rerouting markdown references to html references + let markdown_data = try!(File::open(&src.join(&item.path)).read_to_string()); + let preprocessed_path = tmp.path().join(item.path.filename().unwrap()); + { + let urls = md_urls.replace_all(&markdown_data[], "[$title]($url_stem.html)"); + try!(File::create(&preprocessed_path) + .write_str(&urls[])); + } + + // write the prelude to a temporary HTML file for rustdoc inclusion + let prelude = tmp.path().join("prelude.html"); + { + let mut toc = BufferedWriter::new(try!(File::create(&prelude))); + let _ = write_toc(book, &item.path_to_root, &mut toc); + try!(writeln!(&mut toc, "<div id='page-wrapper'>")); + try!(writeln!(&mut toc, "<div id='page'>")); + } + + // write the postlude to a temporary HTML file for rustdoc inclusion + let postlude = tmp.path().join("postlude.html"); + { + let mut toc = BufferedWriter::new(try!(File::create(&postlude))); + try!(writeln!(&mut toc, "</div></div>")); + } + + try!(fs::mkdir_recursive(&out_path, io::USER_DIR)); + + let rustdoc_args: &[String] = &[ + "".to_string(), + preprocessed_path.display().to_string(), + format!("-o{}", out_path.display()), + format!("--html-before-content={}", prelude.display()), + format!("--html-after-content={}", postlude.display()), + format!("--markdown-css={}", item.path_to_root.join("rust-book.css").display()), + "--markdown-no-toc".to_string(), + ]; + let output_result = rustdoc::main_args(rustdoc_args); + if output_result != 0 { + + let message = format!("Could not execute `rustdoc`: {}", output_result); + return Err(box message as Box<Error>); + } + } + + // create index.html from the root README + try!(fs::copy(&tgt.join("README.html"), &tgt.join("index.html"))); + Ok(()) +} + +impl Subcommand for Build { + fn parse_args(&mut self, _: &[String]) -> CliResult<()> { + Ok(()) + } + fn usage(&self) {} + fn execute(&mut self, term: &mut Term) -> CommandResult<()> { + let cwd = os::getcwd().unwrap(); + let src; + let tgt; + + if os::args().len() < 3 { + src = cwd.clone(); + } else { + src = Path::new(os::args()[2].clone()); + } + + if os::args().len() < 4 { + tgt = cwd.join("_book"); + } else { + tgt = Path::new(os::args()[3].clone()); + } + + let _ = fs::mkdir(&tgt, io::USER_DIR); // FIXME: handle errors + + // FIXME: handle errors + let _ = File::create(&tgt.join("rust-book.css")).write_str(css::STYLE); + + let summary = File::open(&src.join("SUMMARY.md")); + match book::parse_summary(summary, &src) { + Ok(book) => { + // execute rustdoc on the whole book + let _ = render(&book, &tgt).map_err(|err| { + term.err(&format!("error: {}", err.description())[]); + err.detail().map(|detail| { + term.err(&format!("detail: {}", detail)[]); + }) + }); + } + Err(errors) => { + for err in errors.into_iter() { + term.err(&err[]); + } + } + } + + Ok(()) // lol + } +} diff --git a/src/rustbook/css.rs b/src/rustbook/css.rs new file mode 100644 index 00000000000..8eb66e71d3a --- /dev/null +++ b/src/rustbook/css.rs @@ -0,0 +1,72 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// The rust-book CSS in string form. + +pub static STYLE: &'static str = r#" +@import url("//static.rust-lang.org/doc/master/rust.css"); + +body { + max-width:none; +} + +#toc { + position: absolute; + left: 0px; + top: 0px; + bottom: 0px; + width: 250px; + overflow-y: auto; + border-right: 1px solid rgba(0, 0, 0, 0.07); + padding: 10px 10px; + font-size: 16px; + background: none repeat scroll 0% 0% #FFF; + box-sizing: border-box; +} + +#page-wrapper { + position: absolute; + overflow-y: auto; + left: 260px; + right: 0px; + top: 0px; + bottom: 0px; + box-sizing: border-box; + background: none repeat scroll 0% 0% #FFF; +} + +#page { + margin-left: auto; + margin-right:auto; + width: 750px; +} + +.chapter { + list-style: none outside none; + padding-left: 0px; + line-height: 30px; +} + +.section { + list-style: none outside none; + padding-left: 20px; + line-height: 30px; +} + +.section li { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} + +.chapter li a { + color: #000000; +} +"#; diff --git a/src/rustbook/error.rs b/src/rustbook/error.rs new file mode 100644 index 00000000000..1d3baef8c1c --- /dev/null +++ b/src/rustbook/error.rs @@ -0,0 +1,76 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Error handling utilities. WIP. + +use std::fmt; +use std::fmt::{Show, Formatter}; + +use std::io::IoError; + +pub type CliError = Box<Error + 'static>; +pub type CliResult<T> = Result<T, CliError>; + +pub type CommandError = Box<Error + 'static>; +pub type CommandResult<T> = Result<T, CommandError>; + +pub trait Error { + fn description(&self) -> &str; + + fn detail(&self) -> Option<&str> { None } + fn cause(&self) -> Option<&Error> { None } +} + +pub trait FromError<E> { + fn from_err(err: E) -> Self; +} + +impl Show for Box<Error + 'static> { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}", self.description()) + } +} + +impl<E: Error + 'static> FromError<E> for Box<Error + 'static> { + fn from_err(err: E) -> Box<Error + 'static> { + box err as Box<Error> + } +} + +impl<'a> Error for &'a str { + fn description<'b>(&'b self) -> &'b str { + *self + } +} + +impl Error for String { + fn description<'a>(&'a self) -> &'a str { + &self[] + } +} + +impl FromError<()> for () { + fn from_err(_: ()) -> () { () } +} + +impl FromError<IoError> for IoError { + fn from_err(error: IoError) -> IoError { error } +} + +impl Error for IoError { + fn description(&self) -> &str { + self.desc + } + fn detail(&self) -> Option<&str> { + self.detail.as_ref().map(|s| &s[]) + } +} + +//fn iter_map_err<T, U, E, I: Iterator<Result<T,E>>>(iter: I, diff --git a/src/rustbook/help.rs b/src/rustbook/help.rs new file mode 100644 index 00000000000..7fd8214f731 --- /dev/null +++ b/src/rustbook/help.rs @@ -0,0 +1,46 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation of the `help` subcommand. Currently just prints basic usage info. + +use subcommand::Subcommand; +use error::CliResult; +use error::CommandResult; +use term::Term; + +struct Help; + +pub fn parse_cmd(name: &str) -> Option<Box<Subcommand>> { + match name { + "help" | "--help" | "-h" | "-?" => Some(box Help as Box<Subcommand>), + _ => None + } +} + +impl Subcommand for Help { + fn parse_args(&mut self, _: &[String]) -> CliResult<()> { + Ok(()) + } + fn usage(&self) {} + fn execute(&mut self, _: &mut Term) -> CommandResult<()> { + usage(); + Ok(()) + } +} + +pub fn usage() { + println!("Usage: rust-book <command> [<args>]"); + println!(""); + println!("The <command> must be one of:"); + println!(" help Print this message."); + println!(" build Build the book in subdirectory _book"); + println!(" serve --NOT YET IMPLEMENTED--"); + println!(" test --NOT YET IMPLEMENTED--"); +} diff --git a/src/rustbook/main.rs b/src/rustbook/main.rs new file mode 100755 index 00000000000..acb4edb7a45 --- /dev/null +++ b/src/rustbook/main.rs @@ -0,0 +1,74 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(slicing_syntax, box_syntax)] + +extern crate regex; + +extern crate rustdoc; + +use std::os; +use subcommand::Subcommand; +use term::Term; + +macro_rules! try ( + ($expr:expr) => ({ + use error; + match $expr { + Ok(val) => val, + Err(err) => return Err(error::FromError::from_err(err)) + } + }) +); + +mod term; +mod error; +mod book; + +mod subcommand; +mod help; +mod build; +mod serve; +mod test; + +mod css; + +#[cfg(not(test))] // thanks #12327 +fn main() { + let mut term = Term::new(); + let cmd = os::args(); + + if cmd.len() < 1 { + help::usage() + } else { + match subcommand::parse_name(&cmd[1][]) { + Some(mut subcmd) => { + match subcmd.parse_args(cmd.tail()) { + Ok(_) => { + match subcmd.execute(&mut term) { + Ok(_) => (), + Err(_) => os::set_exit_status(-1), + } + } + Err(err) => { + println!("{}", err.description()); + println!(""); + subcmd.usage(); + } + } + } + None => { + println!("Unrecognized command '{}'.", cmd[1]); + println!(""); + help::usage(); + } + } + } +} diff --git a/src/rustbook/serve.rs b/src/rustbook/serve.rs new file mode 100644 index 00000000000..808527dcef9 --- /dev/null +++ b/src/rustbook/serve.rs @@ -0,0 +1,36 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation of the `serve` subcommand. Just a stub for now. + +use subcommand::Subcommand; +use error::CliResult; +use error::CommandResult; +use term::Term; + +struct Serve; + +pub fn parse_cmd(name: &str) -> Option<Box<Subcommand>> { + if name == "serve" { + Some(box Serve as Box<Subcommand>) + } else { + None + } +} + +impl Subcommand for Serve { + fn parse_args(&mut self, _: &[String]) -> CliResult<()> { + Ok(()) + } + fn usage(&self) {} + fn execute(&mut self, _: &mut Term) -> CommandResult<()> { + Ok(()) + } +} diff --git a/src/rustbook/subcommand.rs b/src/rustbook/subcommand.rs new file mode 100644 index 00000000000..473739c919d --- /dev/null +++ b/src/rustbook/subcommand.rs @@ -0,0 +1,44 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Common API for all rust-book subcommands. + +use error::CliResult; +use error::CommandResult; +use term::Term; + +use help; +use build; +use serve; +use test; + +pub trait Subcommand { + /// Mutate the subcommand by parsing its arguments. + /// + /// Returns `Err` on a parsing error. + fn parse_args(&mut self, args: &[String]) -> CliResult<()>; + /// Print the CLI usage information. + fn usage(&self); + /// Actually execute the subcommand. + fn execute(&mut self, term: &mut Term) -> CommandResult<()>; +} + +/// Create a Subcommand object based on its name. +pub fn parse_name(name: &str) -> Option<Box<Subcommand>> { + for parser in [ + help::parse_cmd as fn(&str) -> Option<Box<Subcommand>>, + build::parse_cmd as fn(&str) -> Option<Box<Subcommand>>, + serve::parse_cmd as fn(&str) -> Option<Box<Subcommand>>, + test::parse_cmd as fn(&str) -> Option<Box<Subcommand>>].iter() { + let parsed = (*parser)(name); + if parsed.is_some() { return parsed } + } + None +} diff --git a/src/rustbook/term.rs b/src/rustbook/term.rs new file mode 100644 index 00000000000..18306d6ec20 --- /dev/null +++ b/src/rustbook/term.rs @@ -0,0 +1,31 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! An abstraction of the terminal. Eventually, provide color and +//! verbosity support. For now, just a wrapper around stdout/stderr. + +use std::io::stdio; + +pub struct Term { + err: Box<Writer + 'static> +} + +impl Term { + pub fn new() -> Term { + Term { + err: box stdio::stderr() as Box<Writer>, + } + } + + pub fn err(&mut self, msg: &str) { + // swallow any errors + let _ = self.err.write_line(msg); + } +} diff --git a/src/rustbook/test.rs b/src/rustbook/test.rs new file mode 100644 index 00000000000..f2bf92585f7 --- /dev/null +++ b/src/rustbook/test.rs @@ -0,0 +1,75 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation of the `test` subcommand. Just a stub for now. + +use subcommand::Subcommand; +use error::CliResult; +use error::CommandResult; +use error::Error; +use term::Term; +use book; +use std::io::{Command, File}; +use std::os; + +struct Test; + +pub fn parse_cmd(name: &str) -> Option<Box<Subcommand>> { + if name == "test" { + Some(box Test as Box<Subcommand>) + } else { + None + } +} + +impl Subcommand for Test { + fn parse_args(&mut self, _: &[String]) -> CliResult<()> { + Ok(()) + } + fn usage(&self) {} + fn execute(&mut self, term: &mut Term) -> CommandResult<()> { + let cwd = os::getcwd().unwrap(); + let src = cwd.clone(); + + let summary = File::open(&src.join("SUMMARY.md")); + match book::parse_summary(summary, &src) { + Ok(book) => { + for (_, item) in book.iter() { + let output_result = Command::new("rustdoc") + .arg(&item.path) + .arg("--test") + .output(); + match output_result { + Ok(output) => { + if !output.status.success() { + term.err(&format!("{}\n{}", + String::from_utf8_lossy(&output.output[]), + String::from_utf8_lossy(&output.error[]))[]); + return Err(box "Some tests failed." as Box<Error>); + } + + } + Err(e) => { + let message = format!("Could not execute `rustdoc`: {}", e); + return Err(box message as Box<Error>); + } + } + } + } + Err(errors) => { + for err in errors.into_iter() { + term.err(&err[]); + } + return Err(box "There was an error." as Box<Error>); + } + } + Ok(()) // lol + } +} |
