<commit_before>//! # descriptor
//!
//! Loads the tasks descriptor.

//! It will first load the default descriptor which is defined in cargo-make internally and
//! afterwards tries to find the external descriptor and load it as well.

//! If an extenal descriptor exists, it will be loaded and extend the default descriptor.
//!

#[cfg(test)]
#[path = "./descriptor_test.rs"]
mod descriptor_test;

use crate::command;
use crate::types::{Config, ConfigSection, EnvValue, Extend, ExternalConfig, ModifyConfig, Task};
use crate::version;
use envmnt;
use indexmap::IndexMap;
use std::env;
use std::fs::{canonicalize, File};
use std::io::Read;
use std::path::{Path, PathBuf};
use toml;

fn merge_env(
    base: &mut IndexMap<String, EnvValue>,
    extended: &mut IndexMap<String, EnvValue>,
) -> IndexMap<String, EnvValue> {
    let mut merged = IndexMap::<String, EnvValue>::new();

    for (key, value) in base.iter() {
        let key_str = key.to_string();
        let value_clone = value.clone();
        merged.insert(key_str, value_clone);
    }

    for (key, value) in extended.iter() {
        let key_str = key.to_string();
        let value_clone = value.clone();

        if merged.contains_key(&key_str) {
            let base_value = merged.remove(&key_str).unwrap();

            match (base_value, value_clone.clone()) {
                (
                    EnvValue::Profile(ref base_profile_env),
                    EnvValue::Profile(ref extended_profile_env),
                ) => {
                    let mut base_profile_env_mut = base_profile_env.clone();
                    let mut extended_profile_env_mut = extended_profile_env.clone();

                    let merged_sub_env =
                        merge_env(&mut base_profile_env_mut, &mut extended_profile_env_mut);

                    merged.insert(key_str, EnvValue::Profile(merged_sub_env));
                }
                _ => {
                    merged.insert(key_str, value_clone);
                    ()
                }
            };
        } else {
            merged.insert(key_str, value_clone);
        }
    }

    merged
}

fn merge_tasks(
    base: &mut IndexMap<String, Task>,
    extended: &mut IndexMap<String, Task>,
) -> IndexMap<String, Task> {
    let mut merged = IndexMap::<String, Task>::new();

    for (key, value) in base.iter() {
        let key_str = key.to_string();
        merged.insert(key_str, value.clone());
    }

    for (key, value) in extended.iter() {
        let key_str = key.to_string();
        let mut task = value.clone();

        task = match base.get(key) {
            Some(ref value) => {
                let mut merged_task = Task::new();

                merged_task.extend(value);
                merged_task.extend(&task);

                merged_task
            }
            _ => task,
        };

        merged.insert(key_str, task);
    }

    merged
}

fn run_load_script(external_config: &ExternalConfig) -> bool {
    match external_config.config {
        Some(ref config) => {
            let load_script = config.get_load_script();

            match load_script {
                Some(ref script) => {
                    debug!("Load script found.");

                    command::run_script(script, None, &vec![], true);

                    true
                }
                None => {
                    debug!("No load script defined.");
                    false
                }
            }
        }
        None => {
            debug!("No load script defined.");
            false
        }
    }
}

fn merge_external_configs(config: ExternalConfig, parent_config: ExternalConfig) -> ExternalConfig {
    // merge env
    let mut parent_env = match parent_config.env {
        Some(env) => env,
        None => IndexMap::new(),
    };
    let mut extended_env = match config.env {
        Some(env) => env,
        None => IndexMap::new(),
    };
    let all_env = merge_env(&mut parent_env, &mut extended_env);

    // merge tasks
    let mut parent_tasks = match parent_config.tasks {
        Some(tasks) => tasks,
        None => IndexMap::new(),
    };
    let mut extended_tasks = match config.tasks {
        Some(tasks) => tasks,
        None => IndexMap::new(),
    };
    let all_tasks = merge_tasks(&mut parent_tasks, &mut extended_tasks);

    let mut config_section = ConfigSection::new();
    if parent_config.config.is_some() {
        config_section.extend(&mut parent_config.config.unwrap());
    }
    if config.config.is_some() {
        config_section.extend(&mut config.config.unwrap());
    }

    ExternalConfig {
        extend: None,
        config: Some(config_section),
        env: Some(all_env),
        tasks: Some(all_tasks),
    }
}

fn load_descriptor_extended_makefiles(
    parent_path: &str,
    extend_struct: &Extend,
) -> Result<ExternalConfig, String> {
    match extend_struct {
        Extend::Path(base_file) => load_external_descriptor(parent_path, &base_file, true, false),
        Extend::Options(extend_options) => {
            let force = !extend_options.optional.unwrap_or(false);
            load_external_descriptor(parent_path, &extend_options.path, force, false)
        }
        Extend::List(extend_list) => {
            let mut ordered_list_config = ExternalConfig::new();

            for entry in extend_list.iter() {
                let extend_options = entry.clone();
                let entry_config = load_descriptor_extended_makefiles(
                    parent_path,
                    &Extend::Options(extend_options),
                )?;

                // merge configs
                ordered_list_config = merge_external_configs(entry_config, ordered_list_config);
            }

            Ok(ordered_list_config)
        }
    }
}

/// Ensure the Makefile's min_version, if present, is older than cargo-make's
/// currently running version.
fn check_makefile_minversion(external_descriptor: &str) -> Result<(), String> {
    let value: toml::Value = toml::from_str(&external_descriptor)
        .unwrap_or_else(|error| panic!("Unable to parse external descriptor, {}", error));
    let min_version = value
        .get("config")
        .and_then(|config| config.get("min_version"))
        .and_then(|min_ver| min_ver.as_str());
    if let Some(ref min_version) = min_version {
        if version::is_newer_found(&min_version) {
            return Err(min_version.to_string());
        }
    }
    Ok(())
}

fn load_external_descriptor(
    base_path: &str,
    file_name: &str,
    force: bool,
    set_env: bool,
) -> Result<ExternalConfig, String> {
    debug!(
        "Loading tasks from file: {} base directory: {}",
        &file_name, &base_path
    );

    let file_path = Path::new(base_path).join(file_name);

    if file_path.exists() && file_path.is_file() {
        if set_env {
            let absolute_file_path = match canonicalize(&file_path) {
                Ok(result_path) => result_path,
                _ => file_path.clone(),
            };

            envmnt::set("CARGO_MAKE_MAKEFILE_PATH", &absolute_file_path);
        }

        debug!("Opening file: {:#?}", &file_path);
        let mut file = match File::open(&file_path) {
            Ok(value) => value,
            Err(error) => panic!(
                "Unable to open file, base path: {} file name: {} error: {}",
                base_path, file_name, error
            ),
        };
        let mut external_descriptor = String::new();
        file.read_to_string(&mut external_descriptor).unwrap();

        check_makefile_minversion(&external_descriptor)?;

        let file_config: ExternalConfig = match toml::from_str(&external_descriptor) {
            Ok(value) => value,
            Err(error) => panic!("Unable to parse external descriptor, {}", error),
        };
        debug!("Loaded external config: {:#?}", &file_config);

        run_load_script(&file_config);

        match file_config.extend {
            Some(ref extend_struct) => {
                let parent_path_buf = Path::new(base_path).join(file_name).join("..");
                let parent_path = file_path
                    .parent()
                    .unwrap_or(&parent_path_buf)
                    .to_str()
                    .unwrap_or(".");
                debug!("External config parent path: {}", &parent_path);

                let base_file_config =
                    load_descriptor_extended_makefiles(&parent_path, extend_struct)?;

                Ok(merge_external_configs(
                    file_config.clone(),
                    base_file_config,
                ))
            }
            None => Ok(file_config),
        }
    } else if force {
        error!("Descriptor file: {:#?} not found.", &file_path);
        panic!("Descriptor file: {:#?} not found.", &file_path);
    } else {
        info!("External file not found or is not a file, skipping.");

        Ok(ExternalConfig::new())
    }
}

pub(crate) fn load_internal_descriptors(
    stable: bool,
    experimental: bool,
    modify_config: Option<ModifyConfig>,
) -> Config {
    debug!("Loading base tasks.");

    let base_descriptor = if stable {
        include_str!("Makefile.stable.toml")
    } else {
        include_str!("Makefile.base.toml")
    };

    let mut base_config: Config = match toml::from_str(base_descriptor) {
        Ok(value) => value,
        Err(error) => panic!("Unable to parse base descriptor, {}", error),
    };
    debug!("Loaded base config: {:#?}", &base_config);

    if experimental {
        debug!("Loading experimental tasks.");
        let experimental_descriptor = include_str!("Makefile.beta.toml");

        let experimental_config: Config = match toml::from_str(experimental_descriptor) {
            Ok(value) => value,
            Err(error) => panic!("Unable to parse experimental descriptor, {}", error),
        };
        debug!("Loaded experimental config: {:#?}", &experimental_config);

        let mut base_tasks = base_config.tasks;
        let mut experimental_tasks = experimental_config.tasks;
        let all_tasks = merge_tasks(&mut base_tasks, &mut experimental_tasks);

        base_config.tasks = all_tasks;
    }

    // reset
    envmnt::set("CARGO_MAKE_CORE_TASK_NAMESPACE", "");
    envmnt::set("CARGO_MAKE_CORE_TASK_NAMESPACE_PREFIX", "");

    match modify_config {
        Some(props) => {
            base_config.apply(&props);

            match props.namespace {
                Some(ref namespace) => {
                    let prefix = props.get_namespace_prefix();

                    envmnt::set("CARGO_MAKE_CORE_TASK_NAMESPACE", &namespace);
                    envmnt::set("CARGO_MAKE_CORE_TASK_NAMESPACE_PREFIX", &prefix);
                }
                None => (),
            };
        }
        None => (),
    };

    base_config
}

/// Loads the tasks descriptor.

/// It will first load the default descriptor which is defined in cargo-make internally and
/// afterwards tries to find the external descriptor and load it as well.

