use chrono::Local;
use regex::Regex;
use std::process;
use std::sync::Mutex;
use std::thread;
lazy_static! {
static ref RE_TRIPLE_SINGLE_QUOTE: Mutex<Regex> = Mutex::new(Regex::new(r"'''").unwrap());
}
#[cfg(windows)]
const NEW_LINE_SEQUENCE: &'static str = "\\r\\n";
#[cfg(not(windows))]
const NEW_LINE_SEQUENCE: &'static str = "\\n";
const CARRIAGE_RETURN: &'static char = &'\r';
const LINE_FEED: &'static char = &'\n';
enum NewLineType {
CarriageReturnLineFeed,
LineFeed,
}
pub struct Stringifier {}
impl Stringifier {
pub fn create_identify_table_name(seq: u128) -> String {
format!(
"\"Now={}&Pid={}&Thr={}&Seq={}\"",
Local::now().format("%Y-%m-%dT%H:%M:%S%z"),
process::id(),
Stringifier::thread_id().to_string(),
seq
)
.to_string()
}
pub fn thread_id() -> String {
format!("{:?}", thread::current().id())
}
pub fn format_str_value(value: &str) -> String {
let multi_line = if 1 < value.lines().count() {
true
} else {
false
};
if multi_line {
if value.contains("'''") {
let escaped_string = if let Some(escaped_trailing_newline_string) =
Stringifier::escape_trailing_newline(value)
{
Stringifier::escape_double_quotation(&escaped_trailing_newline_string)
} else {
Stringifier::escape_double_quotation(value)
};
return format!(
"\"\"\"
{}
\"\"\"",
escaped_string
);
}
format!(
"'''
{}
'''",
value
)
} else {
let escaped_trailng_newline_string = Stringifier::escape_trailing_newline(value);
if let Some(escaped_trailng_newline_string) = escaped_trailng_newline_string {
return format!(
"\"{}\"",
Stringifier::escape_double_quotation(&Stringifier::escape_back_slash(
&escaped_trailng_newline_string
))
);
}
if value.contains("'") {
return format!(
"\"{}\"",
Stringifier::escape_double_quotation(&Stringifier::escape_back_slash(value))
);
}
format!("'{}'", value)
}
}
pub fn escape_back_slash(text: &str) -> String {
text.replace("\\", "\\\\")
}
pub fn escape_double_quotation(text: &str) -> String {
text.replace("\"", "\\\"")
}
fn which_new_line_type(ch_vec: &Vec<char>) -> Option<NewLineType> {
if 0 < ch_vec.len() && &ch_vec[ch_vec.len() - 1] == LINE_FEED {
if 1 < ch_vec.len() && &ch_vec[ch_vec.len() - 2] == CARRIAGE_RETURN {
return Some(NewLineType::CarriageReturnLineFeed);
}
Some(NewLineType::LineFeed)
} else {
None
}
}
pub fn escape_trailing_newline(value: &str) -> Option<String> {
let ch_vec: Vec<char> = value.chars().collect();
if let Some(t) = Stringifier::which_new_line_type(&ch_vec) {
match t {
NewLineType::LineFeed => {
Some(format!("{}{}", value.trim_end(), NEW_LINE_SEQUENCE).to_string())
}
NewLineType::CarriageReturnLineFeed => {
Some(format!("{}{}", value.trim_end(), NEW_LINE_SEQUENCE).to_string())
}
}
} else {
None
}
}
}