<commit_before>mod app;
mod error;
mod exec;
mod exit_codes;
mod filesystem;
mod filetypes;
mod filter;
mod options;
mod output;
mod regex_helper;
mod walk;

use std::env;
use std::path::{Path, PathBuf};
use std::process;
use std::sync::Arc;
use std::time;

use anyhow::{anyhow, Context, Result};
use atty::Stream;
use globset::GlobBuilder;
use lscolors::LsColors;
use regex::bytes::{RegexBuilder, RegexSetBuilder};

use crate::error::print_error;
use crate::exec::CommandTemplate;
use crate::exit_codes::ExitCode;
use crate::filetypes::FileTypes;
#[cfg(unix)]
use crate::filter::OwnerFilter;
use crate::filter::{SizeFilter, TimeFilter};
use crate::options::Options;
use crate::regex_helper::pattern_has_uppercase_char;

// We use jemalloc for performance reasons, see https://github.com/sharkdp/fd/pull/481
#[cfg(all(not(windows), not(target_env = "musl")))]
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

fn run() -> Result<ExitCode> {
    let matches = app::build_app().get_matches_from(env::args_os());

    // Set the current working directory of the process
    if let Some(base_directory) = matches.value_of_os("base-directory") {
        let base_directory = Path::new(base_directory);
        if !filesystem::is_dir(base_directory) {
            return Err(anyhow!(
                "The '--base-directory' path '{}' is not a directory.",
                base_directory.to_string_lossy()
            ));
        }
        env::set_current_dir(base_directory).with_context(|| {
            format!(
                "Could not set '{}' as the current working directory.",
                base_directory.to_string_lossy()
            )
        })?;
    }

    let current_directory = Path::new(".");
    if !filesystem::is_dir(current_directory) {
        return Err(anyhow!(
            "Could not retrieve current directory (has it been deleted?)."
        ));
    }

    // Get the search pattern
    let pattern = matches
        .value_of_os("pattern")
        .map(|p| {
            p.to_str()
                .ok_or_else(|| anyhow!("The search pattern includes invalid UTF-8 sequences."))
        })
        .transpose()?
        .unwrap_or("");

    // Get one or more root directories to search.
    let passed_arguments = matches
        .values_of_os("path")
        .or_else(|| matches.values_of_os("search-path"));

    // Assign current directory to search vector.
    let mut dir_vec: Vec<_> = vec![current_directory.to_path_buf()];

    // Assign any valid arguments to search vector.
    if let Some(paths) = passed_arguments {
        dir_vec = vec![];
        for path in paths {
            let path_buffer = PathBuf::from(path);
            if filesystem::is_dir(&path_buffer) {
                dir_vec.push(path_buffer);
            }
            else {
                print_error(format!("Search path '{}' is not a directory.", path_buffer.to_string_lossy()));
            }
        }
    }

    if matches.is_present("absolute-path") {
        dir_vec = dir_vec
            .iter()
            .map(|path_buffer| {
                path_buffer
                    .canonicalize()
                    .and_then(|pb| filesystem::absolute_path(pb.as_path()))
                    .unwrap()
            })
            .collect();
    }

    // Detect if the user accidentally supplied a path instead of a search pattern
    if !matches.is_present("full-path")
        && pattern.contains(std::path::MAIN_SEPARATOR)
        && filesystem::is_dir(Path::new(pattern))
    {
        return Err(anyhow!(
            "The search pattern '{pattern}' contains a path-separation character ('{sep}') \
             and will not lead to any search results.\n\n\
             If you want to search for all files inside the '{pattern}' directory, use a match-all pattern:\n\n  \
             fd . '{pattern}'\n\n\
             Instead, if you want your pattern to match the full file path, use:\n\n  \
             fd --full-path '{pattern}'",
            pattern = pattern,
            sep = std::path::MAIN_SEPARATOR,
        ));
    }

    let pattern_regex = if matches.is_present("glob") && !pattern.is_empty() {
        let glob = GlobBuilder::new(pattern).literal_separator(true).build()?;
        glob.regex().to_owned()
    } else if matches.is_present("fixed-strings") {
        // Treat pattern as literal string if '--fixed-strings' is used
        regex::escape(pattern)
    } else {
        String::from(pattern)
    };

    // The search will be case-sensitive if the command line flag is set or
    // if the pattern has an uppercase character (smart case).
    let case_sensitive = !matches.is_present("ignore-case")
        && (matches.is_present("case-sensitive") || pattern_has_uppercase_char(&pattern_regex));

    let interactive_terminal = atty::is(Stream::Stdout);
    let colored_output = match matches.value_of("color") {
        Some("always") => true,
        Some("never") => false,
        _ => env::var_os("NO_COLOR").is_none() && interactive_terminal,
    };

    let path_separator = matches.value_of("path-separator").map(|str| str.to_owned());

    #[cfg(windows)]
    ansi_term::enable_ansi_support().ok();

    let ls_colors = if colored_output {
        Some(LsColors::from_env().unwrap_or_default())
    } else {
        None
    };

    let command = if let Some(args) = matches.values_of("exec") {
        Some(CommandTemplate::new(args))
    } else if let Some(args) = matches.values_of("exec-batch") {
        Some(CommandTemplate::new_batch(args)?)
    } else if matches.is_present("list-details") {
        let color = matches.value_of("color").unwrap_or("auto");
        let color_arg = ["--color=", color].concat();

        #[allow(unused)]
        let gnu_ls = |command_name| {
            vec![
                command_name,
                "-l",               // long listing format
                "--human-readable", // human readable file sizes
                "--directory",      // list directories themselves, not their contents
                &color_arg,
            ]
        };

        let cmd: Vec<&str> = if cfg!(unix) {
            if !cfg!(any(
                target_os = "macos",
                target_os = "dragonfly",
                target_os = "freebsd",
                target_os = "netbsd",
                target_os = "openbsd"
            )) {
                // Assume ls is GNU ls
                gnu_ls("ls")
            } else {
                // MacOS, DragonFlyBSD, FreeBSD
                use std::process::{Command, Stdio};

                // Use GNU ls, if available (support for --color=auto, better LS_COLORS support)
                let gnu_ls_exists = Command::new("gls")
                    .arg("--version")
                    .stdout(Stdio::null())
                    .stderr(Stdio::null())
                    .status()
                    .is_ok();

                if gnu_ls_exists {
                    gnu_ls("gls")
                } else {
                    let mut cmd = vec![
                        "ls", // BSD version of ls
                        "-l", // long listing format
                        "-h", // '--human-readable' is not available, '-h' is
                        "-d", // '--directory' is not available, but '-d' is
                    ];

                    if !cfg!(any(target_os = "netbsd", target_os = "openbsd")) && colored_output {
                        // -G is not available in NetBSD's and OpenBSD's ls
                        cmd.push("-G");
                    }

                    cmd
                }
            }
        } else if cfg!(windows) {
            use std::process::{Command, Stdio};

            // Use GNU ls, if available
            let gnu_ls_exists = Command::new("ls")
                .arg("--version")
                .stdout(Stdio::null())
                .stderr(Stdio::null())
                .status()
                .is_ok();

            if gnu_ls_exists {
                gnu_ls("ls")
            } else {
                return Err(anyhow!(
                    "'fd --list-details' is not supported on Windows unless GNU 'ls' is installed."
                ));
            }
        } else {
            return Err(anyhow!(
                "'fd --list-details' is not supported on this platform."
            ));
        };

        Some(CommandTemplate::new_batch(&cmd).unwrap())
    } else {
        None
    };

    let size_limits = if let Some(vs) = matches.values_of("size") {
        vs.map(|sf| {
            SizeFilter::from_string(sf)
                .ok_or_else(|| anyhow!("'{}' is not a valid size constraint. See 'fd --help'.", sf))
        })
        .collect::<Result<Vec<_>>>()?
    } else {
        vec![]
    };

    let now = time::SystemTime::now();
    let mut time_constraints: Vec<TimeFilter> = Vec::new();
    if let Some(t) = matches.value_of("changed-within") {
        if let Some(f) = TimeFilter::after(&now, t) {
            time_constraints.push(f);
        } else {
            return Err(anyhow!(
                "'{}' is not a valid date or duration. See 'fd --help'.",
                t
            ));
        }
    }
    if let Some(t) = matches.value_of("changed-before") {
        if let Some(f) = TimeFilter::before(&now, t) {
            time_constraints.push(f);
        } else {
            return Err(anyhow!(
                "'{}' is not a valid date or duration. See 'fd --help'.",
                t
            ));
        }
    }

    #[cfg(unix)]
    let owner_constraint = if let Some(s) = matches.value_of("owner") {
        OwnerFilter::from_string(s)?
    } else {
        None
    };

    let config = Options {
        case_sensitive,
        search_full_path: matches.is_present("full-path"),
        ignore_hidden: !(matches.is_present("hidden")
            || matches.occurrences_of("rg-alias-hidden-ignore") >= 2),
        read_fdignore: !(matches.is_present("no-ignore")
            || matches.is_present("rg-alias-hidden-ignore")),
        read_vcsignore: !(matches.is_present("no-ignore")
            || matches.is_present("rg-alias-hidden-ignore")
            || matches.is_present("no-ignore-vcs")),
        read_global_ignore: !(matches.is_present("no-ignore")
            || matches.is_present("rg-alias-hidden-ignore")
            || matches.is_present("no-global-ignore-file")),
        follow_links: matches.is_present("follow"),
        one_file_system: matches.is_present("one-file-system"),
        null_separator: matches.is_present("null_separator"),
        max_depth: matches
            .value_of("max-depth")
            .or_else(|| matches.value_of("rg-depth"))
            .or_else(|| matches.value_of("exact-depth"))
            .and_then(|n| usize::from_str_radix(n, 10).ok()),
        min_depth: matches
            .value_of("min-depth")
            .or_else(|| matches.value_of("exact-depth"))
            .and_then(|n| usize::from_str_radix(n, 10).ok()),
        threads: std::cmp::max(
            matches
                .value_of("threads")
                .and_then(|n| usize::from_str_radix(n, 10).ok())
                .unwrap_or_else(num_cpus::get),
            1,
        ),
        max_buffer_time: matches
            .value_of("max-buffer-time")
            .and_then(|n| u64::from_str_radix(n, 10).ok())
            .map(time::Duration::from_millis),
        ls_colors,
        interactive_terminal,
        file_types: matches.values_of("file-type").map(|values| {
            let mut file_types = FileTypes::default();
            for value in values {
                match value {
                    "f" | "file" => file_types.files = true,
                    "d" | "directory" => file_types.directories = true,
                    "l" | "symlink" => file_types.symlinks = true,
                    "x" | "executable" => {
                        file_types.executables_only = true;
                        file_types.files = true;
                    }
                    "e" | "empty" => file_types.empty_only = true,
                    "s" | "socket" => file_types.sockets = true,
                    "p" | "pipe" => file_types.pipes = true,
                    _ => unreachable!(),
                }
            }

            // If only 'empty' was specified, search for both files and directories:
            if file_types.empty_only && !(file_types.files || file_types.directories) {
                file_types.files = true;
                file_types.directories = true;
            }

            file_types
        }),
        extensions: matches
            .values_of("extension")
            .map(|exts| {
                let patterns = exts
                    .map(|e| e.trim_start_matches('.'))
                    .map(|e| format!(r".\.{}$", regex::escape(e)));
                RegexSetBuilder::new(patterns)
                    .case_insensitive(true)
                    .build()
            })
            .transpose()?,
        command: command.map(Arc::new),
        exclude_patterns: matches
            .values_of("exclude")
            .map(|v| v.map(|p| String::from("!") + p).collect())
            .unwrap_or_else(|| vec![]),
        ignore_files: matches
            .values_of("ignore-file")
            .map(|vs| vs.map(PathBuf::from).collect())
            .unwrap_or_else(|| vec![]),
        size_constraints: size_limits,
        time_constraints,
        #[cfg(unix)]
        owner_constraint,
        show_filesystem_errors: matches.is_present("show-errors"),
        path_separator,
        max_results: matches
            .value_of("max-results")
            .and_then(|n| usize::from_str_radix(n, 10).ok())
            .filter(|&n| n != 0)
            .or_else(|| {
                if matches.is_present("max-one-result") {
                    Some(1)
                } else {
                    None
                }
            }),
    };

    let re = RegexBuilder::new(&pattern_regex)
        .case_insensitive(!config.case_sensitive)
        .dot_matches_new_line(true)
        .build()
        .map_err(|e| {
            anyhow!(
                "{}\n\nNote: You can use the '--fixed-strings' option to search for a \
                 literal string instead of a regular expression. Alternatively, you can \
                 also use the '--glob' option to match on a glob pattern.",
                e.to_string()
            )
        })?;

    walk::scan(&dir_vec, Arc::new(re), Arc::new(config))
}

