Code Monkey home page Code Monkey logo

clap-verbosity-flag's Introduction

clap

Command Line Argument Parser for Rust

Crates.io Crates.io License License Build Status Coverage Status Contributors

Dual-licensed under Apache 2.0 or MIT.

About

Create your command-line parser, with all of the bells and whistles, declaratively or procedurally.

For more details, see:

Sponsors

Gold

Silver

Bronze

Backer

clap-verbosity-flag's People

Contributors

btodell avatar dependabot-preview[bot] avatar dpc avatar edemir avatar epage avatar hadrieng2 avatar jalil-salame avatar killercup avatar liana-p avatar matthiasbeyer avatar mtilda avatar peter-lyons-kehl avatar pksunkara avatar renovate[bot] avatar wolfv avatar yoshuawuyts avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

clap-verbosity-flag's Issues

Question: why do we filter messages?

@killercup you're probably the right person to ask, but: why are we filtering messages in .setup_env_logger()?

I can imagine it being useful to sometimes log out trace messages from library code (e.g. embedded databases, etc.) Should we perhaps introduce a method / flag that doesn't filter?

set_verbose does not interact with help text

The new set_default mechanism (and the combination with --quiet) allows setting the default level to warn -- something I highly appreciate.

The help text of --verbose, however, is built earlier than the set_default is evaluated (much earlier, in fact -- at build time). This means that programs that use set_default with anything but the built-in default Error get inconsistent help messages, ie. always

By default, it'll only report errors. Passing -v one time also prints
warnings, -vv enables info logging, -vvv debug, and -vvvv trace.

and not, say

By default, it'll only report errors and warnings. Passing -v one time
enables info logging, -vv debug, and -vvv trace.

I'm not well enough versed in structopt to propose any thought-through way toward resolution. One possibility would to add a generic argument to Verbosity that'd indicate the default and serve as help text; could look like

#[derive(StructOpt, Debug)]
struct CliArgsWithVerbosity {
    ...,
    #[structopt(flatten)]
    verbose: clap_verbosity_flag::Verbosity<DefaultVerbosity=clap_verbosity_flag::DefaultWarning>,
}

and where the DefaultWarning struct provides something like

impl VerbosityDefault for DefaultWarning {
    const fn level() -> Option<Level> {
        Some(log::Level::Warn)
    }
    const fn text() -> &'static str {
        "By default, it'll only report errors and warnings [...]"
    }
}

but as said, I don't know whether using that text would be doable with structopt.

Can't be used as a global flag, together with sub-commands.

When using sub-commands it would be good idea to set this flag globally.

#[derive(Parser, Debug)]
pub(crate) struct Args {
  #[clap(subcommand)]
  command: Commands,
  #[clap(flatten, global = true)]
  verbose: Verbosity,
}

Above produces an error:

error: methods are not allowed for flattened entry
  --> src/cli/args.rs:24:10
   |
24 |   #[clap(flatten, global = true)]
   | 

Feature: different loggers, --logger and --pretty

I was wondering what a good approach would be to enable different loggers for production / development. I can't think of any clean approach for automatic switching, so probably the best way to go about it is to add some switches.

Example: HTTP Proxy

Say we were building an HTTP proxy in Rust, similar to Nginx. We would probably want machine-readable log output for production, but in development we would want something that is optimized for human beings.

Also depending on the organizations involved, different people might want to use different loggers. Some might want to run it through syslog, whereas others might prefer to ingest ndjson from stdin.

Proposal

I propose we add 2 new flags: one for selecting a log formatter, and one for choosing the "pretty printer" formatter.

USAGE:
    main [FLAGS] [OPTIONS]

FLAGS:
    -h, --help         Prints help information
    -V, --version      Prints version information
        --pretty       Log output in a human readable format
    -v, --verbosity    Pass many times for more log output

OPTIONS:
    -o, --output <format>   Control the formatting of the log output

Design Considerations

  • I'm not sure if --pretty should have a shorthand. If it does, it should probably be -P (uppercase) to not conflict with the commonly used -p/--port.
  • -o/--outfile is also commonly used as a shorthand for which file to write stdout output to. This could potentially conflict with applications that have opinions about where to store artifacts. -l/--logger (or similar) might have fewer potential conflicts, despite not being rooted in prior art (see below).

Prior Art

Related Discussions

Wrapping Up

I hope all of this makes sense. I'd be really curious to hear people's thoughts on this!

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Awaiting Schedule