/// If an extenal descriptor exists, it will be loaded and extend the default descriptor.
/// If one of the descriptor requires a newer version of cargo-make, returns an error with the
/// minimum version required by the descriptor.
fn load_descriptors(
    file_name: &str,
    force: bool,
    env_map: Option<Vec<String>>,
    stable: bool,
    experimental: bool,
    modify_core_tasks: Option<ModifyConfig>,
) -> Result<Config, String> {
    let default_config = load_internal_descriptors(stable, experimental, modify_core_tasks);

    let mut external_config: ExternalConfig =
        load_external_descriptor(".", file_name, force, true)?;

    external_config = match env::var("CARGO_MAKE_WORKSPACE_MAKEFILE") {
        Ok(workspace_makefile) => {
            let mut pathbuf = PathBuf::from(workspace_makefile);
            match pathbuf.clone().file_name() {
                Some(workspace_file_name) => match workspace_file_name.to_str() {
                    Some(workspace_file_name_str) => {
                        pathbuf.pop();

                        match pathbuf.to_str() {
                            Some(directory) => {
                                let workspace_config = load_external_descriptor(
                                    directory,
                                    workspace_file_name_str,
                                    false,
                                    false,
                                )?;
                                merge_external_configs(external_config, workspace_config)
                            }
                            _ => external_config,
                        }
                    }
                    _ => external_config,
                },
                _ => external_config,
            }
        }
        _ => external_config,
    };

    let mut external_tasks = match external_config.tasks {
        Some(tasks) => tasks,
        None => IndexMap::new(),
    };
    let mut default_tasks = default_config.tasks;

    let mut external_env = match external_config.env {
        Some(env) => env,
        None => IndexMap::new(),
    };
    let mut default_env = default_config.env;

    // merge configs
    let mut all_env = merge_env(&mut default_env, &mut external_env);
    all_env = match env_map {
        Some(values) => {
            let mut cli_env = IndexMap::new();

            for env_pair in &values {
                let env_part: Vec<&str> = env_pair.split('=').collect();
                debug!("Checking env pair: {}", &env_pair);

                if env_part.len() == 2 {
                    cli_env.insert(
                        env_part[0].to_string(),
                        EnvValue::Value(env_part[1].to_string()),
                    );
                }
            }

            merge_env(&mut all_env, &mut cli_env)
        }
        None => all_env,
    };

    let all_tasks = merge_tasks(&mut default_tasks, &mut external_tasks);

    let mut config_section = default_config.config.clone();
    config_section.extend(&mut external_config.config.unwrap_or(ConfigSection::new()));

    let config = Config {
        config: config_section,
        env: all_env,
        tasks: all_tasks,
    };

    debug!("Loaded merged config: {:#?}", &config);

    Ok(config)
}

/// Loads the tasks descriptor.

/// It will first load the default descriptor which is defined in cargo-make internally and
/// afterwards tries to find the external descriptor and load it as well.

/// If an extenal descriptor exists, it will be loaded and extend the default descriptor.

/// If one of the descriptor requires a newer version of cargo-make, returns an error with the
/// minimum version required by the descriptor.
pub(crate) fn load(
    file_name: &str,
    force: bool,
    env_map: Option<Vec<String>>,
    experimental: bool,
) -> Result<Config, String> {
    let mut config =
        load_descriptors(&file_name, force, env_map.clone(), true, experimental, None)?;

    if config.config.skip_core_tasks.unwrap_or(false) {
        config = load_descriptors(&file_name, force, env_map.clone(), false, false, None)?;
    } else {
        let modify_core_tasks = config.config.modify_core_tasks.clone();

        match modify_core_tasks {
            Some(modify_config) => {
                if modify_config.is_modifications_defined() {
                    config = load_descriptors(
                        &file_name,
                        force,
                        env_map.clone(),
                        true,
                        experimental,
                        Some(modify_config),
                    )?;
                }
            }
            None => (),
        };
    }

    Ok(config)
}
<commit_msg>Makefile minver check: don't panic on invalid toml<commit_after>//! # descriptor
//!
//! Loads the tasks descriptor.

//! It will first load the default descriptor which is defined in cargo-make internally and
//! afterwards tries to find the external descriptor and load it as well.

//! If an extenal descriptor exists, it will be loaded and extend the default descriptor.
//!

#[cfg(test)]
#[path = "./descriptor_test.rs"]
mod descriptor_test;

use crate::command;
use crate::types::{Config, ConfigSection, EnvValue, Extend, ExternalConfig, ModifyConfig, Task};
use crate::version;
use envmnt;
use indexmap::IndexMap;
use std::env;
use std::fs::{canonicalize, File};
use std::io::Read;
use std::path::{Path, PathBuf};
use toml;

fn merge_env(
    base: &mut IndexMap<String, EnvValue>,
    extended: &mut IndexMap<String, EnvValue>,
) -> IndexMap<String, EnvValue> {
    let mut merged = IndexMap::<String, EnvValue>::new();

    for (key, value) in base.iter() {
        let key_str = key.to_string();
        let value_clone = value.clone();
        merged.insert(key_str, value_clone);
    }

    for (key, value) in extended.iter() {
        let key_str = key.to_string();
        let value_clone = value.clone();

        if merged.contains_key(&key_str) {
            let base_value = merged.remove(&key_str).unwrap();

            match (base_value, value_clone.clone()) {
                (
                    EnvValue::Profile(ref base_profile_env),
                    EnvValue::Profile(ref extended_profile_env),
                ) => {
                    let mut base_profile_env_mut = base_profile_env.clone();
                    let mut extended_profile_env_mut = extended_profile_env.clone();

                    let merged_sub_env =
                        merge_env(&mut base_profile_env_mut, &mut extended_profile_env_mut);

                    merged.insert(key_str, EnvValue::Profile(merged_sub_env));
                }
                _ => {
                    merged.insert(key_str, value_clone);
                    ()
                }
            };
        } else {
            merged.insert(key_str, value_clone);
        }
    }

    merged
}

fn merge_tasks(
    base: &mut IndexMap<String, Task>,
    extended: &mut IndexMap<String, Task>,
) -> IndexMap<String, Task> {
    let mut merged = IndexMap::<String, Task>::new();

    for (key, value) in base.iter() {
        let key_str = key.to_string();
        merged.insert(key_str, value.clone());
    }

    for (key, value) in extended.iter() {
        let key_str = key.to_string();
        let mut task = value.clone();

        task = match base.get(key) {
            Some(ref value) => {
                let mut merged_task = Task::new();

                merged_task.extend(value);
                merged_task.extend(&task);

                merged_task
            }
            _ => task,
        };

        merged.insert(key_str, task);
    }

    merged
}

fn run_load_script(external_config: &ExternalConfig) -> bool {
    match external_config.config {
        Some(ref config) => {
            let load_script = config.get_load_script();

            match load_script {
                Some(ref script) => {
                    debug!("Load script found.");

                    command::run_script(script, None, &vec![], true);

                    true
                }
                None => {
                    debug!("No load script defined.");
                    false
                }
            }
        }
        None => {
            debug!("No load script defined.");
            false
        }
    }
}

fn merge_external_configs(config: ExternalConfig, parent_config: ExternalConfig) -> ExternalConfig {
    // merge env
    let mut parent_env = match parent_config.env {
        Some(env) => env,
        None => IndexMap::new(),
    };
    let mut extended_env = match config.env {
        Some(env) => env,
        None => IndexMap::new(),
    };
    let all_env = merge_env(&mut parent_env, &mut extended_env);

    // merge tasks
    let mut parent_tasks = match parent_config.tasks {
        Some(tasks) => tasks,
        None => IndexMap::new(),
    };
    let mut extended_tasks = match config.tasks {
        Some(tasks) => tasks,
        None => IndexMap::new(),
    };
    let all_tasks = merge_tasks(&mut parent_tasks, &mut extended_tasks);

    let mut config_section = ConfigSection::new();
    if parent_config.config.is_some() {
        config_section.extend(&mut parent_config.config.unwrap());
    }
    if config.config.is_some() {
        config_section.extend(&mut config.config.unwrap());
    }

    ExternalConfig {
        extend: None,
        config: Some(config_section),
        env: Some(all_env),
        tasks: Some(all_tasks),
    }
}

fn load_descriptor_extended_makefiles(
    parent_path: &str,
    extend_struct: &Extend,
) -> Result<ExternalConfig, String> {
    match extend_struct {
        Extend::Path(base_file) => load_external_descriptor(parent_path, &base_file, true, false),
        Extend::Options(extend_options) => {
            let force = !extend_options.optional.unwrap_or(false);
            load_external_descriptor(parent_path, &extend_options.path, force, false)
        }
        Extend::List(extend_list) => {
            let mut ordered_list_config = ExternalConfig::new();

            for entry in extend_list.iter() {
                let extend_options = entry.clone();
                let entry_config = load_descriptor_extended_makefiles(
                    parent_path,
                    &Extend::Options(extend_options),
                )?;

                // merge configs
                ordered_list_config = merge_external_configs(entry_config, ordered_list_config);
            }

            Ok(ordered_list_config)
        }
    }
}

/// Ensure the Makefile's min_version, if present, is older than cargo-make's
/// currently running version.
fn check_makefile_minversion(external_descriptor: &str) -> Result<(), String> {
    let value: toml::Value = match toml::from_str(&external_descriptor) {
        Ok(value) => value,
        // If there's an error parsing the file, let the caller function figure
        // it out
        Err(_) => return Ok(())
    };

    let min_version = value
        .get("config")
        .and_then(|config| config.get("min_version"))
        .and_then(|min_ver| min_ver.as_str());
    if let Some(ref min_version) = min_version {
        if version::is_newer_found(&min_version) {
            return Err(min_version.to_string());
        }
    }
    Ok(())
}

