getargs-derive-macros 0.1.0

Internal implementation detail of getargs-derive. Not meant for direct use; depend on getargs-derive instead.
Documentation
mod command;
mod group;
mod help;
mod help_text;
mod parsing;
mod text;
mod version;

use super::attr::{Key, dispatch};
use super::doc::{long_doc, short_doc};
use super::errors::Errors;
use super::field::Alias;
pub(crate) use command::validate_command_name;
pub(crate) use group::GroupDecl;
use syn::{Attribute, Meta};

const COMMAND_KEYS: &[Key<RawContainer>] = &[
    Key::new("about", text::about),
    Key::new("after_help", help_text::after_help),
    Key::new("allow_negative_numbers", parsing::allow_negative_numbers),
    Key::new("before_help", help_text::before_help),
    Key::new("disable_help_flag", help::disable_help_flag),
    Key::new("group", group::group),
    Key::new("long_about", help_text::long_about),
    Key::new("max_term_width", help_text::max_term_width),
    Key::new("name", text::name),
    Key::new("version", version::version),
];

const VARIANT_KEYS: &[Key<RawContainer>] = &[
    Key::new("about", text::about),
    Key::new("after_help", help_text::after_help),
    Key::new("alias", command::alias),
    Key::new("aliases", command::aliases),
    Key::new("allow_negative_numbers", parsing::allow_negative_numbers),
    Key::new("before_help", help_text::before_help),
    Key::new("disable_help_flag", help::disable_help_flag),
    Key::new("group", group::group),
    Key::new("hide", command::hide),
    Key::new("long_about", help_text::long_about),
    Key::new("max_term_width", help_text::max_term_width),
    Key::new("name", text::name),
    Key::new("visible_alias", command::visible_alias),
    Key::new("visible_aliases", command::visible_aliases),
];

const NEWTYPE_KEYS: &[Key<RawContainer>] = &[
    Key::new("about", text::about),
    Key::new("alias", command::alias),
    Key::new("aliases", command::aliases),
    Key::new("hide", command::hide),
    Key::new("long_about", help_text::long_about),
    Key::new("name", text::name),
    Key::new("visible_alias", command::visible_alias),
    Key::new("visible_aliases", command::visible_aliases),
];

#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum Keys {
    Struct,
    Variant,
    Newtype,
}

impl Keys {
    fn keys(self) -> &'static [Key<RawContainer>] {
        match self {
            Keys::Struct => COMMAND_KEYS,
            Keys::Variant => VARIANT_KEYS,
            Keys::Newtype => NEWTYPE_KEYS,
        }
    }
}

pub(crate) struct ContainerModel {
    pub(crate) name: Option<String>,
    pub(crate) about: Option<String>,
    pub(crate) version: Option<Version>,
    pub(crate) disable_help_flag: bool,
    pub(crate) groups: Vec<GroupDecl>,
    pub(crate) long_about: Option<String>,
    pub(crate) before_help: Option<String>,
    pub(crate) after_help: Option<String>,
    pub(crate) max_term_width: Option<usize>,
    pub(crate) allow_negative_numbers: bool,
    pub(crate) aliases: Vec<Alias<String>>,
    pub(crate) hide: bool,
}

pub(crate) enum Version {
    Package,
    Literal(String),
}

#[derive(Default)]
struct RawContainer {
    name: Option<String>,
    about: Option<String>,
    version: Option<Version>,
    disable_help_flag: Option<()>,
    groups: Vec<GroupDecl>,
    long_about: Option<String>,
    before_help: Option<String>,
    after_help: Option<String>,
    max_term_width: Option<usize>,
    allow_negative_numbers: Option<bool>,
    aliases: Vec<Alias<String>>,
    hide: Option<()>,
}

pub(crate) fn parse_container(attrs: &[Attribute], keys: Keys) -> syn::Result<ContainerModel> {
    let mut raw = RawContainer::default();
    let mut errors = Errors::default();

    for attr in attrs {
        if !attr.path().is_ident("command") {
            continue;
        }
        if let Meta::Path(_) = attr.meta {
            errors.push(syn::Error::new_spanned(
                attr,
                "expected `#[command(...)]`, e.g. `#[command(name = \"tool\")]`",
            ));
            continue;
        }
        let result =
            attr.parse_nested_meta(|meta| dispatch(meta, "command", keys.keys(), &mut raw));
        if let Err(error) = result {
            errors.push(error);
        }
    }

    errors.finish(ContainerModel {
        name: raw.name,
        about: raw.about.or_else(|| short_doc(attrs)),
        version: raw.version,
        disable_help_flag: raw.disable_help_flag.is_some(),
        groups: raw.groups,
        long_about: raw.long_about.or_else(|| long_doc(attrs)),
        before_help: raw.before_help,
        after_help: raw.after_help,
        max_term_width: raw.max_term_width,
        allow_negative_numbers: raw.allow_negative_numbers.unwrap_or(false),
        aliases: raw.aliases,
        hide: raw.hide.is_some(),
    })
}