These updates are awaiting their schedule. Click on a checkbox to get an update now.

  • chore(deps): Update Rust crate clap to v4.5.16
  • chore(deps): Update EmbarkStudios/cargo-deny-action action to v2

Detected dependencies

cargo
Cargo.toml
  • log 0.4.1
  • clap 4.0.0
  • clap 4.5.4
  • env_logger 0.11.3
  • tracing 0.1
  • tracing-subscriber 0.3
  • tracing-log 0.2
github-actions
.github/workflows/audit.yml
  • actions/checkout v4
  • actions-rs/audit-check v1
  • actions/checkout v4
  • EmbarkStudios/cargo-deny-action v1
.github/workflows/ci.yml
  • actions/checkout v4
  • Swatinem/rust-cache v2
  • actions/checkout v4
  • Swatinem/rust-cache v2
  • actions/checkout v4
  • actions/checkout v4
  • Swatinem/rust-cache v2
  • actions/checkout v4
  • Swatinem/rust-cache v2
  • actions/checkout v4
  • Swatinem/rust-cache v2
  • actions/checkout v4
  • Swatinem/rust-cache v2
  • github/codeql-action v3
  • actions/checkout v4
  • Swatinem/rust-cache v2
.github/workflows/committed.yml
  • actions/checkout v4
.github/workflows/pre-commit.yml
  • actions/checkout v4
  • actions/setup-python v5
  • pre-commit/action v3.0.1
.github/workflows/rust-next.yml
  • actions/checkout v4
  • Swatinem/rust-cache v2
  • actions/checkout v4
  • Swatinem/rust-cache v2
.github/workflows/spelling.yml
  • actions/checkout v4
regex
.github/workflows/ci.yml
  • STABLE 1.80
  • STABLE 1.80
  • STABLE 1.80

  • Check this box to trigger a request for Renovate to run again on this repository

Expose log level

There's currently no way to get the log level standalone. This means that this crate can currently only be used through the .setup_env_logger() method. It would probably be good to make the .log_level() method public, and have it return the level instead of a filter.

Serialize & Deserialize

There was a straightforward PR thrown up for discussion over at #65, effectively looking for a Serialize and Deserialize implementation for this library. The requests from @epage:

I think more of a case is needed for why a CLI flag needs serialization, particularly the fact that this was done in a raw way (separate verbose, quiet as u8s) - #65 (comment)

More design discussion is needed on this because exposing separate verbose and quiet fields that are u8 does not make sense. - #65 (comment)


Use Cases

Instrumentation

If an entire args object is being passed into tracing it is quite nice to have a Serialize so that derive works. I don't want to have to impl Serialize for Args for this reason. If I don't do some serialization then I end up with Debugand that is super-verbose.

use clap::Parser;
use serde::Serialize;
use svix_ksuid::{Ksuid, KsuidLike};
use tracing::instrument;

mod telemetry;

#[derive(Serialize, Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
    // #[command(flatten)]
    // verbose: clap_verbosity_flag::Verbosity,
    #[arg(short, long, default_value_t = 1)]
    count: u8,
}

impl Args {
    fn to_json(&self) -> String {
        return serde_json::to_string(&self).expect("");
    }
}

// Main is a very small setup shim, and then gets out of the way.
fn main() -> proc_exit::ExitResult {
    let id = Ksuid::new(None, None);

    telemetry::init(id);
    let args = Args::parse();

    let result = run(id, args);
    proc_exit::exit(result);
}

#[instrument(name = "run()", fields(args = args.to_json(), id = id.to_string()) ret, err)]
fn run(id: Ksuid, args: Args) -> proc_exit::ExitResult {
    proc_exit::Code::SUCCESS.ok()
}

Args From <Elsewhere>

This some-cli gui command spins up a server and opens a browser to an HTML page. Eventually that HTML page makes a network request back to the server, which contains a serialized form of the arguments that would have been passed in. That's going to be JSON (cause it's coming from the web), deserializing into Args (from the above example).

Starts the server:

$ some-cli gui

(Complicated configuration happens in a browser.)

Data from a "configuration finished!" network request passed back in to the CLI entry point:

let args: Args = serde_json::from_str(args)?;
let result = run(id, args);

Design Space

There are basically only two reasonable options here to represent configuration since it is a count.

As Separate u8s

exposing separate verbose and quiet fields that are u8 does not make sense

Given that we have -q and -v[vvv] exposed as separate args this separation doesn't seem particularly egregious. Further, since the struct can be constructed with clap_verbosity_flag::Verbosity::new(2, 4), any serialization that doesn't give you both of those values is lossy.

