//! This module contains traits that are usable with the `#[derive(...)].`
//! macros in [`clap_derive`].

use crate::{ArgMatches, Command, Error, PossibleValue};

use std::ffi::OsString;

/// Parse command-line arguments into `Self`.
///
/// The primary one-stop-shop trait used to create an instance of a `clap`
/// [`Command`], conduct the parsing, and turn the resulting [`ArgMatches`] back
/// into concrete instance of the user struct.
///
/// This trait is primarily a convenience on top of [`FromArgMatches`] +
/// [`CommandFactory`] which uses those two underlying traits to build the two
/// fundamental functions `parse` which uses the `std::env::args_os` iterator,
/// and `parse_from` which allows the consumer to supply the iterator (along
/// with fallible options for each).
///
/// See also [`Subcommand`] and [`Args`].
///
/// See the
/// [derive reference](https://github.com/clap-rs/clap/blob/v3.1.6/examples/derive_ref/README.md)
/// for attributes and best practices.
///
/// **NOTE:** Deriving requires the `derive` feature flag
///
/// # Examples
///
/// The following example creates a `Context` struct that would be used
/// throughout the application representing the normalized values coming from
/// the CLI.
///
#[cfg_attr(not(feature = "derive"), doc = " ```ignore")]
#[cfg_attr(feature = "derive", doc = " ```")]
/// /// My super CLI
/// #[derive(clap::Parser)]
/// #[clap(name = "demo")]
/// struct Context {
///     /// More verbose output
///     #[clap(long)]
///     verbose: bool,
///     /// An optional name
///     #[clap(short, long)]
///     name: Option<String>,
/// }
/// ```
///
/// The equivalent [`Command`] struct + `From` implementation:
///
/// ```rust
/// # use clap::{Command, Arg, ArgMatches};
/// Command::new("demo")
///     .about("My super CLI")
///     .arg(Arg::new("verbose")
///         .long("verbose")
///         .help("More verbose output"))
///     .arg(Arg::new("name")
///         .long("name")
///         .short('n')
///         .help("An optional name")
///         .takes_value(true));
///
/// struct Context {
///     verbose: bool,
///     name: Option<String>,
/// }
///
/// impl From<ArgMatches> for Context {
///     fn from(m: ArgMatches) -> Self {
///         Context {
///             verbose: m.is_present("verbose"),
///             name: m.value_of("name").map(|n| n.to_owned()),
///         }
///     }
/// }
/// ```
///
pub trait Parser: FromArgMatches + CommandFactory + Sized {
    /// Parse from `std::env::args_os()`, exit on error
    fn parse() -> Self {
        let matches = <Self as CommandFactory>::command().get_matches();
        let res =
            <Self as FromArgMatches>::from_arg_matches(&matches).map_err(format_error::<Self>);
        match res {
            Ok(s) => s,
            Err(e) => {
                // Since this is more of a development-time error, we aren't doing as fancy of a quit
                // as `get_matches`
                e.exit()
            }
        }
    }

    /// Parse from `std::env::args_os()`, return Err on error.
    fn try_parse() -> Result<Self, Error> {
        let matches = <Self as CommandFactory>::command().try_get_matches()?;
        <Self as FromArgMatches>::from_arg_matches(&matches).map_err(format_error::<Self>)
    }

    /// Parse from iterator, exit on error
    fn parse_from<I, T>(itr: I) -> Self
    where
        I: IntoIterator<Item = T>,
        T: Into<OsString> + Clone,
    {
        let matches = <Self as CommandFactory>::command().get_matches_from(itr);
        let res =
            <Self as FromArgMatches>::from_arg_matches(&matches).map_err(format_error::<Self>);
        match res {
            Ok(s) => s,
            Err(e) => {
                // Since this is more of a development-time error, we aren't doing as fancy of a quit
                // as `get_matches_from`
                e.exit()
            }
        }
    }

    /// Parse from iterator, return Err on error.
    fn try_parse_from<I, T>(itr: I) -> Result<Self, Error>
    where
        I: IntoIterator<Item = T>,
        T: Into<OsString> + Clone,
    {
        let matches = <Self as CommandFactory>::command().try_get_matches_from(itr)?;
        <Self as FromArgMatches>::from_arg_matches(&matches).map_err(format_error::<Self>)
    }

    /// Update from iterator, exit on error
    fn update_from<I, T>(&mut self, itr: I)
    where
        I: IntoIterator<Item = T>,
        T: Into<OsString> + Clone,
    {
        let matches = <Self as CommandFactory>::command_for_update().get_matches_from(itr);
        let res = <Self as FromArgMatches>::update_from_arg_matches(self, &matches)
            .map_err(format_error::<Self>);
        if let Err(e) = res {
            // Since this is more of a development-time error, we aren't doing as fancy of a quit
            // as `get_matches_from`
            e.exit()
        }
    }