fn main() {
    let result = run();
    match result {
        Ok(exit_code) => {
            process::exit(exit_code.into());
        }
        Err(err) => {
            eprintln!("[fd error]: {}", err);
            process::exit(ExitCode::GeneralError.into());
        }
    }
}
<commit_msg>Check for no valid search paths to prevent panic<commit_after>mod app;
mod error;
mod exec;
mod exit_codes;
mod filesystem;
mod filetypes;
mod filter;
mod options;
mod output;
mod regex_helper;
mod walk;

use std::env;
use std::path::{Path, PathBuf};
use std::process;
use std::sync::Arc;
use std::time;

use anyhow::{anyhow, Context, Result};
use atty::Stream;
use globset::GlobBuilder;
use lscolors::LsColors;
use regex::bytes::{RegexBuilder, RegexSetBuilder};

use crate::error::print_error;
use crate::exec::CommandTemplate;
use crate::exit_codes::ExitCode;
use crate::filetypes::FileTypes;
#[cfg(unix)]
use crate::filter::OwnerFilter;
use crate::filter::{SizeFilter, TimeFilter};
use crate::options::Options;
use crate::regex_helper::pattern_has_uppercase_char;

// We use jemalloc for performance reasons, see https://github.com/sharkdp/fd/pull/481
#[cfg(all(not(windows), not(target_env = "musl")))]
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

