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

#![cfg(not(test))]

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

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

use getopts::{Matches, Options};

use rustfmt::{run, Input, Summary};
use rustfmt::file_lines::FileLines;
use rustfmt::config::{get_toml_path, Color, Config, WriteMode};

type FmtError = Box<error::Error + Send + Sync>;
type FmtResult<T> = std::result::Result<T, FmtError>;

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

/// Parsed command line options.
#[derive(Clone, Debug, Default)]
struct CliOptions {
    skip_children: bool,
    verbose: bool,
    write_mode: Option<WriteMode>,
    color: Option<Color>,
    file_lines: FileLines, // Default is all lines in all files.
    unstable_features: bool,
}

impl CliOptions {
    fn from_matches(matches: &Matches) -> FmtResult<CliOptions> {
        let mut options = CliOptions::default();
        options.skip_children = matches.opt_present("skip-children");
        options.verbose = matches.opt_present("verbose");
        let unstable_features = matches.opt_present("unstable-features");
        let rust_nightly = option_env!("CFG_RELEASE_CHANNEL")
            .map(|c| c == "nightly")
            .unwrap_or(false);
        if unstable_features && !rust_nightly {
            return Err(FmtError::from(
                "Unstable features are only available on Nightly channel",
            ));
        } else {
            options.unstable_features = unstable_features;
        }

        if let Some(ref write_mode) = matches.opt_str("write-mode") {
            if let Ok(write_mode) = WriteMode::from_str(write_mode) {
                options.write_mode = Some(write_mode);
            } else {
                return Err(FmtError::from(format!(
                    "Invalid write-mode: {}",
                    write_mode
                )));
            }
        }

        if let Some(ref color) = matches.opt_str("color") {
            match Color::from_str(color) {
                Ok(color) => options.color = Some(color),
                _ => return Err(FmtError::from(format!("Invalid color: {}", color))),
            }
        }

        if let Some(ref file_lines) = matches.opt_str("file-lines") {
            options.file_lines = file_lines.parse()?;
        }

        Ok(options)
    }

    fn apply_to(self, config: &mut Config) {
        config.set().skip_children(self.skip_children);
        config.set().verbose(self.verbose);
        config.set().file_lines(self.file_lines);
        config.set().unstable_features(self.unstable_features);
        if let Some(write_mode) = self.write_mode {
            config.set().write_mode(write_mode);
        }
        if let Some(color) = self.color {
            config.set().color(color);
        }
    }
}

/// read the given config file path recursively if present else read the project file path
fn match_cli_path_or_file(
    config_path: Option<PathBuf>,
    input_file: &Path,
) -> FmtResult<(Config, Option<PathBuf>)> {
    if let Some(config_file) = config_path {
        let toml = Config::from_toml_path(config_file.as_ref())?;
        return Ok((toml, Some(config_file)));
    }
    Config::from_resolved_toml_path(input_file).map_err(FmtError::from)
}

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

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

    opts
}

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

    match determine_operation(&matches)? {
        Operation::Help => {
            print_usage_to_stdout(opts, "");
            Summary::print_exit_codes();
            Ok(Summary::default())
        }
        Operation::Version => {
            print_version();
            Ok(Summary::default())
        }
        Operation::ConfigHelp => {
            Config::print_docs();
            Ok(Summary::default())
        }
        Operation::ConfigOutputDefault { path } => {
            let toml = Config::default().all_options().to_toml()?;
            if let Some(path) = path {
                let mut file = File::create(path)?;
                file.write_all(toml.as_bytes())?;
            } else {
                io::stdout().write_all(toml.as_bytes())?;
            }
            Ok(Summary::default())
        }
        Operation::Stdin { input, config_path } => {
            // try to read config from local directory
            let (mut config, _) =
                match_cli_path_or_file(config_path, &env::current_dir().unwrap())?;

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

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

            let mut error_summary = Summary::default();
            if config.version_meets_requirement(&mut error_summary) {
                error_summary.add(run(Input::Text(input), &config));
            }

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

            for f in options.file_lines.files() {
                if !files.contains(&PathBuf::from(f)) {
                    eprintln!("Warning: Extra file listed in file_lines option '{}'", f);
                }
            }

            let mut config = Config::default();
            // Load the config path file if provided
            if let Some(config_file) = config_path.as_ref() {
                config = Config::from_toml_path(config_file.as_ref())?;
            };

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

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

                    if !config.version_meets_requirement(&mut error_summary) {
                        break;
                    }

                    options.clone().apply_to(&mut config);
                    error_summary.add(run(Input::File(file), &config));
                }
            }

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

            Ok(error_summary)
        }
    }
}

fn main() {
    let _ = env_logger::init();

    let opts = make_opts();

    let exit_code = match execute(&opts) {
        Ok(summary) => {
            if summary.has_operational_errors() {
                1
            } else if summary.has_parsing_errors() {
                2
            } else if summary.has_formatting_errors() {
                3
            } else if summary.has_diff {
                // should only happen in diff mode
                4
            } else {
                assert!(summary.has_no_errors());
                0
            }
        }
        Err(e) => {
            print_usage_to_stderr(&opts, &e.to_string());
            1
        }
    };
    // Make sure standard output is flushed before we exit.
    std::io::stdout().flush().unwrap();

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

macro_rules! print_usage {
    ($print:ident, $opts:ident, $reason:expr) => ({
        let msg = format!(
            "{}\n\nusage: {} [options] <file>...",
            $reason,
            env::args_os().next().unwrap().to_string_lossy()
        );
        $print!("{}", $opts.usage(&msg));
    })
}

fn print_usage_to_stdout(opts: &Options, reason: &str) {
    print_usage!(println, opts, reason);
}

fn print_usage_to_stderr(opts: &Options, reason: &str) {
    print_usage!(eprintln, opts, reason);
}

fn print_version() {
    println!(
        "{}-nightly{}",
        env!("CARGO_PKG_VERSION"),
        include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))
    )
}

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

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

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

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

    let config_path_not_found = |path: &str| -> FmtResult<Operation> {
        Err(FmtError::from(format!(
            "Error: unable to find a config file for the given path: `{}`",
            path
        )))
    };

    // Read the config_path and convert to parent dir if a file is provided.
    // If a config file cannot be found from the given path, return error.
    let config_path: Option<PathBuf> = match matches.opt_str("config-path").map(PathBuf::from) {
        Some(ref path) if !path.exists() => return config_path_not_found(path.to_str().unwrap()),
        Some(ref path) if path.is_dir() => {
            let config_file_path = get_toml_path(path)?;
            if config_file_path.is_some() {
                config_file_path
            } else {
                return config_path_not_found(path.to_str().unwrap());
            }
        }
        path => path,
    };

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

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

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

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

    Ok(Operation::Format {
        files: files,
        config_path: config_path,
        minimal_config_path: minimal_config_path,
    })
}
<commit_msg>Set error_on_unformatted_comments_or_strings to true when --error-on-unformatted option is passed<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![cfg(not(test))]

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

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

use getopts::{Matches, Options};

use rustfmt::{run, Input, Summary};
use rustfmt::file_lines::FileLines;
use rustfmt::config::{get_toml_path, Color, Config, WriteMode};

type FmtError = Box<error::Error + Send + Sync>;
type FmtResult<T> = std::result::Result<T, FmtError>;

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

/// Parsed command line options.
#[derive(Clone, Debug, Default)]
struct CliOptions {
    skip_children: bool,
    verbose: bool,
    write_mode: Option<WriteMode>,
    color: Option<Color>,
    file_lines: FileLines, // Default is all lines in all files.
    unstable_features: bool,
    error_on_unformatted: bool,
}

impl CliOptions {
    fn from_matches(matches: &Matches) -> FmtResult<CliOptions> {
        let mut options = CliOptions::default();
        options.skip_children = matches.opt_present("skip-children");
        options.verbose = matches.opt_present("verbose");
        let unstable_features = matches.opt_present("unstable-features");
        let rust_nightly = option_env!("CFG_RELEASE_CHANNEL")
            .map(|c| c == "nightly")
            .unwrap_or(false);
        if unstable_features && !rust_nightly {
            return Err(FmtError::from(
                "Unstable features are only available on Nightly channel",
            ));
        } else {
            options.unstable_features = unstable_features;
        }

        if let Some(ref write_mode) = matches.opt_str("write-mode") {
            if let Ok(write_mode) = WriteMode::from_str(write_mode) {
                options.write_mode = Some(write_mode);
            } else {
                return Err(FmtError::from(format!(
                    "Invalid write-mode: {}",
                    write_mode
                )));
            }
        }

        if let Some(ref color) = matches.opt_str("color") {
            match Color::from_str(color) {
                Ok(color) => options.color = Some(color),
                _ => return Err(FmtError::from(format!("Invalid color: {}", color))),
            }
        }

        if let Some(ref file_lines) = matches.opt_str("file-lines") {
            options.file_lines = file_lines.parse()?;
        }

        if matches.opt_present("error-on-unformatted") {
            options.error_on_unformatted = true;
        }

        Ok(options)
    }

    fn apply_to(self, config: &mut Config) {
        config.set().skip_children(self.skip_children);
        config.set().verbose(self.verbose);
        config.set().file_lines(self.file_lines);
        config.set().unstable_features(self.unstable_features);
        config.set().error_on_unformatted_comments_or_strings(self.error_on_unformatted);
        if let Some(write_mode) = self.write_mode {
            config.set().write_mode(write_mode);
        }
        if let Some(color) = self.color {
            config.set().color(color);
        }
    }
}

