#[cfg(not(feature = "std"))]
use alloc::string::{String, ToString};
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
pub fn smart_quotes(s: &str) -> String {
let mut out = s.to_string();
smart_quotes_in_place(&mut out);
out
}
pub(crate) fn smart_quotes_in_place(output: &mut String) {
let mut scratch = String::with_capacity(output.len());
let mut in_double = false;
let mut in_single = false;
let chars: Vec<char> = output.chars().collect();
for i in 0..chars.len() {
let c = chars[i];
match c {
'"' => {
if in_double {
scratch.push('\u{201D}');
in_double = false;
} else {
scratch.push('\u{201C}');
in_double = true;
}
}
'\'' => {
let prev = if i > 0 { Some(chars[i - 1]) } else { None };
let next = chars.get(i + 1).copied();
let is_apostrophe = prev.map(|p| p.is_alphanumeric()).unwrap_or(false)
&& next.map(|n| n.is_alphanumeric()).unwrap_or(false);
if is_apostrophe {
scratch.push('\u{2019}');
} else if in_single {
scratch.push('\u{2019}');
in_single = false;
} else {
scratch.push('\u{2018}');
in_single = true;
}
}
_ => scratch.push(c),
}
}
core::mem::swap(output, &mut scratch);
}
pub fn em_dash_nested_parentheticals(s: &str) -> String {
s.to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn smart_quotes_opens_and_closes_doubles() {
assert_eq!(
smart_quotes(r#"He said "hello there""#),
"He said \u{201C}hello there\u{201D}"
);
}
#[test]
fn smart_quotes_apostrophe_between_letters() {
assert_eq!(smart_quotes("it's Alice's"), "it\u{2019}s Alice\u{2019}s");
}
#[test]
fn smart_quotes_open_close_singles() {
assert_eq!(
smart_quotes("He said 'hello'"),
"He said \u{2018}hello\u{2019}"
);
}
#[test]
fn smart_quotes_mixed_quotes() {
let input = r#"She said "it's fine""#;
let expected = "She said \u{201C}it\u{2019}s fine\u{201D}";
assert_eq!(smart_quotes(input), expected);
}
#[test]
fn smart_quotes_passes_through_non_quote_content() {
assert_eq!(smart_quotes("plain text"), "plain text");
}
#[test]
fn em_dash_currently_passes_through() {
let s = "The class Foo, which has methods, was renamed";
assert_eq!(em_dash_nested_parentheticals(s), s);
}
}