<commit_before>use anyhow::anyhow;
use cargo::core::{features, CliUnstable};
use cargo::{self, drop_print, drop_println, CliResult, Config};
use clap::{AppSettings, Arg, ArgMatches};
use itertools::Itertools;
use std::collections::HashMap;

use super::commands;
use super::list_commands;
use crate::command_prelude::*;
use cargo::core::features::HIDDEN;

lazy_static::lazy_static! {
    // Maps from commonly known external commands (not builtin to cargo) to their
    // description, for the help page. Reserved for external subcommands that are
    // core within the rust ecosystem (esp ones that might become internal in the future).
    static ref KNOWN_EXTERNAL_COMMAND_DESCRIPTIONS: HashMap<&'static str, &'static str> = vec![
        ("clippy", "Checks a package to catch common mistakes and improve your Rust code."),
        ("fmt", "Formats all bin and lib files of the current crate using rustfmt."),
    ].into_iter().collect();
}

pub fn main(config: &mut Config) -> CliResult {
    // CAUTION: Be careful with using `config` until it is configured below.
    // In general, try to avoid loading config values unless necessary (like
    // the [alias] table).

    if commands::help::handle_embedded_help(config) {
        return Ok(());
    }

    let args = match cli().get_matches_safe() {
        Ok(args) => args,
        Err(e) => {
            if e.kind == clap::ErrorKind::UnrecognizedSubcommand {
                // An unrecognized subcommand might be an external subcommand.
                let cmd = &e.info.as_ref().unwrap()[0].to_owned();
                return super::execute_external_subcommand(config, cmd, &[cmd, "--help"])
                    .map_err(|_| e.into());
            } else {
                return Err(e.into());
            }
        }
    };

    // Global args need to be extracted before expanding aliases because the
    // clap code for extracting a subcommand discards global options
    // (appearing before the subcommand).
    let (expanded_args, global_args) = expand_aliases(config, args)?;

    if expanded_args.value_of("unstable-features") == Some("help") {
        let options = CliUnstable::help();
        let non_hidden_options: Vec<(String, String)> = options
            .iter()
            .filter(|(_, help_message)| *help_message != HIDDEN)
            .map(|(name, help)| (name.to_string(), help.to_string()))
            .collect();
        let longest_option = non_hidden_options
            .iter()
            .map(|(option_name, _)| option_name.len())
            .max()
            .unwrap_or(0);
        let help_lines: Vec<String> = non_hidden_options
            .iter()
            .map(|(option_name, option_help_message)| {
                let option_name_kebab_case = option_name.replace("_", "-");
                let padding = " ".repeat(longest_option - option_name.len()); // safe to substract
                format!(
                    "    -Z {}{} -- {}",
                    option_name_kebab_case, padding, option_help_message
                )
            })
            .collect();
        let joined = help_lines.join("\n");
        drop_println!(
            config,
            "
Available unstable (nightly-only) flags:

{}

Run with 'cargo -Z [FLAG] [SUBCOMMAND]'",
            joined
        );
        if !config.nightly_features_allowed {
            drop_println!(
                config,
                "\nUnstable flags are only available on the nightly channel \
                 of Cargo, but this is the `{}` channel.\n\
                 {}",
                features::channel(),
                features::SEE_CHANNELS
            );
        }
        drop_println!(
            config,
            "\nSee https://doc.rust-lang.org/nightly/cargo/reference/unstable.html \
             for more information about these flags."
        );
        return Ok(());
    }

    let is_verbose = expanded_args.occurrences_of("verbose") > 0;
    if args.is_present("version") {
        let version = get_version_string(is_verbose);
        drop_print!(config, "{}", version);
        return Ok(());
    }

    if let Some(code) = expanded_args.value_of("explain") {
        let mut procss = config.load_global_rustc(None)?.process();
        procss.arg("--explain").arg(code).exec()?;
        return Ok(());
    }

    if expanded_args.is_present("list") {
        drop_println!(config, "Installed Commands:");
        for (name, command) in list_commands(config) {
            let known_external_desc = KNOWN_EXTERNAL_COMMAND_DESCRIPTIONS.get(name.as_str());
            match command {
                CommandInfo::BuiltIn { about } => {
                    assert!(
                        known_external_desc.is_none(),
                        "KNOWN_EXTERNAL_COMMANDS shouldn't contain builtin \"{}\"",
                        name
                    );
                    let summary = about.unwrap_or_default();
                    let summary = summary.lines().next().unwrap_or(&summary); // display only the first line
                    drop_println!(config, "    {:<20} {}", name, summary);
                }
                CommandInfo::External { path } => {
                    if let Some(desc) = known_external_desc {
                        drop_println!(config, "    {:<20} {}", name, desc);
                    } else if is_verbose {
                        drop_println!(config, "    {:<20} {}", name, path.display());
                    } else {
                        drop_println!(config, "    {}", name);
                    }
                }
                CommandInfo::Alias { target } => {
                    drop_println!(config, "    {:<20} {}", name, target.iter().join(" "));
                }
            }
        }
        return Ok(());
    }

    let (cmd, subcommand_args) = match expanded_args.subcommand() {
        (cmd, Some(args)) => (cmd, args),
        _ => {
            // No subcommand provided.
            cli().print_help()?;
            return Ok(());
        }
    };
    config_configure(config, &expanded_args, subcommand_args, global_args)?;
    super::init_git_transports(config);

    execute_subcommand(config, cmd, subcommand_args)
}

pub fn get_version_string(is_verbose: bool) -> String {
    let version = cargo::version();
    let mut version_string = format!("cargo {}\n", version);
    if is_verbose {
        version_string.push_str(&format!(
            "release: {}.{}.{}\n",
            version.major, version.minor, version.patch
        ));
        if let Some(ref cfg) = version.cfg_info {
            if let Some(ref ci) = cfg.commit_info {
                version_string.push_str(&format!("commit-hash: {}\n", ci.commit_hash));
                version_string.push_str(&format!("commit-date: {}\n", ci.commit_date));
            }
        }
    }
    version_string
}

fn expand_aliases(
    config: &mut Config,
    args: ArgMatches<'static>,
    mut already_expanded: Vec<String>,
) -> Result<(ArgMatches<'static>, GlobalArgs), CliError> {
    if let (cmd, Some(args)) = args.subcommand() {
        match (
            commands::builtin_exec(cmd),
            super::aliased_command(config, cmd)?,
        ) {
            (Some(_), Some(_)) => {
                // User alias conflicts with a built-in subcommand
                config.shell().warn(format!(
                    "user-defined alias `{}` is ignored, because it is shadowed by a built-in command",
                    cmd,
                ))?;
            }
            (Some(_), None) => {
                // Command is built-in and is not conflicting with alias, but contains ignored values.
                if let Some(mut values) = args.values_of("") {
                    config.shell().warn(format!(
                        "trailing arguments after built-in command `{}` are ignored: `{}`",
                        cmd,
                        values.join(" "),
                    ))?;
                }
            }
            (None, None) => {}
            (_, Some(mut alias)) => {
                alias.extend(
                    args.values_of("")
                        .unwrap_or_default()
                        .map(|s| s.to_string()),
                );
                // new_args strips out everything before the subcommand, so
                // capture those global options now.
                // Note that an alias to an external command will not receive
                // these arguments. That may be confusing, but such is life.
                let global_args = GlobalArgs::new(args);
                let new_args = cli()
                    .setting(AppSettings::NoBinaryName)
                    .get_matches_from_safe(alias)?;

                let (new_cmd, _) = new_args.subcommand();
                already_expanded.push(cmd.to_string());
                if already_expanded.contains(&new_cmd.to_string()) {
                    // Crash if the aliases are corecursive / unresolvable
                    return Err(anyhow!(
                        "alias {} has unresolvable recursive definition: {} -> {}",
                        already_expanded[0],
                        already_expanded.join(" -> "),
                        new_cmd,
                    )
                    .into());
                }

                let (expanded_args, _) = expand_aliases(config, new_args, already_expanded)?;
                return Ok((expanded_args, global_args));
            }
        }
    };

    Ok((args, GlobalArgs::default()))
}

fn config_configure(
    config: &mut Config,
    args: &ArgMatches<'_>,
    subcommand_args: &ArgMatches<'_>,
    global_args: GlobalArgs,
) -> CliResult {
    let arg_target_dir = &subcommand_args.value_of_path("target-dir", config);
    let verbose = global_args.verbose + args.occurrences_of("verbose") as u32;
    // quiet is unusual because it is redefined in some subcommands in order
    // to provide custom help text.
    let quiet =
        args.is_present("quiet") || subcommand_args.is_present("quiet") || global_args.quiet;
    let global_color = global_args.color; // Extract so it can take reference.
    let color = args.value_of("color").or_else(|| global_color.as_deref());
    let frozen = args.is_present("frozen") || global_args.frozen;
    let locked = args.is_present("locked") || global_args.locked;
    let offline = args.is_present("offline") || global_args.offline;
    let mut unstable_flags = global_args.unstable_flags;
    if let Some(values) = args.values_of("unstable-features") {
        unstable_flags.extend(values.map(|s| s.to_string()));
    }
    let mut config_args = global_args.config_args;
    if let Some(values) = args.values_of("config") {
        config_args.extend(values.map(|s| s.to_string()));
    }
    config.configure(
        verbose,
        quiet,
        color,
        frozen,
        locked,
        offline,
        arg_target_dir,
        &unstable_flags,
        &config_args,
    )?;
    Ok(())
}

fn execute_subcommand(
    config: &mut Config,
    cmd: &str,
    subcommand_args: &ArgMatches<'_>,
) -> CliResult {
    if let Some(exec) = commands::builtin_exec(cmd) {
        return exec(config, subcommand_args);
    }

    let mut ext_args: Vec<&str> = vec![cmd];
    ext_args.extend(subcommand_args.values_of("").unwrap_or_default());
    super::execute_external_subcommand(config, cmd, &ext_args)
}

#[derive(Default)]
struct GlobalArgs {
    verbose: u32,
    quiet: bool,
    color: Option<String>,
    frozen: bool,
    locked: bool,
    offline: bool,
    unstable_flags: Vec<String>,
    config_args: Vec<String>,
}

impl GlobalArgs {
    fn new(args: &ArgMatches<'_>) -> GlobalArgs {
        GlobalArgs {
            verbose: args.occurrences_of("verbose") as u32,
            quiet: args.is_present("quiet"),
            color: args.value_of("color").map(|s| s.to_string()),
            frozen: args.is_present("frozen"),
            locked: args.is_present("locked"),
            offline: args.is_present("offline"),
            unstable_flags: args
                .values_of_lossy("unstable-features")
                .unwrap_or_default(),
            config_args: args
                .values_of("config")
                .unwrap_or_default()
                .map(|s| s.to_string())
                .collect(),
        }
    }
}

