2018-11-05 21:30:39 +01:00
|
|
|
#[macro_use]
|
|
|
|
|
extern crate clap;
|
2019-04-13 18:25:12 +02:00
|
|
|
|
2020-06-01 12:30:27 -06:00
|
|
|
use std::convert::TryFrom;
|
2018-11-05 21:30:39 +01:00
|
|
|
use std::fs::File;
|
2020-04-29 08:22:03 +02:00
|
|
|
use std::io::{self, prelude::*, SeekFrom};
|
2018-11-05 21:30:39 +01:00
|
|
|
|
|
|
|
|
use clap::{App, AppSettings, Arg};
|
|
|
|
|
|
2019-05-29 23:01:38 +02:00
|
|
|
use atty::Stream;
|
2019-07-14 17:38:30 +01:00
|
|
|
|
2020-06-11 12:25:56 -06:00
|
|
|
use anyhow::{anyhow, Context, Error as AnyhowError};
|
|
|
|
|
|
|
|
|
|
use thiserror::Error as ThisError;
|
2020-06-10 13:00:10 -06:00
|
|
|
|
2020-04-29 08:22:03 +02:00
|
|
|
use hexyl::{BorderStyle, Input, Printer};
|
2019-01-14 09:05:12 +01:00
|
|
|
|
2020-06-10 13:00:10 -06:00
|
|
|
fn run() -> Result<(), AnyhowError> {
|
2018-11-05 21:30:39 +01:00
|
|
|
let app = App::new(crate_name!())
|
|
|
|
|
.setting(AppSettings::ColorAuto)
|
|
|
|
|
.setting(AppSettings::ColoredHelp)
|
|
|
|
|
.setting(AppSettings::DeriveDisplayOrder)
|
|
|
|
|
.setting(AppSettings::UnifiedHelpMessage)
|
2020-06-08 23:19:56 +02:00
|
|
|
.max_term_width(90)
|
2018-11-05 21:30:39 +01:00
|
|
|
.version(crate_version!())
|
2018-11-05 23:23:06 +01:00
|
|
|
.about(crate_description!())
|
2020-06-07 14:37:17 +02:00
|
|
|
.arg(
|
|
|
|
|
Arg::with_name("FILE")
|
|
|
|
|
.help("The file to display. If no FILE argument is given, read from STDIN."),
|
|
|
|
|
)
|
2018-11-06 20:18:15 +01:00
|
|
|
.arg(
|
|
|
|
|
Arg::with_name("length")
|
|
|
|
|
.short("n")
|
|
|
|
|
.long("length")
|
|
|
|
|
.takes_value(true)
|
|
|
|
|
.value_name("N")
|
2020-06-07 14:37:17 +02:00
|
|
|
.help(
|
|
|
|
|
"Only read N bytes from the input. The N argument can also include a \
|
2020-06-10 17:01:01 -06:00
|
|
|
unit with a decimal prefix (kB, MB, ..) or binary prefix (kiB, MiB, ..).\n\
|
|
|
|
|
Examples: --length=64, --length=4KiB",
|
2020-06-07 14:37:17 +02:00
|
|
|
),
|
2019-01-11 22:10:20 +10:00
|
|
|
)
|
2019-02-04 17:04:26 -06:00
|
|
|
.arg(
|
2019-02-05 06:45:45 -06:00
|
|
|
Arg::with_name("bytes")
|
2019-02-04 17:04:26 -06:00
|
|
|
.short("c")
|
2019-02-05 06:45:45 -06:00
|
|
|
.long("bytes")
|
2019-02-04 17:04:26 -06:00
|
|
|
.takes_value(true)
|
2019-02-05 16:37:27 -06:00
|
|
|
.value_name("N")
|
|
|
|
|
.help("An alias for -n/--length"),
|
2019-02-04 17:04:26 -06:00
|
|
|
)
|
2020-04-29 08:22:03 +02:00
|
|
|
.arg(
|
|
|
|
|
Arg::with_name("skip")
|
|
|
|
|
.short("s")
|
|
|
|
|
.long("skip")
|
|
|
|
|
.takes_value(true)
|
|
|
|
|
.value_name("N")
|
2020-06-07 14:37:17 +02:00
|
|
|
.help(
|
|
|
|
|
"Skip the first N bytes of the input. The N argument can also include \
|
2020-06-10 17:01:01 -06:00
|
|
|
a unit (see `--length` for details)",
|
2020-06-07 14:37:17 +02:00
|
|
|
),
|
2020-04-29 08:22:03 +02:00
|
|
|
)
|
2020-05-20 09:35:43 -06:00
|
|
|
.arg(
|
|
|
|
|
Arg::with_name("block_size")
|
|
|
|
|
.long("block-size")
|
|
|
|
|
.takes_value(true)
|
|
|
|
|
.value_name("SIZE")
|
|
|
|
|
.help(
|
2020-06-07 14:37:17 +02:00
|
|
|
"Sets the size of the `block` unit to SIZE.\n\
|
2020-06-10 17:01:01 -06:00
|
|
|
Examples: --block-size=1024, --block-size=4kB",
|
2020-05-20 09:35:43 -06:00
|
|
|
),
|
|
|
|
|
)
|
2019-03-26 23:24:55 +01:00
|
|
|
.arg(
|
|
|
|
|
Arg::with_name("nosqueezing")
|
|
|
|
|
.short("v")
|
|
|
|
|
.long("no-squeezing")
|
|
|
|
|
.help(
|
|
|
|
|
"Displays all input data. Otherwise any number of groups of output \
|
|
|
|
|
lines which would be identical to the preceding group of lines, are \
|
|
|
|
|
replaced with a line comprised of a single asterisk.",
|
|
|
|
|
),
|
|
|
|
|
)
|
2019-01-11 22:10:20 +10:00
|
|
|
.arg(
|
2019-01-14 09:05:12 +01:00
|
|
|
Arg::with_name("color")
|
|
|
|
|
.long("color")
|
|
|
|
|
.takes_value(true)
|
2020-06-07 14:37:17 +02:00
|
|
|
.value_name("WHEN")
|
2019-01-14 09:05:12 +01:00
|
|
|
.possible_values(&["always", "auto", "never"])
|
|
|
|
|
.default_value("always")
|
2019-01-14 09:27:09 +01:00
|
|
|
.help(
|
|
|
|
|
"When to use colors. The auto-mode only displays colors if the output \
|
|
|
|
|
goes to an interactive terminal",
|
|
|
|
|
),
|
2019-03-05 21:59:52 +01:00
|
|
|
)
|
|
|
|
|
.arg(
|
|
|
|
|
Arg::with_name("border")
|
|
|
|
|
.long("border")
|
|
|
|
|
.takes_value(true)
|
2020-06-07 14:37:17 +02:00
|
|
|
.value_name("STYLE")
|
2019-03-05 21:59:52 +01:00
|
|
|
.possible_values(&["unicode", "ascii", "none"])
|
|
|
|
|
.default_value("unicode")
|
2020-06-10 17:01:01 -06:00
|
|
|
.help(
|
|
|
|
|
"Whether to draw a border with Unicode characters, ASCII characters, \
|
|
|
|
|
or none at all",
|
|
|
|
|
),
|
2019-10-07 19:20:02 +01:00
|
|
|
)
|
|
|
|
|
.arg(
|
|
|
|
|
Arg::with_name("display_offset")
|
|
|
|
|
.short("o")
|
|
|
|
|
.long("display-offset")
|
|
|
|
|
.takes_value(true)
|
2020-06-07 14:37:17 +02:00
|
|
|
.value_name("N")
|
2020-06-10 17:01:01 -06:00
|
|
|
.help(
|
|
|
|
|
"Add N bytes to the displayed file position. The N argument can also \
|
|
|
|
|
include a unit (see `--length` for details)",
|
|
|
|
|
),
|
2018-11-06 20:18:15 +01:00
|
|
|
);
|
2018-11-05 21:30:39 +01:00
|
|
|
|
2019-01-11 14:06:23 +00:00
|
|
|
let matches = app.get_matches_safe()?;
|
2018-11-05 21:30:39 +01:00
|
|
|
|
2018-11-05 23:21:54 +01:00
|
|
|
let stdin = io::stdin();
|
|
|
|
|
|
2020-06-10 16:00:54 -06:00
|
|
|
let mut reader = match matches.value_of("FILE") {
|
2020-04-29 08:22:03 +02:00
|
|
|
Some(filename) => Input::File(File::open(filename)?),
|
2020-04-30 09:08:41 +02:00
|
|
|
None => Input::Stdin(stdin.lock()),
|
2018-11-05 23:21:54 +01:00
|
|
|
};
|
2018-11-05 21:30:39 +01:00
|
|
|
|
2020-05-20 09:35:43 -06:00
|
|
|
let block_size = matches
|
|
|
|
|
.value_of("block_size")
|
2020-06-11 12:25:56 -06:00
|
|
|
.map(|bs| {
|
|
|
|
|
bs.parse::<i64>()
|
|
|
|
|
.map_err(|e| anyhow!(e))
|
|
|
|
|
.and_then(|x| {
|
|
|
|
|
PositiveI64::new(x)
|
|
|
|
|
.ok_or_else(|| anyhow!("negative block sizes don't make sense"))
|
|
|
|
|
})
|
|
|
|
|
.context(anyhow!(
|
|
|
|
|
"failed to parse `--block-size` arg {:?} as positive integer",
|
|
|
|
|
bs
|
|
|
|
|
))
|
|
|
|
|
})
|
|
|
|
|
.transpose()?
|
2020-06-01 12:30:27 -06:00
|
|
|
.unwrap_or_else(|| PositiveI64::new(512).unwrap());
|
2020-05-20 09:35:43 -06:00
|
|
|
|
|
|
|
|
let skip_arg = matches
|
|
|
|
|
.value_of("skip")
|
2020-06-11 12:25:56 -06:00
|
|
|
.map(|s| {
|
|
|
|
|
parse_byte_count(s, block_size).context(anyhow!(
|
|
|
|
|
"failed to parse `--skip` arg {:?} as byte count",
|
|
|
|
|
s
|
|
|
|
|
))
|
|
|
|
|
})
|
|
|
|
|
.transpose()?;
|
2020-04-29 08:22:03 +02:00
|
|
|
|
2020-06-07 11:34:12 +02:00
|
|
|
if let Some(skip) = skip_arg {
|
2020-06-01 12:30:27 -06:00
|
|
|
reader.seek(SeekFrom::Current(skip.into_inner()))?;
|
2020-04-29 08:22:03 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-11 12:25:56 -06:00
|
|
|
let mut reader = if let Some(length) = matches
|
2020-05-20 09:45:14 -06:00
|
|
|
.value_of("length")
|
2020-06-11 12:25:56 -06:00
|
|
|
.or_else(|| matches.value_of("bytes"))
|
|
|
|
|
.map(|s| {
|
|
|
|
|
parse_byte_count(s, block_size).context(anyhow!(
|
|
|
|
|
"failed to parse `--length` arg {:?} as byte count",
|
|
|
|
|
s
|
|
|
|
|
))
|
|
|
|
|
})
|
|
|
|
|
.transpose()?
|
2020-05-20 09:35:43 -06:00
|
|
|
{
|
2020-06-01 12:30:27 -06:00
|
|
|
Box::new(reader.take(length.into()))
|
2020-04-29 08:22:03 +02:00
|
|
|
} else {
|
|
|
|
|
reader.into_inner()
|
|
|
|
|
};
|
2019-02-04 17:04:26 -06:00
|
|
|
|
2019-01-14 09:05:12 +01:00
|
|
|
let show_color = match matches.value_of("color") {
|
|
|
|
|
Some("never") => false,
|
|
|
|
|
Some("auto") => atty::is(Stream::Stdout),
|
|
|
|
|
_ => true,
|
|
|
|
|
};
|
2019-01-11 22:10:20 +10:00
|
|
|
|
2019-03-05 21:59:52 +01:00
|
|
|
let border_style = match matches.value_of("border") {
|
|
|
|
|
Some("unicode") => BorderStyle::Unicode,
|
|
|
|
|
Some("ascii") => BorderStyle::Ascii,
|
|
|
|
|
_ => BorderStyle::None,
|
|
|
|
|
};
|
|
|
|
|
|
2019-03-26 23:24:55 +01:00
|
|
|
let squeeze = !matches.is_present("nosqueezing");
|
|
|
|
|
|
2019-10-07 19:20:02 +01:00
|
|
|
let display_offset = matches
|
|
|
|
|
.value_of("display_offset")
|
2020-06-11 12:25:56 -06:00
|
|
|
.map(|s| {
|
|
|
|
|
parse_byte_count(s, block_size).context(anyhow!(
|
|
|
|
|
"failed to parse `--display-offset` arg {:?} as byte count",
|
|
|
|
|
s
|
|
|
|
|
))
|
|
|
|
|
})
|
|
|
|
|
.transpose()?
|
2020-05-28 16:32:53 -06:00
|
|
|
.or(skip_arg)
|
2020-06-01 12:30:27 -06:00
|
|
|
.unwrap_or_default()
|
|
|
|
|
.into();
|
2019-10-07 19:20:02 +01:00
|
|
|
|
2018-11-05 21:38:29 +01:00
|
|
|
let stdout = io::stdout();
|
2019-07-14 18:18:13 +01:00
|
|
|
let mut stdout_lock = stdout.lock();
|
|
|
|
|
|
|
|
|
|
let mut printer = Printer::new(&mut stdout_lock, show_color, border_style, squeeze);
|
2020-06-01 12:31:21 -06:00
|
|
|
printer.display_offset(display_offset);
|
2020-06-10 13:00:10 -06:00
|
|
|
printer.print_all(&mut reader).map_err(|e| anyhow!(e))?;
|
2018-11-05 21:30:39 +01:00
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
2018-11-09 20:04:52 +01:00
|
|
|
// Enable ANSI support for Windows
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
|
let _ = ansi_term::enable_ansi_support();
|
|
|
|
|
|
2018-11-05 21:30:39 +01:00
|
|
|
let result = run();
|
2019-01-11 14:07:27 +00:00
|
|
|
|
|
|
|
|
if let Err(err) = result {
|
2019-01-11 14:17:01 +00:00
|
|
|
if let Some(clap_err) = err.downcast_ref::<clap::Error>() {
|
|
|
|
|
eprint!("{}", clap_err); // Clap errors already have newlines
|
2019-01-13 17:24:22 +00:00
|
|
|
|
|
|
|
|
match clap_err.kind {
|
|
|
|
|
// The exit code should not indicate an error for --help / --version
|
|
|
|
|
clap::ErrorKind::HelpDisplayed | clap::ErrorKind::VersionDisplayed => {
|
|
|
|
|
std::process::exit(0)
|
|
|
|
|
}
|
|
|
|
|
_ => (),
|
|
|
|
|
}
|
2019-01-11 14:17:01 +00:00
|
|
|
} else {
|
2020-06-10 13:00:10 -06:00
|
|
|
eprintln!("Error: {:?}", err);
|
2019-01-11 14:17:01 +00:00
|
|
|
}
|
2019-01-11 14:18:13 +00:00
|
|
|
std::process::exit(1);
|
2018-11-05 21:30:39 +01:00
|
|
|
}
|
|
|
|
|
}
|
2019-10-07 19:20:02 +01:00
|
|
|
|
2020-06-01 12:30:27 -06:00
|
|
|
#[derive(Clone, Copy, Debug, Default, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
|
|
|
|
pub struct PositiveI64(i64);
|
|
|
|
|
|
|
|
|
|
impl PositiveI64 {
|
|
|
|
|
pub fn new(x: i64) -> Option<Self> {
|
|
|
|
|
if x.is_negative() {
|
|
|
|
|
None
|
|
|
|
|
} else {
|
|
|
|
|
Some(Self(x))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn into_inner(self) -> i64 {
|
|
|
|
|
self.0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Into<u64> for PositiveI64 {
|
|
|
|
|
fn into(self) -> u64 {
|
|
|
|
|
u64::try_from(self.0)
|
|
|
|
|
.expect("invariant broken: PositiveI64 should contain a positive i64 value")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-11 12:25:56 -06:00
|
|
|
const HEX_PREFIX: &str = "0x";
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq, ThisError)]
|
|
|
|
|
enum ByteCountParseError {
|
|
|
|
|
#[error("no character data found, did you forget to write it?")]
|
|
|
|
|
Empty,
|
|
|
|
|
#[error("no digits found after sign, did you forget to write them?")]
|
|
|
|
|
EmptyAfterSign,
|
|
|
|
|
#[error("negative byte offsets are not accepted (yet)")]
|
|
|
|
|
Negative,
|
|
|
|
|
#[error(
|
|
|
|
|
"found {0:?} sign after hex prefix ({:?}); signs should go before it",
|
|
|
|
|
HEX_PREFIX
|
|
|
|
|
)]
|
|
|
|
|
SignFoundAfterHexPrefix(char),
|
|
|
|
|
#[error("{0:?} is not of the expected form <pos-integer>[<unit>]")]
|
|
|
|
|
InvalidNumAndUnit(String),
|
|
|
|
|
#[error("{0:?} is a valid unit, but an integer should come before it")]
|
|
|
|
|
EmptyWithUnit(String),
|
|
|
|
|
#[error("invalid unit {0:?}")]
|
|
|
|
|
InvalidUnit(String),
|
|
|
|
|
#[error("failed to parse integer part")]
|
|
|
|
|
ParseNum(#[source] std::num::ParseIntError),
|
|
|
|
|
#[error("count multipled by the unit overflowed a signed 64-bit integer; are you sure it should be that big?")]
|
|
|
|
|
UnitMultiplicationOverflow,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_byte_count(n: &str, block_size: PositiveI64) -> Result<PositiveI64, ByteCountParseError> {
|
|
|
|
|
use ByteCountParseError::*;
|
2020-05-18 13:54:43 -06:00
|
|
|
|
2020-05-20 09:32:10 -06:00
|
|
|
let n = {
|
|
|
|
|
let mut chars = n.chars();
|
2020-06-11 12:25:56 -06:00
|
|
|
match chars.next() {
|
|
|
|
|
Some('+') => {
|
|
|
|
|
if chars.clone().next().is_none() {
|
|
|
|
|
return Err(EmptyAfterSign);
|
|
|
|
|
}
|
|
|
|
|
chars.as_str()
|
|
|
|
|
}
|
|
|
|
|
Some('-') => return Err(Negative),
|
|
|
|
|
None => return Err(Empty),
|
2020-05-20 09:32:10 -06:00
|
|
|
_ => n,
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-05-18 13:54:43 -06:00
|
|
|
if n.starts_with(HEX_PREFIX) {
|
2020-05-20 09:40:35 -06:00
|
|
|
let n = &n[HEX_PREFIX.len()..];
|
2020-06-11 12:25:56 -06:00
|
|
|
let mut chars = n.chars();
|
|
|
|
|
match chars.next() {
|
|
|
|
|
Some(c @ '+') | Some(c @ '-') => {
|
|
|
|
|
return if chars.next().is_none() {
|
|
|
|
|
Err(EmptyAfterSign)
|
|
|
|
|
} else {
|
|
|
|
|
Err(SignFoundAfterHexPrefix(c))
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-01 12:30:27 -06:00
|
|
|
_ => (),
|
2020-05-18 13:53:46 -06:00
|
|
|
}
|
2020-06-11 12:25:56 -06:00
|
|
|
return i64::from_str_radix(n, 16)
|
|
|
|
|
.map(|x| Ok(PositiveI64::new(x).unwrap()))
|
|
|
|
|
.map_err(ParseNum)?;
|
2019-10-07 19:20:02 +01:00
|
|
|
}
|
2020-05-20 09:32:10 -06:00
|
|
|
|
2020-06-11 12:25:56 -06:00
|
|
|
let (num, unit) = match n.chars().position(|c| !c.is_ascii_digit()) {
|
2020-05-20 09:32:10 -06:00
|
|
|
Some(unit_begin_idx) => {
|
|
|
|
|
let (n, raw_unit) = n.split_at(unit_begin_idx);
|
2020-06-11 12:25:56 -06:00
|
|
|
let raw_unit_lower = raw_unit.to_lowercase();
|
|
|
|
|
let multiplier = [
|
|
|
|
|
("b", 1),
|
|
|
|
|
("kb", 1000i64.pow(1)),
|
|
|
|
|
("mb", 1000i64.pow(2)),
|
|
|
|
|
("gb", 1000i64.pow(3)),
|
|
|
|
|
("tb", 1000i64.pow(4)),
|
|
|
|
|
("kib", 1024i64.pow(1)),
|
|
|
|
|
("mib", 1024i64.pow(2)),
|
|
|
|
|
("gib", 1024i64.pow(3)),
|
|
|
|
|
("tib", 1024i64.pow(4)),
|
|
|
|
|
("block", block_size.into_inner()),
|
|
|
|
|
]
|
|
|
|
|
.iter()
|
|
|
|
|
.cloned()
|
|
|
|
|
.find_map(|(unit, multiplier)| {
|
|
|
|
|
if unit == raw_unit_lower {
|
|
|
|
|
Some(multiplier)
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.ok_or_else(|| InvalidUnit(raw_unit.to_owned()));
|
|
|
|
|
(n, multiplier.map(|m| (Some(raw_unit), m)))
|
2020-05-20 09:32:10 -06:00
|
|
|
}
|
2020-06-11 12:25:56 -06:00
|
|
|
None => (n, Ok((None, 1))),
|
2020-05-20 09:32:10 -06:00
|
|
|
};
|
|
|
|
|
|
2020-06-11 12:25:56 -06:00
|
|
|
match (num.parse::<i64>(), unit) {
|
|
|
|
|
(Ok(num), Ok((_raw_unit, unit_multiplier))) => num
|
|
|
|
|
.checked_mul(unit_multiplier)
|
|
|
|
|
.ok_or_else(|| UnitMultiplicationOverflow)
|
|
|
|
|
.and_then(|x| PositiveI64::new(x).ok_or(Negative)),
|
|
|
|
|
(Ok(_), Err(e)) => Err(e),
|
|
|
|
|
(Err(e), Ok((raw_unit, _unit_multiplier))) => match raw_unit {
|
|
|
|
|
Some(raw_unit) if num.is_empty() => Err(EmptyWithUnit(raw_unit.to_owned())),
|
|
|
|
|
_ => Err(ParseNum(e)),
|
|
|
|
|
},
|
|
|
|
|
(Err(_), Err(_)) => Err(InvalidNumAndUnit(n.to_owned())),
|
|
|
|
|
}
|
2020-05-20 09:32:10 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_parse_byte_count() {
|
2020-06-11 12:25:56 -06:00
|
|
|
use ByteCountParseError::*;
|
|
|
|
|
|
2020-05-20 09:32:10 -06:00
|
|
|
macro_rules! success {
|
|
|
|
|
($input: expr, $expected: expr) => {
|
2020-05-20 09:35:43 -06:00
|
|
|
success!($input, 512, $expected)
|
|
|
|
|
};
|
|
|
|
|
($input: expr, $block_size: expr, $expected: expr) => {
|
2020-06-01 12:30:27 -06:00
|
|
|
assert_eq!(
|
|
|
|
|
parse_byte_count($input, PositiveI64::new($block_size).unwrap()),
|
2020-06-11 12:25:56 -06:00
|
|
|
Ok(PositiveI64::new($expected).unwrap()),
|
2020-06-01 12:30:27 -06:00
|
|
|
);
|
2020-05-20 09:32:10 -06:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
macro_rules! error {
|
2020-06-11 12:25:56 -06:00
|
|
|
($input: expr, $expected_err: expr) => {
|
2020-06-01 12:30:27 -06:00
|
|
|
assert_eq!(
|
|
|
|
|
parse_byte_count($input, PositiveI64::new(512).unwrap()),
|
2020-06-11 12:25:56 -06:00
|
|
|
Err($expected_err),
|
2020-06-01 12:30:27 -06:00
|
|
|
);
|
2020-05-20 09:32:10 -06:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
success!("0", 0);
|
|
|
|
|
success!("1", 1);
|
|
|
|
|
success!("1", 1);
|
|
|
|
|
success!("100", 100);
|
|
|
|
|
success!("+100", 100);
|
|
|
|
|
|
2020-06-11 12:25:56 -06:00
|
|
|
success!("0x0", 0);
|
|
|
|
|
success!("0xf", 15);
|
|
|
|
|
success!("0xdeadbeef", 3_735_928_559);
|
|
|
|
|
|
2020-05-20 09:32:10 -06:00
|
|
|
success!("1KB", 1000);
|
|
|
|
|
success!("2MB", 2000000);
|
|
|
|
|
success!("3GB", 3000000000);
|
|
|
|
|
success!("4TB", 4000000000000);
|
|
|
|
|
success!("+4TB", 4000000000000);
|
|
|
|
|
|
|
|
|
|
success!("1GiB", 1073741824);
|
|
|
|
|
success!("2TiB", 2199023255552);
|
|
|
|
|
success!("+2TiB", 2199023255552);
|
|
|
|
|
|
|
|
|
|
success!("0xff", 255);
|
|
|
|
|
success!("0xEE", 238);
|
|
|
|
|
success!("+0xFF", 255);
|
|
|
|
|
|
2020-05-20 09:35:43 -06:00
|
|
|
success!("1block", 512, 512);
|
|
|
|
|
success!("2block", 512, 1024);
|
|
|
|
|
success!("1block", 4, 4);
|
|
|
|
|
success!("2block", 4, 8);
|
|
|
|
|
|
2020-05-20 09:32:10 -06:00
|
|
|
// empty string is invalid
|
2020-06-11 12:25:56 -06:00
|
|
|
error!("", Empty);
|
2020-05-20 09:32:10 -06:00
|
|
|
// These are also bad.
|
2020-06-11 12:25:56 -06:00
|
|
|
error!("+", EmptyAfterSign);
|
|
|
|
|
error!("-", Negative);
|
|
|
|
|
error!("K", InvalidNumAndUnit("K".to_owned()));
|
|
|
|
|
error!("k", InvalidNumAndUnit("k".to_owned()));
|
|
|
|
|
error!("m", InvalidNumAndUnit("m".to_owned()));
|
|
|
|
|
error!("block", EmptyWithUnit("block".to_owned()));
|
2020-05-20 09:32:10 -06:00
|
|
|
// leading/trailing space is invalid
|
2020-06-11 12:25:56 -06:00
|
|
|
error!(" 0", InvalidNumAndUnit(" 0".to_owned()));
|
|
|
|
|
error!("0 ", InvalidUnit(" ".to_owned()));
|
2020-05-20 09:32:10 -06:00
|
|
|
// Negatives make no sense for byte counts
|
2020-06-11 12:25:56 -06:00
|
|
|
error!("-1", Negative);
|
|
|
|
|
error!("-0x-12", Negative);
|
|
|
|
|
error!("0x-12", SignFoundAfterHexPrefix('-'));
|
2020-05-20 09:32:10 -06:00
|
|
|
// This was previously accepted but shouldn't be.
|
2020-06-11 12:25:56 -06:00
|
|
|
error!("0x+12", SignFoundAfterHexPrefix('+'));
|
2020-05-20 09:32:10 -06:00
|
|
|
// invalid suffix
|
2020-06-11 12:25:56 -06:00
|
|
|
error!("1234asdf", InvalidUnit("asdf".to_owned()));
|
2020-05-20 09:32:10 -06:00
|
|
|
// bad numbers
|
2020-06-11 12:25:56 -06:00
|
|
|
error!("asdf1234", InvalidNumAndUnit("asdf1234".to_owned()));
|
|
|
|
|
error!("a1s2d3f4", InvalidNumAndUnit("a1s2d3f4".to_owned()));
|
2020-05-20 09:32:10 -06:00
|
|
|
// multiplication overflows u64
|
2020-06-11 12:25:56 -06:00
|
|
|
error!("20000000TiB", UnitMultiplicationOverflow);
|
|
|
|
|
|
|
|
|
|
assert!(match parse_byte_count("99999999999999999999", PositiveI64::new(512).unwrap()) {
|
|
|
|
|
// We can't check against the kind of the `ParseIntError`, so we'll just make sure it's the
|
|
|
|
|
// same as trying to do the parse directly.
|
|
|
|
|
Err(ParseNum(e)) => e == "99999999999999999999".parse::<i64>().unwrap_err(),
|
|
|
|
|
_ => false,
|
|
|
|
|
});
|
2019-10-07 19:20:02 +01:00
|
|
|
}
|