use crate::diagnostics::SchemaError;
use crate::line_helpers::Line;
pub(super) fn split_field_attributes(
attrs: &str,
offset: usize,
field_name: &str,
line: &Line<'_>,
) -> Result<Vec<(String, usize, usize)>, SchemaError> {
let mut attributes = Vec::new();
let mut current = String::new();
let mut depth = 0usize;
let mut current_start = None;
let mut pending_gap: Option<String> = None;
let mut in_string = false;
for (index, ch) in attrs.char_indices() {
if current.is_empty() {
if ch == '@' {
current.push(ch);
current_start = Some(offset + index);
pending_gap = None;
continue;
}
if (ch == '(' || ch == '[') && pending_gap.is_some() {
let attr_raw = pending_gap.take().unwrap_or_default();
let group_len = attribute_group_end(&attrs[index..]);
let group = &attrs[index..index + group_len];
let start = line.start + offset + index;
return Err(SchemaError::new(
format!(
"field `{field_name}`: attribute arguments must not be separated from \
the attribute name by whitespace — write `{attr_raw}{group}`, not \
`{attr_raw} {group}`",
),
start..start + group_len,
line.number,
));
}
if !ch.is_whitespace() {
pending_gap = None;
}
continue;
}
match ch {
'"' => {
in_string = !in_string;
current.push(ch);
}
'(' | '[' if !in_string => {
depth += 1;
current.push(ch);
}
')' | ']' if !in_string => {
depth = depth.saturating_sub(1);
current.push(ch);
}
ch if ch.is_whitespace() && depth == 0 && !in_string => {
let start = current_start.take().unwrap_or(offset + index);
pending_gap = Some(current.clone());
attributes.push((std::mem::take(&mut current), start, offset + index));
}
_ => current.push(ch),
}
}
if !current.is_empty() {
let start = current_start.unwrap_or(offset + attrs.len().saturating_sub(current.len()));
attributes.push((current, start, offset + attrs.len()));
}
Ok(attributes)
}
fn attribute_group_end(text: &str) -> usize {
let mut depth = 0usize;
let mut in_string = false;
for (index, ch) in text.char_indices() {
match ch {
'"' => in_string = !in_string,
'(' | '[' if !in_string => depth += 1,
')' | ']' if !in_string => {
depth -= 1;
if depth == 0 {
return index + ch.len_utf8();
}
}
_ => {}
}
}
text.len()
}