fn cli() -> App {
    let is_rustup = std::env::var_os("RUSTUP_HOME").is_some();
    let usage = if is_rustup {
        "cargo [+toolchain] [OPTIONS] [SUBCOMMAND]"
    } else {
        "cargo [OPTIONS] [SUBCOMMAND]"
    };
    App::new("cargo")
        .settings(&[
            AppSettings::UnifiedHelpMessage,
            AppSettings::DeriveDisplayOrder,
            AppSettings::VersionlessSubcommands,
            AppSettings::AllowExternalSubcommands,
        ])
        .usage(usage)
        .template(
            "\
Rust's package manager

USAGE:
    {usage}

OPTIONS:
{unified}

Some common cargo commands are (see all commands with --list):
    build, b    Compile the current package
    check, c    Analyze the current package and report errors, but don't build object files
    clean       Remove the target directory
    doc, d      Build this package's and its dependencies' documentation
    new         Create a new cargo package
    init        Create a new cargo package in an existing directory
    run, r      Run a binary or example of the local package
    test, t     Run the tests
    bench       Run the benchmarks
    update      Update dependencies listed in Cargo.lock
    search      Search registry for crates
    publish     Package and upload this package to the registry
    install     Install a Rust binary. Default location is $HOME/.cargo/bin
    uninstall   Uninstall a Rust binary

See 'cargo help <command>' for more information on a specific command.\n",
        )
        .arg(opt("version", "Print version info and exit").short("V"))
        .arg(opt("list", "List installed commands"))
        .arg(opt("explain", "Run `rustc --explain CODE`").value_name("CODE"))
        .arg(
            opt(
                "verbose",
                "Use verbose output (-vv very verbose/build.rs output)",
            )
            .short("v")
            .multiple(true)
            .global(true),
        )
        .arg(opt("quiet", "No output printed to stdout").short("q"))
        .arg(
            opt("color", "Coloring: auto, always, never")
                .value_name("WHEN")
                .global(true),
        )
        .arg(opt("frozen", "Require Cargo.lock and cache are up to date").global(true))
        .arg(opt("locked", "Require Cargo.lock is up to date").global(true))
        .arg(opt("offline", "Run without accessing the network").global(true))
        .arg(
            multi_opt(
                "config",
                "KEY=VALUE",
                "Override a configuration value (unstable)",
            )
            .global(true),
        )
        .arg(
            Arg::with_name("unstable-features")
                .help("Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details")
                .short("Z")
                .value_name("FLAG")
                .multiple(true)
                .number_of_values(1)
                .global(true),
        )
        .subcommands(commands::builtin())
}
<commit_msg>fmt&Update Code<commit_after>use anyhow::anyhow;
use cargo::core::{features, CliUnstable};
use cargo::{self, drop_print, drop_println, CliResult, Config};
use clap::{AppSettings, Arg, ArgMatches};
use itertools::Itertools;
use std::collections::HashMap;

use super::commands;
use super::list_commands;
use crate::command_prelude::*;
use cargo::core::features::HIDDEN;

lazy_static::lazy_static! {
    // Maps from commonly known external commands (not builtin to cargo) to their
    // description, for the help page. Reserved for external subcommands that are
    // core within the rust ecosystem (esp ones that might become internal in the future).
    static ref KNOWN_EXTERNAL_COMMAND_DESCRIPTIONS: HashMap<&'static str, &'static str> = vec![
        ("clippy", "Checks a package to catch common mistakes and improve your Rust code."),
        ("fmt", "Formats all bin and lib files of the current crate using rustfmt."),
    ].into_iter().collect();
}

pub fn main(config: &mut Config) -> CliResult {
    // CAUTION: Be careful with using `config` until it is configured below.
    // In general, try to avoid loading config values unless necessary (like
    // the [alias] table).

    if commands::help::handle_embedded_help(config) {
        return Ok(());
    }

    let args = match cli().get_matches_safe() {
        Ok(args) => args,
        Err(e) => {
            if e.kind == clap::ErrorKind::UnrecognizedSubcommand {
                // An unrecognized subcommand might be an external subcommand.
                let cmd = &e.info.as_ref().unwrap()[0].to_owned();
                return super::execute_external_subcommand(config, cmd, &[cmd, "--help"])
                    .map_err(|_| e.into());
            } else {
                return Err(e.into());
            }
        }
    };

    // Global args need to be extracted before expanding aliases because the
    // clap code for extracting a subcommand discards global options
    // (appearing before the subcommand).
    let (expanded_args, global_args) = expand_aliases(config, args, vec![])?;

    if expanded_args.value_of("unstable-features") == Some("help") {
        let options = CliUnstable::help();
        let non_hidden_options: Vec<(String, String)> = options
            .iter()
            .filter(|(_, help_message)| *help_message != HIDDEN)
            .map(|(name, help)| (name.to_string(), help.to_string()))
            .collect();
        let longest_option = non_hidden_options
            .iter()
            .map(|(option_name, _)| option_name.len())
            .max()
            .unwrap_or(0);
        let help_lines: Vec<String> = non_hidden_options
            .iter()
            .map(|(option_name, option_help_message)| {
                let option_name_kebab_case = option_name.replace("_", "-");
                let padding = " ".repeat(longest_option - option_name.len()); // safe to substract
                format!(
                    "    -Z {}{} -- {}",
                    option_name_kebab_case, padding, option_help_message
                )
            })
            .collect();
        let joined = help_lines.join("\n");
        drop_println!(
            config,
            "
Available unstable (nightly-only) flags:

{}

Run with 'cargo -Z [FLAG] [SUBCOMMAND]'",
            joined
        );
        if !config.nightly_features_allowed {
            drop_println!(
                config,
                "\nUnstable flags are only available on the nightly channel \
                 of Cargo, but this is the `{}` channel.\n\
                 {}",
                features::channel(),
                features::SEE_CHANNELS
            );
        }
        drop_println!(
            config,
            "\nSee https://doc.rust-lang.org/nightly/cargo/reference/unstable.html \
             for more information about these flags."
        );
        return Ok(());
    }

    let is_verbose = expanded_args.occurrences_of("verbose") > 0;
    if args.is_present("version") {
        let version = get_version_string(is_verbose);
        drop_print!(config, "{}", version);
        return Ok(());
    }

    if let Some(code) = expanded_args.value_of("explain") {
        let mut procss = config.load_global_rustc(None)?.process();
        procss.arg("--explain").arg(code).exec()?;
        return Ok(());
    }

    if expanded_args.is_present("list") {
        drop_println!(config, "Installed Commands:");
        for (name, command) in list_commands(config) {
            let known_external_desc = KNOWN_EXTERNAL_COMMAND_DESCRIPTIONS.get(name.as_str());
            match command {
                CommandInfo::BuiltIn { about } => {
                    assert!(
                        known_external_desc.is_none(),
                        "KNOWN_EXTERNAL_COMMANDS shouldn't contain builtin \"{}\"",
                        name
                    );
                    let summary = about.unwrap_or_default();
                    let summary = summary.lines().next().unwrap_or(&summary); // display only the first line
                    drop_println!(config, "    {:<20} {}", name, summary);
                }
                CommandInfo::External { path } => {
                    if let Some(desc) = known_external_desc {
                        drop_println!(config, "    {:<20} {}", name, desc);
                    } else if is_verbose {
                        drop_println!(config, "    {:<20} {}", name, path.display());
                    } else {
                        drop_println!(config, "    {}", name);
                    }
                }
                CommandInfo::Alias { target } => {
                    drop_println!(config, "    {:<20} {}", name, target.iter().join(" "));
                }
            }
        }
        return Ok(());
    }

    let (cmd, subcommand_args) = match expanded_args.subcommand() {
        (cmd, Some(args)) => (cmd, args),
        _ => {
            // No subcommand provided.
            cli().print_help()?;
            return Ok(());
        }
    };
    config_configure(config, &expanded_args, subcommand_args, global_args)?;
    super::init_git_transports(config);

    execute_subcommand(config, cmd, subcommand_args)
}

pub fn get_version_string(is_verbose: bool) -> String {
    let version = cargo::version();
    let mut version_string = format!("cargo {}\n", version);
    if is_verbose {
        version_string.push_str(&format!(
            "release: {}.{}.{}\n",
            version.major, version.minor, version.patch
        ));
        if let Some(ref cfg) = version.cfg_info {
            if let Some(ref ci) = cfg.commit_info {
                version_string.push_str(&format!("commit-hash: {}\n", ci.commit_hash));
                version_string.push_str(&format!("commit-date: {}\n", ci.commit_date));
            }
        }
    }
    version_string
}

fn expand_aliases(
    config: &mut Config,
    args: ArgMatches<'static>,
    mut already_expanded: Vec<String>,
) -> Result<(ArgMatches<'static>, GlobalArgs), CliError> {
    if let (cmd, Some(args)) = args.subcommand() {
        match (
            commands::builtin_exec(cmd),
            super::aliased_command(config, cmd)?,
        ) {
            (Some(_), Some(_)) => {
                // User alias conflicts with a built-in subcommand
                config.shell().warn(format!(
                    "user-defined alias `{}` is ignored, because it is shadowed by a built-in command",
                    cmd,
                ))?;
            }
            (Some(_), None) => {
                // Command is built-in and is not conflicting with alias, but contains ignored values.
                if let Some(mut values) = args.values_of("") {
                    config.shell().warn(format!(
                        "trailing arguments after built-in command `{}` are ignored: `{}`",
                        cmd,
                        values.join(" "),
                    ))?;
                }
            }
            (None, None) => {}
            (_, Some(mut alias)) => {
                alias.extend(
                    args.values_of("")
                        .unwrap_or_default()
                        .map(|s| s.to_string()),
                );
                // new_args strips out everything before the subcommand, so
                // capture those global options now.
                // Note that an alias to an external command will not receive
                // these arguments. That may be confusing, but such is life.
                let global_args = GlobalArgs::new(args);
                let new_args = cli()
                    .setting(AppSettings::NoBinaryName)
                    .get_matches_from_safe(alias)?;

                let (new_cmd, _) = new_args.subcommand();
                already_expanded.push(cmd.to_string());
                if already_expanded.contains(&new_cmd.to_string()) {
                    // Crash if the aliases are corecursive / unresolvable
                    return Err(anyhow!(
                        "alias {} has unresolvable recursive definition: {} -> {}",
                        already_expanded[0],
                        already_expanded.join(" -> "),
                        new_cmd,
                    )
                    .into());
                }

                let (expanded_args, _) = expand_aliases(config, new_args, already_expanded)?;
                return Ok((expanded_args, global_args));
            }
        }
    };

    Ok((args, GlobalArgs::default()))
}

