Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automate the generation of negation flags #142

Closed
ducaale opened this issue May 14, 2021 · 4 comments
Closed

Automate the generation of negation flags #142

ducaale opened this issue May 14, 2021 · 4 comments

Comments

@ducaale
Copy link
Owner

ducaale commented May 14, 2021

Currently, we have a NEGATION_FLAGS list that needs to be manually updated whenever we add a new flag. A quick search reveals that it's possible to extract field names from a struct using a macro rule. I think this can be temporarily used to generate the said list until we can come up with a proper derive macro solution.

@ducaale
Copy link
Owner Author

ducaale commented May 22, 2021

Actually, I think this gets tricky when field names are in snake case but the negated flags need to be in kebab case

macro_rules! with_negation_flags {
    (
        $(#[$($struct_meta:meta),+])* $struct_vis:vis struct $struct_name:ident {
            $(
                $(#[$($field_meta:meta),+])*
                $field_vis:vis $field_name:ident: $field_type:ty
            ),*
            $(,)?
        }
    ) => {
        $(#[$($struct_meta),+])* $struct_vis struct $struct_name {
            $(
                $(#[$($field_meta),+])*
                $field_vis $field_name: $field_type
            ),*
        }

        impl $struct_name {
            #[allow(dead_code)]
            fn negated_flags() -> Vec<&'static str> {
                // can we construct kebab cased string with static lifetime?
                vec![$(concat!("--no-", stringify!($field_name))),*]
            }
        }
    }
}

with_negated_flags! {
    /// xh is a friendly and fast tool for sending HTTP requests.
    ///
    /// It reimplements as much as possible of HTTPie's excellent design.
    #[derive(StructOpt, Debug)]
    #[structopt(name = "xh")]
    struct Cli {
        /// Create, or reuse and update a session.
        #[structopt(long, value_name = "FILE")]
        session_read_only: Option<String>
    }
}

We could explicitly annotate each field with clap's name attribute or come up with a less hacky solution using a proc macro.

@blyxxyz
Copy link
Collaborator

blyxxyz commented May 23, 2021

I think I like a macro less than a simple list with no hidden parts. There's a little manual work, but it adds up to less work than the work needed to automate it, and it's easier to understand.

Implementing clap-rs/clap#815 might be a better solution. It could be less fragile and benefit more people.

@ducaale
Copy link
Owner Author

ducaale commented May 23, 2021

I agree that implementing clap-rs/clap#815 would be the best solution. However, it doesn't seem to be a trivial thing. See clap-rs/clap#1041 (comment) and clap-rs/clap#1728.

Lifetimes are less of a problem once macros are in use so I wonder if this could be supported in structopt? Although I am not sure what is their policy regarding compatibility with clap-rs.

#[derive(StructOpt, Debug)]
#[structopt(name = "xh")]
struct Cli {
    /// Create, or reuse and update a session.
    #[structopt(long, value_name = "FILE", overridable)]
    session_read_only: Option<String>
}

@ducaale
Copy link
Owner Author

ducaale commented Feb 15, 2022

This has been addressed in #234

@ducaale ducaale closed this as completed Feb 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants