Skip to content

Allow merge bool v2#2137

Merged
thomas-zahner merged 12 commits into
lycheeverse:masterfrom
cristiklein:allow-merge-bool-v2
Apr 14, 2026
Merged

Allow merge bool v2#2137
thomas-zahner merged 12 commits into
lycheeverse:masterfrom
cristiklein:allow-merge-bool-v2

Conversation

@cristiklein

Copy link
Copy Markdown
Contributor

Fixes #2051

Makes all boolean CLI flags mergeable by converting them to Option<bool>. For example, this allows lychee.toml to specify accept_timeout = true and calling lychee --accept-timeout=false with the combined effect being that timeouts are not accepted.

This PR uses the following overall patterns:

  1. Boolean CLI flags are converted to Option<bool>.
  2. Clap is configured with:
    #[arg(
        long,
        default_missing_value = "true", // `--flag` is equivalent to `--flag=true`
        num_args = 0..=1, // allow `--flag` with no value
        require_equals = true, // ensures `--flag parameter` is interpreted as `--flag=true parameter`
    )]
    
  3. Field is made private and accessed via a getter to ensure all defaults are in config/mod.rs.

Decisions to be challenged:

  1. The #[arg(...)] macro invokation looks tedious and repetative. I opted against factoring this into a macro, since the solution felt beyond my ability.
  2. Should all boolean CLI flags be converted to Option<bool>, even e.g. --dump? I opted for this, since I felt this minimizes astonishment for the user.
  3. Why not --no-flag, like ripgrep? This feels poorly supported in Clap. Furthermore, many CLI seems to move towards --flag=false.

@thomas-zahner

thomas-zahner commented Apr 13, 2026

Copy link
Copy Markdown
Member
  1. I agree, it's very verbose and introduces boilerplate code with the getters. Creating a attribute macro like #[bool_arg] unfortunately would require us to create a new crate as shown in the docs, I think this is a current language limitation. Clap could also be configured programmatically instead of using the macros but it probably makes no sense to combine the current attribute approach with a programmatic approach. (LycheeOptions::command().arg(clap::Arg::new("some-bool-arg"))) So it seems like there is no simple elegant solution.
  2. I agree that we should treat all bool flags the same.
  3. Hmm, from a user-perspective I actually like this PR. I think it's easier to have a single arg which can be set to true/false instead of having different args which affect the same boolean value.

@katrinafyi @mre What are your thoughts?

Comment thread lychee-bin/src/config/mod.rs Outdated

@katrinafyi katrinafyi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I agree with @thomas-zahner on the discussion points.

The PR looks reasonable, it's basically exactly what I expected for this change. Somewhat large, but fairly rote.

Thanks for tackling it!

Comment thread README.md Outdated
@cristiklein

Copy link
Copy Markdown
Contributor Author

Thanks @katrinafyi and @thomas-zahner for your feedback. I believe I addressed all your comments. PTAL.

Comment thread README.md
@katrinafyi

Copy link
Copy Markdown
Member

Re the big #[arg] macro attribute, I asked the question in the Rust discord and got this answer (among other, much more complicated macro answers):

I think you could define an extension trait on Arg and call that with arg() syntax

I have no idea if it actually does but can't imagine why it wouldn't

If you want, it's worth a try @cristiklein. The idea is that the #[arg] macro just transforms itself into the builder API. For example, #[arg(num_args = 1] is transformed into the builder syntax as:

arg.num_args(1)

So, if you add an extension method on Arg, it might be able to be invoked the macro.

pub trait ArgExt  {
    fn optional_with_bool_value(self) -> Arg {
        // apply the needed options using the builder API
    }
}

// then later,
#[arg(optional_with_bool_value)]

@cristiklein cristiklein force-pushed the allow-merge-bool-v2 branch from e5126ac to 5931004 Compare April 14, 2026 14:19
@cristiklein

Copy link
Copy Markdown
Contributor Author

Great find @katrinafyi and works like a charm. See 2521d07. Help message didn't change at all during refactoring.

@mre I found a few ways to beautify the help message. For example:

            .value_name("false|true") // Set the value name for help messages
            .hide_possible_values(true)
            .default_value("false")

Leads to:

      --suggest[=<false|true>]
          Suggest link replacements for broken links, using a web archive. The web archive can be specified with `--archive`
          
          [default: false]

See 5931004

@cristiklein

Copy link
Copy Markdown
Contributor Author

Arrrgggh, sorry, I pressed ENTER too fast. .default_value("false") has the unintended effect that missing flags become false and not None, which defies the whole point of this PR.

So the best I could obtain was:

            .value_name("false|true") // Set the value name for help messages
            .hide_possible_values(true)

Which leads to:

      --suggest[=<false|true>]
          Suggest link replacements for broken links, using a web archive. The web archive can be specified with `--archive`

@katrinafyi

Copy link
Copy Markdown
Member

Great find @katrinafyi and works like a charm.

Incredible! Honestly I had no idea if it would work. Thanks for doing that, I think it's a bit cleaner :)

@thomas-zahner

Copy link
Copy Markdown
Member

@cristiklein @katrinafyi Thank you, that's quite a lot less boilerplate code and quite an improvement!

Comment thread lychee-bin/src/config/mod.rs
Comment thread lychee-bin/tests/cli.rs Outdated
@cristiklein

cristiklein commented Apr 14, 2026 via email

Copy link
Copy Markdown
Contributor Author

@thomas-zahner thomas-zahner merged commit ee3618a into lycheeverse:master Apr 14, 2026
7 checks passed
@mre mre mentioned this pull request Apr 13, 2026
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

Successfully merging this pull request may close these issues.

Unable to override bools in merge chain

4 participants