<commit_before>// #[macro_use] extern crate rustler;
// #[macro_use] extern crate lazy_static;

// Disabled for now, but still
// wondering why rustler generated this in the first place
// #[macro_use] extern crate rustler_codegen;

// use rustler::{Env, Term, NifResult, Encoder}; // MON30AUG210852
use rustler::{NifResult}; // MON30AUG210852
extern crate pact_mock_server;
extern crate libc;

// use pact_mock_server::create_mock_server;// as create_mock_server_a;
use pact_mock_server::create_mock_server;
use pact_mock_server::MockServerError; // unused because of the way the fuctions are being implemented.
use pact_mock_server::mock_server_mismatches;
use pact_mock_server::mock_server_matched;
use pact_mock_server::write_pact_file;
use pact_mock_server::WritePactFileErr;
use pact_mock_server::cleanup_mock_server_ffi;
// use pact_mock_server::cleanup_mock_server;
// mod atoms {
//     rustler::atoms! {
//         atom ok;
//         atom error;
//         atom mock_server_failed_to_start;
//         atom invalid_pact_json;
//         atom io_error;
//         atom no_mock_server_running_on_port;
//         //atom __true__ = "true";
//         //atom __false__ = "false";
//     }
// }

mod atoms {
    rustler::atoms! {
        ok,
        error,
        mock_server_failed_to_start,
        invalid_pact_json,
        io_error,
        no_mock_server_running_on_port,
        //atom __true__ = "true";
        //atom __false__ = "false";
    }
}

// rustler_export_nifs! {
//     "Elixir.PactElixir.RustPactMockServerFacade",
//     [
//         ("create_mock_server", 2, create_mock_server_call),
//         ("mock_server_mismatches", 1, mock_server_mismatches_call),
//         ("mock_server_matched", 1, mock_server_matched_call),
//         ("write_pact_file", 2, write_pact_file_call),
//         ("cleanup_mock_server", 1, cleanup_mock_server_call)
//     ],
//     None
// }

rustler::init!(
    "Elixir.PactElixir.RustPactMockServerFacade",
    [
        create_mock_server_call,
        mock_server_mismatches_call,
        mock_server_matched_call,
        write_pact_file_call,
        cleanup_mock_server_call
    ]
);

#[rustler::nif(name = "create_mock_server")]
fn create_mock_server_call(pact_json:&str, port_arg:i32) -> NifResult<i32> {
// fn create_mock_server(args: &[Term]) -> NifResult<i32> {
    // let pact_json: &str = r#try!(args[0].decode());
    // let port_arg: i32 = r#try!(args[1].decode());
    // let pact_json: &str = (args[0].decode())?;
    // let port_arg: i32 = (args[1].decode())?;
    let port = create_mock_server(pact_json, port_arg);
    // port

    match port {
        Ok(port) => {
            Ok(port)
        }
        Err(MockServerError::MockServerFailedToStart) =>
            NifResult::Err( rustler::error::Error::Atom("mock_server_failed_to_start")),
        Err(MockServerError::InvalidPactJson) =>
            NifResult::Err( rustler::error::Error::Atom("invalid_pact_json"))
    }
}

#[rustler::nif(name = "mock_server_mismatches")]
// fn mock_server_mismatches<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
//     let port: i32 = (args[0].decode())?;
//     Ok((atoms::ok(), mock_server_mismatches(port)).encode(env))
// }
fn mock_server_mismatches_call(port:i32) -> Option<String> {
    mock_server_mismatches(port)
}

#[rustler::nif(name = "mock_server_matched")]
// fn mock_server_matched<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
//     let port: i32 = args[0].decode();

//     let matched: bool = mock_server_matched(port);

//     Ok((atoms::ok(), matched).encode(env))
// }
fn mock_server_matched_call(port:i32) -> bool {

    // let matched: bool = mock_server_matched(port);

    // Ok(matched)
    mock_server_matched(port)
}

// fn write_pact_file<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
//     let port: i32 = (args[0].decode())?;
//     let dir_path: String = (args[1].decode())?;

//     match write_pact_file(port, Some(dir_path)) {
//         Ok(_result) =>
//             Ok((atoms::ok()).encode(env)),
//         Err(WritePactFileErr::IOError) =>
//             Ok((atoms::error(), atoms::io_error()).encode(env)),
//         Err(WritePactFileErr::NoMockServer) =>
//             Ok((atoms::error(), atoms::no_mock_server_running_on_port()).encode(env))
//     }
// }
#[rustler::nif(name = "write_pact_file")]
fn write_pact_file_call(port:i32, dir_path:String) -> NifResult<()> {
    match write_pact_file(port, Some(dir_path)) {
        Ok(()) =>
            Ok(()),
        Err(WritePactFileErr::IOError) =>
            NifResult::Err( rustler::error::Error::Atom("io_error")),
        Err(WritePactFileErr::NoMockServer) =>
            NifResult::Err( rustler::error::Error::Atom("no_mock_server_running_on_port"))
    }
}

// fn cleanup_mock_server<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
//     let port: i32 = (args[0].decode())?;

//     Ok((atoms::ok(), cleanup_mock_server_ffi(port)).encode(env))
// }
#[rustler::nif(name = "cleanup_mock_server")]
fn cleanup_mock_server_call(port:i32) -> bool {
    cleanup_mock_server_ffi(port)
}<commit_msg>Change return type for some Rust code.<commit_after>// #[macro_use] extern crate rustler;
// #[macro_use] extern crate lazy_static;

// Disabled for now, but still
// wondering why rustler generated this in the first place
// #[macro_use] extern crate rustler_codegen;

// use rustler::{Env, Term, NifResult, Encoder}; // MON30AUG210852
use rustler::{NifResult}; // MON30AUG210852
use rustler::types::Atom;
extern crate pact_mock_server;
extern crate libc;

// use pact_mock_server::create_mock_server;// as create_mock_server_a;
use pact_mock_server::create_mock_server;
use pact_mock_server::MockServerError; // unused because of the way the fuctions are being implemented.
use pact_mock_server::mock_server_mismatches;
use pact_mock_server::mock_server_matched;
use pact_mock_server::write_pact_file;
use pact_mock_server::WritePactFileErr;
use pact_mock_server::cleanup_mock_server_ffi;
// use pact_mock_server::cleanup_mock_server;
// mod atoms {
//     rustler::atoms! {
//         atom ok;
//         atom error;
//         atom mock_server_failed_to_start;
//         atom invalid_pact_json;
//         atom io_error;
//         atom no_mock_server_running_on_port;
//         //atom __true__ = "true";
//         //atom __false__ = "false";
//     }
// }

mod atoms {
    rustler::atoms! {
        ok,
        error,
        mock_server_failed_to_start,
        invalid_pact_json,
        io_error,
        no_mock_server_running_on_port,
        //atom __true__ = "true";
        //atom __false__ = "false";
    }
}

// rustler_export_nifs! {
//     "Elixir.PactElixir.RustPactMockServerFacade",
//     [
//         ("create_mock_server", 2, create_mock_server_call),
//         ("mock_server_mismatches", 1, mock_server_mismatches_call),
//         ("mock_server_matched", 1, mock_server_matched_call),
//         ("write_pact_file", 2, write_pact_file_call),
//         ("cleanup_mock_server", 1, cleanup_mock_server_call)
//     ],
//     None
// }

rustler::init!(
    "Elixir.PactElixir.RustPactMockServerFacade",
    [
        create_mock_server_call,
        mock_server_mismatches_call,
        mock_server_matched_call,
        write_pact_file_call,
        cleanup_mock_server_call
    ]
);

#[rustler::nif(name = "create_mock_server")]
fn create_mock_server_call(pact_json:&str, port_arg:i32) -> NifResult<(Atom,i32)> {
// fn create_mock_server(args: &[Term]) -> NifResult<i32> {
    // let pact_json: &str = r#try!(args[0].decode());
    // let port_arg: i32 = r#try!(args[1].decode());
    // let pact_json: &str = (args[0].decode())?;
    // let port_arg: i32 = (args[1].decode())?;
    let port = create_mock_server(pact_json, port_arg);
    // port

    match port {
        Ok(port) => {
            Ok((atoms::ok(), port))
        }
        Err(MockServerError::MockServerFailedToStart) =>
            NifResult::Err( rustler::error::Error::Atom("mock_server_failed_to_start")),
        Err(MockServerError::InvalidPactJson) =>
            NifResult::Err( rustler::error::Error::Atom("invalid_pact_json"))
    }
}

#[rustler::nif(name = "mock_server_mismatches")]
// fn mock_server_mismatches<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
//     let port: i32 = (args[0].decode())?;
//     Ok((atoms::ok(), mock_server_mismatches(port)).encode(env))
// }
fn mock_server_mismatches_call(port:i32) -> Option<String> {
    mock_server_mismatches(port)
}

#[rustler::nif(name = "mock_server_matched")]
// fn mock_server_matched<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
//     let port: i32 = args[0].decode();

//     let matched: bool = mock_server_matched(port);

//     Ok((atoms::ok(), matched).encode(env))
// }
fn mock_server_matched_call(port:i32) -> bool {

    // let matched: bool = mock_server_matched(port);

    // Ok(matched)
    mock_server_matched(port)
}

// fn write_pact_file<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
//     let port: i32 = (args[0].decode())?;
//     let dir_path: String = (args[1].decode())?;

//     match write_pact_file(port, Some(dir_path)) {
//         Ok(_result) =>
//             Ok((atoms::ok()).encode(env)),
//         Err(WritePactFileErr::IOError) =>
//             Ok((atoms::error(), atoms::io_error()).encode(env)),
//         Err(WritePactFileErr::NoMockServer) =>
//             Ok((atoms::error(), atoms::no_mock_server_running_on_port()).encode(env))
//     }
// }
#[rustler::nif(name = "write_pact_file")]
fn write_pact_file_call(port:i32, dir_path:String) -> NifResult<Atom> {
    match write_pact_file(port, Some(dir_path)) {
        Ok(()) =>
            Ok(atoms::ok()),
        Err(WritePactFileErr::IOError) =>
            NifResult::Err( rustler::error::Error::Atom("io_error")),
        Err(WritePactFileErr::NoMockServer) =>
            NifResult::Err( rustler::error::Error::Atom("no_mock_server_running_on_port"))
    }
}