/// read the given config file path recursively if present else read the project file path
fn match_cli_path_or_file(
    config_path: Option<PathBuf>,
    input_file: &Path,
) -> FmtResult<(Config, Option<PathBuf>)> {
    if let Some(config_file) = config_path {
        let toml = Config::from_toml_path(config_file.as_ref())?;
        return Ok((toml, Some(config_file)));
    }
    Config::from_resolved_toml_path(input_file).map_err(FmtError::from)
}

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

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

    opts
}

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

    match determine_operation(&matches)? {
        Operation::Help => {
            print_usage_to_stdout(opts, "");
            Summary::print_exit_codes();
            Ok(Summary::default())
        }
        Operation::Version => {
            print_version();
            Ok(Summary::default())
        }
        Operation::ConfigHelp => {
            Config::print_docs();
            Ok(Summary::default())
        }
        Operation::ConfigOutputDefault { path } => {
            let toml = Config::default().all_options().to_toml()?;
            if let Some(path) = path {
                let mut file = File::create(path)?;
                file.write_all(toml.as_bytes())?;
            } else {
                io::stdout().write_all(toml.as_bytes())?;
            }
            Ok(Summary::default())
        }
        Operation::Stdin { input, config_path } => {
            // try to read config from local directory
            let (mut config, _) =
                match_cli_path_or_file(config_path, &env::current_dir().unwrap())?;

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

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

            let mut error_summary = Summary::default();
            if config.version_meets_requirement(&mut error_summary) {
                error_summary.add(run(Input::Text(input), &config));
            }

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

            for f in options.file_lines.files() {
                if !files.contains(&PathBuf::from(f)) {
                    eprintln!("Warning: Extra file listed in file_lines option '{}'", f);
                }
            }

            let mut config = Config::default();
            // Load the config path file if provided
            if let Some(config_file) = config_path.as_ref() {
                config = Config::from_toml_path(config_file.as_ref())?;
            };

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

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

                    if !config.version_meets_requirement(&mut error_summary) {
                        break;
                    }

                    options.clone().apply_to(&mut config);
                    error_summary.add(run(Input::File(file), &config));
                }
            }

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

            Ok(error_summary)
        }
    }
}

fn main() {
    let _ = env_logger::init();

    let opts = make_opts();

    let exit_code = match execute(&opts) {
        Ok(summary) => {
            if summary.has_operational_errors() {
                1
            } else if summary.has_parsing_errors() {
                2
            } else if summary.has_formatting_errors() {
                3
            } else if summary.has_diff {
                // should only happen in diff mode
                4
            } else {
                assert!(summary.has_no_errors());
                0
            }
        }
        Err(e) => {
            print_usage_to_stderr(&opts, &e.to_string());
            1
        }
    };
    // Make sure standard output is flushed before we exit.
    std::io::stdout().flush().unwrap();

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

macro_rules! print_usage {
    ($print:ident, $opts:ident, $reason:expr) => ({
        let msg = format!(
            "{}\n\nusage: {} [options] <file>...",
            $reason,
            env::args_os().next().unwrap().to_string_lossy()
        );
        $print!("{}", $opts.usage(&msg));
    })
}

fn print_usage_to_stdout(opts: &Options, reason: &str) {
    print_usage!(println, opts, reason);
}

fn print_usage_to_stderr(opts: &Options, reason: &str) {
    print_usage!(eprintln, opts, reason);
}

fn print_version() {
    println!(
        "{}-nightly{}",
        env!("CARGO_PKG_VERSION"),
        include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))
    )
}

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

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

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

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

    let config_path_not_found = |path: &str| -> FmtResult<Operation> {
        Err(FmtError::from(format!(
            "Error: unable to find a config file for the given path: `{}`",
            path
        )))
    };

    // Read the config_path and convert to parent dir if a file is provided.
    // If a config file cannot be found from the given path, return error.
    let config_path: Option<PathBuf> = match matches.opt_str("config-path").map(PathBuf::from) {
        Some(ref path) if !path.exists() => return config_path_not_found(path.to_str().unwrap()),
        Some(ref path) if path.is_dir() => {
            let config_file_path = get_toml_path(path)?;
            if config_file_path.is_some() {
                config_file_path
            } else {
                return config_path_not_found(path.to_str().unwrap());
            }
        }
        path => path,
    };

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

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

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

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

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


<commit_before>use std::collections::HashMap;
use std::fmt;

use anyhow::bail;
use semver::Version;
use serde::{de, ser};
use url::Url;

use crate::core::PackageId;
use crate::util::errors::{CargoResult, CargoResultExt};
use crate::util::interning::InternedString;
use crate::util::lev_distance;
use crate::util::{validate_package_name, IntoUrl, ToSemver};

/// Some or all of the data required to identify a package:
///
///  1. the package name (a `String`, required)
///  2. the package version (a `Version`, optional)
///  3. the package source (a `Url`, optional)
///
/// If any of the optional fields are omitted, then the package ID may be ambiguous, there may be
/// more than one package/version/url combo that will match. However, often just the name is
/// sufficient to uniquely define a package ID.
#[derive(Clone, PartialEq, Eq, Debug, Hash, Ord, PartialOrd)]
pub struct PackageIdSpec {
    name: InternedString,
    version: Option<Version>,
    url: Option<Url>,
}

impl PackageIdSpec {
    /// Parses a spec string and returns a `PackageIdSpec` if the string was valid.
    ///
    /// # Examples
    /// Some examples of valid strings
    ///
    /// ```
    /// use cargo::core::PackageIdSpec;
    ///
    /// let specs = vec![
    ///     "https://crates.io/foo#1.2.3",
    ///     "https://crates.io/foo#bar:1.2.3",
    ///     "crates.io/foo",
    ///     "crates.io/foo#1.2.3",
    ///     "crates.io/foo#bar",
    ///     "crates.io/foo#bar:1.2.3",
    ///     "foo",
    ///     "foo:1.2.3",
    /// ];
    /// for spec in specs {
    ///     assert!(PackageIdSpec::parse(spec).is_ok());
    /// }
    pub fn parse(spec: &str) -> CargoResult<PackageIdSpec> {
        if spec.contains('/') {
            if let Ok(url) = spec.into_url() {
                return PackageIdSpec::from_url(url);
            }
            if !spec.contains("://") {
                if let Ok(url) = Url::parse(&format!("cargo://{}", spec)) {
                    return PackageIdSpec::from_url(url);
                }
            }
        }
        let mut parts = spec.splitn(2, ':');
        let name = parts.next().unwrap();
        let version = match parts.next() {
            Some(version) => Some(version.to_semver()?),
            None => None,
        };
        validate_package_name(name, "pkgid", "")?;
        Ok(PackageIdSpec {
            name: InternedString::new(name),
            version,
            url: None,
        })
    }

    /// Roughly equivalent to `PackageIdSpec::parse(spec)?.query(i)`
    pub fn query_str<I>(spec: &str, i: I) -> CargoResult<PackageId>
    where
        I: IntoIterator<Item = PackageId>,
    {
        let spec = PackageIdSpec::parse(spec)
            .chain_err(|| anyhow::format_err!("invalid package ID specification: `{}`", spec))?;
        spec.query(i)
    }

    /// Convert a `PackageId` to a `PackageIdSpec`, which will have both the `Version` and `Url`
    /// fields filled in.
    pub fn from_package_id(package_id: PackageId) -> PackageIdSpec {
        PackageIdSpec {
            name: package_id.name(),
            version: Some(package_id.version().clone()),
            url: Some(package_id.source_id().url().clone()),
        }
    }

    /// Tries to convert a valid `Url` to a `PackageIdSpec`.
    fn from_url(mut url: Url) -> CargoResult<PackageIdSpec> {
        if url.host().is_none() {
            bail!("pkgid urls must have a host: {}", url)
        }
        if url.query().is_some() {
            bail!("cannot have a query string in a pkgid: {}", url)
        }
        let frag = url.fragment().map(|s| s.to_owned());
        url.set_fragment(None);
        let (name, version) = {
            let mut path = url
                .path_segments()
                .ok_or_else(|| anyhow::format_err!("pkgid urls must have a path: {}", url))?;
            let path_name = path.next_back().ok_or_else(|| {
                anyhow::format_err!(
                    "pkgid urls must have at least one path \
                     component: {}",
                    url
                )
            })?;
            match frag {
                Some(fragment) => {
                    let mut parts = fragment.splitn(2, ':');
                    let name_or_version = parts.next().unwrap();
                    match parts.next() {
                        Some(part) => {
                            let version = part.to_semver()?;
                            (InternedString::new(name_or_version), Some(version))
                        }
                        None => {
                            if name_or_version.chars().next().unwrap().is_alphabetic() {
                                (InternedString::new(name_or_version), None)
                            } else {
                                let version = name_or_version.to_semver()?;
                                (InternedString::new(path_name), Some(version))
                            }
                        }
                    }
                }
                None => (InternedString::new(path_name), None),
            }
        };
        Ok(PackageIdSpec {
            name,
            version,
            url: Some(url),
        })
    }

    pub fn name(&self) -> InternedString {
        self.name
    }

    pub fn version(&self) -> Option<&Version> {
        self.version.as_ref()
    }

    pub fn url(&self) -> Option<&Url> {
        self.url.as_ref()
    }

    pub fn set_url(&mut self, url: Url) {
        self.url = Some(url);
    }

    /// Checks whether the given `PackageId` matches the `PackageIdSpec`.
    pub fn matches(&self, package_id: PackageId) -> bool {
        if self.name() != package_id.name() {
            return false;
        }

        if let Some(ref v) = self.version {
            if v != package_id.version() {
                return false;
            }
        }

        match self.url {
            Some(ref u) => u == package_id.source_id().url(),
            None => true,
        }
    }

    /// Checks a list of `PackageId`s to find 1 that matches this `PackageIdSpec`. If 0, 2, or
    /// more are found, then this returns an error.
    pub fn query<I>(&self, i: I) -> CargoResult<PackageId>
    where
        I: IntoIterator<Item = PackageId>,
    {
        let all_ids: Vec<_> = i.into_iter().collect();
        let mut ids = all_ids.iter().copied().filter(|&id| self.matches(id));
        let ret = match ids.next() {
            Some(id) => id,
            None => {
                let mut suggestion = String::new();
                let try_spec = |spec: PackageIdSpec, suggestion: &mut String| {
                    let try_matches: Vec<_> = all_ids
                        .iter()
                        .copied()
                        .filter(|&id| spec.matches(id))
                        .collect();
                    if !try_matches.is_empty() {
                        suggestion.push_str("\nDid you mean one of these?\n");
                        minimize(suggestion, &try_matches, self);
                    }
                };
                if self.url.is_some() {
                    try_spec(
                        PackageIdSpec {
                            name: self.name,
                            version: self.version.clone(),
                            url: None,
                        },
                        &mut suggestion,
                    );
                }
                if suggestion.is_empty() && self.version.is_some() {
                    try_spec(
                        PackageIdSpec {
                            name: self.name,
                            version: None,
                            url: None,
                        },
                        &mut suggestion,
                    );
                }
                if suggestion.is_empty() {
                    suggestion.push_str(&lev_distance::closest_msg(
                        &self.name,
                        all_ids.iter(),
                        |id| id.name().as_str(),
                    ));
                }

                bail!(
                    "package ID specification `{}` did not match any packages{}",
                    self,
                    suggestion
                );
            }
        };
        return match ids.next() {
            Some(other) => {
                let mut msg = format!(
                    "There are multiple `{}` packages in \
                     your project, and the specification \
                     `{}` is ambiguous.\n\
                     Please re-run this command \
                     with `-p <spec>` where `<spec>` is one \
                     of the following:",
                    self.name(),
                    self
                );
                let mut vec = vec![ret, other];
                vec.extend(ids);
                minimize(&mut msg, &vec, self);
                Err(anyhow::format_err!("{}", msg))
            }
            None => Ok(ret),
        };

        fn minimize(msg: &mut String, ids: &[PackageId], spec: &PackageIdSpec) {
            let mut version_cnt = HashMap::new();
            for id in ids {
                *version_cnt.entry(id.version()).or_insert(0) += 1;
            }
            for id in ids {
                if version_cnt[id.version()] == 1 {
                    msg.push_str(&format!("\n  {}:{}", spec.name(), id.version()));
                } else {
                    msg.push_str(&format!("\n  {}", PackageIdSpec::from_package_id(*id)));
                }
            }
        }
    }
}

