Skip to content

Commit 5165fdf

Browse files
committed
Use u8 for internal verbosity level calculation
This avoids integer overflow / underflow errors when calculating the verbosity level. Prior to this change, constructing a `Verbosity` with either parameter equal to `u8::MAX` would result in incorrect verbosity level calculation. This change adds tests to ensure that the verbosity level is correctly calculated for each log level. These were run before and after the change to ensure that the change is correct. The tests using 255 as either input value were failing before the change and passing after the change.
1 parent c621a6a commit 5165fdf

File tree

1 file changed

+177
-16
lines changed

1 file changed

+177
-16
lines changed

src/lib.rs

+177-16
Original file line numberDiff line numberDiff line change
@@ -126,30 +126,32 @@ impl<L: LogLevel> Verbosity<L> {
126126
self.log_level().is_none()
127127
}
128128

129-
fn verbosity(&self) -> i8 {
130-
level_value(L::default()) - (self.quiet as i8) + (self.verbose as i8)
129+
fn verbosity(&self) -> u8 {
130+
let default_verbosity = level_value(L::default());
131+
let verbosity = default_verbosity as i16 - self.quiet as i16 + self.verbose as i16;
132+
verbosity.clamp(0, u8::MAX as i16) as u8
131133
}
132134
}
133135

134-
fn level_value(level: Option<Level>) -> i8 {
136+
fn level_value(level: Option<Level>) -> u8 {
135137
match level {
136-
None => -1,
137-
Some(Level::Error) => 0,
138-
Some(Level::Warn) => 1,
139-
Some(Level::Info) => 2,
140-
Some(Level::Debug) => 3,
141-
Some(Level::Trace) => 4,
138+
None => 0,
139+
Some(Level::Error) => 1,
140+
Some(Level::Warn) => 2,
141+
Some(Level::Info) => 3,
142+
Some(Level::Debug) => 4,
143+
Some(Level::Trace) => 5,
142144
}
143145
}
144146

145-
fn level_enum(verbosity: i8) -> Option<Level> {
147+
fn level_enum(verbosity: u8) -> Option<Level> {
146148
match verbosity {
147-
i8::MIN..=-1 => None,
148-
0 => Some(Level::Error),
149-
1 => Some(Level::Warn),
150-
2 => Some(Level::Info),
151-
3 => Some(Level::Debug),
152-
4..=i8::MAX => Some(Level::Trace),
149+
0 => None,
150+
1 => Some(Level::Error),
151+
2 => Some(Level::Warn),
152+
3 => Some(Level::Info),
153+
4 => Some(Level::Debug),
154+
5..=u8::MAX => Some(Level::Trace),
153155
}
154156
}
155157

@@ -235,4 +237,163 @@ mod test {
235237
use clap::CommandFactory;
236238
Cli::command().debug_assert();
237239
}
240+
241+
#[test]
242+
fn verbosity_error_level() {
243+
let v: Verbosity<ErrorLevel> = Verbosity::new(0, 0);
244+
assert_eq!(v.log_level(), Some(Level::Error));
245+
assert_eq!(v.log_level_filter(), LevelFilter::Error);
246+
247+
let v: Verbosity<ErrorLevel> = Verbosity::new(1, 0);
248+
assert_eq!(v.log_level(), Some(Level::Warn));
249+
assert_eq!(v.log_level_filter(), LevelFilter::Warn);
250+
251+
let v: Verbosity<ErrorLevel> = Verbosity::new(2, 0);
252+
assert_eq!(v.log_level(), Some(Level::Info));
253+
assert_eq!(v.log_level_filter(), LevelFilter::Info);
254+
255+
let v: Verbosity<ErrorLevel> = Verbosity::new(3, 0);
256+
assert_eq!(v.log_level(), Some(Level::Debug));
257+
assert_eq!(v.log_level_filter(), LevelFilter::Debug);
258+
259+
let v: Verbosity<ErrorLevel> = Verbosity::new(4, 0);
260+
assert_eq!(v.log_level(), Some(Level::Trace));
261+
assert_eq!(v.log_level_filter(), LevelFilter::Trace);
262+
263+
// overflows to trace
264+
let v: Verbosity<ErrorLevel> = Verbosity::new(5, 0);
265+
assert_eq!(v.log_level(), Some(Level::Trace));
266+
assert_eq!(v.log_level_filter(), LevelFilter::Trace);
267+
268+
// max verbosity is trace
269+
let v: Verbosity<ErrorLevel> = Verbosity::new(255, 0);
270+
assert_eq!(v.log_level(), Some(Level::Trace));
271+
assert_eq!(v.log_level_filter(), LevelFilter::Trace);
272+
273+
let v: Verbosity<ErrorLevel> = Verbosity::new(0, 1);
274+
assert_eq!(v.log_level(), None);
275+
assert_eq!(v.log_level_filter(), LevelFilter::Off);
276+
277+
// underflows to off
278+
let v: Verbosity<ErrorLevel> = Verbosity::new(0, 2);
279+
assert_eq!(v.log_level(), None);
280+
assert_eq!(v.log_level_filter(), LevelFilter::Off);
281+
282+
// max quiet is off
283+
let v: Verbosity<ErrorLevel> = Verbosity::new(0, 255);
284+
assert_eq!(v.log_level(), None);
285+
assert_eq!(v.log_level_filter(), LevelFilter::Off);
286+
287+
// This can only happen when vebosity is manually constructed due to the args being marked
288+
// as conflicting
289+
let v: Verbosity<ErrorLevel> = Verbosity::new(255, 255);
290+
assert_eq!(v.log_level(), Some(Level::Error));
291+
assert_eq!(v.log_level_filter(), LevelFilter::Error);
292+
}
293+
294+
#[test]
295+
fn verbosity_warn_level() {
296+
let v: Verbosity<WarnLevel> = Verbosity::new(0, 0);
297+
assert_eq!(v.log_level(), Some(Level::Warn));
298+
assert_eq!(v.log_level_filter(), LevelFilter::Warn);
299+
300+
let v: Verbosity<WarnLevel> = Verbosity::new(1, 0);
301+
assert_eq!(v.log_level(), Some(Level::Info));
302+
assert_eq!(v.log_level_filter(), LevelFilter::Info);
303+
304+
let v: Verbosity<WarnLevel> = Verbosity::new(2, 0);
305+
assert_eq!(v.log_level(), Some(Level::Debug));
306+
assert_eq!(v.log_level_filter(), LevelFilter::Debug);
307+
308+
let v: Verbosity<WarnLevel> = Verbosity::new(3, 0);
309+
assert_eq!(v.log_level(), Some(Level::Trace));
310+
assert_eq!(v.log_level_filter(), LevelFilter::Trace);
311+
312+
// overflows to trace
313+
let v: Verbosity<WarnLevel> = Verbosity::new(4, 0);
314+
assert_eq!(v.log_level(), Some(Level::Trace));
315+
assert_eq!(v.log_level_filter(), LevelFilter::Trace);
316+
317+
// max verbosity is trace
318+
let v: Verbosity<WarnLevel> = Verbosity::new(255, 0);
319+
assert_eq!(v.log_level(), Some(Level::Trace));
320+
assert_eq!(v.log_level_filter(), LevelFilter::Trace);
321+
322+
let v: Verbosity<WarnLevel> = Verbosity::new(0, 1);
323+
assert_eq!(v.log_level(), Some(Level::Error));
324+
assert_eq!(v.log_level_filter(), LevelFilter::Error);
325+
326+
let v: Verbosity<WarnLevel> = Verbosity::new(0, 2);
327+
assert_eq!(v.log_level(), None);
328+
assert_eq!(v.log_level_filter(), LevelFilter::Off);
329+
330+
// underflows to off
331+
let v: Verbosity<WarnLevel> = Verbosity::new(0, 3);
332+
assert_eq!(v.log_level(), None);
333+
assert_eq!(v.log_level_filter(), LevelFilter::Off);
334+
335+
// max quiet is off
336+
let v: Verbosity<WarnLevel> = Verbosity::new(0, 255);
337+
assert_eq!(v.log_level(), None);
338+
assert_eq!(v.log_level_filter(), LevelFilter::Off);
339+
340+
// This can only happen when vebosity is manually constructed due to the args being marked
341+
// as conflicting
342+
let v: Verbosity<WarnLevel> = Verbosity::new(255, 255);
343+
assert_eq!(v.log_level(), Some(Level::Warn));
344+
assert_eq!(v.log_level_filter(), LevelFilter::Warn);
345+
}
346+
347+
#[test]
348+
fn verbosity_info_level() {
349+
let v: Verbosity<InfoLevel> = Verbosity::new(0, 0);
350+
assert_eq!(v.log_level(), Some(Level::Info));
351+
assert_eq!(v.log_level_filter(), LevelFilter::Info);
352+
353+
let v: Verbosity<InfoLevel> = Verbosity::new(1, 0);
354+
assert_eq!(v.log_level(), Some(Level::Debug));
355+
assert_eq!(v.log_level_filter(), LevelFilter::Debug);
356+
357+
let v: Verbosity<InfoLevel> = Verbosity::new(2, 0);
358+
assert_eq!(v.log_level(), Some(Level::Trace));
359+
assert_eq!(v.log_level_filter(), LevelFilter::Trace);
360+
361+
// overflows to trace
362+
let v: Verbosity<InfoLevel> = Verbosity::new(3, 0);
363+
assert_eq!(v.log_level(), Some(Level::Trace));
364+
assert_eq!(v.log_level_filter(), LevelFilter::Trace);
365+
366+
// max verbosity is trace
367+
let v: Verbosity<InfoLevel> = Verbosity::new(255, 0);
368+
assert_eq!(v.log_level(), Some(Level::Trace));
369+
assert_eq!(v.log_level_filter(), LevelFilter::Trace);
370+
371+
let v: Verbosity<InfoLevel> = Verbosity::new(0, 1);
372+
assert_eq!(v.log_level(), Some(Level::Warn));
373+
assert_eq!(v.log_level_filter(), LevelFilter::Warn);
374+
375+
let v: Verbosity<InfoLevel> = Verbosity::new(0, 2);
376+
assert_eq!(v.log_level(), Some(Level::Error));
377+
assert_eq!(v.log_level_filter(), LevelFilter::Error);
378+
379+
let v: Verbosity<InfoLevel> = Verbosity::new(0, 3);
380+
assert_eq!(v.log_level(), None);
381+
assert_eq!(v.log_level_filter(), LevelFilter::Off);
382+
383+
// underflows to off
384+
let v: Verbosity<InfoLevel> = Verbosity::new(0, 4);
385+
assert_eq!(v.log_level(), None);
386+
assert_eq!(v.log_level_filter(), LevelFilter::Off);
387+
388+
// max quiet is off
389+
let v: Verbosity<InfoLevel> = Verbosity::new(0, 255);
390+
assert_eq!(v.log_level(), None);
391+
assert_eq!(v.log_level_filter(), LevelFilter::Off);
392+
393+
// This can only happen when vebosity is manually constructed due to the args being marked
394+
// as conflicting
395+
let v: Verbosity<InfoLevel> = Verbosity::new(255, 255);
396+
assert_eq!(v.log_level(), Some(Level::Info));
397+
assert_eq!(v.log_level_filter(), LevelFilter::Info);
398+
}
238399
}

0 commit comments

Comments
 (0)