    /// Update from iterator, return Err on error.
    fn try_update_from<I, T>(&mut self, itr: I) -> Result<(), Error>
    where
        I: IntoIterator<Item = T>,
        T: Into<OsString> + Clone,
    {
        let matches = <Self as CommandFactory>::command_for_update().try_get_matches_from(itr)?;
        <Self as FromArgMatches>::update_from_arg_matches(self, &matches)
            .map_err(format_error::<Self>)
    }

    /// Deprecated, `StructOpt::clap` replaced with [`IntoCommand::command`] (derive as part of
    /// [`Parser`])
    #[deprecated(
        since = "3.0.0",
        note = "`StructOpt::clap` is replaced with `IntoCommand::command` (derived as part of `Parser`)"
    )]
    #[doc(hidden)]
    fn clap<'help>() -> Command<'help> {
        <Self as CommandFactory>::command()
    }

    /// Deprecated, `StructOpt::from_clap` replaced with [`FromArgMatches::from_arg_matches`] (derive as part of
    /// [`Parser`])
    #[deprecated(
        since = "3.0.0",
        note = "`StructOpt::from_clap` is replaced with `FromArgMatches::from_arg_matches` (derived as part of `Parser`)"
    )]
    #[doc(hidden)]
    fn from_clap(matches: &ArgMatches) -> Self {
        <Self as FromArgMatches>::from_arg_matches(matches).unwrap()
    }

    /// Deprecated, `StructOpt::from_args` replaced with `Parser::parse` (note the change in derives)
    #[deprecated(
        since = "3.0.0",
        note = "`StructOpt::from_args` is replaced with `Parser::parse` (note the change in derives)"
    )]
    #[doc(hidden)]
    fn from_args() -> Self {
        Self::parse()
    }

    /// Deprecated, `StructOpt::from_args_safe` replaced with `Parser::try_parse` (note the change in derives)
    #[deprecated(
        since = "3.0.0",
        note = "`StructOpt::from_args_safe` is replaced with `Parser::try_parse` (note the change in derives)"
    )]
    #[doc(hidden)]
    fn from_args_safe() -> Result<Self, Error> {
        Self::try_parse()
    }

    /// Deprecated, `StructOpt::from_iter` replaced with `Parser::parse_from` (note the change in derives)
    #[deprecated(
        since = "3.0.0",
        note = "`StructOpt::from_iter` is replaced with `Parser::parse_from` (note the change in derives)"
    )]
    #[doc(hidden)]
    fn from_iter<I, T>(itr: I) -> Self
    where
        I: IntoIterator<Item = T>,
        T: Into<OsString> + Clone,
    {
        Self::parse_from(itr)
    }

    /// Deprecated, `StructOpt::from_iter_safe` replaced with `Parser::try_parse_from` (note the
    /// change in derives)
    #[deprecated(
        since = "3.0.0",
        note = "`StructOpt::from_iter_safe` is replaced with `Parser::try_parse_from` (note the change in derives)"
    )]
    #[doc(hidden)]
    fn from_iter_safe<I, T>(itr: I) -> Result<Self, Error>
    where
        I: IntoIterator<Item = T>,
        T: Into<OsString> + Clone,
    {
        Self::try_parse_from(itr)
    }
}

/// Create an [`Command`] relevant for a user-defined container.
///
/// Derived as part of [`Parser`].
pub trait CommandFactory: Sized {
    /// Build an [`Command`] that can instantiate `Self`.
    ///
    /// See [`FromArgMatches::from_arg_matches`] for instantiating `Self`.
    fn command<'help>() -> Command<'help> {
        #[allow(deprecated)]
        Self::into_app()
    }
    /// Deprecated, replaced with `CommandFactory::command`
    #[deprecated(since = "3.1.0", note = "Replaced with `CommandFactory::command")]
    fn into_app<'help>() -> Command<'help>;
    /// Build an [`Command`] that can update `self`.
    ///
    /// See [`FromArgMatches::update_from_arg_matches`] for updating `self`.
    fn command_for_update<'help>() -> Command<'help> {
        #[allow(deprecated)]
        Self::into_app_for_update()
    }
    /// Deprecated, replaced with `CommandFactory::command_for_update`
    #[deprecated(
        since = "3.1.0",
        note = "Replaced with `CommandFactory::command_for_update"
    )]
    fn into_app_for_update<'help>() -> Command<'help>;
}

