enjoy 0.4.0

Play any game rom with associated emulator in RetroArch
use std::path::PathBuf;

use clap::Parser;

/// Play any game ROM with associated emulator in `RetroArch`.
///
/// `enjoy` is a launcher to run games from `RetroArch` without using the GUI.  It is a wrapper
/// around the `retroarch` commandline application.  The main functionality comes from a user
/// configuration file with rules to associate file extensions and emulator cores.  When running
/// `enjoy` only the path to a ROM file is required, either through arguments or the stdin.  Use
/// option `-h` for compact summary or `--help` for longer description of options.
/// <https://github.com/thingsiplay/enjoy/>
///
/// Examples:
///
/// $ enjoy '~/roms/snes/Super Mario World (U) [!].smc'
///
/// $ enjoy 'Super Mario World (U) [!].smc' -w
///
/// $ ls -1 ./snes/* | enjoy --filter '[!]' --core snes --which --highlander
///
/// $ ls -1 $(readlink -f ~/roms/gb)/* | enjoy -xWn
#[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Parser)]
#[clap(version, author, after_help = "https://github.com/thingsiplay/enjoy")]
pub struct Opt {
    /// Path to ROM file
    ///
    /// If multiple files are specified, then the first entry is picked when starting emulator.
    /// Each line from stdin is added as a game entry too.  Globbing and wildcards are not
    /// supported and should be resolved by the shell.  Relative paths and the tilde are supported
    /// and expanded.
    ///
    /// Example: "~/roms/snes/Super Mario World (U) [\!].smc"
    #[clap(parse(from_os_str))]
    pub games: Vec<PathBuf>,

    /// Bypass additional arguments to `retroarch`
    ///
    /// Everything after a standalone double dash `--` is redirected to `retroarch` without
    /// interpreting the arguments.  This can be used for options that are unsupported or not
    /// implemented in `enjoy` yet.  They are and not expanded or error checked and the user has to
    /// ensure correctness.
    ///
    /// Example: "-- --set-shader ''"
    #[clap(last = true)]
    pub retroarch_arguments: Vec<String>,