fn config_configure(
    config: &mut Config,
    args: &ArgMatches<'_>,
    subcommand_args: &ArgMatches<'_>,
    global_args: GlobalArgs,
) -> CliResult {
    let arg_target_dir = &subcommand_args.value_of_path("target-dir", config);
    let verbose = global_args.verbose + args.occurrences_of("verbose") as u32;
    // quiet is unusual because it is redefined in some subcommands in order
    // to provide custom help text.
    let quiet =
        args.is_present("quiet") || subcommand_args.is_present("quiet") || global_args.quiet;
    let global_color = global_args.color; // Extract so it can take reference.
    let color = args.value_of("color").or_else(|| global_color.as_deref());
    let frozen = args.is_present("frozen") || global_args.frozen;
    let locked = args.is_present("locked") || global_args.locked;
    let offline = args.is_present("offline") || global_args.offline;
    let mut unstable_flags = global_args.unstable_flags;
    if let Some(values) = args.values_of("unstable-features") {
        unstable_flags.extend(values.map(|s| s.to_string()));
    }
    let mut config_args = global_args.config_args;
    if let Some(values) = args.values_of("config") {
        config_args.extend(values.map(|s| s.to_string()));
    }
    config.configure(
        verbose,
        quiet,
        color,
        frozen,
        locked,
        offline,
        arg_target_dir,
        &unstable_flags,
        &config_args,
    )?;
    Ok(())
}

fn execute_subcommand(
    config: &mut Config,
    cmd: &str,
    subcommand_args: &ArgMatches<'_>,
) -> CliResult {
    if let Some(exec) = commands::builtin_exec(cmd) {
        return exec(config, subcommand_args);
    }

    let mut ext_args: Vec<&str> = vec![cmd];
    ext_args.extend(subcommand_args.values_of("").unwrap_or_default());
    super::execute_external_subcommand(config, cmd, &ext_args)
}

#[derive(Default)]
struct GlobalArgs {
    verbose: u32,
    quiet: bool,
    color: Option<String>,
    frozen: bool,
    locked: bool,
    offline: bool,
    unstable_flags: Vec<String>,
    config_args: Vec<String>,
}

impl GlobalArgs {
    fn new(args: &ArgMatches<'_>) -> GlobalArgs {
        GlobalArgs {
            verbose: args.occurrences_of("verbose") as u32,
            quiet: args.is_present("quiet"),
            color: args.value_of("color").map(|s| s.to_string()),
            frozen: args.is_present("frozen"),
            locked: args.is_present("locked"),
            offline: args.is_present("offline"),
            unstable_flags: args
                .values_of_lossy("unstable-features")
                .unwrap_or_default(),
            config_args: args
                .values_of("config")
                .unwrap_or_default()
                .map(|s| s.to_string())
                .collect(),
        }
    }
}

fn cli() -> App {
    let is_rustup = std::env::var_os("RUSTUP_HOME").is_some();
    let usage = if is_rustup {
        "cargo [+toolchain] [OPTIONS] [SUBCOMMAND]"
    } else {
        "cargo [OPTIONS] [SUBCOMMAND]"
    };
    App::new("cargo")
        .settings(&[
            AppSettings::UnifiedHelpMessage,
            AppSettings::DeriveDisplayOrder,
            AppSettings::VersionlessSubcommands,
            AppSettings::AllowExternalSubcommands,
        ])
        .usage(usage)
        .template(
            "\
Rust's package manager

USAGE:
    {usage}

OPTIONS:
{unified}

Some common cargo commands are (see all commands with --list):
    build, b    Compile the current package
    check, c    Analyze the current package and report errors, but don't build object files
    clean       Remove the target directory
    doc, d      Build this package's and its dependencies' documentation
    new         Create a new cargo package
    init        Create a new cargo package in an existing directory
    run, r      Run a binary or example of the local package
    test, t     Run the tests
    bench       Run the benchmarks
    update      Update dependencies listed in Cargo.lock
    search      Search registry for crates
    publish     Package and upload this package to the registry
    install     Install a Rust binary. Default location is $HOME/.cargo/bin
    uninstall   Uninstall a Rust binary

See 'cargo help <command>' for more information on a specific command.\n",
        )
        .arg(opt("version", "Print version info and exit").short("V"))
        .arg(opt("list", "List installed commands"))
        .arg(opt("explain", "Run `rustc --explain CODE`").value_name("CODE"))
        .arg(
            opt(
                "verbose",
                "Use verbose output (-vv very verbose/build.rs output)",
            )
            .short("v")
            .multiple(true)
            .global(true),
        )
        .arg(opt("quiet", "No output printed to stdout").short("q"))
        .arg(
            opt("color", "Coloring: auto, always, never")
                .value_name("WHEN")
                .global(true),
        )
        .arg(opt("frozen", "Require Cargo.lock and cache are up to date").global(true))
        .arg(opt("locked", "Require Cargo.lock is up to date").global(true))
        .arg(opt("offline", "Run without accessing the network").global(true))
        .arg(
            multi_opt(
                "config",
                "KEY=VALUE",
                "Override a configuration value (unstable)",
            )
            .global(true),
        )
        .arg(
            Arg::with_name("unstable-features")
                .help("Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details")
                .short("Z")
                .value_name("FLAG")
                .multiple(true)
                .number_of_values(1)
                .global(true),
        )
        .subcommands(commands::builtin())
}
<|endoftext|>


<commit_before>#![feature(iter_arith)]
extern crate mpi;
extern crate num;
extern crate time;

use mpi::traits::*;
use mpi::topology::SystemCommunicator;
use num::pow;
use time::precise_time_s;

const MAX_MSG_SIZE: isize = (1<<22);
const MAX_ALIGNMENT: isize = 16384;
const BUF_SIZE: isize = MAX_MSG_SIZE + MAX_ALIGNMENT;
const LARGE_MSG_SIZE: isize = 8192 - 1;
const LOOP_SMALL: isize = 10000;
const LOOP_LARGE: isize = 1000;
const SKIP_SMALL: isize = 100;
const SKIP_LARGE: isize = 10;

fn main() {
  let universe = mpi::initialize().unwrap();
  let world = universe.world();
  let size = world.size();
  let rank = world.rank();
  let pairs = size/2;

  if rank == 0 {
    println!("# RUST OSU MPI Multi Latency Test v1.0");
    println!("# Size               Latency (us)");
  }

  world.barrier();
  multi_latency(rank, pairs, world);
  world.barrier();
}

fn multi_latency(rank: i32, pairs: i32, world: SystemCommunicator) {
  let s_buf: std::vec::Vec<u32> = (0..BUF_SIZE).map(|_| 1).collect();
  let root_process = world.process_at_rank(0);

  for size in (0..1).chain((0..23).map(|x| pow(2, x))) {
    let mut t_start = 0.0f64;

    world.barrier();

    let (range, extra) = match size {
      x @ _ if x > LARGE_MSG_SIZE => (LOOP_LARGE, SKIP_LARGE),
      _ => (LOOP_SMALL, SKIP_SMALL)
    };

    match rank {
      _ if rank < pairs => {
        let peer_rank = rank + pairs;

        for iter in (0 .. range + extra) {
          if iter == extra {
            // TODO: make a PR to add MPI_Wtime
            t_start = precise_time_s();
            world.barrier();
          }

          world.process_at_rank(peer_rank).send(&s_buf[0.. size as usize]);
          // XXX: This will probabaly hurt performance in comarison to C.
          let (_, _) = world.receive_vec::<u32>();
        }
      },
      _ => {
        let peer_rank = rank - pairs;

        for iter in (0 .. range + extra) {
          if iter == extra {
            t_start = precise_time_s();
            world.barrier();
          }

          // XXX: This will probabaly hurt performance in comarison to C.
          let (_, _) = world.receive_vec::<u32>();
          world.process_at_rank(peer_rank).send(&s_buf[0.. size as usize]);
        }
      }
    }

    let t_end = precise_time_s();
    let latency = (t_end - t_start) * 1.0e6 / (2.0 * range as f64);

    // XXX: Using AllGather instead of Reduce.
    let mut a = if world.rank() == 0 {
      Some(std::iter::repeat(0.0f64).take(world.size() as usize).collect::<Vec<f64>>())
    } else {
      None
    };

    root_process.gather_into(&latency, a.as_mut().map(|x| &mut x[..]));

    if world.rank() == 0 {
      let t: Vec<f64> = a.unwrap();
      let avg_latency: f64 = t.iter().sum::<f64>() / (pairs * 2) as f64;
      println!("{:<28} {1:.2}", size, avg_latency);
    }
  }
}
<commit_msg>Use mpi reduce improve send buffer, was kind of a hack before.<commit_after>extern crate mpi;
extern crate num;
extern crate time;

use mpi::traits::*;
use mpi::topology::SystemCommunicator;
use num::pow;
use time::precise_time_s;
use mpi::collective::SystemOperation;

const MAX_MSG_SIZE: usize = (1<<22);
const MAX_ALIGNMENT: usize = 16384;
const BUF_SIZE: usize = MAX_MSG_SIZE + MAX_ALIGNMENT;
const LARGE_MSG_SIZE: isize = 8192 - 1;
const LOOP_SMALL: isize = 10000;
const LOOP_LARGE: isize = 1000;
const SKIP_SMALL: isize = 100;
const SKIP_LARGE: isize = 10;

fn main() {
  let universe = mpi::initialize().unwrap();
  let world = universe.world();
  let size = world.size();
  let rank = world.rank();
  let pairs = size/2;

  if rank == 0 {
    println!("# RUST OSU MPI Multi Latency Test v1.0");
    println!("# Size               Latency (us)");
  }

  world.barrier();
  multi_latency(rank, pairs, world);
  world.barrier();
}