impl fmt::Display for PackageIdSpec {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut printed_name = false;
        match self.url {
            Some(ref url) => {
                if url.scheme() == "cargo" {
                    let host = url.host().unwrap_or(url::Host::Domain(""));
                    write!(f, "{}{}", host, url.path())?;
                } else {
                    write!(f, "{}", url)?;
                }
                if url.path_segments().unwrap().next_back().unwrap() != &*self.name {
                    printed_name = true;
                    write!(f, "#{}", self.name)?;
                }
            }
            None => {
                printed_name = true;
                write!(f, "{}", self.name)?
            }
        }
        if let Some(ref v) = self.version {
            write!(f, "{}{}", if printed_name { ":" } else { "#" }, v)?;
        }
        Ok(())
    }
}

impl ser::Serialize for PackageIdSpec {
    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
    where
        S: ser::Serializer,
    {
        self.to_string().serialize(s)
    }
}

impl<'de> de::Deserialize<'de> for PackageIdSpec {
    fn deserialize<D>(d: D) -> Result<PackageIdSpec, D::Error>
    where
        D: de::Deserializer<'de>,
    {
        let string = String::deserialize(d)?;
        PackageIdSpec::parse(&string).map_err(de::Error::custom)
    }
}

#[cfg(test)]
mod tests {
    use super::PackageIdSpec;
    use crate::core::{PackageId, SourceId};
    use crate::util::interning::InternedString;
    use crate::util::ToSemver;
    use url::Url;

    #[test]
    fn good_parsing() {
        fn ok(spec: &str, expected: PackageIdSpec) {
            let parsed = PackageIdSpec::parse(spec).unwrap();
            assert_eq!(parsed, expected);
            assert_eq!(parsed.to_string(), spec);
        }

        ok(
            "https://crates.io/foo#1.2.3",
            PackageIdSpec {
                name: InternedString::new("foo"),
                version: Some("1.2.3".to_semver().unwrap()),
                url: Some(Url::parse("https://crates.io/foo").unwrap()),
            },
        );
        ok(
            "https://crates.io/foo#bar:1.2.3",
            PackageIdSpec {
                name: InternedString::new("bar"),
                version: Some("1.2.3".to_semver().unwrap()),
                url: Some(Url::parse("https://crates.io/foo").unwrap()),
            },
        );
        ok(
            "crates.io/foo",
            PackageIdSpec {
                name: InternedString::new("foo"),
                version: None,
                url: Some(Url::parse("cargo://crates.io/foo").unwrap()),
            },
        );
        ok(
            "crates.io/foo#1.2.3",
            PackageIdSpec {
                name: InternedString::new("foo"),
                version: Some("1.2.3".to_semver().unwrap()),
                url: Some(Url::parse("cargo://crates.io/foo").unwrap()),
            },
        );
        ok(
            "crates.io/foo#bar",
            PackageIdSpec {
                name: InternedString::new("bar"),
                version: None,
                url: Some(Url::parse("cargo://crates.io/foo").unwrap()),
            },
        );
        ok(
            "crates.io/foo#bar:1.2.3",
            PackageIdSpec {
                name: InternedString::new("bar"),
                version: Some("1.2.3".to_semver().unwrap()),
                url: Some(Url::parse("cargo://crates.io/foo").unwrap()),
            },
        );
        ok(
            "foo",
            PackageIdSpec {
                name: InternedString::new("foo"),
                version: None,
                url: None,
            },
        );
        ok(
            "foo:1.2.3",
            PackageIdSpec {
                name: InternedString::new("foo"),
                version: Some("1.2.3".to_semver().unwrap()),
                url: None,
            },
        );
    }

    #[test]
    fn bad_parsing() {
        assert!(PackageIdSpec::parse("baz:").is_err());
        assert!(PackageIdSpec::parse("baz:*").is_err());
        assert!(PackageIdSpec::parse("baz:1.0").is_err());
        assert!(PackageIdSpec::parse("https://baz:1.0").is_err());
        assert!(PackageIdSpec::parse("https://#baz:1.0").is_err());
        assert!(PackageIdSpec::parse("file:///baz").is_err());
        assert!(PackageIdSpec::parse("/baz").is_err());
    }

    #[test]
    fn matching() {
        let url = Url::parse("https://example.com").unwrap();
        let sid = SourceId::for_registry(&url).unwrap();
        let foo = PackageId::new("foo", "1.2.3", sid).unwrap();
        let bar = PackageId::new("bar", "1.2.3", sid).unwrap();

        assert!(PackageIdSpec::parse("foo").unwrap().matches(foo));
        assert!(!PackageIdSpec::parse("foo").unwrap().matches(bar));
        assert!(PackageIdSpec::parse("foo:1.2.3").unwrap().matches(foo));
        assert!(!PackageIdSpec::parse("foo:1.2.2").unwrap().matches(foo));
    }
}
<commit_msg>fix(pkgid): let absolute local path fail to pase<commit_after>use std::collections::HashMap;
use std::fmt;

use anyhow::bail;
use semver::Version;
use serde::{de, ser};
use url::Url;

use crate::core::PackageId;
use crate::util::errors::{CargoResult, CargoResultExt};
use crate::util::interning::InternedString;
use crate::util::lev_distance;
use crate::util::{validate_package_name, IntoUrl, ToSemver};

/// Some or all of the data required to identify a package:
///
///  1. the package name (a `String`, required)
///  2. the package version (a `Version`, optional)
///  3. the package source (a `Url`, optional)
///
/// If any of the optional fields are omitted, then the package ID may be ambiguous, there may be
/// more than one package/version/url combo that will match. However, often just the name is
/// sufficient to uniquely define a package ID.
#[derive(Clone, PartialEq, Eq, Debug, Hash, Ord, PartialOrd)]
pub struct PackageIdSpec {
    name: InternedString,
    version: Option<Version>,
    url: Option<Url>,
}

impl PackageIdSpec {
    /// Parses a spec string and returns a `PackageIdSpec` if the string was valid.
    ///
    /// # Examples
    /// Some examples of valid strings
    ///
    /// ```
    /// use cargo::core::PackageIdSpec;
    ///
    /// let specs = vec![
    ///     "https://crates.io/foo#1.2.3",
    ///     "https://crates.io/foo#bar:1.2.3",
    ///     "crates.io/foo",
    ///     "crates.io/foo#1.2.3",
    ///     "crates.io/foo#bar",
    ///     "crates.io/foo#bar:1.2.3",
    ///     "foo",
    ///     "foo:1.2.3",
    /// ];
    /// for spec in specs {
    ///     assert!(PackageIdSpec::parse(spec).is_ok());
    /// }
    pub fn parse(spec: &str) -> CargoResult<PackageIdSpec> {
        if spec.contains('/') {
            if let Ok(url) = spec.into_url() {
                return PackageIdSpec::from_url(url);
            }
            if !spec.contains("://") {
                if spec.starts_with('/') {
                    // This is out of current pkgid spec.
                    // Only for fixing rust-lang/cargo#9041
                    bail!(
                        "pkgid urls with local paths must be prefixed \
                         with a `file://` scheme: {}",
                        spec
                    )
                }
                if let Ok(url) = Url::parse(&format!("cargo://{}", spec)) {
                    return PackageIdSpec::from_url(url);
                }
            }
        }
        let mut parts = spec.splitn(2, ':');
        let name = parts.next().unwrap();
        let version = match parts.next() {
            Some(version) => Some(version.to_semver()?),
            None => None,
        };
        validate_package_name(name, "pkgid", "")?;
        Ok(PackageIdSpec {
            name: InternedString::new(name),
            version,
            url: None,
        })
    }

    /// Roughly equivalent to `PackageIdSpec::parse(spec)?.query(i)`
    pub fn query_str<I>(spec: &str, i: I) -> CargoResult<PackageId>
    where
        I: IntoIterator<Item = PackageId>,
    {
        let spec = PackageIdSpec::parse(spec)
            .chain_err(|| anyhow::format_err!("invalid package ID specification: `{}`", spec))?;
        spec.query(i)
    }

    /// Convert a `PackageId` to a `PackageIdSpec`, which will have both the `Version` and `Url`
    /// fields filled in.
    pub fn from_package_id(package_id: PackageId) -> PackageIdSpec {
        PackageIdSpec {
            name: package_id.name(),
            version: Some(package_id.version().clone()),
            url: Some(package_id.source_id().url().clone()),
        }
    }

    /// Tries to convert a valid `Url` to a `PackageIdSpec`.
    fn from_url(mut url: Url) -> CargoResult<PackageIdSpec> {
        if url.query().is_some() {
            bail!("cannot have a query string in a pkgid: {}", url)
        }
        let frag = url.fragment().map(|s| s.to_owned());
        url.set_fragment(None);
        let (name, version) = {
            let mut path = url
                .path_segments()
                .ok_or_else(|| anyhow::format_err!("pkgid urls must have a path: {}", url))?;
            let path_name = path.next_back().ok_or_else(|| {
                anyhow::format_err!(
                    "pkgid urls must have at least one path \
                     component: {}",
                    url
                )
            })?;
            match frag {
                Some(fragment) => {
                    let mut parts = fragment.splitn(2, ':');
                    let name_or_version = parts.next().unwrap();
                    match parts.next() {
                        Some(part) => {
                            let version = part.to_semver()?;
                            (InternedString::new(name_or_version), Some(version))
                        }
                        None => {
                            if name_or_version.chars().next().unwrap().is_alphabetic() {
                                (InternedString::new(name_or_version), None)
                            } else {
                                let version = name_or_version.to_semver()?;
                                (InternedString::new(path_name), Some(version))
                            }
                        }
                    }
                }
                None => (InternedString::new(path_name), None),
            }
        };
        Ok(PackageIdSpec {
            name,
            version,
            url: Some(url),
        })
    }