// fn cleanup_mock_server<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
//     let port: i32 = (args[0].decode())?;

//     Ok((atoms::ok(), cleanup_mock_server_ffi(port)).encode(env))
// }
#[rustler::nif(name = "cleanup_mock_server")]
fn cleanup_mock_server_call(port:i32) -> bool {
    cleanup_mock_server_ffi(port)
}<|endoftext|>


<commit_before>// Copyright 2013-2015, The Rust-GNOME Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! GdkPixbufLoader — Application-driven progressive image loading.

use ffi;
use glib::{to_bool, GlibContainer};
use glib::translate::ToGlibPtr;
use libc::{c_int, c_uint};

#[repr(C)]
pub struct PixbufLoader {
    pointer: *mut ffi::C_GdkPixbufLoader
}

impl PixbufLoader {
    pub fn new() -> Option<PixbufLoader> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_new() };

        if tmp.is_null() {
            None
        } else {
            Some(PixbufLoader::wrap_pointer(tmp))
        }
    }

    pub fn new_with_type(image_type: &str, error: &mut ::glib::Error) -> Option<PixbufLoader> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_new_with_type(image_type.borrow_to_glib().0, &mut error.unwrap()) };

        if tmp.is_null() {
            None
        } else {
            Some(PixbufLoader::wrap_pointer(tmp))
        }
    }

    pub fn new_with_mime_type(mime_type: &str, error: &mut ::glib::Error) -> Option<PixbufLoader> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_new_with_mime_type(mime_type.borrow_to_glib().0, &mut error.unwrap()) };

        if tmp.is_null() {
            None
        } else {
            Some(PixbufLoader::wrap_pointer(tmp))
        }
    }

    pub fn get_format(&self) -> Option<::PixbufFormat> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_get_format(self.unwrap_pointer()) };

        if tmp.is_null() {
            None
        } else {
            Some(::PixbufFormat::wrap_pointer(tmp))
        }
    }

    pub fn loader_write(&self, buf: &[u8], error: &mut ::glib::Error) -> bool {
        unsafe { to_bool(ffi::gdk_pixbuf_loader_write(self.unwrap_pointer(), buf.as_ptr(), buf.len() as c_uint, &mut error.unwrap())) }
    }

    /*pub fn loader_write_bytes(&self, buffer: &glib::Bytes, error: &mut ::glib::Error) -> bool {
        gdk_pixbuf_loader_write_bytes
    }*/

    pub fn set_size(&self, width: isize, height: isize) {
        unsafe { ffi::gdk_pixbuf_loader_set_size(self.unwrap_pointer(), width as c_int, height as c_int) }
    }

    pub fn get_pixbuf(&self) -> Option<::Pixbuf> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_get_pixbuf(self.unwrap_pointer()) };

        if tmp.is_null() {
            None
        } else {
            Some(::Pixbuf::wrap_pointer(tmp))
        }
    }

    /*pub fn get_animation(&self) -> Option<::PixbufAnimation> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_get_animation(self.unwrap_pointer()) };

        if tmp.is_null() {
            None
        } else {
            Some(::PixbufAnimation::wrap_pointer(tmp))
        }
    }*/

    pub fn close(&self, error: &mut ::glib::Error) -> bool {
        unsafe { to_bool(ffi::gdk_pixbuf_loader_close(self.unwrap_pointer(), &mut error.unwrap())) }
    }
}

impl_GObjectFunctions!(PixbufLoader, C_GdkPixbufLoader);<commit_msg>Fix error handling in `PixbufLoader`<commit_after>// Copyright 2013-2015, The Rust-GNOME Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! GdkPixbufLoader — Application-driven progressive image loading.

use std;
use ffi;
use glib::{to_bool, GlibContainer};
use glib::translate::ToGlibPtr;
use libc::{c_int, c_uint};

#[repr(C)]
pub struct PixbufLoader {
    pointer: *mut ffi::C_GdkPixbufLoader
}

impl PixbufLoader {
    pub fn new() -> Option<PixbufLoader> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_new() };

        if tmp.is_null() {
            None
        } else {
            Some(PixbufLoader::wrap_pointer(tmp))
        }
    }

    pub fn new_with_type(image_type: &str, error: &mut ::glib::Error) -> Option<PixbufLoader> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_new_with_type(image_type.borrow_to_glib().0, &mut error.unwrap()) };

        if tmp.is_null() {
            None
        } else {
            Some(PixbufLoader::wrap_pointer(tmp))
        }
    }

    pub fn new_with_mime_type(mime_type: &str, error: &mut ::glib::Error) -> Option<PixbufLoader> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_new_with_mime_type(mime_type.borrow_to_glib().0, &mut error.unwrap()) };

        if tmp.is_null() {
            None
        } else {
            Some(PixbufLoader::wrap_pointer(tmp))
        }
    }

    pub fn get_format(&self) -> Option<::PixbufFormat> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_get_format(self.unwrap_pointer()) };

        if tmp.is_null() {
            None
        } else {
            Some(::PixbufFormat::wrap_pointer(tmp))
        }
    }

    pub fn loader_write(&self, buf: &[u8]) -> Result<(), ::glib::Error> {
        unsafe {
            let mut error: *mut ::glib::ffi::C_GError = std::ptr::null_mut();
            match to_bool(ffi::gdk_pixbuf_loader_write(self.unwrap_pointer(), buf.as_ptr(), buf.len() as c_uint, &mut error)) {
                true => Ok(()),
                false => Err(::glib::Error::wrap(error))
            }
        }
    }

    /*pub fn loader_write_bytes(&self, buffer: &glib::Bytes, error: &mut ::glib::Error) -> bool {
        gdk_pixbuf_loader_write_bytes
    }*/

    pub fn set_size(&self, width: isize, height: isize) {
        unsafe { ffi::gdk_pixbuf_loader_set_size(self.unwrap_pointer(), width as c_int, height as c_int) }
    }

    pub fn get_pixbuf(&self) -> Option<::Pixbuf> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_get_pixbuf(self.unwrap_pointer()) };

        if tmp.is_null() {
            None
        } else {
            Some(::Pixbuf::wrap_pointer(tmp))
        }
    }

    /*pub fn get_animation(&self) -> Option<::PixbufAnimation> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_get_animation(self.unwrap_pointer()) };

        if tmp.is_null() {
            None
        } else {
            Some(::PixbufAnimation::wrap_pointer(tmp))
        }
    }*/

    pub fn close(&self) -> Result<(), ::glib::Error> {
        unsafe {
            let mut error: *mut ::glib::ffi::C_GError = std::ptr::null_mut();
            match to_bool(ffi::gdk_pixbuf_loader_close(self.unwrap_pointer(), &mut error)) {
                true => Ok(()),
                false => Err(::glib::Error::wrap(error))
            }
        }
    }
}

impl_GObjectFunctions!(PixbufLoader, C_GdkPixbufLoader);
<|endoftext|>


<commit_before>// Copyright 2015 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.

#![cfg(not(test))]

extern crate env_logger;
extern crate failure;
extern crate getopts;
extern crate rustfmt_nightly as rustfmt;

use std::env;
use std::fs::File;
use std::io::{self, stdout, Read, Write};
use std::path::PathBuf;

use failure::err_msg;

use getopts::{Matches, Options};

use rustfmt::{emit_post_matter, emit_pre_matter, load_config, CliOptions, Config, FmtResult,
              WriteMode, WRITE_MODE_LIST};
use rustfmt::{format_and_emit_report, FileName, Input, Summary};

fn main() {
    env_logger::init();
    let opts = make_opts();

    let exit_code = match execute(&opts) {
        Ok((write_mode, summary)) => {
            if summary.has_operational_errors() || summary.has_parsing_errors()
                || (summary.has_diff && write_mode == WriteMode::Check)
            {
                1
            } else {
                0
            }
        }
        Err(e) => {
            eprintln!("{}", e.to_string());
            1
        }
    };
    // Make sure standard output is flushed before we exit.
    std::io::stdout().flush().unwrap();

    // Exit with given exit code.
    //
    // NOTE: This immediately terminates the process without doing any cleanup,
    // so make sure to finish all necessary cleanup before this is called.
    std::process::exit(exit_code);
}

/// Rustfmt operations.
enum Operation {
    /// Format files and their child modules.
    Format {
        files: Vec<PathBuf>,
        minimal_config_path: Option<String>,
    },
    /// Print the help message.
    Help,
    // Print version information
    Version,
    /// Print detailed configuration help.
    ConfigHelp,
    /// Output default config to a file, or stdout if None
    ConfigOutputDefault {
        path: Option<String>,
    },
    /// No file specified, read from stdin
    Stdin {
        input: String,
    },
}

fn make_opts() -> Options {
    let mut opts = Options::new();

    // Sorted in alphabetical order.
    opts.optopt(
        "",
        "color",
        "Use colored output (if supported)",
        "[always|never|auto]",
    );
    opts.optflag(
        "",
        "config-help",
        "Show details of rustfmt configuration options",
    );
    opts.optopt(
        "",
        "config-path",
        "Recursively searches the given path for the rustfmt.toml config file. If not \
         found reverts to the input file path",
        "[Path for the configuration file]",
    );
    opts.opt(
        "",
        "dump-default-config",
        "Dumps default configuration to PATH. PATH defaults to stdout, if omitted.",
        "PATH",
        getopts::HasArg::Maybe,
        getopts::Occur::Optional,
    );
    opts.optopt(
        "",
        "dump-minimal-config",
        "Dumps configuration options that were checked during formatting to a file.",
        "PATH",
    );
    opts.optflag(
        "",
        "error-on-unformatted",
        "Error if unable to get comments or string literals within max_width, \
         or they are left with trailing whitespaces",
    );
    opts.optopt(
        "",
        "file-lines",
        "Format specified line ranges. See README for more detail on the JSON format.",
        "JSON",
    );
    opts.optflag(
        "",
        "verbose-diff",
        "Emit a more verbose diff, indicating the end of lines.",
    );
    opts.optflag("h", "help", "Show this message");
    opts.optflag("", "skip-children", "Don't reformat child modules");
    opts.optflag(
        "",
        "unstable-features",
        "Enables unstable features. Only available on nightly channel",
    );
    opts.optflag("v", "verbose", "Print verbose output");
    opts.optflag("V", "version", "Show version information");
    opts.optopt(
        "",
        "write-mode",
        "How to write output (not usable when piping from stdin)",
        WRITE_MODE_LIST,
    );

    opts
}