fn run() -> Result<ExitCode> {
    let matches = app::build_app().get_matches_from(env::args_os());

    // Set the current working directory of the process
    if let Some(base_directory) = matches.value_of_os("base-directory") {
        let base_directory = Path::new(base_directory);
        if !filesystem::is_dir(base_directory) {
            return Err(anyhow!(
                "The '--base-directory' path '{}' is not a directory.",
                base_directory.to_string_lossy()
            ));
        }
        env::set_current_dir(base_directory).with_context(|| {
            format!(
                "Could not set '{}' as the current working directory.",
                base_directory.to_string_lossy()
            )
        })?;
    }

    let current_directory = Path::new(".");
    if !filesystem::is_dir(current_directory) {
        return Err(anyhow!(
            "Could not retrieve current directory (has it been deleted?)."
        ));
    }

    // Get the search pattern
    let pattern = matches
        .value_of_os("pattern")
        .map(|p| {
            p.to_str()
                .ok_or_else(|| anyhow!("The search pattern includes invalid UTF-8 sequences."))
        })
        .transpose()?
        .unwrap_or("");

    // Get one or more root directories to search.
    let passed_arguments = matches
        .values_of_os("path")
        .or_else(|| matches.values_of_os("search-path"));

    // Assign current directory to search vector.
    let mut dir_vec: Vec<_> = vec![current_directory.to_path_buf()];

    // Assign any valid arguments to search vector.
    if let Some(paths) = passed_arguments {
        dir_vec = vec![];
        for path in paths {
            let path_buffer = PathBuf::from(path);
            if filesystem::is_dir(&path_buffer) {
                dir_vec.push(path_buffer);
            }
            else {
                print_error(format!("Search path '{}' is not a directory.", path_buffer.to_string_lossy()));
            }
        }
    }

    // Check if we have no valid search paths.
    if dir_vec.is_empty() {
      anyhow!("No valid search paths given.");
    }

    if matches.is_present("absolute-path") {
        dir_vec = dir_vec
            .iter()
            .map(|path_buffer| {
                path_buffer
                    .canonicalize()
                    .and_then(|pb| filesystem::absolute_path(pb.as_path()))
                    .unwrap()
            })
            .collect();
    }

    // Detect if the user accidentally supplied a path instead of a search pattern
    if !matches.is_present("full-path")
        && pattern.contains(std::path::MAIN_SEPARATOR)
        && filesystem::is_dir(Path::new(pattern))
    {
        return Err(anyhow!(
            "The search pattern '{pattern}' contains a path-separation character ('{sep}') \
             and will not lead to any search results.\n\n\
             If you want to search for all files inside the '{pattern}' directory, use a match-all pattern:\n\n  \
             fd . '{pattern}'\n\n\
             Instead, if you want your pattern to match the full file path, use:\n\n  \
             fd --full-path '{pattern}'",
            pattern = pattern,
            sep = std::path::MAIN_SEPARATOR,
        ));
    }

    let pattern_regex = if matches.is_present("glob") && !pattern.is_empty() {
        let glob = GlobBuilder::new(pattern).literal_separator(true).build()?;
        glob.regex().to_owned()
    } else if matches.is_present("fixed-strings") {
        // Treat pattern as literal string if '--fixed-strings' is used
        regex::escape(pattern)
    } else {
        String::from(pattern)
    };

    // The search will be case-sensitive if the command line flag is set or
    // if the pattern has an uppercase character (smart case).
    let case_sensitive = !matches.is_present("ignore-case")
        && (matches.is_present("case-sensitive") || pattern_has_uppercase_char(&pattern_regex));

    let interactive_terminal = atty::is(Stream::Stdout);
    let colored_output = match matches.value_of("color") {
        Some("always") => true,
        Some("never") => false,
        _ => env::var_os("NO_COLOR").is_none() && interactive_terminal,
    };

    let path_separator = matches.value_of("path-separator").map(|str| str.to_owned());

    #[cfg(windows)]
    ansi_term::enable_ansi_support().ok();

    let ls_colors = if colored_output {
        Some(LsColors::from_env().unwrap_or_default())
    } else {
        None
    };

    let command = if let Some(args) = matches.values_of("exec") {
        Some(CommandTemplate::new(args))
    } else if let Some(args) = matches.values_of("exec-batch") {
        Some(CommandTemplate::new_batch(args)?)
    } else if matches.is_present("list-details") {
        let color = matches.value_of("color").unwrap_or("auto");
        let color_arg = ["--color=", color].concat();

        #[allow(unused)]
        let gnu_ls = |command_name| {
            vec![
                command_name,
                "-l",               // long listing format
                "--human-readable", // human readable file sizes
                "--directory",      // list directories themselves, not their contents
                &color_arg,
            ]
        };

        let cmd: Vec<&str> = if cfg!(unix) {
            if !cfg!(any(
                target_os = "macos",
                target_os = "dragonfly",
                target_os = "freebsd",
                target_os = "netbsd",
                target_os = "openbsd"
            )) {
                // Assume ls is GNU ls
                gnu_ls("ls")
            } else {
                // MacOS, DragonFlyBSD, FreeBSD
                use std::process::{Command, Stdio};

                // Use GNU ls, if available (support for --color=auto, better LS_COLORS support)
                let gnu_ls_exists = Command::new("gls")
                    .arg("--version")
                    .stdout(Stdio::null())
                    .stderr(Stdio::null())
                    .status()
                    .is_ok();

                if gnu_ls_exists {
                    gnu_ls("gls")
                } else {
                    let mut cmd = vec![
                        "ls", // BSD version of ls
                        "-l", // long listing format
                        "-h", // '--human-readable' is not available, '-h' is
                        "-d", // '--directory' is not available, but '-d' is
                    ];

                    if !cfg!(any(target_os = "netbsd", target_os = "openbsd")) && colored_output {
                        // -G is not available in NetBSD's and OpenBSD's ls
                        cmd.push("-G");
                    }

                    cmd
                }
            }
        } else if cfg!(windows) {
            use std::process::{Command, Stdio};

            // Use GNU ls, if available
            let gnu_ls_exists = Command::new("ls")
                .arg("--version")
                .stdout(Stdio::null())
                .stderr(Stdio::null())
                .status()
                .is_ok();

            if gnu_ls_exists {
                gnu_ls("ls")
            } else {
                return Err(anyhow!(
                    "'fd --list-details' is not supported on Windows unless GNU 'ls' is installed."
                ));
            }
        } else {
            return Err(anyhow!(
                "'fd --list-details' is not supported on this platform."
            ));
        };

        Some(CommandTemplate::new_batch(&cmd).unwrap())
    } else {
        None
    };

    let size_limits = if let Some(vs) = matches.values_of("size") {
        vs.map(|sf| {
            SizeFilter::from_string(sf)
                .ok_or_else(|| anyhow!("'{}' is not a valid size constraint. See 'fd --help'.", sf))
        })
        .collect::<Result<Vec<_>>>()?
    } else {
        vec![]
    };

    let now = time::SystemTime::now();
    let mut time_constraints: Vec<TimeFilter> = Vec::new();
    if let Some(t) = matches.value_of("changed-within") {
        if let Some(f) = TimeFilter::after(&now, t) {
            time_constraints.push(f);
        } else {
            return Err(anyhow!(
                "'{}' is not a valid date or duration. See 'fd --help'.",
                t
            ));
        }
    }
    if let Some(t) = matches.value_of("changed-before") {
        if let Some(f) = TimeFilter::before(&now, t) {
            time_constraints.push(f);
        } else {
            return Err(anyhow!(
                "'{}' is not a valid date or duration. See 'fd --help'.",
                t
            ));
        }
    }

    #[cfg(unix)]
    let owner_constraint = if let Some(s) = matches.value_of("owner") {
        OwnerFilter::from_string(s)?
    } else {
        None
    };

    let config = Options {
        case_sensitive,
        search_full_path: matches.is_present("full-path"),
        ignore_hidden: !(matches.is_present("hidden")
            || matches.occurrences_of("rg-alias-hidden-ignore") >= 2),
        read_fdignore: !(matches.is_present("no-ignore")
            || matches.is_present("rg-alias-hidden-ignore")),
        read_vcsignore: !(matches.is_present("no-ignore")
            || matches.is_present("rg-alias-hidden-ignore")
            || matches.is_present("no-ignore-vcs")),
        read_global_ignore: !(matches.is_present("no-ignore")
            || matches.is_present("rg-alias-hidden-ignore")
            || matches.is_present("no-global-ignore-file")),
        follow_links: matches.is_present("follow"),
        one_file_system: matches.is_present("one-file-system"),
        null_separator: matches.is_present("null_separator"),
        max_depth: matches
            .value_of("max-depth")
            .or_else(|| matches.value_of("rg-depth"))
            .or_else(|| matches.value_of("exact-depth"))
            .and_then(|n| usize::from_str_radix(n, 10).ok()),
        min_depth: matches
            .value_of("min-depth")
            .or_else(|| matches.value_of("exact-depth"))
            .and_then(|n| usize::from_str_radix(n, 10).ok()),
        threads: std::cmp::max(
            matches
                .value_of("threads")
                .and_then(|n| usize::from_str_radix(n, 10).ok())
                .unwrap_or_else(num_cpus::get),
            1,
        ),
        max_buffer_time: matches
            .value_of("max-buffer-time")
            .and_then(|n| u64::from_str_radix(n, 10).ok())
            .map(time::Duration::from_millis),
        ls_colors,
        interactive_terminal,
        file_types: matches.values_of("file-type").map(|values| {
            let mut file_types = FileTypes::default();
            for value in values {
                match value {
                    "f" | "file" => file_types.files = true,
                    "d" | "directory" => file_types.directories = true,
                    "l" | "symlink" => file_types.symlinks = true,
                    "x" | "executable" => {
                        file_types.executables_only = true;
                        file_types.files = true;
                    }
                    "e" | "empty" => file_types.empty_only = true,
                    "s" | "socket" => file_types.sockets = true,
                    "p" | "pipe" => file_types.pipes = true,
                    _ => unreachable!(),
                }
            }

            // If only 'empty' was specified, search for both files and directories:
            if file_types.empty_only && !(file_types.files || file_types.directories) {
                file_types.files = true;
                file_types.directories = true;
            }

            file_types
        }),
        extensions: matches
            .values_of("extension")
            .map(|exts| {
                let patterns = exts
                    .map(|e| e.trim_start_matches('.'))
                    .map(|e| format!(r".\.{}$", regex::escape(e)));
                RegexSetBuilder::new(patterns)
                    .case_insensitive(true)
                    .build()
            })
            .transpose()?,
        command: command.map(Arc::new),
        exclude_patterns: matches
            .values_of("exclude")
            .map(|v| v.map(|p| String::from("!") + p).collect())
            .unwrap_or_else(|| vec![]),
        ignore_files: matches
            .values_of("ignore-file")
            .map(|vs| vs.map(PathBuf::from).collect())
            .unwrap_or_else(|| vec![]),
        size_constraints: size_limits,
        time_constraints,
        #[cfg(unix)]
        owner_constraint,
        show_filesystem_errors: matches.is_present("show-errors"),
        path_separator,
        max_results: matches
            .value_of("max-results")
            .and_then(|n| usize::from_str_radix(n, 10).ok())
            .filter(|&n| n != 0)
            .or_else(|| {
                if matches.is_present("max-one-result") {
                    Some(1)
                } else {
                    None
                }
            }),
    };

    let re = RegexBuilder::new(&pattern_regex)
        .case_insensitive(!config.case_sensitive)
        .dot_matches_new_line(true)
        .build()
        .map_err(|e| {
            anyhow!(
                "{}\n\nNote: You can use the '--fixed-strings' option to search for a \
                 literal string instead of a regular expression. Alternatively, you can \
                 also use the '--glob' option to match on a glob pattern.",
                e.to_string()
            )
        })?;

    walk::scan(&dir_vec, Arc::new(re), Arc::new(config))
}

