Skip to content

Commit 9712949

Browse files
committed
Pretty print ascii set digit and letter ranges
1 parent f1e7e55 commit 9712949

File tree

1 file changed

+55
-10
lines changed

1 file changed

+55
-10
lines changed

crates/utils/src/ascii.rs

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,22 +43,67 @@ impl AsciiSet {
4343
pub const fn len(&self) -> usize {
4444
self.set.count_ones() as usize
4545
}
46+
47+
#[expect(clippy::cast_possible_truncation)]
48+
fn write_next_entry(&mut self, f: &mut Formatter<'_>) -> std::fmt::Result {
49+
let b = self.set.trailing_zeros() as u8;
50+
let run = (self.set >> b).trailing_ones() as u8;
51+
52+
let class_end = match b {
53+
b'0'..=b'9' => b'9',
54+
b'a'..=b'z' => b'z',
55+
b'A'..=b'Z' => b'Z',
56+
_ => b,
57+
};
58+
let range_len = (class_end - b + 1).min(run);
59+
60+
if range_len >= 4 {
61+
self.set &= !(((1 << range_len) - 1) << b);
62+
write!(f, "{:?}-{:?}", b as char, (b + range_len - 1) as char)
63+
} else {
64+
self.set &= !(1 << b);
65+
write!(f, "{:?}", b as char)
66+
}
67+
}
4668
}
4769

70+
/// Format the set for display, combining digit and letter ranges.
71+
///
72+
/// # Examples
73+
///
74+
/// ```
75+
/// # use utils::ascii::AsciiSet;
76+
/// assert_eq!(
77+
/// AsciiSet::from(|b: u8| b.is_ascii_lowercase()).to_string(),
78+
/// "'a'-'z'"
79+
/// );
80+
/// assert_eq!(
81+
/// AsciiSet::from(|b: u8| b.is_ascii_alphabetic()).to_string(),
82+
/// "'A'-'Z', 'a'-'z'"
83+
/// );
84+
/// assert_eq!(
85+
/// AsciiSet::from(|b: u8| matches!(b, b'.' | b'#' | b'0'..=b'9')).to_string(),
86+
/// "'#', '.', '0'-'9'"
87+
/// );
88+
/// assert_eq!(
89+
/// AsciiSet::from(|b: u8| b.is_ascii_graphic()).to_string(),
90+
/// concat!(
91+
/// "'!', '\"', '#', '$', '%', '&', '\\'', '(', ')', '*', '+', ',', '-', '.', '/', ",
92+
/// "'0'-'9', ':', ';', '<', '=', '>', '?', '@', 'A'-'Z', '[', '\\\\', ']', '^', '_', ",
93+
/// "'`', 'a'-'z', '{', '|', '}', '~'"
94+
/// )
95+
/// );
96+
/// ```
4897
impl Display for AsciiSet {
49-
#[expect(clippy::cast_possible_truncation)]
5098
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
51-
if self.set == 0 {
99+
if self.is_empty() {
52100
return write!(f, "(empty)");
53101
}
54-
55-
for (i, (c, _)) in BitIterator::ones(self.set).enumerate() {
56-
let c = c as u8 as char;
57-
if i == 0 {
58-
write!(f, "{c:?}")?;
59-
} else {
60-
write!(f, ", {c:?}")?;
61-
}
102+
let mut set = *self;
103+
set.write_next_entry(f)?;
104+
while !set.is_empty() {
105+
write!(f, ", ")?;
106+
set.write_next_entry(f)?;
62107
}
63108
Ok(())
64109
}

0 commit comments

Comments
 (0)