    pub fn name(&self) -> InternedString {
        self.name
    }

    pub fn version(&self) -> Option<&Version> {
        self.version.as_ref()
    }

    pub fn url(&self) -> Option<&Url> {
        self.url.as_ref()
    }

    pub fn set_url(&mut self, url: Url) {
        self.url = Some(url);
    }

    /// Checks whether the given `PackageId` matches the `PackageIdSpec`.
    pub fn matches(&self, package_id: PackageId) -> bool {
        if self.name() != package_id.name() {
            return false;
        }

        if let Some(ref v) = self.version {
            if v != package_id.version() {
                return false;
            }
        }

        match self.url {
            Some(ref u) => u == package_id.source_id().url(),
            None => true,
        }
    }

    /// Checks a list of `PackageId`s to find 1 that matches this `PackageIdSpec`. If 0, 2, or
    /// more are found, then this returns an error.
    pub fn query<I>(&self, i: I) -> CargoResult<PackageId>
    where
        I: IntoIterator<Item = PackageId>,
    {
        let all_ids: Vec<_> = i.into_iter().collect();
        let mut ids = all_ids.iter().copied().filter(|&id| self.matches(id));
        let ret = match ids.next() {
            Some(id) => id,
            None => {
                let mut suggestion = String::new();
                let try_spec = |spec: PackageIdSpec, suggestion: &mut String| {
                    let try_matches: Vec<_> = all_ids
                        .iter()
                        .copied()
                        .filter(|&id| spec.matches(id))
                        .collect();
                    if !try_matches.is_empty() {
                        suggestion.push_str("\nDid you mean one of these?\n");
                        minimize(suggestion, &try_matches, self);
                    }
                };
                if self.url.is_some() {
                    try_spec(
                        PackageIdSpec {
                            name: self.name,
                            version: self.version.clone(),
                            url: None,
                        },
                        &mut suggestion,
                    );
                }
                if suggestion.is_empty() && self.version.is_some() {
                    try_spec(
                        PackageIdSpec {
                            name: self.name,
                            version: None,
                            url: None,
                        },
                        &mut suggestion,
                    );
                }
                if suggestion.is_empty() {
                    suggestion.push_str(&lev_distance::closest_msg(
                        &self.name,
                        all_ids.iter(),
                        |id| id.name().as_str(),
                    ));
                }

                bail!(
                    "package ID specification `{}` did not match any packages{}",
                    self,
                    suggestion
                );
            }
        };
        return match ids.next() {
            Some(other) => {
                let mut msg = format!(
                    "There are multiple `{}` packages in \
                     your project, and the specification \
                     `{}` is ambiguous.\n\
                     Please re-run this command \
                     with `-p <spec>` where `<spec>` is one \
                     of the following:",
                    self.name(),
                    self
                );
                let mut vec = vec![ret, other];
                vec.extend(ids);
                minimize(&mut msg, &vec, self);
                Err(anyhow::format_err!("{}", msg))
            }
            None => Ok(ret),
        };

        fn minimize(msg: &mut String, ids: &[PackageId], spec: &PackageIdSpec) {
            let mut version_cnt = HashMap::new();
            for id in ids {
                *version_cnt.entry(id.version()).or_insert(0) += 1;
            }
            for id in ids {
                if version_cnt[id.version()] == 1 {
                    msg.push_str(&format!("\n  {}:{}", spec.name(), id.version()));
                } else {
                    msg.push_str(&format!("\n  {}", PackageIdSpec::from_package_id(*id)));
                }
            }
        }
    }
}

impl fmt::Display for PackageIdSpec {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut printed_name = false;
        match self.url {
            Some(ref url) => {
                if url.scheme() == "cargo" {
                    write!(f, "{}{}", url.host().unwrap(), url.path())?;
                } else {
                    write!(f, "{}", url)?;
                }
                if url.path_segments().unwrap().next_back().unwrap() != &*self.name {
                    printed_name = true;
                    write!(f, "#{}", self.name)?;
                }
            }
            None => {
                printed_name = true;
                write!(f, "{}", self.name)?
            }
        }
        if let Some(ref v) = self.version {
            write!(f, "{}{}", if printed_name { ":" } else { "#" }, v)?;
        }
        Ok(())
    }
}

impl ser::Serialize for PackageIdSpec {
    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
    where
        S: ser::Serializer,
    {
        self.to_string().serialize(s)
    }
}

impl<'de> de::Deserialize<'de> for PackageIdSpec {
    fn deserialize<D>(d: D) -> Result<PackageIdSpec, D::Error>
    where
        D: de::Deserializer<'de>,
    {
        let string = String::deserialize(d)?;
        PackageIdSpec::parse(&string).map_err(de::Error::custom)
    }
}

#[cfg(test)]
mod tests {
    use super::PackageIdSpec;
    use crate::core::{PackageId, SourceId};
    use crate::util::interning::InternedString;
    use crate::util::ToSemver;
    use url::Url;

    #[test]
    fn good_parsing() {
        fn ok(spec: &str, expected: PackageIdSpec) {
            let parsed = PackageIdSpec::parse(spec).unwrap();
            assert_eq!(parsed, expected);
            assert_eq!(parsed.to_string(), spec);
        }

        ok(
            "https://crates.io/foo#1.2.3",
            PackageIdSpec {
                name: InternedString::new("foo"),
                version: Some("1.2.3".to_semver().unwrap()),
                url: Some(Url::parse("https://crates.io/foo").unwrap()),
            },
        );
        ok(
            "https://crates.io/foo#bar:1.2.3",
            PackageIdSpec {
                name: InternedString::new("bar"),
                version: Some("1.2.3".to_semver().unwrap()),
                url: Some(Url::parse("https://crates.io/foo").unwrap()),
            },
        );
        ok(
            "crates.io/foo",
            PackageIdSpec {
                name: InternedString::new("foo"),
                version: None,
                url: Some(Url::parse("cargo://crates.io/foo").unwrap()),
            },
        );
        ok(
            "crates.io/foo#1.2.3",
            PackageIdSpec {
                name: InternedString::new("foo"),
                version: Some("1.2.3".to_semver().unwrap()),
                url: Some(Url::parse("cargo://crates.io/foo").unwrap()),
            },
        );
        ok(
            "crates.io/foo#bar",
            PackageIdSpec {
                name: InternedString::new("bar"),
                version: None,
                url: Some(Url::parse("cargo://crates.io/foo").unwrap()),
            },
        );
        ok(
            "crates.io/foo#bar:1.2.3",
            PackageIdSpec {
                name: InternedString::new("bar"),
                version: Some("1.2.3".to_semver().unwrap()),
                url: Some(Url::parse("cargo://crates.io/foo").unwrap()),
            },
        );
        ok(
            "foo",
            PackageIdSpec {
                name: InternedString::new("foo"),
                version: None,
                url: None,
            },
        );
        ok(
            "foo:1.2.3",
            PackageIdSpec {
                name: InternedString::new("foo"),
                version: Some("1.2.3".to_semver().unwrap()),
                url: None,
            },
        );
    }

    #[test]
    fn bad_parsing() {
        assert!(PackageIdSpec::parse("baz:").is_err());
        assert!(PackageIdSpec::parse("baz:*").is_err());
        assert!(PackageIdSpec::parse("baz:1.0").is_err());
        assert!(PackageIdSpec::parse("https://baz:1.0").is_err());
        assert!(PackageIdSpec::parse("https://#baz:1.0").is_err());
        assert!(PackageIdSpec::parse("/baz").is_err());
    }

    #[test]
    fn matching() {
        let url = Url::parse("https://example.com").unwrap();
        let sid = SourceId::for_registry(&url).unwrap();
        let foo = PackageId::new("foo", "1.2.3", sid).unwrap();
        let bar = PackageId::new("bar", "1.2.3", sid).unwrap();

        assert!(PackageIdSpec::parse("foo").unwrap().matches(foo));
        assert!(!PackageIdSpec::parse("foo").unwrap().matches(bar));
        assert!(PackageIdSpec::parse("foo:1.2.3").unwrap().matches(foo));
        assert!(!PackageIdSpec::parse("foo:1.2.2").unwrap().matches(foo));
    }
}
<|endoftext|>


<commit_before>use reqwest::header::{ContentRange, ContentRangeSpec};
use reqwest::{Error, Response};
use std::fs::{self, rename, File, OpenOptions};
use std::io::{Read, Seek, SeekFrom, Write};
use std::mem::transmute;
use std::ops::Deref;
use std::sync::Mutex;
use ui_helper;

lazy_static! {
    static ref FLOCK: Mutex<u8> = Mutex::new(0u8);
}

pub const CHUNK_SIZE_USIZE: usize = 128 * 1024;
pub const CHUNK_SIZE_U64: u64 = 128 * 1024;

pub fn create_file(path: &str, bytes: u64) -> u64 {
    let (chunk_count, chunk_space) = calculate_chunk_count_and_space(bytes);
    let tmp_name = tmp_file_name(path);

    let footer_space = chunk_space + 8;
    let footer_space_u64 = footer_space as u64;
    let existing_file_length = fs::metadata(tmp_name.clone())
        .map(|metadata| metadata.len())
        .unwrap_or(0);

    if existing_file_length != footer_space_u64 + bytes {
        let mut buf: Vec<u8> = vec![0_u8; chunk_space];

        #[allow(unsafe_code)]
        let chunk_count_bytes: [u8; 8] = unsafe { transmute(chunk_count.to_be()) };
        buf.extend_from_slice(&chunk_count_bytes);

        let mut file = File::create(tmp_name).unwrap();
        file.set_len(footer_space_u64 + bytes).unwrap();
        file.seek(SeekFrom::End(-(footer_space_u64 as i64)))
            .unwrap();
        file.write_all(&buf[..]).unwrap();
    }
    footer_space_u64
}

pub fn remove_footer_and_save(path: &str, bytes: u64) {
    let tmp_path = tmp_file_name(path);
    let file = OpenOptions::new()
        .write(true)
        .open(tmp_path.clone())
        .unwrap();
    file.set_len(bytes).unwrap();
    rename(tmp_path, path).unwrap();
}