fn load_external_descriptor(
    base_path: &str,
    file_name: &str,
    force: bool,
    set_env: bool,
) -> Result<ExternalConfig, String> {
    debug!(
        "Loading tasks from file: {} base directory: {}",
        &file_name, &base_path
    );

    let file_path = Path::new(base_path).join(file_name);

    if file_path.exists() && file_path.is_file() {
        if set_env {
            let absolute_file_path = match canonicalize(&file_path) {
                Ok(result_path) => result_path,
                _ => file_path.clone(),
            };

            envmnt::set("CARGO_MAKE_MAKEFILE_PATH", &absolute_file_path);
        }

        debug!("Opening file: {:#?}", &file_path);
        let mut file = match File::open(&file_path) {
            Ok(value) => value,
            Err(error) => panic!(
                "Unable to open file, base path: {} file name: {} error: {}",
                base_path, file_name, error
            ),
        };
        let mut external_descriptor = String::new();
        file.read_to_string(&mut external_descriptor).unwrap();

        check_makefile_minversion(&external_descriptor)?;

        let file_config: ExternalConfig = match toml::from_str(&external_descriptor) {
            Ok(value) => value,
            Err(error) => panic!("Unable to parse external descriptor, {}", error),
        };
        debug!("Loaded external config: {:#?}", &file_config);

        run_load_script(&file_config);

        match file_config.extend {
            Some(ref extend_struct) => {
                let parent_path_buf = Path::new(base_path).join(file_name).join("..");
                let parent_path = file_path
                    .parent()
                    .unwrap_or(&parent_path_buf)
                    .to_str()
                    .unwrap_or(".");
                debug!("External config parent path: {}", &parent_path);

                let base_file_config =
                    load_descriptor_extended_makefiles(&parent_path, extend_struct)?;

                Ok(merge_external_configs(
                    file_config.clone(),
                    base_file_config,
                ))
            }
            None => Ok(file_config),
        }
    } else if force {
        error!("Descriptor file: {:#?} not found.", &file_path);
        panic!("Descriptor file: {:#?} not found.", &file_path);
    } else {
        info!("External file not found or is not a file, skipping.");

        Ok(ExternalConfig::new())
    }
}

pub(crate) fn load_internal_descriptors(
    stable: bool,
    experimental: bool,
    modify_config: Option<ModifyConfig>,
) -> Config {
    debug!("Loading base tasks.");

    let base_descriptor = if stable {
        include_str!("Makefile.stable.toml")
    } else {
        include_str!("Makefile.base.toml")
    };

    let mut base_config: Config = match toml::from_str(base_descriptor) {
        Ok(value) => value,
        Err(error) => panic!("Unable to parse base descriptor, {}", error),
    };
    debug!("Loaded base config: {:#?}", &base_config);

    if experimental {
        debug!("Loading experimental tasks.");
        let experimental_descriptor = include_str!("Makefile.beta.toml");

        let experimental_config: Config = match toml::from_str(experimental_descriptor) {
            Ok(value) => value,
            Err(error) => panic!("Unable to parse experimental descriptor, {}", error),
        };
        debug!("Loaded experimental config: {:#?}", &experimental_config);

        let mut base_tasks = base_config.tasks;
        let mut experimental_tasks = experimental_config.tasks;
        let all_tasks = merge_tasks(&mut base_tasks, &mut experimental_tasks);

        base_config.tasks = all_tasks;
    }

    // reset
    envmnt::set("CARGO_MAKE_CORE_TASK_NAMESPACE", "");
    envmnt::set("CARGO_MAKE_CORE_TASK_NAMESPACE_PREFIX", "");

    match modify_config {
        Some(props) => {
            base_config.apply(&props);

            match props.namespace {
                Some(ref namespace) => {
                    let prefix = props.get_namespace_prefix();

                    envmnt::set("CARGO_MAKE_CORE_TASK_NAMESPACE", &namespace);
                    envmnt::set("CARGO_MAKE_CORE_TASK_NAMESPACE_PREFIX", &prefix);
                }
                None => (),
            };
        }
        None => (),
    };

    base_config
}

/// Loads the tasks descriptor.

/// It will first load the default descriptor which is defined in cargo-make internally and
/// afterwards tries to find the external descriptor and load it as well.

/// If an extenal descriptor exists, it will be loaded and extend the default descriptor.
/// If one of the descriptor requires a newer version of cargo-make, returns an error with the
/// minimum version required by the descriptor.
fn load_descriptors(
    file_name: &str,
    force: bool,
    env_map: Option<Vec<String>>,
    stable: bool,
    experimental: bool,
    modify_core_tasks: Option<ModifyConfig>,
) -> Result<Config, String> {
    let default_config = load_internal_descriptors(stable, experimental, modify_core_tasks);

    let mut external_config: ExternalConfig =
        load_external_descriptor(".", file_name, force, true)?;

    external_config = match env::var("CARGO_MAKE_WORKSPACE_MAKEFILE") {
        Ok(workspace_makefile) => {
            let mut pathbuf = PathBuf::from(workspace_makefile);
            match pathbuf.clone().file_name() {
                Some(workspace_file_name) => match workspace_file_name.to_str() {
                    Some(workspace_file_name_str) => {
                        pathbuf.pop();

                        match pathbuf.to_str() {
                            Some(directory) => {
                                let workspace_config = load_external_descriptor(
                                    directory,
                                    workspace_file_name_str,
                                    false,
                                    false,
                                )?;
                                merge_external_configs(external_config, workspace_config)
                            }
                            _ => external_config,
                        }
                    }
                    _ => external_config,
                },
                _ => external_config,
            }
        }
        _ => external_config,
    };

    let mut external_tasks = match external_config.tasks {
        Some(tasks) => tasks,
        None => IndexMap::new(),
    };
    let mut default_tasks = default_config.tasks;

    let mut external_env = match external_config.env {
        Some(env) => env,
        None => IndexMap::new(),
    };
    let mut default_env = default_config.env;

    // merge configs
    let mut all_env = merge_env(&mut default_env, &mut external_env);
    all_env = match env_map {
        Some(values) => {
            let mut cli_env = IndexMap::new();

            for env_pair in &values {
                let env_part: Vec<&str> = env_pair.split('=').collect();
                debug!("Checking env pair: {}", &env_pair);

                if env_part.len() == 2 {
                    cli_env.insert(
                        env_part[0].to_string(),
                        EnvValue::Value(env_part[1].to_string()),
                    );
                }
            }

            merge_env(&mut all_env, &mut cli_env)
        }
        None => all_env,
    };

    let all_tasks = merge_tasks(&mut default_tasks, &mut external_tasks);

    let mut config_section = default_config.config.clone();
    config_section.extend(&mut external_config.config.unwrap_or(ConfigSection::new()));

    let config = Config {
        config: config_section,
        env: all_env,
        tasks: all_tasks,
    };

    debug!("Loaded merged config: {:#?}", &config);

    Ok(config)
}

/// Loads the tasks descriptor.

/// It will first load the default descriptor which is defined in cargo-make internally and
/// afterwards tries to find the external descriptor and load it as well.

/// If an extenal descriptor exists, it will be loaded and extend the default descriptor.

/// If one of the descriptor requires a newer version of cargo-make, returns an error with the
/// minimum version required by the descriptor.
pub(crate) fn load(
    file_name: &str,
    force: bool,
    env_map: Option<Vec<String>>,
    experimental: bool,
) -> Result<Config, String> {
    let mut config =
        load_descriptors(&file_name, force, env_map.clone(), true, experimental, None)?;

    if config.config.skip_core_tasks.unwrap_or(false) {
        config = load_descriptors(&file_name, force, env_map.clone(), false, false, None)?;
    } else {
        let modify_core_tasks = config.config.modify_core_tasks.clone();

        match modify_core_tasks {
            Some(modify_config) => {
                if modify_config.is_modifications_defined() {
                    config = load_descriptors(
                        &file_name,
                        force,
                        env_map.clone(),
                        true,
                        experimental,
                        Some(modify_config),
                    )?;
                }
            }
            None => (),
        };
    }

    Ok(config)
}
<|endoftext|>


<commit_before>//! Managed vectors

use ptr::addr_of;

/// Code for dealing with @-vectors. This is pretty incomplete, and
/// contains a bunch of duplication from the code for ~-vectors.

#[abi = "cdecl"]
extern mod rustrt {
    #[legacy_exports];
    fn vec_reserve_shared_actual(++t: *sys::TypeDesc,
                                 ++v: **vec::raw::VecRepr,
                                 ++n: libc::size_t);
}

#[abi = "rust-intrinsic"]
extern mod rusti {
    #[legacy_exports];
    fn move_val_init<T>(&dst: T, -src: T);
}

/// Returns the number of elements the vector can hold without reallocating
#[inline(always)]
pub pure fn capacity<T>(&&v: @[const T]) -> uint {
    unsafe {
        let repr: **raw::VecRepr =
            ::cast::reinterpret_cast(&addr_of(v));
        (**repr).unboxed.alloc / sys::size_of::<T>()
    }
}

/**
 * Builds a vector by calling a provided function with an argument
 * function that pushes an element to the back of a vector.
 * This version takes an initial size for the vector.
 *
 * # Arguments
 *
 * * size - An initial size of the vector to reserve
 * * builder - A function that will construct the vector. It recieves
 *             as an argument a function that will push an element
 *             onto the vector being constructed.
 */
#[inline(always)]
pub pure fn build_sized<A>(size: uint,
                           builder: fn(push: pure fn(+v: A))) -> @[A] {
    let mut vec = @[];
    unsafe { raw::reserve(vec, size); }
    builder(|+x| unsafe { raw::push(vec, move x) });
    return vec;
}

/**
 * Builds a vector by calling a provided function with an argument
 * function that pushes an element to the back of a vector.
 *
 * # Arguments
 *
 * * builder - A function that will construct the vector. It recieves
 *             as an argument a function that will push an element
 *             onto the vector being constructed.
 */
#[inline(always)]
pub pure fn build<A>(builder: fn(push: pure fn(+v: A))) -> @[A] {
    build_sized(4, builder)
}

/**
 * Builds a vector by calling a provided function with an argument
 * function that pushes an element to the back of a vector.
 * This version takes an initial size for the vector.
 *
 * # Arguments
 *
 * * size - An option, maybe containing initial size of the vector to reserve
 * * builder - A function that will construct the vector. It recieves
 *             as an argument a function that will push an element
 *             onto the vector being constructed.
 */
#[inline(always)]
pub pure fn build_sized_opt<A>(size: Option<uint>,
                           builder: fn(push: pure fn(+v: A))) -> @[A] {
    build_sized(size.get_default(4), builder)
}

// Appending
#[inline(always)]
pub pure fn append<T: Copy>(lhs: @[T], rhs: &[const T]) -> @[T] {
    do build_sized(lhs.len() + rhs.len()) |push| {
        for vec::each(lhs) |x| { push(*x); }
        for uint::range(0, rhs.len()) |i| { push(rhs[i]); }
    }
}