fn multi_latency(rank: i32, pairs: i32, world: SystemCommunicator) {
  let s_buf = vec![0;BUF_SIZE];
  let root_process = world.process_at_rank(0);

  for size in (0..1).chain((0..23).map(|x| pow(2, x))) {
    let mut t_start = 0.0f64;

    world.barrier();

    let (range, extra) = match size {
      x @ _ if x > LARGE_MSG_SIZE => (LOOP_LARGE, SKIP_LARGE),
      _ => (LOOP_SMALL, SKIP_SMALL)
    };

    match rank {
      _ if rank < pairs => {
        let peer_rank = rank + pairs;

        for iter in (0 .. range + extra) {
          if iter == extra {
            // TODO: make a PR to add MPI_Wtime
            t_start = precise_time_s();
            world.barrier();
          }

          world.process_at_rank(peer_rank).send(&s_buf[0.. size as usize]);
          // XXX: This will probabaly hurt performance in comarison to C.
          let (_, _) = world.receive_vec::<u32>();
        }
      },
      _ => {
        let peer_rank = rank - pairs;

        for iter in (0 .. range + extra) {
          if iter == extra {
            t_start = precise_time_s();
            world.barrier();
          }

          // XXX: This will probabaly hurt performance in comarison to C.
          let (_, _) = world.receive_vec::<u32>();
          world.process_at_rank(peer_rank).send(&s_buf[0.. size as usize]);
        }
      }
    }

    let t_end = precise_time_s();
    let latency = ((t_end - t_start) * 1.0e6) / (2.0 * range as f64);

    let mut latency_sum = 0.0f64;
    root_process.reduce_into(&latency, Some(&mut latency_sum), SystemOperation::sum());

    if world.rank() == 0 {
      let avg_latency: f64 = latency_sum / (pairs * 2) as f64;
      println!("{:<28} {1:.2}", size, avg_latency);
    }
  }
}
<|endoftext|>


<commit_before>use std::cmp::Ordering::{Equal, Greater, Less};

use crate::{fastcmp, IVec};

pub(crate) fn binary_search_lub(key: &[u8], s: &[IVec]) -> Option<usize> {
    match binary_search(key, s) {
        Ok(i) => Some(i),
        Err(i) if i == 0 => None,
        Err(i) => Some(i - 1),
    }
}

pub fn binary_search(key: &[u8], s: &[IVec]) -> Result<usize, usize> {
    let mut size = s.len();
    if size == 0 || *key < *s[0] {
        return Err(0);
    }
    let mut base = 0_usize;
    while size > 1 {
        let half = size / 2;
        let mid = base + half;
        // mid is always in [0, size), that means mid is >= 0 and < size.
        // mid >= 0: by definition
        // mid < size: mid = size / 2 + size / 4 + size / 8 ...
        #[allow(unsafe_code)]
        let l = unsafe { s.get_unchecked(mid).as_ref() };
        let cmp = fastcmp(l, key);
        base = if cmp == Greater { base } else { mid };
        size -= half;
    }
    // base is always in [0, size) because base <= mid.
    #[allow(unsafe_code)]
    let l = unsafe { s.get_unchecked(base).as_ref() };
    let cmp = fastcmp(l, key);
    if cmp == Equal { Ok(base) } else { Err(base + (cmp == Less) as usize) }
}

#[test]
fn basic_functionality() {
    let s = vec![
        vec![4].into(),
        vec![5].into(),
        vec![5].into(),
        vec![6].into(),
        vec![9].into(),
    ];
    assert_eq!(binary_search_lub(&[3], &*s), None);
    assert_eq!(binary_search_lub(&[4], &*s), Some(0));
    assert_eq!(binary_search_lub(&[5], &*s), Some(2));
    assert_eq!(binary_search_lub(&[6], &*s), Some(3));
    assert_eq!(binary_search_lub(&[7], &*s), Some(3));
    assert_eq!(binary_search_lub(&[8], &*s), Some(3));
    assert_eq!(binary_search_lub(&[9], &*s), Some(4));
    assert_eq!(binary_search_lub(&[10], &*s), Some(4));

    let mut s = s;
    s.clear();
    assert_eq!(binary_search_lub(&[8], &*s), None);
}
<commit_msg>Remove binary search module<commit_after><|endoftext|>


<commit_before>use std::collections::{BTreeMap, HashMap};
use std::path::{Path, PathBuf};
use syn::spanned::Spanned;
use syn::*;

#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct BranchContext {
    files: HashMap<PathBuf, BranchAnalysis>,
}

impl BranchContext {
    pub fn is_branch<P: AsRef<Path>>(&self, path: P, line: usize) -> bool {
        if let Some(file) = self.files.get(path.as_ref()) {
            file.is_branch(line)
        } else {
            false
        }
    }
}

/// Coverage context for all the branches
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct BranchAnalysis {
    /// Each key is `LineRange` showing a region of the code containing a set of branches with the
    /// value being a `LineRange` for each branch in the code
    /// TODO consider BTreeMap then can order on line range start
    branches: BTreeMap<LineRange, Branches>,
}

impl BranchAnalysis {
    /// Returns true if the line is part of a branch
    pub fn is_branch(&self, line: usize) -> bool {
        self.branches.iter().any(|(k, _)| k.contains(line))
    }

    pub fn register_expr(&mut self, expr: &Expr) {
        let range = LineRange::from(expr);
        match *expr {
            Expr::If(ref e) => {
                self.branches.insert(range, e.into());
            }
            Expr::Match(ref e) => {
                self.branches.insert(range, e.into());
            }
            Expr::ForLoop(ref e) => {
                self.branches.insert(range, e.into());
            }
            Expr::While(ref e) => {
                self.branches.insert(range, e.into());
            }
            _ => {}
        }
    }
}

/// Represents possible branches through an execution
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Branches {
    /// Line ranges for each branch
    ranges: Vec<LineRange>,
    /// Whether there is an implicit or empty default branch i.e. missing or empty `else` in an
    /// `if` statement
    implicit_default: bool,
}

impl From<&ExprIf> for Branches {
    fn from(expr: &ExprIf) -> Self {
        let mut then = LineRange::from(&expr.then_branch);
        then.end -= 1;
        let mut ranges = vec![then];
        let mut else_block = expr.else_branch.as_ref().map(|x| x.1.clone());
        let mut implicit_default = else_block.is_none();
        while let Some(el) = else_block {
            let mut lr = LineRange::from(&el);
            if let Expr::If(ref i) = *el {
                then = LineRange::from(&i.then_branch);
                then.end -= 1;
                ranges.push(then);
                else_block = i.else_branch.as_ref().map(|x| x.1.clone());
                implicit_default = else_block.is_none();
                if let Some(s) = &else_block {
                    let lr2 = LineRange::from(s);
                    if lr2.start < lr.end {
                        lr.end = lr2.start - 1;
                    }
                    ranges.push(lr);
                }
            } else {
                else_block = None;
                ranges.push(lr);
            }
        }
        Self {
            ranges,
            implicit_default,
        }
    }
}

impl From<&ExprMatch> for Branches {
    fn from(expr: &ExprMatch) -> Self {
        let ranges = expr
            .arms
            .iter()
            .map(|x| LineRange::from(x))
            .collect::<Vec<_>>();
        Self {
            implicit_default: false,
            ranges,
        }
    }
}

impl From<&ExprForLoop> for Branches {
    fn from(expr: &ExprForLoop) -> Self {
        let mut range = LineRange::from(&expr.body);
        range.start = expr.expr.span().end().line + 1;
        Self {
            implicit_default: true,
            ranges: vec![range],
        }
    }
}

impl From<&ExprWhile> for Branches {
    fn from(expr: &ExprWhile) -> Self {
        let mut range = LineRange::from(&expr.body);
        range.start = expr.cond.span().end().line + 1;
        Self {
            implicit_default: true,
            ranges: vec![range],
        }
    }
}

/// The start and end of contiguous range of lines. The range is contained within
/// `start..end`
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct LineRange {
    /// Start of the line range (inclusive)
    start: usize,
    /// End of the line range (exclusive)
    end: usize,
}

impl<T> From<T> for LineRange
where
    T: spanned::Spanned,
{
    fn from(t: T) -> Self {
        Self {
            start: t.span().start().line,
            end: t.span().end().line + 1,
        }
    }
}

impl LineRange {
    pub fn new(start: usize, end: usize) -> Self {
        Self { start, end }
    }

    /// Returns true if the line is contained within the line range
    pub fn contains(&self, line: usize) -> bool {
        line >= self.start && line < self.end
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use syn::{parse_file, Expr, Item, Stmt};

    #[test]
    fn if_branches() {
        let source = "fn foo(x: i32) {
            if x > 0 {
                println!(\"BOO\");
            } else if x < 0 {
                todo!()
            }
        }";
        let parser = parse_file(source).unwrap();
        let func = &parser.items[0];
        let mut branches = None;
        if let Item::Fn(func) = func {
            for stmt in &func.block.stmts {
                match stmt {
                    Stmt::Expr(e) => {
                        if let Expr::If(i) = e {
                            branches = Some(Branches::from(i));
                        }
                    }
                    Stmt::Semi(e, _) => {
                        if let Expr::If(i) = e {
                            branches = Some(Branches::from(i));
                        }
                    }
                    _ => {}
                }
            }
        }
        let branches = branches.unwrap();
        assert!(branches.implicit_default);
        assert_eq!(branches.ranges.len(), 2);
        assert_eq!(branches.ranges[0], LineRange::new(2, 4));
        assert_eq!(branches.ranges[1], LineRange::new(4, 6));
        let source = "fn foo(x: i32) {
            if x > 0 {
                println!(\"BOO\");
            } else {
                todo!()
            }
        }";
        let parser = parse_file(source).unwrap();
        let func = &parser.items[0];
        let mut branches = None;
        if let Item::Fn(func) = func {
            for stmt in &func.block.stmts {
                match stmt {
                    Stmt::Expr(e) => {
                        if let Expr::If(i) = e {
                            branches = Some(Branches::from(i));
                        }
                    }
                    Stmt::Semi(e, _) => {
                        if let Expr::If(i) = e {
                            branches = Some(Branches::from(i));
                        }
                    }
                    _ => {}
                }
            }
        }
        let branches = branches.unwrap();
        assert!(!branches.implicit_default);
        assert_eq!(branches.ranges.len(), 2);
        assert_eq!(branches.ranges[0], LineRange::new(2, 4));
        assert_eq!(branches.ranges[1], LineRange::new(4, 7));
    }

    #[test]
    fn match_branches() {
        let source = "fn foo(x: i32) {
            match 4 {
                0 => {
                    todo!();
                },
                1 => {
                    todo!();
                },
                _ => {},
            }
        }";
        let parser = parse_file(source).unwrap();
        let func = &parser.items[0];
        let mut branches = None;
        if let Item::Fn(func) = func {
            for stmt in &func.block.stmts {
                match stmt {
                    Stmt::Expr(e) => {
                        if let Expr::Match(m) = e {
                            branches = Some(Branches::from(m));
                        }
                    }
                    Stmt::Semi(e, _) => {
                        if let Expr::Match(m) = e {
                            branches = Some(Branches::from(m));
                        }
                    }
                    _ => {}
                }
            }
        }
        let branches = branches.unwrap();
        assert!(!branches.implicit_default);
        assert_eq!(branches.ranges.len(), 3);
        assert_eq!(branches.ranges[0], LineRange::new(3, 6));
        assert_eq!(branches.ranges[1], LineRange::new(6, 9));
        assert_eq!(branches.ranges[2], LineRange::new(9, 10));
    }