fn execute(opts: &Options) -> FmtResult<(WriteMode, Summary)> {
    let matches = opts.parse(env::args().skip(1))?;

    match determine_operation(&matches)? {
        Operation::Help => {
            print_usage_to_stdout(opts, "");
            Summary::print_exit_codes();
            Ok((WriteMode::None, Summary::default()))
        }
        Operation::Version => {
            print_version();
            Ok((WriteMode::None, Summary::default()))
        }
        Operation::ConfigHelp => {
            Config::print_docs(&mut stdout(), matches.opt_present("unstable-features"));
            Ok((WriteMode::None, Summary::default()))
        }
        Operation::ConfigOutputDefault { path } => {
            let toml = Config::default().all_options().to_toml().map_err(err_msg)?;
            if let Some(path) = path {
                let mut file = File::create(path)?;
                file.write_all(toml.as_bytes())?;
            } else {
                io::stdout().write_all(toml.as_bytes())?;
            }
            Ok((WriteMode::None, Summary::default()))
        }
        Operation::Stdin { input } => {
            // try to read config from local directory
            let options = CliOptions::from_matches(&matches)?;
            let (mut config, _) = load_config(None, Some(&options))?;

            // write_mode is always Plain for Stdin.
            config.set().write_mode(WriteMode::Plain);

            // parse file_lines
            if let Some(ref file_lines) = matches.opt_str("file-lines") {
                config
                    .set()
                    .file_lines(file_lines.parse().map_err(err_msg)?);
                for f in config.file_lines().files() {
                    match *f {
                        FileName::Custom(ref f) if f == "stdin" => {}
                        _ => eprintln!("Warning: Extra file listed in file_lines option '{}'", f),
                    }
                }
            }

            let mut error_summary = Summary::default();
            emit_pre_matter(&config)?;
            match format_and_emit_report(Input::Text(input), &config) {
                Ok(summary) => error_summary.add(summary),
                Err(_) => error_summary.add_operational_error(),
            }
            emit_post_matter(&config)?;

            Ok((WriteMode::Plain, error_summary))
        }
        Operation::Format {
            files,
            minimal_config_path,
        } => {
            let options = CliOptions::from_matches(&matches)?;
            format(files, minimal_config_path, options)
        }
    }
}

fn format(
    files: Vec<PathBuf>,
    minimal_config_path: Option<String>,
    options: CliOptions,
) -> FmtResult<(WriteMode, Summary)> {
    options.verify_file_lines(&files);
    let (config, config_path) = load_config(None, Some(&options))?;

    if config.verbose() {
        if let Some(path) = config_path.as_ref() {
            println!("Using rustfmt config file {}", path.display());
        }
    }

    emit_pre_matter(&config)?;
    let mut error_summary = Summary::default();

    for file in files {
        if !file.exists() {
            eprintln!("Error: file `{}` does not exist", file.to_str().unwrap());
            error_summary.add_operational_error();
        } else if file.is_dir() {
            eprintln!("Error: `{}` is a directory", file.to_str().unwrap());
            error_summary.add_operational_error();
        } else {
            // Check the file directory if the config-path could not be read or not provided
            let local_config = if config_path.is_none() {
                let (local_config, config_path) =
                    load_config(Some(file.parent().unwrap()), Some(&options))?;
                if local_config.verbose() {
                    if let Some(path) = config_path {
                        println!(
                            "Using rustfmt config file {} for {}",
                            path.display(),
                            file.display()
                        );
                    }
                }
                local_config
            } else {
                config.clone()
            };

            match format_and_emit_report(Input::File(file), &local_config) {
                Ok(summary) => error_summary.add(summary),
                Err(_) => {
                    error_summary.add_operational_error();
                    break;
                }
            }
        }
    }
    emit_post_matter(&config)?;

    // If we were given a path via dump-minimal-config, output any options
    // that were used during formatting as TOML.
    if let Some(path) = minimal_config_path {
        let mut file = File::create(path)?;
        let toml = config.used_options().to_toml().map_err(err_msg)?;
        file.write_all(toml.as_bytes())?;
    }

    Ok((config.write_mode(), error_summary))
}

fn print_usage_to_stdout(opts: &Options, reason: &str) {
    let sep = if reason.is_empty() {
        String::new()
    } else {
        format!("{}\n\n", reason)
    };
    let msg = format!(
        "{}Format Rust code\n\nusage: {} [options] <file>...",
        sep,
        env::args_os().next().unwrap().to_string_lossy()
    );
    println!("{}", opts.usage(&msg));
}

fn print_version() {
    let version_info = format!(
        "{}-{}",
        option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"),
        include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))
    );

    println!("rustfmt {}", version_info);
}

fn determine_operation(matches: &Matches) -> FmtResult<Operation> {
    if matches.opt_present("h") {
        return Ok(Operation::Help);
    }

    if matches.opt_present("config-help") {
        return Ok(Operation::ConfigHelp);
    }

    if matches.opt_present("dump-default-config") {
        // NOTE for some reason when configured with HasArg::Maybe + Occur::Optional opt_default
        // doesn't recognize `--foo bar` as a long flag with an argument but as a long flag with no
        // argument *plus* a free argument. Thus we check for that case in this branch -- this is
        // required for backward compatibility.
        if let Some(path) = matches.free.get(0) {
            return Ok(Operation::ConfigOutputDefault {
                path: Some(path.clone()),
            });
        } else {
            return Ok(Operation::ConfigOutputDefault {
                path: matches.opt_str("dump-default-config"),
            });
        }
    }

    if matches.opt_present("version") {
        return Ok(Operation::Version);
    }

    // If no path is given, we won't output a minimal config.
    let minimal_config_path = matches.opt_str("dump-minimal-config");

    // if no file argument is supplied, read from stdin
    if matches.free.is_empty() {
        let mut buffer = String::new();
        io::stdin().read_to_string(&mut buffer)?;

        return Ok(Operation::Stdin { input: buffer });
    }

    let files: Vec<_> = matches
        .free
        .iter()
        .map(|s| {
            let p = PathBuf::from(s);
            // we will do comparison later, so here tries to canonicalize first
            // to get the expected behavior.
            p.canonicalize().unwrap_or(p)
        })
        .collect();

    Ok(Operation::Format {
        files,
        minimal_config_path,
    })
}
<commit_msg>set stdin default config path to curr<commit_after>// Copyright 2015 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.

#![cfg(not(test))]

extern crate env_logger;
extern crate failure;
extern crate getopts;
extern crate rustfmt_nightly as rustfmt;

use std::env;
use std::fs::File;
use std::io::{self, stdout, Read, Write};
use std::path::{Path, PathBuf};

use failure::err_msg;

use getopts::{Matches, Options};

use rustfmt::{emit_post_matter, emit_pre_matter, load_config, CliOptions, Config, FmtResult,
              WriteMode, WRITE_MODE_LIST};
use rustfmt::{format_and_emit_report, FileName, Input, Summary};

fn main() {
    env_logger::init();
    let opts = make_opts();

    let exit_code = match execute(&opts) {
        Ok((write_mode, summary)) => {
            if summary.has_operational_errors() || summary.has_parsing_errors()
                || (summary.has_diff && write_mode == WriteMode::Check)
            {
                1
            } else {
                0
            }
        }
        Err(e) => {
            eprintln!("{}", e.to_string());
            1
        }
    };
    // Make sure standard output is flushed before we exit.
    std::io::stdout().flush().unwrap();

    // Exit with given exit code.
    //
    // NOTE: This immediately terminates the process without doing any cleanup,
    // so make sure to finish all necessary cleanup before this is called.
    std::process::exit(exit_code);
}

/// Rustfmt operations.
enum Operation {
    /// Format files and their child modules.
    Format {
        files: Vec<PathBuf>,
        minimal_config_path: Option<String>,
    },
    /// Print the help message.
    Help,
    // Print version information
    Version,
    /// Print detailed configuration help.
    ConfigHelp,
    /// Output default config to a file, or stdout if None
    ConfigOutputDefault {
        path: Option<String>,
    },
    /// No file specified, read from stdin
    Stdin {
        input: String,
    },
}

fn make_opts() -> Options {
    let mut opts = Options::new();

    // Sorted in alphabetical order.
    opts.optopt(
        "",
        "color",
        "Use colored output (if supported)",
        "[always|never|auto]",
    );
    opts.optflag(
        "",
        "config-help",
        "Show details of rustfmt configuration options",
    );
    opts.optopt(
        "",
        "config-path",
        "Recursively searches the given path for the rustfmt.toml config file. If not \
         found reverts to the input file path",
        "[Path for the configuration file]",
    );
    opts.opt(
        "",
        "dump-default-config",
        "Dumps default configuration to PATH. PATH defaults to stdout, if omitted.",
        "PATH",
        getopts::HasArg::Maybe,
        getopts::Occur::Optional,
    );
    opts.optopt(
        "",
        "dump-minimal-config",
        "Dumps configuration options that were checked during formatting to a file.",
        "PATH",
    );
    opts.optflag(
        "",
        "error-on-unformatted",
        "Error if unable to get comments or string literals within max_width, \
         or they are left with trailing whitespaces",
    );
    opts.optopt(
        "",
        "file-lines",
        "Format specified line ranges. See README for more detail on the JSON format.",
        "JSON",
    );
    opts.optflag(
        "",
        "verbose-diff",
        "Emit a more verbose diff, indicating the end of lines.",
    );
    opts.optflag("h", "help", "Show this message");
    opts.optflag("", "skip-children", "Don't reformat child modules");
    opts.optflag(
        "",
        "unstable-features",
        "Enables unstable features. Only available on nightly channel",
    );
    opts.optflag("v", "verbose", "Print verbose output");
    opts.optflag("V", "version", "Show version information");
    opts.optopt(
        "",
        "write-mode",
        "How to write output (not usable when piping from stdin)",
        WRITE_MODE_LIST,
    );

    opts
}

