<commit_before>// Copyright (c) 2017 fd developers
// 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. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
use std::collections::HashMap;

use clap::{App, AppSettings, Arg};

struct Help {
    short: &'static str,
    long: &'static str,
}

macro_rules! doc {
    ($map:expr, $name:expr, $short:expr) => {
        doc!($map, $name, $short, $short)
    };
    ($map:expr, $name:expr, $short:expr, $long:expr) => {
        $map.insert(
            $name,
            Help {
                short: $short,
                long: concat!($long, "\n "),
            },
        );
    };
}

pub fn build_app() -> App<'static, 'static> {
    let helps = usage();
    let arg = |name| {
        Arg::with_name(name)
            .help(helps[name].short)
            .long_help(helps[name].long)
    };

    App::new("fd")
        .version(crate_version!())
        .usage("fd [FLAGS/OPTIONS] [<pattern>] [<path>...]")
        .setting(AppSettings::ColoredHelp)
        .setting(AppSettings::DeriveDisplayOrder)
        .after_help("Note: `fd -h` prints a short and concise overview while `fd --help` \
                     gives all details.")
        .arg(arg("hidden").long("hidden").short("H"))
        .arg(arg("no-ignore").long("no-ignore").short("I"))
        .arg(arg("no-ignore-vcs").long("no-ignore-vcs"))
        .arg(
            arg("rg-alias-hidden-ignore")
                .short("u")
                .multiple(true)
                .hidden(true),
        )
        .arg(
            arg("case-sensitive")
                .long("case-sensitive")
                .short("s")
                .overrides_with("ignore-case"),
        )
        .arg(
            arg("ignore-case")
                .long("ignore-case")
                .short("i")
                .overrides_with("case-sensitive"),
        )
        .arg(
            arg("fixed-strings")
                .long("fixed-strings")
                .short("F")
                .alias("literal"),
        )
        .arg(arg("absolute-path").long("absolute-path").short("a"))
        .arg(arg("follow").long("follow").short("L").alias("dereference"))
        .arg(arg("full-path").long("full-path").short("p"))
        .arg(arg("null_separator").long("print0").short("0"))
        .arg(arg("depth").long("max-depth").short("d").takes_value(true))
        // support --maxdepth as well, for compatibility with rg
        .arg(
            arg("rg-depth")
                .long("maxdepth")
                .hidden(true)
                .takes_value(true),
        )
        .arg(
            arg("file-type")
                .long("type")
                .short("t")
                .multiple(true)
                .number_of_values(1)
                .takes_value(true)
                .value_name("filetype")
                .possible_values(&[
                    "f",
                    "file",
                    "d",
                    "directory",
                    "l",
                    "symlink",
                    "x",
                    "executable",
                    "e",
                    "empty",
                ])
                .hide_possible_values(true),
        )
        .arg(
            arg("extension")
                .long("extension")
                .short("e")
                .multiple(true)
                .number_of_values(1)
                .takes_value(true)
                .value_name("ext"),
        )
        .arg(
            arg("exec")
                .long("exec")
                .short("x")
                .min_values(1)
                .allow_hyphen_values(true)
                .value_terminator(";")
                .value_name("cmd"),
        )
        .arg(
            arg("exec-batch")
                .long("exec-batch")
                .short("X")
                .min_values(1)
                .allow_hyphen_values(true)
                .value_terminator(";")
                .value_name("cmd")
                .conflicts_with("exec"),
        )
        .arg(
            arg("exclude")
                .long("exclude")
                .short("E")
                .takes_value(true)
                .value_name("pattern")
                .number_of_values(1)
                .multiple(true),
        )
        .arg(
            arg("ignore-file")
                .long("ignore-file")
                .takes_value(true)
                .value_name("path")
                .number_of_values(1)
                .multiple(true)
                .hidden_short_help(true),
        )
        .arg(
            arg("color")
                .long("color")
                .short("c")
                .takes_value(true)
                .value_name("when")
                .possible_values(&["never", "auto", "always"])
                .hide_possible_values(true),
        )
        .arg(
            arg("threads")
                .long("threads")
                .short("j")
                .takes_value(true)
                .value_name("num")
                .hidden_short_help(true),
        )
        .arg(
            arg("size")
                .long("size")
                .short("S")
                .takes_value(true)
                .number_of_values(1)
                .allow_hyphen_values(true)
                .multiple(true),
        )
        .arg(
            arg("max-buffer-time")
                .long("max-buffer-time")
                .takes_value(true)
                .hidden(true),
        )
        .arg(
            arg("changed-within")
                .long("changed-within")
                .alias("change-newer-than")
                .takes_value(true)
                .value_name("date|dur")
                .number_of_values(1),
        )
        .arg(
            arg("changed-before")
                .long("changed-before")
                .alias("change-older-than")
                .takes_value(true)
                .value_name("date|dur")
                .number_of_values(1),
        )
        .arg(
            arg("show-errors")
                .long("show-errors")
                .hidden_short_help(true),
        )
        .arg(arg("pattern"))
        .arg(arg("path").multiple(true))
        .arg(
            arg("search-path")
                .long("search-path")
                .takes_value(true)
                .conflicts_with("path")
                .multiple(true)
                .hidden_short_help(true)
                .number_of_values(1),
        )
}

#[cfg_attr(rustfmt, rustfmt_skip)]
fn usage() -> HashMap<&'static str, Help> {
    let mut h = HashMap::new();
    doc!(h, "hidden"
        , "Search hidden files and directories"
        , "Include hidden directories and files in the search results (default: hidden files \
           and directories are skipped). Files and directories are considered to be hidden if \
           their name starts with a `.` sign (dot).");
    doc!(h, "no-ignore"
        , "Do not respect .(git|fd)ignore files"
        , "Show search results from files and directories that would otherwise be ignored by \
            '.gitignore', '.ignore' or '.fdignore' files.");
    doc!(h, "no-ignore-vcs"
        , "Do not respect .gitignore files"
        , "Show search results from files and directories that would otherwise be ignored by \
            '.gitignore' files.");
    doc!(h, "case-sensitive"
        , "Case-sensitive search (default: smart case)"
        , "Perform a case-sensitive search. By default, fd uses case-insensitive searches, \
           unless the pattern contains an uppercase character (smart case).");
    doc!(h, "ignore-case"
        , "Case-insensitive search (default: smart case)"
        , "Perform a case-insensitive search. By default, fd uses case-insensitive searches, \
           unless the pattern contains an uppercase character (smart case).");
    doc!(h, "fixed-strings"
        , "Treat the pattern as a literal string"
        , "Treat the pattern as a literal string instead of a regular expression.");
    doc!(h, "absolute-path"
        , "Show absolute instead of relative paths"
        , "Shows the full path starting from the root as opposed to relative paths.");
    doc!(h, "follow"
        , "Follow symbolic links"
        , "By default, fd does not descend into symlinked directories. Using this flag, symbolic \
           links are also traversed.");
    doc!(h, "full-path"
        , "Search full path (default: file-/dirname only)"
        , "By default, the search pattern is only matched against the filename (or directory \
           name). Using this flag, the pattern is matched against the full path.");
    doc!(h, "null_separator"
        , "Separate results by the null character"
        , "Separate search results by the null character (instead of newlines). Useful for \
           piping results to 'xargs'.");
    doc!(h, "depth"
        , "Set maximum search depth (default: none)"
        , "Limit the directory traversal to a given depth. By default, there is no limit \
           on the search depth.");
    doc!(h, "rg-depth"
        , "See --max-depth"
        , "See --max-depth");
    doc!(h, "file-type"
        , "Filter by type: file (f), directory (d), symlink (l),\nexecutable (x), empty (e)"
        , "Filter the search by type (multiple allowable filetypes can be specified):\n  \
             'f' or 'file':         regular files\n  \
             'd' or 'directory':    directories\n  \
             'l' or 'symlink':      symbolic links\n  \
             'x' or 'executable':   executables\n  \
             'e' or 'empty':        empty files or directories");
    doc!(h, "extension"
        , "Filter by file extension"
        , "(Additionally) filter search results by their file extension. Multiple allowable file \
           extensions can be specified.");
    doc!(h, "exec"
        , "Execute a command for each search result"
        , "Execute a command for each search result.\n\
           All arguments following --exec are taken to be arguments to the command until the \
           argument ';' is encountered.\n\
           Each occurrence of the following placeholders is substituted by a path derived from the \
           current search result before the command is executed:\n  \
             '{}':   path\n  \
             '{/}':  basename\n  \
             '{//}': parent directory\n  \
             '{.}':  path without file extension\n  \
             '{/.}': basename without file extension");
    doc!(h, "exec-batch"
        , "Execute a command with all search results at once"
        , "Execute a command with all search results at once.\n\
           All arguments following --exec-batch are taken to be arguments to the command until the \
           argument ';' is encountered.\n\
           A single occurence of the following placeholders is authorized and substituted by the paths derived from the \
           search results before the command is executed:\n  \
             '{}':   path\n  \
             '{/}':  basename\n  \
             '{//}': parent directory\n  \
             '{.}':  path without file extension\n  \
             '{/.}': basename without file extension");
    doc!(h, "exclude"
        , "Exclude entries that match the given glob pattern"
        , "Exclude files/directories that match the given glob pattern. This overrides any \
           other ignore logic. Multiple exclude patterns can be specified.");
    doc!(h, "ignore-file"
        , "Add a custom ignore-file in .gitignore format"
        , "Add a custom ignore-file in '.gitignore' format. These files have a low precedence.");
    doc!(h, "color"
        , "When to use colors: never, *auto*, always"
        , "Declare when to use color for the pattern match output:\n  \
             'auto':      show colors if the output goes to an interactive console (default)\n  \
             'never':     do not use colorized output\n  \
             'always':    always use colorized output");
    doc!(h, "threads"
        , "Set number of threads to use for searching & executing"
        , "Set number of threads to use for searching & executing (default: number of available \
           CPU cores)");
    doc!(h, "max-buffer-time"
        , "the time (in ms) to buffer, before streaming to the console"
        , "Amount of time in milliseconds to buffer, before streaming the search results to \
           the console.");
    doc!(h, "pattern"
        , "the search pattern, a regular expression (optional)");
    doc!(h, "path"
        , "the root directory for the filesystem search (optional)"
        , "The directory where the filesystem search is rooted (optional). \
           If omitted, search the current working directory.");
    doc!(h, "rg-alias-hidden-ignore"
        , "Alias for no-ignore and/or hidden"
        , "Alias for no-ignore ('u') and no-ignore and hidden ('uu')");
    doc!(h, "size"
        , "Limit results based on the size of files."
        , "Limit results based on the size of files using the format <+-><NUM><UNIT>.\n   \
            '+': file size must be greater than or equal to this\n   \
            '-': file size must be less than or equal to this\n   \
            'NUM':  The numeric size (e.g. 500)\n   \
            'UNIT': The units for NUM. They are not case-sensitive.\n\
            Allowed unit values:\n   \
                'b':  bytes\n   \
                'k':  kilobytes\n   \
                'm':  megabytes\n   \
                'g':  gigabytes\n   \
                't':  terabytes\n   \
                'ki': kibibytes\n   \
                'mi': mebibytes\n   \
                'gi': gibibytes\n   \
                'ti': tebibytes");
    doc!(h, "changed-within"
        , "Filter by file modification time (newer than)"
        , "Filter results based on the file modification time. The argument can be provided \
           as a specific point in time (YYYY-MM-DD HH:MM:SS) or as a duration (10h, 1d, 35min). \
           '--change-newer-than' can be used as an alias.\n\
           Examples:\n    \
               --changed-within 2weeks\n    \
               --change-newer-than '2018-10-27 10:00:00'");
    doc!(h, "changed-before"
        , "Filter by file modification time (older than)"
        , "Filter results based on the file modification time. The argument can be provided \
           as a specific point in time (YYYY-MM-DD HH:MM:SS) or as a duration (10h, 1d, 35min). \
           '--change-older-than' can be used as an alias.\n\
           Examples:\n    \
               --changed-before '2018-10-27 10:00:00'\n    \
               --change-older-than 2weeks");
    doc!(h, "show-errors"
        , "Enable display of filesystem errors"
        , "Enable the display of filesystem errors for situations such as insufficient permissions \
            or dead symlinks.");
    doc!(h, "search-path"
        , "(hidden)"
        , "Provide paths to search as an alternative to the positional <path> argument. \
           Changes the usage to `fd [FLAGS/OPTIONS] --search-path <path> --search-path <path2> [<pattern>]`");
    h
}
<commit_msg>Formatting<commit_after>// Copyright (c) 2017 fd developers
// 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. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
use std::collections::HashMap;

