use std::borrow::Cow;
#[must_use]
pub fn separator_run_len(bytes: &[u8], at: usize) -> usize {
if at == 0 || !matches!(bytes.get(at - 1), Some(d) if d.is_ascii_digit()) {
return 0;
}
let mut end = at;
while matches!(bytes.get(end), Some(b'_')) {
end += 1;
}
if end == at || !matches!(bytes.get(end), Some(d) if d.is_ascii_digit()) {
return 0;
}
end - at
}
#[must_use]
pub fn strip_digit_separators(s: &str) -> Cow<'_, str> {
if s.contains('_') {
Cow::Owned(s.chars().filter(|c| *c != '_').collect())
} else {
Cow::Borrowed(s)
}
}
#[must_use]
pub fn parse_int_literal(text: &str) -> Option<i64> {
strip_digit_separators(text).parse::<i64>().ok()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_separator_is_underscores_with_a_digit_on_each_side() {
assert_eq!(separator_run_len(b"1_0", 1), 1);
assert_eq!(separator_run_len(b"1__0", 1), 2);
assert_eq!(separator_run_len(b"1_", 1), 0);
assert_eq!(separator_run_len(b"1_a", 1), 0);
assert_eq!(separator_run_len(b"_1", 0), 0);
assert_eq!(separator_run_len(b"a_1", 1), 0);
assert_eq!(separator_run_len(b"10", 1), 0);
assert_eq!(separator_run_len(b"", 0), 0);
}
#[test]
fn stripping_removes_every_separator_and_borrows_when_there_are_none() {
assert_eq!(strip_digit_separators("1_000"), "1000");
assert_eq!(strip_digit_separators("1_0_0"), "100");
assert_eq!(strip_digit_separators("3.141_592"), "3.141592");
assert!(matches!(strip_digit_separators("1000"), Cow::Borrowed(_)));
assert!(matches!(strip_digit_separators("1_000"), Cow::Owned(_)));
}
#[test]
fn an_int_literal_decodes_through_the_range_or_not_at_all() {
assert_eq!(parse_int_literal("0"), Some(0));
assert_eq!(parse_int_literal("1_000"), Some(1000));
assert_eq!(parse_int_literal("9223372036854775807"), Some(i64::MAX));
assert_eq!(parse_int_literal("9223372036854775808"), None);
assert_eq!(parse_int_literal("9_223_372_036_854_775_808"), None);
assert_eq!(parse_int_literal("99999999999999999999999"), None);
}
}