Being able to represent the error conditions (error: the argument '--quiet...' cannot be used with '--verbose...') that can be triggered by command line usage is reasonable—even though typically it would never get through ::parse().

As A Single i16

Since we have the conflicts_with lockout from the primary clap use case, we could choose to serialize them into a single value, in spite of it being lossy. Take a page from the log mapping, and do quiet as -(count).

An i16 gives us way more bits than we need, but there isn't an i9.

...
-4 => -qqqq
-3 => -qqq
-2 => -qq
-1 => -q
0 => None
1 => -v
2 => -vv
3 => -vvv
4 => -vvvv
...

This approach can't support { quiet: 4, verbosity: 4 } like a manually constructed struct can.


I'm in favor of the separate u8s for representation fidelity reasons and nominate reopening and accepting #65.

Not clear how to customise help menu

Maybe I am being thick, but it isn't really clear how to customise the help menus.

I know it says in the documentation to implement LogLevel, but what am I supposed to implement it on?

Maybe an example in the examples directory could be useful for others like me who want to alter the help menu but can't figure it out.

Include usage in example

The current example only shows adding a verbose field to the CLI struct, but doesn't demonstrate its usage. I imagine one could figure it out from the documentation, but it'd be nice if the example was more self-service.

Use case of this vs. `quicli`?

I know that you're the author of the quicli crate, which seems to have a subset of functionality that conflicts with this (since both crates would set up env-logger, right?). Is the use case to have a more minimal set of dependencies and cut out the main! macro and its associated failure dependency? Perhaps you think that this is more appropriate to use generally now that ?-in-main is available on stable?

Option descriptions only make sense "in hindsight"

To be perfectly frank, I only know what "More output per occurrence" and "Less output per occurrence" mean because I already know what the options they describe do and how they react to being used more than once.

"per occurrence" is such an opaque choice of phrasing that I'm not even sure they effectively communicate that the somewhat self-documenting --verbose --verbose will do something different from --verbose.

May I suggest taking inspiration from this excerpt from the UltiSnips snippet I use for starting Python scripts?

    parser.add_argument('-v', '--verbose', action="count",
        default=2, help="Increase the verbosity. Use twice for extra effect.")
    parser.add_argument('-q', '--quiet', action="count",
        default=0, help="Decrease the verbosity. Use twice for extra effect.")

That's not ideal and, to be honest, I think I let a little "Avoid having to wrap it onto the next line of the source code" contaminate it, but it's still much more helpful.

Maybe something like "Make console output more verbose. Can be repeated for greater effect."

Set up a CI

This would be a big help for reviewers to avoid nitpicking over little things.

Usage of this crate with clap

As the title say, I can't find a way to use this with clap. There is only one example, and it concerns structopt, not clap directly. My question is : is there a way to make this crate work with default clap ? If not, why is it named "clap-verbosity-flag" ?

The comment of the Verbosity struct shows up in latest clap

After updating all dependencies, the comment of /// Logging flags to #[command(flatte)] into your CLI shows up as the first line in our help text. I don't think that's intended.

Logging flags to `#[command(flatte)]` into your CLI

Usage: rattler-build [OPTIONS] [COMMAND]

Commands:
  build       Build a package
  test        Test a package
...

I can send a patch to remove this comment, but also happy to hear other options.

Support for clap3?

Clap3 is now in the rc stage, so I'm looking to upgrade. Any idea when clap3 might be supported?

structopt::StructOpt is not satisfied by clap_verbosity_flag::Verbosity

I'm having the following issue when trying to use clap-verbosity-flag

Errors

the trait bound `clap_verbosity_flag::Verbosity: structopt::StructOpt` is not satisfied

the trait `structopt::StructOpt` is not implemented for `clap_verbosity_flag::Verbosity`rustc(E0277)
main.rs(18, 17): the trait `structopt::StructOpt` is not implemented for `clap_verbosity_flag::Verbosity`
the trait bound `clap_verbosity_flag::Verbosity: structopt::StructOptInternal` is not satisfied

the trait `structopt::StructOptInternal` is not implemented for `clap_verbosity_flag::Verbosity`rustc(E0277)

Versions:

  • clap-verbosity-flag = "1.0.0"
  • structopt = "0.3.26"
  • rustc 1.60.0 (7737e0b5c 2022-04-04)

Code to reproduce

use structopt::StructOpt;

#[derive(StructOpt)]
struct Cli {
    #[structopt(flatten)]
    verbosity: clap_verbosity_flag::Verbosity,
}

Guidance would be appreciated. 🙏

Add `is_set` function