/// Converts an instance of [`ArgMatches`] to a user-defined container.
///
/// Derived as part of [`Parser`], [`Args`], and [`Subcommand`].
pub trait FromArgMatches: Sized {
    /// Instantiate `Self` from [`ArgMatches`], parsing the arguments as needed.
    ///
    /// Motivation: If our application had two CLI options, `--name
    /// <STRING>` and the flag `--debug`, we may create a struct as follows:
    ///
    #[cfg_attr(not(feature = "derive"), doc = " ```ignore")]
    #[cfg_attr(feature = "derive", doc = " ```no_run")]
    /// struct Context {
    ///     name: String,
    ///     debug: bool
    /// }
    /// ```
    ///
    /// We then need to convert the `ArgMatches` that `clap` generated into our struct.
    /// `from_arg_matches` serves as the equivalent of:
    ///
    #[cfg_attr(not(feature = "derive"), doc = " ```ignore")]
    #[cfg_attr(feature = "derive", doc = " ```no_run")]
    /// # use clap::ArgMatches;
    /// # struct Context {
    /// #   name: String,
    /// #   debug: bool
    /// # }
    /// impl From<ArgMatches> for Context {
    ///    fn from(m: ArgMatches) -> Self {
    ///        Context {
    ///            name: m.value_of("name").unwrap().to_string(),
    ///            debug: m.is_present("debug"),
    ///        }
    ///    }
    /// }
    /// ```
    fn from_arg_matches(matches: &ArgMatches) -> Result<Self, Error>;

    /// Assign values from `ArgMatches` to `self`.
    fn update_from_arg_matches(&mut self, matches: &ArgMatches) -> Result<(), Error>;
}

