From e6e24ca6a0d7da396db84c825586394aa003b05f Mon Sep 17 00:00:00 2001 From: Jakub Jirutka Date: Mon, 9 Jan 2023 20:42:36 +0100 Subject: [PATCH] Allow to disable requirement for uncapitalized descriptions This strict requirement makes sense only for programs designed for Fuchsia, no other system has this convention. This commit adds a type-level attribute `lax_description` that disables the option descriptions validation when present. --- argh/examples/simple_example.rs | 5 +++++ argh/src/lib.rs | 18 ++++++++++++++++++ argh/tests/lib.rs | 15 +++++++++++++++ argh_derive/src/lib.rs | 2 +- argh_derive/src/parse_attrs.rs | 23 ++++++++++++++++++----- 5 files changed, 57 insertions(+), 6 deletions(-) diff --git a/argh/examples/simple_example.rs b/argh/examples/simple_example.rs index d977495..ade3212 100644 --- a/argh/examples/simple_example.rs +++ b/argh/examples/simple_example.rs @@ -6,7 +6,12 @@ use {argh::FromArgs, std::fmt::Debug}; #[derive(FromArgs, PartialEq, Debug)] /// Top-level command. +#[argh(lax_descriptions)] struct TopLevel { + /// Foo + #[argh(switch)] + common: bool, + #[argh(subcommand)] nested: MySubCommandEnum, } diff --git a/argh/src/lib.rs b/argh/src/lib.rs index 4fb6ff5..56812d1 100644 --- a/argh/src/lib.rs +++ b/argh/src/lib.rs @@ -313,6 +313,24 @@ //! real_first_arg: String, //! } //! ``` +//! +//! Programs that are not designed to conform to the Fuchsia commandline tools +//! specification may find the requirement for descriptions to begin with a +//! lowercase letter too restrictive. This can be disabled by adding the +//! `lax_descriptions` attribute to the type: +//! +//! ```rust,no_run +//! use argh::FromArgs; +//! +//! #[derive(FromArgs)] +//! #[argh(lax_descriptions)] +//! /// Reach new heights. +//! struct GoUp { +//! /// Now you can capitalize descriptions. +//! #[argh(switch, short = 'j')] +//! jump: bool, +//! } +//! ``` #![deny(missing_docs)] diff --git a/argh/tests/lib.rs b/argh/tests/lib.rs index 713c73d..9ac65ed 100644 --- a/argh/tests/lib.rs +++ b/argh/tests/lib.rs @@ -285,6 +285,21 @@ struct DescriptionStartsWithInitialism { x: u8, } +/// Test that descriptions can start with any case when +/// the type attribute `lax_descriptions` is specified. +#[derive(FromArgs)] +#[argh(lax_descriptions)] +#[allow(unused)] +struct LaxDescription { + /// Don't be so strict. + #[argh(option)] + x: u8, + + /// don't be so strict. + #[argh(option)] + y: u8, +} + #[test] fn default_number() { #[derive(FromArgs)] diff --git a/argh_derive/src/lib.rs b/argh_derive/src/lib.rs index 94cc437..82b4bb4 100644 --- a/argh_derive/src/lib.rs +++ b/argh_derive/src/lib.rs @@ -259,7 +259,7 @@ fn impl_from_args_struct( .named .iter() .filter_map(|field| { - let attrs = FieldAttrs::parse(errors, field); + let attrs = FieldAttrs::parse(errors, field, type_attrs); StructField::new(errors, field, attrs) }) .collect(); diff --git a/argh_derive/src/parse_attrs.rs b/argh_derive/src/parse_attrs.rs index 04dcbdd..7f1866b 100644 --- a/argh_derive/src/parse_attrs.rs +++ b/argh_derive/src/parse_attrs.rs @@ -61,7 +61,7 @@ pub struct Description { } impl FieldAttrs { - pub fn parse(errors: &Errors, field: &syn::Field) -> Self { + pub fn parse(errors: &Errors, field: &syn::Field, type_attrs: &TypeAttrs) -> Self { let mut this = Self::default(); for attr in &field.attrs { @@ -160,8 +160,10 @@ impl FieldAttrs { _ => {} } - if let Some(d) = &this.description { - check_option_description(errors, d.content.value().trim(), d.content.span()); + if !type_attrs.lax_descriptions { + if let Some(d) = &this.description { + check_option_description(errors, d.content.value().trim(), d.content.span()); + } } this @@ -298,6 +300,7 @@ pub struct TypeAttrs { pub examples: Vec, pub notes: Vec, pub error_codes: Vec<(syn::LitInt, syn::LitStr)>, + pub lax_descriptions: bool, } impl TypeAttrs { @@ -345,13 +348,15 @@ impl TypeAttrs { if let Some(ident) = errors.expect_meta_word(meta).and_then(|p| p.get_ident()) { this.parse_attr_subcommand(errors, ident); } + } else if name.is_ident("lax_descriptions") { + this.lax_descriptions = true; } else { errors.err( &meta, concat!( "Invalid type-level `argh` attribute\n", "Expected one of: `description`, `error_code`, `example`, `name`, ", - "`note`, `subcommand`", + "`note`, `subcommand`, `lax_descriptions`", ), ); } @@ -566,7 +571,15 @@ fn parse_attr_description(errors: &Errors, m: &syn::MetaNameValue, slot: &mut Op /// Checks that a `#![derive(FromArgs)]` enum has an `#[argh(subcommand)]` /// attribute and that it does not have any other type-level `#[argh(...)]` attributes. pub fn check_enum_type_attrs(errors: &Errors, type_attrs: &TypeAttrs, type_span: &Span) { - let TypeAttrs { is_subcommand, name, description, examples, notes, error_codes } = type_attrs; + let TypeAttrs { + is_subcommand, + name, + description, + examples, + notes, + error_codes, + lax_descriptions: _, + } = type_attrs; // Ensure that `#[argh(subcommand)]` is present. if is_subcommand.is_none() {