fn main() {
    let result = run();
    match result {
        Ok(exit_code) => {
            process::exit(exit_code.into());
        }
        Err(err) => {
            eprintln!("[fd error]: {}", err);
            process::exit(ExitCode::GeneralError.into());
        }
    }
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate env_logger;
extern crate log;
extern crate shlex;
extern crate skim;
extern crate time;

use clap::{App, Arg};
use skim::{Skim, SkimOptions};
use std::env;

const VERSION: &str = "0.5.2";

const USAGE: &str = "
Usage: sk [options]

  Options
    -h, --help           print this help menu
    --version            print out the current version of skim

  Search
    --tac                reverse the order of input
    -t, --tiebreak [score,index,begin,end,-score,...]
                         comma seperated criteria
    -n, --nth 1,2..5     specify the fields to be matched
    --with-nth 1,2..5    specify the fields to be transformed
    -d, --delimiter \\t  specify the delimiter(in REGEX) for fields
    -e, --exact          start skim in exact mode
    --regex              use regex instead of fuzzy match

  Interface
    -b, --bind KEYBINDS  comma seperated keybindings, in KEY:ACTION
                         such as 'ctrl-j:accept,ctrl-k:kill-line'
    -m, --multi          Enable Multiple Selection
    --no-multi           Disable Multiple Selection
    -c, --cmd ag         command to invoke dynamically
    -I replstr           replace `replstr` with the selected item
    -i, --interactive    Start skim in interactive(command) mode
    --color [BASE][,COLOR:ANSI]
                         change color theme
    --no-hscroll         Disable horizontal scroll

  Layout
    --reverse            Reverse orientation
    --height=HEIGHT      Height of skim's window (--height 40%)
    --no-height          Disable height feature
    --min-height=HEIGHT  Minimum height when --height is given by percent
                         (default: 10)
    --margin=MARGIN      Screen Margin (TRBL / TB,RL / T,RL,B / T,R,B,L)
                         e.g. (sk --margin 1,10%)
    -p, --prompt '> '    prompt string for query mode
    --cmd-prompt '> '    prompt string for command mode

  Display
    --ansi               parse ANSI color codes for input strings
    --tabstop=SPACES     Number of spaces for a tab character (default: 8)

  Preview
    --preview=COMMAND    command to preview current highlighted line ({})
                         We can specify the fields. e.g. ({1}, {..3}, {0..})
    --preview-window=OPT Preview window layout (default: right:50%)
                         [up|down|left|right][:SIZE[%]][:hidden]

  Scripting
    -q, --query \"\"       specify the initial query
    --cmd-query \"\"       specify the initial query for interactive mode
    --expect KEYS        comma seperated keys that can be used to complete skim
    --read0              Read input delimited by ASCII NUL(\\0) characters
    --print0             Print output delimited by ASCII NUL(\\0) characters
    --print-query        Print query as the first line
    --print-cmd          Print command query as the first line (after --print-query)

  Environment variables
    SKIM_DEFAULT_COMMAND Default command to use when input is tty
    SKIM_DEFAULT_OPTIONS Default options (e.g. '--ansi --regex')
                         You should not include other environment variables
                         (e.g. '-c \"$HOME/bin/ag\"')

  Reserved (not used for now)
    --extended
    --algo=TYPE
    --literal
    --no-mouse
    --cycle
    --hscroll-off=COL
    --filepath-word
    --jump-labels=CHARS
    --border
    --inline-info
    --header=STR
    --header-lines=N
    --no-bold
    --history=FILE
    --history-size=N
    --sync
    --no-sort
    --select-1
    --exit-0
    --filter
";

fn main() {
    use env_logger::LogBuilder;
    use log::{LogLevelFilter, LogRecord};

    let format = |record: &LogRecord| {
        let t = time::now();
        format!(
            "{},{:03} - {} - {}",
            time::strftime("%Y-%m-%d %H:%M:%S", &t).expect("main: time format error"),
            t.tm_nsec / 1_000_000,
            record.level(),
            record.args()
        )
    };

    let mut builder = LogBuilder::new();
    builder.format(format).filter(None, LogLevelFilter::Info);

    if env::var("RUST_LOG").is_ok() {
        builder.parse(&env::var("RUST_LOG").unwrap());
    }

    builder.init().expect("failed to initialize logger builder");

    let exit_code = real_main();
    std::process::exit(exit_code);
}

#[cfg_attr(rustfmt, rustfmt_skip)]
fn real_main() -> i32 {
    let mut args = Vec::new();

    args.push(env::args().next().expect("there should be at least one arg: the application name"));
    args.extend(env::var("SKIM_DEFAULT_OPTIONS")
                .ok()
                .and_then(|val| shlex::split(&val))
                .unwrap_or_default());
    for arg in env::args().skip(1) {
        args.push(arg);
    }

    //------------------------------------------------------------------------------
    // parse options
    let opts = App::new("sk")
        .author("Jinzhou Zhang<lotabout@gmail.com")
        .arg(Arg::with_name("help").long("help").short("h"))
        .arg(Arg::with_name("version").long("version").short("v"))
        .arg(Arg::with_name("bind").long("bind").short("b").multiple(true).takes_value(true))
        .arg(Arg::with_name("multi").long("multi").short("m").multiple(true))
        .arg(Arg::with_name("no-multi").long("no-multi").multiple(true))
        .arg(Arg::with_name("prompt").long("prompt").short("p").multiple(true).takes_value(true).default_value("> "))
        .arg(Arg::with_name("cmd-prompt").long("cmd-prompt").multiple(true).takes_value(true).default_value("c> "))
        .arg(Arg::with_name("expect").long("expect").multiple(true).takes_value(true))
        .arg(Arg::with_name("tac").long("tac").multiple(true))
        .arg(Arg::with_name("tiebreak").long("tiebreak").short("t").multiple(true).takes_value(true))
        .arg(Arg::with_name("ansi").long("ansi").multiple(true))
        .arg(Arg::with_name("exact").long("exact").short("e").multiple(true))
        .arg(Arg::with_name("cmd").long("cmd").short("cmd").multiple(true).takes_value(true))
        .arg(Arg::with_name("interactive").long("interactive").short("i").multiple(true))
        .arg(Arg::with_name("query").long("query").short("q").multiple(true).takes_value(true))
        .arg(Arg::with_name("cmd-query").long("cmd-query").multiple(true).takes_value(true))
        .arg(Arg::with_name("regex").long("regex").multiple(true))
        .arg(Arg::with_name("delimiter").long("delimiter").short("d").multiple(true).takes_value(true))
        .arg(Arg::with_name("nth").long("nth").short("n").multiple(true).takes_value(true))
        .arg(Arg::with_name("with-nth").long("with-nth").multiple(true).takes_value(true))
        .arg(Arg::with_name("replstr").short("I").multiple(true).takes_value(true))
        .arg(Arg::with_name("color").long("color").multiple(true).takes_value(true))
        .arg(Arg::with_name("margin").long("margin").multiple(true).takes_value(true).default_value("0,0,0,0"))
        .arg(Arg::with_name("min-height").long("min-height").multiple(true).takes_value(true).default_value("10"))
        .arg(Arg::with_name("height").long("height").multiple(true).takes_value(true).default_value("100%"))
        .arg(Arg::with_name("no-height").long("no-height").multiple(true))
        .arg(Arg::with_name("preview").long("preview").multiple(true).takes_value(true))
        .arg(Arg::with_name("preview-window").long("preview-window").multiple(true).takes_value(true).default_value("right:50%"))
        .arg(Arg::with_name("reverse").long("reverse").multiple(true))

        .arg(Arg::with_name("algorithm").long("algo").multiple(true).takes_value(true).default_value(""))
        .arg(Arg::with_name("literal").long("literal").multiple(true))
        .arg(Arg::with_name("no-mouse").long("no-mouse").multiple(true))
        .arg(Arg::with_name("cycle").long("cycle").multiple(true))
        .arg(Arg::with_name("no-hscroll").long("no-hscroll").multiple(true))
        .arg(Arg::with_name("hscroll-off").long("hscroll-off").multiple(true).takes_value(true).default_value("10"))
        .arg(Arg::with_name("filepath-word").long("filepath-word").multiple(true))
        .arg(Arg::with_name("jump-labels").long("jump-labels").multiple(true).takes_value(true).default_value("abcdefghijklmnopqrstuvwxyz"))
        .arg(Arg::with_name("border").long("border").multiple(true))
        .arg(Arg::with_name("inline-info").long("inline-info").multiple(true))
        .arg(Arg::with_name("header").long("header").multiple(true).takes_value(true).default_value(""))
        .arg(Arg::with_name("header-lines").long("header-lines").multiple(true).takes_value(true).default_value("1"))
        .arg(Arg::with_name("tabstop").long("tabstop").multiple(true).takes_value(true).default_value("8"))
        .arg(Arg::with_name("no-bold").long("no-bold").multiple(true))
        .arg(Arg::with_name("history").long("history").multiple(true).takes_value(true).default_value(""))
        .arg(Arg::with_name("history-size").long("history-size").multiple(true).takes_value(true).default_value("500"))
        .arg(Arg::with_name("print-query").long("print-query").multiple(true))
        .arg(Arg::with_name("print-cmd").long("print-cmd").multiple(true))
        .arg(Arg::with_name("read0").long("read0").multiple(true))
        .arg(Arg::with_name("print0").long("print0").multiple(true))
        .arg(Arg::with_name("sync").long("sync").multiple(true))
        .arg(Arg::with_name("extended").long("extended").short("x").multiple(true))
        .arg(Arg::with_name("no-sort").long("no-sort").multiple(true))
        .arg(Arg::with_name("select-1").long("select-1").short("1").multiple(true))
        .arg(Arg::with_name("exit-0").long("exit-0").short("0").multiple(true))
        .arg(Arg::with_name("filter").long("filter").short("f").multiple(true))
        .get_matches_from(args);

    if opts.is_present("help") {
        print!("{}", USAGE);
        return 0;
    }

    if opts.is_present("version") {
        println!("{}", VERSION);
        return 0;
    }

    let options = SkimOptions::from_options(&opts);
    let output_ending = if options.print0 {"\0"} else {"\n"};

    let output = Skim::run_with(&options, None);
    if let None = output {
        return 130;
    }

    let output = output.unwrap();

    // output query
    if options.print_query {
        print!("{}{}", output.query, output_ending);
    }

    if options.print_cmd {
        print!("{}{}", output.cmd, output_ending);
    }

    output.accept_key.map(|key| {
        print!("{}{}", key, output_ending);
    });

    for item in output.selected_items.iter() {
        print!("{}{}", item.get_output_text(), output_ending);
    }

    if output.selected_items.len() > 0 {0} else {1}
}
<commit_msg>use env! get version<commit_after>extern crate clap;
extern crate env_logger;
extern crate log;
extern crate shlex;
extern crate skim;
extern crate time;

use clap::{App, Arg};
use skim::{Skim, SkimOptions};
use std::env;

const VERSION: &str = env!("CARGO_PKG_VERSION");

const USAGE: &str = "
Usage: sk [options]

  Options
    -h, --help           print this help menu
    --version            print out the current version of skim

  Search
    --tac                reverse the order of input
    -t, --tiebreak [score,index,begin,end,-score,...]
                         comma seperated criteria
    -n, --nth 1,2..5     specify the fields to be matched
    --with-nth 1,2..5    specify the fields to be transformed
    -d, --delimiter \\t  specify the delimiter(in REGEX) for fields
    -e, --exact          start skim in exact mode
    --regex              use regex instead of fuzzy match

  Interface
    -b, --bind KEYBINDS  comma seperated keybindings, in KEY:ACTION
                         such as 'ctrl-j:accept,ctrl-k:kill-line'
    -m, --multi          Enable Multiple Selection
    --no-multi           Disable Multiple Selection
    -c, --cmd ag         command to invoke dynamically
    -I replstr           replace `replstr` with the selected item
    -i, --interactive    Start skim in interactive(command) mode
    --color [BASE][,COLOR:ANSI]
                         change color theme
    --no-hscroll         Disable horizontal scroll

  Layout
    --reverse            Reverse orientation
    --height=HEIGHT      Height of skim's window (--height 40%)
    --no-height          Disable height feature
    --min-height=HEIGHT  Minimum height when --height is given by percent
                         (default: 10)
    --margin=MARGIN      Screen Margin (TRBL / TB,RL / T,RL,B / T,R,B,L)
                         e.g. (sk --margin 1,10%)
    -p, --prompt '> '    prompt string for query mode
    --cmd-prompt '> '    prompt string for command mode

  Display
    --ansi               parse ANSI color codes for input strings
    --tabstop=SPACES     Number of spaces for a tab character (default: 8)

  Preview
    --preview=COMMAND    command to preview current highlighted line ({})
                         We can specify the fields. e.g. ({1}, {..3}, {0..})
    --preview-window=OPT Preview window layout (default: right:50%)
                         [up|down|left|right][:SIZE[%]][:hidden]

  Scripting
    -q, --query \"\"       specify the initial query
    --cmd-query \"\"       specify the initial query for interactive mode
    --expect KEYS        comma seperated keys that can be used to complete skim
    --read0              Read input delimited by ASCII NUL(\\0) characters
    --print0             Print output delimited by ASCII NUL(\\0) characters
    --print-query        Print query as the first line
    --print-cmd          Print command query as the first line (after --print-query)

  Environment variables
    SKIM_DEFAULT_COMMAND Default command to use when input is tty
    SKIM_DEFAULT_OPTIONS Default options (e.g. '--ansi --regex')
                         You should not include other environment variables
                         (e.g. '-c \"$HOME/bin/ag\"')

  Reserved (not used for now)
    --extended
    --algo=TYPE
    --literal
    --no-mouse
    --cycle
    --hscroll-off=COL
    --filepath-word
    --jump-labels=CHARS
    --border
    --inline-info
    --header=STR
    --header-lines=N
    --no-bold
    --history=FILE
    --history-size=N
    --sync
    --no-sort
    --select-1
    --exit-0
    --filter
";

fn main() {
    use env_logger::LogBuilder;
    use log::{LogLevelFilter, LogRecord};

    let format = |record: &LogRecord| {
        let t = time::now();
        format!(
            "{},{:03} - {} - {}",
            time::strftime("%Y-%m-%d %H:%M:%S", &t).expect("main: time format error"),
            t.tm_nsec / 1_000_000,
            record.level(),
            record.args()
        )
    };

    let mut builder = LogBuilder::new();
    builder.format(format).filter(None, LogLevelFilter::Info);

    if env::var("RUST_LOG").is_ok() {
        builder.parse(&env::var("RUST_LOG").unwrap());
    }

    builder.init().expect("failed to initialize logger builder");

    let exit_code = real_main();
    std::process::exit(exit_code);
}

#[cfg_attr(rustfmt, rustfmt_skip)]
fn real_main() -> i32 {
    let mut args = Vec::new();

    args.push(env::args().next().expect("there should be at least one arg: the application name"));
    args.extend(env::var("SKIM_DEFAULT_OPTIONS")
                .ok()
                .and_then(|val| shlex::split(&val))
                .unwrap_or_default());
    for arg in env::args().skip(1) {
        args.push(arg);
    }

    //------------------------------------------------------------------------------
    // parse options
    let opts = App::new("sk")
        .author("Jinzhou Zhang<lotabout@gmail.com")
        .arg(Arg::with_name("help").long("help").short("h"))
        .arg(Arg::with_name("version").long("version").short("v"))
        .arg(Arg::with_name("bind").long("bind").short("b").multiple(true).takes_value(true))
        .arg(Arg::with_name("multi").long("multi").short("m").multiple(true))
        .arg(Arg::with_name("no-multi").long("no-multi").multiple(true))
        .arg(Arg::with_name("prompt").long("prompt").short("p").multiple(true).takes_value(true).default_value("> "))
        .arg(Arg::with_name("cmd-prompt").long("cmd-prompt").multiple(true).takes_value(true).default_value("c> "))
        .arg(Arg::with_name("expect").long("expect").multiple(true).takes_value(true))
        .arg(Arg::with_name("tac").long("tac").multiple(true))
        .arg(Arg::with_name("tiebreak").long("tiebreak").short("t").multiple(true).takes_value(true))
        .arg(Arg::with_name("ansi").long("ansi").multiple(true))
        .arg(Arg::with_name("exact").long("exact").short("e").multiple(true))
        .arg(Arg::with_name("cmd").long("cmd").short("cmd").multiple(true).takes_value(true))
        .arg(Arg::with_name("interactive").long("interactive").short("i").multiple(true))
        .arg(Arg::with_name("query").long("query").short("q").multiple(true).takes_value(true))
        .arg(Arg::with_name("cmd-query").long("cmd-query").multiple(true).takes_value(true))
        .arg(Arg::with_name("regex").long("regex").multiple(true))
        .arg(Arg::with_name("delimiter").long("delimiter").short("d").multiple(true).takes_value(true))
        .arg(Arg::with_name("nth").long("nth").short("n").multiple(true).takes_value(true))
        .arg(Arg::with_name("with-nth").long("with-nth").multiple(true).takes_value(true))
        .arg(Arg::with_name("replstr").short("I").multiple(true).takes_value(true))
        .arg(Arg::with_name("color").long("color").multiple(true).takes_value(true))
        .arg(Arg::with_name("margin").long("margin").multiple(true).takes_value(true).default_value("0,0,0,0"))
        .arg(Arg::with_name("min-height").long("min-height").multiple(true).takes_value(true).default_value("10"))
        .arg(Arg::with_name("height").long("height").multiple(true).takes_value(true).default_value("100%"))
        .arg(Arg::with_name("no-height").long("no-height").multiple(true))
        .arg(Arg::with_name("preview").long("preview").multiple(true).takes_value(true))
        .arg(Arg::with_name("preview-window").long("preview-window").multiple(true).takes_value(true).default_value("right:50%"))
        .arg(Arg::with_name("reverse").long("reverse").multiple(true))

        .arg(Arg::with_name("algorithm").long("algo").multiple(true).takes_value(true).default_value(""))
        .arg(Arg::with_name("literal").long("literal").multiple(true))
        .arg(Arg::with_name("no-mouse").long("no-mouse").multiple(true))
        .arg(Arg::with_name("cycle").long("cycle").multiple(true))
        .arg(Arg::with_name("no-hscroll").long("no-hscroll").multiple(true))
        .arg(Arg::with_name("hscroll-off").long("hscroll-off").multiple(true).takes_value(true).default_value("10"))
        .arg(Arg::with_name("filepath-word").long("filepath-word").multiple(true))
        .arg(Arg::with_name("jump-labels").long("jump-labels").multiple(true).takes_value(true).default_value("abcdefghijklmnopqrstuvwxyz"))
        .arg(Arg::with_name("border").long("border").multiple(true))
        .arg(Arg::with_name("inline-info").long("inline-info").multiple(true))
        .arg(Arg::with_name("header").long("header").multiple(true).takes_value(true).default_value(""))
        .arg(Arg::with_name("header-lines").long("header-lines").multiple(true).takes_value(true).default_value("1"))
        .arg(Arg::with_name("tabstop").long("tabstop").multiple(true).takes_value(true).default_value("8"))
        .arg(Arg::with_name("no-bold").long("no-bold").multiple(true))
        .arg(Arg::with_name("history").long("history").multiple(true).takes_value(true).default_value(""))
        .arg(Arg::with_name("history-size").long("history-size").multiple(true).takes_value(true).default_value("500"))
        .arg(Arg::with_name("print-query").long("print-query").multiple(true))
        .arg(Arg::with_name("print-cmd").long("print-cmd").multiple(true))
        .arg(Arg::with_name("read0").long("read0").multiple(true))
        .arg(Arg::with_name("print0").long("print0").multiple(true))
        .arg(Arg::with_name("sync").long("sync").multiple(true))
        .arg(Arg::with_name("extended").long("extended").short("x").multiple(true))
        .arg(Arg::with_name("no-sort").long("no-sort").multiple(true))
        .arg(Arg::with_name("select-1").long("select-1").short("1").multiple(true))
        .arg(Arg::with_name("exit-0").long("exit-0").short("0").multiple(true))
        .arg(Arg::with_name("filter").long("filter").short("f").multiple(true))
        .get_matches_from(args);

    if opts.is_present("help") {
        print!("{}", USAGE);
        return 0;
    }

    if opts.is_present("version") {
        println!("{}", VERSION);
        return 0;
    }

    let options = SkimOptions::from_options(&opts);
    let output_ending = if options.print0 {"\0"} else {"\n"};

    let output = Skim::run_with(&options, None);
    if let None = output {
        return 130;
    }

    let output = output.unwrap();

    // output query
    if options.print_query {
        print!("{}{}", output.query, output_ending);
    }

    if options.print_cmd {
        print!("{}{}", output.cmd, output_ending);
    }

    output.accept_key.map(|key| {
        print!("{}{}", key, output_ending);
    });

    for item in output.selected_items.iter() {
        print!("{}{}", item.get_output_text(), output_ending);
    }

    if output.selected_items.len() > 0 {0} else {1}
}
<|endoftext|>


<commit_before>extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate exonum;
extern crate router;
extern crate bodyparser;
extern crate iron;

use exonum::blockchain::{Blockchain, Service, ServiceContext, Schema, GenesisConfig,
                         ValidatorKeys, Transaction, ApiContext};
use exonum::node::{Node, NodeConfig, NodeApiConfig, ApiSender};
use exonum::messages::{RawTransaction, FromRaw, Message};
use exonum::encoding::serialize::json::reexport::Value;
use exonum::storage::{Fork, Snapshot, MemoryDB, MapIndex, Entry};
use exonum::crypto::{PublicKey, Hash};
use exonum::encoding;
use exonum::api::Api;
use iron::prelude::*;
use iron::Handler;
use router::Router;
use std::time::SystemTime;

const SERVICE_ID: u16 = 1;
const TX_TIME_ID: u16 = 1;

// SCHEMA

encoding_struct! {
    struct Time {
        const SIZE = 12;

        field time:     SystemTime  [00 => 12]
    }
}

pub struct TimeSchema<T> {
    view: T,
}

impl<T: AsRef<Snapshot>> TimeSchema<T> {
    pub fn new(view: T) -> Self {
        TimeSchema { view }
    }

    pub fn validators_time(&self) -> MapIndex<&Snapshot, PublicKey, Time> {
        MapIndex::new("time.validators_time", self.view.as_ref())
    }

    pub fn time(&self) -> Entry<&Snapshot, Time> {
        Entry::new("time.time", self.view.as_ref())
    }
}

impl<'a> TimeSchema<&'a mut Fork> {
    pub fn validators_time_mut(&mut self) -> MapIndex<&mut Fork, PublicKey, Time> {
        MapIndex::new("time.validators_time", self.view)
    }

    pub fn time_mut(&mut self) -> Entry<&mut Fork, Time> {
        Entry::new("time.time", self.view)
    }
}

// TRANSACTION

message! {
    struct TxTime {
        const TYPE = SERVICE_ID;
        const ID = TX_TIME_ID;
        const SIZE = 44;

        field time:     SystemTime  [00 => 12]
        field pub_key:  &PublicKey  [12 => 44]
    }
}

impl Transaction for TxTime {
    fn verify(&self) -> bool {
        self.verify_signature(self.pub_key())
    }

    fn execute(&self, view: &mut Fork) {
        let validator_keys = Schema::new(&view).actual_configuration().validator_keys;

        if validator_keys.iter().any(|&validator| {
            validator.service_key == *self.pub_key()
        })
        {
            let mut schema = TimeSchema::new(view);
            if let Some(storage_time) = schema.validators_time().get(self.pub_key()) {
                if storage_time.time() < self.time() {
                    schema.validators_time_mut().put(
                        self.pub_key(),
                        Time::new(self.time()),
                    );

                    let mut validators_time = Vec::new();
                    {
                        let idx = schema.validators_time();

                        for pair in idx.iter() {
                            let (pub_key, time) = (pair.0, pair.1.time());
                            if validator_keys.iter().any(|validator| {
                                validator.service_key == pub_key
                            })
                            {
                                validators_time.push(time);
                            }
                        }
                    }

                    let f = validator_keys.len() / 3 + 1;
                    if validators_time.len() > f {
                        validators_time.sort();
                        validators_time.reverse();
                        if let Some(current_time) = schema.time().get() {
                            if current_time.time() < validators_time[f] {
                                schema.time_mut().set(Time::new(validators_time[f]));
                            }
                        }
                    }
                }
            }
        }
    }
}

// API

#[derive(Serialize, Deserialize)]
pub struct TxResponse {
    pub tx_hash: Hash,
}

#[derive(Clone)]
struct TimeApi {
    channel: ApiSender,
    blockchain: Blockchain,
}

impl TimeApi {
    fn get_current_time(&self, _: &mut Request) -> IronResult<Response> {
        let view = self.blockchain.snapshot();
        let schema = TimeSchema::new(&view);
        let current_time = schema.time().get();
        self.ok_response(&serde_json::to_value(current_time).unwrap())
    }

    fn get_validators_time(&self, _: &mut Request) -> IronResult<Response> {
        let view = self.blockchain.snapshot();
        let schema = TimeSchema::new(&view);
        let idx = schema.validators_time();
        let validators_time: Vec<Time> = idx.values().collect();
        if validators_time.is_empty() {
            self.not_found_response(&serde_json::to_value("Validators time database if empty")
                .unwrap())
        } else {
            self.ok_response(&serde_json::to_value(validators_time).unwrap())
        }
    }

    fn wire_private(&self, router: &mut Router) {
        let self_ = self.clone();
        let get_validators_time = move |req: &mut Request| self_.get_validators_time(req);
        router.get(
            "/validators_time",
            get_validators_time,
            "get_validators_time",
        );
    }
}

impl Api for TimeApi {
    fn wire(&self, router: &mut Router) {
        let self_ = self.clone();
        let get_current_time = move |req: &mut Request| self_.get_current_time(req);
        router.get("/current_time", get_current_time, "get_current_time");
    }
}

// SERVICE DECLARATION

struct TimeService;

impl Service for TimeService {
    fn service_name(&self) -> &'static str {
        "time"
    }

    fn service_id(&self) -> u16 {
        SERVICE_ID
    }

    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, encoding::Error> {
        match raw.message_type() {
            TX_TIME_ID => Ok(Box::new(TxTime::from_raw(raw)?)),
            _ => {
                let error =
                    encoding::Error::IncorrectMessageType { message_type: raw.message_type() };
                Err(error)
            }
        }
    }

    fn initialize(&self, _fork: &mut Fork) -> Value {
        Value::Null
    }

    fn handle_commit(&self, context: &mut ServiceContext) {
        let (pub_key, sec_key) = (*context.public_key(), context.secret_key().clone());
        context.add_transaction(Box::new(TxTime::new(SystemTime::now(), &pub_key, &sec_key)));
    }

    fn private_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = TimeApi {
            channel: ctx.node_channel().clone(),
            blockchain: ctx.blockchain().clone(),
        };
        api.wire(&mut router);
        Some(Box::new(router))
    }

    fn public_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = TimeApi {
            channel: ctx.node_channel().clone(),
            blockchain: ctx.blockchain().clone(),
        };
        api.wire_private(&mut router);
        Some(Box::new(router))
    }
}

