use super::super::lexical::javascript::{
find_javascript_pattern_outside_strings, statement_ends_with_semicolon,
};
use super::syntax::express_route_start_position;
const MAX_EXPRESS_ROUTE_REGISTRATION_LINES: usize = 12;
pub(super) fn express_route_statement(lines: &[String], start: usize) -> String {
if find_javascript_pattern_outside_strings(&lines[start], ".route(").is_some() {
return express_route_chain_statement(lines, start);
}
express_method_call_statement(lines, start)
}
fn express_method_call_statement(lines: &[String], start: usize) -> String {
let mut statement = String::new();
let mut depth = 0usize;
let mut quote = None;
let mut escaped = false;
let mut saw_route_call = false;
for (offset, line) in lines
.iter()
.skip(start)
.take(MAX_EXPRESS_ROUTE_REGISTRATION_LINES)
.enumerate()
{
let segment = line.trim();
if segment.is_empty() {
continue;
}
if !statement.is_empty() {
statement.push(' ');
}
statement.push_str(segment);
let scan_start = if offset == 0 {
route_method_open_position(segment).unwrap_or(0)
} else {
0
};
if route_call_is_closed(
&segment[scan_start..],
&mut depth,
&mut quote,
&mut escaped,
&mut saw_route_call,
) {
break;
}
}
statement
}
fn express_route_chain_statement(lines: &[String], start: usize) -> String {
let mut statement = String::new();
for (offset, line) in lines
.iter()
.skip(start)
.take(MAX_EXPRESS_ROUTE_REGISTRATION_LINES)
.enumerate()
{
let segment = line.trim();
if segment.is_empty() {
continue;
}
if offset > 0 && !segment.starts_with('.') {
break;
}
if !statement.is_empty() {
statement.push(' ');
}
statement.push_str(segment);
if statement_ends_with_semicolon(segment) {
break;
}
}
statement
}
pub(super) fn express_use_statement(lines: &[String], start: usize) -> String {
let mut statement = String::new();
let mut depth = 0usize;
let mut quote = None;
let mut escaped = false;
let mut saw_route_call = false;
for (offset, line) in lines
.iter()
.skip(start)
.take(MAX_EXPRESS_ROUTE_REGISTRATION_LINES)
.enumerate()
{
let segment = line.trim();
if segment.is_empty() {
continue;
}
if !statement.is_empty() {
statement.push(' ');
}
statement.push_str(segment);
let scan_start = if offset == 0 {
find_javascript_pattern_outside_strings(segment, ".use(").unwrap_or(0)
} else {
0
};
if route_call_is_closed(
&segment[scan_start..],
&mut depth,
&mut quote,
&mut escaped,
&mut saw_route_call,
) {
break;
}
}
statement
}
fn route_method_open_position(line: &str) -> Option<usize> {
let method_pos = express_route_start_position(line)?;
let open_relative_pos = line[method_pos..].find('(')?;
Some(method_pos + open_relative_pos)
}
fn route_call_is_closed(
segment: &str,
depth: &mut usize,
quote: &mut Option<char>,
escaped: &mut bool,
saw_route_call: &mut bool,
) -> bool {
for character in segment.chars() {
if let Some(quote_char) = quote {
if *escaped {
*escaped = false;
continue;
}
if character == '\\' {
*escaped = true;
continue;
}
if character == *quote_char {
*quote = None;
}
continue;
}
match character {
'\'' | '"' | '`' => *quote = Some(character),
'(' => {
*depth += 1;
*saw_route_call = true;
}
')' => {
*depth = depth.saturating_sub(1);
if *saw_route_call && *depth == 0 {
return true;
}
}
_ => {}
}
}
false
}
#[cfg(test)]
#[path = "statements_tests.rs"]
mod tests;