Skip to content

Separated argument parsing for serve subcommand. #625

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ tempfile = "3.0"
itertools = "0.7"
shlex = "0.1"
toml-query = "0.6"
structopt = "0.2"

# Watch feature
notify = { version = "4.0", optional = true }
Expand Down
33 changes: 15 additions & 18 deletions src/bin/build.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,32 @@
use std::path::PathBuf;
use clap::{App, ArgMatches, SubCommand};
use mdbook::MDBook;
use mdbook::errors::Result;
use {get_book_dir, open};

// Create clap subcommand arguments
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name("build")
.about("Build the book from the markdown files")
.arg_from_usage("-o, --open 'Open the compiled book in a web browser'")
.arg_from_usage(
"-d, --dest-dir=[dest-dir] 'The output directory for your book{n}(Defaults to ./book \
when omitted)'",
)
.arg_from_usage(
"[dir] 'A directory for your book{n}(Defaults to Current Directory when omitted)'",
)
#[derive(StructOpt)]
pub struct BuildArgs {
#[structopt(long = "open", short = "o", help = "Open the compiled book in a web browser")]
open: bool,
#[structopt(help = "A directory for your book{n}(Defaults to Current Directory when omitted)")]
dir: Option<String>,
#[structopt(long = "dest-dir", short = "d",
help = "The output directory for your book{n}(Defaults to ./book when omitted)",
parse(from_os_str))]
dest_dir: Option<PathBuf>,
}