fn main() {
    exonum::helpers::init_logger().unwrap();

    println!("Creating in-memory database...");
    let db = MemoryDB::new();
    let services: Vec<Box<Service>> = vec![Box::new(TimeService)];
    let blockchain = Blockchain::new(Box::new(db), services);

    let (consensus_public_key, consensus_secret_key) = exonum::crypto::gen_keypair();
    let (service_public_key, service_secret_key) = exonum::crypto::gen_keypair();

    let validator_keys = ValidatorKeys {
        consensus_key: consensus_public_key,
        service_key: service_public_key,
    };
    let genesis = GenesisConfig::new(vec![validator_keys].into_iter());

    let api_address = "0.0.0.0:8000".parse().unwrap();
    let api_cfg = NodeApiConfig {
        public_api_address: Some(api_address),
        ..Default::default()
    };

    let peer_address = "0.0.0.0:2000".parse().unwrap();

    let node_cfg = NodeConfig {
        listen_address: peer_address,
        peers: vec![],
        service_public_key,
        service_secret_key,
        consensus_public_key,
        consensus_secret_key,
        genesis,
        external_address: None,
        network: Default::default(),
        whitelist: Default::default(),
        api: api_cfg,
        mempool: Default::default(),
        services_configs: Default::default(),
    };

    println!("Starting a single node...");
    let node = Node::new(blockchain, node_cfg);

    println!("Blockchain is ready for transactions!");
    node.run().unwrap();
}
<commit_msg>v.0.1<commit_after>extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate exonum;
extern crate router;
extern crate bodyparser;
extern crate iron;