fn execute(opts: &Options) -> FmtResult<(WriteMode, Summary)> {
    let matches = opts.parse(env::args().skip(1))?;

    match determine_operation(&matches)? {
        Operation::Help => {
            print_usage_to_stdout(opts, "");
            Summary::print_exit_codes();
            Ok((WriteMode::None, Summary::default()))
        }
        Operation::Version => {
            print_version();
            Ok((WriteMode::None, Summary::default()))
        }
        Operation::ConfigHelp => {
            Config::print_docs(&mut stdout(), matches.opt_present("unstable-features"));
            Ok((WriteMode::None, Summary::default()))
        }
        Operation::ConfigOutputDefault { path } => {
            let toml = Config::default().all_options().to_toml().map_err(err_msg)?;
            if let Some(path) = path {
                let mut file = File::create(path)?;
                file.write_all(toml.as_bytes())?;
            } else {
                io::stdout().write_all(toml.as_bytes())?;
            }
            Ok((WriteMode::None, Summary::default()))
        }
        Operation::Stdin { input } => {
            // try to read config from local directory
            let options = CliOptions::from_matches(&matches)?;
            let (mut config, _) = load_config(Some(Path::new(".")), Some(&options))?;

            // write_mode is always Plain for Stdin.
            config.set().write_mode(WriteMode::Plain);

            // parse file_lines
            if let Some(ref file_lines) = matches.opt_str("file-lines") {
                config
                    .set()
                    .file_lines(file_lines.parse().map_err(err_msg)?);
                for f in config.file_lines().files() {
                    match *f {
                        FileName::Custom(ref f) if f == "stdin" => {}
                        _ => eprintln!("Warning: Extra file listed in file_lines option '{}'", f),
                    }
                }
            }

            let mut error_summary = Summary::default();
            emit_pre_matter(&config)?;
            match format_and_emit_report(Input::Text(input), &config) {
                Ok(summary) => error_summary.add(summary),
                Err(_) => error_summary.add_operational_error(),
            }
            emit_post_matter(&config)?;

            Ok((WriteMode::Plain, error_summary))
        }
        Operation::Format {
            files,
            minimal_config_path,
        } => {
            let options = CliOptions::from_matches(&matches)?;
            format(files, minimal_config_path, options)
        }
    }
}

fn format(
    files: Vec<PathBuf>,
    minimal_config_path: Option<String>,
    options: CliOptions,
) -> FmtResult<(WriteMode, Summary)> {
    options.verify_file_lines(&files);
    let (config, config_path) = load_config(None, Some(&options))?;

    if config.verbose() {
        if let Some(path) = config_path.as_ref() {
            println!("Using rustfmt config file {}", path.display());
        }
    }

    emit_pre_matter(&config)?;
    let mut error_summary = Summary::default();

    for file in files {
        if !file.exists() {
            eprintln!("Error: file `{}` does not exist", file.to_str().unwrap());
            error_summary.add_operational_error();
        } else if file.is_dir() {
            eprintln!("Error: `{}` is a directory", file.to_str().unwrap());
            error_summary.add_operational_error();
        } else {
            // Check the file directory if the config-path could not be read or not provided
            let local_config = if config_path.is_none() {
                let (local_config, config_path) =
                    load_config(Some(file.parent().unwrap()), Some(&options))?;
                if local_config.verbose() {
                    if let Some(path) = config_path {
                        println!(
                            "Using rustfmt config file {} for {}",
                            path.display(),
                            file.display()
                        );
                    }
                }
                local_config
            } else {
                config.clone()
            };

            match format_and_emit_report(Input::File(file), &local_config) {
                Ok(summary) => error_summary.add(summary),
                Err(_) => {
                    error_summary.add_operational_error();
                    break;
                }
            }
        }
    }
    emit_post_matter(&config)?;

    // If we were given a path via dump-minimal-config, output any options
    // that were used during formatting as TOML.
    if let Some(path) = minimal_config_path {
        let mut file = File::create(path)?;
        let toml = config.used_options().to_toml().map_err(err_msg)?;
        file.write_all(toml.as_bytes())?;
    }

    Ok((config.write_mode(), error_summary))
}

fn print_usage_to_stdout(opts: &Options, reason: &str) {
    let sep = if reason.is_empty() {
        String::new()
    } else {
        format!("{}\n\n", reason)
    };
    let msg = format!(
        "{}Format Rust code\n\nusage: {} [options] <file>...",
        sep,
        env::args_os().next().unwrap().to_string_lossy()
    );
    println!("{}", opts.usage(&msg));
}

fn print_version() {
    let version_info = format!(
        "{}-{}",
        option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"),
        include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))
    );

    println!("rustfmt {}", version_info);
}

fn determine_operation(matches: &Matches) -> FmtResult<Operation> {
    if matches.opt_present("h") {
        return Ok(Operation::Help);
    }

    if matches.opt_present("config-help") {
        return Ok(Operation::ConfigHelp);
    }

    if matches.opt_present("dump-default-config") {
        // NOTE for some reason when configured with HasArg::Maybe + Occur::Optional opt_default
        // doesn't recognize `--foo bar` as a long flag with an argument but as a long flag with no
        // argument *plus* a free argument. Thus we check for that case in this branch -- this is
        // required for backward compatibility.
        if let Some(path) = matches.free.get(0) {
            return Ok(Operation::ConfigOutputDefault {
                path: Some(path.clone()),
            });
        } else {
            return Ok(Operation::ConfigOutputDefault {
                path: matches.opt_str("dump-default-config"),
            });
        }
    }

    if matches.opt_present("version") {
        return Ok(Operation::Version);
    }

    // If no path is given, we won't output a minimal config.
    let minimal_config_path = matches.opt_str("dump-minimal-config");

    // if no file argument is supplied, read from stdin
    if matches.free.is_empty() {
        let mut buffer = String::new();
        io::stdin().read_to_string(&mut buffer)?;

        return Ok(Operation::Stdin { input: buffer });
    }

    let files: Vec<_> = matches
        .free
        .iter()
        .map(|s| {
            let p = PathBuf::from(s);
            // we will do comparison later, so here tries to canonicalize first
            // to get the expected behavior.
            p.canonicalize().unwrap_or(p)
        })
        .collect();

    Ok(Operation::Format {
        files,
        minimal_config_path,
    })
}
<|endoftext|>


<commit_before>use cargo::core::Workspace;
use cargo::ops::{self, MessageFormat, Packages};
use cargo::util::{CliResult, CliError, Human, human, Config};
use cargo::util::important_paths::find_root_manifest_for_wd;

#[derive(RustcDecodable)]
pub struct Options {
    arg_args: Vec<String>,
    flag_features: Vec<String>,
    flag_all_features: bool,
    flag_jobs: Option<u32>,
    flag_manifest_path: Option<String>,
    flag_no_default_features: bool,
    flag_no_run: bool,
    flag_package: Vec<String>,
    flag_target: Option<String>,
    flag_lib: bool,
    flag_doc: bool,
    flag_bin: Vec<String>,
    flag_example: Vec<String>,
    flag_test: Vec<String>,
    flag_bench: Vec<String>,
    flag_verbose: u32,
    flag_quiet: Option<bool>,
    flag_color: Option<String>,
    flag_message_format: MessageFormat,
    flag_release: bool,
    flag_no_fail_fast: bool,
    flag_frozen: bool,
    flag_locked: bool,
    flag_all: bool,
}

pub const USAGE: &'static str = "
Execute all unit and integration tests of a local package

Usage:
    cargo test [options] [--] [<args>...]

Options:
    -h, --help                   Print this message
    --lib                        Test only this package's library
    --doc                        Test only this library's documentation
    --bin NAME                   Test only the specified binary
    --example NAME               Test only the specified example
    --test NAME                  Test only the specified integration test target
    --bench NAME                 Test only the specified benchmark target
    --no-run                     Compile, but don't run tests
    -p SPEC, --package SPEC ...  Package to run tests for
    --all                        Test all packages in the workspace
    -j N, --jobs N               Number of parallel jobs, defaults to # of CPUs
    --release                    Build artifacts in release mode, with optimizations
    --features FEATURES          Space-separated list of features to also build
    --all-features               Build all available features
    --no-default-features        Do not build the `default` feature
    --target TRIPLE              Build for the target triple
    --manifest-path PATH         Path to the manifest to build tests for
    -v, --verbose ...            Use verbose output (-vv very verbose/build.rs output)
    -q, --quiet                  No output printed to stdout
    --color WHEN                 Coloring: auto, always, never
    --message-format FMT         Error format: human, json [default: human]
    --no-fail-fast               Run all tests regardless of failure
    --frozen                     Require Cargo.lock and cache are up to date
    --locked                     Require Cargo.lock is up to date

All of the trailing arguments are passed to the test binaries generated for
filtering tests and generally providing options configuring how they run. For
example, this will run all tests with the name `foo` in their name:

    cargo test foo

If the --package argument is given, then SPEC is a package id specification
which indicates which package should be tested. If it is not given, then the
current package is tested. For more information on SPEC and its format, see the
`cargo help pkgid` command.

All packages in the workspace are tested if the `--all` flag is supplied. The
`--all` flag may be supplied in the presence of a virtual manifest.

The --jobs argument affects the building of the test executable but does
not affect how many jobs are used when running the tests.