pub fn save_response(
    path: &str,
    mut res: Response,
    footer_space: u64,
    child_id: usize,
    prefilled: u64,
) -> Result<u64, Error> {
    let first_byte = match *res.headers().get::<ContentRange>().unwrap().deref() {
        ContentRangeSpec::Bytes {
            range,
            instance_length: _,
        } => range.unwrap().0,
        _ => panic!("Response header of incorrect form!"),
    };

    let file_name = tmp_file_name(path);
    let mut file = OpenOptions::new()
        .write(true)
        .read(true)
        .open(file_name)
        .unwrap();

    file.seek(SeekFrom::Start(first_byte)).unwrap();
    let mut buf = [0; CHUNK_SIZE_USIZE];
    let mut written = 0;

    while let Ok(len) = res.read(&mut buf) {
        if len == 0 {
            return Ok(written + prefilled);
        }
        file.write_all(&buf[..len]).unwrap();
        let last_working_chunk = (written + first_byte) / CHUNK_SIZE_U64;
        written += len as u64;
        let current_working_chunk = (written + first_byte) / CHUNK_SIZE_U64;
        set_written_chunks(
            path,
            footer_space,
            (last_working_chunk, current_working_chunk),
        );
        ui_helper::update_bar(child_id, written + prefilled);
    }

    Ok(0u64)
}

pub fn get_first_empty_chunk(path: &str, footer_space: u64, byte_range: (u64, u64)) -> u64 {
    let _guard = FLOCK.lock().expect("Failed to aquire lock, lock poisoned!");
    let mut file = OpenOptions::new()
        .read(true)
        .open(tmp_file_name(path))
        .unwrap();
    let first_chunk = byte_range.0 / CHUNK_SIZE_U64;
    let last_chunk = byte_range.1 / CHUNK_SIZE_U64;
    let first_byte = get_chunk_status_offset(footer_space as i64, first_chunk as i64);
    let last_byte = get_chunk_status_offset(footer_space as i64, last_chunk as i64);

    file.seek(SeekFrom::End(first_byte)).unwrap();
    let mut buf = [0; 1];
    let mut chunk_num = first_chunk;
    for byte_num in first_byte..=last_byte {
        file.read_exact(&mut buf).unwrap();

        let start_offset = if byte_num == first_byte {
            first_chunk % 8
        } else {
            0
        };

        let finish_offset = if byte_num == last_byte {
            last_chunk % 8 + 1
        } else {
            8
        };

        let byte = buf[0];

        for bit_offset in start_offset..finish_offset {
            if byte & (1 << (7 - bit_offset)) == 0 {
                return chunk_num * CHUNK_SIZE_U64;
            }
            chunk_num += 1;
        }
    }
    chunk_num * CHUNK_SIZE_U64
}

fn set_written_chunks(path: &str, footer_space: u64, working_chunk_from_to: (u64, u64)) {
    let (last_working_chunk, current_working_chunk) = working_chunk_from_to;
    if current_working_chunk <= last_working_chunk {
        return;
    }

    let current_complete_chunk = current_working_chunk - 1;

    let _guard = FLOCK.lock().expect("Failed to aquire lock, lock poisoned!");
    let mut file = OpenOptions::new()
        .write(true)
        .read(true)
        .open(tmp_file_name(path))
        .unwrap();

    let first_byte = get_chunk_status_offset(footer_space as i64, last_working_chunk as i64);
    let last_byte = get_chunk_status_offset(footer_space as i64, current_complete_chunk as i64);
    let mut buf = [0; 1];
    for byte_num in first_byte..=last_byte {
        file.seek(SeekFrom::End(byte_num)).unwrap();
        file.read_exact(&mut buf).unwrap();

        let start_offset = if byte_num == first_byte {
            last_working_chunk % 8
        } else {
            0
        };

        let finish_offset = if byte_num == last_byte {
            current_complete_chunk % 8 + 1
        } else {
            8
        };

        let mut byte = buf[0];

        for bit_offset in start_offset..finish_offset {
            byte |= 1 << (7 - bit_offset);
        }

        file.seek(SeekFrom::End(byte_num)).unwrap();
        file.write_all(&[byte]).unwrap();
    }
}

fn get_chunk_status_offset(footer_space: i64, chunk: i64) -> i64 {
    -footer_space + (chunk / 8)
}

fn tmp_file_name(path: &str) -> String {
    format!("{}.grapplepartial", path)
}

fn calculate_chunk_count_and_space(bytes: u64) -> (u64, usize) {
    let mut num_chunks = bytes / CHUNK_SIZE_U64;
    if bytes % CHUNK_SIZE_U64 > 0 {
        num_chunks += 1;
    }
    let mut chunk_space = num_chunks / 8;
    if num_chunks % 8 > 0 {
        chunk_space += 1;
    }

    (num_chunks, chunk_space as usize)
}
<commit_msg>Remove contents of file lock<commit_after>use reqwest::header::{ContentRange, ContentRangeSpec};
use reqwest::{Error, Response};
use std::fs::{self, rename, File, OpenOptions};
use std::io::{Read, Seek, SeekFrom, Write};
use std::mem::transmute;
use std::ops::Deref;
use std::sync::Mutex;
use ui_helper;

lazy_static! {
    static ref FLOCK: Mutex<()> = Mutex::new(());
}

pub const CHUNK_SIZE_USIZE: usize = 128 * 1024;
pub const CHUNK_SIZE_U64: u64 = 128 * 1024;

pub fn create_file(path: &str, bytes: u64) -> u64 {
    let (chunk_count, chunk_space) = calculate_chunk_count_and_space(bytes);
    let tmp_name = tmp_file_name(path);

    let footer_space = chunk_space + 8;
    let footer_space_u64 = footer_space as u64;
    let existing_file_length = fs::metadata(tmp_name.clone())
        .map(|metadata| metadata.len())
        .unwrap_or(0);

    if existing_file_length != footer_space_u64 + bytes {
        let mut buf: Vec<u8> = vec![0_u8; chunk_space];

        #[allow(unsafe_code)]
        let chunk_count_bytes: [u8; 8] = unsafe { transmute(chunk_count.to_be()) };
        buf.extend_from_slice(&chunk_count_bytes);

        let mut file = File::create(tmp_name).unwrap();
        file.set_len(footer_space_u64 + bytes).unwrap();
        file.seek(SeekFrom::End(-(footer_space_u64 as i64)))
            .unwrap();
        file.write_all(&buf[..]).unwrap();
    }
    footer_space_u64
}

pub fn remove_footer_and_save(path: &str, bytes: u64) {
    let tmp_path = tmp_file_name(path);
    let file = OpenOptions::new()
        .write(true)
        .open(tmp_path.clone())
        .unwrap();
    file.set_len(bytes).unwrap();
    rename(tmp_path, path).unwrap();
}

pub fn save_response(
    path: &str,
    mut res: Response,
    footer_space: u64,
    child_id: usize,
    prefilled: u64,
) -> Result<u64, Error> {
    let first_byte = match *res.headers().get::<ContentRange>().unwrap().deref() {
        ContentRangeSpec::Bytes {
            range,
            instance_length: _,
        } => range.unwrap().0,
        _ => panic!("Response header of incorrect form!"),
    };

    let file_name = tmp_file_name(path);
    let mut file = OpenOptions::new()
        .write(true)
        .read(true)
        .open(file_name)
        .unwrap();

    file.seek(SeekFrom::Start(first_byte)).unwrap();
    let mut buf = [0; CHUNK_SIZE_USIZE];
    let mut written = 0;

    while let Ok(len) = res.read(&mut buf) {
        if len == 0 {
            return Ok(written + prefilled);
        }
        file.write_all(&buf[..len]).unwrap();
        let last_working_chunk = (written + first_byte) / CHUNK_SIZE_U64;
        written += len as u64;
        let current_working_chunk = (written + first_byte) / CHUNK_SIZE_U64;
        set_written_chunks(
            path,
            footer_space,
            (last_working_chunk, current_working_chunk),
        );
        ui_helper::update_bar(child_id, written + prefilled);
    }

    Ok(0u64)
}

pub fn get_first_empty_chunk(path: &str, footer_space: u64, byte_range: (u64, u64)) -> u64 {
    let _guard = FLOCK.lock().expect("Failed to aquire lock, lock poisoned!");
    let mut file = OpenOptions::new()
        .read(true)
        .open(tmp_file_name(path))
        .unwrap();
    let first_chunk = byte_range.0 / CHUNK_SIZE_U64;
    let last_chunk = byte_range.1 / CHUNK_SIZE_U64;
    let first_byte = get_chunk_status_offset(footer_space as i64, first_chunk as i64);
    let last_byte = get_chunk_status_offset(footer_space as i64, last_chunk as i64);

    file.seek(SeekFrom::End(first_byte)).unwrap();
    let mut buf = [0; 1];
    let mut chunk_num = first_chunk;
    for byte_num in first_byte..=last_byte {
        file.read_exact(&mut buf).unwrap();

        let start_offset = if byte_num == first_byte {
            first_chunk % 8
        } else {
            0
        };

        let finish_offset = if byte_num == last_byte {
            last_chunk % 8 + 1
        } else {
            8
        };

        let byte = buf[0];

        for bit_offset in start_offset..finish_offset {
            if byte & (1 << (7 - bit_offset)) == 0 {
                return chunk_num * CHUNK_SIZE_U64;
            }
            chunk_num += 1;
        }
    }
    chunk_num * CHUNK_SIZE_U64
}

fn set_written_chunks(path: &str, footer_space: u64, working_chunk_from_to: (u64, u64)) {
    let (last_working_chunk, current_working_chunk) = working_chunk_from_to;
    if current_working_chunk <= last_working_chunk {
        return;
    }

    let current_complete_chunk = current_working_chunk - 1;

    let _guard = FLOCK.lock().expect("Failed to aquire lock, lock poisoned!");
    let mut file = OpenOptions::new()
        .write(true)
        .read(true)
        .open(tmp_file_name(path))
        .unwrap();

    let first_byte = get_chunk_status_offset(footer_space as i64, last_working_chunk as i64);
    let last_byte = get_chunk_status_offset(footer_space as i64, current_complete_chunk as i64);
    let mut buf = [0; 1];
    for byte_num in first_byte..=last_byte {
        file.seek(SeekFrom::End(byte_num)).unwrap();
        file.read_exact(&mut buf).unwrap();

        let start_offset = if byte_num == first_byte {
            last_working_chunk % 8
        } else {
            0
        };

        let finish_offset = if byte_num == last_byte {
            current_complete_chunk % 8 + 1
        } else {
            8
        };

        let mut byte = buf[0];

        for bit_offset in start_offset..finish_offset {
            byte |= 1 << (7 - bit_offset);
        }

        file.seek(SeekFrom::End(byte_num)).unwrap();
        file.write_all(&[byte]).unwrap();
    }
}