/// Parse a set of arguments into a user-defined container.
///
/// Implementing this trait lets a parent container delegate argument parsing behavior to `Self`.
/// with:
/// - `#[clap(flatten)] args: ChildArgs`: Attribute can only be used with struct fields that impl
///   `Args`.
/// - `Variant(ChildArgs)`: No attribute is used with enum variants that impl `Args`.
///
/// See the
/// [derive reference](https://github.com/clap-rs/clap/blob/v3.1.6/examples/derive_ref/README.md)
/// for attributes and best practices.
///
/// **NOTE:** Deriving requires the `derive` feature flag
///
/// # Example
///
#[cfg_attr(not(feature = "derive"), doc = " ```ignore")]
#[cfg_attr(feature = "derive", doc = " ```")]
/// #[derive(clap::Parser)]
/// struct Args {
///     #[clap(flatten)]
///     logging: LogArgs,
/// }
///
/// #[derive(clap::Args)]
/// struct LogArgs {
///     #[clap(long, short = 'v', parse(from_occurrences))]
///     verbose: i8,
/// }
/// ```
pub trait Args: FromArgMatches + Sized {
    /// Append to [`Command`] so it can instantiate `Self`.
    ///
    /// See also [`CommandFactory`].
    fn augment_args(cmd: Command<'_>) -> Command<'_>;
    /// Append to [`Command`] so it can update `self`.
    ///
    /// This is used to implement `#[clap(flatten)]`
    ///
    /// See also [`CommandFactory`].
    fn augment_args_for_update(cmd: Command<'_>) -> Command<'_>;
}

/// Parse a sub-command into a user-defined enum.
///
/// Implementing this trait lets a parent container delegate subcommand behavior to `Self`.
/// with:
/// - `#[clap(subcommand)] field: SubCmd`: Attribute can be used with either struct fields or enum
///   variants that impl `Subcommand`.
/// - `#[clap(flatten)] Variant(SubCmd)`: Attribute can only be used with enum variants that impl
///   `Subcommand`.
///
/// See the
/// [derive reference](https://github.com/clap-rs/clap/blob/v3.1.6/examples/derive_ref/README.md)
/// for attributes and best practices.
///
/// **NOTE:** Deriving requires the `derive` feature flag
///
/// # Example
///
#[cfg_attr(not(feature = "derive"), doc = " ```ignore")]
#[cfg_attr(feature = "derive", doc = " ```")]
/// #[derive(clap::Parser)]
/// struct Args {
///     #[clap(subcommand)]
///     action: Action,
/// }
///
/// #[derive(clap::Subcommand)]
/// enum Action {
///     Add,
///     Remove,
/// }
/// ```
pub trait Subcommand: FromArgMatches + Sized {
    /// Append to [`Command`] so it can instantiate `Self`.
    ///
    /// See also [`CommandFactory`].
    fn augment_subcommands(cmd: Command<'_>) -> Command<'_>;
    /// Append to [`Command`] so it can update `self`.
    ///
    /// This is used to implement `#[clap(flatten)]`
    ///
    /// See also [`CommandFactory`].
    fn augment_subcommands_for_update(cmd: Command<'_>) -> Command<'_>;
    /// Test whether `Self` can parse a specific subcommand
    fn has_subcommand(name: &str) -> bool;
}

/// Parse arguments into enums.
///
/// When deriving [`Parser`], a field whose type implements `ArgEnum` can have the attribute
/// `#[clap(arg_enum)]` which will
/// - Call [`Arg::possible_values`][crate::Arg::possible_values]
/// - Allowing using the `#[clap(default_value_t)]` attribute without implementing `Display`.
///
/// See the
/// [derive reference](https://github.com/clap-rs/clap/blob/v3.1.6/examples/derive_ref/README.md)
/// for attributes and best practices.
///
/// **NOTE:** Deriving requires the `derive` feature flag
///
/// # Example
///
#[cfg_attr(not(feature = "derive"), doc = " ```ignore")]
#[cfg_attr(feature = "derive", doc = " ```")]
/// #[derive(clap::Parser)]
/// struct Args {
///     #[clap(arg_enum)]
///     level: Level,
/// }
///
/// #[derive(clap::ArgEnum, Clone)]
/// enum Level {
///     Debug,
///     Info,
///     Warning,
///     Error,
/// }
/// ```
pub trait ArgEnum: Sized + Clone {
    /// All possible argument values, in display order.
    fn value_variants<'a>() -> &'a [Self];

    /// Parse an argument into `Self`.
    fn from_str(input: &str, ignore_case: bool) -> Result<Self, String> {
        Self::value_variants()
            .iter()
            .find(|v| {
                v.to_possible_value()
                    .expect("ArgEnum::value_variants contains only values with a corresponding ArgEnum::to_possible_value")
                    .matches(input, ignore_case)
            })
            .cloned()
            .ok_or_else(|| format!("Invalid variant: {}", input))
    }

    /// The canonical argument value.
    ///
    /// The value is `None` for skipped variants.
    fn to_possible_value<'a>(&self) -> Option<PossibleValue<'a>>;
}

impl<T: Parser> Parser for Box<T> {
    fn parse() -> Self {
        Box::new(<T as Parser>::parse())
    }

    fn try_parse() -> Result<Self, Error> {
        <T as Parser>::try_parse().map(Box::new)
    }

    fn parse_from<I, It>(itr: I) -> Self
    where
        I: IntoIterator<Item = It>,
        It: Into<OsString> + Clone,
    {
        Box::new(<T as Parser>::parse_from(itr))
    }

    fn try_parse_from<I, It>(itr: I) -> Result<Self, Error>
    where
        I: IntoIterator<Item = It>,
        It: Into<OsString> + Clone,
    {
        <T as Parser>::try_parse_from(itr).map(Box::new)
    }
}

#[allow(deprecated)]
impl<T: CommandFactory> CommandFactory for Box<T> {
    fn into_app<'help>() -> Command<'help> {
        <T as CommandFactory>::into_app()
    }
    fn into_app_for_update<'help>() -> Command<'help> {
        <T as CommandFactory>::into_app_for_update()
    }
}

impl<T: FromArgMatches> FromArgMatches for Box<T> {
    fn from_arg_matches(matches: &ArgMatches) -> Result<Self, Error> {
        <T as FromArgMatches>::from_arg_matches(matches).map(Box::new)
    }
    fn update_from_arg_matches(&mut self, matches: &ArgMatches) -> Result<(), Error> {
        <T as FromArgMatches>::update_from_arg_matches(self, matches)
    }
}

impl<T: Args> Args for Box<T> {
    fn augment_args(cmd: Command<'_>) -> Command<'_> {
        <T as Args>::augment_args(cmd)
    }
    fn augment_args_for_update(cmd: Command<'_>) -> Command<'_> {
        <T as Args>::augment_args_for_update(cmd)
    }
}

impl<T: Subcommand> Subcommand for Box<T> {
    fn augment_subcommands(cmd: Command<'_>) -> Command<'_> {
        <T as Subcommand>::augment_subcommands(cmd)
    }
    fn augment_subcommands_for_update(cmd: Command<'_>) -> Command<'_> {
        <T as Subcommand>::augment_subcommands_for_update(cmd)
    }
    fn has_subcommand(name: &str) -> bool {
        <T as Subcommand>::has_subcommand(name)
    }
}

fn format_error<I: CommandFactory>(err: crate::Error) -> crate::Error {
    let mut cmd = I::command();
    err.format(&mut cmd)
}