use clap::{App, AppSettings, Arg};

struct Help {
    short: &'static str,
    long: &'static str,
}

macro_rules! doc {
    ($map:expr, $name:expr, $short:expr) => {
        doc!($map, $name, $short, $short)
    };
    ($map:expr, $name:expr, $short:expr, $long:expr) => {
        $map.insert(
            $name,
            Help {
                short: $short,
                long: concat!($long, "\n "),
            },
        );
    };
}

pub fn build_app() -> App<'static, 'static> {
    let helps = usage();
    let arg = |name| {
        Arg::with_name(name)
            .help(helps[name].short)
            .long_help(helps[name].long)
    };

    App::new("fd")
        .version(crate_version!())
        .usage("fd [FLAGS/OPTIONS] [<pattern>] [<path>...]")
        .setting(AppSettings::ColoredHelp)
        .setting(AppSettings::DeriveDisplayOrder)
        .after_help(
            "Note: `fd -h` prints a short and concise overview while `fd --help` \
             gives all details.",
        )
        .arg(arg("hidden").long("hidden").short("H"))
        .arg(arg("no-ignore").long("no-ignore").short("I"))
        .arg(arg("no-ignore-vcs").long("no-ignore-vcs"))
        .arg(
            arg("rg-alias-hidden-ignore")
                .short("u")
                .multiple(true)
                .hidden(true),
        )
        .arg(
            arg("case-sensitive")
                .long("case-sensitive")
                .short("s")
                .overrides_with("ignore-case"),
        )
        .arg(
            arg("ignore-case")
                .long("ignore-case")
                .short("i")
                .overrides_with("case-sensitive"),
        )
        .arg(
            arg("fixed-strings")
                .long("fixed-strings")
                .short("F")
                .alias("literal"),
        )
        .arg(arg("absolute-path").long("absolute-path").short("a"))
        .arg(arg("follow").long("follow").short("L").alias("dereference"))
        .arg(arg("full-path").long("full-path").short("p"))
        .arg(arg("null_separator").long("print0").short("0"))
        .arg(arg("depth").long("max-depth").short("d").takes_value(true))
        // support --maxdepth as well, for compatibility with rg
        .arg(
            arg("rg-depth")
                .long("maxdepth")
                .hidden(true)
                .takes_value(true),
        )
        .arg(
            arg("file-type")
                .long("type")
                .short("t")
                .multiple(true)
                .number_of_values(1)
                .takes_value(true)
                .value_name("filetype")
                .possible_values(&[
                    "f",
                    "file",
                    "d",
                    "directory",
                    "l",
                    "symlink",
                    "x",
                    "executable",
                    "e",
                    "empty",
                ])
                .hide_possible_values(true),
        )
        .arg(
            arg("extension")
                .long("extension")
                .short("e")
                .multiple(true)
                .number_of_values(1)
                .takes_value(true)
                .value_name("ext"),
        )
        .arg(
            arg("exec")
                .long("exec")
                .short("x")
                .min_values(1)
                .allow_hyphen_values(true)
                .value_terminator(";")
                .value_name("cmd"),
        )
        .arg(
            arg("exec-batch")
                .long("exec-batch")
                .short("X")
                .min_values(1)
                .allow_hyphen_values(true)
                .value_terminator(";")
                .value_name("cmd")
                .conflicts_with("exec"),
        )
        .arg(
            arg("exclude")
                .long("exclude")
                .short("E")
                .takes_value(true)
                .value_name("pattern")
                .number_of_values(1)
                .multiple(true),
        )
        .arg(
            arg("ignore-file")
                .long("ignore-file")
                .takes_value(true)
                .value_name("path")
                .number_of_values(1)
                .multiple(true)
                .hidden_short_help(true),
        )
        .arg(
            arg("color")
                .long("color")
                .short("c")
                .takes_value(true)
                .value_name("when")
                .possible_values(&["never", "auto", "always"])
                .hide_possible_values(true),
        )
        .arg(
            arg("threads")
                .long("threads")
                .short("j")
                .takes_value(true)
                .value_name("num")
                .hidden_short_help(true),
        )
        .arg(
            arg("size")
                .long("size")
                .short("S")
                .takes_value(true)
                .number_of_values(1)
                .allow_hyphen_values(true)
                .multiple(true),
        )
        .arg(
            arg("max-buffer-time")
                .long("max-buffer-time")
                .takes_value(true)
                .hidden(true),
        )
        .arg(
            arg("changed-within")
                .long("changed-within")
                .alias("change-newer-than")
                .takes_value(true)
                .value_name("date|dur")
                .number_of_values(1),
        )
        .arg(
            arg("changed-before")
                .long("changed-before")
                .alias("change-older-than")
                .takes_value(true)
                .value_name("date|dur")
                .number_of_values(1),
        )
        .arg(
            arg("show-errors")
                .long("show-errors")
                .hidden_short_help(true),
        )
        .arg(arg("pattern"))
        .arg(arg("path").multiple(true))
        .arg(
            arg("search-path")
                .long("search-path")
                .takes_value(true)
                .conflicts_with("path")
                .multiple(true)
                .hidden_short_help(true)
                .number_of_values(1),
        )
}

#[cfg_attr(rustfmt, rustfmt_skip)]
fn usage() -> HashMap<&'static str, Help> {
    let mut h = HashMap::new();
    doc!(h, "hidden"
        , "Search hidden files and directories"
        , "Include hidden directories and files in the search results (default: hidden files \
           and directories are skipped). Files and directories are considered to be hidden if \
           their name starts with a `.` sign (dot).");
    doc!(h, "no-ignore"
        , "Do not respect .(git|fd)ignore files"
        , "Show search results from files and directories that would otherwise be ignored by \
            '.gitignore', '.ignore' or '.fdignore' files.");
    doc!(h, "no-ignore-vcs"
        , "Do not respect .gitignore files"
        , "Show search results from files and directories that would otherwise be ignored by \
            '.gitignore' files.");
    doc!(h, "case-sensitive"
        , "Case-sensitive search (default: smart case)"
        , "Perform a case-sensitive search. By default, fd uses case-insensitive searches, \
           unless the pattern contains an uppercase character (smart case).");
    doc!(h, "ignore-case"
        , "Case-insensitive search (default: smart case)"
        , "Perform a case-insensitive search. By default, fd uses case-insensitive searches, \
           unless the pattern contains an uppercase character (smart case).");
    doc!(h, "fixed-strings"
        , "Treat the pattern as a literal string"
        , "Treat the pattern as a literal string instead of a regular expression.");
    doc!(h, "absolute-path"
        , "Show absolute instead of relative paths"
        , "Shows the full path starting from the root as opposed to relative paths.");
    doc!(h, "follow"
        , "Follow symbolic links"
        , "By default, fd does not descend into symlinked directories. Using this flag, symbolic \
           links are also traversed.");
    doc!(h, "full-path"
        , "Search full path (default: file-/dirname only)"
        , "By default, the search pattern is only matched against the filename (or directory \
           name). Using this flag, the pattern is matched against the full path.");
    doc!(h, "null_separator"
        , "Separate results by the null character"
        , "Separate search results by the null character (instead of newlines). Useful for \
           piping results to 'xargs'.");
    doc!(h, "depth"
        , "Set maximum search depth (default: none)"
        , "Limit the directory traversal to a given depth. By default, there is no limit \
           on the search depth.");
    doc!(h, "rg-depth"
        , "See --max-depth"
        , "See --max-depth");
    doc!(h, "file-type"
        , "Filter by type: file (f), directory (d), symlink (l),\nexecutable (x), empty (e)"
        , "Filter the search by type (multiple allowable filetypes can be specified):\n  \
             'f' or 'file':         regular files\n  \
             'd' or 'directory':    directories\n  \
             'l' or 'symlink':      symbolic links\n  \
             'x' or 'executable':   executables\n  \
             'e' or 'empty':        empty files or directories");
    doc!(h, "extension"
        , "Filter by file extension"
        , "(Additionally) filter search results by their file extension. Multiple allowable file \
           extensions can be specified.");
    doc!(h, "exec"
        , "Execute a command for each search result"
        , "Execute a command for each search result.\n\
           All arguments following --exec are taken to be arguments to the command until the \
           argument ';' is encountered.\n\
           Each occurrence of the following placeholders is substituted by a path derived from the \
           current search result before the command is executed:\n  \
             '{}':   path\n  \
             '{/}':  basename\n  \
             '{//}': parent directory\n  \
             '{.}':  path without file extension\n  \
             '{/.}': basename without file extension");
    doc!(h, "exec-batch"
        , "Execute a command with all search results at once"
        , "Execute a command with all search results at once.\n\
           All arguments following --exec-batch are taken to be arguments to the command until the \
           argument ';' is encountered.\n\
           A single occurence of the following placeholders is authorized and substituted by the paths derived from the \
           search results before the command is executed:\n  \
             '{}':   path\n  \
             '{/}':  basename\n  \
             '{//}': parent directory\n  \
             '{.}':  path without file extension\n  \
             '{/.}': basename without file extension");
    doc!(h, "exclude"
        , "Exclude entries that match the given glob pattern"
        , "Exclude files/directories that match the given glob pattern. This overrides any \
           other ignore logic. Multiple exclude patterns can be specified.");
    doc!(h, "ignore-file"
        , "Add a custom ignore-file in .gitignore format"
        , "Add a custom ignore-file in '.gitignore' format. These files have a low precedence.");
    doc!(h, "color"
        , "When to use colors: never, *auto*, always"
        , "Declare when to use color for the pattern match output:\n  \
             'auto':      show colors if the output goes to an interactive console (default)\n  \
             'never':     do not use colorized output\n  \
             'always':    always use colorized output");
    doc!(h, "threads"
        , "Set number of threads to use for searching & executing"
        , "Set number of threads to use for searching & executing (default: number of available \
           CPU cores)");
    doc!(h, "max-buffer-time"
        , "the time (in ms) to buffer, before streaming to the console"
        , "Amount of time in milliseconds to buffer, before streaming the search results to \
           the console.");
    doc!(h, "pattern"
        , "the search pattern, a regular expression (optional)");
    doc!(h, "path"
        , "the root directory for the filesystem search (optional)"
        , "The directory where the filesystem search is rooted (optional). \
           If omitted, search the current working directory.");
    doc!(h, "rg-alias-hidden-ignore"
        , "Alias for no-ignore and/or hidden"
        , "Alias for no-ignore ('u') and no-ignore and hidden ('uu')");
    doc!(h, "size"
        , "Limit results based on the size of files."
        , "Limit results based on the size of files using the format <+-><NUM><UNIT>.\n   \
            '+': file size must be greater than or equal to this\n   \
            '-': file size must be less than or equal to this\n   \
            'NUM':  The numeric size (e.g. 500)\n   \
            'UNIT': The units for NUM. They are not case-sensitive.\n\
            Allowed unit values:\n   \
                'b':  bytes\n   \
                'k':  kilobytes\n   \
                'm':  megabytes\n   \
                'g':  gigabytes\n   \
                't':  terabytes\n   \
                'ki': kibibytes\n   \
                'mi': mebibytes\n   \
                'gi': gibibytes\n   \
                'ti': tebibytes");
    doc!(h, "changed-within"
        , "Filter by file modification time (newer than)"
        , "Filter results based on the file modification time. The argument can be provided \
           as a specific point in time (YYYY-MM-DD HH:MM:SS) or as a duration (10h, 1d, 35min). \
           '--change-newer-than' can be used as an alias.\n\
           Examples:\n    \
               --changed-within 2weeks\n    \
               --change-newer-than '2018-10-27 10:00:00'");
    doc!(h, "changed-before"
        , "Filter by file modification time (older than)"
        , "Filter results based on the file modification time. The argument can be provided \
           as a specific point in time (YYYY-MM-DD HH:MM:SS) or as a duration (10h, 1d, 35min). \
           '--change-older-than' can be used as an alias.\n\
           Examples:\n    \
               --changed-before '2018-10-27 10:00:00'\n    \
               --change-older-than 2weeks");
    doc!(h, "show-errors"
        , "Enable display of filesystem errors"
        , "Enable the display of filesystem errors for situations such as insufficient permissions \
            or dead symlinks.");
    doc!(h, "search-path"
        , "(hidden)"
        , "Provide paths to search as an alternative to the positional <path> argument. \
           Changes the usage to `fd [FLAGS/OPTIONS] --search-path <path> --search-path <path2> [<pattern>]`");
    h
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// 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. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::error;
use std::fmt;
use std::mem;
use std::ops::Deref;
use std::ptr;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use std::time::Duration;
use smallvec::SmallVec;

