2015-07-07 03:12:24 +08:00
extern crate mdbook ;
2015-08-01 12:59:05 +08:00
#[ macro_use ]
extern crate clap ;
2016-08-14 21:55:10 +08:00
extern crate log ;
extern crate env_logger ;
2017-01-02 01:42:47 +08:00
extern crate open ;
2015-11-09 21:31:00 +08:00
2015-07-07 03:12:24 +08:00
use std ::env ;
2015-08-01 12:59:05 +08:00
use std ::error ::Error ;
2017-01-02 01:42:47 +08:00
use std ::ffi ::OsStr ;
2015-08-01 12:59:05 +08:00
use std ::io ::{ self , Write } ;
use std ::path ::{ Path , PathBuf } ;
2016-03-20 00:45:58 +08:00
use clap ::{ App , ArgMatches , SubCommand , AppSettings } ;
2015-07-07 03:12:24 +08:00
2017-06-26 06:53:58 +08:00
pub mod build ;
2017-06-26 03:41:23 +08:00
#[ cfg(feature = " serve " ) ]
pub mod serve ;
2015-09-27 20:38:37 +08:00
#[ cfg(feature = " watch " ) ]
2017-06-26 05:44:28 +08:00
pub mod watch ;
2015-11-09 21:31:00 +08:00
2015-07-07 08:56:19 +08:00
use mdbook ::MDBook ;
2015-07-07 03:12:24 +08:00
const NAME : & 'static str = " mdbook " ;
fn main ( ) {
2016-08-14 21:55:10 +08:00
env_logger ::init ( ) . unwrap ( ) ;
2015-08-01 12:59:05 +08:00
// Create a list of valid arguments and sub-commands
2017-06-26 06:31:42 +08:00
let app = App ::new ( NAME )
2015-08-01 12:59:05 +08:00
. about ( " Create a book in form of a static website from markdown files " )
. author ( " Mathieu David <mathieudavid@mathieudavid.org> " )
// Get the version from our Cargo.toml using clap's crate_version!() macro
2017-06-26 06:31:42 +08:00
. version ( concat! ( " v " , crate_version! ( ) ) )
2016-03-20 00:45:58 +08:00
. setting ( AppSettings ::SubcommandRequired )
2016-06-12 07:08:48 +08:00
. after_help ( " For more information about a specific command, try `mdbook <command> --help` \n Source code for mdbook available at: https://github.com/azerupi/mdBook " )
2015-08-01 12:59:05 +08:00
. subcommand ( SubCommand ::with_name ( " init " )
. about ( " Create boilerplate structure and files in the directory " )
2015-08-11 22:13:41 +08:00
// the {n} denotes a newline which will properly aligned in all help messages
2017-01-12 20:23:39 +08:00
. arg_from_usage ( " [dir] 'A directory for your book{n}(Defaults to Current Directory when omitted)' " )
2015-08-11 22:42:19 +08:00
. arg_from_usage ( " --theme 'Copies the default theme into your source folder' " )
. arg_from_usage ( " --force 'skip confirmation prompts' " ) )
2017-06-26 06:53:58 +08:00
. subcommand ( build ::make_subcommand ( ) )
2015-12-16 02:55:23 +08:00
. subcommand ( SubCommand ::with_name ( " test " )
2017-06-26 06:31:42 +08:00
. about ( " Test that code samples compile " ) ) ;
#[ cfg(feature = " watch " ) ]
let app = app . subcommand ( watch ::make_subcommand ( ) ) ;
#[ cfg(feature = " serve " ) ]
let app = app . subcommand ( serve ::make_subcommand ( ) ) ;
2015-08-01 12:59:05 +08:00
// Check which subcomamnd the user ran...
2017-06-26 06:31:42 +08:00
let res = match app . get_matches ( ) . subcommand ( ) {
2016-03-18 05:31:28 +08:00
( " init " , Some ( sub_matches ) ) = > init ( sub_matches ) ,
2017-06-26 06:53:58 +08:00
( " build " , Some ( sub_matches ) ) = > build ::build ( sub_matches ) ,
2015-09-27 20:38:37 +08:00
#[ cfg(feature = " watch " ) ]
2017-06-26 05:44:28 +08:00
( " watch " , Some ( sub_matches ) ) = > watch ::watch ( sub_matches ) ,
2016-04-02 10:46:05 +08:00
#[ cfg(feature = " serve " ) ]
2017-06-14 20:43:43 +08:00
( " serve " , Some ( sub_matches ) ) = > serve ::serve ( sub_matches ) ,
2015-12-16 02:55:23 +08:00
( " test " , Some ( sub_matches ) ) = > test ( sub_matches ) ,
2016-03-18 05:31:28 +08:00
( _ , _ ) = > unreachable! ( ) ,
2015-07-07 03:12:24 +08:00
} ;
2015-08-01 12:59:05 +08:00
if let Err ( e ) = res {
2015-08-11 22:13:41 +08:00
writeln! ( & mut io ::stderr ( ) , " An error occured: \n {} " , e ) . ok ( ) ;
2016-08-07 02:54:07 +08:00
::std ::process ::exit ( 101 ) ;
2015-07-07 03:12:24 +08:00
}
}
2015-11-09 21:31:00 +08:00
// Simple function that user comfirmation
2015-08-11 22:42:19 +08:00
fn confirm ( ) -> bool {
io ::stdout ( ) . flush ( ) . unwrap ( ) ;
let mut s = String ::new ( ) ;
io ::stdin ( ) . read_line ( & mut s ) . ok ( ) ;
match & * s . trim ( ) {
" Y " | " y " | " yes " | " Yes " = > true ,
2016-03-18 05:31:28 +08:00
_ = > false ,
2015-08-11 22:42:19 +08:00
}
}
2015-11-09 21:31:00 +08:00
// Init command implementation
2015-08-01 12:59:05 +08:00
fn init ( args : & ArgMatches ) -> Result < ( ) , Box < Error > > {
2015-08-11 22:13:41 +08:00
2015-08-01 12:59:05 +08:00
let book_dir = get_book_dir ( args ) ;
2015-08-30 00:51:23 +08:00
let mut book = MDBook ::new ( & book_dir ) ;
2015-07-07 03:12:24 +08:00
2015-08-11 22:13:41 +08:00
// Call the function that does the initialization
2017-05-19 19:04:37 +08:00
book . init ( ) ? ;
2015-08-11 22:13:41 +08:00
// If flag `--theme` is present, copy theme to src
if args . is_present ( " theme " ) {
2016-02-28 23:28:11 +08:00
// Skip this if `--force` is present
2015-08-11 22:42:19 +08:00
if ! args . is_present ( " force " ) {
// Print warning
2017-05-19 05:52:38 +08:00
print! ( " \n Copying the default theme to {:?} " , book . get_source ( ) ) ;
2015-08-11 22:42:19 +08:00
println! ( " could potentially overwrite files already present in that directory. " ) ;
print! ( " \n Are you sure you want to continue? (y/n) " ) ;
// Read answer from user and exit if it's not 'yes'
if ! confirm ( ) {
2015-08-13 16:46:56 +08:00
println! ( " \n Skipping... \n " ) ;
println! ( " All done, no errors... " ) ;
2015-08-11 22:42:19 +08:00
::std ::process ::exit ( 0 ) ;
}
}
2015-08-11 22:13:41 +08:00
// Call the function that copies the theme
2017-05-19 19:04:37 +08:00
book . copy_theme ( ) ? ;
2015-08-11 22:42:19 +08:00
println! ( " \n Theme copied. " ) ;
2015-08-11 22:13:41 +08:00
}
2016-03-07 16:52:19 +08:00
// Because of `src/book/mdbook.rs#L37-L39`, `dest` will always start with `root`
2017-05-19 05:52:38 +08:00
let is_dest_inside_root = book . get_destination ( )
. map ( | p | p . starts_with ( book . get_root ( ) ) )
. unwrap_or ( false ) ;
2016-02-28 23:28:11 +08:00
if ! args . is_present ( " force " ) & & is_dest_inside_root {
2016-03-03 02:38:39 +08:00
println! ( " \n Do you want a .gitignore to be created? (y/n) " ) ;
2016-02-28 23:28:11 +08:00
if confirm ( ) {
book . create_gitignore ( ) ;
println! ( " \n .gitignore created. " ) ;
2016-03-18 05:31:28 +08:00
}
2016-02-28 23:28:11 +08:00
}
2015-08-13 16:46:56 +08:00
println! ( " \n All done, no errors... " ) ;
2015-08-11 22:13:41 +08:00
Ok ( ( ) )
2015-07-07 03:12:24 +08:00
}
2015-12-16 02:55:23 +08:00
fn test ( args : & ArgMatches ) -> Result < ( ) , Box < Error > > {
let book_dir = get_book_dir ( args ) ;
2017-05-19 05:52:38 +08:00
let mut book = MDBook ::new ( & book_dir ) . read_config ( ) ? ;
2015-12-16 02:55:23 +08:00
2017-05-19 19:04:37 +08:00
book . test ( ) ? ;
2015-12-16 02:55:23 +08:00
2015-09-27 20:38:37 +08:00
Ok ( ( ) )
}
2015-08-01 12:59:05 +08:00
fn get_book_dir ( args : & ArgMatches ) -> PathBuf {
if let Some ( dir ) = args . value_of ( " dir " ) {
// Check if path is relative from current dir, or absolute...
let p = Path ::new ( dir ) ;
if p . is_relative ( ) {
2016-03-18 05:31:28 +08:00
env ::current_dir ( ) . unwrap ( ) . join ( dir )
2015-08-01 12:59:05 +08:00
} else {
2016-03-18 05:31:28 +08:00
p . to_path_buf ( )
2015-08-01 12:59:05 +08:00
}
2015-07-18 06:04:20 +08:00
} else {
2015-08-01 12:59:05 +08:00
env ::current_dir ( ) . unwrap ( )
2015-07-07 03:12:24 +08:00
}
}
2016-04-02 10:46:05 +08:00
2017-01-02 01:42:47 +08:00
fn open < P : AsRef < OsStr > > ( path : P ) {
if let Err ( e ) = open ::that ( path ) {
println! ( " Error opening web browser: {} " , e ) ;
}
}