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:
parent
44850710df
commit
a63a519ede
@ -1,12 +1,14 @@
|
|||||||
mod message;
|
mod message;
|
||||||
mod mtterror;
|
mod mtterror;
|
||||||
|
mod name;
|
||||||
|
|
||||||
use message::{
|
use message::{
|
||||||
Action, Addition, CalcValue, Calculation, Clock, CreateDoc, Field, FieldType, Include, Message,
|
Action, Addition, CalcValue, Calculation, Clock, CreateDoc, Field, FieldType, Include, Message,
|
||||||
Name, NameType, Operand, Path, Queue, RegMsg, Register, Session,
|
Operand, Path, Queue, RegMsg, Register, Session,
|
||||||
};
|
};
|
||||||
pub use message::{MsgAction, Query};
|
pub use message::{MsgAction, Query};
|
||||||
use mtterror::MTTError;
|
use mtterror::MTTError;
|
||||||
|
use name::{Name, NameType};
|
||||||
use std::sync::mpsc::{channel, Receiver};
|
use std::sync::mpsc::{channel, Receiver};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
|||||||
517
src/message.rs
517
src/message.rs
@ -1,5 +1,6 @@
|
|||||||
|
use super::MTTError;
|
||||||
|
use crate::name::{Name, NameType, Names};
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use isolang::Language;
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
ops::{Add, AddAssign},
|
ops::{Add, AddAssign},
|
||||||
@ -11,16 +12,6 @@ use std::{
|
|||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use super::MTTError;
|
|
||||||
|
|
||||||
/*
|
|
||||||
#[cfg(test)]
|
|
||||||
mod support_test {
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
pub static TIMEOUT: Duration = Duration::from_millis(500);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
@ -573,60 +564,11 @@ mod route_ids {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToString for NameType {
|
|
||||||
fn to_string(&self) -> String {
|
|
||||||
match self {
|
|
||||||
Self::ID(data) => data.to_string(),
|
|
||||||
Self::Name(data) => data.to_string(),
|
|
||||||
Self::None => "'{None}'".to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Path {
|
pub struct Path {
|
||||||
msg_id: Include<Uuid>,
|
pub msg_id: Include<Uuid>,
|
||||||
doc: Include<NameType>,
|
pub doc: Include<NameType>,
|
||||||
action: Include<Action>,
|
pub action: Include<Action>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Path {
|
impl Path {
|
||||||
@ -639,439 +581,6 @@ impl Path {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
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)]
|
|
||||||
struct Names {
|
|
||||||
names: HashMap<Name, Uuid>,
|
|
||||||
ids: HashMap<Uuid, HashMap<Language, Name>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Names {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
names: HashMap::new(),
|
|
||||||
ids: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_names(&mut self, names: Vec<Name>) -> Result<Uuid, MTTError> {
|
|
||||||
for name in names.iter() {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
fn add_translation(&mut self, name: Name, translation: Name) -> Result<Uuid, MTTError> {
|
|
||||||
let id = match self.get_id(&name) {
|
|
||||||
Ok(data) => data.clone(),
|
|
||||||
Err(err) => return Err(err),
|
|
||||||
};
|
|
||||||
match self.get_id(&translation) {
|
|
||||||
Ok(_) => return Err(MTTError::NameDuplicate(translation)),
|
|
||||||
Err(_) => {}
|
|
||||||
}
|
|
||||||
let holder = self.ids.get_mut(&id).unwrap();
|
|
||||||
holder.insert(translation.get_language().clone(), translation.clone());
|
|
||||||
self.names.insert(translation, id);
|
|
||||||
Ok(id.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_name(&self, id: &Uuid, lang: &Language) -> Result<Name, MTTError> {
|
|
||||||
match self.ids.get(id) {
|
|
||||||
Some(langdb) => match langdb.get(lang) {
|
|
||||||
Some(name) => Ok(name.clone()),
|
|
||||||
None => Err(MTTError::NameMissingTranslation(lang.clone())),
|
|
||||||
},
|
|
||||||
None => Err(MTTError::NameInvalidID(id.clone())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
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::NameNotFound(Name::english(
|
|
||||||
data.to_string().as_str(),
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NameType::None => Ok(Uuid::nil()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
fn is_empty(&self) -> bool {
|
|
||||||
self.names.is_empty()
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod names {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[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, name2);
|
|
||||||
assert_eq!(name1, name3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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 does_id_return_name() {
|
|
||||||
let mut names = Names::new();
|
|
||||||
let data = ["one", "two"];
|
|
||||||
let mut ids: HashMap<Name, Uuid> = HashMap::new();
|
|
||||||
for item in data.iter() {
|
|
||||||
let name = Name::english(item.clone());
|
|
||||||
ids.insert(name.clone(), names.add_names([name].to_vec()).unwrap());
|
|
||||||
}
|
|
||||||
for (name, id) in ids.iter() {
|
|
||||||
assert_eq!(
|
|
||||||
&names
|
|
||||||
.get_name(id, &Language::from_639_1("en").unwrap())
|
|
||||||
.unwrap(),
|
|
||||||
name
|
|
||||||
);
|
|
||||||
assert_eq!(&names.get_id(name).unwrap(), id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn errors_on_name_not_found() {
|
|
||||||
let names = Names::new();
|
|
||||||
let name = Name::english("missing");
|
|
||||||
let result = names.get_id(&name);
|
|
||||||
match result {
|
|
||||||
Ok(_) => unreachable!("got {:?}, should have been error", result),
|
|
||||||
Err(err) => match err {
|
|
||||||
MTTError::NameNotFound(output) => assert_eq!(output, name),
|
|
||||||
_ => unreachable!("got {:?}, should have been name not found", err),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
#[test]
|
|
||||||
fn errors_on_bad_id() {
|
|
||||||
let mut names = Names::new();
|
|
||||||
let id = Uuid::new_v4();
|
|
||||||
let result = names.get_name(&id, &Language::from_639_1("en").unwrap());
|
|
||||||
match result {
|
|
||||||
Ok(_) => unreachable!("got {:?}, should be invalid id error", result),
|
|
||||||
Err(err) => match err {
|
|
||||||
MTTError::NameInvalidID(data) => assert_eq!(data, id),
|
|
||||||
_ => unreachable!("got {:?}, should have been invalid id", err),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn errors_on_missing_translation() {
|
|
||||||
let mut names = Names::new();
|
|
||||||
let name = Name::english("task");
|
|
||||||
let lang = Language::from_639_1("ja").unwrap();
|
|
||||||
let id = names.add_names([name].to_vec()).unwrap();
|
|
||||||
let result = names.get_name(&id, &lang);
|
|
||||||
match result {
|
|
||||||
Ok(_) => unreachable!("got {:?}, should be invalid id error", result),
|
|
||||||
Err(err) => match err {
|
|
||||||
MTTError::NameMissingTranslation(data) => assert_eq!(data, lang),
|
|
||||||
_ => unreachable!("got {:?}, should have been invalid id", err),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn errors_on_duplicate_names() {
|
|
||||||
let mut names = Names::new();
|
|
||||||
let data = "test";
|
|
||||||
let name = Name::english(data);
|
|
||||||
names.add_names([name.clone()].to_vec()).unwrap();
|
|
||||||
let output = names.add_names([name.clone()].to_vec());
|
|
||||||
match output {
|
|
||||||
Ok(_) => unreachable!(
|
|
||||||
"got {:?}, should have produced duplicate name error",
|
|
||||||
output
|
|
||||||
),
|
|
||||||
Err(err) => match err {
|
|
||||||
MTTError::NameDuplicate(result) => assert_eq!(result, name),
|
|
||||||
_ => unreachable!("got {:?}, should have been duplicate name", err),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
#[test]
|
|
||||||
fn allows_alternate_names() {
|
|
||||||
let mut names = Names::new();
|
|
||||||
let data = "test";
|
|
||||||
let alt = "テスト";
|
|
||||||
let english = Name::english(data);
|
|
||||||
let japanese = Name::japanese(alt);
|
|
||||||
let id = names.add_names([english.clone()].to_vec()).unwrap();
|
|
||||||
let result = names.add_translation(english, japanese.clone()).unwrap();
|
|
||||||
assert_eq!(result, id);
|
|
||||||
let output = names.get_name(&id, &Language::from_639_1("ja").unwrap());
|
|
||||||
assert_eq!(output.unwrap().to_string(), alt);
|
|
||||||
assert_eq!(names.get_id(&japanese).unwrap(), id);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn errors_on_bad_translation() {
|
|
||||||
let mut names = Names::new();
|
|
||||||
let data = "test";
|
|
||||||
let alt = "テスト";
|
|
||||||
let english = Name::english(data);
|
|
||||||
let japanese = Name::japanese(alt);
|
|
||||||
let result = names.add_translation(japanese.clone(), english);
|
|
||||||
match result {
|
|
||||||
Ok(_) => unreachable!("got {:?}, should be invalid id error", result),
|
|
||||||
Err(err) => match err {
|
|
||||||
MTTError::NameNotFound(output) => assert_eq!(output, japanese),
|
|
||||||
_ => unreachable!("got {:?}, should have been invalid id", err),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn errors_on_translation_duplicates() {
|
|
||||||
let mut names = Names::new();
|
|
||||||
let data = "test";
|
|
||||||
let alt = "テスト";
|
|
||||||
let english = Name::english(data);
|
|
||||||
let japanese = Name::japanese(alt);
|
|
||||||
let id = names.add_names([english.clone()].to_vec()).unwrap();
|
|
||||||
let id = names.add_names([japanese.clone()].to_vec()).unwrap();
|
|
||||||
let result = names.add_translation(english, japanese.clone());
|
|
||||||
match result {
|
|
||||||
Ok(_) => unreachable!(
|
|
||||||
"got {:?}, should have produced duplicate name error",
|
|
||||||
result
|
|
||||||
),
|
|
||||||
Err(err) => match err {
|
|
||||||
MTTError::NameDuplicate(result) => assert_eq!(result, japanese),
|
|
||||||
_ => unreachable!("got {:?}, should have been duplicate name", err),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn convert_path_to_route_with_ids() {
|
|
||||||
let mut names = Names::new();
|
|
||||||
let data = "data";
|
|
||||||
let english = Name::english(data);
|
|
||||||
let id = names.add_names([english.clone()].to_vec()).unwrap();
|
|
||||||
let msg_id = Uuid::new_v4();
|
|
||||||
let action = Action::Query;
|
|
||||||
let path = Path::new(
|
|
||||||
Include::Just(msg_id.clone()),
|
|
||||||
Include::Just(id.into()),
|
|
||||||
Include::Just(action.clone()),
|
|
||||||
);
|
|
||||||
let result = names.path_to_route(&path).unwrap();
|
|
||||||
assert_eq!(result.msg_id, Include::Just(msg_id));
|
|
||||||
assert_eq!(result.doc_type, Include::Just(id));
|
|
||||||
assert_eq!(result.action, Include::Just(action));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn convert_path_name_to_route() {
|
|
||||||
let mut names = Names::new();
|
|
||||||
let data = "data";
|
|
||||||
let english = Name::english(data);
|
|
||||||
let id = names.add_names([english.clone()].to_vec()).unwrap();
|
|
||||||
let msg_id = Uuid::new_v4();
|
|
||||||
let action = Action::Error;
|
|
||||||
let path = Path::new(
|
|
||||||
Include::Just(msg_id.clone()),
|
|
||||||
Include::Just(english.into()),
|
|
||||||
Include::Just(action.clone()),
|
|
||||||
);
|
|
||||||
let result = names.path_to_route(&path).unwrap();
|
|
||||||
assert_eq!(result.msg_id, Include::Just(msg_id));
|
|
||||||
assert_eq!(result.doc_type, Include::Just(id));
|
|
||||||
assert_eq!(result.action, Include::Just(action));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn convert_path_with_no_document_to_route() {
|
|
||||||
let names = Names::new();
|
|
||||||
let msg_id = Uuid::new_v4();
|
|
||||||
let action = Action::Show;
|
|
||||||
let path = Path::new(
|
|
||||||
Include::Just(msg_id.clone()),
|
|
||||||
Include::Just(NameType::None),
|
|
||||||
Include::Just(action.clone()),
|
|
||||||
);
|
|
||||||
let result = names.path_to_route(&path).unwrap();
|
|
||||||
assert_eq!(result.msg_id, Include::Just(msg_id));
|
|
||||||
assert_eq!(result.doc_type, Include::Just(Uuid::nil()));
|
|
||||||
assert_eq!(result.action, Include::Just(action));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn convert_path_to_route_all_documents() {
|
|
||||||
let names = Names::new();
|
|
||||||
let msg_id = Uuid::new_v4();
|
|
||||||
let action = Action::Query;
|
|
||||||
let path = Path::new(
|
|
||||||
Include::Just(msg_id.clone()),
|
|
||||||
Include::All,
|
|
||||||
Include::Just(action.clone()),
|
|
||||||
);
|
|
||||||
let result = names.path_to_route(&path).unwrap();
|
|
||||||
assert_eq!(result.msg_id, Include::Just(msg_id));
|
|
||||||
match result.doc_type {
|
|
||||||
Include::All => {}
|
|
||||||
Include::Just(_) => unreachable!("should return all"),
|
|
||||||
}
|
|
||||||
assert_eq!(result.action, Include::Just(action));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn convert_path_with_bad_id() {
|
|
||||||
let names = Names::new();
|
|
||||||
let msg_id = Uuid::new_v4();
|
|
||||||
let id = Uuid::new_v4();
|
|
||||||
let action = Action::Query;
|
|
||||||
let path = Path::new(
|
|
||||||
Include::Just(msg_id.clone()),
|
|
||||||
Include::Just(id.into()),
|
|
||||||
Include::Just(action.clone()),
|
|
||||||
);
|
|
||||||
match names.path_to_route(&path) {
|
|
||||||
Ok(data) => unreachable!("got {:?}, should have been an error", data),
|
|
||||||
Err(err) => match err {
|
|
||||||
MTTError::NameInvalidID(output) => assert_eq!(output, id),
|
|
||||||
_ => unreachable!("got {:?}, should have gotten invalid id", err),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn convert_path_with_bad_name() {
|
|
||||||
let names = Names::new();
|
|
||||||
let msg_id = Uuid::new_v4();
|
|
||||||
let name = Name::english("wrong");
|
|
||||||
let action = Action::Query;
|
|
||||||
let path = Path::new(
|
|
||||||
Include::Just(msg_id.clone()),
|
|
||||||
Include::Just(name.clone().into()),
|
|
||||||
Include::Just(action.clone()),
|
|
||||||
);
|
|
||||||
match names.path_to_route(&path) {
|
|
||||||
Ok(data) => unreachable!("got {:?}, should have been an error", data),
|
|
||||||
Err(err) => match err {
|
|
||||||
MTTError::NameNotFound(output) => assert_eq!(output, name),
|
|
||||||
_ => unreachable!("got {:?}, should have gotten invalid id", err),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum RegMsg {
|
pub enum RegMsg {
|
||||||
AddRoute(Path),
|
AddRoute(Path),
|
||||||
@ -1115,14 +624,14 @@ impl Register {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
struct Route {
|
pub struct Route {
|
||||||
action: Include<Action>,
|
action: Include<Action>,
|
||||||
doc_type: Include<Uuid>,
|
doc_type: Include<Uuid>,
|
||||||
msg_id: Include<Uuid>,
|
msg_id: Include<Uuid>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Route {
|
impl Route {
|
||||||
fn new(msg_id: Include<Uuid>, doc: Include<Uuid>, action: Include<Action>) -> Self {
|
pub fn new(msg_id: Include<Uuid>, doc: Include<Uuid>, action: Include<Action>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
action: action,
|
action: action,
|
||||||
doc_type: doc,
|
doc_type: doc,
|
||||||
@ -1571,8 +1080,8 @@ impl Router {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod routers {
|
mod routers {
|
||||||
use crate::support_tests::TIMEOUT;
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::support_tests::TIMEOUT;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_pass_message() {
|
fn can_pass_message() {
|
||||||
@ -1681,8 +1190,8 @@ impl Queue {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod queues {
|
mod queues {
|
||||||
use crate::support_tests::TIMEOUT;
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::support_tests::TIMEOUT;
|
||||||
use std::sync::mpsc::RecvTimeoutError;
|
use std::sync::mpsc::RecvTimeoutError;
|
||||||
|
|
||||||
struct TestQueue {
|
struct TestQueue {
|
||||||
@ -5244,8 +4753,8 @@ impl DocumentFile {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod document_files {
|
mod document_files {
|
||||||
use crate::support_tests::TIMEOUT;
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::support_tests::TIMEOUT;
|
||||||
use std::{sync::mpsc::RecvTimeoutError, thread::sleep};
|
use std::{sync::mpsc::RecvTimeoutError, thread::sleep};
|
||||||
|
|
||||||
fn standard_paths() -> Vec<Path> {
|
fn standard_paths() -> Vec<Path> {
|
||||||
@ -6925,8 +6434,8 @@ mod document_files {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod createdocs {
|
mod createdocs {
|
||||||
use crate::support_tests::TIMEOUT;
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::support_tests::TIMEOUT;
|
||||||
|
|
||||||
struct TestCreateDoc {
|
struct TestCreateDoc {
|
||||||
queue: Queue,
|
queue: Queue,
|
||||||
@ -7333,8 +6842,8 @@ impl MessageLog {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod message_logs {
|
mod message_logs {
|
||||||
use crate::support_tests::TIMEOUT;
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::support_tests::TIMEOUT;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn does_log_store_messages() {
|
fn does_log_store_messages() {
|
||||||
@ -7458,8 +6967,8 @@ impl Session {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod sessions {
|
mod sessions {
|
||||||
use crate::support_tests::TIMEOUT;
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::support_tests::TIMEOUT;
|
||||||
use std::{sync::mpsc::RecvTimeoutError, thread::sleep};
|
use std::{sync::mpsc::RecvTimeoutError, thread::sleep};
|
||||||
|
|
||||||
struct Setup {
|
struct Setup {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
|
use super::message::{Field, FieldType};
|
||||||
|
use crate::name::Name;
|
||||||
use isolang::Language;
|
use isolang::Language;
|
||||||
use super::message::{Field, FieldType, Name};
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -20,6 +21,7 @@ pub enum MTTError {
|
|||||||
NameDuplicate(Name),
|
NameDuplicate(Name),
|
||||||
NameInvalidID(Uuid),
|
NameInvalidID(Uuid),
|
||||||
NameMissingTranslation(Language),
|
NameMissingTranslation(Language),
|
||||||
|
NameNotUniquePerLanguage(Name),
|
||||||
NameNotFound(Name),
|
NameNotFound(Name),
|
||||||
QueryCannotChangeData,
|
QueryCannotChangeData,
|
||||||
RouteRequiresDocumentID,
|
RouteRequiresDocumentID,
|
||||||
|
|||||||
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),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user