Compilation can be configured via the `test` profile in the manifest.

By default the rust test harness hides output from test execution to
keep results readable. Test output can be recovered (e.g. for debugging)
by passing `--nocapture` to the test binaries:

  cargo test -- --nocapture

To get the list of all options available for the test binaries use this:

  cargo test -- --help
";

pub fn execute(options: Options, config: &Config) -> CliResult {
    config.configure(options.flag_verbose,
                     options.flag_quiet,
                     &options.flag_color,
                     options.flag_frozen,
                     options.flag_locked)?;

    let root = find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())?;

    let empty = Vec::new();
    let (mode, filter);
    if options.flag_doc {
        mode = ops::CompileMode::Doctest;
        filter = ops::CompileFilter::new(true, &empty, &empty, &empty, &empty);
    } else {
        mode = ops::CompileMode::Test;
        filter = ops::CompileFilter::new(options.flag_lib,
                                         &options.flag_bin,
                                         &options.flag_test,
                                         &options.flag_example,
                                         &options.flag_bench);
    }

    let spec = if options.flag_all {
        Packages::All
    } else {
        Packages::Packages(&options.flag_package)
    };

    let ops = ops::TestOptions {
        no_run: options.flag_no_run,
        no_fail_fast: options.flag_no_fail_fast,
        only_doc: options.flag_doc,
        compile_opts: ops::CompileOptions {
            config: config,
            jobs: options.flag_jobs,
            target: options.flag_target.as_ref().map(|s| &s[..]),
            features: &options.flag_features,
            all_features: options.flag_all_features,
            no_default_features: options.flag_no_default_features,
            spec: spec,
            release: options.flag_release,
            mode: mode,
            filter: filter,
            message_format: options.flag_message_format,
            target_rustdoc_args: None,
            target_rustc_args: None,
        },
    };

    let ws = Workspace::new(&root, config)?;
    let err = ops::run_tests(&ws, &ops, &options.arg_args)?;
    match err {
        None => Ok(()),
        Some(err) => {
            Err(match err.exit.as_ref().and_then(|e| e.code()) {
                Some(i) => CliError::new(human("test failed"), i),
                None => CliError::new(Box::new(Human(err)), 101),
            })
        }
    }
}
<commit_msg>Allow to run several integration tests<commit_after>use cargo::core::Workspace;
use cargo::ops::{self, MessageFormat, Packages};
use cargo::util::{CliResult, CliError, Human, human, Config};
use cargo::util::important_paths::find_root_manifest_for_wd;

#[derive(RustcDecodable)]
pub struct Options {
    arg_args: Vec<String>,
    flag_features: Vec<String>,
    flag_all_features: bool,
    flag_jobs: Option<u32>,
    flag_manifest_path: Option<String>,
    flag_no_default_features: bool,
    flag_no_run: bool,
    flag_package: Vec<String>,
    flag_target: Option<String>,
    flag_lib: bool,
    flag_doc: bool,
    flag_bin: Vec<String>,
    flag_example: Vec<String>,
    flag_test: Vec<String>,
    flag_bench: Vec<String>,
    flag_verbose: u32,
    flag_quiet: Option<bool>,
    flag_color: Option<String>,
    flag_message_format: MessageFormat,
    flag_release: bool,
    flag_no_fail_fast: bool,
    flag_frozen: bool,
    flag_locked: bool,
    flag_all: bool,
}

pub const USAGE: &'static str = "
Execute all unit and integration tests of a local package

Usage:
    cargo test [options] [--] [<args>...]

Options:
    -h, --help                   Print this message
    --lib                        Test only this package's library
    --doc                        Test only this library's documentation
    --bin NAME ...               Test only the specified binaries
    --example NAME ...           Test only the specified examples
    --test NAME ...              Test only the specified integration test targets
    --bench NAME ...             Test only the specified benchmark targets
    --no-run                     Compile, but don't run tests
    -p SPEC, --package SPEC ...  Package to run tests for
    --all                        Test all packages in the workspace
    -j N, --jobs N               Number of parallel jobs, defaults to # of CPUs
    --release                    Build artifacts in release mode, with optimizations
    --features FEATURES          Space-separated list of features to also build
    --all-features               Build all available features
    --no-default-features        Do not build the `default` feature
    --target TRIPLE              Build for the target triple
    --manifest-path PATH         Path to the manifest to build tests for
    -v, --verbose ...            Use verbose output (-vv very verbose/build.rs output)
    -q, --quiet                  No output printed to stdout
    --color WHEN                 Coloring: auto, always, never
    --message-format FMT         Error format: human, json [default: human]
    --no-fail-fast               Run all tests regardless of failure
    --frozen                     Require Cargo.lock and cache are up to date
    --locked                     Require Cargo.lock is up to date

All of the trailing arguments are passed to the test binaries generated for
filtering tests and generally providing options configuring how they run. For
example, this will run all tests with the name `foo` in their name:

    cargo test foo

If the --package argument is given, then SPEC is a package id specification
which indicates which package should be tested. If it is not given, then the
current package is tested. For more information on SPEC and its format, see the
`cargo help pkgid` command.

All packages in the workspace are tested if the `--all` flag is supplied. The
`--all` flag may be supplied in the presence of a virtual manifest.

The --jobs argument affects the building of the test executable but does
not affect how many jobs are used when running the tests.

Compilation can be configured via the `test` profile in the manifest.

By default the rust test harness hides output from test execution to
keep results readable. Test output can be recovered (e.g. for debugging)
by passing `--nocapture` to the test binaries:

  cargo test -- --nocapture

To get the list of all options available for the test binaries use this:

  cargo test -- --help
";

pub fn execute(options: Options, config: &Config) -> CliResult {
    config.configure(options.flag_verbose,
                     options.flag_quiet,
                     &options.flag_color,
                     options.flag_frozen,
                     options.flag_locked)?;

    let root = find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())?;

    let empty = Vec::new();
    let (mode, filter);
    if options.flag_doc {
        mode = ops::CompileMode::Doctest;
        filter = ops::CompileFilter::new(true, &empty, &empty, &empty, &empty);
    } else {
        mode = ops::CompileMode::Test;
        filter = ops::CompileFilter::new(options.flag_lib,
                                         &options.flag_bin,
                                         &options.flag_test,
                                         &options.flag_example,
                                         &options.flag_bench);
    }

    let spec = if options.flag_all {
        Packages::All
    } else {
        Packages::Packages(&options.flag_package)
    };

    let ops = ops::TestOptions {
        no_run: options.flag_no_run,
        no_fail_fast: options.flag_no_fail_fast,
        only_doc: options.flag_doc,
        compile_opts: ops::CompileOptions {
            config: config,
            jobs: options.flag_jobs,
            target: options.flag_target.as_ref().map(|s| &s[..]),
            features: &options.flag_features,
            all_features: options.flag_all_features,
            no_default_features: options.flag_no_default_features,
            spec: spec,
            release: options.flag_release,
            mode: mode,
            filter: filter,
            message_format: options.flag_message_format,
            target_rustdoc_args: None,
            target_rustc_args: None,
        },
    };

    let ws = Workspace::new(&root, config)?;
    let err = ops::run_tests(&ws, &ops, &options.arg_args)?;
    match err {
        None => Ok(()),
        Some(err) => {
            Err(match err.exit.as_ref().and_then(|e| e.code()) {
                Some(i) => CliError::new(human("test failed"), i),
                None => CliError::new(Box::new(Human(err)), 101),
            })
        }
    }
}
<|endoftext|>


<commit_before>//! The internal representation of a book and infrastructure for loading it from
//! disk and building it.
//!
//! For examples on using `MDBook`, consult the [top-level documentation][1].
//!
//! [1]: ../index.html

#![deny(missing_docs)]

mod summary;
mod book;
mod init;

pub use self::book::{load_book, Book, BookItem, BookItems, Chapter};
pub use self::summary::{parse_summary, Link, SectionNumber, Summary, SummaryItem};
pub use self::init::BookBuilder;

use std::path::PathBuf;
use std::io::Write;
use std::process::Command;
use tempdir::TempDir;
use toml::Value;

use utils;
use renderer::{CmdRenderer, HtmlHandlebars, RenderContext, Renderer};
use preprocess::{Preprocessor, LinkPreprocessor, PreprocessorContext};
use errors::*;

use config::Config;

/// The object used to manage and build a book.
pub struct MDBook {
    /// The book's root directory.
    pub root: PathBuf,
    /// The configuration used to tweak now a book is built.
    pub config: Config,
    /// A representation of the book's contents in memory.
    pub book: Book,
    renderers: Vec<Box<Renderer>>,

    /// The URL used for live reloading when serving up the book.
    pub livereload: Option<String>,

    /// List of pre-processors to be run on the book
    preprocessors: Vec<Box<Preprocessor>>
}

impl MDBook {
    /// Load a book from its root directory on disk.
    pub fn load<P: Into<PathBuf>>(book_root: P) -> Result<MDBook> {
        let book_root = book_root.into();
        let config_location = book_root.join("book.toml");

        // the book.json file is no longer used, so we should emit a warning to
        // let people know to migrate to book.toml
        if book_root.join("book.json").exists() {
            warn!("It appears you are still using book.json for configuration.");
            warn!("This format is no longer used, so you should migrate to the");
            warn!("book.toml format.");
            warn!("Check the user guide for migration information:");
            warn!("\thttps://rust-lang-nursery.github.io/mdBook/format/config.html");
        }

        let mut config = if config_location.exists() {
            debug!("[*] Loading config from {}", config_location.display());
            Config::from_disk(&config_location)?
        } else {
            Config::default()
        };

        config.update_from_env();

        if log_enabled!(::log::Level::Trace) {
            for line in format!("Config: {:#?}", config).lines() {
                trace!("{}", line);
            }
        }

        MDBook::load_with_config(book_root, config)
    }