// Build command implementation
pub fn execute(args: &ArgMatches) -> Result<()> {
let book_dir = get_book_dir(args);
pub fn execute(args: BuildArgs) -> Result<()> {
let book_dir = get_book_dir(args.dir);
let mut book = MDBook::load(&book_dir)?;

if let Some(dest_dir) = args.value_of("dest-dir") {
book.config.build.build_dir = PathBuf::from(dest_dir);
if let Some(dest_dir) = args.dest_dir {
book.config.build.build_dir = dest_dir;
}

book.build()?;

if args.is_present("open") {
if args.open {
// FIXME: What's the right behaviour if we don't use the HTML renderer?
open(book.build_dir_for("html").join("index.html"));
}
Expand Down
21 changes: 9 additions & 12 deletions src/bin/clean.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
use std::fs;
use std::path::PathBuf;
use clap::{App, ArgMatches, SubCommand};
use mdbook::MDBook;
use mdbook::errors::*;
use get_book_dir;

// Create clap subcommand arguments
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name("clean")
.about("Delete built book")
.arg_from_usage(
"-d, --dest-dir=[dest-dir] 'The directory of built book{n}(Defaults to ./book when \
omitted)'",
)
#[derive(StructOpt)]
pub struct CleanArgs {
#[structopt(long = "dest-dir", short = "d",
help = "The output directory for your book{n}(Defaults to ./book when omitted)",
parse(from_os_str))]
dest_dir: Option<PathBuf>,
}

// Clean command implementation
pub fn execute(args: &ArgMatches) -> ::mdbook::errors::Result<()> {
let book_dir = get_book_dir(args);
pub fn execute(args: CleanArgs) -> ::mdbook::errors::Result<()> {
let book_dir = get_book_dir(None);
let book = MDBook::load(&book_dir)?;

let dir_to_remove = match args.value_of("dest-dir") {
let dir_to_remove = match args.dest_dir {
Some(dest_dir) => PathBuf::from(dest_dir),
None => book.root.join(&book.config.build.build_dir),
};
Expand Down
27 changes: 12 additions & 15 deletions src/bin/init.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,31 @@
use std::io;
use std::io::Write;
use std::process::Command;
use clap::{App, ArgMatches, SubCommand};
use std::io::Write;
use mdbook::MDBook;
use mdbook::errors::Result;
use mdbook::config;
use get_book_dir;

// Create clap subcommand arguments
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name("init")
.about("Create boilerplate structure and files in the directory")
// the {n} denotes a newline which will properly aligned in all help messages
.arg_from_usage("[dir] 'A directory for your book{n}(Defaults to Current Directory \
when omitted)'")
.arg_from_usage("--theme 'Copies the default theme into your source folder'")
.arg_from_usage("--force 'skip confirmation prompts'")
#[derive(StructOpt)]
pub struct InitArgs {
#[structopt(long = "theme", help = "Copies the default theme into your source folder")]
pub theme: bool,
#[structopt(long = "force", help = "Skip confirmation prompts")] pub force: bool,
#[structopt(help = "A directory for your book{n}(Defaults to Current Directory when omitted)")]
pub dir: Option<String>,
}

// Init command implementation
pub fn execute(args: &ArgMatches) -> Result<()> {
let book_dir = get_book_dir(args);
pub fn execute(args: InitArgs) -> Result<()> {
let book_dir = get_book_dir(args.dir);
let mut builder = MDBook::init(&book_dir);
let mut config = config::Config::default();

// If flag `--theme` is present, copy theme to src
if args.is_present("theme") {
if args.theme {
config.set("output.html.theme", "src/theme")?;
// Skip this if `--force` is present
if !args.is_present("force") {
if !args.force {
// Print warning
println!();
println!(
Expand Down
72 changes: 37 additions & 35 deletions src/bin/mdbook.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
extern crate chrono;
#[macro_use]
extern crate clap;
extern crate env_logger;
extern crate error_chain;
#[macro_use]
extern crate log;
extern crate mdbook;
extern crate open;
#[macro_use]
extern crate structopt;

use std::env;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::io::Write;
use clap::{App, AppSettings, ArgMatches};
use chrono::Local;
use log::LevelFilter;
use env_logger::Builder;
use mdbook::utils;
use structopt::StructOpt;

pub mod build;
pub mod clean;
Expand All @@ -27,43 +28,20 @@ pub mod serve;
#[cfg(feature = "watch")]
pub mod watch;

const NAME: &'static str = "mdbook";

fn main() {
init_logger();

// Create a list of valid arguments and sub-commands
let app = App::new(NAME)
.about("Create a book in form of a static website from markdown files")
.author("Mathieu David <[email protected]>")
// Get the version from our Cargo.toml using clap's crate_version!() macro
.version(concat!("v",crate_version!()))
.setting(AppSettings::ArgRequiredElseHelp)
.after_help("For more information about a specific command, \
try `mdbook <command> --help`\n\
Source code for mdbook available \
at: https://github.com/rust-lang-nursery/mdBook")
.subcommand(init::make_subcommand())
.subcommand(build::make_subcommand())
.subcommand(test::make_subcommand())
.subcommand(clean::make_subcommand());

#[cfg(feature = "watch")]
let app = app.subcommand(watch::make_subcommand());
#[cfg(feature = "serve")]
let app = app.subcommand(serve::make_subcommand());
let opt = Opts::from_args();

// Check which subcomamnd the user ran...
let res = match app.get_matches().subcommand() {
("init", Some(sub_matches)) => init::execute(sub_matches),
("build", Some(sub_matches)) => build::execute(sub_matches),
("clean", Some(sub_matches)) => clean::execute(sub_matches),
let res = match opt {
Opts::Init(x) => init::execute(x),
Opts::Build(x) => build::execute(x),
Opts::Clean(x) => clean::execute(x),
#[cfg(feature = "watch")]
("watch", Some(sub_matches)) => watch::execute(sub_matches),
Opts::Watch(x) => watch::execute(x),
#[cfg(feature = "serve")]
("serve", Some(sub_matches)) => serve::execute(sub_matches),
("test", Some(sub_matches)) => test::execute(sub_matches),
(_, _) => unreachable!(),
Opts::Serve(x) => serve::execute(x),
Opts::Test(x) => test::execute(x),
};

if let Err(e) = res {
Expand All @@ -73,6 +51,30 @@ fn main() {
}
}

/// Subcommands and their respective parameters.
#[derive(StructOpt)]
#[structopt(about = "Create a book in form of a static website from markdown files",
after_help = "For more information about a specific command, \
try `mdbook <command> --help`\n\
Source code for mdbook available \
at: https://github.com/rust-lang-nursery/mdBook",
author = "Mathieu David <[email protected]>")]
enum Opts {
#[structopt(name = "init", about = "Create boilerplate structure and files in the directory")]
Init(init::InitArgs),
#[structopt(name = "build", about = "Build the book from the markdown files")]
Build(build::BuildArgs),
#[structopt(name = "clean", about = "Delete built book")] Clean(clean::CleanArgs),
#[cfg(feature = "watch")]
#[structopt(name = "watch", about = "Watch the files for changes")]
Watch(watch::WatchArgs),
#[cfg(feature = "serve")]
#[structopt(name = "serve",
about = "Serve the book at http://localhost:3000. Rebuild and reload on change.")]
Serve(serve::ServeArgs),
#[structopt(name = "test", about = "Test that code samples compile")] Test(test::TestArgs),
}

fn init_logger() {
let mut builder = Builder::new();

Expand All @@ -99,8 +101,8 @@ fn init_logger() {
builder.init();
}

fn get_book_dir(args: &ArgMatches) -> PathBuf {
if let Some(dir) = args.value_of("dir") {
fn get_book_dir(book_dir: Option<String>) -> PathBuf {
if let Some(ref dir) = book_dir {
// Check if path is relative from current dir, or absolute...
let p = Path::new(dir);
if p.is_relative() {
Expand Down
Loading