use exonum::blockchain::{Blockchain, Service, ServiceContext, Schema, GenesisConfig,
                         ValidatorKeys, Transaction, ApiContext};
use exonum::node::{Node, NodeConfig, NodeApiConfig, ApiSender};
use exonum::messages::{RawTransaction, FromRaw, Message};
use exonum::encoding::serialize::json::reexport::Value;
use exonum::storage::{Fork, Snapshot, MemoryDB, MapIndex, Entry};
use exonum::crypto::{PublicKey, Hash};
use exonum::encoding;
use exonum::api::Api;
use iron::prelude::*;
use iron::Handler;
use router::Router;
use std::time::SystemTime;

const SERVICE_ID: u16 = 1;
const TX_TIME_ID: u16 = 1;

// SCHEMA

encoding_struct! {
    struct Time {
        const SIZE = 12;

        field time:     SystemTime  [00 => 12]
    }
}

pub struct TimeSchema<T> {
    view: T,
}

impl<T: AsRef<Snapshot>> TimeSchema<T> {
    pub fn new(view: T) -> Self {
        TimeSchema { view }
    }

    pub fn validators_time(&self) -> MapIndex<&Snapshot, PublicKey, Time> {
        MapIndex::new("time.validators_time", self.view.as_ref())
    }

    pub fn time(&self) -> Entry<&Snapshot, Time> {
        Entry::new("time.time", self.view.as_ref())
    }
}

impl<'a> TimeSchema<&'a mut Fork> {
    pub fn validators_time_mut(&mut self) -> MapIndex<&mut Fork, PublicKey, Time> {
        MapIndex::new("time.validators_time", self.view)
    }

    pub fn time_mut(&mut self) -> Entry<&mut Fork, Time> {
        Entry::new("time.time", self.view)
    }
}

// TRANSACTION

message! {
    struct TxTime {
        const TYPE = SERVICE_ID;
        const ID = TX_TIME_ID;
        const SIZE = 44;

        field time:     SystemTime  [00 => 12]
        field pub_key:  &PublicKey  [12 => 44]
    }
}

impl Transaction for TxTime {
    fn verify(&self) -> bool {
        self.verify_signature(self.pub_key())
    }

    fn execute(&self, view: &mut Fork) {
        let validator_keys = Schema::new(&view).actual_configuration().validator_keys;

        if validator_keys.iter().any(|&validator| {
            validator.service_key == *self.pub_key()
        })
        {
            let mut schema = TimeSchema::new(view);
            if let Some(storage_time) = schema.validators_time().get(self.pub_key()) {
                if storage_time.time() < self.time() {
                    schema.validators_time_mut().put(
                        self.pub_key(),
                        Time::new(self.time()),
                    );

                    let mut validators_time = Vec::new();
                    {
                        let idx = schema.validators_time();

                        for pair in idx.iter() {
                            let (pub_key, time) = (pair.0, pair.1.time());
                            if validator_keys.iter().any(|validator| {
                                validator.service_key == pub_key
                            })
                            {
                                validators_time.push(time);
                            }
                        }
                    }

                    let f = validator_keys.len() / 3;
                    if validators_time.len() > f {
                        validators_time.sort();
                        validators_time.reverse();
                        if let Some(current_time) = schema.time().get() {
                            if current_time.time() < validators_time[f] {
                                schema.time_mut().set(Time::new(validators_time[f]));
                            }
                        }
                    }
                }
            }
        }
    }
}

// API

#[derive(Serialize, Deserialize)]
pub struct TxResponse {
    pub tx_hash: Hash,
}

#[derive(Clone)]
struct TimeApi {
    channel: ApiSender,
    blockchain: Blockchain,
}

impl TimeApi {
    fn get_current_time(&self, _: &mut Request) -> IronResult<Response> {
        let view = self.blockchain.snapshot();
        let schema = TimeSchema::new(&view);
        let current_time = schema.time().get();
        self.ok_response(&serde_json::to_value(current_time).unwrap())
    }

    fn get_validators_time(&self, _: &mut Request) -> IronResult<Response> {
        let view = self.blockchain.snapshot();
        let schema = TimeSchema::new(&view);
        let idx = schema.validators_time();
        let validators_time: Vec<Time> = idx.values().collect();
        if validators_time.is_empty() {
            self.not_found_response(&serde_json::to_value("Validators time database if empty")
                .unwrap())
        } else {
            self.ok_response(&serde_json::to_value(validators_time).unwrap())
        }
    }

    fn wire_private(&self, router: &mut Router) {
        let self_ = self.clone();
        let get_validators_time = move |req: &mut Request| self_.get_validators_time(req);
        router.get(
            "/validators_time",
            get_validators_time,
            "get_validators_time",
        );
    }
}