    /// Load a book from its root directory using a custom config.
    pub fn load_with_config<P: Into<PathBuf>>(book_root: P, config: Config) -> Result<MDBook> {
        let root = book_root.into();

        let src_dir = root.join(&config.book.src);
        let book = book::load_book(&src_dir, &config.build)?;
        let livereload = None;

        let renderers = determine_renderers(&config);
        let preprocessors = determine_preprocessors(&config)?;

        Ok(MDBook {
            root,
            config,
            book,
            renderers,
            livereload,
            preprocessors,
        })
    }

    /// Returns a flat depth-first iterator over the elements of the book,
    /// it returns an [BookItem enum](bookitem.html):
    /// `(section: String, bookitem: &BookItem)`
    ///
    /// ```no_run
    /// # extern crate mdbook;
    /// # use mdbook::MDBook;
    /// # use mdbook::book::BookItem;
    /// # #[allow(unused_variables)]
    /// # fn main() {
    /// # let book = MDBook::load("mybook").unwrap();
    /// for item in book.iter() {
    ///     match *item {
    ///         BookItem::Chapter(ref chapter) => {},
    ///         BookItem::Separator => {},
    ///     }
    /// }
    ///
    /// // would print something like this:
    /// // 1. Chapter 1
    /// // 1.1 Sub Chapter
    /// // 1.2 Sub Chapter
    /// // 2. Chapter 2
    /// //
    /// // etc.
    /// # }
    /// ```
    pub fn iter(&self) -> BookItems {
        self.book.iter()
    }

    /// `init()` gives you a `BookBuilder` which you can use to setup a new book
    /// and its accompanying directory structure.
    ///
    /// The `BookBuilder` creates some boilerplate files and directories to get
    /// you started with your book.
    ///
    /// ```text
    /// book-test/
    /// ├── book
    /// └── src
    ///     ├── chapter_1.md
    ///     └── SUMMARY.md
    /// ```
    ///
    /// It uses the path provided as the root directory for your book, then adds
    /// in a `src/` directory containing a `SUMMARY.md` and `chapter_1.md` file
    /// to get you started.
    pub fn init<P: Into<PathBuf>>(book_root: P) -> BookBuilder {
        BookBuilder::new(book_root)
    }

    /// Tells the renderer to build our book and put it in the build directory.
    pub fn build(&self) -> Result<()> {
        debug!("[fn]: build");

        let mut preprocessed_book = self.book.clone();
        let preprocess_ctx = PreprocessorContext {
            src_dir: self.source_dir(),
        };

        for preprocessor in &self.preprocessors {
            preprocessor.run(&preprocess_ctx, &mut preprocessed_book)?;
        }

        for renderer in &self.renderers {
            self.run_renderer(&preprocessed_book, renderer.as_ref())?;
        }

        Ok(())
    }

    fn run_renderer(&self, preprocessed_book: &Book, renderer: &Renderer) -> Result<()> {
        let name = renderer.name();
        let build_dir = self.build_dir_for(name);
        if build_dir.exists() {
            debug!(
                "Cleaning build dir for the \"{}\" renderer ({})",
                name,
                build_dir.display()
            );

            utils::fs::remove_dir_content(&build_dir)
                .chain_err(|| "Unable to clear output directory")?;
        }

        let render_context = RenderContext::new(
            self.root.clone(),
            preprocessed_book.clone(),
            self.config.clone(),
            build_dir,
        );

        renderer
            .render(&render_context)
            .chain_err(|| "Rendering failed")
    }

    /// You can change the default renderer to another one by using this method.
    /// The only requirement is for your renderer to implement the [Renderer
    /// trait](../../renderer/renderer/trait.Renderer.html)
    pub fn with_renderer<R: Renderer + 'static>(&mut self, renderer: R) -> &mut Self {
        self.renderers.push(Box::new(renderer));
        self
    }

    /// You can add a new preprocessor by using this method.
    /// The only requirement is for your renderer to implement the Preprocessor trait.
    pub fn with_preprecessor<P: Preprocessor + 'static>(&mut self, preprocessor: P) -> &mut Self {
        self.preprocessors.push(Box::new(preprocessor));
        self
    }

    /// Run `rustdoc` tests on the book, linking against the provided libraries.
    pub fn test(&mut self, library_paths: Vec<&str>) -> Result<()> {
        let library_args: Vec<&str> = (0..library_paths.len())
            .map(|_| "-L")
            .zip(library_paths.into_iter())
            .flat_map(|x| vec![x.0, x.1])
            .collect();

        let temp_dir = TempDir::new("mdbook")?;

        let src_dir = self.source_dir();
        let preprocess_context = PreprocessorContext {
            src_dir
        };

        LinkPreprocessor::new().run(&preprocess_context, &mut self.book)?;

        for item in self.iter() {
            if let BookItem::Chapter(ref ch) = *item {
                if !ch.path.as_os_str().is_empty() {
                    let path = self.source_dir().join(&ch.path);
                    let content = utils::fs::file_to_string(&path)?;
                    println!("[*]: Testing file: {:?}", path);

                    // write preprocessed file to tempdir
                    let path = temp_dir.path().join(&ch.path);
                    let mut tmpf = utils::fs::create_file(&path)?;
                    tmpf.write_all(content.as_bytes())?;

                    let output = Command::new("rustdoc")
                        .arg(&path)
                        .arg("--test")
                        .args(&library_args)
                        .output()?;

                    if !output.status.success() {
                        bail!(ErrorKind::Subprocess(
                            "Rustdoc returned an error".to_string(),
                            output
                        ));
                    }
                }
            }
        }
        Ok(())
    }

    /// The logic for determining where a backend should put its build
    /// artefacts.
    ///
    /// If there is only 1 renderer, put it in the directory pointed to by the
    /// `build.build_dir` key in `Config`. If there is more than one then the
    /// renderer gets its own directory within the main build dir.
    ///
    /// i.e. If there were only one renderer (in this case, the HTML renderer):
    ///
    /// - build/
    ///   - index.html
    ///   - ...
    ///
    /// Otherwise if there are multiple:
    ///
    /// - build/
    ///   - epub/
    ///     - my_awesome_book.epub
    ///   - html/
    ///     - index.html
    ///     - ...
    ///   - latex/
    ///     - my_awesome_book.tex
    ///
    pub fn build_dir_for(&self, backend_name: &str) -> PathBuf {
        let build_dir = self.root.join(&self.config.build.build_dir);

        if self.renderers.len() <= 1 {
            build_dir
        } else {
            build_dir.join(backend_name)
        }
    }

    /// Get the directory containing this book's source files.
    pub fn source_dir(&self) -> PathBuf {
        self.root.join(&self.config.book.src)
    }

    // FIXME: This really belongs as part of the `HtmlConfig`.
    #[doc(hidden)]
    pub fn theme_dir(&self) -> PathBuf {
        match self.config.html_config().and_then(|h| h.theme) {
            Some(d) => self.root.join(d),
            None => self.root.join("theme"),
        }
    }
}

/// Look at the `Config` and try to figure out what renderers to use.
fn determine_renderers(config: &Config) -> Vec<Box<Renderer>> {
    let mut renderers: Vec<Box<Renderer>> = Vec::new();

    if let Some(output_table) = config.get("output").and_then(|o| o.as_table()) {
        for (key, table) in output_table.iter() {
            // the "html" backend has its own Renderer
            if key == "html" {
                renderers.push(Box::new(HtmlHandlebars::new()));
            } else {
                let renderer = interpret_custom_renderer(key, table);
                renderers.push(renderer);
            }
        }
    }

    // if we couldn't find anything, add the HTML renderer as a default
    if renderers.is_empty() {
        renderers.push(Box::new(HtmlHandlebars::new()));
    }

    renderers
}

fn default_preprocessors() -> Vec<Box<Preprocessor>> {
    vec![Box::new(LinkPreprocessor::new())]
}

/// Look at the `MDBook` and try to figure out what preprocessors to run.
fn determine_preprocessors(config: &Config) -> Result<Vec<Box<Preprocessor>>> {

    let preprocess_list = match config.build.preprocess {
        Some(ref p) => p,
        // If no preprocessor field is set, default to the LinkPreprocessor. This allows you
        // to disable the LinkPreprocessor by setting "preprocess" to an empty list.
        None => return Ok(default_preprocessors())
    };

    let mut preprocessors: Vec<Box<Preprocessor>> = Vec::new();

    for key in preprocess_list {
        match key.as_ref() {
            "links" => {
                preprocessors.push(Box::new(LinkPreprocessor::new()))
            }
            _ =>  bail!("{:?} is not a recognised preprocessor", key),
        }
    }

    Ok(preprocessors)
}

fn interpret_custom_renderer(key: &str, table: &Value) -> Box<Renderer> {
    // look for the `command` field, falling back to using the key
    // prepended by "mdbook-"
    let table_dot_command = table
        .get("command")
        .and_then(|c| c.as_str())
        .map(|s| s.to_string());

    let command = table_dot_command.unwrap_or_else(|| format!("mdbook-{}", key));

    Box::new(CmdRenderer::new(key.to_string(), command.to_string()))
}

#[cfg(test)]
mod tests {
    use super::*;
    use toml::value::{Table, Value};

    #[test]
    fn config_defaults_to_html_renderer_if_empty() {
        let cfg = Config::default();

        // make sure we haven't got anything in the `output` table
        assert!(cfg.get("output").is_none());

        let got = determine_renderers(&cfg);

        assert_eq!(got.len(), 1);
        assert_eq!(got[0].name(), "html");
    }

    #[test]
    fn add_a_random_renderer_to_the_config() {
        let mut cfg = Config::default();
        cfg.set("output.random", Table::new()).unwrap();

        let got = determine_renderers(&cfg);

        assert_eq!(got.len(), 1);
        assert_eq!(got[0].name(), "random");
    }

    #[test]
    fn add_a_random_renderer_with_custom_command_to_the_config() {
        let mut cfg = Config::default();

        let mut table = Table::new();
        table.insert("command".to_string(), Value::String("false".to_string()));
        cfg.set("output.random", table).unwrap();

        let got = determine_renderers(&cfg);

        assert_eq!(got.len(), 1);
        assert_eq!(got[0].name(), "random");
    }

