use oxc_allocator::{Allocator, ArenaStringBuilder};
use oxc_str::Str;
use crate::parser::{
pattern_parser::{
character,
unicode_escape::{UnicodeEscape, decode},
},
reader::CodePoint,
};
use crate::surrogate_pair;
pub(super) fn capturing_group_name<'a>(
raw: &'a str,
units: &[CodePoint],
unicode_mode: bool,
allocator: &'a Allocator,
) -> Option<Str<'a>> {
if units.is_empty() {
return None;
}
let mut name =
raw.contains('\\').then(|| ArenaStringBuilder::with_capacity_in(raw.len(), allocator));
let mut index = 0;
while index < units.len() {
let (mut value, mut next) = decode_code_point(units, index)?;
if !unicode_mode
&& units[index].value != '\\' as u32
&& surrogate_pair::is_lead_surrogate(value)
&& let Some(trail) = units.get(next).map(|unit| unit.value)
&& surrogate_pair::is_trail_surrogate(trail)
{
value = surrogate_pair::combine_surrogate_pair(value, trail);
next += 1;
}
let valid = if index == 0 {
character::is_identifier_start_char(value)
} else {
character::is_identifier_part_char(value)
};
if !valid {
return None;
}
if let Some(name) = &mut name {
name.push(char::from_u32(value)?);
}
index = next;
}
Some(name.map_or_else(|| Str::from(raw), |name| Str::from(name.into_str())))
}
fn decode_code_point(units: &[CodePoint], start: usize) -> Option<(u32, usize)> {
let value = units.get(start)?.value;
if value != '\\' as u32 {
return Some((value, start + 1));
}
match decode(&units[start + 1..], true) {
UnicodeEscape::Value { code_point, length } => Some((code_point, start + 1 + length)),
UnicodeEscape::Invalid | UnicodeEscape::Overflow { .. } => None,
}
}