Moved names into separate module.
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 1s
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 1s
This commit is contained in:
272
src/name.rs
Normal file
272
src/name.rs
Normal file
@@ -0,0 +1,272 @@
|
||||
use crate::{
|
||||
message::{Include, Path, Route},
|
||||
mtterror::MTTError,
|
||||
};
|
||||
use isolang::Language;
|
||||
use std::collections::HashMap;
|
||||
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 ToString for Name {
|
||||
fn to_string(&self) -> String {
|
||||
self.name.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[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) {
|
||||
return Err(MTTError::NameNotUniquePerLanguage(name.clone()));
|
||||
} else {
|
||||
languages.push(lang);
|
||||
}
|
||||
if self.names.contains_key(&name) {
|
||||
return Err(MTTError::NameDuplicate(name.clone()));
|
||||
}
|
||||
}
|
||||
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::NameNotFound(data.clone())),
|
||||
},
|
||||
NameType::ID(data) => {
|
||||
if self.ids.contains_key(&data) {
|
||||
Ok(data)
|
||||
} else {
|
||||
if data == Uuid::nil() {
|
||||
Ok(data)
|
||||
} else {
|
||||
Err(MTTError::NameInvalidID(data))
|
||||
}
|
||||
}
|
||||
}
|
||||
NameType::None => Ok(Uuid::nil()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path_to_route(&self, path: &Path) -> Result<Route, MTTError> {
|
||||
let doc_id = match &path.doc {
|
||||
Include::Just(id_info) => match id_info {
|
||||
NameType::ID(id) => {
|
||||
if self.ids.contains_key(&id) {
|
||||
Include::Just(id.clone())
|
||||
} else {
|
||||
return Err(MTTError::NameInvalidID(id.clone()));
|
||||
}
|
||||
}
|
||||
NameType::Name(name) => {
|
||||
let id = match self.get_id(name) {
|
||||
Ok(data) => data,
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
Include::Just(id.clone())
|
||||
}
|
||||
NameType::None => Include::Just(Uuid::nil()),
|
||||
},
|
||||
Include::All => Include::All,
|
||||
};
|
||||
Ok(Route::new(path.msg_id.clone(), doc_id, path.action.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
#[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 {
|
||||
MTTError::NameDuplicate(result) => assert_eq!(result, name),
|
||||
_ => 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 {
|
||||
MTTError::NameNotUniquePerLanguage(data) => assert_eq!(data, name3),
|
||||
_ => 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 {
|
||||
MTTError::NameNotFound(result) => assert_eq!(result, name),
|
||||
_ => 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 {
|
||||
MTTError::NameInvalidID(result) => assert_eq!(result, id),
|
||||
_ => unreachable!("got {:?}, should have been missing error", err),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user