pareg_proc 0.13.0

This library contains proc macros for the pareg library.
Documentation
use proc_macro::TokenStream;

/// Derives the [`pareg_core::FromArg`] macro for an enum. The enum must not be
/// generic and the enum members cannot contain any  fields.
///
/// The parsing is case insensitive.
///
/// The arguments for the `arg` attribute must be lowercase to match properly.
///
/// Options on enum:
/// - `exact`: Dont infer any name and dont do any case conversions.
///   Everything must match the arguments specified in `arg`. If there is
///   nothing specified for a variant, that variant cannot be created by
///   parsing.
/// - `split = <char>`: For variants with values, split the value from the
///   variant name with the given character literal.
///
/// Options on variants:
/// - `exact`: This variant will have no implicit name.
/// - `split = <char>`: For variants with values, split the value from the
///   variant name with the given character literal.
/// - `default`, `default = <expr>`: Specify the default value for variant with
///   value.
/// - `parser = <expr>`: Callable that parses the value.
///
/// # Examples
/// ```
/// use pareg_core::{self as pareg, FromArg};
/// use pareg_proc::FromArg;
///
/// #[derive(FromArg, PartialEq, Debug)]
/// enum ColorMode {
///     Auto,
///     #[arg("yes", "ok")]
///     Always,
///     #[arg("no")]
///     Never,
/// }
///
/// assert_eq!(ColorMode::Auto, ColorMode::from_arg("auto").unwrap());
/// assert_eq!(ColorMode::Always, ColorMode::from_arg("Always").unwrap());
/// assert_eq!(ColorMode::Never, ColorMode::from_arg("NEVER").unwrap());
/// assert_eq!(ColorMode::Always, ColorMode::from_arg("yes").unwrap());
/// assert_eq!(ColorMode::Always, ColorMode::from_arg("oK").unwrap());
/// assert_eq!(ColorMode::Never, ColorMode::from_arg("NO").unwrap());
/// assert_eq!(ColorMode::Auto, ColorMode::from_arg("AuTo").unwrap());
/// ```
#[proc_macro_derive(FromArg, attributes(arg))]
pub fn derive_from_arg(item: TokenStream) -> TokenStream {
    pareg_core::proc::result_to_token_stream(
        pareg_core::proc::derive_from_arg(item.into()),
    )
    .into()
}

