use vize_carton::String;
#[derive(Clone)]
pub(super) struct StringTrackState {
pub(super) in_string: bool,
string_char: char,
escape: bool,
pub(super) template_expr_brace_stack: Vec<i32>,
in_block_comment: bool,
}
impl Default for StringTrackState {
fn default() -> Self {
Self {
in_string: false,
string_char: '\0',
escape: false,
template_expr_brace_stack: Vec::new(),
in_block_comment: false,
}
}
}
pub(super) fn count_braces_with_state(line: &str, state: &mut StringTrackState) -> i32 {
let mut count: i32 = 0;
let chars: Vec<char> = line.chars().collect();
let len = chars.len();
let mut i = 0;
while i < len {
let ch = chars[i];
if state.escape {
state.escape = false;
i += 1;
continue;
}
if state.in_block_comment {
if ch == '*' && i + 1 < len && chars[i + 1] == '/' {
state.in_block_comment = false;
i += 2; continue;
}
i += 1;
continue;
}
if state.in_string {
if ch == '\\' {
state.escape = true;
i += 1;
continue;
}
if state.string_char == '`' {
if ch == '`' {
state.in_string = false;
} else if ch == '$' && i + 1 < len && chars[i + 1] == '{' {
state.in_string = false;
state.template_expr_brace_stack.push(0);
i += 2; continue;
}
} else if ch == state.string_char {
state.in_string = false;
}
} else {
match ch {
'/' if i + 1 < len && chars[i + 1] == '*' => {
state.in_block_comment = true;
i += 2; continue;
}
'/' if i + 1 < len && chars[i + 1] == '/' => {
break;
}
'\'' | '"' => {
state.in_string = true;
state.string_char = ch;
}
'`' => {
state.in_string = true;
state.string_char = '`';
}
'{' => {
if let Some(depth) = state.template_expr_brace_stack.last_mut() {
*depth += 1;
}
count += 1;
}
'}' => {
if let Some(&depth) = state.template_expr_brace_stack.last() {
if depth == 0 {
state.template_expr_brace_stack.pop();
state.in_string = true;
state.string_char = '`';
i += 1;
continue;
} else {
*state.template_expr_brace_stack.last_mut().unwrap() -= 1;
count -= 1;
}
} else {
count -= 1;
}
}
_ => {}
}
}
i += 1;
}
count
}
#[cfg(test)]
pub(super) fn count_braces_outside_strings(line: &str) -> i32 {
let mut state = StringTrackState::default();
count_braces_with_state(line, &mut state)
}
pub(super) fn count_parens_with_state(line: &str, state: &mut StringTrackState) -> i32 {
let mut count: i32 = 0;
let chars: Vec<char> = line.chars().collect();
let len = chars.len();
let mut i = 0;
while i < len {
let ch = chars[i];
if state.escape {
state.escape = false;
i += 1;
continue;
}
if state.in_block_comment {
if ch == '*' && i + 1 < len && chars[i + 1] == '/' {
state.in_block_comment = false;
i += 2; continue;
}
i += 1;
continue;
}
if state.in_string {
if ch == '\\' {
state.escape = true;
i += 1;
continue;
}
if state.string_char == '`' {
if ch == '`' {
state.in_string = false;
} else if ch == '$' && i + 1 < len && chars[i + 1] == '{' {
state.in_string = false;
state.template_expr_brace_stack.push(0);
i += 2;
continue;
}
} else if ch == state.string_char {
state.in_string = false;
}
} else {
match ch {
'/' if i + 1 < len && chars[i + 1] == '*' => {
state.in_block_comment = true;
i += 2; continue;
}
'/' if i + 1 < len && chars[i + 1] == '/' => {
break;
}
'\'' | '"' => {
state.in_string = true;
state.string_char = ch;
}
'`' => {
state.in_string = true;
state.string_char = '`';
}
'{' => {
if let Some(depth) = state.template_expr_brace_stack.last_mut() {
*depth += 1;
}
}
'}' => {
if let Some(&depth) = state.template_expr_brace_stack.last() {
if depth == 0 {
state.template_expr_brace_stack.pop();
state.in_string = true;
state.string_char = '`';
i += 1;
continue;
} else {
*state.template_expr_brace_stack.last_mut().unwrap() -= 1;
}
}
}
'(' => {
count += 1;
}
')' => {
count -= 1;
}
_ => {}
}
}
i += 1;
}
count
}
#[allow(dead_code)]
pub(super) fn compact_render_body(render_body: &str) -> String {
let mut result = String::default();
let mut chars = render_body.chars().peekable();
let mut paren_depth: i32 = 0;
let mut bracket_depth: i32 = 0;
let mut brace_depth: i32 = 0;
let mut in_string = false;
let mut string_char = '\0';
let mut in_template = false;
while let Some(ch) = chars.next() {
match ch {
'"' | '\'' if !in_template => {
if !in_string {
in_string = true;
string_char = ch;
} else if string_char == ch {
in_string = false;
}
result.push(ch);
}
'`' => {
in_template = !in_template;
result.push(ch);
}
'(' if !in_string && !in_template => {
paren_depth += 1;
result.push(ch);
}
')' if !in_string && !in_template => {
paren_depth = paren_depth.saturating_sub(1);
result.push(ch);
}
'[' if !in_string && !in_template => {
bracket_depth += 1;
result.push(ch);
}
']' if !in_string && !in_template => {
bracket_depth = bracket_depth.saturating_sub(1);
result.push(ch);
}
'{' if !in_string && !in_template => {
brace_depth += 1;
result.push(ch);
}
'}' if !in_string && !in_template => {
brace_depth = brace_depth.saturating_sub(1);
result.push(ch);
}
'\n' => {
if brace_depth > 0 && !in_string && !in_template {
result.push('\n');
} else if (paren_depth > 0 || bracket_depth > 0) && !in_string && !in_template {
result.push(' ');
while let Some(&next_ch) = chars.peek() {
if next_ch.is_whitespace() && next_ch != '\n' {
chars.next();
} else {
break;
}
}
} else {
result.push(ch);
}
}
_ => result.push(ch),
}
}
result
}