/// Apply a function to each element of a vector and return the results
pub pure fn map<T, U>(v: &[T], f: fn(T) -> U) -> @[U] {
    do build_sized(v.len()) |push| {
        for vec::each(v) |elem| {
            push(f(*elem));
        }
    }
}

/**
 * Creates and initializes an immutable vector.
 *
 * Creates an immutable vector of size `n_elts` and initializes the elements
 * to the value returned by the function `op`.
 */
pub pure fn from_fn<T>(n_elts: uint, op: iter::InitOp<T>) -> @[T] {
    do build_sized(n_elts) |push| {
        let mut i: uint = 0u;
        while i < n_elts { push(op(i)); i += 1u; }
    }
}

/**
 * Creates and initializes an immutable vector.
 *
 * Creates an immutable vector of size `n_elts` and initializes the elements
 * to the value `t`.
 */
pub pure fn from_elem<T: Copy>(n_elts: uint, t: T) -> @[T] {
    do build_sized(n_elts) |push| {
        let mut i: uint = 0u;
        while i < n_elts { push(t); i += 1u; }
    }
}

#[cfg(notest)]
mod traits {
    #[legacy_exports];
    impl<T: Copy> @[T] : Add<&[const T],@[T]> {
        #[inline(always)]
        pure fn add(rhs: & &[const T]) -> @[T] {
            append(self, (*rhs))
        }
    }
}

#[cfg(test)]
pub mod traits {
    #[legacy_exports];}

pub mod raw {
    pub type VecRepr = vec::raw::VecRepr;
    pub type SliceRepr = vec::raw::SliceRepr;

    /**
     * Sets the length of a vector
     *
     * This will explicitly set the size of the vector, without actually
     * modifing its buffers, so it is up to the caller to ensure that
     * the vector is actually the specified size.
     */
    #[inline(always)]
    pub unsafe fn set_len<T>(&&v: @[const T], new_len: uint) {
        let repr: **VecRepr = ::cast::reinterpret_cast(&addr_of(v));
        (**repr).unboxed.fill = new_len * sys::size_of::<T>();
    }

    #[inline(always)]
    pub unsafe fn push<T>(&v: @[const T], +initval: T) {
        let repr: **VecRepr = ::cast::reinterpret_cast(&addr_of(v));
        let fill = (**repr).unboxed.fill;
        if (**repr).unboxed.alloc > fill {
            push_fast(v, move initval);
        }
        else {
            push_slow(v, move initval);
        }
    }
    // This doesn't bother to make sure we have space.
    #[inline(always)] // really pretty please
    pub unsafe fn push_fast<T>(&v: @[const T], +initval: T) {
        let repr: **VecRepr = ::cast::reinterpret_cast(&addr_of(v));
        let fill = (**repr).unboxed.fill;
        (**repr).unboxed.fill += sys::size_of::<T>();
        let p = ptr::addr_of((**repr).unboxed.data);
        let p = ptr::offset(p, fill) as *mut T;
        rusti::move_val_init(*p, move initval);
    }

    pub unsafe fn push_slow<T>(&v: @[const T], +initval: T) {
        reserve_at_least(v, v.len() + 1u);
        push_fast(v, move initval);
    }

    /**
     * Reserves capacity for exactly `n` elements in the given vector.
     *
     * If the capacity for `v` is already equal to or greater than the
     * requested capacity, then no action is taken.
     *
     * # Arguments
     *
     * * v - A vector
     * * n - The number of elements to reserve space for
     */
    pub unsafe fn reserve<T>(&v: @[const T], n: uint) {
        // Only make the (slow) call into the runtime if we have to
        if capacity(v) < n {
            let ptr = addr_of(v) as **VecRepr;
            rustrt::vec_reserve_shared_actual(sys::get_type_desc::<T>(),
                                              ptr, n as libc::size_t);
        }
    }

    /**
     * Reserves capacity for at least `n` elements in the given vector.
     *
     * This function will over-allocate in order to amortize the
     * allocation costs in scenarios where the caller may need to
     * repeatedly reserve additional space.
     *
     * If the capacity for `v` is already equal to or greater than the
     * requested capacity, then no action is taken.
     *
     * # Arguments
     *
     * * v - A vector
     * * n - The number of elements to reserve space for
     */
    pub unsafe fn reserve_at_least<T>(&v: @[const T], n: uint) {
        reserve(v, uint::next_power_of_two(n));
    }

}

#[test]
pub fn test() {
    // Some code that could use that, then:
    fn seq_range(lo: uint, hi: uint) -> @[uint] {
        do build |push| {
            for uint::range(lo, hi) |i| {
                push(i);
            }
        }
    }

    assert seq_range(10, 15) == @[10, 11, 12, 13, 14];
    assert from_fn(5, |x| x+1) == @[1, 2, 3, 4, 5];
    assert from_elem(5, 3.14) == @[3.14, 3.14, 3.14, 3.14, 3.14];
}

#[test]
pub fn append_test() {
    assert @[1,2,3] + @[4,5,6] == @[1,2,3,4,5,6];
}

<commit_msg>Comment out at_vec test that fails<commit_after>//! Managed vectors

use ptr::addr_of;

/// Code for dealing with @-vectors. This is pretty incomplete, and
/// contains a bunch of duplication from the code for ~-vectors.

#[abi = "cdecl"]
extern mod rustrt {
    #[legacy_exports];
    fn vec_reserve_shared_actual(++t: *sys::TypeDesc,
                                 ++v: **vec::raw::VecRepr,
                                 ++n: libc::size_t);
}

#[abi = "rust-intrinsic"]
extern mod rusti {
    #[legacy_exports];
    fn move_val_init<T>(&dst: T, -src: T);
}

/// Returns the number of elements the vector can hold without reallocating
#[inline(always)]
pub pure fn capacity<T>(&&v: @[const T]) -> uint {
    unsafe {
        let repr: **raw::VecRepr =
            ::cast::reinterpret_cast(&addr_of(v));
        (**repr).unboxed.alloc / sys::size_of::<T>()
    }
}

/**
 * Builds a vector by calling a provided function with an argument
 * function that pushes an element to the back of a vector.
 * This version takes an initial size for the vector.
 *
 * # Arguments
 *
 * * size - An initial size of the vector to reserve
 * * builder - A function that will construct the vector. It recieves
 *             as an argument a function that will push an element
 *             onto the vector being constructed.
 */
#[inline(always)]
pub pure fn build_sized<A>(size: uint,
                           builder: fn(push: pure fn(+v: A))) -> @[A] {
    let mut vec = @[];
    unsafe { raw::reserve(vec, size); }
    builder(|+x| unsafe { raw::push(vec, move x) });
    return vec;
}

/**
 * Builds a vector by calling a provided function with an argument
 * function that pushes an element to the back of a vector.
 *
 * # Arguments
 *
 * * builder - A function that will construct the vector. It recieves
 *             as an argument a function that will push an element
 *             onto the vector being constructed.
 */
#[inline(always)]
pub pure fn build<A>(builder: fn(push: pure fn(+v: A))) -> @[A] {
    build_sized(4, builder)
}

/**
 * Builds a vector by calling a provided function with an argument
 * function that pushes an element to the back of a vector.
 * This version takes an initial size for the vector.
 *
 * # Arguments
 *
 * * size - An option, maybe containing initial size of the vector to reserve
 * * builder - A function that will construct the vector. It recieves
 *             as an argument a function that will push an element
 *             onto the vector being constructed.
 */
#[inline(always)]
pub pure fn build_sized_opt<A>(size: Option<uint>,
                           builder: fn(push: pure fn(+v: A))) -> @[A] {
    build_sized(size.get_default(4), builder)
}

// Appending
#[inline(always)]
pub pure fn append<T: Copy>(lhs: @[T], rhs: &[const T]) -> @[T] {
    do build_sized(lhs.len() + rhs.len()) |push| {
        for vec::each(lhs) |x| { push(*x); }
        for uint::range(0, rhs.len()) |i| { push(rhs[i]); }
    }
}

/// Apply a function to each element of a vector and return the results
pub pure fn map<T, U>(v: &[T], f: fn(T) -> U) -> @[U] {
    do build_sized(v.len()) |push| {
        for vec::each(v) |elem| {
            push(f(*elem));
        }
    }
}

/**
 * Creates and initializes an immutable vector.
 *
 * Creates an immutable vector of size `n_elts` and initializes the elements
 * to the value returned by the function `op`.
 */
pub pure fn from_fn<T>(n_elts: uint, op: iter::InitOp<T>) -> @[T] {
    do build_sized(n_elts) |push| {
        let mut i: uint = 0u;
        while i < n_elts { push(op(i)); i += 1u; }
    }
}

/**
 * Creates and initializes an immutable vector.
 *
 * Creates an immutable vector of size `n_elts` and initializes the elements
 * to the value `t`.
 */
pub pure fn from_elem<T: Copy>(n_elts: uint, t: T) -> @[T] {
    do build_sized(n_elts) |push| {
        let mut i: uint = 0u;
        while i < n_elts { push(t); i += 1u; }
    }
}

#[cfg(notest)]
mod traits {
    #[legacy_exports];
    impl<T: Copy> @[T] : Add<&[const T],@[T]> {
        #[inline(always)]
        pure fn add(rhs: & &[const T]) -> @[T] {
            append(self, (*rhs))
        }
    }
}

#[cfg(test)]
pub mod traits {
    #[legacy_exports];}

pub mod raw {
    pub type VecRepr = vec::raw::VecRepr;
    pub type SliceRepr = vec::raw::SliceRepr;

    /**
     * Sets the length of a vector
     *
     * This will explicitly set the size of the vector, without actually
     * modifing its buffers, so it is up to the caller to ensure that
     * the vector is actually the specified size.
     */
    #[inline(always)]
    pub unsafe fn set_len<T>(&&v: @[const T], new_len: uint) {
        let repr: **VecRepr = ::cast::reinterpret_cast(&addr_of(v));
        (**repr).unboxed.fill = new_len * sys::size_of::<T>();
    }