use device::Device;
use Error;
use OomError;
use Success;
use VulkanObject;
use VulkanPointers;
use check_errors;
use vk;

/// A fence is used to know when a command buffer submission has finished its execution.
///
/// When a command buffer accesses a ressource, you have to ensure that the CPU doesn't access
/// the same ressource simultaneously (except for concurrent reads). Therefore in order to know
/// when the CPU can access a ressource again, a fence has to be used.
#[derive(Debug)]
pub struct Fence<D = Arc<Device>> where D: Deref<Target = Device> {
    fence: vk::Fence,

    device: D,
    device_raw: vk::Device,

    // If true, we know that the `Fence` is signaled. If false, we don't know.
    // This variable exists so that we don't need to call `vkGetFenceStatus` or `vkWaitForFences`
    // multiple times.
    signaled: AtomicBool,
}

impl<D> Fence<D> where D: Deref<Target = Device> {
    /// Builds a new fence.
    #[inline]
    pub fn new(device: &D) -> Result<Arc<Fence<D>>, OomError>
        where D: Clone
    {
        Fence::new_impl(device, false)
    }

    /// Builds a new fence already in the "signaled" state.
    #[inline]
    pub fn signaled(device: &D) -> Result<Arc<Fence<D>>, OomError>
        where D: Clone
    {
        Fence::new_impl(device, true)
    }

    fn new_impl(device_ptr: &D, signaled: bool) -> Result<Arc<Fence<D>>, OomError>
        where D: Clone
    {
        let device: &Device = &*device_ptr;
        let vk = device.pointers();

        let fence = unsafe {
            let infos = vk::FenceCreateInfo {
                sType: vk::STRUCTURE_TYPE_FENCE_CREATE_INFO,
                pNext: ptr::null(),
                flags: if signaled { vk::FENCE_CREATE_SIGNALED_BIT } else { 0 },
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateFence(device.internal_object(), &infos,
                                             ptr::null(), &mut output)));
            output
        };

        Ok(Arc::new(Fence {
            fence: fence,
            device: device_ptr.clone(),
            device_raw: device.internal_object(),
            signaled: AtomicBool::new(signaled),
        }))
    }

    /// Returns true if the fence is signaled.
    #[inline]
    pub fn ready(&self) -> Result<bool, OomError> {
        unsafe {
            let device: &Device = &*self.device;
            assert_eq!(device.internal_object(), self.device_raw);

            if self.signaled.load(Ordering::Relaxed) { return Ok(true); }

            let vk = device.pointers();
            let result = try!(check_errors(vk.GetFenceStatus(device.internal_object(),
                                                             self.fence)));
            match result {
                Success::Success => {
                    self.signaled.store(true, Ordering::Relaxed);
                    Ok(true)
                },
                Success::NotReady => Ok(false),
                _ => unreachable!()
            }
        }
    }

    /// Waits until the fence is signaled, or at least until the number of nanoseconds of the
    /// timeout has elapsed.
    ///
    /// Returns `Ok` if the fence is now signaled. Returns `Err` if the timeout was reached instead.
    pub fn wait(&self, timeout: Duration) -> Result<(), FenceWaitError> {
        unsafe {
            let device: &Device = &*self.device;
            assert_eq!(device.internal_object(), self.device_raw);

            if self.signaled.load(Ordering::Relaxed) { return Ok(()); }

            let timeout_ns = timeout.as_secs().saturating_mul(1_000_000_000)
                                              .saturating_add(timeout.subsec_nanos() as u64);

            let vk = device.pointers();
            let r = try!(check_errors(vk.WaitForFences(device.internal_object(), 1,
                                                       &self.fence, vk::TRUE, timeout_ns)));

            match r {
                Success::Success => {
                    self.signaled.store(true, Ordering::Relaxed);
                    Ok(())
                },
                Success::Timeout => {
                    Err(FenceWaitError::Timeout)
                },
                _ => unreachable!()
            }
        }
    }

    /// Waits for multiple fences at once.
    ///
    /// # Panic
    ///
    /// Panicks if not all fences belong to the same device.
    pub fn multi_wait<'a, I>(iter: I, timeout: Duration) -> Result<(), FenceWaitError>
        where I: IntoIterator<Item = &'a Fence>
    {
        let mut device: Option<&Device> = None;

        let fences: SmallVec<[vk::Fence; 8]> = iter.into_iter().filter_map(|fence| {
            match &mut device {
                dev @ &mut None => *dev = Some(&*fence.device),
                &mut Some(ref dev) if &**dev as *const Device == &*fence.device as *const Device => {},
                _ => panic!("Tried to wait for multiple fences that didn't belong to the same device"),
            };

            if fence.signaled.load(Ordering::Relaxed) {
                None
            } else {
                Some(fence.fence)
            }
        }).collect();

        let timeout_ns = timeout.as_secs().saturating_mul(1_000_000_000)
                                          .saturating_add(timeout.subsec_nanos() as u64);

        let r = if let Some(device) = device {
            unsafe {
                let vk = device.pointers();
                try!(check_errors(vk.WaitForFences(device.internal_object(), fences.len() as u32,
                                                   fences.as_ptr(), vk::TRUE, timeout_ns)))
            }
        } else {
            return Ok(());
        };

        match r {
            Success::Success => Ok(()),
            Success::Timeout => Err(FenceWaitError::Timeout),
            _ => unreachable!()
        }
    }

    /// Resets the fence.
    // FIXME: must synchronize the fence
    #[inline]
    pub fn reset(&self) {
        unsafe {
            let device: &Device = &*self.device;
            assert_eq!(device.internal_object(), self.device_raw);

            let vk = device.pointers();
            vk.ResetFences(device.internal_object(), 1, &self.fence);
            self.signaled.store(false, Ordering::Relaxed);
        }
    }

    /// Resets multiple fences at once.
    ///
    /// # Panic
    ///
    /// Panicks if not all fences belong to the same device.
    pub fn multi_reset<'a, I>(iter: I)
        where I: IntoIterator<Item = &'a Fence>
    {
        let mut device: Option<&Device> = None;

        let fences: SmallVec<[vk::Fence; 8]> = iter.into_iter().map(|fence| {
            match &mut device {
                dev @ &mut None => *dev = Some(&*fence.device),
                &mut Some(ref dev) if &**dev as *const Device == &*fence.device as *const Device => {},
                _ => panic!("Tried to reset multiple fences that didn't belong to the same device"),
            };

            fence.signaled.store(false, Ordering::Relaxed);
            fence.fence
        }).collect();

        if let Some(device) = device {
            unsafe {
                let vk = device.pointers();
                vk.ResetFences(device.internal_object(), fences.len() as u32, fences.as_ptr());
            }
        }
    }
}

unsafe impl<D> VulkanObject for Fence<D> where D: Deref<Target = Device> {
    type Object = vk::Fence;

    #[inline]
    fn internal_object(&self) -> vk::Fence {
        self.fence
    }
}

impl<D> Drop for Fence<D> where D: Deref<Target = Device> {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let device: &Device = &*self.device;
            assert_eq!(device.internal_object(), self.device_raw);

            let vk = device.pointers();
            vk.DestroyFence(device.internal_object(), self.fence, ptr::null());
        }
    }
}

/// Error that can be returned when waiting on a fence.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum FenceWaitError {
    /// Not enough memory to complete the wait.
    OomError(OomError),

    /// The specified timeout wasn't long enough.
    Timeout,

    /// The device has been lost.
    DeviceLostError,
}

impl error::Error for FenceWaitError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            FenceWaitError::OomError(_) => "no memory available",
            FenceWaitError::Timeout => "the timeout has been reached",
            FenceWaitError::DeviceLostError => "the device was lost",
        }
    }

    #[inline]
    fn cause(&self) -> Option<&error::Error> {
        match *self {
            FenceWaitError::OomError(ref err) => Some(err),
            _ => None
        }
    }
}

impl fmt::Display for FenceWaitError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