    /// Path to the user settings
    ///
    /// This programs own configuration file in INI format.  It contains all user defined rules to
    /// associated extensions and core name aliases.  Any option specified at commandline have
    /// higher priority over the individual settings in this file.
    ///
    /// Example: "/home/user/.config/enjoy/alternative.ini"
    #[clap(
        short = 'c',
        long,
        parse(from_os_str),
        value_name = "FILE",
        display_order = 1,
        default_value = "~/.config/enjoy/default.ini"
    )]
    pub config: PathBuf,

    /// Open user settings
    ///
    /// Opens the user config INI file with it's associated default application and exit.
    #[clap(short = 'O', long, display_order = 1)]
    pub open_config: bool,

    /// Print path of user settings
    ///
    /// Prints path of the user config INI file to stdout and exit.
    #[clap(short = 'o', long, display_order = 1)]
    pub config_path: bool,

    /// Path or name of `RetroArch` command
    ///
    /// The executable name or path to the RetroArch commandline application to run.  If this is a
    /// filename without directory part, then the systems `$PATH` is searched.
    ///
    /// Example: "/usr/bin/retroarch" [default: retroarch]
    #[clap(
        short = 'A',
        long,
        parse(from_os_str),
        value_name = "APP",
        display_order = 7
    )]
    pub retroarch: Option<PathBuf>,

    /// Path to `RetroArch` base configuration
    ///
    /// The `retroarch.cfg` base configuration file of `RetroArch` itself.  Usually it is found in
    /// the config folder of `RetroArch` itself.  By default these locations are looked up in this
    /// particular order: `$XDG_CONFIG_HOME/retroarch/retroarch.cfg`,
    /// `$HOME/.config/retroarch/retroarch.cfg`, `$HOME/.retroarch.cfg`.
    ///
    /// Example: "/home/user/.config/retroarch/retroarch.cfg"
    #[clap(
        short = 'B',
        long,
        parse(from_os_str),
        value_name = "FILE",
        display_order = 7
    )]
    pub retroarch_config: Option<PathBuf>,

    /// Force specific libretro core by filename
    ///
    /// The explicit filename of the emulator in `RetroArch`.  This option overwrites any previous
    /// setting or rule and forces to launch the specified emulator.  This can be a fullpath or
    /// filename only.  If this is filename only, then the directory part is looked up from
    /// `libretro-directory`.  The filename part `_libretro.so` is optional and will be added
    /// automatically.  As an example `snes9x` could be expanded into
    /// `/home/user/.config/retroarch/cores/snes9x_libretro.so`.
    ///
    /// Example: "snes9x"
    #[clap(
        short = 'L',
        long,
        parse(from_os_str),
        value_name = "FILE",
        display_order = 5,
        conflicts_with = "core"
    )]
    pub libretro: Option<PathBuf>,

    /// Directory of libretro core files
    ///
    /// The installation directory of libretro cores.  It is looked up whenever the `libretro` path
    /// is a relative filename.  At default this directory is extracted from `RetroArch` base
    /// configuration file `retroarch.cfg`.
    ///
    /// Example: "/home/user/.config/retroarch/cores"
    #[clap(
        short = 'D',
        long,
        parse(from_os_str),
        value_name = "DIR",
        display_order = 6
    )]
    pub libretro_directory: Option<PathBuf>,

    /// Force specific libretro core by user defined alias
    ///
    /// A custom identificator specified in the user configuration INI file.  The alias will be
    /// looked up and resolved into a real `libretro` path.  These are specified under the section
    /// `[cores]` as `alias=libretro_path`.
    ///
    /// Example: "snes"
    #[clap(short = 'C', long, value_name = "ALIAS", display_order = 4)]
    pub core: Option<String>,

    /// Apply simple wildcard to filter list of games
    ///
    /// Removes all games from the list, which do not match the `pattern`.  The wildcard
    /// functionality is limited and only the star `*` and questionmark `?` are supported.  The
    /// comparison is always case insensitive.  It will compare the base filename portion of the
    /// ROM path to the pattern, ignoring it's parent directory and filename extension.  At default
    /// a star is added in front and end of pattern automatically when comparing.  It is useful if
    /// more than one game entry is given to the program.  This option can be specified multiple
    /// times.  All of them have to match.
    ///
    /// Example: "mario*[\!]"
    #[clap(short = 'f', long, value_name = "PATTERN", display_order = 2)]
    pub filter: Option<Vec<String>>,

    /// Strict mode for filter
    ///
    /// Turns the option `--filter` to be more strict when comparing filenames.  It makes it case
    /// sensitive and a word will match the beginning to end of filename, no longer are stars "*"
    /// surrounding the search pattern added to match any part.
    #[clap(short = 's', long, display_order = 2)]
    pub strict: bool,

    /// Print selected game ROM
    ///
    /// Writes the full filepath of the selected game to stdout.
    #[clap(short = 'w', long, display_order = 1)]
    pub which: bool,

    /// Print RetroArch commandline
    ///
    /// Writes full command with all arguments used to run RetroArch to stdout. Has higher priority
    /// than option --which.
    #[clap(short = 'W', long, display_order = 1)]
    pub which_command: bool,

    /// Print all core names
    ///
    /// Lists all core names on the left side of the user configuration under section "\[cores\]".
    /// Will output matching cores to the libretro core that would be used with the game.  Without
    /// a game, all cores are listed.
    #[clap(short = 'n', long, display_order = 3)]
    pub list_cores: bool,

    /// Force fullscreen mode
    ///
    /// Runs the emulator and `RetroArch` UI in fullscreen, regardless of any other setting.
    #[clap(short = 'F', long, display_order = 3)]
    pub fullscreen: bool,

    /// There Can Only Be One!
    ///
    /// Prevents running another `retroarch` process, if one is already active.  In this case the
    /// final command of the emulator will not execute.
    #[clap(short = '1', long, display_order = 3)]
    pub highlander: bool,

    /// Ignore user settings
    ///
    /// The config INI file of this program will be ignored and not loaded up.  The entire
    /// application relies on commandline options and environmental variables.  Therefore any
    /// predefined rules and aliases from that file are ignored.
    #[clap(
        short = 'i',
        long,
        display_order = 8,
        conflicts_with_all = &["config", "open-config", "core"]
    )]
    pub noconfig: bool,

    /// Do not run `RetroArch`
    ///
    /// The `retroarch` run command to play ROMs will not be executed.  Internally the process is
    /// still simulated, up until to the point of running the emulator.  If a game ROM is not
    /// found, then the simulation will continue to allow execution of other options.
    #[clap(short = 'x', long, display_order = 8)]
    pub norun: bool,

    /// Dismiss reading from stdin
    ///
    /// Ignores the `stdin` and do not test or read any data from it.  Normally the program will
    /// look and read all lines from `stdin` as additional game entries.  This option will disable
    /// that.
    #[clap(short = 'z', long, display_order = 8)]
    pub nostdin: bool,

    /// Print version information
    ///
    /// Print the version number of this app and exit
    #[clap(short = 'v', long, display_order = 9)]
    pub version: bool,
}