impl Api for TimeApi {
    fn wire(&self, router: &mut Router) {
        let self_ = self.clone();
        let get_current_time = move |req: &mut Request| self_.get_current_time(req);
        router.get("/current_time", get_current_time, "get_current_time");
    }
}

// SERVICE DECLARATION

struct TimeService;

impl Service for TimeService {
    fn service_name(&self) -> &'static str {
        "time"
    }

    fn service_id(&self) -> u16 {
        SERVICE_ID
    }

    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, encoding::Error> {
        match raw.message_type() {
            TX_TIME_ID => Ok(Box::new(TxTime::from_raw(raw)?)),
            _ => {
                let error =
                    encoding::Error::IncorrectMessageType { message_type: raw.message_type() };
                Err(error)
            }
        }
    }

    fn initialize(&self, _fork: &mut Fork) -> Value {
        Value::Null
    }

    fn handle_commit(&self, context: &mut ServiceContext) {
        let (pub_key, sec_key) = (*context.public_key(), context.secret_key().clone());
        context.add_transaction(Box::new(TxTime::new(SystemTime::now(), &pub_key, &sec_key)));
    }

    fn private_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = TimeApi {
            channel: ctx.node_channel().clone(),
            blockchain: ctx.blockchain().clone(),
        };
        api.wire(&mut router);
        Some(Box::new(router))
    }

    fn public_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = TimeApi {
            channel: ctx.node_channel().clone(),
            blockchain: ctx.blockchain().clone(),
        };
        api.wire_private(&mut router);
        Some(Box::new(router))
    }
}

fn main() {
    exonum::helpers::init_logger().unwrap();

    println!("Creating in-memory database...");
    let db = MemoryDB::new();
    let services: Vec<Box<Service>> = vec![Box::new(TimeService)];
    let blockchain = Blockchain::new(Box::new(db), services);

    let (consensus_public_key, consensus_secret_key) = exonum::crypto::gen_keypair();
    let (service_public_key, service_secret_key) = exonum::crypto::gen_keypair();

    let validator_keys = ValidatorKeys {
        consensus_key: consensus_public_key,
        service_key: service_public_key,
    };
    let genesis = GenesisConfig::new(vec![validator_keys].into_iter());

    let api_address = "0.0.0.0:8000".parse().unwrap();
    let api_cfg = NodeApiConfig {
        public_api_address: Some(api_address),
        ..Default::default()
    };

    let peer_address = "0.0.0.0:2000".parse().unwrap();

    let node_cfg = NodeConfig {
        listen_address: peer_address,
        peers: vec![],
        service_public_key,
        service_secret_key,
        consensus_public_key,
        consensus_secret_key,
        genesis,
        external_address: None,
        network: Default::default(),
        whitelist: Default::default(),
        api: api_cfg,
        mempool: Default::default(),
        services_configs: Default::default(),
    };

    println!("Starting a single node...");
    let node = Node::new(blockchain, node_cfg);

    println!("Blockchain is ready for transactions!");
    node.run().unwrap();
}
<|endoftext|>


<commit_before>//#![deny(warnings)]

extern crate git2;
extern crate docopt;
extern crate rustc_serialize;

use std::str;
use docopt::Docopt;
use git2::{Repository, Error, StatusOptions, ErrorCode, SubmoduleIgnore};

#[derive(RustcDecodable)]
struct Args {
    arg_spec: Vec<String>,
    flag_branch: bool,
    flag_git_dir: Option<String>,
}

fn run(args: &Args) -> Result<(), Error> {
    let path = args.flag_git_dir.clone().unwrap_or(".".to_string());
    let repo = try!(Repository::open(&path));

    if repo.is_bare() {
        return Err(Error::from_str("Bare repositories are not allowed"))
    }

    let mut opts = StatusOptions::new();

    for spec in args.arg_spec.iter() {
        opts.pathspec(spec);
    }

    loop {
        let statuses = try!(repo.statuses(Some(&mut opts)));

        if args.flag_branch {
            try!(show_branch(&repo));
        }

        print_short(&repo, statuses);

        return Ok(())
    }
}

fn show_branch(repo: &Repository) -> Result<(), Error> {
    let head = match repo.head() {
        Ok(head) => Some(head),
        Err(ref e) if e.code() == ErrorCode::UnbornBranch ||
                      e.code() == ErrorCode::NotFound => None,
        Err(e) => return Err(e),
    };
    let head = head.as_ref().and_then(|h| h.shorthand());

    println!("## {}", head.unwrap_or("HEAD (no branch)"));

    Ok(())
}

// This version of the output prefixes each path with two status columns and
// shows submodule status information.
fn print_short(repo: &Repository, statuses: git2::Statuses) {
    for entry in statuses.iter().filter(|e| e.status() != git2::STATUS_CURRENT) {
        let mut istatus = match entry.status() {
            s if s.contains(git2::STATUS_INDEX_NEW) => 'A',
            s if s.contains(git2::STATUS_INDEX_MODIFIED) => 'M',
            s if s.contains(git2::STATUS_INDEX_DELETED) => 'D',
            s if s.contains(git2::STATUS_INDEX_RENAMED) => 'R',
            s if s.contains(git2::STATUS_INDEX_TYPECHANGE) => 'T',
            _ => ' ',
        };
        let mut wstatus = match entry.status() {
            s if s.contains(git2::STATUS_WT_NEW) => {
                if istatus == ' ' { istatus = '?'; } '?'
            }
            s if s.contains(git2::STATUS_WT_MODIFIED) => 'M',
            s if s.contains(git2::STATUS_WT_DELETED) => 'D',
            s if s.contains(git2::STATUS_WT_RENAMED) => 'R',
            s if s.contains(git2::STATUS_WT_TYPECHANGE) => 'T',
            _ => ' ',
        };

        if entry.status().contains(git2::STATUS_IGNORED) {
            istatus = '!';
            wstatus = '!';
        }
        if istatus == '?' && wstatus == '?' { continue }
        let mut extra = "";

        // TODO: check for GIT_FILEMODE_COMMIT
        let status = entry.index_to_workdir().and_then(|diff| {
            let ignore = SubmoduleIgnore::Unspecified;
            diff.new_file().path_bytes()
                .and_then(|s| str::from_utf8(s).ok())
                .and_then(|name| repo.submodule_status(name, ignore).ok())
        });
        if let Some(status) = status {
            if status.contains(git2::SUBMODULE_STATUS_WD_MODIFIED) {
                extra = " (new commits)";
            } else if status.contains(git2::SUBMODULE_STATUS_WD_INDEX_MODIFIED) {
                extra = " (modified content)";
            } else if status.contains(git2::SUBMODULE_STATUS_WD_WD_MODIFIED) {
                extra = " (modified content)";
            } else if status.contains(git2::SUBMODULE_STATUS_WD_UNTRACKED) {
                extra = " (untracked content)";
            }
        }

        let (mut a, mut b, mut c) = (None, None, None);
        if let Some(diff) = entry.head_to_index() {
            a = diff.old_file().path();
            b = diff.new_file().path();
        }
        if let Some(diff) = entry.index_to_workdir() {
            a = a.or(diff.old_file().path());
            b = b.or(diff.old_file().path());
            c = diff.new_file().path();
        }

        match (istatus, wstatus) {
            ('R', 'R') => println!("RR {} {} {}{}", a.unwrap().display(),
                                   b.unwrap().display(), c.unwrap().display(),
                                   extra),
            ('R', w) => println!("R{} {} {}{}", w, a.unwrap().display(),
                                 b.unwrap().display(), extra),
            (i, 'R') => println!("{}R {} {}{}", i, a.unwrap().display(),
                                 c.unwrap().display(), extra),
            (i, w) => println!("{}{} {}{}", i, w, a.unwrap().display(), extra),
        }
    }

    for entry in statuses.iter().filter(|e| e.status() == git2::STATUS_WT_NEW) {
        println!("?? {}", entry.index_to_workdir().unwrap().old_file()
                               .path().unwrap().display());
    }
}

// impl Args {
// }

fn main() {
    const USAGE: &'static str = "
usage: absorb [options] [--] [<spec>..]

Options:
    -b, --branch                show branch information
    -h, --help                  show this message
";

    let args = Docopt::new(USAGE).and_then(|d| d.decode())
                                 .unwrap_or_else(|e| e.exit());
    match run(&args) {
        Ok(()) => {}
        Err(e) => println!("error: {}", e),
    }
}
<commit_msg>Change branch() to return branchname<commit_after>//#![deny(warnings)]

extern crate git2;
extern crate docopt;
extern crate rustc_serialize;

use std::str;
use docopt::Docopt;
use git2::{Repository, Error, StatusOptions, ErrorCode, SubmoduleIgnore};

#[derive(RustcDecodable)]
struct Args {
    arg_spec: Vec<String>,
    flag_git_dir: Option<String>,
}

fn run(args: &Args) -> Result<(), Error> {
    let path = args.flag_git_dir.clone().unwrap_or(".".to_string());
    let repo = try!(Repository::open(&path));

    if repo.is_bare() {
        return Err(Error::from_str("Bare repositories are not allowed"))
    }

    let mut opts = StatusOptions::new();

    for spec in args.arg_spec.iter() {
        opts.pathspec(spec);
    }

    loop {
        let statuses = try!(repo.statuses(Some(&mut opts)));
        let current_branch = try!(branch(&repo));

        println!("Current branch {:?}", current_branch);

        print_short(&repo, statuses);

        return Ok(())
    }
}

fn branch<'a>(repo: &'a Repository) -> Result<String, Error> {
    let head = match repo.head() {
        Ok(head) => Some(head),
            Err(ref e) if e.code() == ErrorCode::UnbornBranch ||
                          e.code() == ErrorCode::NotFound
                       => None,
            Err(e) => return Err(e),
    };
    let branchref = head.as_ref().and_then(|h| h.shorthand());
    let branchname = branchref.unwrap_or("HEAD (no branch)").to_string();

    Ok(branchname)
}