    #[test]
    fn for_branches() {
        let source = "fn foo(x: i32) {
            for i in 0..1 {
                todo!();
            }
        }";
        let parser = parse_file(source).unwrap();
        let func = &parser.items[0];
        let mut branches = None;
        if let Item::Fn(func) = func {
            for stmt in &func.block.stmts {
                match stmt {
                    Stmt::Expr(e) => {
                        if let Expr::ForLoop(f) = e {
                            branches = Some(Branches::from(f));
                        }
                    }
                    Stmt::Semi(e, _) => {
                        if let Expr::ForLoop(f) = e {
                            branches = Some(Branches::from(f));
                        }
                    }
                    _ => {}
                }
            }
        }
        let branches = branches.unwrap();
        assert!(branches.implicit_default);
        assert_eq!(branches.ranges.len(), 1);
        assert_eq!(branches.ranges[0], LineRange::new(3, 5));
    }

    #[test]
    fn while_branches() {
        let source = "fn foo(x: i32) {
            let mut i = 0;
            while i < 10 {
                i+=1;
            }
        }";
        let parser = parse_file(source).unwrap();
        let func = &parser.items[0];
        let mut branches = None;
        if let Item::Fn(func) = func {
            for stmt in &func.block.stmts {
                match stmt {
                    Stmt::Expr(e) => {
                        if let Expr::While(w) = e {
                            branches = Some(Branches::from(w));
                        }
                    }
                    Stmt::Semi(e, _) => {
                        if let Expr::While(w) = e {
                            branches = Some(Branches::from(w));
                        }
                    }
                    _ => {}
                }
            }
        }
        let branches = branches.unwrap();
        assert!(branches.implicit_default);
        assert_eq!(branches.ranges.len(), 1);
        assert_eq!(branches.ranges[0], LineRange::new(4, 6));
    }
}
<commit_msg>Update comments<commit_after>use std::collections::{BTreeMap, HashMap};
use std::path::{Path, PathBuf};
use syn::spanned::Spanned;
use syn::*;

#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct BranchContext {
    /// Map of files and the branches contained within them
    files: HashMap<PathBuf, BranchAnalysis>,
}

impl BranchContext {
    pub fn is_branch<P: AsRef<Path>>(&self, path: P, line: usize) -> bool {
        if let Some(file) = self.files.get(path.as_ref()) {
            file.is_branch(line)
        } else {
            false
        }
    }
}

/// Coverage context for all the branches
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct BranchAnalysis {
    /// Each key is `LineRange` showing a region of the code containing a set of branches with the
    /// value being a `LineRange` for each branch in the code
    branches: BTreeMap<LineRange, Branches>,
}

impl BranchAnalysis {
    /// Returns true if the line is part of a branch
    pub fn is_branch(&self, line: usize) -> bool {
        self.branches.iter().any(|(k, _)| k.contains(line))
    }

    pub fn register_expr(&mut self, expr: &Expr) {
        let range = LineRange::from(expr);
        match *expr {
            Expr::If(ref e) => {
                self.branches.insert(range, e.into());
            }
            Expr::Match(ref e) => {
                self.branches.insert(range, e.into());
            }
            Expr::ForLoop(ref e) => {
                self.branches.insert(range, e.into());
            }
            Expr::While(ref e) => {
                self.branches.insert(range, e.into());
            }
            _ => {}
        }
    }
}

/// Represents possible branches through an execution
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Branches {
    /// Line ranges for each branch
    ranges: Vec<LineRange>,
    /// Whether there is an implicit or empty default branch i.e. missing or empty `else` in an
    /// `if` statement
    implicit_default: bool,
}

impl From<&ExprIf> for Branches {
    fn from(expr: &ExprIf) -> Self {
        let mut then = LineRange::from(&expr.then_branch);
        then.end -= 1;
        let mut ranges = vec![then];
        let mut else_block = expr.else_branch.as_ref().map(|x| x.1.clone());
        let mut implicit_default = else_block.is_none();
        while let Some(el) = else_block {
            let mut lr = LineRange::from(&el);
            if let Expr::If(ref i) = *el {
                then = LineRange::from(&i.then_branch);
                then.end -= 1;
                ranges.push(then);
                else_block = i.else_branch.as_ref().map(|x| x.1.clone());
                implicit_default = else_block.is_none();
                if let Some(s) = &else_block {
                    let lr2 = LineRange::from(s);
                    if lr2.start < lr.end {
                        lr.end = lr2.start - 1;
                    }
                    ranges.push(lr);
                }
            } else {
                else_block = None;
                ranges.push(lr);
            }
        }
        Self {
            ranges,
            implicit_default,
        }
    }
}

impl From<&ExprMatch> for Branches {
    fn from(expr: &ExprMatch) -> Self {
        let ranges = expr
            .arms
            .iter()
            .map(|x| LineRange::from(x))
            .collect::<Vec<_>>();
        Self {
            implicit_default: false,
            ranges,
        }
    }
}

impl From<&ExprForLoop> for Branches {
    fn from(expr: &ExprForLoop) -> Self {
        let mut range = LineRange::from(&expr.body);
        range.start = expr.expr.span().end().line + 1;
        Self {
            implicit_default: true,
            ranges: vec![range],
        }
    }
}

impl From<&ExprWhile> for Branches {
    fn from(expr: &ExprWhile) -> Self {
        let mut range = LineRange::from(&expr.body);
        range.start = expr.cond.span().end().line + 1;
        Self {
            implicit_default: true,
            ranges: vec![range],
        }
    }
}

/// The start and end of contiguous range of lines. The range is contained within
/// `start..end`
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct LineRange {
    /// Start of the line range (inclusive)
    start: usize,
    /// End of the line range (exclusive)
    end: usize,
}

impl<T> From<T> for LineRange
where
    T: spanned::Spanned,
{
    fn from(t: T) -> Self {
        Self {
            start: t.span().start().line,
            end: t.span().end().line + 1,
        }
    }
}

impl LineRange {
    pub fn new(start: usize, end: usize) -> Self {
        Self { start, end }
    }

    /// Returns true if the line is contained within the line range
    pub fn contains(&self, line: usize) -> bool {
        line >= self.start && line < self.end
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use syn::{parse_file, Expr, Item, Stmt};

    #[test]
    fn if_branches() {
        let source = "fn foo(x: i32) {
            if x > 0 {
                println!(\"BOO\");
            } else if x < 0 {
                todo!()
            }
        }";
        let parser = parse_file(source).unwrap();
        let func = &parser.items[0];
        let mut branches = None;
        if let Item::Fn(func) = func {
            for stmt in &func.block.stmts {
                match stmt {
                    Stmt::Expr(e) => {
                        if let Expr::If(i) = e {
                            branches = Some(Branches::from(i));
                        }
                    }
                    Stmt::Semi(e, _) => {
                        if let Expr::If(i) = e {
                            branches = Some(Branches::from(i));
                        }
                    }
                    _ => {}
                }
            }
        }
        let branches = branches.unwrap();
        assert!(branches.implicit_default);
        assert_eq!(branches.ranges.len(), 2);
        assert_eq!(branches.ranges[0], LineRange::new(2, 4));
        assert_eq!(branches.ranges[1], LineRange::new(4, 6));
        let source = "fn foo(x: i32) {
            if x > 0 {
                println!(\"BOO\");
            } else {
                todo!()
            }
        }";
        let parser = parse_file(source).unwrap();
        let func = &parser.items[0];
        let mut branches = None;
        if let Item::Fn(func) = func {
            for stmt in &func.block.stmts {
                match stmt {
                    Stmt::Expr(e) => {
                        if let Expr::If(i) = e {
                            branches = Some(Branches::from(i));
                        }
                    }
                    Stmt::Semi(e, _) => {
                        if let Expr::If(i) = e {
                            branches = Some(Branches::from(i));
                        }
                    }
                    _ => {}
                }
            }
        }
        let branches = branches.unwrap();
        assert!(!branches.implicit_default);
        assert_eq!(branches.ranges.len(), 2);
        assert_eq!(branches.ranges[0], LineRange::new(2, 4));
        assert_eq!(branches.ranges[1], LineRange::new(4, 7));
    }

    #[test]
    fn match_branches() {
        let source = "fn foo(x: i32) {
            match 4 {
                0 => {
                    todo!();
                },
                1 => {
                    todo!();
                },
                _ => {},
            }
        }";
        let parser = parse_file(source).unwrap();
        let func = &parser.items[0];
        let mut branches = None;
        if let Item::Fn(func) = func {
            for stmt in &func.block.stmts {
                match stmt {
                    Stmt::Expr(e) => {
                        if let Expr::Match(m) = e {
                            branches = Some(Branches::from(m));
                        }
                    }
                    Stmt::Semi(e, _) => {
                        if let Expr::Match(m) = e {
                            branches = Some(Branches::from(m));
                        }
                    }
                    _ => {}
                }
            }
        }
        let branches = branches.unwrap();
        assert!(!branches.implicit_default);
        assert_eq!(branches.ranges.len(), 3);
        assert_eq!(branches.ranges[0], LineRange::new(3, 6));
        assert_eq!(branches.ranges[1], LineRange::new(6, 9));
        assert_eq!(branches.ranges[2], LineRange::new(9, 10));
    }

    #[test]
    fn for_branches() {
        let source = "fn foo(x: i32) {
            for i in 0..1 {
                todo!();
            }
        }";
        let parser = parse_file(source).unwrap();
        let func = &parser.items[0];
        let mut branches = None;
        if let Item::Fn(func) = func {
            for stmt in &func.block.stmts {
                match stmt {
                    Stmt::Expr(e) => {
                        if let Expr::ForLoop(f) = e {
                            branches = Some(Branches::from(f));
                        }
                    }
                    Stmt::Semi(e, _) => {
                        if let Expr::ForLoop(f) = e {
                            branches = Some(Branches::from(f));
                        }
                    }
                    _ => {}
                }
            }
        }
        let branches = branches.unwrap();
        assert!(branches.implicit_default);
        assert_eq!(branches.ranges.len(), 1);
        assert_eq!(branches.ranges[0], LineRange::new(3, 5));
    }

    #[test]
    fn while_branches() {
        let source = "fn foo(x: i32) {
            let mut i = 0;
            while i < 10 {
                i+=1;
            }
        }";
        let parser = parse_file(source).unwrap();
        let func = &parser.items[0];
        let mut branches = None;
        if let Item::Fn(func) = func {
            for stmt in &func.block.stmts {
                match stmt {
                    Stmt::Expr(e) => {
                        if let Expr::While(w) = e {
                            branches = Some(Branches::from(w));
                        }
                    }
                    Stmt::Semi(e, _) => {
                        if let Expr::While(w) = e {
                            branches = Some(Branches::from(w));
                        }
                    }
                    _ => {}
                }
            }
        }
        let branches = branches.unwrap();
        assert!(branches.implicit_default);
        assert_eq!(branches.ranges.len(), 1);
        assert_eq!(branches.ranges[0], LineRange::new(4, 6));
    }
}
<|endoftext|>