impl From<Error> for FenceWaitError {
    #[inline]
    fn from(err: Error) -> FenceWaitError {
        match err {
            Error::OutOfHostMemory => FenceWaitError::OomError(From::from(err)),
            Error::OutOfDeviceMemory => FenceWaitError::OomError(From::from(err)),
            Error::DeviceLost => FenceWaitError::DeviceLostError,
            _ => panic!("Unexpected error value: {}", err as i32)
        }
    }
}

#[cfg(test)]
mod tests {
    use std::time::Duration;
    use sync::Fence;

    #[test]
    #[ignore]       // TODO: fails on AMD + Windows
    fn fence_create() {
        let (device, _) = gfx_dev_and_queue!();

        let fence = Fence::new(&device).unwrap();
        assert!(!fence.ready().unwrap());
    }

    #[test]
    fn fence_create_signaled() {
        let (device, _) = gfx_dev_and_queue!();

        let fence = Fence::signaled(&device).unwrap();
        assert!(fence.ready().unwrap());
    }

    #[test]
    fn fence_signaled_wait() {
        let (device, _) = gfx_dev_and_queue!();

        let fence = Fence::signaled(&device).unwrap();
        fence.wait(Duration::new(0, 10)).unwrap();
    }

    #[test]
    #[ignore]       // TODO: fails on AMD + Windows
    fn fence_reset() {
        let (device, _) = gfx_dev_and_queue!();

        let fence = Fence::signaled(&device).unwrap();
        fence.reset();
        assert!(!fence.ready().unwrap());
    }
}
<commit_msg>Add some tests for fences<commit_after>// Copyright (c) 2016 The vulkano developers
// 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. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::error;
use std::fmt;
use std::mem;
use std::ops::Deref;
use std::ptr;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use std::time::Duration;
use smallvec::SmallVec;

use device::Device;
use Error;
use OomError;
use Success;
use VulkanObject;
use VulkanPointers;
use check_errors;
use vk;

/// A fence is used to know when a command buffer submission has finished its execution.
///
/// When a command buffer accesses a ressource, you have to ensure that the CPU doesn't access
/// the same ressource simultaneously (except for concurrent reads). Therefore in order to know
/// when the CPU can access a ressource again, a fence has to be used.
#[derive(Debug)]
pub struct Fence<D = Arc<Device>> where D: Deref<Target = Device> {
    fence: vk::Fence,

    device: D,
    device_raw: vk::Device,

    // If true, we know that the `Fence` is signaled. If false, we don't know.
    // This variable exists so that we don't need to call `vkGetFenceStatus` or `vkWaitForFences`
    // multiple times.
    signaled: AtomicBool,
}

impl<D> Fence<D> where D: Deref<Target = Device> {
    /// Builds a new fence.
    #[inline]
    pub fn new(device: &D) -> Result<Arc<Fence<D>>, OomError>
        where D: Clone
    {
        Fence::new_impl(device, false)
    }

    /// Builds a new fence already in the "signaled" state.
    #[inline]
    pub fn signaled(device: &D) -> Result<Arc<Fence<D>>, OomError>
        where D: Clone
    {
        Fence::new_impl(device, true)
    }

    fn new_impl(device_ptr: &D, signaled: bool) -> Result<Arc<Fence<D>>, OomError>
        where D: Clone
    {
        let device: &Device = &*device_ptr;
        let vk = device.pointers();

        let fence = unsafe {
            let infos = vk::FenceCreateInfo {
                sType: vk::STRUCTURE_TYPE_FENCE_CREATE_INFO,
                pNext: ptr::null(),
                flags: if signaled { vk::FENCE_CREATE_SIGNALED_BIT } else { 0 },
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateFence(device.internal_object(), &infos,
                                             ptr::null(), &mut output)));
            output
        };

        Ok(Arc::new(Fence {
            fence: fence,
            device: device_ptr.clone(),
            device_raw: device.internal_object(),
            signaled: AtomicBool::new(signaled),
        }))
    }

    /// Returns true if the fence is signaled.
    #[inline]
    pub fn ready(&self) -> Result<bool, OomError> {
        unsafe {
            let device: &Device = &*self.device;
            assert_eq!(device.internal_object(), self.device_raw);

            if self.signaled.load(Ordering::Relaxed) { return Ok(true); }

            let vk = device.pointers();
            let result = try!(check_errors(vk.GetFenceStatus(device.internal_object(),
                                                             self.fence)));
            match result {
                Success::Success => {
                    self.signaled.store(true, Ordering::Relaxed);
                    Ok(true)
                },
                Success::NotReady => Ok(false),
                _ => unreachable!()
            }
        }
    }

    /// Waits until the fence is signaled, or at least until the number of nanoseconds of the
    /// timeout has elapsed.
    ///
    /// Returns `Ok` if the fence is now signaled. Returns `Err` if the timeout was reached instead.
    pub fn wait(&self, timeout: Duration) -> Result<(), FenceWaitError> {
        unsafe {
            let device: &Device = &*self.device;
            assert_eq!(device.internal_object(), self.device_raw);

            if self.signaled.load(Ordering::Relaxed) { return Ok(()); }

            let timeout_ns = timeout.as_secs().saturating_mul(1_000_000_000)
                                              .saturating_add(timeout.subsec_nanos() as u64);

            let vk = device.pointers();
            let r = try!(check_errors(vk.WaitForFences(device.internal_object(), 1,
                                                       &self.fence, vk::TRUE, timeout_ns)));

            match r {
                Success::Success => {
                    self.signaled.store(true, Ordering::Relaxed);
                    Ok(())
                },
                Success::Timeout => {
                    Err(FenceWaitError::Timeout)
                },
                _ => unreachable!()
            }
        }
    }

    /// Waits for multiple fences at once.
    ///
    /// # Panic
    ///
    /// Panicks if not all fences belong to the same device.
    pub fn multi_wait<'a, I>(iter: I, timeout: Duration) -> Result<(), FenceWaitError>
        where I: IntoIterator<Item = &'a Fence<D>>, D: 'a
    {
        let mut device: Option<&Device> = None;

        let fences: SmallVec<[vk::Fence; 8]> = iter.into_iter().filter_map(|fence| {
            match &mut device {
                dev @ &mut None => *dev = Some(&*fence.device),
                &mut Some(ref dev) if &**dev as *const Device == &*fence.device as *const Device => {},
                _ => panic!("Tried to wait for multiple fences that didn't belong to the \
                             same device"),
            };

            if fence.signaled.load(Ordering::Relaxed) {
                None
            } else {
                Some(fence.fence)
            }
        }).collect();

        let timeout_ns = timeout.as_secs().saturating_mul(1_000_000_000)
                                          .saturating_add(timeout.subsec_nanos() as u64);

        let r = if let Some(device) = device {
            unsafe {
                let vk = device.pointers();
                try!(check_errors(vk.WaitForFences(device.internal_object(), fences.len() as u32,
                                                   fences.as_ptr(), vk::TRUE, timeout_ns)))
            }
        } else {
            return Ok(());
        };

        match r {
            Success::Success => Ok(()),
            Success::Timeout => Err(FenceWaitError::Timeout),
            _ => unreachable!()
        }
    }

    /// Resets the fence.
    // FIXME: must synchronize the fence
    #[inline]
    pub fn reset(&self) {
        unsafe {
            let device: &Device = &*self.device;
            assert_eq!(device.internal_object(), self.device_raw);

            let vk = device.pointers();
            vk.ResetFences(device.internal_object(), 1, &self.fence);
            self.signaled.store(false, Ordering::Relaxed);
        }
    }

    /// Resets multiple fences at once.
    ///
    /// # Panic
    ///
    /// Panicks if not all fences belong to the same device.
    pub fn multi_reset<'a, I>(iter: I)
        where I: IntoIterator<Item = &'a Fence<D>>, D: 'a
    {
        let mut device: Option<&Device> = None;

        let fences: SmallVec<[vk::Fence; 8]> = iter.into_iter().map(|fence| {
            match &mut device {
                dev @ &mut None => *dev = Some(&*fence.device),
                &mut Some(ref dev) if &**dev as *const Device == &*fence.device as *const Device => {},
                _ => panic!("Tried to reset multiple fences that didn't belong to the same device"),
            };

            fence.signaled.store(false, Ordering::Relaxed);
            fence.fence
        }).collect();

        if let Some(device) = device {
            unsafe {
                let vk = device.pointers();
                vk.ResetFences(device.internal_object(), fences.len() as u32, fences.as_ptr());
            }
        }
    }
}

unsafe impl<D> VulkanObject for Fence<D> where D: Deref<Target = Device> {
    type Object = vk::Fence;

    #[inline]
    fn internal_object(&self) -> vk::Fence {
        self.fence
    }
}

impl<D> Drop for Fence<D> where D: Deref<Target = Device> {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let device: &Device = &*self.device;
            assert_eq!(device.internal_object(), self.device_raw);

            let vk = device.pointers();
            vk.DestroyFence(device.internal_object(), self.fence, ptr::null());
        }
    }
}

/// Error that can be returned when waiting on a fence.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum FenceWaitError {
    /// Not enough memory to complete the wait.
    OomError(OomError),

    /// The specified timeout wasn't long enough.
    Timeout,

    /// The device has been lost.
    DeviceLostError,
}

impl error::Error for FenceWaitError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            FenceWaitError::OomError(_) => "no memory available",
            FenceWaitError::Timeout => "the timeout has been reached",
            FenceWaitError::DeviceLostError => "the device was lost",
        }
    }

    #[inline]
    fn cause(&self) -> Option<&error::Error> {
        match *self {
            FenceWaitError::OomError(ref err) => Some(err),
            _ => None
        }
    }
}

impl fmt::Display for FenceWaitError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

impl From<Error> for FenceWaitError {
    #[inline]
    fn from(err: Error) -> FenceWaitError {
        match err {
            Error::OutOfHostMemory => FenceWaitError::OomError(From::from(err)),
            Error::OutOfDeviceMemory => FenceWaitError::OomError(From::from(err)),
            Error::DeviceLost => FenceWaitError::DeviceLostError,
            _ => panic!("Unexpected error value: {}", err as i32)
        }
    }
}

#[cfg(test)]
mod tests {
    use std::time::Duration;
    use sync::Fence;

    #[test]
    #[ignore]       // TODO: fails on AMD + Windows
    fn fence_create() {
        let (device, _) = gfx_dev_and_queue!();

        let fence = Fence::new(&device).unwrap();
        assert!(!fence.ready().unwrap());
    }

    #[test]
    fn fence_create_signaled() {
        let (device, _) = gfx_dev_and_queue!();

        let fence = Fence::signaled(&device).unwrap();
        assert!(fence.ready().unwrap());
    }

    #[test]
    fn fence_signaled_wait() {
        let (device, _) = gfx_dev_and_queue!();

        let fence = Fence::signaled(&device).unwrap();
        fence.wait(Duration::new(0, 10)).unwrap();
    }

    #[test]
    #[ignore]       // TODO: fails on AMD + Windows
    fn fence_reset() {
        let (device, _) = gfx_dev_and_queue!();

        let fence = Fence::signaled(&device).unwrap();
        fence.reset();
        assert!(!fence.ready().unwrap());
    }

