pub fn to_snake_case(s: &str) -> String {
let mut result = String::new();
let mut prev_was_upper = false;
for (i, c) in s.chars().enumerate() {
if c.is_uppercase() {
if i > 0 && (prev_was_upper || c.is_alphanumeric()) {
result.push('_');
}
result.extend(c.to_lowercase());
prev_was_upper = true;
} else {
result.push(c);
prev_was_upper = false;
}
}
result
}
pub fn to_camel_case(s: &str) -> String {
let mut result = String::new();
let mut capitalize_next = false;
for c in s.chars() {
if c == '_' {
capitalize_next = true;
} else if capitalize_next {
result.extend(c.to_uppercase());
capitalize_next = false;
} else {
result.push(c);
}
}
result
}
pub fn to_pascal_case(s: &str) -> String {
let mut result = String::new();
let mut capitalize_next = true;
for c in s.chars() {
if c == '_' {
capitalize_next = true;
} else if capitalize_next {
result.extend(c.to_uppercase());
capitalize_next = false;
} else {
result.push(c);
}
}
result
}
pub fn unescape_string(s: &str) -> String {
let mut result = String::new();
let mut chars = s.chars();
while let Some(c) = chars.next() {
if c == '\\' {
match chars.next() {
Some('n') => result.push('\n'),
Some('t') => result.push('\t'),
Some('r') => result.push('\r'),
Some('\\') => result.push('\\'),
Some('\'') => result.push('\''),
Some('"') => result.push('"'),
Some('0') => result.push('\0'),
Some(escape_char) => {
result.push('\\');
result.push(escape_char);
}
None => result.push('\\'),
}
} else {
result.push(c);
}
}
result
}
pub fn escape_string(s: &str) -> String {
let mut result = String::new();
for c in s.chars() {
match c {
'\n' => result.push_str("\\n"),
'\t' => result.push_str("\\t"),
'\r' => result.push_str("\\r"),
'\\' => result.push_str("\\\\"),
'\'' => result.push_str("\\'"),
'"' => result.push_str("\\\""),
'\0' => result.push_str("\\0"),
c => result.push(c),
}
}
result
}
pub fn strip_quotes(s: &str) -> Option<&str> {
if s.len() < 2 {
return None;
}
let bytes = s.as_bytes();
let (start, end) = match (bytes[0], bytes[bytes.len() - 1]) {
(b'"', b'"') | (b'\'', b'\'') => (1, s.len() - 1),
_ => return None,
};
Some(&s[start..end])
}
pub fn is_valid_identifier(s: &str) -> bool {
if s.is_empty() {
return false;
}
let mut chars = s.chars();
let first = chars.next().unwrap();
if !first.is_alphabetic() && first != '_' {
return false;
}
chars.all(|c| c.is_alphanumeric() || c == '_')
}
pub fn normalize_whitespace(s: &str) -> String {
s.split_whitespace().collect::<Vec<_>>().join(" ")
}
pub fn indent_lines(s: &str, indent: &str) -> String {
s.lines()
.map(|line| format!("{}{}", indent, line))
.collect::<Vec<_>>()
.join("\n")
}
pub fn split_lines(s: &str) -> Vec<&str> {
s.split(|c| c == '\n' || c == '\r')
.filter(|line| !line.is_empty())
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_to_snake_case() {
assert_eq!(to_snake_case("MyFunction"), "my_function");
assert_eq!(to_snake_case("myFunction"), "my_function");
assert_eq!(to_snake_case("MY_FUNCTION"), "m_y__f_u_n_c_t_i_o_n");
}
#[test]
fn test_to_camel_case() {
assert_eq!(to_camel_case("my_function"), "myFunction");
assert_eq!(to_camel_case("my_function_name"), "myFunctionName");
}
#[test]
fn test_to_pascal_case() {
assert_eq!(to_pascal_case("my_function"), "MyFunction");
assert_eq!(to_pascal_case("my_function_name"), "MyFunctionName");
}
#[test]
fn test_unescape_string() {
assert_eq!(unescape_string("hello\\nworld"), "hello\nworld");
assert_eq!(unescape_string("tab\\there"), "tab\there");
}
#[test]
fn test_escape_string() {
assert_eq!(escape_string("hello\nworld"), "hello\\nworld");
assert_eq!(escape_string("tab\there"), "tab\\there");
}
#[test]
fn test_strip_quotes() {
assert_eq!(strip_quotes("\"hello\""), Some("hello"));
assert_eq!(strip_quotes("'hello'"), Some("hello"));
assert_eq!(strip_quotes("hello"), None);
}
#[test]
fn test_is_valid_identifier() {
assert!(is_valid_identifier("myVariable"));
assert!(is_valid_identifier("_myVar"));
assert!(is_valid_identifier("my_var123"));
assert!(!is_valid_identifier("123var"));
assert!(!is_valid_identifier(""));
}
}