<commit_before>#![recursion_limit = "256"]
// http://stackoverflow.com/questions/25877285/how-to-disable-unused-code-warnings-in-rust
// https://users.rust-lang.org/t/turning-off-compiler-warning-messages/4975/2
#![allow(non_snake_case)]

mod api;
// mod cors;
// mod files;

use serde_derive::{Deserialize, Serialize};

extern crate dotenv;
use dotenv::dotenv;
use std::env;

use actix_web::{http::Method, middleware, server, App, FromRequest, HttpRequest, HttpResponse};

extern crate futures;
use futures::future::{result, FutureResult};

extern crate typed_html;
extern crate typed_html_macros;

use typed_html::elements::FlowContent;
use typed_html::types::Metadata;
use typed_html::{dom::DOMTree, html, text, OutputType};

#[derive(Serialize, Debug)]
struct TemplateContext {
    parent: String,
    name: String,
    content: String,
    items: Vec<String>,
}

fn index(_req: &HttpRequest) -> &'static str {
    "Hello world"
}

use std::path;
use std::process::Command;

#[derive(Deserialize)]
struct TemplateParams {
    ssr: bool,
}

fn template(req: &HttpRequest) -> FutureResult<HttpResponse, actix_web::error::Error> {
    let params = actix_web::Path::<TemplateParams>::extract(req).unwrap();
    let ssr = params.ssr;
    let s2 = getStr();
    let s: String = if ssr { s2.to_owned() } else { "".to_owned() };
    let context = TemplateContext {
        parent: "index".to_owned(),
        name: "Roman".to_owned(),
        content: s.to_owned(),
        items: vec!["One", "Two", "Three"]
            .iter()
            .map(|s| s.to_string())
            .collect(),
    };

    let doc: DOMTree<String> = doc(
        html!(

"Hello Kitty"

                    "She is not a cat. She is a human girl."

                { (0..3).map(|_| html!(

                        "Her name is Kitty White."

                )) }

                    "We still don't know how she eats."

"Hello Kitty"

                "DYNAMIC_CONTENT"

        ),
        context,
    );
    let mut doc_str = "<!doctype html>".to_owned() + &doc.to_string();

    // https://users.rust-lang.org/t/how-to-create-a-macro-from-dynamic-content/5079
    doc_str = doc_str.replacen("DYNAMIC_CONTENT", &s2, 1);

    result(Ok(HttpResponse::Ok()
        .content_type("text/html")
        .body(doc_str)))
}

fn doc<T: OutputType + 'static>(
    tree: Box<dyn FlowContent<T>>,
    context: TemplateContext,
) -> DOMTree<T> {
    let TemplateContext { name, items, .. } = context;
    html!(

                <title>"Hello Kitty"</title>
                <meta charset="utf-8" />
                <meta name="viewport" content="width=device-width, initial-scale=1.0" />
                <link rel="icon" href="/favicon.ico?v=1" />
                <link href="/styles/normalize.css" rel="stylesheet" type="text/css" media="all" />
                <link href="/styles/style.css" rel="stylesheet" type="text/css" media="all" />
                <base href="/"></base>
                <meta name=Metadata::Author content="Not Sanrio Co., Ltd" />

{text!("{}", name)}

"Here are your items:"

                   {
                    items.iter().map(|item| html!(

{text!("{}", item)}

                    ))
                   }

                { tree }

"Try going to ""/hello/YourName"

    )
}

use std::fs;
use std::fs::OpenOptions;
use std::io::prelude::*;
extern crate time;

fn getStr() -> String {
    fs::copy(
        "../client/src/elm/Main.elm",
        "../client/src/elm/Main.elm.bak",
    )
    .expect("file not copied");
    let stdout: Vec<u8>;
    {
        let mut main_file = OpenOptions::new()
            .append(true)
            .open("../client/src/elm/Main.elm")
            .expect("file not opened");

        let str1 = format!(
            r#"
            view : Html Msg
            view =
                viewWithModel <|
                    Model [] HomeRoute <|
                        Flags "" {time} ""
            "#,
            time = time::get_time().sec
        );

        main_file
            .write_all(str1.as_bytes())
            .expect("file content not saved");

        stdout = Command::new("node")
            .current_dir(&path::Path::new("../client"))
            .arg("./node_modules/elm-static-html/index.js")
            .arg("-f")
            .arg("src/elm/Main.elm")
            .output()
            .expect("elm-static-html command failed to start")
            .stdout;
    }
    fs::rename(
        "../client/src/elm/Main.elm.bak",
        "../client/src/elm/Main.elm",
    )
    .expect("file not renamed");

    String::from_utf8_lossy(&*stdout)
        .lines()
        .skip_while(|x| x.find("id=\"content\"").is_none())
        .collect()
}

// #[error(404)]
// fn not_found(req: &Request) -> CORS<Template> {
//     let mut map = std::collections::HashMap::new();
//     map.insert("path", req.uri().as_str());
//     CORS::any(Template::render("error/404", &map))
// }

fn main() {
    dotenv().ok();
    env::set_var("RUST_LOG", "actix_web=info");
    env_logger::init();
    let sys = actix::System::new("hello-world");

    server::new(|| {
        App::new()
            .middleware(middleware::Logger::default())
            .resource("/", |r| r.f(index))
            .resource("/template/{ssr}", |r| r.method(Method::GET).a(template))
            .resource("/userInformation", |r| {
                r.method(Method::GET).a(api::user_information)
            })
            .resource("/dataSets", |r| r.method(Method::GET).a(api::data_sets))
            .resource("/dataSets/{url}", |r| {
                r.method(Method::GET).a(api::data_set)
            })
    })
    .bind("127.0.0.1:8080")
    .unwrap()
    .start();

    println!("Starting http server: 127.0.0.1:8080");

    let _ = sys.run();

    // println!("{:?}", env::var("data_server").unwrap());
    // rocket::ignite()
    //     .mount(
    //         "/",
    //         routes![
    //             template,
    //             files::favicon,
    //             files::index,
    //             files::redirect_to_index,
    //             files::js,
    //             files::styles,
    //             files::styles_with_query,
    //             files::images
    //         ],
    //     )
    //     .mount(
    //         "/api",
    //         routes![
    //             api::user_information,
    //             api::data_sets,
    //             api::data_set,
    //             api::data_set_category,
    //             api::data_sets_categories,
    //             api::data_sets_new
    //         ],
    //     )
    //     .catch(errors![not_found])
    //     .launch();
}
<commit_msg>Remove remaining externs, add expect<commit_after>#![recursion_limit = "256"]
// http://stackoverflow.com/questions/25877285/how-to-disable-unused-code-warnings-in-rust
// https://users.rust-lang.org/t/turning-off-compiler-warning-messages/4975/2
#![allow(non_snake_case)]

mod api;
// mod cors;
// mod files;

use serde_derive::{Deserialize, Serialize};

use dotenv::dotenv;
use std::env;

use actix_web::{http::Method, middleware, server, App, FromRequest, HttpRequest, HttpResponse};

use futures::future::{result, FutureResult};

use typed_html::elements::FlowContent;
use typed_html::types::Metadata;
use typed_html::{dom::DOMTree, html, text, OutputType};

#[derive(Serialize, Debug)]
struct TemplateContext {
    parent: String,
    name: String,
    content: String,
    items: Vec<String>,
}

fn index(_req: &HttpRequest) -> &'static str {
    "Hello world"
}

use std::path;
use std::process::Command;

#[derive(Deserialize)]
struct TemplateParams {
    ssr: bool,
}

fn template(req: &HttpRequest) -> FutureResult<HttpResponse, actix_web::error::Error> {
    let params = actix_web::Path::<TemplateParams>::extract(req).expect("Path extract failed");
    let ssr = params.ssr;
    let s2 = getStr();
    let s: String = if ssr { s2.to_owned() } else { "".to_owned() };
    let context = TemplateContext {
        parent: "index".to_owned(),
        name: "Roman".to_owned(),
        content: s.to_owned(),
        items: vec!["One", "Two", "Three"]
            .iter()
            .map(|s| s.to_string())
            .collect(),
    };

    let doc: DOMTree<String> = doc(
        html!(

"Hello Kitty"

                    "She is not a cat. She is a human girl."

                { (0..3).map(|_| html!(

                        "Her name is Kitty White."

                )) }

                    "We still don't know how she eats."

"Hello Kitty"

                "DYNAMIC_CONTENT"

        ),
        context,
    );
    let mut doc_str = "<!doctype html>".to_owned() + &doc.to_string();

    // https://users.rust-lang.org/t/how-to-create-a-macro-from-dynamic-content/5079
    doc_str = doc_str.replacen("DYNAMIC_CONTENT", &s2, 1);

    result(Ok(HttpResponse::Ok()
        .content_type("text/html")
        .body(doc_str)))
}

fn doc<T: OutputType + 'static>(
    tree: Box<dyn FlowContent<T>>,
    context: TemplateContext,
) -> DOMTree<T> {
    let TemplateContext { name, items, .. } = context;
    html!(

                <title>"Hello Kitty"</title>
                <meta charset="utf-8" />
                <meta name="viewport" content="width=device-width, initial-scale=1.0" />
                <link rel="icon" href="/favicon.ico?v=1" />
                <link href="/styles/normalize.css" rel="stylesheet" type="text/css" media="all" />
                <link href="/styles/style.css" rel="stylesheet" type="text/css" media="all" />
                <base href="/"></base>
                <meta name=Metadata::Author content="Not Sanrio Co., Ltd" />

{text!("{}", name)}

"Here are your items:"

                   {
                    items.iter().map(|item| html!(

{text!("{}", item)}

                    ))
                   }

                { tree }

"Try going to ""/hello/YourName"

    )
}

use std::fs;
use std::fs::OpenOptions;
use std::io::prelude::*;

