use crate::ast::{Expr, Identifier, Span, StringSegment};
const FLUENT_MARKER: char = '\u{07DF}';
pub fn parse_string_segments(content: &str, span_start: usize, is_raw: bool) -> Vec<StringSegment> {
if is_raw {
return vec![StringSegment::Text { content: content.to_string(), span: Span { start: span_start, end: span_start + content.len() } }];
}
let mut segments = Vec::new();
let mut current_text = String::new();
let mut text_start = span_start;
let mut chars = content.char_indices().peekable();
let content_len = content.len();
while let Some((idx, ch)) = chars.next() {
match ch {
'\\' => {
if let Some((_, next_ch)) = chars.peek() {
if *next_ch == '{' || *next_ch == '}' {
current_text.push(*next_ch);
chars.next();
continue;
}
}
current_text.push(ch);
}
'{' => {
if !current_text.is_empty() {
segments.push(StringSegment::Text { content: current_text.clone(), span: Span { start: text_start, end: span_start + idx } });
current_text.clear();
}
let is_fluent = if let Some((_, next_ch)) = chars.peek() { *next_ch == FLUENT_MARKER } else { false };
if is_fluent {
chars.next();
}
let expr_start = span_start + idx;
let mut expr_content = String::new();
let mut brace_count = 1;
let mut expr_end = span_start + idx + 1;
while let Some((inner_idx, inner_ch)) = chars.next() {
match inner_ch {
'{' => {
brace_count += 1;
expr_content.push(inner_ch);
}
'}' => {
brace_count -= 1;
if brace_count == 0 {
expr_end = span_start + inner_idx;
break;
}
expr_content.push(inner_ch);
}
_ => {
expr_content.push(inner_ch);
}
}
}
let trimmed_expr = expr_content.trim();
segments.push(StringSegment::Interpolation { expr: Box::new(Expr::Ident(Identifier { name: trimmed_expr.to_string(), span: Span { start: expr_start, end: expr_end } })), is_fluent, span: Span { start: expr_start, end: expr_end + 1 } });
text_start = expr_end + 1;
}
_ => {
current_text.push(ch);
}
}
}
if !current_text.is_empty() {
segments.push(StringSegment::Text { content: current_text, span: Span { start: text_start, end: span_start + content_len } });
}
segments
}