    #[test]
    #[should_panic = "Tried to wait for multiple fences that didn't belong to the same device"]
    fn multiwait_different_devices() {
        let (device1, _) = gfx_dev_and_queue!();
        let (device2, _) = gfx_dev_and_queue!();

        let fence1 = Fence::signaled(&device1).unwrap();
        let fence2 = Fence::signaled(&device2).unwrap();

        let _ = Fence::multi_wait([&*fence1, &*fence2].iter().cloned(), Duration::new(0, 10));
    }

    #[test]
    #[should_panic = "Tried to reset multiple fences that didn't belong to the same device"]
    fn multireset_different_devices() {
        let (device1, _) = gfx_dev_and_queue!();
        let (device2, _) = gfx_dev_and_queue!();

        let fence1 = Fence::signaled(&device1).unwrap();
        let fence2 = Fence::signaled(&device2).unwrap();

        let _ = Fence::multi_reset([&*fence1, &*fence2].iter().cloned());
    }
}
<|endoftext|>


<commit_before>//NOTE: apparently, when looking up statics by ID, they're offset by 0x4000.
export MapTile;
export StaticTile;
export load_tiles;
export to_bitmap;
export parse_map_tile;

type MapTile = {
    header: u32,
    image: ~[u16] //TODO: Consider making Pixel a type
};

type StaticTile = {
    data_size: u16,
    trigger: u16,
    width: u16,
    height: u16,
    image: ~[u16]
};

const transparent: u16 = 0b1000000000000000;
const expected_tile_size: uint = 2048;

fn load_tiles(root_path: ~str) -> (~[(uint, MapTile)], ~[(uint, StaticTile)]) { //TODO: Find a better return type for this
    let maybe_reader: option::Option<mul_reader::MulReader> = mul_reader::reader(root_path, ~"artidx.mul", ~"art.mul");

    if option::is_none(maybe_reader) {
        io::println("Error reading art tiles");
        assert false;
    }

    let reader: mul_reader::MulReader = option::unwrap(maybe_reader);

    let mut map_tiles: ~[(uint, MapTile)] = ~[];
    let mut static_tiles: ~[(uint, StaticTile)] = ~[];

    let mut index:uint = 0;
    while (reader.eof() != true) {
        let item: option::Option<mul_reader::MulRecord> = reader.read();
        if option::is_some(item) {
            let unwrapped: mul_reader::MulRecord = option::unwrap(item);
            //let record_header = byte_helpers::bytes_to_le_uint(vec::slice(unwrapped.data, 0, 3));

            if (index < 0x4000) {
                /*let maybe_map_tile: option::Option<MapTile> = parse_map_tile(unwrapped);
                if option::is_some(maybe_map_tile) {
                    vec::push(map_tiles, (index, option::unwrap(maybe_map_tile.unwrap)));
                }*/
            } else if (index < 0x8000){
                let maybe_static_tile: option::Option<StaticTile> = parse_static_tile(unwrapped);
                if option::is_some(maybe_static_tile) {
                    vec::push(static_tiles, (index, option::unwrap(maybe_static_tile)));
                }
            }
        }
        index += 1;
    }

    return (map_tiles, static_tiles);
}

//TODO: Use borrowed pointers;
fn parse_map_tile(record: mul_reader::MulRecord) -> option::Option<MapTile> { //Interestingly, pixels seem to be 565, rather than 555

    if (vec::len(record.data) != expected_tile_size) {
        return option::None;
    }

    let data_source = utils::ByteBuffer(record.data);
    let record_header = byte_helpers::bytes_to_le_uint(data_source.read(4));
    let mut image: ~[u16] = ~[];

    for uint::range(0, 44) |i| {

        let slice_size: uint = if (i >= 22) {(44 - i) * 2} else {(i + 1) * 2};
        vec::grow(image, (22 - (slice_size / 2)), transparent);
        let slice_data = data_source.read(slice_size * 2);
        vec::push_all(image, byte_helpers::u8vec_to_u16vec(slice_data));
        vec::grow(image, (22 - (slice_size / 2)), transparent);
    };

    return option::Some({
        header: record_header as u32,
        image: image
    });
}

