#![deny(missing_docs)]
use regex::Regex;
pub fn minify_sql(document: &str) -> String {
let document_without_multiline_comments = remove_multiline_comments(document);
let mut document_without_comments =
remove_single_line_comments(&document_without_multiline_comments);
#[cfg(feature = "sqlite")]
{
document_without_comments =
protect_sqlite_autoincrement_integer(&document_without_comments);
}
for (long, short) in LONG_FORMAT_TYPES {
let re = Regex::new(&format!(r"\b{}\b", long)).unwrap();
document_without_comments = re
.replace_all(&document_without_comments, short)
.to_string();
}
#[cfg(feature = "sqlite")]
{
document_without_comments =
restore_sqlite_autoincrement_integer(&document_without_comments);
}
let mut output = document_without_comments
.split_whitespace()
.collect::<Vec<&str>>()
.join(" ");
for symbols in vec![
",", ";", "(", ")", ">", "<", ">=", "<=", "!=", "<>", "=", "+", "-", "*", "/",
] {
output = output.replace(&format!(" {}", symbols), symbols);
output = output.replace(&format!("{} ", symbols), symbols);
}
if output.ends_with(';') {
output.pop();
}
output
}
#[cfg(not(feature = "gluesql"))]
const LONG_FORMAT_TYPES: [(&str, &str); 5] = [
("INTEGER", "INT"),
("BOOLEAN", "BOOL"),
("CHARACTER", "CHAR"),
("DECIMAL", "DEC"),
("TEMPORARY", "TEMP"),
];
#[cfg(feature = "gluesql")]
const LONG_FORMAT_TYPES: [(&str, &str); 4] = [
("INTEGER", "INT"),
("CHARACTER", "CHAR"),
("DECIMAL", "DEC"),
("TEMPORARY", "TEMP"),
];
fn remove_multiline_comments(sql_content: &str) -> String {
let mut output = String::new();
let mut last_char = char::default();
let mut number_of_open_comments: u32 = 0;
for mut c in sql_content.chars() {
if number_of_open_comments > 0 && last_char == '*' && c == '/' {
number_of_open_comments -= 1;
c = char::default();
} else if last_char == '/' && c == '*' {
number_of_open_comments += 1;
c = char::default();
} else if number_of_open_comments == 0 {
if c != '/' {
if last_char == '/' {
output.push('/');
}
output.push(c);
}
}
last_char = c;
}
output
}
fn remove_single_line_comments(document: &str) -> String {
let mut output = String::new();
for line in document.lines() {
let mut last_char = char::default();
for c in line.chars() {
if last_char == '-' && c == '-' {
output.pop();
break;
}
output.push(c);
last_char = c;
}
output.push(' ');
}
output
}
#[cfg(feature = "sqlite")]
fn protect_sqlite_autoincrement_integer(sql: &str) -> String {
let column_re = Regex::new(r"(?i)\b\w+\s+[^,]*\bAUTOINCREMENT\b[^,]*").unwrap();
let protected_sql = column_re.replace_all(sql, |caps: ®ex::Captures| {
let column_def = &caps[0];
let modified_column_def = column_def.replace("INTEGER", "INT_PROTECTED");
modified_column_def
});
protected_sql.to_string()
}
#[cfg(feature = "sqlite")]
fn restore_sqlite_autoincrement_integer(sql: &str) -> String {
sql.replace("INT_PROTECTED", "INTEGER")
}