    #[inline(always)]
    pub unsafe fn push<T>(&v: @[const T], +initval: T) {
        let repr: **VecRepr = ::cast::reinterpret_cast(&addr_of(v));
        let fill = (**repr).unboxed.fill;
        if (**repr).unboxed.alloc > fill {
            push_fast(v, move initval);
        }
        else {
            push_slow(v, move initval);
        }
    }
    // This doesn't bother to make sure we have space.
    #[inline(always)] // really pretty please
    pub unsafe fn push_fast<T>(&v: @[const T], +initval: T) {
        let repr: **VecRepr = ::cast::reinterpret_cast(&addr_of(v));
        let fill = (**repr).unboxed.fill;
        (**repr).unboxed.fill += sys::size_of::<T>();
        let p = ptr::addr_of((**repr).unboxed.data);
        let p = ptr::offset(p, fill) as *mut T;
        rusti::move_val_init(*p, move initval);
    }

    pub unsafe fn push_slow<T>(&v: @[const T], +initval: T) {
        reserve_at_least(v, v.len() + 1u);
        push_fast(v, move initval);
    }

    /**
     * Reserves capacity for exactly `n` elements in the given vector.
     *
     * If the capacity for `v` is already equal to or greater than the
     * requested capacity, then no action is taken.
     *
     * # Arguments
     *
     * * v - A vector
     * * n - The number of elements to reserve space for
     */
    pub unsafe fn reserve<T>(&v: @[const T], n: uint) {
        // Only make the (slow) call into the runtime if we have to
        if capacity(v) < n {
            let ptr = addr_of(v) as **VecRepr;
            rustrt::vec_reserve_shared_actual(sys::get_type_desc::<T>(),
                                              ptr, n as libc::size_t);
        }
    }

    /**
     * Reserves capacity for at least `n` elements in the given vector.
     *
     * This function will over-allocate in order to amortize the
     * allocation costs in scenarios where the caller may need to
     * repeatedly reserve additional space.
     *
     * If the capacity for `v` is already equal to or greater than the
     * requested capacity, then no action is taken.
     *
     * # Arguments
     *
     * * v - A vector
     * * n - The number of elements to reserve space for
     */
    pub unsafe fn reserve_at_least<T>(&v: @[const T], n: uint) {
        reserve(v, uint::next_power_of_two(n));
    }

}

#[test]
pub fn test() {
    // Some code that could use that, then:
    fn seq_range(lo: uint, hi: uint) -> @[uint] {
        do build |push| {
            for uint::range(lo, hi) |i| {
                push(i);
            }
        }
    }

    assert seq_range(10, 15) == @[10, 11, 12, 13, 14];
    assert from_fn(5, |x| x+1) == @[1, 2, 3, 4, 5];
    assert from_elem(5, 3.14) == @[3.14, 3.14, 3.14, 3.14, 3.14];
}

/*
#[test]
pub fn append_test() {
    assert @[1,2,3] + @[4,5,6] == @[1,2,3,4,5,6];
}
*/
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A module for working with borrowed data.
//!
//! # The `BorrowFrom` traits
//!
//! In general, there may be several ways to "borrow" a piece of data.  The
//! typical ways of borrowing a type `T` are `&T` (a shared borrow) and `&mut T`
//! (a mutable borrow). But types like `Vec<T>` provide additional kinds of
//! borrows: the borrowed slices `&[T]` and `&mut [T]`.
//!
//! When writing generic code, it is often desirable to abstract over all ways
//! of borrowing data from a given type. That is the role of the `BorrowFrom`
//! trait: if `T: BorrowFrom<U>`, then `&T` can be borrowed from `&U`.  A given
//! type can be borrowed as multiple different types. In particular, `Vec<T>:
//! BorrowFrom<Vec<T>>` and `[T]: BorrowFrom<Vec<T>>`.
//!
//! # The `ToOwned` trait
//!
//! Some types make it possible to go from borrowed to owned, usually by
//! implementing the `Clone` trait. But `Clone` works only for going from `&T`
//! to `T`. The `ToOwned` trait generalizes `Clone` to construct owned data
//! from any borrow of a given type.
//!
//! # The `Cow` (clone-on-write) type
//!
//! The type `Cow` is a smart pointer providing clone-on-write functionality: it
//! can enclose and provide immutable access to borrowed data, and clone the
//! data lazily when mutation or ownership is required. The type is designed to
//! work with general borrowed data via the `BorrowFrom` trait.
//!
//! `Cow` implements both `Deref`, which means that you can call
//! non-mutating methods directly on the data it encloses. If mutation
//! is desired, `to_mut` will obtain a mutable references to an owned
//! value, cloning if necessary.

#![unstable(feature = "core",
            reason = "recently added as part of collections reform")]

use clone::Clone;
use cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
use fmt;
use marker::Sized;
use ops::Deref;
use option::Option;
use self::Cow::*;

/// A trait for borrowing data.
#[old_orphan_check]
pub trait BorrowFrom<Owned: ?Sized> {
    /// Immutably borrow from an owned value.
    fn borrow_from(owned: &Owned) -> &Self;
}

/// A trait for mutably borrowing data.
#[old_orphan_check]
pub trait BorrowFromMut<Owned: ?Sized> : BorrowFrom<Owned> {
    /// Mutably borrow from an owned value.
    fn borrow_from_mut(owned: &mut Owned) -> &mut Self;
}

impl<T: ?Sized> BorrowFrom<T> for T {
    fn borrow_from(owned: &T) -> &T { owned }
}

impl<T: ?Sized> BorrowFromMut<T> for T {
    fn borrow_from_mut(owned: &mut T) -> &mut T { owned }
}

impl<'a, T: ?Sized> BorrowFrom<&'a T> for T {
    fn borrow_from<'b>(owned: &'b &'a T) -> &'b T { &**owned }
}

impl<'a, T: ?Sized> BorrowFrom<&'a mut T> for T {
    fn borrow_from<'b>(owned: &'b &'a mut T) -> &'b T { &**owned }
}

impl<'a, T: ?Sized> BorrowFromMut<&'a mut T> for T {
    fn borrow_from_mut<'b>(owned: &'b mut &'a mut T) -> &'b mut T { &mut **owned }
}

impl<'a, T, B: ?Sized> BorrowFrom<Cow<'a, T, B>> for B where B: ToOwned<T> {
    fn borrow_from<'b>(owned: &'b Cow<'a, T, B>) -> &'b B {
        &**owned
    }
}

/// Trait for moving into a `Cow`
#[old_orphan_check]
pub trait IntoCow<'a, T, B: ?Sized> {
    /// Moves `self` into `Cow`
    fn into_cow(self) -> Cow<'a, T, B>;
}

impl<'a, T, B: ?Sized> IntoCow<'a, T, B> for Cow<'a, T, B> where B: ToOwned<T> {
    fn into_cow(self) -> Cow<'a, T, B> {
        self
    }
}

/// A generalization of Clone to borrowed data.
#[old_orphan_check]
pub trait ToOwned<Owned>: BorrowFrom<Owned> {
    /// Create owned data from borrowed data, usually by copying.
    fn to_owned(&self) -> Owned;
}

impl<T> ToOwned<T> for T where T: Clone {
    fn to_owned(&self) -> T { self.clone() }
}

/// A clone-on-write smart pointer.
///
/// # Example
///
/// ```rust
/// use std::borrow::Cow;
///
/// fn abs_all(input: &mut Cow<Vec<int>, [int]>) {
///     for i in 0..input.len() {
///         let v = input[i];
///         if v < 0 {
///             // clones into a vector the first time (if not already owned)
///             input.to_mut()[i] = -v;
///         }
///     }
/// }
/// ```
pub enum Cow<'a, T, B: ?Sized + 'a> where B: ToOwned<T> {
    /// Borrowed data.
    Borrowed(&'a B),

    /// Owned data.
    Owned(T)
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T, B: ?Sized> Clone for Cow<'a, T, B> where B: ToOwned<T> {
    fn clone(&self) -> Cow<'a, T, B> {
        match *self {
            Borrowed(b) => Borrowed(b),
            Owned(ref o) => {
                let b: &B = BorrowFrom::borrow_from(o);
                Owned(b.to_owned())
            },
        }
    }
}

impl<'a, T, B: ?Sized> Cow<'a, T, B> where B: ToOwned<T> {
    /// Acquire a mutable reference to the owned form of the data.
    ///
    /// Copies the data if it is not already owned.
    pub fn to_mut(&mut self) -> &mut T {
        match *self {
            Borrowed(borrowed) => {
                *self = Owned(borrowed.to_owned());
                self.to_mut()
            }
            Owned(ref mut owned) => owned
        }
    }

    /// Extract the owned data.
    ///
    /// Copies the data if it is not already owned.
    pub fn into_owned(self) -> T {
        match self {
            Borrowed(borrowed) => borrowed.to_owned(),
            Owned(owned) => owned
        }
    }

    /// Returns true if this `Cow` wraps a borrowed value
    pub fn is_borrowed(&self) -> bool {
        match *self {
            Borrowed(_) => true,
            _ => false,
        }
    }

