Skip to content

Commit

Permalink
Improve panic! messages in hex-literal (#664)
Browse files Browse the repository at this point in the history
  • Loading branch information
thomascastleman committed Nov 11, 2021
1 parent 3b81567 commit e9a6b98
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 3 deletions.
8 changes: 8 additions & 0 deletions hex-literal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [UNRELEASED]
### Changed
- Provide more info in `panic!` messages

### Added
- More tests for `hex!()` macro
- More internal documentation

## 0.3.3 (2021-07-17)
### Added
- Accept sequence of string literals ([#519])
Expand Down
23 changes: 20 additions & 3 deletions hex-literal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,16 @@ struct TokenTreeIter {
}

impl TokenTreeIter {
/// Constructs a new `TokenTreeIter` from a given `proc_macro::Literal`.
///
/// # Panics
/// This panics if the given `Literal` is not a string literal.
fn new(input: Literal) -> Self {
let mut buf: Vec<u8> = input.to_string().into();

match buf.as_slice() {
[b'"', .., b'"'] => (),
_ => panic!("expected string literals"),
_ => panic!("expected string literal, got `{}`", input),
};
buf.pop();
let mut iter = buf.into_iter().exclude_comments();
Expand All @@ -102,6 +106,11 @@ impl TokenTreeIter {
}
}

/// Parses a single hex character (a-f/A-F/0-9) as a `u8` from the `TokenTreeIter`'s
/// internal buffer, ignoring whitespace.
///
/// # Panics
/// This panics if a non-hex, non-whitespace character is encountered.
fn next_hex_val(&mut self) -> Option<u8> {
loop {
let v = self.buf.next()?;
Expand All @@ -110,7 +119,8 @@ impl TokenTreeIter {
b'A'..=b'F' => v - 55,
b'a'..=b'f' => v - 87,
b' ' | b'\r' | b'\n' | b'\t' => continue,
_ => panic!("encountered invalid character"),
0..=127 => panic!("encountered invalid character: `{}`", v as char),
_ => panic!("encountered invalid non-ASCII character"),
};
return Some(n);
}
Expand All @@ -120,6 +130,13 @@ impl TokenTreeIter {
impl Iterator for TokenTreeIter {
type Item = TokenTree;

/// Produces hex values (as `u8` literals) parsed from the `TokenTreeIter`'s
/// internal buffer, alternating with commas to separate the elements of the
/// generated array of bytes.
///
/// # Panics
/// This panics if the internal buffer contains an odd number of hex
/// characters.
fn next(&mut self) -> Option<TokenTree> {
let v = if self.is_punct {
TokenTree::Punct(Punct::new(',', Spacing::Alone))
Expand All @@ -145,7 +162,7 @@ pub fn hex(input: TokenStream) -> TokenStream {
for tt in ignore_groups(input) {
let iter = match tt {
TokenTree::Literal(literal) => TokenTreeIter::new(literal),
_ => panic!("expected string literals"),
unexpected => panic!("expected string literal, got `{}`", unexpected),
};
out_ts.extend(iter);
}
Expand Down
83 changes: 83 additions & 0 deletions hex-literal/tests/basic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use hex_literal::hex;

#[test]
fn single_literal() {
assert_eq!(hex!("ff e4"), [0xff, 0xe4]);
}

#[test]
fn empty() {
let nothing: [u8; 0] = hex!();
let empty_literals: [u8; 0] = hex!("" "" "");
let expected: [u8; 0] = [];
assert_eq!(nothing, expected);
assert_eq!(empty_literals, expected);
}

#[test]
fn upper_case() {
assert_eq!(hex!("AE DF 04 B2"), [0xae, 0xdf, 0x04, 0xb2]);
assert_eq!(hex!("FF BA 8C 00 01"), [0xff, 0xba, 0x8c, 0x00, 0x01]);
}

#[test]
fn mixed_case() {
assert_eq!(hex!("bF dd E4 Cd"), [0xbf, 0xdd, 0xe4, 0xcd]);
}

#[test]
fn multiple_literals() {
assert_eq!(
hex!(
"01 dd f7 7f"
"ee f0 d8"
),
[0x01, 0xdd, 0xf7, 0x7f, 0xee, 0xf0, 0xd8]
);
assert_eq!(
hex!(
"ff"
"e8 d0"
""
"01 1f"
"ab"
),
[0xff, 0xe8, 0xd0, 0x01, 0x1f, 0xab]
);
}

#[test]
fn no_spacing() {
assert_eq!(hex!("abf0d8bb0f14"), [0xab, 0xf0, 0xd8, 0xbb, 0x0f, 0x14]);
assert_eq!(
hex!("09FFd890cbcCd1d08F"),
[0x09, 0xff, 0xd8, 0x90, 0xcb, 0xcc, 0xd1, 0xd0, 0x8f]
);
}

#[test]
fn allows_various_spacing() {
// newlines
assert_eq!(
hex!(
"f
f
d
0
e
8
"
),
[0xff, 0xd0, 0xe8]
);
// tabs
assert_eq!(hex!("9f d 1 f07 3 01 "), [0x9f, 0xd1, 0xf0, 0x73, 0x01]);
// spaces
assert_eq!(hex!(" e e d0 9 1 f f "), [0xee, 0xd0, 0x91, 0xff]);
}

#[test]
fn can_use_const() {
const _: [u8; 4] = hex!("ff d3 01 7f");
}
41 changes: 41 additions & 0 deletions hex-literal/tests/comments.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use hex_literal::hex;

#[test]
fn single_line_comments() {
assert_eq!(hex!("dd 03 // comment"), [0xdd, 0x03]);
assert_eq!(
hex!(
"00 04 f0 // a comment here
54 fe // another comment"
),
[0x00, 0x04, 0xf0, 0x54, 0xfe]
);
assert_eq!(
hex!(
"// initial comment
01 02"
),
[0x01, 0x02]
);
}

#[test]
fn block_comments() {
assert_eq!(
hex!("00 01 02 /* intervening comment */ 03 04"),
[0x00, 0x01, 0x02, 0x03, 0x04]
);
assert_eq!(hex!("/* initial comment */ ff df dd"), [0xff, 0xdf, 0xdd]);
assert_eq!(
hex!(
"8f ff 7d /*
comment
on
several
lines
*/
d0 a3"
),
[0x8f, 0xff, 0x7d, 0xd0, 0xa3]
);
}

0 comments on commit e9a6b98

Please sign in to comment.