    #[test]
    fn config_defaults_to_link_preprocessor_if_not_set() {
        let cfg = Config::default();

        // make sure we haven't got anything in the `output` table
        assert!(cfg.build.preprocess.is_none());

        let got = determine_preprocessors(&cfg);

        assert!(got.is_ok());
        assert_eq!(got.as_ref().unwrap().len(), 1);
        assert_eq!(got.as_ref().unwrap()[0].name(), "links");
    }

    #[test]
    fn config_doesnt_default_if_empty() {
        let cfg_str: &'static str = r#"
        [book]
        title = "Some Book"

        [build]
        build-dir = "outputs"
        create-missing = false
        preprocess = []
        "#;

        let cfg = Config::from_str(cfg_str).unwrap();

        // make sure we have something in the `output` table
        assert!(cfg.build.preprocess.is_some());

        let got = determine_preprocessors(&cfg);

        assert!(got.is_ok());
        assert!(got.unwrap().is_empty());
    }

    #[test]
    fn config_complains_if_unimplemented_preprocessor() {
        let cfg_str: &'static str = r#"
        [book]
        title = "Some Book"

        [build]
        build-dir = "outputs"
        create-missing = false
        preprocess = ["random"]
        "#;

        let cfg = Config::from_str(cfg_str).unwrap();

        // make sure we have something in the `output` table
        assert!(cfg.build.preprocess.is_some());

        let got = determine_preprocessors(&cfg);

        assert!(got.is_err());
    }
}
<commit_msg>Update comment with rustdoc link<commit_after>//! The internal representation of a book and infrastructure for loading it from
//! disk and building it.
//!
//! For examples on using `MDBook`, consult the [top-level documentation][1].
//!
//! [1]: ../index.html

#![deny(missing_docs)]

mod summary;
mod book;
mod init;

pub use self::book::{load_book, Book, BookItem, BookItems, Chapter};
pub use self::summary::{parse_summary, Link, SectionNumber, Summary, SummaryItem};
pub use self::init::BookBuilder;

use std::path::PathBuf;
use std::io::Write;
use std::process::Command;
use tempdir::TempDir;
use toml::Value;

use utils;
use renderer::{CmdRenderer, HtmlHandlebars, RenderContext, Renderer};
use preprocess::{Preprocessor, LinkPreprocessor, PreprocessorContext};
use errors::*;

use config::Config;

/// The object used to manage and build a book.
pub struct MDBook {
    /// The book's root directory.
    pub root: PathBuf,
    /// The configuration used to tweak now a book is built.
    pub config: Config,
    /// A representation of the book's contents in memory.
    pub book: Book,
    renderers: Vec<Box<Renderer>>,

    /// The URL used for live reloading when serving up the book.
    pub livereload: Option<String>,

    /// List of pre-processors to be run on the book
    preprocessors: Vec<Box<Preprocessor>>
}

impl MDBook {
    /// Load a book from its root directory on disk.
    pub fn load<P: Into<PathBuf>>(book_root: P) -> Result<MDBook> {
        let book_root = book_root.into();
        let config_location = book_root.join("book.toml");

        // the book.json file is no longer used, so we should emit a warning to
        // let people know to migrate to book.toml
        if book_root.join("book.json").exists() {
            warn!("It appears you are still using book.json for configuration.");
            warn!("This format is no longer used, so you should migrate to the");
            warn!("book.toml format.");
            warn!("Check the user guide for migration information:");
            warn!("\thttps://rust-lang-nursery.github.io/mdBook/format/config.html");
        }

        let mut config = if config_location.exists() {
            debug!("[*] Loading config from {}", config_location.display());
            Config::from_disk(&config_location)?
        } else {
            Config::default()
        };

        config.update_from_env();

        if log_enabled!(::log::Level::Trace) {
            for line in format!("Config: {:#?}", config).lines() {
                trace!("{}", line);
            }
        }

        MDBook::load_with_config(book_root, config)
    }

    /// Load a book from its root directory using a custom config.
    pub fn load_with_config<P: Into<PathBuf>>(book_root: P, config: Config) -> Result<MDBook> {
        let root = book_root.into();

        let src_dir = root.join(&config.book.src);
        let book = book::load_book(&src_dir, &config.build)?;
        let livereload = None;

        let renderers = determine_renderers(&config);
        let preprocessors = determine_preprocessors(&config)?;

        Ok(MDBook {
            root,
            config,
            book,
            renderers,
            livereload,
            preprocessors,
        })
    }

    /// Returns a flat depth-first iterator over the elements of the book,
    /// it returns an [BookItem enum](bookitem.html):
    /// `(section: String, bookitem: &BookItem)`
    ///
    /// ```no_run
    /// # extern crate mdbook;
    /// # use mdbook::MDBook;
    /// # use mdbook::book::BookItem;
    /// # #[allow(unused_variables)]
    /// # fn main() {
    /// # let book = MDBook::load("mybook").unwrap();
    /// for item in book.iter() {
    ///     match *item {
    ///         BookItem::Chapter(ref chapter) => {},
    ///         BookItem::Separator => {},
    ///     }
    /// }
    ///
    /// // would print something like this:
    /// // 1. Chapter 1
    /// // 1.1 Sub Chapter
    /// // 1.2 Sub Chapter
    /// // 2. Chapter 2
    /// //
    /// // etc.
    /// # }
    /// ```
    pub fn iter(&self) -> BookItems {
        self.book.iter()
    }

    /// `init()` gives you a `BookBuilder` which you can use to setup a new book
    /// and its accompanying directory structure.
    ///
    /// The `BookBuilder` creates some boilerplate files and directories to get
    /// you started with your book.
    ///
    /// ```text
    /// book-test/
    /// ├── book
    /// └── src
    ///     ├── chapter_1.md
    ///     └── SUMMARY.md
    /// ```
    ///
    /// It uses the path provided as the root directory for your book, then adds
    /// in a `src/` directory containing a `SUMMARY.md` and `chapter_1.md` file
    /// to get you started.
    pub fn init<P: Into<PathBuf>>(book_root: P) -> BookBuilder {
        BookBuilder::new(book_root)
    }

    /// Tells the renderer to build our book and put it in the build directory.
    pub fn build(&self) -> Result<()> {
        debug!("[fn]: build");

        let mut preprocessed_book = self.book.clone();
        let preprocess_ctx = PreprocessorContext {
            src_dir: self.source_dir(),
        };

        for preprocessor in &self.preprocessors {
            preprocessor.run(&preprocess_ctx, &mut preprocessed_book)?;
        }

        for renderer in &self.renderers {
            self.run_renderer(&preprocessed_book, renderer.as_ref())?;
        }

        Ok(())
    }

    fn run_renderer(&self, preprocessed_book: &Book, renderer: &Renderer) -> Result<()> {
        let name = renderer.name();
        let build_dir = self.build_dir_for(name);
        if build_dir.exists() {
            debug!(
                "Cleaning build dir for the \"{}\" renderer ({})",
                name,
                build_dir.display()
            );

            utils::fs::remove_dir_content(&build_dir)
                .chain_err(|| "Unable to clear output directory")?;
        }

        let render_context = RenderContext::new(
            self.root.clone(),
            preprocessed_book.clone(),
            self.config.clone(),
            build_dir,
        );

        renderer
            .render(&render_context)
            .chain_err(|| "Rendering failed")
    }

    /// You can change the default renderer to another one by using this method.
    /// The only requirement is for your renderer to implement the [`Renderer`
    /// trait](../renderer/trait.Renderer.html)
    pub fn with_renderer<R: Renderer + 'static>(&mut self, renderer: R) -> &mut Self {
        self.renderers.push(Box::new(renderer));
        self
    }

    /// Register a [`Preprocessor`](../preprocess/trait.Preprocessor.html) to be used when rendering the book.
    pub fn with_preprecessor<P: Preprocessor + 'static>(&mut self, preprocessor: P) -> &mut Self {
        self.preprocessors.push(Box::new(preprocessor));
        self
    }

    /// Run `rustdoc` tests on the book, linking against the provided libraries.
    pub fn test(&mut self, library_paths: Vec<&str>) -> Result<()> {
        let library_args: Vec<&str> = (0..library_paths.len())
            .map(|_| "-L")
            .zip(library_paths.into_iter())
            .flat_map(|x| vec![x.0, x.1])
            .collect();

        let temp_dir = TempDir::new("mdbook")?;

        let src_dir = self.source_dir();
        let preprocess_context = PreprocessorContext {
            src_dir
        };

        LinkPreprocessor::new().run(&preprocess_context, &mut self.book)?;

        for item in self.iter() {
            if let BookItem::Chapter(ref ch) = *item {
                if !ch.path.as_os_str().is_empty() {
                    let path = self.source_dir().join(&ch.path);
                    let content = utils::fs::file_to_string(&path)?;
                    println!("[*]: Testing file: {:?}", path);

                    // write preprocessed file to tempdir
                    let path = temp_dir.path().join(&ch.path);
                    let mut tmpf = utils::fs::create_file(&path)?;
                    tmpf.write_all(content.as_bytes())?;

                    let output = Command::new("rustdoc")
                        .arg(&path)
                        .arg("--test")
                        .args(&library_args)
                        .output()?;

                    if !output.status.success() {
                        bail!(ErrorKind::Subprocess(
                            "Rustdoc returned an error".to_string(),
                            output
                        ));
                    }
                }
            }
        }
        Ok(())
    }

    /// The logic for determining where a backend should put its build
    /// artefacts.
    ///
    /// If there is only 1 renderer, put it in the directory pointed to by the
    /// `build.build_dir` key in `Config`. If there is more than one then the
    /// renderer gets its own directory within the main build dir.
    ///
    /// i.e. If there were only one renderer (in this case, the HTML renderer):
    ///
    /// - build/
    ///   - index.html
    ///   - ...
    ///
    /// Otherwise if there are multiple:
    ///
    /// - build/
    ///   - epub/
    ///     - my_awesome_book.epub
    ///   - html/
    ///     - index.html
    ///     - ...
    ///   - latex/
    ///     - my_awesome_book.tex
    ///
    pub fn build_dir_for(&self, backend_name: &str) -> PathBuf {
        let build_dir = self.root.join(&self.config.build.build_dir);

        if self.renderers.len() <= 1 {
            build_dir
        } else {
            build_dir.join(backend_name)
        }
    }

    /// Get the directory containing this book's source files.
    pub fn source_dir(&self) -> PathBuf {
        self.root.join(&self.config.book.src)
    }

    // FIXME: This really belongs as part of the `HtmlConfig`.
    #[doc(hidden)]
    pub fn theme_dir(&self) -> PathBuf {
        match self.config.html_config().and_then(|h| h.theme) {
            Some(d) => self.root.join(d),
            None => self.root.join("theme"),
        }
    }
}