fn get_chunk_status_offset(footer_space: i64, chunk: i64) -> i64 {
    -footer_space + (chunk / 8)
}

fn tmp_file_name(path: &str) -> String {
    format!("{}.grapplepartial", path)
}

fn calculate_chunk_count_and_space(bytes: u64) -> (u64, usize) {
    let mut num_chunks = bytes / CHUNK_SIZE_U64;
    if bytes % CHUNK_SIZE_U64 > 0 {
        num_chunks += 1;
    }
    let mut chunk_space = num_chunks / 8;
    if num_chunks % 8 > 0 {
        chunk_space += 1;
    }

    (num_chunks, chunk_space as usize)
}
<|endoftext|>


<commit_before>// SPDX-License-Identifier: MIT
// Copyright (C) 2018-present iced project and contributors

#[cfg(not(feature = "no_evex"))]
mod data_evex;
mod data_legacy;
#[cfg(not(feature = "no_vex"))]
mod data_vex;
#[cfg(not(feature = "no_xop"))]
mod data_xop;
mod enums;
#[cfg(not(feature = "no_evex"))]
mod evex_reader;
mod legacy_reader;
#[cfg(any(not(feature = "no_vex"), not(feature = "no_xop")))]
mod vex_reader;

use crate::data_reader::DataReader;
use crate::decoder::handlers::OpCodeHandler;
use crate::decoder::handlers::{is_null_instance_handler, OpCodeHandlerDecodeFn};
use crate::decoder::table_de::enums::*;
use crate::iced_constants::IcedConstants;
use crate::{Code, CodeUnderlyingType, Register, RegisterUnderlyingType};
#[cfg(not(feature = "no_evex"))]
use crate::{TupleType, TupleTypeUnderlyingType};
use alloc::vec::Vec;
use core::mem;

#[inline]
fn box_opcode_handler<T>((decode, handler): (OpCodeHandlerDecodeFn, T)) -> (OpCodeHandlerDecodeFn, *const OpCodeHandler) {
	// All handlers are #[repr(C)] and the first fields are the same as OpCodeHandler
	(decode, Box::into_raw(Box::new(handler)) as *const OpCodeHandler)
}

enum HandlerInfo {
	Handler((OpCodeHandlerDecodeFn, &'static OpCodeHandler)),
	Handlers(Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>),
}

struct TableDeserializer<'a> {
	reader: DataReader<'a>,
	handler_reader: fn(deserializer: &mut TableDeserializer<'_>, result: &mut Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>),
	id_to_handler: Vec<HandlerInfo>,
	temp_vecs: Vec<Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>>,
}

impl<'a> TableDeserializer<'a> {
	#[must_use]
	#[inline]
	fn new(
		data: &'a [u8], max_ids: usize,
		handler_reader: fn(deserializer: &mut TableDeserializer<'_>, result: &mut Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>),
	) -> Self {
		Self { reader: DataReader::new(data), handler_reader, id_to_handler: Vec::with_capacity(max_ids), temp_vecs: Vec::new() }
	}

	fn deserialize(&mut self) {
		while self.reader.can_read() {
			// SAFETY: generated (and also immutable) data is valid
			let kind: SerializedDataKind = unsafe { mem::transmute(self.reader.read_u8() as u8) };
			match kind {
				SerializedDataKind::HandlerReference => {
					let tmp = self.read_handler();
					self.id_to_handler.push(HandlerInfo::Handler(tmp));
				}

				SerializedDataKind::ArrayReference => {
					let size = self.reader.read_compressed_u32() as usize;
					let tmp = self.read_handlers(size);
					self.id_to_handler.push(HandlerInfo::Handlers(tmp));
				}
			}
		}
		debug_assert!(!self.reader.can_read());
	}

	#[must_use]
	#[inline]
	fn read_op_code_handler_kind(&mut self) -> OpCodeHandlerKind {
		// SAFETY: generated (and also immutable) data is valid
		unsafe { mem::transmute(self.reader.read_u8() as u8) }
	}

	#[cfg(any(not(feature = "no_vex"), not(feature = "no_xop")))]
	#[must_use]
	#[inline]
	fn read_vex_op_code_handler_kind(&mut self) -> VexOpCodeHandlerKind {
		// SAFETY: generated (and also immutable) data is valid
		unsafe { mem::transmute(self.reader.read_u8() as u8) }
	}

	#[cfg(not(feature = "no_evex"))]
	#[must_use]
	#[inline]
	fn read_evex_op_code_handler_kind(&mut self) -> EvexOpCodeHandlerKind {
		// SAFETY: generated (and also immutable) data is valid
		unsafe { mem::transmute(self.reader.read_u8() as u8) }
	}

	#[must_use]
	#[inline]
	fn read_code(&mut self) -> Code {
		let v = self.reader.read_compressed_u32();
		debug_assert!(v < IcedConstants::CODE_ENUM_COUNT as u32);
		// SAFETY: generated (and also immutable) data is valid
		unsafe { mem::transmute(v as CodeUnderlyingType) }
	}

	#[must_use]
	#[inline]
	fn read_code2(&mut self) -> (Code, Code) {
		let v = self.reader.read_compressed_u32();
		debug_assert!(v < IcedConstants::CODE_ENUM_COUNT as u32);
		debug_assert!(v + 1 < IcedConstants::CODE_ENUM_COUNT as u32);
		// SAFETY: generated (and also immutable) data is valid
		(unsafe { mem::transmute(v as CodeUnderlyingType) }, unsafe { mem::transmute((v + 1) as CodeUnderlyingType) })
	}

	#[must_use]
	#[inline]
	fn read_code3(&mut self) -> (Code, Code, Code) {
		let v = self.reader.read_compressed_u32();
		debug_assert!(v < IcedConstants::CODE_ENUM_COUNT as u32);
		debug_assert!(v + 2 < IcedConstants::CODE_ENUM_COUNT as u32);
		// SAFETY: generated (and also immutable) data is valid
		(unsafe { mem::transmute(v as CodeUnderlyingType) }, unsafe { mem::transmute((v + 1) as CodeUnderlyingType) }, unsafe {
			mem::transmute((v + 2) as CodeUnderlyingType)
		})
	}

	#[must_use]
	#[inline]
	fn read_register(&mut self) -> Register {
		let v = self.reader.read_u8();
		debug_assert!(v < IcedConstants::REGISTER_ENUM_COUNT);
		// SAFETY: generated (and also immutable) data is valid
		unsafe { mem::transmute(v as RegisterUnderlyingType) }
	}

	#[must_use]
	#[inline]
	fn read_decoder_options(&mut self) -> u32 {
		self.reader.read_compressed_u32()
	}

	#[must_use]
	#[inline]
	fn read_handler_flags(&mut self) -> u32 {
		self.reader.read_compressed_u32()
	}

	#[must_use]
	#[inline]
	fn read_legacy_handler_flags(&mut self) -> u32 {
		self.reader.read_compressed_u32()
	}

	#[cfg(not(feature = "no_evex"))]
	#[must_use]
	#[inline]
	fn read_tuple_type(&mut self) -> TupleType {
		let v = self.reader.read_u8();
		debug_assert!(v < IcedConstants::TUPLE_TYPE_ENUM_COUNT);
		// SAFETY: generated (and also immutable) data is valid
		unsafe { mem::transmute(v as TupleTypeUnderlyingType) }
	}

	#[must_use]
	#[inline]
	fn read_boolean(&mut self) -> bool {
		self.reader.read_u8() != 0
	}

	#[must_use]
	#[inline]
	fn read_u32(&mut self) -> u32 {
		self.reader.read_compressed_u32()
	}

	#[must_use]
	#[inline]
	fn read_handler(&mut self) -> (OpCodeHandlerDecodeFn, &'static OpCodeHandler) {
		let result = self.read_handler_or_null_instance();
		debug_assert!(!is_null_instance_handler(result.1));
		result
	}

	#[must_use]
	#[allow(clippy::unwrap_used)]
	fn read_handler_or_null_instance(&mut self) -> (OpCodeHandlerDecodeFn, &'static OpCodeHandler) {
		let mut tmp_vec = self.temp_vecs.pop().unwrap_or_else(|| Vec::with_capacity(1));
		debug_assert!(tmp_vec.is_empty());
		(self.handler_reader)(self, &mut tmp_vec);
		debug_assert_eq!(tmp_vec.len(), 1);
		let result = tmp_vec.pop().unwrap();
		debug_assert!(tmp_vec.is_empty());
		self.temp_vecs.push(tmp_vec);
		result
	}

	#[must_use]
	fn read_handlers(&mut self, count: usize) -> Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)> {
		let mut handlers: Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)> = Vec::with_capacity(count);
		let mut i = 0;
		while handlers.len() < count {
			let len = handlers.len();
			(self.handler_reader)(self, &mut handlers);
			debug_assert!(handlers.len() >= len);
			let size = handlers.len() - len;
			if size == 0 {
				break; // will panic
			}
			i += size;
			debug_assert_eq!(handlers.len(), i);
		}
		debug_assert_eq!(handlers.len(), count);
		debug_assert_eq!(count, i);
		handlers
	}

	#[must_use]
	#[allow(clippy::get_unwrap)]
	fn read_handler_reference(&mut self) -> (OpCodeHandlerDecodeFn, &'static OpCodeHandler) {
		let index = self.reader.read_u8();
		if let &HandlerInfo::Handler(handler) = self.id_to_handler.get(index).unwrap() {
			handler
		} else {
			unreachable!();
		}
	}

	#[must_use]
	#[allow(clippy::get_unwrap)]
	fn read_array_reference(&mut self, kind: u32) -> Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)> {
		let read_kind = self.reader.read_u8() as u32;
		debug_assert_eq!(read_kind, kind);
		let index = self.reader.read_u8();
		if let &HandlerInfo::Handlers(ref handlers) = self.id_to_handler.get(index).unwrap() {
			// There are a few dupe refs, clone the whole thing
			handlers.to_vec()
		} else {
			unreachable!();
		}
	}