/// Derives the [`pareg_core::FromArgs`] trait.
///
/// ## `#[from_args]` on field
/// - `<string literal>`: variant for the given field.
/// - `default`: The field is not required. Use the [`Default`] implementation
///   for default value. When used with `collect` and `option`, the type inside
///   the [`Option`] must implement [`Default`]. Otherwise ignored when used
///   with `option`.
/// - `default = <expr>`: same as `default` but uses the given expression for
///   default value instead of the [`Default`] implementation. When used with
///   `collect` and `option`, the expression has to produce the type inside the
///   [`Option`] and not the option itself. Otherwise ignored when used with
///   `option`.
/// - `flag`: The field is type that implements `From<bool>` which will be set
///   to `true.into()` if the flag is present. When used with `collect`, the
///   type is expected to be collection of that type. When used with `option`
///   the field has to be option of that type.
/// - `positional`: Specifies that this argument may be set by any unknown
///   argument. Positional arguments are filled in the order that they are
///   present in the source code. Positional arguments can also have names
///   specified to signify option to specify them explicitly. In addition,
///   multiple positional arguments may have the same name. In that case that
///   name will fill the first empty positional argument with that name.
/// - `collect`: Specifies that this argument is expected to be present
///   multiple times and all occurences will be collected into a collection.
///   The type has to have method `extend` available with the same sematics as
///   that of the trait [`Extend`]. The type must implement [`Default`] or the
///   default value must be specified with `default = <expr>`. This default is
///   representing empty collection. If `collect` is combined with
///   `positional`, and there are positional fields after collect, the
///   positional fields after this one will never be filled as positional as
///   the collection will consume all positional fields and never move to the
///   next field. When used with `opition`, the type inside the option is the
///   collection and it is required to implement method `.is_empty()` which
///   checks whether the collection is empty or not by returning [`bool`]. This
///   method will decide if the result is the collection or [`None`].
/// - `collect = <range>`: Same as collect. This will also enable verification
///   that the number of items is within the given range. `<range>` may be any
///   expression for which `(<range>).contains(&field.len())` is valid and
///   returns [`bool`] where <field> is variable of the type of this field.
///   This is valid for example for standard ranges (e.g. `2..`) or arrays
///   (e.g. `[2]`), if the collection has method `len` which returns the number
///   of elements as [`usize`]. This range limit doesn't affect the behaviour
///   of combination of `positional` and `collect` (collect will consume all
///   remaining positional fields no matter the limitation in `<range>`).
/// - `no_rewrite`: Decides how repeating arguments are handled. If set, this
///   field will throw error when it would be set more than once. By default
///   the action is decided by attribute on the `FromArgs` type of which this
///   field is part, which is by default set to overwrite the old value. This
///   is ignored by fields with `collect`. This doesn't affect positional
///   arguments.
/// - `rewrite`: The reverse of `no_rewrite`. This us useful to allow
///   owerwriting the default set by the `FromArgs` type of which is this
///   field.
/// - `option`: The field type is option. The option will be set to a value if
///   the argument is present and otherwise it will be [`None`].
/// - `check = <expr>`: If the field is set, the condition in `<expr>` is
///   checked. If the condition is `false` an error is emited. This field is
///   available as not option reference for this condition and other fields are
///   available either as options or as the fields themself depending on the
///   field configuration. The condition is evaluated only after all arguments
///   have been successfully parsed.
/// - `otherwise = <cond>`: If the field is not set, the given condition must
///   be true. If it is not true, the parsing will result in error.
/// - `conflict = [<fields>]`: specifies that the fields are in conflict with
///   this field. If this field is set and at least one of the given fields is
///   also set, it will produce error.
/// - `require = [<fields>]`: specifies that if this field is set, all of the
///   given fields have to be also set. If at least one of them is not set,
///   parsing will result in error.
///
/// ## `#[from_args]` on the type
/// - `match start { <arms> }`: custom match arms that will be before the arms
///   for the fields. All fields are accesible with their name, but they may be
///   option of that type instead of that type itself depending on the
///   configuration of the field.
/// - `match end { <arms> }`: same as `match start` but places the arms after
///   the arms for fields.
/// - `positional_guard`: if present, enables guarding of positional arguments.
///   This means that positional arguments starting with `-` are rejected as
///   unknown argument.
/// - `no_rewrite`: Decides how repeating arguments are handled. If set, fields
///   will throw error when they would be set more than once. By default,
///   rewrites are allowed and the latest value is used. This is ignored by
///   fields with `collect`. This doesn't affect positional arguments.
/// - `check = <expr>`: Checks the given condition after all arguments have
///   been parsed and their conditions succeeded. If the condition is `false`,
///   an error is emited.
/// - `conflict = [<fields>]`: Specify that the given fields are mutually in
///   conflict. This means that only one of them may be set. If more of them
///   are set, it will result in error.
/// - `require = [<fields>]`: Specifies that the given fields have to be set
///   together. If some of them is set but not all, parsing will result in
///   error.
///
/// # Example
/// ```
/// use std::path::PathBuf;
/// use pareg_core::{self as pareg, Pareg};
/// use pareg_proc::FromArgs;
///
/// #[derive(FromArgs)]
/// #[from_args(match start { "-h" | "-?" | "--help" => println!("help") })]
/// struct Args {
///     #[from_args("-o", "--output", default = "output.png".into())]
///     output: PathBuf,
///     #[from_args("-v", "--verbose", flag, default)]
///     verbose: bool,
/// }
///
/// let mut args = Pareg::new(vec!["-o", "test.png"]);
/// let parsed: Args = args.next_sub().unwrap();
///
/// assert_eq!(parsed.output, PathBuf::from("test.png"));
/// assert_eq!(parsed.verbose, false);
///
/// let mut args = Pareg::new(vec!["-v"]);
/// let parsed: Args = args.next_sub().unwrap();
///
/// assert_eq!(parsed.output, PathBuf::from("output.png"));
/// assert_eq!(parsed.verbose, true);
///
/// let mut args = Pareg::new(vec!["--lol"]);
///
/// assert!(args.next_sub::<Args>().is_err());
/// ```
#[proc_macro_derive(FromArgs, attributes(from_args))]
pub fn derive_from_args(item: TokenStream) -> TokenStream {
    pareg_core::proc::result_to_token_stream(
        pareg_core::proc::derive_from_args(item.into()),
    )
    .into()
}