/// Look at the `Config` and try to figure out what renderers to use.
fn determine_renderers(config: &Config) -> Vec<Box<Renderer>> {
    let mut renderers: Vec<Box<Renderer>> = Vec::new();

    if let Some(output_table) = config.get("output").and_then(|o| o.as_table()) {
        for (key, table) in output_table.iter() {
            // the "html" backend has its own Renderer
            if key == "html" {
                renderers.push(Box::new(HtmlHandlebars::new()));
            } else {
                let renderer = interpret_custom_renderer(key, table);
                renderers.push(renderer);
            }
        }
    }

    // if we couldn't find anything, add the HTML renderer as a default
    if renderers.is_empty() {
        renderers.push(Box::new(HtmlHandlebars::new()));
    }

    renderers
}

fn default_preprocessors() -> Vec<Box<Preprocessor>> {
    vec![Box::new(LinkPreprocessor::new())]
}

/// Look at the `MDBook` and try to figure out what preprocessors to run.
fn determine_preprocessors(config: &Config) -> Result<Vec<Box<Preprocessor>>> {

    let preprocess_list = match config.build.preprocess {
        Some(ref p) => p,
        // If no preprocessor field is set, default to the LinkPreprocessor. This allows you
        // to disable the LinkPreprocessor by setting "preprocess" to an empty list.
        None => return Ok(default_preprocessors())
    };

    let mut preprocessors: Vec<Box<Preprocessor>> = Vec::new();

    for key in preprocess_list {
        match key.as_ref() {
            "links" => {
                preprocessors.push(Box::new(LinkPreprocessor::new()))
            }
            _ =>  bail!("{:?} is not a recognised preprocessor", key),
        }
    }

    Ok(preprocessors)
}

fn interpret_custom_renderer(key: &str, table: &Value) -> Box<Renderer> {
    // look for the `command` field, falling back to using the key
    // prepended by "mdbook-"
    let table_dot_command = table
        .get("command")
        .and_then(|c| c.as_str())
        .map(|s| s.to_string());

    let command = table_dot_command.unwrap_or_else(|| format!("mdbook-{}", key));

    Box::new(CmdRenderer::new(key.to_string(), command.to_string()))
}

#[cfg(test)]
mod tests {
    use super::*;
    use toml::value::{Table, Value};

    #[test]
    fn config_defaults_to_html_renderer_if_empty() {
        let cfg = Config::default();

        // make sure we haven't got anything in the `output` table
        assert!(cfg.get("output").is_none());

        let got = determine_renderers(&cfg);

        assert_eq!(got.len(), 1);
        assert_eq!(got[0].name(), "html");
    }

    #[test]
    fn add_a_random_renderer_to_the_config() {
        let mut cfg = Config::default();
        cfg.set("output.random", Table::new()).unwrap();

        let got = determine_renderers(&cfg);

        assert_eq!(got.len(), 1);
        assert_eq!(got[0].name(), "random");
    }

    #[test]
    fn add_a_random_renderer_with_custom_command_to_the_config() {
        let mut cfg = Config::default();

        let mut table = Table::new();
        table.insert("command".to_string(), Value::String("false".to_string()));
        cfg.set("output.random", table).unwrap();

        let got = determine_renderers(&cfg);

        assert_eq!(got.len(), 1);
        assert_eq!(got[0].name(), "random");
    }

    #[test]
    fn config_defaults_to_link_preprocessor_if_not_set() {
        let cfg = Config::default();

        // make sure we haven't got anything in the `output` table
        assert!(cfg.build.preprocess.is_none());

        let got = determine_preprocessors(&cfg);

        assert!(got.is_ok());
        assert_eq!(got.as_ref().unwrap().len(), 1);
        assert_eq!(got.as_ref().unwrap()[0].name(), "links");
    }

    #[test]
    fn config_doesnt_default_if_empty() {
        let cfg_str: &'static str = r#"
        [book]
        title = "Some Book"

        [build]
        build-dir = "outputs"
        create-missing = false
        preprocess = []
        "#;

        let cfg = Config::from_str(cfg_str).unwrap();

        // make sure we have something in the `output` table
        assert!(cfg.build.preprocess.is_some());

        let got = determine_preprocessors(&cfg);

        assert!(got.is_ok());
        assert!(got.unwrap().is_empty());
    }

    #[test]
    fn config_complains_if_unimplemented_preprocessor() {
        let cfg_str: &'static str = r#"
        [book]
        title = "Some Book"

        [build]
        build-dir = "outputs"
        create-missing = false
        preprocess = ["random"]
        "#;

        let cfg = Config::from_str(cfg_str).unwrap();

        // make sure we have something in the `output` table
        assert!(cfg.build.preprocess.is_some());

        let got = determine_preprocessors(&cfg);

        assert!(got.is_err());
    }
}
<|endoftext|>


<commit_before>use std::kinds::marker;
use std::ptr;

use error::{LmdbResult, lmdb_result};
use ffi::*;
use transaction::{Transaction, ReadTransaction, WriteTransaction};

/// A handle to an individual database in an environment.
///
/// A database handle denotes the name and parameters of a database in an environment. The database
/// may not exist in the environment (for instance, if the database is opened during a transaction
/// that has not yet committed).
#[deriving(Clone, Copy)]
pub struct Database<'env> {
    dbi: MDB_dbi,
    _marker: marker::ContravariantLifetime<'env>,
}

impl <'env> Database<'env> {

    pub unsafe fn open<'env>(txn: &ReadTransaction<'env>,
                             name: Option<&str>)
                             -> LmdbResult<Database<'env>> {
        let c_name = name.map(|n| n.to_c_str());
        let name_ptr = if let Some(ref c_name) = c_name { c_name.as_ptr() } else { ptr::null() };
        let mut dbi: MDB_dbi = 0;
        try!(lmdb_result(mdb_dbi_open(txn.txn(), name_ptr, 0, &mut dbi)));
        Ok(Database { dbi: dbi,
                      _marker: marker::ContravariantLifetime::<'env> })
    }

    pub unsafe fn create<'env>(txn: &WriteTransaction<'env>,
                               name: Option<&str>,
                               flags: DatabaseFlags)
                               -> LmdbResult<Database<'env>> {
        let c_name = name.map(|n| n.to_c_str());
        let name_ptr = if let Some(ref c_name) = c_name { c_name.as_ptr() } else { ptr::null() };
        let mut dbi: MDB_dbi = 0;
        try!(lmdb_result(mdb_dbi_open(txn.txn(), name_ptr, flags.bits() | MDB_CREATE, &mut dbi)));
        Ok(Database { dbi: dbi,
                      _marker: marker::ContravariantLifetime::<'env> })
    }

    /// Returns the underlying LMDB database handle.
    ///
    /// The caller **must** ensure that the handle is not used after the lifetime of the
    /// environment, or after the database handle has been closed.
    pub fn dbi(&self) -> MDB_dbi {
        self.dbi
    }
}
<commit_msg>remove lifetime shadowing<commit_after>use std::kinds::marker;
use std::ptr;

use error::{LmdbResult, lmdb_result};
use ffi::*;
use transaction::{Transaction, ReadTransaction, WriteTransaction};

/// A handle to an individual database in an environment.
///
/// A database handle denotes the name and parameters of a database in an environment. The database
/// may not exist in the environment (for instance, if the database is opened during a transaction
/// that has not yet committed).
#[deriving(Clone, Copy)]
pub struct Database<'env> {
    dbi: MDB_dbi,
    _marker: marker::ContravariantLifetime<'env>,
}

impl <'env> Database<'env> {

    pub unsafe fn open<'e>(txn: &ReadTransaction<'e>,
                             name: Option<&str>)
                             -> LmdbResult<Database<'e>> {
        let c_name = name.map(|n| n.to_c_str());
        let name_ptr = if let Some(ref c_name) = c_name { c_name.as_ptr() } else { ptr::null() };
        let mut dbi: MDB_dbi = 0;
        try!(lmdb_result(mdb_dbi_open(txn.txn(), name_ptr, 0, &mut dbi)));
        Ok(Database { dbi: dbi,
                      _marker: marker::ContravariantLifetime::<'e> })
    }

    pub unsafe fn create<'e>(txn: &WriteTransaction<'e>,
                               name: Option<&str>,
                               flags: DatabaseFlags)
                               -> LmdbResult<Database<'e>> {
        let c_name = name.map(|n| n.to_c_str());
        let name_ptr = if let Some(ref c_name) = c_name { c_name.as_ptr() } else { ptr::null() };
        let mut dbi: MDB_dbi = 0;
        try!(lmdb_result(mdb_dbi_open(txn.txn(), name_ptr, flags.bits() | MDB_CREATE, &mut dbi)));
        Ok(Database { dbi: dbi,
                      _marker: marker::ContravariantLifetime::<'e> })
    }

    /// Returns the underlying LMDB database handle.
    ///
    /// The caller **must** ensure that the handle is not used after the lifetime of the
    /// environment, or after the database handle has been closed.
    pub fn dbi(&self) -> MDB_dbi {
        self.dbi
    }
}
<|endoftext|>