	#[must_use]
	fn read_array_reference_no_clone(&mut self, kind: u32) -> Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)> {
		let read_kind = self.reader.read_u8() as u32;
		debug_assert_eq!(read_kind, kind);
		let index = self.reader.read_u8();
		self.table(index)
	}

	#[must_use]
	#[allow(clippy::get_unwrap)]
	fn table(&mut self, index: usize) -> Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)> {
		if let &mut HandlerInfo::Handlers(ref mut tmp) = self.id_to_handler.get_mut(index).unwrap() {
			let handlers = mem::take(tmp);
			debug_assert!(!handlers.is_empty());
			handlers
		} else {
			unreachable!();
		}
	}
}

#[must_use]
pub(super) fn read_legacy() -> Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)> {
	let handler_reader = self::legacy_reader::read_handlers;
	let mut deserializer = TableDeserializer::new(data_legacy::TBL_DATA, data_legacy::MAX_ID_NAMES, handler_reader);
	deserializer.deserialize();
	deserializer.table(data_legacy::HANDLERS_MAP0_INDEX)
}

#[cfg(not(feature = "no_evex"))]
#[must_use]
pub(super) fn read_evex() -> (
	Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>,
	Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>,
	Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>,
	Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>,
	Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>,
) {
	let handler_reader = self::evex_reader::read_handlers;
	let mut deserializer = TableDeserializer::new(data_evex::TBL_DATA, data_evex::MAX_ID_NAMES, handler_reader);
	deserializer.deserialize();
	(
		deserializer.table(data_evex::HANDLERS_0F_INDEX),
		deserializer.table(data_evex::HANDLERS_0F38_INDEX),
		deserializer.table(data_evex::HANDLERS_0F3A_INDEX),
		deserializer.table(data_evex::HANDLERS_MAP5_INDEX),
		deserializer.table(data_evex::HANDLERS_MAP6_INDEX),
	)
}

#[cfg(not(feature = "no_vex"))]
#[must_use]
pub(super) fn read_vex() -> (
	Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>,
	Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>,
	Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>,
) {
	let handler_reader = self::vex_reader::read_handlers;
	let mut deserializer = TableDeserializer::new(data_vex::TBL_DATA, data_vex::MAX_ID_NAMES, handler_reader);
	deserializer.deserialize();
	(
		deserializer.table(data_vex::HANDLERS_0F_INDEX),
		deserializer.table(data_vex::HANDLERS_0F38_INDEX),
		deserializer.table(data_vex::HANDLERS_0F3A_INDEX),
	)
}

#[cfg(not(feature = "no_xop"))]
#[must_use]
pub(super) fn read_xop() -> (
	Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>,
	Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>,
	Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>,
) {
	let handler_reader = self::vex_reader::read_handlers;
	let mut deserializer = TableDeserializer::new(data_xop::TBL_DATA, data_xop::MAX_ID_NAMES, handler_reader);
	deserializer.deserialize();
	(
		deserializer.table(data_xop::HANDLERS_MAP8_INDEX),
		deserializer.table(data_xop::HANDLERS_MAP9_INDEX),
		deserializer.table(data_xop::HANDLERS_MAP10_INDEX),
	)
}
<commit_msg>Add missing use statement<commit_after>// SPDX-License-Identifier: MIT
// Copyright (C) 2018-present iced project and contributors

#[cfg(not(feature = "no_evex"))]
mod data_evex;
mod data_legacy;
#[cfg(not(feature = "no_vex"))]
mod data_vex;
#[cfg(not(feature = "no_xop"))]
mod data_xop;
mod enums;
#[cfg(not(feature = "no_evex"))]
mod evex_reader;
mod legacy_reader;
#[cfg(any(not(feature = "no_vex"), not(feature = "no_xop")))]
mod vex_reader;

use crate::data_reader::DataReader;
use crate::decoder::handlers::OpCodeHandler;
use crate::decoder::handlers::{is_null_instance_handler, OpCodeHandlerDecodeFn};
use crate::decoder::table_de::enums::*;
use crate::iced_constants::IcedConstants;
use crate::{Code, CodeUnderlyingType, Register, RegisterUnderlyingType};
#[cfg(not(feature = "no_evex"))]
use crate::{TupleType, TupleTypeUnderlyingType};
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::mem;

#[inline]
fn box_opcode_handler<T>((decode, handler): (OpCodeHandlerDecodeFn, T)) -> (OpCodeHandlerDecodeFn, *const OpCodeHandler) {
	// All handlers are #[repr(C)] and the first fields are the same as OpCodeHandler
	(decode, Box::into_raw(Box::new(handler)) as *const OpCodeHandler)
}

enum HandlerInfo {
	Handler((OpCodeHandlerDecodeFn, &'static OpCodeHandler)),
	Handlers(Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>),
}

struct TableDeserializer<'a> {
	reader: DataReader<'a>,
	handler_reader: fn(deserializer: &mut TableDeserializer<'_>, result: &mut Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>),
	id_to_handler: Vec<HandlerInfo>,
	temp_vecs: Vec<Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>>,
}

impl<'a> TableDeserializer<'a> {
	#[must_use]
	#[inline]
	fn new(
		data: &'a [u8], max_ids: usize,
		handler_reader: fn(deserializer: &mut TableDeserializer<'_>, result: &mut Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>),
	) -> Self {
		Self { reader: DataReader::new(data), handler_reader, id_to_handler: Vec::with_capacity(max_ids), temp_vecs: Vec::new() }
	}

	fn deserialize(&mut self) {
		while self.reader.can_read() {
			// SAFETY: generated (and also immutable) data is valid
			let kind: SerializedDataKind = unsafe { mem::transmute(self.reader.read_u8() as u8) };
			match kind {
				SerializedDataKind::HandlerReference => {
					let tmp = self.read_handler();
					self.id_to_handler.push(HandlerInfo::Handler(tmp));
				}

				SerializedDataKind::ArrayReference => {
					let size = self.reader.read_compressed_u32() as usize;
					let tmp = self.read_handlers(size);
					self.id_to_handler.push(HandlerInfo::Handlers(tmp));
				}
			}
		}
		debug_assert!(!self.reader.can_read());
	}

	#[must_use]
	#[inline]
	fn read_op_code_handler_kind(&mut self) -> OpCodeHandlerKind {
		// SAFETY: generated (and also immutable) data is valid
		unsafe { mem::transmute(self.reader.read_u8() as u8) }
	}

	#[cfg(any(not(feature = "no_vex"), not(feature = "no_xop")))]
	#[must_use]
	#[inline]
	fn read_vex_op_code_handler_kind(&mut self) -> VexOpCodeHandlerKind {
		// SAFETY: generated (and also immutable) data is valid
		unsafe { mem::transmute(self.reader.read_u8() as u8) }
	}

	#[cfg(not(feature = "no_evex"))]
	#[must_use]
	#[inline]
	fn read_evex_op_code_handler_kind(&mut self) -> EvexOpCodeHandlerKind {
		// SAFETY: generated (and also immutable) data is valid
		unsafe { mem::transmute(self.reader.read_u8() as u8) }
	}

	#[must_use]
	#[inline]
	fn read_code(&mut self) -> Code {
		let v = self.reader.read_compressed_u32();
		debug_assert!(v < IcedConstants::CODE_ENUM_COUNT as u32);
		// SAFETY: generated (and also immutable) data is valid
		unsafe { mem::transmute(v as CodeUnderlyingType) }
	}

	#[must_use]
	#[inline]
	fn read_code2(&mut self) -> (Code, Code) {
		let v = self.reader.read_compressed_u32();
		debug_assert!(v < IcedConstants::CODE_ENUM_COUNT as u32);
		debug_assert!(v + 1 < IcedConstants::CODE_ENUM_COUNT as u32);
		// SAFETY: generated (and also immutable) data is valid
		(unsafe { mem::transmute(v as CodeUnderlyingType) }, unsafe { mem::transmute((v + 1) as CodeUnderlyingType) })
	}

	#[must_use]
	#[inline]
	fn read_code3(&mut self) -> (Code, Code, Code) {
		let v = self.reader.read_compressed_u32();
		debug_assert!(v < IcedConstants::CODE_ENUM_COUNT as u32);
		debug_assert!(v + 2 < IcedConstants::CODE_ENUM_COUNT as u32);
		// SAFETY: generated (and also immutable) data is valid
		(unsafe { mem::transmute(v as CodeUnderlyingType) }, unsafe { mem::transmute((v + 1) as CodeUnderlyingType) }, unsafe {
			mem::transmute((v + 2) as CodeUnderlyingType)
		})
	}

	#[must_use]
	#[inline]
	fn read_register(&mut self) -> Register {
		let v = self.reader.read_u8();
		debug_assert!(v < IcedConstants::REGISTER_ENUM_COUNT);
		// SAFETY: generated (and also immutable) data is valid
		unsafe { mem::transmute(v as RegisterUnderlyingType) }
	}

	#[must_use]
	#[inline]
	fn read_decoder_options(&mut self) -> u32 {
		self.reader.read_compressed_u32()
	}

	#[must_use]
	#[inline]
	fn read_handler_flags(&mut self) -> u32 {
		self.reader.read_compressed_u32()
	}

	#[must_use]
	#[inline]
	fn read_legacy_handler_flags(&mut self) -> u32 {
		self.reader.read_compressed_u32()
	}

	#[cfg(not(feature = "no_evex"))]
	#[must_use]
	#[inline]
	fn read_tuple_type(&mut self) -> TupleType {
		let v = self.reader.read_u8();
		debug_assert!(v < IcedConstants::TUPLE_TYPE_ENUM_COUNT);
		// SAFETY: generated (and also immutable) data is valid
		unsafe { mem::transmute(v as TupleTypeUnderlyingType) }
	}

	#[must_use]
	#[inline]
	fn read_boolean(&mut self) -> bool {
		self.reader.read_u8() != 0
	}

	#[must_use]
	#[inline]
	fn read_u32(&mut self) -> u32 {
		self.reader.read_compressed_u32()
	}

	#[must_use]
	#[inline]
	fn read_handler(&mut self) -> (OpCodeHandlerDecodeFn, &'static OpCodeHandler) {
		let result = self.read_handler_or_null_instance();
		debug_assert!(!is_null_instance_handler(result.1));
		result
	}

	#[must_use]
	#[allow(clippy::unwrap_used)]
	fn read_handler_or_null_instance(&mut self) -> (OpCodeHandlerDecodeFn, &'static OpCodeHandler) {
		let mut tmp_vec = self.temp_vecs.pop().unwrap_or_else(|| Vec::with_capacity(1));
		debug_assert!(tmp_vec.is_empty());
		(self.handler_reader)(self, &mut tmp_vec);
		debug_assert_eq!(tmp_vec.len(), 1);
		let result = tmp_vec.pop().unwrap();
		debug_assert!(tmp_vec.is_empty());
		self.temp_vecs.push(tmp_vec);
		result
	}

	#[must_use]
	fn read_handlers(&mut self, count: usize) -> Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)> {
		let mut handlers: Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)> = Vec::with_capacity(count);
		let mut i = 0;
		while handlers.len() < count {
			let len = handlers.len();
			(self.handler_reader)(self, &mut handlers);
			debug_assert!(handlers.len() >= len);
			let size = handlers.len() - len;
			if size == 0 {
				break; // will panic
			}
			i += size;
			debug_assert_eq!(handlers.len(), i);
		}
		debug_assert_eq!(handlers.len(), count);
		debug_assert_eq!(count, i);
		handlers
	}

	#[must_use]
	#[allow(clippy::get_unwrap)]
	fn read_handler_reference(&mut self) -> (OpCodeHandlerDecodeFn, &'static OpCodeHandler) {
		let index = self.reader.read_u8();
		if let &HandlerInfo::Handler(handler) = self.id_to_handler.get(index).unwrap() {
			handler
		} else {
			unreachable!();
		}
	}

	#[must_use]
	#[allow(clippy::get_unwrap)]
	fn read_array_reference(&mut self, kind: u32) -> Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)> {
		let read_kind = self.reader.read_u8() as u32;
		debug_assert_eq!(read_kind, kind);
		let index = self.reader.read_u8();
		if let &HandlerInfo::Handlers(ref handlers) = self.id_to_handler.get(index).unwrap() {
			// There are a few dupe refs, clone the whole thing
			handlers.to_vec()
		} else {
			unreachable!();
		}
	}

