use crate::errors::LisaError;
use fnv::FnvHashMap;
use std::hash::BuildHasherDefault;
pub fn parse_character_replacements(
replacements: &str,
) -> Result<FnvHashMap<char, Option<char>>, LisaError> {
if replacements.trim().is_empty() {
return Ok(FnvHashMap::with_capacity_and_hasher(
0,
BuildHasherDefault::default(),
));
}
let mut final_replacements = FnvHashMap::default();
for replacement_object in replacements.split(',') {
let trimmed = replacement_object.trim();
if trimmed.is_empty() {
continue;
}
let Some((key, value)) = trimmed.split_once('=') else {
return Err(LisaError::RemapError(trimmed.to_owned()));
};
let Some(replacement) = remap_to_character(key)? else {
return Err(LisaError::RemapError(trimmed.to_owned()));
};
let replace_with = remap_to_character(value)?;
final_replacements.insert(replacement, replace_with);
}
Ok(final_replacements)
}
pub fn remap_to_character(data: &str) -> Result<Option<char>, LisaError> {
if data.is_empty() {
return Err(LisaError::RemapError(data.to_owned()));
}
if data == "undef" {
return Ok(None);
}
let character_amount = data.chars().count();
let first_character = data.chars().next().unwrap_or_default();
if character_amount == 1 {
if first_character.is_ascii_lowercase() {
return Ok(Some(first_character));
}
if first_character.is_ascii_uppercase() {
return Ok(Some(first_character));
}
} else if character_amount == 2 && first_character == '^' {
let second_character = data.chars().nth(1).unwrap_or_default();
if second_character.is_ascii_lowercase() {
return Ok(char::from_u32((second_character as u32 - 'a' as u32) + 1));
} else if second_character.is_ascii_uppercase() {
return Ok(char::from_u32((second_character as u32 - 'A' as u32) + 1));
} else if second_character == '-' {
return Ok(None);
}
return Err(LisaError::RemapError(data.to_owned()));
}
if first_character == '0' {
let second_char = data.chars().nth(1).unwrap_or_default();
if second_char == 'x'
&& let Ok(parsed) = u32::from_str_radix(&data[2..], 16)
{
return Ok(char::from_u32(parsed));
} else if let Ok(value) = u32::from_str_radix(&data[1..], 8) {
return Ok(char::from_u32(value));
}
}
if let Ok(number) = data.parse::<u32>() {
return Ok(char::from_u32(number));
}
Err(LisaError::RemapError(data.to_owned()))
}
#[cfg(test)]
mod unit_tests {
use super::*;
#[test]
pub fn remap_grabbag_strings() {
for (string, expected_value) in [
("", None),
("undef", Some(None)),
("a", Some(Some('a'))),
("A", Some(Some('A'))),
("-", None),
("^a", Some(Some('\u{1}'))),
("^Z", Some(Some('\u{1a}'))),
("^-", Some(None)),
("^$", None),
("abcdefghijklmnopqrstuvwxyz", None),
("0x10", Some(Some('\u{10}'))),
("010", Some(Some('\u{8}'))),
("9", Some(Some('\u{9}'))),
("0x99999999999999999999999999999999999999", None),
("099999999999999999999999999999999999999", None),
] {
assert_eq!(remap_to_character(string).ok(), expected_value);
}
}
}