morethantext/src/name.rs

257 lines
7.3 KiB
Rust
Raw Normal View History

use crate::mtterror::{ErrorID, MTTError};
2025-12-26 13:02:15 -05:00
use isolang::Language;
use std::{collections::HashMap, fmt};
2025-12-26 13:02:15 -05:00
use uuid::Uuid;
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum NameType {
ID(Uuid),
Name(Name),
None,
}
impl From<&NameType> for NameType {
fn from(value: &NameType) -> Self {
value.clone()
}
}
impl From<Name> for NameType {
fn from(value: Name) -> Self {
Self::Name(value)
}
}
impl From<&Name> for NameType {
fn from(value: &Name) -> Self {
let name = value.clone();
Self::from(name)
}
}
impl From<Uuid> for NameType {
fn from(value: Uuid) -> Self {
Self::ID(value)
}
}
impl From<&Uuid> for NameType {
fn from(value: &Uuid) -> Self {
let id = value.clone();
Self::from(id)
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Name {
name: String,
lang: Language,
}
impl Name {
fn new(name: &str, lang: Language) -> Self {
Self {
name: name.to_lowercase(),
lang: lang,
}
}
fn get_language(&self) -> &Language {
&self.lang
}
pub fn english(name: &str) -> Self {
Self::new(name, Language::from_639_1("en").unwrap())
}
pub fn japanese(name: &str) -> Self {
Self::new(name, Language::from_639_1("ja").unwrap())
}
}
impl fmt::Display for Name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name)
2025-12-26 13:02:15 -05:00
}
}
#[cfg(test)]
pub mod test_support {
use super::*;
pub fn random_name() -> Name {
Name::english(Uuid::new_v4().to_string().as_str())
}
}
2025-12-26 13:02:15 -05:00
#[derive(Clone, Debug, PartialEq)]
pub struct Names {
names: HashMap<Name, Uuid>,
ids: HashMap<Uuid, HashMap<Language, Name>>,
}
impl Names {
pub fn new() -> Self {
Self {
names: HashMap::new(),
ids: HashMap::new(),
}
}
pub fn add_names(&mut self, names: Vec<Name>) -> Result<Uuid, MTTError> {
let mut languages: Vec<&Language> = Vec::new();
for name in names.iter() {
let lang = name.get_language();
if languages.contains(&lang) {
let err = MTTError::new(NameType::None, ErrorID::NameLanguageNotUnique);
return Err(err);
2025-12-26 13:02:15 -05:00
} else {
languages.push(lang);
}
if self.names.contains_key(&name) {
let err = MTTError::new(NameType::None, ErrorID::NameAlreadyExists);
return Err(err);
2025-12-26 13:02:15 -05:00
}
}
let mut id = Uuid::new_v4();
while self.ids.contains_key(&id) {
id = Uuid::new_v4();
}
for name in names.iter() {
self.names.insert(name.clone(), id.clone());
let mut holder: HashMap<Language, Name> = HashMap::new();
holder.insert(name.get_language().clone(), name.clone());
self.ids.insert(id.clone(), holder);
}
Ok(id)
}
pub fn get_id<NT>(&self, name: NT) -> Result<Uuid, MTTError>
where
NT: Into<NameType>,
{
match name.into() {
NameType::Name(data) => match self.names.get(&data) {
Some(id) => Ok(id.clone()),
None => Err(MTTError::new(NameType::None, ErrorID::NameNotFound)),
2025-12-26 13:02:15 -05:00
},
NameType::ID(data) => {
if self.ids.contains_key(&data) {
Ok(data)
} else {
if data == Uuid::nil() {
Ok(data)
} else {
Err(MTTError::new(NameType::None, ErrorID::NameNotFound))
2025-12-26 13:02:15 -05:00
}
}
}
NameType::None => Ok(Uuid::nil()),
}
}
}
#[cfg(test)]
mod names {
use super::*;
use std::collections::HashSet;
#[test]
fn are_names_lowercase() {
let name1 = Name::new("session", Language::from_639_1("en").unwrap());
let name2 = Name::new("Session", Language::from_639_1("en").unwrap());
let name3 = Name::english("SESSION");
assert_eq!(name1.to_string(), "session".to_string());
assert_eq!(name1, name2);
assert_eq!(name1, name3);
}
#[test]
fn does_new_id_match_retrieval_id() {
let name = Name::english("tester");
let mut names = Names::new();
let id = names.add_names(vec![name.clone()]).unwrap();
assert_eq!(names.get_id(name).unwrap(), id);
assert_eq!(names.get_id(id).unwrap(), id);
}
#[test]
fn multiple_languages_can_stored_on_same_id() {
let english = Name::english("tester");
let japanese = Name::japanese("テスト");
let mut names = Names::new();
let id = names
.add_names(vec![english.clone(), japanese.clone()])
.unwrap();
assert_eq!(names.get_id(english).unwrap(), id);
assert_eq!(names.get_id(japanese).unwrap(), id);
}
#[test]
fn are_name_ids_unique() {
let mut names = Names::new();
let data = ["one", "two", "three", "four", "five"];
let mut ids: HashSet<Uuid> = HashSet::new();
for item in data.iter() {
let name = Name::english(item);
ids.insert(names.add_names([name].to_vec()).unwrap());
}
assert_eq!(ids.len(), data.len());
}
#[test]
fn errors_on_duplicates() {
let name = Name::english("duplicate");
let mut names = Names::new();
names.add_names(vec![name.clone()]).unwrap();
match names.add_names(vec![name.clone()]) {
Ok(data) => unreachable!("got {:?}, should have been duplicate error", data),
Err(err) => match err.error_id() {
ErrorID::NameAlreadyExists => {}
2025-12-26 13:02:15 -05:00
_ => unreachable!("got {:?}, should have been duplicate error", err),
},
}
}
#[test]
fn errors_if_same_language_is_used() {
let name1 = Name::english("test");
let name2 = Name::japanese("テスト");
let name3 = Name::english("tester");
let mut names = Names::new();
match names.add_names(vec![name1, name2, name3.clone()]) {
Ok(data) => unreachable!("got {:?}, should have been needs to be unique", data),
Err(err) => match err.error_id() {
ErrorID::NameLanguageNotUnique => {}
2025-12-26 13:02:15 -05:00
_ => unreachable!("got {:?}, should have been name needs to be unique", err),
},
}
}
#[test]
fn errors_on_bad_name() {
let name = Name::english(Uuid::new_v4().to_string().as_str());
let names = Names::new();
match names.get_id(name.clone()) {
Ok(data) => unreachable!("got {:?}, should have been missing error", data),
Err(err) => match err.error_id() {
ErrorID::NameNotFound => {}
2025-12-26 13:02:15 -05:00
_ => unreachable!("got {:?}, should have been missing error", err),
},
}
}
#[test]
fn errors_on_bad_id() {
let id = Uuid::new_v4();
let names = Names::new();
match names.get_id(id.clone()) {
Ok(data) => unreachable!("got {:?}, should have been missing error", data),
Err(err) => match err.error_id() {
ErrorID::NameNotFound => {}
2025-12-26 13:02:15 -05:00
_ => unreachable!("got {:?}, should have been missing error", err),
},
}
}
}