	#[must_use]
	fn read_array_reference_no_clone(&mut self, kind: u32) -> Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)> {
		let read_kind = self.reader.read_u8() as u32;
		debug_assert_eq!(read_kind, kind);
		let index = self.reader.read_u8();
		self.table(index)
	}

	#[must_use]
	#[allow(clippy::get_unwrap)]
	fn table(&mut self, index: usize) -> Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)> {
		if let &mut HandlerInfo::Handlers(ref mut tmp) = self.id_to_handler.get_mut(index).unwrap() {
			let handlers = mem::take(tmp);
			debug_assert!(!handlers.is_empty());
			handlers
		} else {
			unreachable!();
		}
	}
}

#[must_use]
pub(super) fn read_legacy() -> Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)> {
	let handler_reader = self::legacy_reader::read_handlers;
	let mut deserializer = TableDeserializer::new(data_legacy::TBL_DATA, data_legacy::MAX_ID_NAMES, handler_reader);
	deserializer.deserialize();
	deserializer.table(data_legacy::HANDLERS_MAP0_INDEX)
}

#[cfg(not(feature = "no_evex"))]
#[must_use]
pub(super) fn read_evex() -> (
	Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>,
	Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>,
	Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>,
	Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>,
	Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>,
) {
	let handler_reader = self::evex_reader::read_handlers;
	let mut deserializer = TableDeserializer::new(data_evex::TBL_DATA, data_evex::MAX_ID_NAMES, handler_reader);
	deserializer.deserialize();
	(
		deserializer.table(data_evex::HANDLERS_0F_INDEX),
		deserializer.table(data_evex::HANDLERS_0F38_INDEX),
		deserializer.table(data_evex::HANDLERS_0F3A_INDEX),
		deserializer.table(data_evex::HANDLERS_MAP5_INDEX),
		deserializer.table(data_evex::HANDLERS_MAP6_INDEX),
	)
}

#[cfg(not(feature = "no_vex"))]
#[must_use]
pub(super) fn read_vex() -> (
	Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>,
	Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>,
	Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>,
) {
	let handler_reader = self::vex_reader::read_handlers;
	let mut deserializer = TableDeserializer::new(data_vex::TBL_DATA, data_vex::MAX_ID_NAMES, handler_reader);
	deserializer.deserialize();
	(
		deserializer.table(data_vex::HANDLERS_0F_INDEX),
		deserializer.table(data_vex::HANDLERS_0F38_INDEX),
		deserializer.table(data_vex::HANDLERS_0F3A_INDEX),
	)
}

#[cfg(not(feature = "no_xop"))]
#[must_use]
pub(super) fn read_xop() -> (
	Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>,
	Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>,
	Vec<(OpCodeHandlerDecodeFn, &'static OpCodeHandler)>,
) {
	let handler_reader = self::vex_reader::read_handlers;
	let mut deserializer = TableDeserializer::new(data_xop::TBL_DATA, data_xop::MAX_ID_NAMES, handler_reader);
	deserializer.deserialize();
	(
		deserializer.table(data_xop::HANDLERS_MAP8_INDEX),
		deserializer.table(data_xop::HANDLERS_MAP9_INDEX),
		deserializer.table(data_xop::HANDLERS_MAP10_INDEX),
	)
}
<|endoftext|>


<commit_before>use std::sync::mpsc;
use specs;
use game::constants::{VIEW_DISTANCE};

#[derive(Clone, Debug)]
pub struct InputState {
  pub distance: f32,
  pub x_pos: f32,
  pub y_pos: f32,
}

impl InputState {
  pub fn new() -> InputState {
    InputState {
      distance: VIEW_DISTANCE,
      x_pos: 0.0,
      y_pos: 0.0,
    }
  }
}

impl specs::Component for InputState {
  type Storage = specs::HashMapStorage<InputState>;
}

#[derive(Debug)]
pub enum TerrainControl {
  ZoomOut,
  ZoomIn,
  ZoomStop,
  Left,
  Right,
  Up,
  Down,
  XMoveStop,
  YMoveStop,
}

#[derive(Debug)]
pub struct TerrainControlSystem {
  queue: mpsc::Receiver<TerrainControl>,
  zoom_level: Option<f32>,
  x_move: Option<f32>,
  y_move: Option<f32>,
}

impl TerrainControlSystem {
  pub fn new() -> (TerrainControlSystem, mpsc::Sender<TerrainControl>) {
    let (tx, rx) = mpsc::channel();
    (TerrainControlSystem {
      queue: rx,
      zoom_level: None,
      x_move: None,
      y_move: None,
    }, tx)
  }
}

impl<C> specs::System<C> for TerrainControlSystem {
  fn run(&mut self, arg: specs::RunArg, _: C) {
    use specs::Join;

    let mut map_input = arg.fetch(|w| w.write::<InputState>());
    while let Ok(control) = self.queue.try_recv() {
      match control {
        TerrainControl::ZoomIn => self.zoom_level = Some(2.0),
        TerrainControl::ZoomOut => self.zoom_level = Some(-2.0),
        TerrainControl::ZoomStop => self.zoom_level = None,
        TerrainControl::Up => self.y_move = Some(-1.0),
        TerrainControl::Down => self.y_move = Some(1.0),
        TerrainControl::YMoveStop => self.y_move = None,
        TerrainControl::Right => self.x_move = Some(1.0),
        TerrainControl::Left => self.x_move = Some(-1.0),
        TerrainControl::XMoveStop => self.x_move = None,
      }
    }
    if let Some(zoom) = self.zoom_level {
      for m in (&mut map_input).join() {
        m.distance += zoom;
      }
    }
    if let Some(x) = self.x_move {
      for m in (&mut map_input).join() {
        m.x_pos += x;
      }
    }
    if let Some(y) = self.y_move {
      for m in (&mut map_input).join() {
        m.y_pos += y;
      }
    }
  }
}
<commit_msg>Restrict zooming in / out<commit_after>use std::sync::mpsc;
use specs;
use game::constants::{VIEW_DISTANCE};

#[derive(Clone, Debug)]
pub struct InputState {
  pub distance: f32,
  pub x_pos: f32,
  pub y_pos: f32,
}

impl InputState {
  pub fn new() -> InputState {
    InputState {
      distance: VIEW_DISTANCE,
      x_pos: 0.0,
      y_pos: 0.0,
    }
  }
}

impl specs::Component for InputState {
  type Storage = specs::HashMapStorage<InputState>;
}

#[derive(Debug)]
pub enum TerrainControl {
  ZoomOut,
  ZoomIn,
  ZoomStop,
  Left,
  Right,
  Up,
  Down,
  XMoveStop,
  YMoveStop,
}

#[derive(Debug)]
pub struct TerrainControlSystem {
  queue: mpsc::Receiver<TerrainControl>,
  zoom_level: Option<f32>,
  x_move: Option<f32>,
  y_move: Option<f32>,
}

impl TerrainControlSystem {
  pub fn new() -> (TerrainControlSystem, mpsc::Sender<TerrainControl>) {
    let (tx, rx) = mpsc::channel();
    (TerrainControlSystem {
      queue: rx,
      zoom_level: None,
      x_move: None,
      y_move: None,
    }, tx)
  }
}

impl<C> specs::System<C> for TerrainControlSystem {
  fn run(&mut self, arg: specs::RunArg, _: C) {
    use specs::Join;

    let mut map_input = arg.fetch(|w| w.write::<InputState>());
    while let Ok(control) = self.queue.try_recv() {
      match control {
        TerrainControl::ZoomIn => self.zoom_level = Some(2.0),
        TerrainControl::ZoomOut => self.zoom_level = Some(-2.0),
        TerrainControl::ZoomStop => self.zoom_level = None,
        TerrainControl::Up => self.y_move = Some(-1.0),
        TerrainControl::Down => self.y_move = Some(1.0),
        TerrainControl::YMoveStop => self.y_move = None,
        TerrainControl::Right => self.x_move = Some(1.0),
        TerrainControl::Left => self.x_move = Some(-1.0),
        TerrainControl::XMoveStop => self.x_move = None,
      }
    }
    if let Some(zoom) = self.zoom_level {
      for m in (&mut map_input).join() {
        if m.distance > 200.0 && zoom < 0.0 || m.distance < 1000.0 && zoom > 0.0 {
          m.distance += zoom;
        }
      }
    }
    if let Some(x) = self.x_move {
      for m in (&mut map_input).join() {
        m.x_pos += x;
      }
    }
    if let Some(y) = self.y_move {
      for m in (&mut map_input).join() {
        m.y_pos += y;
      }
    }
  }
}
<|endoftext|>