fn parse_static_tile(record: mul_reader::MulRecord) -> option::Option<StaticTile> {

    io::println("NEW STATIC");
    let data_source = utils::ByteBuffer(record.data);

    let data_size: u16 = byte_helpers::bytes_to_le_uint(data_source.read(2)) as u16; //Might not be size :P
    let trigger: u16 = byte_helpers::bytes_to_le_uint(data_source.read(2)) as u16;

    let width: u16 = byte_helpers::bytes_to_le_uint(data_source.read(2)) as u16;
    let height: u16 = byte_helpers::bytes_to_le_uint(data_source.read(2)) as u16;

    if (width == 0 || height >= 1024 || height == 0 || height >= 1024) {
        io::println("Bad image dimensions found");
        return option::None;
    }

    let mut image: ~[u16] = ~[];

    //Offset table
    //let mut offset_table: ~[u16] = ~[];
    //TODO: Less magic numbers
    //Read the offset table
    let mut offset_table: ~[u16] = ~[];
    for uint::range(0, height as uint) |index| {
        let offset = byte_helpers::bytes_to_le_uint(data_source.read(2)) as u16;
        io::println(#fmt("Offset %u", offset as uint));
        vec::push(offset_table, offset);
    }

    let data_start_pos = data_source.pos;

    for offset_table.each |offset| {
        data_source.seek(data_start_pos as uint + (offset as uint * 2));
        let x_offset = byte_helpers::bytes_to_le_uint(data_source.read(2)) as u16;
        let run_length = byte_helpers::bytes_to_le_uint(data_source.read(2)) as u16;
        io::println(#fmt("%u pixels of padding, run length of %u", x_offset as uint, run_length as uint * 2));
        if (x_offset + run_length == 0) {
             io::println("Null line!");
        } else {
            let run = byte_helpers::u8vec_to_u16vec(data_source.read((run_length as uint) * 2));
        }
    }

    /*
    for uint::range(0, height as uint) |index| {
        io::println("ROW");
        let offset = byte_helpers::bytes_to_le_uint(vec::slice(record.data, 8 + (index * 2), 9 + (index * 2)));
        let mut data_start = offset + 8 + (height as uint * 2);
        let mut current_row_width:uint = 0;
        //From here, read the padding length, run length, and then Run pixels
        loop {
            io::println("LOOP");
            let padding_pixels = byte_helpers::bytes_to_le_uint(vec::slice(record.data, data_start, data_start + 1));
            let run_by = byte_helpers::bytes_to_le_uint(vec::slice(record.data, data_start + 2, data_start + 3));
            if padding_pixels == 0 && run_by == 0 {
                //Fill until current row width is less than width
                io::println("BREAK");
                if current_row_width < width as uint {
                    vec::grow(image, width as uint - current_row_width, transparent);
                }
                break;
            }
            let run_data = byte_helpers::u8vec_to_u16vec(vec::slice(record.data, data_start + 4, data_start + 4 + (run_by * 2)));

            vec::grow(image, padding_pixels, transparent);
            vec::push_all(image, run_data);

            data_start += 4 + (run_by * 2);
            current_row_width += padding_pixels + (run_by * 2);
        }
        //offset_table.push(byte_helpers::bytes_to_le_uint(vec::slice(record.data, 8 + (index * 2), 9 + (index * 2)))
    };*/

    return option::Some({
        data_size: data_size,
        trigger: trigger,
        width: width,
        height: height,
        image: image
    });
}

fn to_bitmap(width: u32, height: u32, data: ~[u16]) -> ~[u8] { //TODO: Make this take arbitrary pixel depths
    let signature: ~[u8] = ~[0x42, 0x4D];
    let file_size: ~[u8] = byte_helpers::uint_to_le_bytes(((width * height * 2) + 14 + 40) as u64, 4);
    let reserved: ~[u8] = ~[0, 0, 0, 0];
    let data_offset: ~[u8] = byte_helpers::uint_to_le_bytes(54, 4);

    let header_size: ~[u8] = byte_helpers::uint_to_le_bytes(40, 4);
    let width_bytes: ~[u8] = byte_helpers::uint_to_le_bytes(width as u64, 4); //FIXME: should be signed?
    let height_bytes: ~[u8] = byte_helpers::uint_to_le_bytes(height as u64, 4);
    let colour_panes: ~[u8] = ~[1, 0];
    let depth: ~[u8] = ~[16, 0];
    let compression: ~[u8] = ~[0,0,0,0];
    let image_size: ~[u8] = ~[0,0,0,0];
    let horizontal_res: ~[u8] = ~[0, 0, 0, 0];
    let vertical_res: ~[u8] = ~[0, 0, 0, 0];
    let palette_count: ~[u8] = ~[0, 0, 0, 0];
    let important_colours: ~[u8] = ~[0, 0, 0, 0];

    //54 bytes so far
    //TODO: explode the image vector, iterate backwards, turn it into bytes
    let mut rows: ~[mut ~[u8]] = ~[mut];
    for uint::range(0, height as uint) |i| {
        let slice = vec::slice(data, i * (width as uint), (i+1) * (width as uint));
        let mut row: ~[u8] = ~[];
        for slice.each |sliced| {
            vec::push_all(row, byte_helpers::uint_to_le_bytes(sliced as u64, 2));
        }
        vec::push(rows, row);
    };
    vec::reverse(rows);
    //vec::grow(pixels, 44 * 44 * 4, 0x7f);

    return vec::concat(~[
        signature,
        file_size,
        reserved,
        data_offset,

        header_size,
        width_bytes,
        height_bytes,
        colour_panes,
        depth,
        compression,
        image_size,
        horizontal_res,
        vertical_res,
        palette_count,
        important_colours,

        vec::concat(rows)
    ]);
}
<commit_msg>Art reading code, should be nearly there now!<commit_after>//NOTE: apparently, when looking up statics by ID, they're offset by 0x4000.
export MapTile;
export StaticTile;
export load_tiles;
export to_bitmap;
export parse_map_tile;

type MapTile = {
    header: u32,
    image: ~[u16] //TODO: Consider making Pixel a type
};

type StaticTile = {
    data_size: u16,
    trigger: u16,
    width: u16,
    height: u16,
    image: ~[u16]
};

const transparent: u16 = 0b1000000000000000;
const expected_tile_size: uint = 2048;

fn load_tiles(root_path: ~str) -> (~[(uint, MapTile)], ~[(uint, StaticTile)]) { //TODO: Find a better return type for this
    let maybe_reader: option::Option<mul_reader::MulReader> = mul_reader::reader(root_path, ~"artidx.mul", ~"art.mul");

    if option::is_none(maybe_reader) {
        io::println("Error reading art tiles");
        assert false;
    }

    let reader: mul_reader::MulReader = option::unwrap(maybe_reader);

    let mut map_tiles: ~[(uint, MapTile)] = ~[];
    let mut static_tiles: ~[(uint, StaticTile)] = ~[];

    let mut index:uint = 0;
    while (reader.eof() != true) {
        let item: option::Option<mul_reader::MulRecord> = reader.read();
        if option::is_some(item) {
            let unwrapped: mul_reader::MulRecord = option::unwrap(item);
            //let record_header = byte_helpers::bytes_to_le_uint(vec::slice(unwrapped.data, 0, 3));

            if (index < 0x4000) {
                /*let maybe_map_tile: option::Option<MapTile> = parse_map_tile(unwrapped);
                if option::is_some(maybe_map_tile) {
                    vec::push(map_tiles, (index, option::unwrap(maybe_map_tile.unwrap)));
                }*/
            } else if (index < 0x8000){
                let maybe_static_tile: option::Option<StaticTile> = parse_static_tile(unwrapped);
                if option::is_some(maybe_static_tile) {
                    vec::push(static_tiles, (index, option::unwrap(maybe_static_tile)));
                }
            }
        }
        index += 1;
    }

    return (map_tiles, static_tiles);
}

//TODO: Use borrowed pointers;
fn parse_map_tile(record: mul_reader::MulRecord) -> option::Option<MapTile> { //Interestingly, pixels seem to be 565, rather than 555

    if (vec::len(record.data) != expected_tile_size) {
        return option::None;
    }

    let data_source = utils::ByteBuffer(record.data);
    let record_header = byte_helpers::bytes_to_le_uint(data_source.read(4));
    let mut image: ~[u16] = ~[];

    for uint::range(0, 44) |i| {

        let slice_size: uint = if (i >= 22) {(44 - i) * 2} else {(i + 1) * 2};
        vec::grow(image, (22 - (slice_size / 2)), transparent);
        let slice_data = data_source.read(slice_size * 2);
        vec::push_all(image, byte_helpers::u8vec_to_u16vec(slice_data));
        vec::grow(image, (22 - (slice_size / 2)), transparent);
    };

    return option::Some({
        header: record_header as u32,
        image: image
    });
}

fn parse_static_tile(record: mul_reader::MulRecord) -> option::Option<StaticTile> {

    io::println("NEW STATIC");
    let data_source = utils::ByteBuffer(record.data);

    let data_size: u16 = byte_helpers::bytes_to_le_uint(data_source.read(2)) as u16; //Might not be size :P
    let trigger: u16 = byte_helpers::bytes_to_le_uint(data_source.read(2)) as u16;

    let width: u16 = byte_helpers::bytes_to_le_uint(data_source.read(2)) as u16;
    let height: u16 = byte_helpers::bytes_to_le_uint(data_source.read(2)) as u16;

    if (width == 0 || height >= 1024 || height == 0 || height >= 1024) {
        io::println("Bad image dimensions found");
        return option::None;
    }

    let mut image: ~[u16] = ~[];

    //Offset table
    //let mut offset_table: ~[u16] = ~[];
    //TODO: Less magic numbers
    //Read the offset table
    let mut offset_table: ~[u16] = ~[];
    for uint::range(0, height as uint) |index| {
        let offset = byte_helpers::bytes_to_le_uint(data_source.read(2)) as u16;
        io::println(#fmt("Offset %u", offset as uint));
        vec::push(offset_table, offset);
    }

    let data_start_pos = data_source.pos;

    for offset_table.each |offset| {
        data_source.seek(data_start_pos as uint + (offset as uint * 2));
        let mut current_row_width: uint = 0;

        loop {
            let x_offset = byte_helpers::bytes_to_le_uint(data_source.read(2)) as u16;
            let run_length = byte_helpers::bytes_to_le_uint(data_source.read(2)) as u16;
            io::println(#fmt("%u pixels of padding, run length of %u", x_offset as uint, run_length as uint * 2));
            if (x_offset + run_length == 0) {
                io::println("Null line!");
                vec::grow(image, width as uint - current_row_width, transparent);
                break;
            } else {
                let run = byte_helpers::u8vec_to_u16vec(data_source.read((run_length as uint) * 2));
                vec::grow(image, x_offset as uint, transparent);
                vec::push_all(image, run);
                current_row_width += x_offset as uint + run_length as uint;
                assert(current_row_width <= width as uint);
            }
        }
    }

    /*
    for uint::range(0, height as uint) |index| {
        io::println("ROW");
        let offset = byte_helpers::bytes_to_le_uint(vec::slice(record.data, 8 + (index * 2), 9 + (index * 2)));
        let mut data_start = offset + 8 + (height as uint * 2);
        let mut current_row_width:uint = 0;
        //From here, read the padding length, run length, and then Run pixels
        loop {
            io::println("LOOP");
            let padding_pixels = byte_helpers::bytes_to_le_uint(vec::slice(record.data, data_start, data_start + 1));
            let run_by = byte_helpers::bytes_to_le_uint(vec::slice(record.data, data_start + 2, data_start + 3));
            if padding_pixels == 0 && run_by == 0 {
                //Fill until current row width is less than width
                io::println("BREAK");
                if current_row_width < width as uint {
                    vec::grow(image, width as uint - current_row_width, transparent);
                }
                break;
            }
            let run_data = byte_helpers::u8vec_to_u16vec(vec::slice(record.data, data_start + 4, data_start + 4 + (run_by * 2)));

            vec::grow(image, padding_pixels, transparent);
            vec::push_all(image, run_data);

            data_start += 4 + (run_by * 2);
            current_row_width += padding_pixels + (run_by * 2);
        }
        //offset_table.push(byte_helpers::bytes_to_le_uint(vec::slice(record.data, 8 + (index * 2), 9 + (index * 2)))
    };*/

    return option::Some({
        data_size: data_size,
        trigger: trigger,
        width: width,
        height: height,
        image: image
    });
}

fn to_bitmap(width: u32, height: u32, data: ~[u16]) -> ~[u8] { //TODO: Make this take arbitrary pixel depths
    let signature: ~[u8] = ~[0x42, 0x4D];
    let file_size: ~[u8] = byte_helpers::uint_to_le_bytes(((width * height * 2) + 14 + 40) as u64, 4);
    let reserved: ~[u8] = ~[0, 0, 0, 0];
    let data_offset: ~[u8] = byte_helpers::uint_to_le_bytes(54, 4);

    let header_size: ~[u8] = byte_helpers::uint_to_le_bytes(40, 4);
    let width_bytes: ~[u8] = byte_helpers::uint_to_le_bytes(width as u64, 4); //FIXME: should be signed?
    let height_bytes: ~[u8] = byte_helpers::uint_to_le_bytes(height as u64, 4);
    let colour_panes: ~[u8] = ~[1, 0];
    let depth: ~[u8] = ~[16, 0];
    let compression: ~[u8] = ~[0,0,0,0];
    let image_size: ~[u8] = ~[0,0,0,0];
    let horizontal_res: ~[u8] = ~[0, 0, 0, 0];
    let vertical_res: ~[u8] = ~[0, 0, 0, 0];
    let palette_count: ~[u8] = ~[0, 0, 0, 0];
    let important_colours: ~[u8] = ~[0, 0, 0, 0];

    //54 bytes so far
    //TODO: explode the image vector, iterate backwards, turn it into bytes
    let mut rows: ~[mut ~[u8]] = ~[mut];
    for uint::range(0, height as uint) |i| {
        let slice = vec::slice(data, i * (width as uint), (i+1) * (width as uint));
        let mut row: ~[u8] = ~[];
        for slice.each |sliced| {
            vec::push_all(row, byte_helpers::uint_to_le_bytes(sliced as u64, 2));
        }
        vec::push(rows, row);
    };
    vec::reverse(rows);
    //vec::grow(pixels, 44 * 44 * 4, 0x7f);

    return vec::concat(~[
        signature,
        file_size,
        reserved,
        data_offset,

        header_size,
        width_bytes,
        height_bytes,
        colour_panes,
        depth,
        compression,
        image_size,
        horizontal_res,
        vertical_res,
        palette_count,
        important_colours,

        vec::concat(rows)
    ]);
}
<|endoftext|>


<commit_before>extern crate byteorder;
pub mod rom_loader;
pub mod memory;
pub mod cpu;
pub mod logger;

use cpu::CpuState;
use memory::Memory;

fn main() {
    let rom = rom_loader::load_ines("roms/nestest.nes").unwrap();
    let mut cpu: CpuState = Default::default();
    let mut mem:Memory = Memory::new();

    // TODO: handle this properly instead of just unwrapping
    let opcode_info = cpu::opcode::load_from_file("resources/opcodes.csv").unwrap();
    let executor = cpu::CpuExecutor::new(opcode_info.0);
    let mut logger = logger::NesTest::new("resources/nestest.out",opcode_info.1);

    executor.power_on(&mut cpu, &mut mem);
    cpu.pc = 0xC000;
    mem.write(0,&rom);

    for _ in 0..10 {
        executor.fetch_and_decode(&mut cpu,&mut mem).is_ok();
        logger.log_after_fetch(&cpu,&mem);
        executor.execute(&mut cpu,&mut mem).is_ok();
    }
}

<commit_msg>for loop now continues past 10 iterations :)<commit_after>extern crate byteorder;
pub mod rom_loader;
pub mod memory;
pub mod cpu;
pub mod logger;

use cpu::CpuState;
use memory::Memory;

fn main() {
    let rom = rom_loader::load_ines("roms/nestest.nes").unwrap();
    let mut cpu: CpuState = Default::default();
    let mut mem:Memory = Memory::new();

    // TODO: handle this properly instead of just unwrapping
    let opcode_info = cpu::opcode::load_from_file("resources/opcodes.csv").unwrap();
    let executor = cpu::CpuExecutor::new(opcode_info.0);
    let mut logger = logger::NesTest::new("resources/nestest.out",opcode_info.1);

    executor.power_on(&mut cpu, &mut mem);
    cpu.pc = 0xC000;
    mem.write(0,&rom);

    for _ in 0..8991 {
        executor.fetch_and_decode(&mut cpu,&mut mem).unwrap();
        logger.log_after_fetch(&cpu,&mem);
        executor.execute(&mut cpu,&mut mem).unwrap();
    }
}

<|endoftext|>


<commit_before>#[macro_use]
extern crate slog;
extern crate slog_term;
extern crate slog_async;

use slog::Drain;

fn main() {
	let decorator = slog_term::TermDecorator::new().build();
    let drain = slog_term::FullFormat::new(decorator).build().fuse();
    let drain = slog_async::Async::new(drain).build().fuse();
	let log = slog::Logger::root(drain, o!());

	trace!(log, "hello world");
	debug!(log, "hello world");
	info!(log, "hello world");
	warn!(log, "hello world");
	error!(log, "hello world");
	crit!(log, "hello world");
}
<commit_msg>Start on a LogTimer class.<commit_after>#[macro_use]
extern crate slog;
extern crate slog_term;
extern crate slog_async;

use slog::Drain;
use slog::Logger;

struct LogTimer {
	name: String,
	log: &Logger
}

impl LogTimer {
	pub fn new(logger: &Logger, name: &str) -> LogTimer {
		LogTimer { log: logger, name: String::from(name) }
	}
}

impl Drop for LogTimer {
	fn drop(&mut self) {
		debug!(self.log, "Dropping {}", self.name);
	}
}

fn main() {
	let decorator = slog_term::TermDecorator::new().build();
    let drain = slog_term::FullFormat::new(decorator).build().fuse();
    let drain = slog_async::Async::new(drain).build().fuse();
	let log = slog::Logger::root(drain, o!());

	//trace!(log, "hello world");
	//debug!(log, "hello world");
	//info!(log, "hello world");
	//warn!(log, "hello world");
	//error!(log, "hello world");
	//crit!(log, "hello world");

	let t = LogTimer::new(&log, "Main");
	other(&log);

}

fn other(log: &Logger) {
	let t2 = LogTimer::new(log, "Other");
}
<|endoftext|>


<commit_before>//! Type safe native types for safe context/surface creation

use super::{
    display::EGLDisplayHandle, ffi, wrap_egl_call, EGLError, Error, SurfaceCreationError, SwapBuffersError,
};
use nix::libc::{c_int, c_void};
use std::sync::Arc;

#[cfg(feature = "backend_winit")]
use wayland_egl as wegl;
#[cfg(feature = "backend_winit")]
use winit::platform::unix::WindowExtUnix;
#[cfg(feature = "backend_winit")]
use winit::window::Window as WinitWindow;

/// Trait for typed backend variants (X11/Wayland/GBM)
pub trait Backend {
    /// Surface type created by this backend
    type Surface: NativeSurface<Error = Self::Error>;
    /// Error type thrown by the surface creation in case of failure.
    type Error: ::std::error::Error + Send + 'static;

    /// Return an [`EGLDisplay`](ffi::egl::types::EGLDisplay) based on this backend
    ///
    /// # Safety
    ///
    /// The returned [`EGLDisplay`](ffi::egl::types::EGLDisplay) needs to be a valid pointer for EGL,
    /// but there is no way to test that.
    unsafe fn get_display<F: Fn(&str) -> bool>(
        display: ffi::NativeDisplayType,
        attribs: &[ffi::EGLint],
        has_dp_extension: F,
        log: ::slog::Logger,
    ) -> Result<ffi::egl::types::EGLDisplay, EGLError>;
}

#[cfg(feature = "backend_winit")]
/// Wayland backend type
pub enum Wayland {}
#[cfg(feature = "backend_winit")]
impl Backend for Wayland {
    type Surface = wegl::WlEglSurface;
    type Error = Error;

    unsafe fn get_display<F>(
        display: ffi::NativeDisplayType,
        attribs: &[ffi::EGLint],
        has_dp_extension: F,
        log: ::slog::Logger,
    ) -> Result<ffi::egl::types::EGLDisplay, EGLError>
    where
        F: Fn(&str) -> bool,
    {
        if has_dp_extension("EGL_KHR_platform_wayland") && ffi::egl::GetPlatformDisplay::is_loaded() {
            trace!(log, "EGL Display Initialization via EGL_KHR_platform_wayland");
            let attribs = attribs.iter().map(|x| *x as isize).collect::<Vec<_>>();
            wrap_egl_call(|| {
                ffi::egl::GetPlatformDisplay(
                    ffi::egl::PLATFORM_WAYLAND_KHR,
                    display as *mut _,
                    attribs.as_ptr(),
                )
            })
        } else if has_dp_extension("EGL_EXT_platform_wayland") && ffi::egl::GetPlatformDisplayEXT::is_loaded()
        {
            trace!(log, "EGL Display Initialization via EGL_EXT_platform_wayland");
            wrap_egl_call(|| {
                ffi::egl::GetPlatformDisplayEXT(
                    ffi::egl::PLATFORM_WAYLAND_EXT,
                    display as *mut _,
                    attribs.as_ptr(),
                )
            })
        } else {
            trace!(log, "Default EGL Display Initialization via GetDisplay");
            wrap_egl_call(|| ffi::egl::GetDisplay(display as *mut _))
        }
    }
}

#[cfg(feature = "backend_winit")]
/// Typed Xlib window for the `X11` backend
pub struct XlibWindow(u64);
#[cfg(feature = "backend_winit")]
/// X11 backend type
pub enum X11 {}
#[cfg(feature = "backend_winit")]
impl Backend for X11 {
    type Surface = XlibWindow;
    type Error = Error;

    unsafe fn get_display<F>(
        display: ffi::NativeDisplayType,
        attribs: &[ffi::EGLint],
        has_dp_extension: F,
        log: ::slog::Logger,
    ) -> Result<ffi::egl::types::EGLDisplay, EGLError>
    where
        F: Fn(&str) -> bool,
    {
        if has_dp_extension("EGL_KHR_platform_x11") && ffi::egl::GetPlatformDisplay::is_loaded() {
            trace!(log, "EGL Display Initialization via EGL_KHR_platform_x11");
            let attribs = attribs.iter().map(|x| *x as isize).collect::<Vec<_>>();
            wrap_egl_call(|| {
                ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, display as *mut _, attribs.as_ptr())
            })
        } else if has_dp_extension("EGL_EXT_platform_x11") && ffi::egl::GetPlatformDisplayEXT::is_loaded() {
            trace!(log, "EGL Display Initialization via EGL_EXT_platform_x11");
            wrap_egl_call(|| {
                ffi::egl::GetPlatformDisplayEXT(
                    ffi::egl::PLATFORM_X11_EXT,
                    display as *mut _,
                    attribs.as_ptr(),
                )
            })
        } else {
            trace!(log, "Default EGL Display Initialization via GetDisplay");
            wrap_egl_call(|| ffi::egl::GetDisplay(display as *mut _))
        }
    }
}

/// Trait for types returning Surfaces which can be used to initialize [`EGLSurface`](super::EGLSurface)s
///
/// ## Unsafety
///
/// The returned [`NativeDisplayType`](super::ffi::NativeDisplayType) must be valid for EGL and there is no way to test that.
pub unsafe trait NativeDisplay<B: Backend> {
    /// Arguments used to surface creation.
    type Arguments;
    /// Because one type might implement multiple [`Backend`]s this function must be called to check
    /// if the expected [`Backend`] is used at runtime.
    fn is_backend(&self) -> bool;
    /// Return a raw pointer EGL will accept for context creation.
    fn ptr(&self) -> Result<ffi::NativeDisplayType, Error>;
    /// Return attributes that might be used by `B::get_display`
    ///
    /// Default implementation returns an empty list
    fn attributes(&self) -> Vec<ffi::EGLint> {
        vec![ffi::egl::NONE as ffi::EGLint]
    }
    /// Type of surfaces created
    fn surface_type(&self) -> ffi::EGLint {
        ffi::egl::WINDOW_BIT as ffi::EGLint
    }
    /// Create a surface
    fn create_surface(&mut self, args: Self::Arguments) -> Result<B::Surface, B::Error>;
}

#[cfg(feature = "backend_winit")]
unsafe impl NativeDisplay<X11> for WinitWindow {
    type Arguments = ();

    fn is_backend(&self) -> bool {
        self.xlib_display().is_some()
    }

    fn ptr(&self) -> Result<ffi::NativeDisplayType, Error> {
        self.xlib_display()
            .map(|ptr| ptr as *const _)
            .ok_or_else(|| Error::NonMatchingBackend("X11"))
    }

    fn create_surface(&mut self, _args: ()) -> Result<XlibWindow, Error> {
        self.xlib_window()
            .map(XlibWindow)
            .ok_or_else(|| Error::NonMatchingBackend("X11"))
    }
}

#[cfg(feature = "backend_winit")]
unsafe impl NativeDisplay<Wayland> for WinitWindow {
    type Arguments = ();

    fn is_backend(&self) -> bool {
        self.wayland_display().is_some()
    }

    fn ptr(&self) -> Result<ffi::NativeDisplayType, Error> {
        self.wayland_display()
            .map(|ptr| ptr as *const _)
            .ok_or_else(|| Error::NonMatchingBackend("Wayland"))
    }

    fn create_surface(&mut self, _args: ()) -> Result<wegl::WlEglSurface, Error> {
        if let Some(surface) = self.wayland_surface() {
            let size = self.inner_size();
            Ok(unsafe {
                wegl::WlEglSurface::new_from_raw(surface as *mut _, size.width as i32, size.height as i32)
            })
        } else {
            Err(Error::NonMatchingBackend("Wayland"))
        }
    }
}

/// Trait for types returning valid surface pointers for initializing egl
///
/// ## Unsafety
///
/// The returned [`NativeWindowType`](ffi::NativeWindowType) must be valid for EGL
/// and there is no way to test that.
pub unsafe trait NativeSurface {
    /// Error type thrown by the surface creation in case of failure.
    type Error: ::std::error::Error + Send + 'static;
    /// Create an EGLSurface from the internal native type.
    ///
    /// Must be able to deal with re-creation of existing resources,
    /// if `needs_recreation` can return `true`.
    ///
    /// # Safety
    /// This is usually an unsafe operation returning a raw pointer.
    unsafe fn create(
        &self,
        display: &Arc<EGLDisplayHandle>,
        config_id: ffi::egl::types::EGLConfig,
        surface_attributes: &[c_int],
    ) -> Result<*const c_void, SurfaceCreationError<Self::Error>>;

    /// Will be called to check if any internal resources will need
    /// to be recreated. Old resources must be used until `create`
    /// was called again and a new surface was optained.
    ///
    /// Only needs to be recreated, if this may return true.
    /// The default implementation always returns false.
    fn needs_recreation(&self) -> bool {
        false
    }

    /// Adds additional semantics when calling
    /// [EGLSurface::swap_buffers](::backend::egl::surface::EGLSurface::swap_buffers)
    ///
    /// Only implement if required by the backend.
    fn swap_buffers(
        &self,
        display: &Arc<EGLDisplayHandle>,
        surface: ffi::egl::types::EGLSurface,
    ) -> Result<(), SwapBuffersError<Self::Error>> {
        wrap_egl_call(|| unsafe {
            ffi::egl::SwapBuffers(***display, surface as *const _);
        })
        .map_err(SwapBuffersError::EGLSwapBuffers)
    }
}

#[cfg(feature = "backend_winit")]
unsafe impl NativeSurface for XlibWindow {
    type Error = Error;

    unsafe fn create(
        &self,
        display: &Arc<EGLDisplayHandle>,
        config_id: ffi::egl::types::EGLConfig,
        surface_attributes: &[c_int],
    ) -> Result<*const c_void, SurfaceCreationError<Error>> {
        wrap_egl_call(|| {
            ffi::egl::CreateWindowSurface(
                display.handle,
                config_id,
                self.0 as *const _,
                surface_attributes.as_ptr(),
            )
        })
        .map_err(SurfaceCreationError::EGLSurfaceCreationFailed)
    }
}

#[cfg(feature = "backend_winit")]
unsafe impl NativeSurface for wegl::WlEglSurface {
    type Error = Error;

    unsafe fn create(
        &self,
        display: &Arc<EGLDisplayHandle>,
        config_id: ffi::egl::types::EGLConfig,
        surface_attributes: &[c_int],
    ) -> Result<*const c_void, SurfaceCreationError<Error>> {
        wrap_egl_call(|| {
            ffi::egl::CreateWindowSurface(
                display.handle,
                config_id,
                self.ptr() as *const _,
                surface_attributes.as_ptr(),
            )
        })
        .map_err(SurfaceCreationError::EGLSurfaceCreationFailed)
    }
}
<commit_msg>Fix clippy::unnecessary-lazy-evaluations warnings<commit_after>//! Type safe native types for safe context/surface creation

use super::{
    display::EGLDisplayHandle, ffi, wrap_egl_call, EGLError, Error, SurfaceCreationError, SwapBuffersError,
};
use nix::libc::{c_int, c_void};
use std::sync::Arc;

#[cfg(feature = "backend_winit")]
use wayland_egl as wegl;
#[cfg(feature = "backend_winit")]
use winit::platform::unix::WindowExtUnix;
#[cfg(feature = "backend_winit")]
use winit::window::Window as WinitWindow;

/// Trait for typed backend variants (X11/Wayland/GBM)
pub trait Backend {
    /// Surface type created by this backend
    type Surface: NativeSurface<Error = Self::Error>;
    /// Error type thrown by the surface creation in case of failure.
    type Error: ::std::error::Error + Send + 'static;

    /// Return an [`EGLDisplay`](ffi::egl::types::EGLDisplay) based on this backend
    ///
    /// # Safety
    ///
    /// The returned [`EGLDisplay`](ffi::egl::types::EGLDisplay) needs to be a valid pointer for EGL,
    /// but there is no way to test that.
    unsafe fn get_display<F: Fn(&str) -> bool>(
        display: ffi::NativeDisplayType,
        attribs: &[ffi::EGLint],
        has_dp_extension: F,
        log: ::slog::Logger,
    ) -> Result<ffi::egl::types::EGLDisplay, EGLError>;
}

#[cfg(feature = "backend_winit")]
/// Wayland backend type
pub enum Wayland {}
#[cfg(feature = "backend_winit")]
impl Backend for Wayland {
    type Surface = wegl::WlEglSurface;
    type Error = Error;

    unsafe fn get_display<F>(
        display: ffi::NativeDisplayType,
        attribs: &[ffi::EGLint],
        has_dp_extension: F,
        log: ::slog::Logger,
    ) -> Result<ffi::egl::types::EGLDisplay, EGLError>
    where
        F: Fn(&str) -> bool,
    {
        if has_dp_extension("EGL_KHR_platform_wayland") && ffi::egl::GetPlatformDisplay::is_loaded() {
            trace!(log, "EGL Display Initialization via EGL_KHR_platform_wayland");
            let attribs = attribs.iter().map(|x| *x as isize).collect::<Vec<_>>();
            wrap_egl_call(|| {
                ffi::egl::GetPlatformDisplay(
                    ffi::egl::PLATFORM_WAYLAND_KHR,
                    display as *mut _,
                    attribs.as_ptr(),
                )
            })
        } else if has_dp_extension("EGL_EXT_platform_wayland") && ffi::egl::GetPlatformDisplayEXT::is_loaded()
        {
            trace!(log, "EGL Display Initialization via EGL_EXT_platform_wayland");
            wrap_egl_call(|| {
                ffi::egl::GetPlatformDisplayEXT(
                    ffi::egl::PLATFORM_WAYLAND_EXT,
                    display as *mut _,
                    attribs.as_ptr(),
                )
            })
        } else {
            trace!(log, "Default EGL Display Initialization via GetDisplay");
            wrap_egl_call(|| ffi::egl::GetDisplay(display as *mut _))
        }
    }
}

#[cfg(feature = "backend_winit")]
/// Typed Xlib window for the `X11` backend
pub struct XlibWindow(u64);
#[cfg(feature = "backend_winit")]
/// X11 backend type
pub enum X11 {}
#[cfg(feature = "backend_winit")]
impl Backend for X11 {
    type Surface = XlibWindow;
    type Error = Error;

    unsafe fn get_display<F>(
        display: ffi::NativeDisplayType,
        attribs: &[ffi::EGLint],
        has_dp_extension: F,
        log: ::slog::Logger,
    ) -> Result<ffi::egl::types::EGLDisplay, EGLError>
    where
        F: Fn(&str) -> bool,
    {
        if has_dp_extension("EGL_KHR_platform_x11") && ffi::egl::GetPlatformDisplay::is_loaded() {
            trace!(log, "EGL Display Initialization via EGL_KHR_platform_x11");
            let attribs = attribs.iter().map(|x| *x as isize).collect::<Vec<_>>();
            wrap_egl_call(|| {
                ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, display as *mut _, attribs.as_ptr())
            })
        } else if has_dp_extension("EGL_EXT_platform_x11") && ffi::egl::GetPlatformDisplayEXT::is_loaded() {
            trace!(log, "EGL Display Initialization via EGL_EXT_platform_x11");
            wrap_egl_call(|| {
                ffi::egl::GetPlatformDisplayEXT(
                    ffi::egl::PLATFORM_X11_EXT,
                    display as *mut _,
                    attribs.as_ptr(),
                )
            })
        } else {
            trace!(log, "Default EGL Display Initialization via GetDisplay");
            wrap_egl_call(|| ffi::egl::GetDisplay(display as *mut _))
        }
    }
}

/// Trait for types returning Surfaces which can be used to initialize [`EGLSurface`](super::EGLSurface)s
///
/// ## Unsafety
///
/// The returned [`NativeDisplayType`](super::ffi::NativeDisplayType) must be valid for EGL and there is no way to test that.
pub unsafe trait NativeDisplay<B: Backend> {
    /// Arguments used to surface creation.
    type Arguments;
    /// Because one type might implement multiple [`Backend`]s this function must be called to check
    /// if the expected [`Backend`] is used at runtime.
    fn is_backend(&self) -> bool;
    /// Return a raw pointer EGL will accept for context creation.
    fn ptr(&self) -> Result<ffi::NativeDisplayType, Error>;
    /// Return attributes that might be used by `B::get_display`
    ///
    /// Default implementation returns an empty list
    fn attributes(&self) -> Vec<ffi::EGLint> {
        vec![ffi::egl::NONE as ffi::EGLint]
    }
    /// Type of surfaces created
    fn surface_type(&self) -> ffi::EGLint {
        ffi::egl::WINDOW_BIT as ffi::EGLint
    }
    /// Create a surface
    fn create_surface(&mut self, args: Self::Arguments) -> Result<B::Surface, B::Error>;
}

#[cfg(feature = "backend_winit")]
unsafe impl NativeDisplay<X11> for WinitWindow {
    type Arguments = ();

    fn is_backend(&self) -> bool {
        self.xlib_display().is_some()
    }

    fn ptr(&self) -> Result<ffi::NativeDisplayType, Error> {
        self.xlib_display()
            .map(|ptr| ptr as *const _)
            .ok_or(Error::NonMatchingBackend("X11"))
    }

    fn create_surface(&mut self, _args: ()) -> Result<XlibWindow, Error> {
        self.xlib_window()
            .map(XlibWindow)
            .ok_or(Error::NonMatchingBackend("X11"))
    }
}

#[cfg(feature = "backend_winit")]
unsafe impl NativeDisplay<Wayland> for WinitWindow {
    type Arguments = ();

    fn is_backend(&self) -> bool {
        self.wayland_display().is_some()
    }

    fn ptr(&self) -> Result<ffi::NativeDisplayType, Error> {
        self.wayland_display()
            .map(|ptr| ptr as *const _)
            .ok_or(Error::NonMatchingBackend("Wayland"))
    }

    fn create_surface(&mut self, _args: ()) -> Result<wegl::WlEglSurface, Error> {
        if let Some(surface) = self.wayland_surface() {
            let size = self.inner_size();
            Ok(unsafe {
                wegl::WlEglSurface::new_from_raw(surface as *mut _, size.width as i32, size.height as i32)
            })
        } else {
            Err(Error::NonMatchingBackend("Wayland"))
        }
    }
}

/// Trait for types returning valid surface pointers for initializing egl
///
/// ## Unsafety
///
/// The returned [`NativeWindowType`](ffi::NativeWindowType) must be valid for EGL
/// and there is no way to test that.
pub unsafe trait NativeSurface {
    /// Error type thrown by the surface creation in case of failure.
    type Error: ::std::error::Error + Send + 'static;
    /// Create an EGLSurface from the internal native type.
    ///
    /// Must be able to deal with re-creation of existing resources,
    /// if `needs_recreation` can return `true`.
    ///
    /// # Safety
    /// This is usually an unsafe operation returning a raw pointer.
    unsafe fn create(
        &self,
        display: &Arc<EGLDisplayHandle>,
        config_id: ffi::egl::types::EGLConfig,
        surface_attributes: &[c_int],
    ) -> Result<*const c_void, SurfaceCreationError<Self::Error>>;

    /// Will be called to check if any internal resources will need
    /// to be recreated. Old resources must be used until `create`
    /// was called again and a new surface was optained.
    ///
    /// Only needs to be recreated, if this may return true.
    /// The default implementation always returns false.
    fn needs_recreation(&self) -> bool {
        false
    }

    /// Adds additional semantics when calling
    /// [EGLSurface::swap_buffers](::backend::egl::surface::EGLSurface::swap_buffers)
    ///
    /// Only implement if required by the backend.
    fn swap_buffers(
        &self,
        display: &Arc<EGLDisplayHandle>,
        surface: ffi::egl::types::EGLSurface,
    ) -> Result<(), SwapBuffersError<Self::Error>> {
        wrap_egl_call(|| unsafe {
            ffi::egl::SwapBuffers(***display, surface as *const _);
        })
        .map_err(SwapBuffersError::EGLSwapBuffers)
    }
}

#[cfg(feature = "backend_winit")]
unsafe impl NativeSurface for XlibWindow {
    type Error = Error;

    unsafe fn create(
        &self,
        display: &Arc<EGLDisplayHandle>,
        config_id: ffi::egl::types::EGLConfig,
        surface_attributes: &[c_int],
    ) -> Result<*const c_void, SurfaceCreationError<Error>> {
        wrap_egl_call(|| {
            ffi::egl::CreateWindowSurface(
                display.handle,
                config_id,
                self.0 as *const _,
                surface_attributes.as_ptr(),
            )
        })
        .map_err(SurfaceCreationError::EGLSurfaceCreationFailed)
    }
}

#[cfg(feature = "backend_winit")]
unsafe impl NativeSurface for wegl::WlEglSurface {
    type Error = Error;

    unsafe fn create(
        &self,
        display: &Arc<EGLDisplayHandle>,
        config_id: ffi::egl::types::EGLConfig,
        surface_attributes: &[c_int],
    ) -> Result<*const c_void, SurfaceCreationError<Error>> {
        wrap_egl_call(|| {
            ffi::egl::CreateWindowSurface(
                display.handle,
                config_id,
                self.ptr() as *const _,
                surface_attributes.as_ptr(),
            )
        })
        .map_err(SurfaceCreationError::EGLSurfaceCreationFailed)
    }
}
<|endoftext|>