fn getStr() -> String {
    fs::copy(
        "../client/src/elm/Main.elm",
        "../client/src/elm/Main.elm.bak",
    )
    .expect("file not copied");
    let stdout: Vec<u8>;
    {
        let mut main_file = OpenOptions::new()
            .append(true)
            .open("../client/src/elm/Main.elm")
            .expect("file not opened");

        let str1 = format!(
            r#"
            view : Html Msg
            view =
                viewWithModel <|
                    Model [] HomeRoute <|
                        Flags "" {time} ""
            "#,
            time = time::get_time().sec
        );

        main_file
            .write_all(str1.as_bytes())
            .expect("file content not saved");

        stdout = Command::new("node")
            .current_dir(&path::Path::new("../client"))
            .arg("./node_modules/elm-static-html/index.js")
            .arg("-f")
            .arg("src/elm/Main.elm")
            .output()
            .expect("elm-static-html command failed to start")
            .stdout;
    }
    fs::rename(
        "../client/src/elm/Main.elm.bak",
        "../client/src/elm/Main.elm",
    )
    .expect("file not renamed");

    String::from_utf8_lossy(&*stdout)
        .lines()
        .skip_while(|x| x.find("id=\"content\"").is_none())
        .collect()
}

// #[error(404)]
// fn not_found(req: &Request) -> CORS<Template> {
//     let mut map = std::collections::HashMap::new();
//     map.insert("path", req.uri().as_str());
//     CORS::any(Template::render("error/404", &map))
// }

fn main() {
    dotenv().ok();
    env::set_var("RUST_LOG", "actix_web=info");
    env_logger::init();
    let sys = actix::System::new("hello-world");

    server::new(|| {
        App::new()
            .middleware(middleware::Logger::default())
            .resource("/", |r| r.f(index))
            .resource("/template/{ssr}", |r| r.method(Method::GET).a(template))
            .resource("/userInformation", |r| {
                r.method(Method::GET).a(api::user_information)
            })
            .resource("/dataSets", |r| r.method(Method::GET).a(api::data_sets))
            .resource("/dataSets/{url}", |r| {
                r.method(Method::GET).a(api::data_set)
            })
    })
    .bind("127.0.0.1:8080")
    .unwrap()
    .start();

    println!("Starting http server: 127.0.0.1:8080");

    let _ = sys.run();

    // println!("{:?}", env::var("data_server").unwrap());
    // rocket::ignite()
    //     .mount(
    //         "/",
    //         routes![
    //             template,
    //             files::favicon,
    //             files::index,
    //             files::redirect_to_index,
    //             files::js,
    //             files::styles,
    //             files::styles_with_query,
    //             files::images
    //         ],
    //     )
    //     .mount(
    //         "/api",
    //         routes![
    //             api::user_information,
    //             api::data_sets,
    //             api::data_set,
    //             api::data_set_category,
    //             api::data_sets_categories,
    //             api::data_sets_new
    //         ],
    //     )
    //     .catch(errors![not_found])
    //     .launch();
}
<|endoftext|>


<commit_before>extern crate luthor;

use std::rc::Rc;
use std::cell::RefCell;
use std::str::from_utf8;
use std::fs::File;
use std::io;
use std::io::{Read, Write};
use std::path::PathBuf;
use super::GapBuffer;
use super::gap_buffer;
use super::Position;
use super::Range;
use super::Cursor;
use super::type_detection;
use self::luthor::token::{Token, Category};
use self::luthor::lexers;

/// A UTF-8 buffer with bounds-checked cursor management and persistence.
pub struct Buffer {
    data: Rc<RefCell<GapBuffer>>,
    lexer: Option<fn(&str) -> Vec<Token>>,
    pub path: Option<PathBuf>,
    pub cursor: Cursor,
}

impl Buffer {
    /// Returns the contents of the buffer as a string.
    ///
    /// # Examples
    ///
    /// ```
    /// let mut buffer = scribe::buffer::new();
    /// buffer.insert("scribe");
    /// assert_eq!(buffer.data(), "scribe");
    /// ```
    pub fn data(&self) -> String {
        self.data.borrow().to_string()
    }

    /// Writes the contents of the buffer to its path.
    ///
    /// # Examples
    ///
    /// ```
    /// # #![feature(convert)]
    /// # use std::path::PathBuf;
    /// # use std::path::Path;
    /// # use std::fs::File;
    /// # use std::io::Read;
    ///
    /// // Set up a buffer and point it to a path.
    /// let mut buffer = scribe::buffer::new();
    /// let write_path = PathBuf::from("my_doc");
    /// buffer.path = Some(write_path.clone());
    ///
    /// // Put some data into the buffer and save it.
    /// buffer.insert("scribe");
    /// buffer.save();
    ///
    /// # let mut saved_data = String::new();
    /// # File::open(Path::new("my_doc")).unwrap().
    /// #   read_to_string(&mut saved_data).unwrap();
    /// # assert_eq!(saved_data, "scribe");
    ///
    /// # std::fs::remove_file(&write_path);
    /// ```
    pub fn save(&self) -> Option<io::Error> {
        let path = match self.path.clone() {
            Some(p) => p,
            None => PathBuf::new(),
        };

        // Try to open and write to the file, returning any errors encountered.
        let mut file = match File::create(&path) {
            Ok(f) => f,
            Err(error) => return Some(error),
        };

        // We use to_string here because we don't want to write the gap contents.
        match file.write_all(self.data().to_string().as_bytes()) {
            Ok(_) => (),
            Err(error) => return Some(error),
        }

        return None
    }

    /// Inserts `data` into the buffer at the cursor position.
    ///
    /// # Examples
    ///
    /// ```
    /// let mut buffer = scribe::buffer::new();
    /// buffer.insert("scribe");
    /// assert_eq!(buffer.data(), "scribe");
    /// ```
    pub fn insert(&mut self, data: &str) {
        self.data.borrow_mut().insert(data, &self.cursor);
    }

    /// Deletes a character at the cursor position. If at the end
    /// of the current line, it'll try to delete a newline character
    /// (joining the lines), succeeding if there's a line below.
    ///
    /// # Examples
    ///
    /// ```
    /// let mut buffer = scribe::buffer::new();
    /// buffer.insert("scribe");
    /// buffer.delete();
    /// assert_eq!(buffer.data(), "cribe");
    /// ```
    pub fn delete(&mut self) {
        // We need to specify a range to delete, so start at
        // the current offset and delete the character to the right.
        let mut end = Position{ line: self.cursor.line, offset: self.cursor.offset + 1 };

        // If there isn't a character to the right,
        // delete the newline by jumping to the start
        // of the next line. If it doesn't exist, that's okay;
        // these values are bounds-checked by delete() anyway.
        if !self.data.borrow().in_bounds(&end) {
            end.line += 1;
            end.offset = 0;
        }

        self.data.borrow_mut().delete(&Range{ start: *self.cursor, end: end});
    }

    /// Produces a set of tokens based on the buffer data
    /// suitable for colorized display, using a lexer for the
    /// buffer data's language and/or format. If a lexer is not
    /// available, the set will consist of a single text-category token.
    ///
    /// # Examples
    ///
    /// ```
    /// let mut buffer = scribe::buffer::new();
    /// buffer.insert("scribe");
    ///
    /// // Build the buffer data string back by combining its token lexemes.
    /// let mut data = String::new();
    /// for token in buffer.tokens().iter() {
    ///     data.push_str(&token.lexeme);
    /// }
    /// assert_eq!(data, "scribe");
    /// ```
    pub fn tokens(&self) -> Vec<Token> {
        match self.lexer {
            Some(lexer) => lexer(&self.data()),
            None => vec![Token{ lexeme: self.data(), category: Category::Text }],
        }
    }

    /// Returns the file name portion of the buffer's path, if
    /// the path is set and its file name is a valid UTF-8 sequence.
    ///
    /// # Examples
    ///
    /// ```
    /// # #![feature(convert)]
    /// use std::path::PathBuf;
    ///
    /// let file_path = PathBuf::from("tests/sample/file");
    /// let buffer = scribe::buffer::from_file(file_path).unwrap();
    /// assert_eq!(buffer.file_name().unwrap(), "file");
    /// ```
    pub fn file_name(&self) -> Option<String> {
        match self.path {
            Some(ref path) => {
                match path.file_name() {
                    Some(file_name) => {
                        match file_name.to_str() {
                            Some(utf8_file_name) => Some(utf8_file_name.to_string()),
                            None => None,
                        }
                    },
                    None => None,
                }
            },
            None => None,
        }
    }
}

/// Creates a new empty buffer. The buffer's cursor is set to the beginning of the buffer.
///
/// # Examples
///
/// ```
/// let buffer = scribe::buffer::new();
/// # assert_eq!(buffer.cursor.line, 0);
/// # assert_eq!(buffer.cursor.offset, 0);
/// ```
pub fn new() -> Buffer {
    let data = Rc::new(RefCell::new(gap_buffer::new(String::new())));
    let cursor = Cursor{ data: data.clone(), position: Position{ line: 0, offset: 0 }};

    Buffer{ data: data.clone(), path: None, cursor: cursor, lexer: None }
}

/// Creates a new buffer by reading the UTF-8 interpreted file contents of the specified path.
/// The buffer's cursor is set to the beginning of the buffer. The buffer data's type will be
/// inferred based on its extension, and an appropriate lexer will be used, if available (see
/// tokens method for further information on why this happens).
///
/// # Examples
///
/// ```
/// # #![feature(convert)]
/// use std::path::PathBuf;
///
/// let file_path = PathBuf::from("tests/sample/file");
/// let buffer = scribe::buffer::from_file(file_path).unwrap();
/// assert_eq!(buffer.data(), "it works!\n");
/// # assert_eq!(buffer.cursor.line, 0);
/// # assert_eq!(buffer.cursor.offset, 0);
/// ```
pub fn from_file(path: PathBuf) -> io::Result<Buffer> {
    // Try to open and read the file, returning any errors encountered.
    let mut file = match File::open(path.clone()) {
        Ok(f) => f,
        Err(error) => return Err(error),
    };
    let mut data = String::new();
    match file.read_to_string(&mut data) {
        Ok(_) => (),
        Err(error) => return Err(error),
    };

    let data = Rc::new(RefCell::new(gap_buffer::new(data)));
    let cursor = Cursor{ data: data.clone(), position: Position{ line: 0, offset: 0 }};

    // Detect the file type and use its corresponding lexer, if available.
    let lexer = match type_detection::from_path(&path) {
        Some(type_detection::Type::JSON) => Some(lexers::json::lex as fn(&str) -> Vec<Token>),
        Some(type_detection::Type::XML) => Some(lexers::xml::lex as fn(&str) -> Vec<Token>),
        _ => None,
    };

    // Create a new buffer using the loaded data, path, and other defaults.
    Ok(Buffer{ data: data.clone(), path: Some(path), cursor: cursor, lexer: lexer })
}

