#[must_use]
pub fn is_swift_safe(c: char) -> bool {
matches!(
c,
'A'..='Z'
| 'a'..='z'
| '0'..='9'
| ' '
| '/'
| '-'
| '?'
| ':'
| '('
| ')'
| '.'
| ','
| '\''
| '+'
| '{'
| '}'
| '\r'
| '\n'
)
}
pub fn to_swift_charset(s: &str) -> (String, bool) {
let mut out = String::with_capacity(s.len());
let mut had_replacements = false;
for c in s.chars() {
if is_swift_safe(c) {
out.push(c);
} else {
had_replacements = true;
let replacement = approximate(c);
out.push_str(replacement);
}
}
(out, had_replacements)
}
fn approximate(c: char) -> &'static str {
match c {
'À' | 'Á' | 'Â' | 'Ã' | 'Ä' | 'Å' => "A",
'Æ' => "AE",
'Ç' => "C",
'È' | 'É' | 'Ê' | 'Ë' => "E",
'Ì' | 'Í' | 'Î' | 'Ï' => "I",
'Ð' => "D",
'Ñ' => "N",
'Ò' | 'Ó' | 'Ô' | 'Õ' | 'Ö' | 'Ø' => "O",
'Ù' | 'Ú' | 'Û' | 'Ü' => "U",
'Ý' => "Y",
'Þ' => "TH",
'ß' => "ss",
'à' | 'á' | 'â' | 'ã' | 'ä' | 'å' => "a",
'æ' => "ae",
'ç' => "c",
'è' | 'é' | 'ê' | 'ë' => "e",
'ì' | 'í' | 'î' | 'ï' => "i",
'ð' => "d",
'ñ' => "n",
'ò' | 'ó' | 'ô' | 'õ' | 'ö' | 'ø' => "o",
'ù' | 'ú' | 'û' | 'ü' => "u",
'ý' | 'ÿ' => "y",
'þ' => "th",
'€' => "EUR",
'£' => "GBP",
'¥' => "JPY",
'\u{2018}' | '\u{2019}' | '\u{201C}' | '\u{201D}' => "'",
'\u{2013}' | '\u{2014}' => "-", '\u{2026}' => "...", _ => " ",
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ascii_letters_are_safe() {
for c in 'A'..='Z' {
assert!(is_swift_safe(c));
}
for c in 'a'..='z' {
assert!(is_swift_safe(c));
}
}
#[test]
fn test_digits_are_safe() {
for c in '0'..='9' {
assert!(is_swift_safe(c));
}
}
#[test]
fn test_swift_punctuation_safe() {
for c in [
' ', '/', '-', '?', ':', '(', ')', '.', ',', '\'', '+', '{', '}',
] {
assert!(is_swift_safe(c), "expected '{c}' to be SWIFT-safe");
}
}
#[test]
fn test_non_swift_chars_not_safe() {
assert!(!is_swift_safe('€'));
assert!(!is_swift_safe('ü'));
assert!(!is_swift_safe('ñ'));
}
#[test]
fn test_pure_ascii_no_replacement() {
let (s, replaced) = to_swift_charset("HELLO WORLD 123");
assert_eq!(s, "HELLO WORLD 123");
assert!(!replaced);
}
#[test]
fn test_umlaut_replacement() {
let (s, replaced) = to_swift_charset("Müller");
assert_eq!(s, "Muller");
assert!(replaced);
}
#[test]
fn test_euro_sign_replacement() {
let (s, replaced) = to_swift_charset("100€");
assert_eq!(s, "100EUR");
assert!(replaced);
}
#[test]
fn test_empty_string() {
let (s, replaced) = to_swift_charset("");
assert_eq!(s, "");
assert!(!replaced);
}
}