    /// Returns true if this `Cow` wraps an owned value
    pub fn is_owned(&self) -> bool {
        match *self {
            Owned(_) => true,
            _ => false,
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T, B: ?Sized> Deref for Cow<'a, T, B> where B: ToOwned<T>  {
    type Target = B;

    fn deref(&self) -> &B {
        match *self {
            Borrowed(borrowed) => borrowed,
            Owned(ref owned) => BorrowFrom::borrow_from(owned)
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T, B: ?Sized> Eq for Cow<'a, T, B> where B: Eq + ToOwned<T> {}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T, B: ?Sized> Ord for Cow<'a, T, B> where B: Ord + ToOwned<T> {
    #[inline]
    fn cmp(&self, other: &Cow<'a, T, B>) -> Ordering {
        Ord::cmp(&**self, &**other)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, 'b, T, U, B: ?Sized, C: ?Sized> PartialEq<Cow<'b, U, C>> for Cow<'a, T, B> where
    B: PartialEq<C> + ToOwned<T>,
    C: ToOwned<U>,
{
    #[inline]
    fn eq(&self, other: &Cow<'b, U, C>) -> bool {
        PartialEq::eq(&**self, &**other)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T, B: ?Sized> PartialOrd for Cow<'a, T, B> where B: PartialOrd + ToOwned<T> {
    #[inline]
    fn partial_cmp(&self, other: &Cow<'a, T, B>) -> Option<Ordering> {
        PartialOrd::partial_cmp(&**self, &**other)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T, B: ?Sized> fmt::Debug for Cow<'a, T, B> where
    B: fmt::Debug + ToOwned<T>,
    T: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Borrowed(ref b) => fmt::Debug::fmt(b, f),
            Owned(ref o) => fmt::Debug::fmt(o, f),
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T, B: ?Sized> fmt::Display for Cow<'a, T, B> where
    B: fmt::Display + ToOwned<T>,
    T: fmt::Display,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Borrowed(ref b) => fmt::Display::fmt(b, f),
            Owned(ref o) => fmt::Display::fmt(o, f),
        }
    }
}
<commit_msg>Audit `core::borrow` for use of `int/uint`: use `i32` in doc example.<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A module for working with borrowed data.
//!
//! # The `BorrowFrom` traits
//!
//! In general, there may be several ways to "borrow" a piece of data.  The
//! typical ways of borrowing a type `T` are `&T` (a shared borrow) and `&mut T`
//! (a mutable borrow). But types like `Vec<T>` provide additional kinds of
//! borrows: the borrowed slices `&[T]` and `&mut [T]`.
//!
//! When writing generic code, it is often desirable to abstract over all ways
//! of borrowing data from a given type. That is the role of the `BorrowFrom`
//! trait: if `T: BorrowFrom<U>`, then `&T` can be borrowed from `&U`.  A given
//! type can be borrowed as multiple different types. In particular, `Vec<T>:
//! BorrowFrom<Vec<T>>` and `[T]: BorrowFrom<Vec<T>>`.
//!
//! # The `ToOwned` trait
//!
//! Some types make it possible to go from borrowed to owned, usually by
//! implementing the `Clone` trait. But `Clone` works only for going from `&T`
//! to `T`. The `ToOwned` trait generalizes `Clone` to construct owned data
//! from any borrow of a given type.
//!
//! # The `Cow` (clone-on-write) type
//!
//! The type `Cow` is a smart pointer providing clone-on-write functionality: it
//! can enclose and provide immutable access to borrowed data, and clone the
//! data lazily when mutation or ownership is required. The type is designed to
//! work with general borrowed data via the `BorrowFrom` trait.
//!
//! `Cow` implements both `Deref`, which means that you can call
//! non-mutating methods directly on the data it encloses. If mutation
//! is desired, `to_mut` will obtain a mutable references to an owned
//! value, cloning if necessary.

#![unstable(feature = "core",
            reason = "recently added as part of collections reform")]

use clone::Clone;
use cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
use fmt;
use marker::Sized;
use ops::Deref;
use option::Option;
use self::Cow::*;

/// A trait for borrowing data.
#[old_orphan_check]
pub trait BorrowFrom<Owned: ?Sized> {
    /// Immutably borrow from an owned value.
    fn borrow_from(owned: &Owned) -> &Self;
}

/// A trait for mutably borrowing data.
#[old_orphan_check]
pub trait BorrowFromMut<Owned: ?Sized> : BorrowFrom<Owned> {
    /// Mutably borrow from an owned value.
    fn borrow_from_mut(owned: &mut Owned) -> &mut Self;
}

impl<T: ?Sized> BorrowFrom<T> for T {
    fn borrow_from(owned: &T) -> &T { owned }
}

impl<T: ?Sized> BorrowFromMut<T> for T {
    fn borrow_from_mut(owned: &mut T) -> &mut T { owned }
}

impl<'a, T: ?Sized> BorrowFrom<&'a T> for T {
    fn borrow_from<'b>(owned: &'b &'a T) -> &'b T { &**owned }
}

impl<'a, T: ?Sized> BorrowFrom<&'a mut T> for T {
    fn borrow_from<'b>(owned: &'b &'a mut T) -> &'b T { &**owned }
}

impl<'a, T: ?Sized> BorrowFromMut<&'a mut T> for T {
    fn borrow_from_mut<'b>(owned: &'b mut &'a mut T) -> &'b mut T { &mut **owned }
}

impl<'a, T, B: ?Sized> BorrowFrom<Cow<'a, T, B>> for B where B: ToOwned<T> {
    fn borrow_from<'b>(owned: &'b Cow<'a, T, B>) -> &'b B {
        &**owned
    }
}

/// Trait for moving into a `Cow`
#[old_orphan_check]
pub trait IntoCow<'a, T, B: ?Sized> {
    /// Moves `self` into `Cow`
    fn into_cow(self) -> Cow<'a, T, B>;
}

impl<'a, T, B: ?Sized> IntoCow<'a, T, B> for Cow<'a, T, B> where B: ToOwned<T> {
    fn into_cow(self) -> Cow<'a, T, B> {
        self
    }
}

/// A generalization of Clone to borrowed data.
#[old_orphan_check]
pub trait ToOwned<Owned>: BorrowFrom<Owned> {
    /// Create owned data from borrowed data, usually by copying.
    fn to_owned(&self) -> Owned;
}

impl<T> ToOwned<T> for T where T: Clone {
    fn to_owned(&self) -> T { self.clone() }
}

/// A clone-on-write smart pointer.
///
/// # Example
///
/// ```rust
/// use std::borrow::Cow;
///
/// fn abs_all(input: &mut Cow<Vec<i32>, [i32]>) {
///     for i in 0..input.len() {
///         let v = input[i];
///         if v < 0 {
///             // clones into a vector the first time (if not already owned)
///             input.to_mut()[i] = -v;
///         }
///     }
/// }
/// ```
pub enum Cow<'a, T, B: ?Sized + 'a> where B: ToOwned<T> {
    /// Borrowed data.
    Borrowed(&'a B),

    /// Owned data.
    Owned(T)
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T, B: ?Sized> Clone for Cow<'a, T, B> where B: ToOwned<T> {
    fn clone(&self) -> Cow<'a, T, B> {
        match *self {
            Borrowed(b) => Borrowed(b),
            Owned(ref o) => {
                let b: &B = BorrowFrom::borrow_from(o);
                Owned(b.to_owned())
            },
        }
    }
}

impl<'a, T, B: ?Sized> Cow<'a, T, B> where B: ToOwned<T> {
    /// Acquire a mutable reference to the owned form of the data.
    ///
    /// Copies the data if it is not already owned.
    pub fn to_mut(&mut self) -> &mut T {
        match *self {
            Borrowed(borrowed) => {
                *self = Owned(borrowed.to_owned());
                self.to_mut()
            }
            Owned(ref mut owned) => owned
        }
    }

    /// Extract the owned data.
    ///
    /// Copies the data if it is not already owned.
    pub fn into_owned(self) -> T {
        match self {
            Borrowed(borrowed) => borrowed.to_owned(),
            Owned(owned) => owned
        }
    }

    /// Returns true if this `Cow` wraps a borrowed value
    pub fn is_borrowed(&self) -> bool {
        match *self {
            Borrowed(_) => true,
            _ => false,
        }
    }

    /// Returns true if this `Cow` wraps an owned value
    pub fn is_owned(&self) -> bool {
        match *self {
            Owned(_) => true,
            _ => false,
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T, B: ?Sized> Deref for Cow<'a, T, B> where B: ToOwned<T>  {
    type Target = B;

    fn deref(&self) -> &B {
        match *self {
            Borrowed(borrowed) => borrowed,
            Owned(ref owned) => BorrowFrom::borrow_from(owned)
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T, B: ?Sized> Eq for Cow<'a, T, B> where B: Eq + ToOwned<T> {}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T, B: ?Sized> Ord for Cow<'a, T, B> where B: Ord + ToOwned<T> {
    #[inline]
    fn cmp(&self, other: &Cow<'a, T, B>) -> Ordering {
        Ord::cmp(&**self, &**other)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, 'b, T, U, B: ?Sized, C: ?Sized> PartialEq<Cow<'b, U, C>> for Cow<'a, T, B> where
    B: PartialEq<C> + ToOwned<T>,
    C: ToOwned<U>,
{
    #[inline]
    fn eq(&self, other: &Cow<'b, U, C>) -> bool {
        PartialEq::eq(&**self, &**other)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T, B: ?Sized> PartialOrd for Cow<'a, T, B> where B: PartialOrd + ToOwned<T> {
    #[inline]
    fn partial_cmp(&self, other: &Cow<'a, T, B>) -> Option<Ordering> {
        PartialOrd::partial_cmp(&**self, &**other)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T, B: ?Sized> fmt::Debug for Cow<'a, T, B> where
    B: fmt::Debug + ToOwned<T>,
    T: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Borrowed(ref b) => fmt::Debug::fmt(b, f),
            Owned(ref o) => fmt::Debug::fmt(o, f),
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T, B: ?Sized> fmt::Display for Cow<'a, T, B> where
    B: fmt::Display + ToOwned<T>,
    T: fmt::Display,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Borrowed(ref b) => fmt::Display::fmt(b, f),
            Owned(ref o) => fmt::Display::fmt(o, f),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#[crate_id = "rustdoc#0.10-pre"];
#[desc = "rustdoc, the Rust documentation extractor"];
#[license = "MIT/ASL2"];
#[crate_type = "dylib"];

#[feature(globs, struct_variant, managed_boxes)];

extern mod syntax;
extern mod rustc;
extern mod extra;

use std::local_data;
use std::io;
use std::io::{File, MemWriter};
use std::str;
use extra::getopts;
use extra::getopts::groups;
use extra::json;
use extra::serialize::{Decodable, Encodable};
use extra::time;

pub mod clean;
pub mod core;
pub mod doctree;
pub mod fold;
pub mod html {
    pub mod escape;
    pub mod format;
    pub mod layout;
    pub mod markdown;
    pub mod render;
}
pub mod passes;
pub mod plugins;
pub mod visit_ast;
pub mod test;

pub static SCHEMA_VERSION: &'static str = "0.8.1";

type Pass = (&'static str,                                      // name
             extern fn(clean::Crate) -> plugins::PluginResult,  // fn
             &'static str);                                     // description

static PASSES: &'static [Pass] = &[
    ("strip-hidden", passes::strip_hidden,
     "strips all doc(hidden) items from the output"),
    ("unindent-comments", passes::unindent_comments,
     "removes excess indentation on comments in order for markdown to like it"),
    ("collapse-docs", passes::collapse_docs,
     "concatenates all document attributes into one document attribute"),
    ("strip-private", passes::strip_private,
     "strips all private items from a crate which cannot be seen externally"),
];

static DEFAULT_PASSES: &'static [&'static str] = &[
    "strip-hidden",
    "strip-private",
    "collapse-docs",
    "unindent-comments",
];

local_data_key!(pub ctxtkey: @core::DocContext)
local_data_key!(pub analysiskey: core::CrateAnalysis)

type Output = (clean::Crate, ~[plugins::PluginJson]);

pub fn main() {
    std::os::set_exit_status(main_args(std::os::args()));
}

pub fn opts() -> ~[groups::OptGroup] {
    use extra::getopts::groups::*;
    ~[
        optflag("h", "help", "show this help message"),
        optflag("", "version", "print rustdoc's version"),
        optopt("r", "input-format", "the input type of the specified file",
               "[rust|json]"),
        optopt("w", "output-format", "the output type to write",
               "[html|json]"),
        optopt("o", "output", "where to place the output", "PATH"),
        optmulti("L", "library-path", "directory to add to crate search path",
                 "DIR"),
        optmulti("", "cfg", "pass a --cfg to rustc", ""),
        optmulti("", "plugin-path", "directory to load plugins from", "DIR"),
        optmulti("", "passes", "space separated list of passes to also run, a \
                                value of `list` will print available passes",
                 "PASSES"),
        optmulti("", "plugins", "space separated list of plugins to also load",
                 "PLUGINS"),
        optflag("", "no-defaults", "don't run the default passes"),
        optflag("", "test", "run code examples as tests"),
        optmulti("", "test-args", "arguments to pass to the test runner",
                 "ARGS"),
    ]
}

pub fn usage(argv0: &str) {
    println!("{}", groups::usage(format!("{} [options] <input>", argv0), opts()));
}

pub fn main_args(args: &[~str]) -> int {
    let matches = match groups::getopts(args.tail(), opts()) {
        Ok(m) => m,
        Err(err) => {
            println!("{}", err.to_err_msg());
            return 1;
        }
    };
    if matches.opt_present("h") || matches.opt_present("help") {
        usage(args[0]);
        return 0;
    } else if matches.opt_present("version") {
        rustc::version(args[0]);
        return 0;
    }

    if matches.free.len() == 0 {
        println!("expected an input file to act on");
        return 1;
    } if matches.free.len() > 1 {
        println!("only one input file may be specified");
        return 1;
    }
    let input = matches.free[0].as_slice();

    if matches.opt_present("test") {
        return test::run(input, &matches);
    }

    if matches.opt_strs("passes") == ~[~"list"] {
        println!("Available passes for running rustdoc:");
        for &(name, _, description) in PASSES.iter() {
            println!("{:>20s} - {}", name, description);
        }
        println!("{}", "\nDefault passes for rustdoc:"); // FIXME: #9970
        for &name in DEFAULT_PASSES.iter() {
            println!("{:>20s}", name);
        }
        return 0;
    }

    let (crate, res) = match acquire_input(input, &matches) {
        Ok(pair) => pair,
        Err(s) => {
            println!("input error: {}", s);
            return 1;
        }
    };

    info!("going to format");
    let started = time::precise_time_ns();
    let output = matches.opt_str("o").map(|s| Path::new(s));
    match matches.opt_str("w") {
        Some(~"html") | None => {
            html::render::run(crate, output.unwrap_or(Path::new("doc")))
        }
        Some(~"json") => {
            json_output(crate, res, output.unwrap_or(Path::new("doc.json")))
        }
        Some(s) => {
            println!("unknown output format: {}", s);
            return 1;
        }
    }
    let ended = time::precise_time_ns();
    info!("Took {:.03f}s", (ended as f64 - started as f64) / 1e9f64);

    return 0;
}

/// Looks inside the command line arguments to extract the relevant input format
/// and files and then generates the necessary rustdoc output for formatting.
fn acquire_input(input: &str,
                 matches: &getopts::Matches) -> Result<Output, ~str> {
    match matches.opt_str("r") {
        Some(~"rust") => Ok(rust_input(input, matches)),
        Some(~"json") => json_input(input),
        Some(s) => Err("unknown input format: " + s),
        None => {
            if input.ends_with(".json") {
                json_input(input)
            } else {
                Ok(rust_input(input, matches))
            }
        }
    }
}

/// Interprets the input file as a rust source file, passing it through the
/// compiler all the way through the analysis passes. The rustdoc output is then
/// generated from the cleaned AST of the crate.
///
/// This form of input will run all of the plug/cleaning passes
fn rust_input(cratefile: &str, matches: &getopts::Matches) -> Output {
    let mut default_passes = !matches.opt_present("no-defaults");
    let mut passes = matches.opt_strs("passes");
    let mut plugins = matches.opt_strs("plugins");

    // First, parse the crate and extract all relevant information.
    let libs = matches.opt_strs("L").map(|s| Path::new(s.as_slice()));
    let cfgs = matches.opt_strs("cfg");
    let cr = Path::new(cratefile);
    info!("starting to run rustc");
    let (crate, analysis) = do std::task::try {
        let cr = cr;
        core::run_core(libs.move_iter().collect(), cfgs, &cr)
    }.unwrap();
    info!("finished with rustc");
    local_data::set(analysiskey, analysis);

    // Process all of the crate attributes, extracting plugin metadata along
    // with the passes which we are supposed to run.
    match crate.module.get_ref().doc_list() {
        Some(nested) => {
            for inner in nested.iter() {
                match *inner {
                    clean::Word(~"no_default_passes") => {
                        default_passes = false;
                    }
                    clean::NameValue(~"passes", ref value) => {
                        for pass in value.words() {
                            passes.push(pass.to_owned());
                        }
                    }
                    clean::NameValue(~"plugins", ref value) => {
                        for p in value.words() {
                            plugins.push(p.to_owned());
                        }
                    }
                    _ => {}
                }
            }
        }
        None => {}
    }
    if default_passes {
        for name in DEFAULT_PASSES.rev_iter() {
            passes.unshift(name.to_owned());
        }
    }

    // Load all plugins/passes into a PluginManager
    let path = matches.opt_str("plugin-path").unwrap_or(~"/tmp/rustdoc/plugins");
    let mut pm = plugins::PluginManager::new(Path::new(path));
    for pass in passes.iter() {
        let plugin = match PASSES.iter().position(|&(p, _, _)| p == *pass) {
            Some(i) => PASSES[i].n1(),
            None => {
                error!("unknown pass {}, skipping", *pass);
                continue
            },
        };
        pm.add_plugin(plugin);
    }
    info!("loading plugins...");
    for pname in plugins.move_iter() {
        pm.load_plugin(pname);
    }

    // Run everything!
    info!("Executing passes/plugins");
    return pm.run_plugins(crate);
}

/// This input format purely deserializes the json output file. No passes are
/// run over the deserialized output.
fn json_input(input: &str) -> Result<Output, ~str> {
    let mut input = match File::open(&Path::new(input)) {
        Some(f) => f,
        None => return Err(format!("couldn't open {} for reading", input)),
    };
    match json::from_reader(&mut input) {
        Err(s) => Err(s.to_str()),
        Ok(json::Object(obj)) => {
            let mut obj = obj;
            // Make sure the schema is what we expect
            match obj.pop(&~"schema") {
                Some(json::String(version)) => {
                    if version.as_slice() != SCHEMA_VERSION {
                        return Err(format!("sorry, but I only understand \
                                            version {}", SCHEMA_VERSION))
                    }
                }
                Some(..) => return Err(~"malformed json"),
                None => return Err(~"expected a schema version"),
            }
            let crate = match obj.pop(&~"crate") {
                Some(json) => {
                    let mut d = json::Decoder::new(json);
                    Decodable::decode(&mut d)
                }
                None => return Err(~"malformed json"),
            };
            // FIXME: this should read from the "plugins" field, but currently
            //      Json doesn't implement decodable...
            let plugin_output = ~[];
            Ok((crate, plugin_output))
        }
        Ok(..) => Err(~"malformed json input: expected an object at the top"),
    }
}

/// Outputs the crate/plugin json as a giant json blob at the specified
/// destination.
fn json_output(crate: clean::Crate, res: ~[plugins::PluginJson], dst: Path) {
    // {
    //   "schema": version,
    //   "crate": { parsed crate ... },
    //   "plugins": { output of plugins ... }
    // }
    let mut json = ~extra::treemap::TreeMap::new();
    json.insert(~"schema", json::String(SCHEMA_VERSION.to_owned()));
    let plugins_json = ~res.move_iter().filter_map(|opt| opt).collect();

    // FIXME #8335: yuck, Rust -> str -> JSON round trip! No way to .encode
    // straight to the Rust JSON representation.
    let crate_json_str = {
        let mut w = MemWriter::new();
        {
            let mut encoder = json::Encoder::new(&mut w as &mut io::Writer);
            crate.encode(&mut encoder);
        }
        str::from_utf8_owned(w.unwrap()).unwrap()
    };
    let crate_json = match json::from_str(crate_json_str) {
        Ok(j) => j,
        Err(_) => fail!("Rust generated JSON is invalid??")
    };

    json.insert(~"crate", crate_json);
    json.insert(~"plugins", json::Object(plugins_json));

    let mut file = File::create(&dst).unwrap();
    json::Object(json).to_writer(&mut file);
}
<commit_msg>Removing do keyword from librustdoc<commit_after>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#[crate_id = "rustdoc#0.10-pre"];
#[desc = "rustdoc, the Rust documentation extractor"];
#[license = "MIT/ASL2"];
#[crate_type = "dylib"];

#[feature(globs, struct_variant, managed_boxes)];

extern mod syntax;
extern mod rustc;
extern mod extra;

use std::local_data;
use std::io;
use std::io::{File, MemWriter};
use std::str;
use extra::getopts;
use extra::getopts::groups;
use extra::json;
use extra::serialize::{Decodable, Encodable};
use extra::time;

pub mod clean;
pub mod core;
pub mod doctree;
pub mod fold;
pub mod html {
    pub mod escape;
    pub mod format;
    pub mod layout;
    pub mod markdown;
    pub mod render;
}
pub mod passes;
pub mod plugins;
pub mod visit_ast;
pub mod test;

pub static SCHEMA_VERSION: &'static str = "0.8.1";

type Pass = (&'static str,                                      // name
             extern fn(clean::Crate) -> plugins::PluginResult,  // fn
             &'static str);                                     // description

static PASSES: &'static [Pass] = &[
    ("strip-hidden", passes::strip_hidden,
     "strips all doc(hidden) items from the output"),
    ("unindent-comments", passes::unindent_comments,
     "removes excess indentation on comments in order for markdown to like it"),
    ("collapse-docs", passes::collapse_docs,
     "concatenates all document attributes into one document attribute"),
    ("strip-private", passes::strip_private,
     "strips all private items from a crate which cannot be seen externally"),
];

static DEFAULT_PASSES: &'static [&'static str] = &[
    "strip-hidden",
    "strip-private",
    "collapse-docs",
    "unindent-comments",
];

local_data_key!(pub ctxtkey: @core::DocContext)
local_data_key!(pub analysiskey: core::CrateAnalysis)

type Output = (clean::Crate, ~[plugins::PluginJson]);

pub fn main() {
    std::os::set_exit_status(main_args(std::os::args()));
}

pub fn opts() -> ~[groups::OptGroup] {
    use extra::getopts::groups::*;
    ~[
        optflag("h", "help", "show this help message"),
        optflag("", "version", "print rustdoc's version"),
        optopt("r", "input-format", "the input type of the specified file",
               "[rust|json]"),
        optopt("w", "output-format", "the output type to write",
               "[html|json]"),
        optopt("o", "output", "where to place the output", "PATH"),
        optmulti("L", "library-path", "directory to add to crate search path",
                 "DIR"),
        optmulti("", "cfg", "pass a --cfg to rustc", ""),
        optmulti("", "plugin-path", "directory to load plugins from", "DIR"),
        optmulti("", "passes", "space separated list of passes to also run, a \
                                value of `list` will print available passes",
                 "PASSES"),
        optmulti("", "plugins", "space separated list of plugins to also load",
                 "PLUGINS"),
        optflag("", "no-defaults", "don't run the default passes"),
        optflag("", "test", "run code examples as tests"),
        optmulti("", "test-args", "arguments to pass to the test runner",
                 "ARGS"),
    ]
}

pub fn usage(argv0: &str) {
    println!("{}", groups::usage(format!("{} [options] <input>", argv0), opts()));
}

pub fn main_args(args: &[~str]) -> int {
    let matches = match groups::getopts(args.tail(), opts()) {
        Ok(m) => m,
        Err(err) => {
            println!("{}", err.to_err_msg());
            return 1;
        }
    };
    if matches.opt_present("h") || matches.opt_present("help") {
        usage(args[0]);
        return 0;
    } else if matches.opt_present("version") {
        rustc::version(args[0]);
        return 0;
    }

    if matches.free.len() == 0 {
        println!("expected an input file to act on");
        return 1;
    } if matches.free.len() > 1 {
        println!("only one input file may be specified");
        return 1;
    }
    let input = matches.free[0].as_slice();

    if matches.opt_present("test") {
        return test::run(input, &matches);
    }

    if matches.opt_strs("passes") == ~[~"list"] {
        println!("Available passes for running rustdoc:");
        for &(name, _, description) in PASSES.iter() {
            println!("{:>20s} - {}", name, description);
        }
        println!("{}", "\nDefault passes for rustdoc:"); // FIXME: #9970
        for &name in DEFAULT_PASSES.iter() {
            println!("{:>20s}", name);
        }
        return 0;
    }

    let (crate, res) = match acquire_input(input, &matches) {
        Ok(pair) => pair,
        Err(s) => {
            println!("input error: {}", s);
            return 1;
        }
    };

    info!("going to format");
    let started = time::precise_time_ns();
    let output = matches.opt_str("o").map(|s| Path::new(s));
    match matches.opt_str("w") {
        Some(~"html") | None => {
            html::render::run(crate, output.unwrap_or(Path::new("doc")))
        }
        Some(~"json") => {
            json_output(crate, res, output.unwrap_or(Path::new("doc.json")))
        }
        Some(s) => {
            println!("unknown output format: {}", s);
            return 1;
        }
    }
    let ended = time::precise_time_ns();
    info!("Took {:.03f}s", (ended as f64 - started as f64) / 1e9f64);

    return 0;
}

/// Looks inside the command line arguments to extract the relevant input format
/// and files and then generates the necessary rustdoc output for formatting.
fn acquire_input(input: &str,
                 matches: &getopts::Matches) -> Result<Output, ~str> {
    match matches.opt_str("r") {
        Some(~"rust") => Ok(rust_input(input, matches)),
        Some(~"json") => json_input(input),
        Some(s) => Err("unknown input format: " + s),
        None => {
            if input.ends_with(".json") {
                json_input(input)
            } else {
                Ok(rust_input(input, matches))
            }
        }
    }
}

/// Interprets the input file as a rust source file, passing it through the
/// compiler all the way through the analysis passes. The rustdoc output is then
/// generated from the cleaned AST of the crate.
///
/// This form of input will run all of the plug/cleaning passes
fn rust_input(cratefile: &str, matches: &getopts::Matches) -> Output {
    let mut default_passes = !matches.opt_present("no-defaults");
    let mut passes = matches.opt_strs("passes");
    let mut plugins = matches.opt_strs("plugins");

    // First, parse the crate and extract all relevant information.
    let libs = matches.opt_strs("L").map(|s| Path::new(s.as_slice()));
    let cfgs = matches.opt_strs("cfg");
    let cr = Path::new(cratefile);
    info!("starting to run rustc");
    let (crate, analysis) = std::task::try(proc() {
        let cr = cr;
        core::run_core(libs.move_iter().collect(), cfgs, &cr)
    }).unwrap();
    info!("finished with rustc");
    local_data::set(analysiskey, analysis);

    // Process all of the crate attributes, extracting plugin metadata along
    // with the passes which we are supposed to run.
    match crate.module.get_ref().doc_list() {
        Some(nested) => {
            for inner in nested.iter() {
                match *inner {
                    clean::Word(~"no_default_passes") => {
                        default_passes = false;
                    }
                    clean::NameValue(~"passes", ref value) => {
                        for pass in value.words() {
                            passes.push(pass.to_owned());
                        }
                    }
                    clean::NameValue(~"plugins", ref value) => {
                        for p in value.words() {
                            plugins.push(p.to_owned());
                        }
                    }
                    _ => {}
                }
            }
        }
        None => {}
    }
    if default_passes {
        for name in DEFAULT_PASSES.rev_iter() {
            passes.unshift(name.to_owned());
        }
    }

    // Load all plugins/passes into a PluginManager
    let path = matches.opt_str("plugin-path").unwrap_or(~"/tmp/rustdoc/plugins");
    let mut pm = plugins::PluginManager::new(Path::new(path));
    for pass in passes.iter() {
        let plugin = match PASSES.iter().position(|&(p, _, _)| p == *pass) {
            Some(i) => PASSES[i].n1(),
            None => {
                error!("unknown pass {}, skipping", *pass);
                continue
            },
        };
        pm.add_plugin(plugin);
    }
    info!("loading plugins...");
    for pname in plugins.move_iter() {
        pm.load_plugin(pname);
    }

    // Run everything!
    info!("Executing passes/plugins");
    return pm.run_plugins(crate);
}

/// This input format purely deserializes the json output file. No passes are
/// run over the deserialized output.
fn json_input(input: &str) -> Result<Output, ~str> {
    let mut input = match File::open(&Path::new(input)) {
        Some(f) => f,
        None => return Err(format!("couldn't open {} for reading", input)),
    };
    match json::from_reader(&mut input) {
        Err(s) => Err(s.to_str()),
        Ok(json::Object(obj)) => {
            let mut obj = obj;
            // Make sure the schema is what we expect
            match obj.pop(&~"schema") {
                Some(json::String(version)) => {
                    if version.as_slice() != SCHEMA_VERSION {
                        return Err(format!("sorry, but I only understand \
                                            version {}", SCHEMA_VERSION))
                    }
                }
                Some(..) => return Err(~"malformed json"),
                None => return Err(~"expected a schema version"),
            }
            let crate = match obj.pop(&~"crate") {
                Some(json) => {
                    let mut d = json::Decoder::new(json);
                    Decodable::decode(&mut d)
                }
                None => return Err(~"malformed json"),
            };
            // FIXME: this should read from the "plugins" field, but currently
            //      Json doesn't implement decodable...
            let plugin_output = ~[];
            Ok((crate, plugin_output))
        }
        Ok(..) => Err(~"malformed json input: expected an object at the top"),
    }
}

/// Outputs the crate/plugin json as a giant json blob at the specified
/// destination.
fn json_output(crate: clean::Crate, res: ~[plugins::PluginJson], dst: Path) {
    // {
    //   "schema": version,
    //   "crate": { parsed crate ... },
    //   "plugins": { output of plugins ... }
    // }
    let mut json = ~extra::treemap::TreeMap::new();
    json.insert(~"schema", json::String(SCHEMA_VERSION.to_owned()));
    let plugins_json = ~res.move_iter().filter_map(|opt| opt).collect();

    // FIXME #8335: yuck, Rust -> str -> JSON round trip! No way to .encode
    // straight to the Rust JSON representation.
    let crate_json_str = {
        let mut w = MemWriter::new();
        {
            let mut encoder = json::Encoder::new(&mut w as &mut io::Writer);
            crate.encode(&mut encoder);
        }
        str::from_utf8_owned(w.unwrap()).unwrap()
    };
    let crate_json = match json::from_str(crate_json_str) {
        Ok(j) => j,
        Err(_) => fail!("Rust generated JSON is invalid??")
    };

    json.insert(~"crate", crate_json);
    json.insert(~"plugins", json::Object(plugins_json));

    let mut file = File::create(&dst).unwrap();
    json::Object(json).to_writer(&mut file);
}
<|endoftext|>