#[cfg(test)]
mod tests {
    use super::new;
    use super::luthor::token::{Token, Category};

    fn placeholder_lexer(_: &str) -> Vec<Token> {
        vec![Token{ lexeme: "lexer".to_string(), category: Category::Text }]
    }

    #[test]
    fn tokens_returns_one_text_token_when_no_lexer_is_set() {
        let mut buffer = new();
        buffer.insert("scribe");
        let expected_tokens = vec![Token{ lexeme: "scribe".to_string(), category: Category::Text }];
        assert_eq!(buffer.tokens(), expected_tokens);
    }

    #[test]
    fn tokens_returns_result_of_lexer_when_set() {
        let mut buffer = new();
        let expected_tokens = placeholder_lexer("scribe");
        buffer.lexer = Some(placeholder_lexer);
        assert_eq!(buffer.tokens(), expected_tokens);
    }

    #[test]
    fn delete_joins_lines_when_invoked_at_end_of_line() {
        let mut buffer = new();
        buffer.insert("scribe\n library");
        buffer.cursor.move_to_end_of_line();
        buffer.delete();
        assert_eq!(buffer.data(), "scribe library");
    }

    #[test]
    fn delete_does_nothing_when_invoked_at_the_end_of_the_document() {
        let mut buffer = new();
        buffer.insert("scribe\n library");
        buffer.cursor.move_down();
        buffer.cursor.move_to_end_of_line();
        buffer.delete();
        assert_eq!(buffer.data(), "scribe\n library");
    }
}
<commit_msg>Buffer lexer is no longer optional (uses luthor default implementation).<commit_after>extern crate luthor;

use std::rc::Rc;
use std::cell::RefCell;
use std::str::from_utf8;
use std::fs::File;
use std::io;
use std::io::{Read, Write};
use std::path::PathBuf;
use super::GapBuffer;
use super::gap_buffer;
use super::Position;
use super::Range;
use super::Cursor;
use super::type_detection;
use self::luthor::token::{Token, Category};
use self::luthor::lexers;

/// A UTF-8 buffer with bounds-checked cursor management and persistence.
pub struct Buffer {
    data: Rc<RefCell<GapBuffer>>,
    lexer: fn(&str) -> Vec<Token>,
    pub path: Option<PathBuf>,
    pub cursor: Cursor,
}

impl Buffer {
    /// Returns the contents of the buffer as a string.
    ///
    /// # Examples
    ///
    /// ```
    /// let mut buffer = scribe::buffer::new();
    /// buffer.insert("scribe");
    /// assert_eq!(buffer.data(), "scribe");
    /// ```
    pub fn data(&self) -> String {
        self.data.borrow().to_string()
    }

    /// Writes the contents of the buffer to its path.
    ///
    /// # Examples
    ///
    /// ```
    /// # #![feature(convert)]
    /// # use std::path::PathBuf;
    /// # use std::path::Path;
    /// # use std::fs::File;
    /// # use std::io::Read;
    ///
    /// // Set up a buffer and point it to a path.
    /// let mut buffer = scribe::buffer::new();
    /// let write_path = PathBuf::from("my_doc");
    /// buffer.path = Some(write_path.clone());
    ///
    /// // Put some data into the buffer and save it.
    /// buffer.insert("scribe");
    /// buffer.save();
    ///
    /// # let mut saved_data = String::new();
    /// # File::open(Path::new("my_doc")).unwrap().
    /// #   read_to_string(&mut saved_data).unwrap();
    /// # assert_eq!(saved_data, "scribe");
    ///
    /// # std::fs::remove_file(&write_path);
    /// ```
    pub fn save(&self) -> Option<io::Error> {
        let path = match self.path.clone() {
            Some(p) => p,
            None => PathBuf::new(),
        };

        // Try to open and write to the file, returning any errors encountered.
        let mut file = match File::create(&path) {
            Ok(f) => f,
            Err(error) => return Some(error),
        };

        // We use to_string here because we don't want to write the gap contents.
        match file.write_all(self.data().to_string().as_bytes()) {
            Ok(_) => (),
            Err(error) => return Some(error),
        }

        return None
    }

    /// Inserts `data` into the buffer at the cursor position.
    ///
    /// # Examples
    ///
    /// ```
    /// let mut buffer = scribe::buffer::new();
    /// buffer.insert("scribe");
    /// assert_eq!(buffer.data(), "scribe");
    /// ```
    pub fn insert(&mut self, data: &str) {
        self.data.borrow_mut().insert(data, &self.cursor);
    }

    /// Deletes a character at the cursor position. If at the end
    /// of the current line, it'll try to delete a newline character
    /// (joining the lines), succeeding if there's a line below.
    ///
    /// # Examples
    ///
    /// ```
    /// let mut buffer = scribe::buffer::new();
    /// buffer.insert("scribe");
    /// buffer.delete();
    /// assert_eq!(buffer.data(), "cribe");
    /// ```
    pub fn delete(&mut self) {
        // We need to specify a range to delete, so start at
        // the current offset and delete the character to the right.
        let mut end = Position{ line: self.cursor.line, offset: self.cursor.offset + 1 };

        // If there isn't a character to the right,
        // delete the newline by jumping to the start
        // of the next line. If it doesn't exist, that's okay;
        // these values are bounds-checked by delete() anyway.
        if !self.data.borrow().in_bounds(&end) {
            end.line += 1;
            end.offset = 0;
        }

        self.data.borrow_mut().delete(&Range{ start: *self.cursor, end: end});
    }

    /// Produces a set of tokens based on the buffer data
    /// suitable for colorized display, using a lexer for the
    /// buffer data's language and/or format.
    ///
    /// # Examples
    ///
    /// ```
    /// let mut buffer = scribe::buffer::new();
    /// buffer.insert("scribe data");
    ///
    /// // Build the buffer data string back by combining its token lexemes.
    /// let mut data = String::new();
    /// for token in buffer.tokens().iter() {
    ///     data.push_str(&token.lexeme);
    /// }
    /// assert_eq!(data, "scribe data");
    /// ```
    pub fn tokens(&self) -> Vec<Token> {
        (self.lexer)(&self.data())
    }

    /// Returns the file name portion of the buffer's path, if
    /// the path is set and its file name is a valid UTF-8 sequence.
    ///
    /// # Examples
    ///
    /// ```
    /// # #![feature(convert)]
    /// use std::path::PathBuf;
    ///
    /// let file_path = PathBuf::from("tests/sample/file");
    /// let buffer = scribe::buffer::from_file(file_path).unwrap();
    /// assert_eq!(buffer.file_name().unwrap(), "file");
    /// ```
    pub fn file_name(&self) -> Option<String> {
        match self.path {
            Some(ref path) => {
                match path.file_name() {
                    Some(file_name) => {
                        match file_name.to_str() {
                            Some(utf8_file_name) => Some(utf8_file_name.to_string()),
                            None => None,
                        }
                    },
                    None => None,
                }
            },
            None => None,
        }
    }
}

/// Creates a new empty buffer. The buffer's cursor is set to the beginning of the buffer.
///
/// # Examples
///
/// ```
/// let buffer = scribe::buffer::new();
/// # assert_eq!(buffer.cursor.line, 0);
/// # assert_eq!(buffer.cursor.offset, 0);
/// ```
pub fn new() -> Buffer {
    let data = Rc::new(RefCell::new(gap_buffer::new(String::new())));
    let cursor = Cursor{ data: data.clone(), position: Position{ line: 0, offset: 0 }};

    Buffer{ data: data.clone(), path: None, cursor: cursor, lexer: lexers::default::lex as fn(&str) -> Vec<Token> }
}

/// Creates a new buffer by reading the UTF-8 interpreted file contents of the specified path.
/// The buffer's cursor is set to the beginning of the buffer. The buffer data's type will be
/// inferred based on its extension, and an appropriate lexer will be used, if available (see
/// tokens method for further information on why this happens).
///
/// # Examples
///
/// ```
/// # #![feature(convert)]
/// use std::path::PathBuf;
///
/// let file_path = PathBuf::from("tests/sample/file");
/// let buffer = scribe::buffer::from_file(file_path).unwrap();
/// assert_eq!(buffer.data(), "it works!\n");
/// # assert_eq!(buffer.cursor.line, 0);
/// # assert_eq!(buffer.cursor.offset, 0);
/// ```
pub fn from_file(path: PathBuf) -> io::Result<Buffer> {
    // Try to open and read the file, returning any errors encountered.
    let mut file = match File::open(path.clone()) {
        Ok(f) => f,
        Err(error) => return Err(error),
    };
    let mut data = String::new();
    match file.read_to_string(&mut data) {
        Ok(_) => (),
        Err(error) => return Err(error),
    };

    let data = Rc::new(RefCell::new(gap_buffer::new(data)));
    let cursor = Cursor{ data: data.clone(), position: Position{ line: 0, offset: 0 }};

    // Detect the file type and use its corresponding lexer, if available.
    let lexer = match type_detection::from_path(&path) {
        Some(type_detection::Type::JSON) => lexers::json::lex as fn(&str) -> Vec<Token>,
        Some(type_detection::Type::XML) => lexers::xml::lex as fn(&str) -> Vec<Token>,
        _ => lexers::default::lex as fn(&str) -> Vec<Token>,
    };

    // Create a new buffer using the loaded data, path, and other defaults.
    Ok(Buffer{ data: data.clone(), path: Some(path), cursor: cursor, lexer: lexer })
}

#[cfg(test)]
mod tests {
    use super::new;
    use super::luthor::token::{Token, Category};

    #[test]
    fn tokens_returns_result_of_lexer() {
        let mut buffer = new();
        buffer.insert("scribe data");
        let expected_tokens = vec![
            Token{ lexeme: "scribe".to_string(), category: Category::Text },
            Token{ lexeme: " ".to_string(), category: Category::Whitespace },
            Token{ lexeme: "data".to_string(), category: Category::Text },
        ];
        assert_eq!(buffer.tokens(), expected_tokens);
    }

    #[test]
    fn delete_joins_lines_when_invoked_at_end_of_line() {
        let mut buffer = new();
        buffer.insert("scribe\n library");
        buffer.cursor.move_to_end_of_line();
        buffer.delete();
        assert_eq!(buffer.data(), "scribe library");
    }

    #[test]
    fn delete_does_nothing_when_invoked_at_the_end_of_the_document() {
        let mut buffer = new();
        buffer.insert("scribe\n library");
        buffer.cursor.move_down();
        buffer.cursor.move_to_end_of_line();
        buffer.delete();
        assert_eq!(buffer.data(), "scribe\n library");
    }
}
<|endoftext|>