1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
//! Utilities for parsing options and arguments on the start of a CLI application
use clap::Parser;
use libpt_log::Level;
#[cfg(feature = "log")]
use log;
/// Custom help template for displaying command-line usage information
///
/// This template modifies the default template provided by Clap to include additional information
/// and customize the layout of the help message.
///
/// Differences from the default template:
/// - Includes the application version and author information at the end
///
/// Apply like this:
/// ```
/// # use libpt_cli::args::HELP_TEMPLATE;
/// use clap::Parser;
/// #[derive(Parser, Debug, Clone, PartialEq, Eq, Hash)]
/// #[command(help_template = HELP_TEMPLATE)]
/// pub struct MyArgs {
/// /// show more details
/// #[arg(short, long)]
/// pub verbose: bool,
/// }
/// ```
///
/// ## Example
///
/// Don't forget to set `authors` in your `Cargo.toml`!
///
/// ```bash
/// $ cargo run -- -h
/// about: short
///
/// Usage: aaa [OPTIONS]
///
/// Options:
/// -v, --verbose show more details
/// -h, --help Print help (see more with '--help')
/// -V, --version Print version
///
/// aaa: 0.1.0
/// Author: Christoph J. Scherr <software@cscherr.de>
///
/// ```
pub const HELP_TEMPLATE: &str = r"{about-section}
{usage-heading} {usage}
{all-args}{tab}
{name}: {version}
Author: {author-with-newline}
";
/// Transform -v and -q flags to some kind of loglevel
///
/// # Example
///
/// Include this into your [clap] derive struct like this:
///
/// ```
/// use libpt_cli::args::VerbosityLevel;
/// use clap::Parser;
///
/// #[derive(Parser, Debug)]
/// pub struct Opts {
/// #[command(flatten)]
/// pub verbose: VerbosityLevel,
/// #[arg(short, long)]
/// pub mynum: usize,
/// }
///
/// ```
///
/// Get the loglevel like this:
///
/// ```no_run
/// # use libpt_cli::args::VerbosityLevel;
/// use libpt_log::Level;
/// # use clap::Parser;
///
/// # #[derive(Parser, Debug)]
/// # pub struct Opts {
/// # #[command(flatten)]
/// # pub verbose: VerbosityLevel,
/// # }
///
/// fn main() {
/// let opts = Opts::parse();
///
/// // Level might be None if the user wants no output at all.
/// // for the 'tracing' level:
/// let level: Level = opts.verbose.level();
/// }
/// ```
#[derive(Parser, Clone, PartialEq, Eq, Hash)]
pub struct VerbosityLevel {
/// make the output more verbose
#[arg(
long,
short = 'v',
action = clap::ArgAction::Count,
global = true,
// help = L::verbose_help(),
// long_help = L::verbose_long_help(),
)]
verbose: i8,
/// make the output less verbose
///
/// ( -qqq for completely quiet)
#[arg(
long,
short = 'q',
action = clap::ArgAction::Count,
global = true,
conflicts_with = "verbose",
)]
quiet: i8,
}
impl VerbosityLevel {
/// true only if no verbose and no quiet was set (user is using defaults)
#[inline]
#[must_use]
#[allow(clippy::missing_const_for_fn)] // the values of self can change
pub fn changed(&self) -> bool {
self.verbose != 0 || self.quiet != 0
}
#[inline]
#[must_use]
fn value(&self) -> i8 {
Self::level_value(Level::INFO)
.saturating_sub((self.quiet).min(10))
.saturating_add((self.verbose).min(10))
}
/// get the [Level] for that [`VerbosityLevel`]
///
/// # Examples
///
/// ```
/// use libpt_log::Level; // reexport: tracing
/// use libpt_cli::args::VerbosityLevel;
///
/// let verbosity_level = VerbosityLevel::INFO;
/// assert_eq!(verbosity_level.level(), Level::INFO);
/// ```
#[inline]
#[must_use]
pub fn level(&self) -> Level {
let v = self.value();
match v {
0 => Level::ERROR,
1 => Level::WARN,
2 => Level::INFO,
3 => Level::DEBUG,
4 => Level::TRACE,
_ => {
if v > 4 {
Level::TRACE
} else {
/* v < 0 */
Level::ERROR
}
}
}
}
/// get the [`log::Level`] for that `VerbosityLevel`
///
/// This is the method for the [log] crate, which I use less often.
///
/// [None] means that absolutely no output is wanted (completely quiet)
#[inline]
#[must_use]
#[cfg(feature = "log")]
pub fn level_for_log_crate(&self) -> log::Level {
match self.level() {
Level::TRACE => log::Level::Trace,
Level::DEBUG => log::Level::Debug,
Level::INFO => log::Level::Info,
Level::WARN => log::Level::Warn,
Level::ERROR => log::Level::Error,
}
}
#[inline]
#[must_use]
const fn level_value(level: Level) -> i8 {
match level {
Level::TRACE => 4,
Level::DEBUG => 3,
Level::INFO => 2,
Level::WARN => 1,
Level::ERROR => 0,
}
}
/// # Examples
///
/// ```
/// use libpt_log::Level; // reexport: tracing
/// use libpt_cli::args::VerbosityLevel;
///
/// let verbosity_level = VerbosityLevel::TRACE;
/// assert_eq!(verbosity_level.level(), Level::TRACE);
/// ```
pub const TRACE: Self = Self {
verbose: 2,
quiet: 0,
};
/// # Examples
///
/// ```
/// use libpt_log::Level; // reexport: tracing
/// use libpt_cli::args::VerbosityLevel;
///
/// let verbosity_level = VerbosityLevel::DEBUG;
/// assert_eq!(verbosity_level.level(), Level::DEBUG);
/// ```
pub const DEBUG: Self = Self {
verbose: 1,
quiet: 0,
};
/// # Examples
///
/// ```
/// use libpt_log::Level; // reexport: tracing
/// use libpt_cli::args::VerbosityLevel;
///
/// let verbosity_level = VerbosityLevel::INFO;
/// assert_eq!(verbosity_level.level(), Level::INFO);
/// ```
pub const INFO: Self = Self {
verbose: 0,
quiet: 0,
};
/// # Examples
///
/// ```
/// use libpt_log::Level; // reexport: tracing
/// use libpt_cli::args::VerbosityLevel;
///
/// let verbosity_level = VerbosityLevel::WARN;
/// assert_eq!(verbosity_level.level(), Level::WARN);
/// ```
pub const WARN: Self = Self {
verbose: 0,
quiet: 1,
};
/// # Examples
///
/// ```
/// use libpt_log::Level; // reexport: tracing
/// use libpt_cli::args::VerbosityLevel;
///
/// let verbosity_level = VerbosityLevel::ERROR;
/// assert_eq!(verbosity_level.level(), Level::ERROR);
/// ```
pub const ERROR: Self = Self {
verbose: 0,
quiet: 2,
};
}
impl std::fmt::Debug for VerbosityLevel {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.level())
}
}
impl Default for VerbosityLevel {
fn default() -> Self {
Self::INFO
}
}