pub(crate) fn scan_meta_name(bytes: &[u8], start: usize) -> Option<(usize, usize, usize)> {
debug_assert!(bytes.get(start) == Some(&b'$'));
let name_start = start + 1;
if name_start >= bytes.len() || !is_ident_start(bytes[name_start]) {
return None;
}
let name_end = scan_ident_continue(bytes, name_start + 1);
Some((name_start, name_end, name_end))
}
pub(crate) fn scan_ellipsis_name(bytes: &[u8], start: usize) -> Option<(usize, usize, usize)> {
debug_assert!(bytes.get(start) == Some(&b'$'));
if start + 3 >= bytes.len() || bytes[start + 1] != b'$' || bytes[start + 2] != b'$' {
return None;
}
let name_start = start + 3;
if !is_ident_start(bytes[name_start]) {
return None;
}
let name_end = scan_ident_continue(bytes, name_start + 1);
Some((name_start, name_end, name_end))
}
pub(crate) fn scan_braced_name(input: &str, start: usize) -> Option<(usize, usize, usize)> {
debug_assert!(input.as_bytes().get(start) == Some(&b'$'));
debug_assert!(input.as_bytes().get(start + 1) == Some(&b'{'));
let name_start = start + 2;
let close_offset = input.get(name_start..)?.find('}')?;
let name_end = name_start + close_offset;
Some((name_start, name_end, name_end + 1))
}
pub(crate) fn utf8_char_len(b: u8) -> usize {
match b {
0x00..=0x7F => 1,
0xC0..=0xDF => 2,
0xE0..=0xEF => 3,
0xF0..=0xF7 => 4,
_ => 1,
}
}
fn is_ident_start(b: u8) -> bool {
b.is_ascii_alphabetic() || b == b'_'
}
fn scan_ident_continue(bytes: &[u8], mut i: usize) -> usize {
while i < bytes.len() && (bytes[i].is_ascii_alphanumeric() || bytes[i] == b'_') {
i += 1;
}
i
}