/// This macro can be tought of as opposite of [`write!`] or as something like
/// `fscanf` in C.
///
/// As arguments, takes reader to parse, format string and than arguments to
/// which result will be written.
///
/// The format string can contain format strings for the specific arguments
/// after `:`. The format is `CTS..ER` where:
/// - `CT` is optional trim mode.
///     - `C` is optional character to trim. If not present, trim whitespace.
///     - `T` is the side from which to trim. It is the opposite of alignment
///       in format functions:
///         - `<` trim from right.
///         - `>` trim from left.
///         - `^` trim from both sides.
/// - `S..E` is optional length range. The parsing function should use at least
///   `S` and at most `E` characters.
///     - `S`, `E` or both may be omited. In that case `S` will be same as `0`
///       and `E` will be same as max length.
///     - If only `S` is present (without `..E`), it is same as `S..S`.
/// - `R` is optional radix for conversion. It may be:
///     - `D` as decimal.
///     - `X` as hexadecimal.
///     - `O` as octal.
///
/// Anything else after the format is custom format string for the given type.
/// Nothing forces the parsing function to follow the standart formatting and
/// no format is invalid.
///
/// # Returns
/// [`pareg_core::Result<()>`] that indicates success or failure.
///
/// # Example
///
/// ```rust
/// use std::str::FromStr;
/// use pareg_core::{self as pareg, ArgError, check};
/// use pareg_proc::parsef;
///
/// #[derive(Debug, Default, PartialEq)]
/// struct Address {
///     adr: (u8, u8, u8, u8),
///     mask: u8,
/// }
///
/// impl FromStr for Address {
///     type Err = ArgError;
///
///     fn from_str(s: &str) -> Result<Self, Self::Err> {
///         let mut res = Self::default();
///         parsef!(
///             &mut s.into(),
///             "{}.{}.{}.{}/{}",
///             &mut res.adr.0,
///             &mut res.adr.1,
///             &mut res.adr.2,
///             &mut res.adr.3,
///             &mut check::InRange(&mut res.mask, 0..33),
///         )?;
///
///         Ok(res)
///     }
/// }
///
/// assert_eq!(
///     Address::from_str("127.5.20.1/24").unwrap(),
///     Address {
///         adr: (127, 5, 20, 1),
///         mask: 24
///     }
/// );
/// ```
#[proc_macro]
pub fn parsef(args: TokenStream) -> TokenStream {
    pareg_core::proc::proc_parsef(args.into(), false).into()
}

/// Simmilar to [`parsef!`], but doesn't expect to parse the whole string, but
/// only start of the string. It macro can be tought of as opposite of
/// [`write!`] or as something like `fscanf` in C.
///
/// As arguments, takes reader to parse, format string and than arguments to
/// which result will be written.
///
/// The format string can contain format strings for the specific arguments
/// after `:`. The format is `CTS..ER` where:
/// - `CT` is optional trim mode.
///     - `C` is optional character to trim. If not present, trim whitespace.
///     - `T` is the side from which to trim. It is the opposite of alignment
///       in format functions:
///         - `<` trim from right.
///         - `>` trim from left.
///         - `^` trim from both sides.
/// - `S..E` is optional length range. The parsing function should use at least
///   `S` and at most `E` characters.
///     - `S`, `E` or both may be omited. In that case `S` will be same as `0`
///       and `E` will be same as max length.
///     - If only `S` is present (without `..E`), it is same as `S..S`.
/// - `R` is optional radix for conversion. It may be:
///     - `D` as decimal.
///     - `X` as hexadecimal.
///     - `O` as octal.
///
/// # Returns
/// `pareg_core::Result<Option<pareg_core::ArgError>>` that indicates success
/// or failure. On success, if the string was not fully parsed also returns
/// error that should be raised if it was expected to parse more of the string.
///
/// # Example
/// ```rust
/// use pareg_core::{self as pareg, ArgError, check};
/// use pareg_proc::parsef_part;
///
/// #[derive(Debug, Default, PartialEq)]
/// struct Address {
///     adr: (u8, u8, u8, u8),
///     mask: u8,
/// }
///
/// let mut adr = Address::default();
/// let res = parsef_part!(
///     &mut "127.5.20.1/24some other stuff".into(),
///     "{}.{}.{}.{}/{}",
///     &mut adr.adr.0,
///     &mut adr.adr.1,
///     &mut adr.adr.2,
///     &mut adr.adr.3,
///     &mut check::InRange(&mut adr.mask, 0..33),
/// );
/// assert!(res.is_ok());
///
/// assert_eq!(
///     adr,
///     Address {
///         adr: (127, 5, 20, 1),
///         mask: 24
///     }
/// );
/// ```
#[proc_macro]
pub fn parsef_part(args: TokenStream) -> TokenStream {
    pareg_core::proc::proc_parsef(args.into(), true).into()
}