There is currently no way to detect if the two internal verbose and quiet fields are both zero. When they are both zero, this indicates that nothing related to log level has been specified on the command line. For my use case, I want to fallback to using the environment to determine log level if nothing is specified on the command line.

I will be opening a PR in a couple hours for this feature.

Let me know if you'd like the function called something different besides is_set.

Is error the right default?

Right now, only errors are shown by default.

I would assume at least warnings would show up. Probably also info(rmational) messages.

Of course, this requires adding a way to make things quieter (which we might need already, see #9). We'd probably need a --quiet / -q flag to go the other way.

Consider implementing PartialEq, Eq, PartialOrd and Ord for Verbosity

I'm using these implementations personally:

#[derive(Debug, Clone, Default)]
pub struct Verbosity<L: LogLevel>(clap_verbosity_flag::Verbosity<L>);

impl<L: LogLevel> Eq for Verbosity<L> {}
impl<L: LogLevel> PartialEq for Verbosity<L> {
    fn eq(&self, other: &Self) -> bool {
        self.0.log_level() == other.0.log_level()
    }
}

impl<L: LogLevel> Ord for Verbosity<L> {
    fn cmp(&self, other: &Self) -> Ordering {
        self.0.log_level().cmp(&other.0.log_level())
    }
}
impl<L: LogLevel> PartialOrd for Verbosity<L> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

Including `Verbosity` adds the doc comment to the help output

The simple example in the crate docs demonstrates the behaviour:

use structopt::StructOpt;
use clap_verbosity_flag::Verbosity;

#[derive(Debug, StructOpt)]
struct Cli {
    #[structopt(flatten)]
    verbose: Verbosity,
}

fn main() {
    Cli::from_args();
}

Then running:

❯ ./target/debug/examples/simple --help
clap-verbosity-flag 0.3.0
Easily add a `--verbose` flag to CLIs using Structopt

# Examples

```rust use structopt::StructOpt; use clap_verbosity_flag::Verbosity;

/// Le CLI #[derive(Debug, StructOpt)] struct Cli { #[structopt(flatten)] verbose: Verbosity, } # # fn main() {} ```

USAGE:
    simple [FLAGS]

FLAGS:
    -h, --help
            Prints help information

    -q, --quiet
            Pass many times for less log output

    -V, --version
            Prints version information

    -v, --verbose
            Pass many times for more log output

            By default, it'll only report errors. Passing `-v` one time also prints warnings, `-vv` enables info
            logging, `-vvv` debug, and `-vvvv` trace.

Is this perhaps due to a new version of structopt?

Need to depend on the `log` crate to manually compare `LevelFilter`

To make the sample code I wrote in #53 work, we need to depend on the log crate.
Fixed sample code:

use log; // Need this and adding the dependency in Cargo.toml

fn foo() {
    if opt.verbose.log_level_filter() <= LevelFilter::Warn {
        eprintln!("Hide command's stdout");
    }

    if opt.verbose.log_level_filter() <= LevelFilter::Error {
        eprintln!("Hide command's stderr");
    }
}

I’m not sure if I want to use the log crate or tracing yet… Since this crate can support both logging frameworks, it feels strange to be forced to depends on the log crate to manually manage the log level.

I think the LevelFilter could be re-exported (and maybe Level too).

RUSTSEC-2021-0139: ansi_term is Unmaintained

ansi_term is Unmaintained

Details
Status unmaintained
Package ansi_term
Version 0.12.1
URL ogham/rust-ansi-term#72
Date 2021-08-18

The maintainer has adviced that this crate is deprecated and will not receive any maintenance.

The crate does not seem to have much dependencies and may or may not be ok to use as-is.

Last release seems to have been three years ago.

Possible Alternative(s)

The below list has not been vetted in any way and may or may not contain alternatives;

Dependency Specific Migration(s)

See advisory page for additional details.

Make dependency on log explicit in the README

Regarding the discussion in #37 (whether it is sensible to add support for returning tracing's type.)
I just came across the same issue/question, and I understand @epage's concerns. So while I would hope that you'd find a solution to support that crate, I won't reopen the discussion here. 😉

However I want to advocate for adding a requirement to the usage of the verbosity-field into the minimal example in the README, so that this implicit dependency becomes more explicit.
The crate documentation contains a usage example that kinda hints at that with the instantiation of an env_logger, but without knowing what to look for it's difficult to spot that the Log*-types are all borrowed from the log-crate.

A line to the effect of "This crate relies on the log-crate by returning log::{Level,LevelFilter} values." in the README and/or crate documentation would be great.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.