// This version of the output prefixes each path with two status columns and
// shows submodule status information.
fn print_short(repo: &Repository, statuses: git2::Statuses) {
    for entry in statuses.iter().filter(|e| e.status() != git2::STATUS_CURRENT) {
        let mut istatus = match entry.status() {
            s if s.contains(git2::STATUS_INDEX_NEW) => 'A',
            s if s.contains(git2::STATUS_INDEX_MODIFIED) => 'M',
            s if s.contains(git2::STATUS_INDEX_DELETED) => 'D',
            s if s.contains(git2::STATUS_INDEX_RENAMED) => 'R',
            s if s.contains(git2::STATUS_INDEX_TYPECHANGE) => 'T',
            _ => ' ',
        };
        let mut wstatus = match entry.status() {
            s if s.contains(git2::STATUS_WT_NEW) => {
                if istatus == ' ' { istatus = '?'; } '?'
            }
            s if s.contains(git2::STATUS_WT_MODIFIED) => 'M',
            s if s.contains(git2::STATUS_WT_DELETED) => 'D',
            s if s.contains(git2::STATUS_WT_RENAMED) => 'R',
            s if s.contains(git2::STATUS_WT_TYPECHANGE) => 'T',
            _ => ' ',
        };

        if entry.status().contains(git2::STATUS_IGNORED) {
            istatus = '!';
            wstatus = '!';
        }
        if istatus == '?' && wstatus == '?' { continue }
        let mut extra = "";

        // TODO: check for GIT_FILEMODE_COMMIT
        let status = entry.index_to_workdir().and_then(|diff| {
            let ignore = SubmoduleIgnore::Unspecified;
            diff.new_file().path_bytes()
                .and_then(|s| str::from_utf8(s).ok())
                .and_then(|name| repo.submodule_status(name, ignore).ok())
        });
        if let Some(status) = status {
            if status.contains(git2::SUBMODULE_STATUS_WD_MODIFIED) {
                extra = " (new commits)";
            } else if status.contains(git2::SUBMODULE_STATUS_WD_INDEX_MODIFIED) {
                extra = " (modified content)";
            } else if status.contains(git2::SUBMODULE_STATUS_WD_WD_MODIFIED) {
                extra = " (modified content)";
            } else if status.contains(git2::SUBMODULE_STATUS_WD_UNTRACKED) {
                extra = " (untracked content)";
            }
        }

        let (mut a, mut b, mut c) = (None, None, None);
        if let Some(diff) = entry.head_to_index() {
            a = diff.old_file().path();
            b = diff.new_file().path();
        }
        if let Some(diff) = entry.index_to_workdir() {
            a = a.or(diff.old_file().path());
            b = b.or(diff.old_file().path());
            c = diff.new_file().path();
        }

        match (istatus, wstatus) {
            ('R', 'R') => println!("RR {} {} {}{}", a.unwrap().display(),
                                   b.unwrap().display(), c.unwrap().display(),
                                   extra),
            ('R', w) => println!("R{} {} {}{}", w, a.unwrap().display(),
                                 b.unwrap().display(), extra),
            (i, 'R') => println!("{}R {} {}{}", i, a.unwrap().display(),
                                 c.unwrap().display(), extra),
            (i, w) => println!("{}{} {}{}", i, w, a.unwrap().display(), extra),
        }
    }

    for entry in statuses.iter().filter(|e| e.status() == git2::STATUS_WT_NEW) {
        println!("?? {}", entry.index_to_workdir().unwrap().old_file()
                               .path().unwrap().display());
    }
}

// impl Args {
// }

fn main() {
    const USAGE: &'static str = "
usage: absorb [options] [--] [<branchname>]

Options:
    -h, --help                  show this message
";

    let args = Docopt::new(USAGE).and_then(|d| d.decode())
                                 .unwrap_or_else(|e| e.exit());
    match run(&args) {
        Ok(()) => {}
        Err(e) => println!("error: {}", e),
    }
}
<|endoftext|>


<commit_before>extern crate image;

use std::env::args;
use std::process::exit;
use std::fs::File;
use std::path::Path;
use image::GenericImage;

fn get_top_left(in_path: &str) -> u32 {
	let im = image::open(&Path::new(in_path)).unwrap();
	for x in 0..(im.dimensions().1) {
		for y in 0..(im.dimensions().0) {
			let col = im.get_pixel(y, x);
			if col[0] != 255 && col[1] != 255 && col[2] != 255 {
				return y;
			}
		}
	}
	unreachable!();
}

fn get_top_right(in_path: &str) -> u32 {
	let im = image::open(&Path::new(in_path)).unwrap();
	for x in 0..(im.dimensions().0) {
		for y in 0..(im.dimensions().1) {
			let col = im.get_pixel(x, y);
			if col[0] != 255 && col[1] != 255 && col[2] != 255 {
				return y;
			}
		}
	}
	unreachable!();
}

fn get_lower_left(in_path: &str) -> u32 {
	let im = image::open(&Path::new(in_path)).unwrap();
	let mut x = im.dimensions().1 as i32 - 1;
	let mut y = im.dimensions().0 as i32 - 1;
	// Using while loop as there is no reliable way
	// to use custom steps in range() currently
	while x >= 0 {
		while y >= 0 {
			let col = im.get_pixel(y as u32, x as u32);
			if col[0] != 255 && col[1] != 255 && col[2] != 255 {
				return y as u32 + 1;
			}
			x -= 1;
			y -= 1;
		}
	}
	unreachable!();
}

fn get_lower_right(in_path: &str) -> u32 {
	let im = image::open(&Path::new(in_path)).unwrap();
	let mut x = im.dimensions().0 as i32 - 1;
	let mut y = im.dimensions().1 as i32 - 1;
	// Using while loop as there is no reliable way
	// to use custom steps in range() currently
	while x >= 0 {
		while y >= 0 {
			let col = im.get_pixel(x as u32, y as u32);
			if col[0] != 255 && col[1] != 255 && col[2] != 255 {
				return y as u32 + 1;
			}
			x -= 1;
			y -= 1;
		}
	}
	unreachable!();
}

fn main() {
	let args: Vec<_> = args().collect();

	let (in_path, out_path) = if args.len() == 3 {
		(args[1].as_str(), args[2].as_str())
	} else {
		println!("usage: {} <input_image> <output_image>", args[0]);
		exit(1)
	};

	// Top left corner
	let (a, b) = (get_top_left(in_path), get_top_right(in_path));
	// Lower right corner
	let (x, y) = (get_lower_left(in_path), get_lower_right(in_path));

	println!("Cropping area ({0}, {1}, {2}, {3}) from {4} to {5}",
	a, b, x, y, in_path, out_path);

	let mut im = image::open(&Path::new(in_path)).unwrap();
	let subim = im.crop(a, b, x - a, y - b);

	let ref mut fout = File::create(&Path::new(out_path)).unwrap();

	let _ = subim.save(fout, image::PNG).unwrap();
}
<commit_msg>Fix a looping bug<commit_after>extern crate image;

use std::env::args;
use std::process::exit;
use std::fs::File;
use std::path::Path;
use image::GenericImage;

fn get_top_left(in_path: &str) -> u32 {
	let im = image::open(&Path::new(in_path)).unwrap();
	for x in 0..(im.dimensions().1) {
		for y in 0..(im.dimensions().0) {
			let col = im.get_pixel(y, x);
			if col[0] != 255 && col[1] != 255 && col[2] != 255 {
				return x;
			}
		}
	}
	unreachable!();
}

fn get_top_right(in_path: &str) -> u32 {
	let im = image::open(&Path::new(in_path)).unwrap();
	for x in 0..(im.dimensions().0) {
		for y in 0..(im.dimensions().1) {
			let col = im.get_pixel(x, y);
			if col[0] != 255 && col[1] != 255 && col[2] != 255 {
				return x;
			}
		}
	}
	unreachable!();
}

fn get_lower_left(in_path: &str) -> u32 {
	let im = image::open(&Path::new(in_path)).unwrap();
	let mut x = im.dimensions().1 as i32 - 1;
	// Using while loop as there is no reliable way
	// to use custom steps in range() currently
	while x >= 0 {
		let mut y = im.dimensions().0 as i32 - 1;
		while y >= 0 {
			let col = im.get_pixel(y as u32, x as u32);
			if col[0] != 255 && col[1] != 255 && col[2] != 255 {
				return x as u32 + 1;
			}
			y -= 1;
		}
		x -= 1;
	}
	unreachable!();
}

fn get_lower_right(in_path: &str) -> u32 {
	let im = image::open(&Path::new(in_path)).unwrap();
	let mut x = im.dimensions().0 as i32 - 1;
	// Using while loop as there is no reliable way
	// to use custom steps in range() currently
	while x >= 0 {
		let mut y = im.dimensions().1 as i32 - 1;
		while y >= 0 {
			let col = im.get_pixel(x as u32, y as u32);
			if col[0] != 255 && col[1] != 255 && col[2] != 255 {
				return x as u32 + 1;
			}
			y -= 1;
		}
		x -= 1;
	}
	unreachable!();
}

fn main() {
	let args: Vec<_> = args().collect();

	let (in_path, out_path) = if args.len() == 3 {
		(args[1].as_str(), args[2].as_str())
	} else {
		println!("usage: {} <input_image> <output_image>", args[0]);
		exit(1)
	};

	// Top left corner
	let (a, b) = (get_top_left(in_path), get_top_right(in_path));
	// Lower right corner
	let (x, y) = (get_lower_left(in_path), get_lower_right(in_path));

	println!("Cropping area ({0}, {1}, {2}, {3}) from {4} to {5}",
	a, b, x, y, in_path, out_path);

	let mut im = image::open(&Path::new(in_path)).unwrap();
	let subim = im.crop(a, b, x - a, y - b);

	let ref mut fout = File::create(&Path::new(out_path)).unwrap();

	let _ = subim.save(fout, image::PNG).unwrap();
}
<|endoftext|>