From 5e448a0071d5d35ad40b113ee4843b9171c50456 Mon Sep 17 00:00:00 2001 From: Jeff Baskin Date: Mon, 12 Jan 2026 15:05:45 -0500 Subject: [PATCH] Moved document definitions into separate module. --- src/document.rs | 1 + src/document/create.rs | 5 +- src/document/definition.rs | 572 +++++++++++++++++++++++++++++++++++++ src/message.rs | 565 +----------------------------------- 4 files changed, 581 insertions(+), 562 deletions(-) create mode 100644 src/document/definition.rs diff --git a/src/document.rs b/src/document.rs index a5a2824..6c3ee71 100644 --- a/src/document.rs +++ b/src/document.rs @@ -1,2 +1,3 @@ pub mod clock; pub mod create; +pub mod definition; diff --git a/src/document/create.rs b/src/document/create.rs index 5c0fe51..1f08ca6 100644 --- a/src/document/create.rs +++ b/src/document/create.rs @@ -1,9 +1,10 @@ use crate::{ data_director::{Include, Path, RegMsg, Register, RouteID}, + document::definition::{DocDef, DocFuncType}, field::Field, message::{ - Action, CalcValue, Calculation, DocDef, DocFuncType, InternalRecord, InternalRecords, - Message, MsgAction, Oid, Query, Records, Reply, Update, + Action, CalcValue, Calculation, InternalRecord, InternalRecords, Message, MsgAction, Oid, + Query, Records, Reply, Update, }, mtterror::MTTError, name::NameType, diff --git a/src/document/definition.rs b/src/document/definition.rs new file mode 100644 index 0000000..f692184 --- /dev/null +++ b/src/document/definition.rs @@ -0,0 +1,572 @@ +use crate::{ + data_director::{Include, Path}, + document::create::IndexType, + field::{Field, FieldType}, + message::{Action, CalcValue, MsgAction}, + mtterror::MTTError, + name::{Name, NameType, Names}, +}; +use std::collections::{HashMap, HashSet}; +use uuid::Uuid; + +#[derive(Clone, Debug)] +struct FieldSetting { + fieldtype: FieldType, + default_value: CalcValue, +} + +impl FieldSetting { + fn new(ftype: FieldType) -> Self { + Self { + fieldtype: ftype, + default_value: CalcValue::None, + } + } + + fn set_default(&mut self, holder: CV) -> Result<(), MTTError> + where + CV: Into, + { + let value = holder.into(); + match &value { + CalcValue::Calculate(calc) => { + if self.fieldtype != calc.get_type() { + return Err(MTTError::FieldInvalidType); + } + } + CalcValue::Existing(ftype) | CalcValue::FType(ftype) => { + if &self.fieldtype != ftype { + return Err(MTTError::FieldInvalidType); + } + } + CalcValue::Value(ref item) => { + if item.get_type() != self.fieldtype { + return Err(MTTError::FieldInvalidType); + } + } + CalcValue::None => {} + } + self.default_value = value; + Ok(()) + } + + fn validate(&self, value: &Field) -> Result { + match value { + Field::None => match &self.default_value { + CalcValue::None => Err(MTTError::InvalidNone), + _ => Ok(self.default_value.get(&Field::None)), + }, + _ => { + let vft: FieldType = value.into(); + if vft != self.fieldtype { + return Err(MTTError::DocumentFieldWrongDataType( + self.fieldtype.clone(), + vft, + )); + } + Ok(value.clone()) + } + } + } +} + +#[cfg(test)] +mod fieldsettings { + use super::*; + use crate::message::{Calculation, Operand}; + use chrono::Utc; + use std::time::Duration; + + #[test] + fn validates_field_type() { + let fset = FieldSetting::new(FieldType::Uuid); + let value: Field = Uuid::new_v4().into(); + match fset.validate(&value) { + Ok(data) => assert_eq!(data, value), + Err(err) => unreachable!("got {:?}: should have gotten a value", err), + } + } + + #[test] + fn validates_for_bad_field_type() { + let fset = FieldSetting::new(FieldType::Uuid); + let value: Field = "text".into(); + match fset.validate(&value) { + Ok(data) => unreachable!("got {:?}: should have gotten an error", data), + Err(err) => match err { + MTTError::DocumentFieldWrongDataType(expected, got) => { + assert_eq!(expected, FieldType::Uuid); + assert_eq!(got, FieldType::StaticString); + } + _ => unreachable!("got {:?}: should have gotten a value", err), + }, + } + } + + #[test] + fn no_default_returns_error() { + let fset = FieldSetting::new(FieldType::Uuid); + match fset.validate(&Field::None) { + Ok(data) => unreachable!("got {:?}: should have gotten an error", data), + Err(err) => match err { + MTTError::InvalidNone => {} + _ => unreachable!("got {:?}: should have gotten a invalid none", err), + }, + } + } + + #[test] + fn returns_value_if_default_is_set() { + let mut fset = FieldSetting::new(FieldType::StaticString); + fset.set_default(FieldType::StaticString).unwrap(); + match fset.validate(&Field::None) { + Ok(data) => assert_eq!(data, "".into()), + Err(err) => unreachable!("got {:?}: should have gotten a value", err), + } + } + + #[test] + fn returns_default_value() { + let mut fset = FieldSetting::new(FieldType::StaticString); + let input = "fred"; + fset.set_default(input).unwrap(); + match fset.validate(&Field::None) { + Ok(data) => assert_eq!(data, input.into()), + Err(err) => unreachable!("got {:?}: should have gotten a value", err), + } + } + + #[test] + fn can_default_be_calculated() { + let mut fset = FieldSetting::new(FieldType::DateTime); + let duration = Duration::from_secs(3600); + let mut calc = Calculation::new(Operand::Add); + calc.add_value(FieldType::DateTime).unwrap(); + calc.add_value(duration).unwrap(); + fset.set_default(calc).unwrap(); + let start = Utc::now() + duration; + let result = match fset.validate(&Field::None).unwrap() { + Field::DateTime(data) => data, + _ => unreachable!("should return datetime"), + }; + let stop = Utc::now() + duration; + assert!( + result > start, + "{:?} should have been greater than {:?}", + result, + start + ); + assert!( + result < stop, + "{:?} should have been less than {:?}", + result, + stop + ); + } +} + +#[derive(Clone, Debug)] +pub enum DocFuncType { + Add, + Delete, + ExistingQuery(MsgAction), + Query, + Show, + Trigger(MsgAction), + Update, +} + +#[derive(Clone, Debug)] +pub struct PathAction { + path: Path, + func_type: DocFuncType, +} + +impl PathAction { + fn new(path: Path, func_type: DocFuncType) -> Self { + Self { + path: path, + func_type: func_type, + } + } + + pub fn path(&self) -> Path { + self.path.clone() + } + + pub fn doc_function(&self) -> DocFuncType { + self.func_type.clone() + } +} + +#[derive(Clone, Debug)] +pub struct DocDef { + doc_names: Vec, + field_names: Names, + fields: HashMap, + indexes: HashMap, + routes: Vec, +} + +impl DocDef { + pub fn new(name: Name) -> Self { + let names = vec![name]; + Self::with_names(names) + } + + fn with_names(names: Vec) -> Self { + let routes = vec![ + PathAction::new( + Path::new( + Include::All, + Include::Just(names[0].clone().into()), + Include::Just(Action::Addition), + ), + DocFuncType::Add, + ), + PathAction::new( + Path::new( + Include::All, + Include::Just(names[0].clone().into()), + Include::Just(Action::Delete), + ), + DocFuncType::Delete, + ), + PathAction::new( + Path::new( + Include::All, + Include::Just(names[0].clone().into()), + Include::Just(Action::Query), + ), + DocFuncType::Query, + ), + PathAction::new( + Path::new( + Include::All, + Include::Just(names[0].clone().into()), + Include::Just(Action::Show), + ), + DocFuncType::Show, + ), + PathAction::new( + Path::new( + Include::All, + Include::Just(names[0].clone().into()), + Include::Just(Action::Update), + ), + DocFuncType::Update, + ), + ]; + Self { + doc_names: names, + field_names: Names::new(), + fields: HashMap::new(), + indexes: HashMap::new(), + routes: routes, + } + } + + pub fn get_document_names(&self) -> &Vec { + &self.doc_names + } + + pub fn get_field_names(&self) -> &Names { + &self.field_names + } + + #[allow(dead_code)] + fn get_field_names_mut(&mut self) -> &mut Names { + &mut self.field_names + } + + pub fn add_field(&mut self, name: Name, ftype: FieldType) { + let id = self.field_names.add_names([name].to_vec()).unwrap(); + self.fields.insert(id, FieldSetting::new(ftype)); + } + + pub fn get_field_id(&self, field_name: NT) -> Result + where + NT: Into, + { + match self.field_names.get_id(field_name) { + Ok(data) => Ok(data), + Err(err) => Err(err), + } + } + + #[allow(dead_code)] + fn get_field(&self, field_name: NT) -> Result<&FieldSetting, MTTError> + where + NT: Into, + { + let id = match self.field_names.get_id(field_name) { + Ok(data) => data, + Err(err) => return Err(err), + }; + Ok(self.fields.get(&id).unwrap()) + } + + #[allow(dead_code)] + fn get_field_mut(&mut self, field_name: NT) -> Result<&mut FieldSetting, MTTError> + where + NT: Into, + { + let id = match self.field_names.get_id(field_name) { + Ok(data) => data, + Err(err) => return Err(err), + }; + Ok(self.fields.get_mut(&id).unwrap()) + } + + pub fn field_ids(&self) -> HashSet { + self.fields.keys().cloned().collect() + } + + pub fn validate(&self, field_name: NT, value: &Field) -> Result + where + NT: Into, + { + let id = match self.field_names.get_id(field_name) { + Ok(data) => data, + Err(err) => return Err(err), + }; + self.fields.get(&id).unwrap().validate(value) + } + + pub fn set_default(&mut self, field_name: &Name, value: CV) -> Result<(), MTTError> + where + CV: Into, + { + let id = match self.field_names.get_id(field_name) { + Ok(data) => data, + Err(err) => return Err(err), + }; + match self.fields.get_mut(&id).unwrap().set_default(value) { + Ok(_) => Ok(()), + Err(err) => Err(err), + } + } + + pub fn add_index(&mut self, field_name: &Name, index_type: IndexType) -> Result<(), MTTError> { + let id = match self.field_names.get_id(field_name) { + Ok(data) => data, + Err(err) => return Err(err), + }; + self.indexes.insert(id.clone(), index_type); + Ok(()) + } + + pub fn get_indexes(&self) -> &HashMap { + &self.indexes + } + + #[allow(dead_code)] + fn iter(&self) -> impl Iterator { + self.fields.iter() + } + + pub fn iter_routes(&self) -> impl Iterator { + self.routes.iter() + } + + pub fn add_route(&mut self, path: Path, action: DocFuncType) { + self.routes.push(PathAction::new(path, action)); + } +} + +#[cfg(test)] +mod docdefs { + use super::*; + use crate::message::{Query, Update}; + + #[test] + fn can_field_be_added() { + let docname = Name::english("tester"); + let mut docdef = DocDef::new(docname); + let name = Name::english(Uuid::new_v4().to_string().as_str()); + let field_type = FieldType::Uuid; + docdef.add_field(name.clone(), field_type.clone()); + let result = docdef.get_field(name).unwrap(); + match result.validate(&Uuid::new_v4().into()) { + Ok(_) => {} + Err(err) => unreachable!("got {:?}: should have been a value", err), + } + } + + #[test] + fn produces_error_for_bad_fields() { + let docname = Name::english("tester"); + let docdef = DocDef::new(docname); + let name = Name::english(Uuid::new_v4().to_string().as_str()); + match docdef.get_field(&name) { + Ok(_) => unreachable!("should return non existant field error"), + Err(err) => match err { + MTTError::NameNotFound(data) => assert_eq!(data, name), + _ => unreachable!("got {:?}: should have been document field not found", err), + }, + } + } + + #[test] + fn can_multiple_fields_be_added() { + let docname = Name::english("testing"); + let mut docdef = DocDef::new(docname); + let names = ["one", "two", "three"]; + let field_type = FieldType::StaticString; + for name in names.iter() { + docdef.add_field(Name::english(name), field_type.clone()); + } + for name in names.iter() { + let result = docdef.get_field(Name::english(name)).unwrap(); + match result.validate(&"".into()) { + Ok(_) => {} + Err(err) => unreachable!("got {:?}: should have been a value", err), + } + } + } + + #[test] + fn can_change_field_default_to_function() { + let docname = Name::english("something"); + let mut docdef = DocDef::new(docname); + let name = Name::english("defaultfunction"); + docdef.add_field(name.clone(), FieldType::StaticString); + docdef.set_default(&name, FieldType::StaticString).unwrap(); + match docdef.get_field(name).unwrap().validate(&Field::None) { + Ok(data) => match data { + Field::StaticString(result) => assert_eq!(result, ""), + _ => unreachable!("got {:?}: should return a static string", data), + }, + Err(err) => unreachable!("got {:?}: should return a value", err), + } + } + + #[test] + fn does_set_default_value_error_on_bad_field_name() { + let docname = Name::english("something"); + let mut docdef = DocDef::new(docname); + let field_name = Name::english(Uuid::new_v4().to_string().as_str()); + match docdef.set_default(&field_name, FieldType::Uuid) { + Ok(_) => unreachable!("should be an error"), + Err(err) => match err { + MTTError::NameNotFound(data) => assert_eq!(data, field_name), + _ => unreachable!("got {:?}: should have been field not found", err), + }, + } + } + + #[test] + fn does_set_default_value_error_on_bad_field_type() { + let docname = Name::english("something"); + let mut docdef = DocDef::new(docname); + let name = Name::english("defaultvalue"); + docdef.add_field(name.clone(), FieldType::Uuid); + match docdef.set_default(&name, "fred") { + Ok(data) => unreachable!("got {:?}, should be an error", data), + Err(err) => match err { + MTTError::FieldInvalidType => {} + _ => unreachable!("got {:?}: should have been field not found", err), + }, + } + } + + #[test] + fn does_default_routes_get_set() { + let default_num = 5; + let docname = Name::english(Uuid::new_v4().to_string().as_str()); + let docdef = DocDef::new(docname.clone()); + assert_eq!( + docdef.iter_routes().count(), + default_num, + "routes contained the following:\n{:?}", + docdef.routes + ); + let mut actions: HashSet = HashSet::new(); + let mut doc_funcs: HashSet = HashSet::new(); + for path_action in docdef.iter_routes() { + let path = path_action.path(); + match &path.msg_id { + Include::All => {} + _ => unreachable!("got {:?}, message id should include all", path.msg_id), + } + match &path.doc { + Include::Just(output) => match output { + NameType::Name(data) => assert_eq!(data, &docname), + _ => unreachable!("got {:?}, name type should be {:?}", path.doc, docname), + }, + _ => unreachable!("got {:?}, name type should be {:?}", path.doc, docname), + }; + match &path.action { + Include::Just(output) => match output { + Action::Addition => actions.insert(output.clone()), + Action::Delete => actions.insert(output.clone()), + Action::Query => actions.insert(output.clone()), + Action::Show => actions.insert(output.clone()), + Action::Update => actions.insert(output.clone()), + _ => unreachable!("got {:?} which is not a default action", output), + }, + _ => unreachable!("got {:?}, which is not a default action", path.action), + }; + let file_func = path_action.doc_function(); + match file_func { + DocFuncType::Add => doc_funcs.insert("Add".to_string()), + DocFuncType::Delete => doc_funcs.insert("Delete".to_string()), + DocFuncType::Query => doc_funcs.insert("Query".to_string()), + DocFuncType::Show => doc_funcs.insert("Show".to_string()), + DocFuncType::Update => doc_funcs.insert("Update".to_string()), + _ => unreachable!("got {:?}, which is not a default function", file_func), + }; + } + assert_eq!( + actions.len(), + default_num, + "got {:?}, missing some actions", + actions + ); + assert_eq!( + doc_funcs.len(), + default_num, + "got {:?}, missing some actions", + doc_funcs + ); + } + + #[test] + fn add_route_function() { + let docname = Name::english(Uuid::new_v4().to_string().as_str()); + let mut docdef = DocDef::new(docname.clone()); + docdef.add_route( + Path::new( + Include::All, + Include::Just(docname.clone().into()), + Include::Just(Action::OnQuery), + ), + DocFuncType::Trigger(Update::new(Query::new()).into()), + ); + let path_action = docdef.iter_routes().last().unwrap(); + let path = path_action.path(); + match &path.msg_id { + Include::All => {} + _ => unreachable!("got {:?}, message id should include all", path.msg_id), + }; + match &path.doc { + Include::Just(output) => match output { + NameType::Name(data) => assert_eq!(data, &docname), + _ => unreachable!("got {:?}, name type should be {:?}", path.doc, docname), + }, + _ => unreachable!("got {:?}, name type should be {:?}", path.doc, docname), + }; + match &path.action { + Include::Just(output) => match output { + Action::OnQuery => {} + _ => unreachable!("got {:?} which is not a additional action", output), + }, + _ => unreachable!("got {:?}, which is not on query action", path.action), + } + let file_func = path_action.doc_function(); + match file_func { + DocFuncType::Trigger(_) => {} + _ => unreachable!("got {:?}, which is not a default function", file_func), + } + } +} diff --git a/src/message.rs b/src/message.rs index 2f35e43..fc5ef46 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,6 +1,9 @@ use crate::{ data_director::{Include, Path, RegMsg, Register, Route}, - document::create::IndexType, + document::{ + create::IndexType, + definition::{DocDef, DocFuncType}, + }, field::{Field, FieldType}, mtterror::MTTError, name::{Name, NameType, Names}, @@ -435,159 +438,6 @@ mod messages { } } -#[derive(Clone, Debug)] -struct FieldSetting { - fieldtype: FieldType, - default_value: CalcValue, -} - -impl FieldSetting { - fn new(ftype: FieldType) -> Self { - Self { - fieldtype: ftype, - default_value: CalcValue::None, - } - } - - fn set_default(&mut self, holder: CV) -> Result<(), MTTError> - where - CV: Into, - { - let value = holder.into(); - match &value { - CalcValue::Calculate(calc) => { - if self.fieldtype != calc.get_type() { - return Err(MTTError::FieldInvalidType); - } - } - CalcValue::Existing(ftype) | CalcValue::FType(ftype) => { - if &self.fieldtype != ftype { - return Err(MTTError::FieldInvalidType); - } - } - CalcValue::Value(ref item) => { - if item.get_type() != self.fieldtype { - return Err(MTTError::FieldInvalidType); - } - } - CalcValue::None => {} - } - self.default_value = value; - Ok(()) - } - - fn validate(&self, value: &Field) -> Result { - match value { - Field::None => match &self.default_value { - CalcValue::None => Err(MTTError::InvalidNone), - _ => Ok(self.default_value.get(&Field::None)), - }, - _ => { - let vft: FieldType = value.into(); - if vft != self.fieldtype { - return Err(MTTError::DocumentFieldWrongDataType( - self.fieldtype.clone(), - vft, - )); - } - Ok(value.clone()) - } - } - } -} - -#[cfg(test)] -mod fieldsettings { - use super::*; - - #[test] - fn validates_field_type() { - let fset = FieldSetting::new(FieldType::Uuid); - let value: Field = Uuid::new_v4().into(); - match fset.validate(&value) { - Ok(data) => assert_eq!(data, value), - Err(err) => unreachable!("got {:?}: should have gotten a value", err), - } - } - - #[test] - fn validates_for_bad_field_type() { - let fset = FieldSetting::new(FieldType::Uuid); - let value: Field = "text".into(); - match fset.validate(&value) { - Ok(data) => unreachable!("got {:?}: should have gotten an error", data), - Err(err) => match err { - MTTError::DocumentFieldWrongDataType(expected, got) => { - assert_eq!(expected, FieldType::Uuid); - assert_eq!(got, FieldType::StaticString); - } - _ => unreachable!("got {:?}: should have gotten a value", err), - }, - } - } - - #[test] - fn no_default_returns_error() { - let fset = FieldSetting::new(FieldType::Uuid); - match fset.validate(&Field::None) { - Ok(data) => unreachable!("got {:?}: should have gotten an error", data), - Err(err) => match err { - MTTError::InvalidNone => {} - _ => unreachable!("got {:?}: should have gotten a invalid none", err), - }, - } - } - - #[test] - fn returns_value_if_default_is_set() { - let mut fset = FieldSetting::new(FieldType::StaticString); - fset.set_default(FieldType::StaticString).unwrap(); - match fset.validate(&Field::None) { - Ok(data) => assert_eq!(data, "".into()), - Err(err) => unreachable!("got {:?}: should have gotten a value", err), - } - } - - #[test] - fn returns_default_value() { - let mut fset = FieldSetting::new(FieldType::StaticString); - let input = "fred"; - fset.set_default(input).unwrap(); - match fset.validate(&Field::None) { - Ok(data) => assert_eq!(data, input.into()), - Err(err) => unreachable!("got {:?}: should have gotten a value", err), - } - } - - #[test] - fn can_default_be_calculated() { - let mut fset = FieldSetting::new(FieldType::DateTime); - let duration = Duration::from_secs(3600); - let mut calc = Calculation::new(Operand::Add); - calc.add_value(FieldType::DateTime).unwrap(); - calc.add_value(duration).unwrap(); - fset.set_default(calc).unwrap(); - let start = Utc::now() + duration; - let result = match fset.validate(&Field::None).unwrap() { - Field::DateTime(data) => data, - _ => unreachable!("should return datetime"), - }; - let stop = Utc::now() + duration; - assert!( - result > start, - "{:?} should have been greater than {:?}", - result, - start - ); - assert!( - result < stop, - "{:?} should have been less than {:?}", - result, - stop - ); - } -} - #[derive(Clone, Debug)] pub struct Addition { data: Document, @@ -681,411 +531,6 @@ mod additions { } } -#[derive(Clone, Debug)] -pub enum DocFuncType { - Add, - Delete, - ExistingQuery(MsgAction), - Query, - Show, - Trigger(MsgAction), - Update, -} - -#[derive(Clone, Debug)] -pub struct PathAction { - path: Path, - func_type: DocFuncType, -} - -impl PathAction { - fn new(path: Path, func_type: DocFuncType) -> Self { - Self { - path: path, - func_type: func_type, - } - } - - pub fn path(&self) -> Path { - self.path.clone() - } - - pub fn doc_function(&self) -> DocFuncType { - self.func_type.clone() - } -} - -#[derive(Clone, Debug)] -pub struct DocDef { - doc_names: Vec, - field_names: Names, - fields: HashMap, - indexes: HashMap, - routes: Vec, -} - -impl DocDef { - pub fn new(name: Name) -> Self { - let names = vec![name]; - Self::with_names(names) - } - - fn with_names(names: Vec) -> Self { - let routes = vec![ - PathAction::new( - Path::new( - Include::All, - Include::Just(names[0].clone().into()), - Include::Just(Action::Addition), - ), - DocFuncType::Add, - ), - PathAction::new( - Path::new( - Include::All, - Include::Just(names[0].clone().into()), - Include::Just(Action::Delete), - ), - DocFuncType::Delete, - ), - PathAction::new( - Path::new( - Include::All, - Include::Just(names[0].clone().into()), - Include::Just(Action::Query), - ), - DocFuncType::Query, - ), - PathAction::new( - Path::new( - Include::All, - Include::Just(names[0].clone().into()), - Include::Just(Action::Show), - ), - DocFuncType::Show, - ), - PathAction::new( - Path::new( - Include::All, - Include::Just(names[0].clone().into()), - Include::Just(Action::Update), - ), - DocFuncType::Update, - ), - ]; - Self { - doc_names: names, - field_names: Names::new(), - fields: HashMap::new(), - indexes: HashMap::new(), - routes: routes, - } - } - - pub fn get_document_names(&self) -> &Vec { - &self.doc_names - } - - pub fn get_field_names(&self) -> &Names { - &self.field_names - } - - #[allow(dead_code)] - fn get_field_names_mut(&mut self) -> &mut Names { - &mut self.field_names - } - - pub fn add_field(&mut self, name: Name, ftype: FieldType) { - let id = self.field_names.add_names([name].to_vec()).unwrap(); - self.fields.insert(id, FieldSetting::new(ftype)); - } - - pub fn get_field_id(&self, field_name: NT) -> Result - where - NT: Into, - { - match self.field_names.get_id(field_name) { - Ok(data) => Ok(data), - Err(err) => Err(err), - } - } - - #[allow(dead_code)] - fn get_field(&self, field_name: NT) -> Result<&FieldSetting, MTTError> - where - NT: Into, - { - let id = match self.field_names.get_id(field_name) { - Ok(data) => data, - Err(err) => return Err(err), - }; - Ok(self.fields.get(&id).unwrap()) - } - - #[allow(dead_code)] - fn get_field_mut(&mut self, field_name: NT) -> Result<&mut FieldSetting, MTTError> - where - NT: Into, - { - let id = match self.field_names.get_id(field_name) { - Ok(data) => data, - Err(err) => return Err(err), - }; - Ok(self.fields.get_mut(&id).unwrap()) - } - - pub fn field_ids(&self) -> HashSet { - self.fields.keys().cloned().collect() - } - - pub fn validate(&self, field_name: NT, value: &Field) -> Result - where - NT: Into, - { - let id = match self.field_names.get_id(field_name) { - Ok(data) => data, - Err(err) => return Err(err), - }; - self.fields.get(&id).unwrap().validate(value) - } - - pub fn set_default(&mut self, field_name: &Name, value: CV) -> Result<(), MTTError> - where - CV: Into, - { - let id = match self.field_names.get_id(field_name) { - Ok(data) => data, - Err(err) => return Err(err), - }; - match self.fields.get_mut(&id).unwrap().set_default(value) { - Ok(_) => Ok(()), - Err(err) => Err(err), - } - } - - pub fn add_index(&mut self, field_name: &Name, index_type: IndexType) -> Result<(), MTTError> { - let id = match self.field_names.get_id(field_name) { - Ok(data) => data, - Err(err) => return Err(err), - }; - self.indexes.insert(id.clone(), index_type); - Ok(()) - } - - pub fn get_indexes(&self) -> &HashMap { - &self.indexes - } - - #[allow(dead_code)] - fn iter(&self) -> impl Iterator { - self.fields.iter() - } - - pub fn iter_routes(&self) -> impl Iterator { - self.routes.iter() - } - - pub fn add_route(&mut self, path: Path, action: DocFuncType) { - self.routes.push(PathAction::new(path, action)); - } -} - -#[cfg(test)] -mod docdefs { - use super::*; - - #[test] - fn can_field_be_added() { - let docname = Name::english("tester"); - let mut docdef = DocDef::new(docname); - let name = Name::english(Uuid::new_v4().to_string().as_str()); - let field_type = FieldType::Uuid; - docdef.add_field(name.clone(), field_type.clone()); - let result = docdef.get_field(name).unwrap(); - match result.validate(&Uuid::new_v4().into()) { - Ok(_) => {} - Err(err) => unreachable!("got {:?}: should have been a value", err), - } - } - - #[test] - fn produces_error_for_bad_fields() { - let docname = Name::english("tester"); - let docdef = DocDef::new(docname); - let name = Name::english(Uuid::new_v4().to_string().as_str()); - match docdef.get_field(&name) { - Ok(_) => unreachable!("should return non existant field error"), - Err(err) => match err { - MTTError::NameNotFound(data) => assert_eq!(data, name), - _ => unreachable!("got {:?}: should have been document field not found", err), - }, - } - } - - #[test] - fn can_multiple_fields_be_added() { - let docname = Name::english("testing"); - let mut docdef = DocDef::new(docname); - let names = ["one", "two", "three"]; - let field_type = FieldType::StaticString; - for name in names.iter() { - docdef.add_field(Name::english(name), field_type.clone()); - } - for name in names.iter() { - let result = docdef.get_field(Name::english(name)).unwrap(); - match result.validate(&"".into()) { - Ok(_) => {} - Err(err) => unreachable!("got {:?}: should have been a value", err), - } - } - } - - #[test] - fn can_change_field_default_to_function() { - let docname = Name::english("something"); - let mut docdef = DocDef::new(docname); - let name = Name::english("defaultfunction"); - docdef.add_field(name.clone(), FieldType::StaticString); - docdef.set_default(&name, FieldType::StaticString).unwrap(); - match docdef.get_field(name).unwrap().validate(&Field::None) { - Ok(data) => match data { - Field::StaticString(result) => assert_eq!(result, ""), - _ => unreachable!("got {:?}: should return a static string", data), - }, - Err(err) => unreachable!("got {:?}: should return a value", err), - } - } - - #[test] - fn does_set_default_value_error_on_bad_field_name() { - let docname = Name::english("something"); - let mut docdef = DocDef::new(docname); - let field_name = Name::english(Uuid::new_v4().to_string().as_str()); - match docdef.set_default(&field_name, FieldType::Uuid) { - Ok(_) => unreachable!("should be an error"), - Err(err) => match err { - MTTError::NameNotFound(data) => assert_eq!(data, field_name), - _ => unreachable!("got {:?}: should have been field not found", err), - }, - } - } - - #[test] - fn does_set_default_value_error_on_bad_field_type() { - let docname = Name::english("something"); - let mut docdef = DocDef::new(docname); - let name = Name::english("defaultvalue"); - docdef.add_field(name.clone(), FieldType::Uuid); - match docdef.set_default(&name, "fred") { - Ok(data) => unreachable!("got {:?}, should be an error", data), - Err(err) => match err { - MTTError::FieldInvalidType => {} - _ => unreachable!("got {:?}: should have been field not found", err), - }, - } - } - - #[test] - fn does_default_routes_get_set() { - let default_num = 5; - let docname = Name::english(Uuid::new_v4().to_string().as_str()); - let docdef = DocDef::new(docname.clone()); - assert_eq!( - docdef.iter_routes().count(), - default_num, - "routes contained the following:\n{:?}", - docdef.routes - ); - let mut actions: HashSet = HashSet::new(); - let mut doc_funcs: HashSet = HashSet::new(); - for path_action in docdef.iter_routes() { - let path = path_action.path(); - match &path.msg_id { - Include::All => {} - _ => unreachable!("got {:?}, message id should include all", path.msg_id), - } - match &path.doc { - Include::Just(output) => match output { - NameType::Name(data) => assert_eq!(data, &docname), - _ => unreachable!("got {:?}, name type should be {:?}", path.doc, docname), - }, - _ => unreachable!("got {:?}, name type should be {:?}", path.doc, docname), - }; - match &path.action { - Include::Just(output) => match output { - Action::Addition => actions.insert(output.clone()), - Action::Delete => actions.insert(output.clone()), - Action::Query => actions.insert(output.clone()), - Action::Show => actions.insert(output.clone()), - Action::Update => actions.insert(output.clone()), - _ => unreachable!("got {:?} which is not a default action", output), - }, - _ => unreachable!("got {:?}, which is not a default action", path.action), - }; - let file_func = path_action.doc_function(); - match file_func { - DocFuncType::Add => doc_funcs.insert("Add".to_string()), - DocFuncType::Delete => doc_funcs.insert("Delete".to_string()), - DocFuncType::Query => doc_funcs.insert("Query".to_string()), - DocFuncType::Show => doc_funcs.insert("Show".to_string()), - DocFuncType::Update => doc_funcs.insert("Update".to_string()), - _ => unreachable!("got {:?}, which is not a default function", file_func), - }; - } - assert_eq!( - actions.len(), - default_num, - "got {:?}, missing some actions", - actions - ); - assert_eq!( - doc_funcs.len(), - default_num, - "got {:?}, missing some actions", - doc_funcs - ); - } - - #[test] - fn add_route_function() { - let docname = Name::english(Uuid::new_v4().to_string().as_str()); - let mut docdef = DocDef::new(docname.clone()); - docdef.add_route( - Path::new( - Include::All, - Include::Just(docname.clone().into()), - Include::Just(Action::OnQuery), - ), - DocFuncType::Trigger(Update::new(Query::new()).into()), - ); - let path_action = docdef.iter_routes().last().unwrap(); - let path = path_action.path(); - match &path.msg_id { - Include::All => {} - _ => unreachable!("got {:?}, message id should include all", path.msg_id), - }; - match &path.doc { - Include::Just(output) => match output { - NameType::Name(data) => assert_eq!(data, &docname), - _ => unreachable!("got {:?}, name type should be {:?}", path.doc, docname), - }, - _ => unreachable!("got {:?}, name type should be {:?}", path.doc, docname), - }; - match &path.action { - Include::Just(output) => match output { - Action::OnQuery => {} - _ => unreachable!("got {:?} which is not a additional action", output), - }, - _ => unreachable!("got {:?}, which is not on query action", path.action), - } - let file_func = path_action.doc_function(); - match file_func { - DocFuncType::Trigger(_) => {} - _ => unreachable!("got {:?}, which is not a default function", file_func), - } - } -} - #[derive(Clone, Debug)] pub enum Operand { Add, @@ -1403,7 +848,7 @@ impl Calculation { output } - fn get_type(&self) -> FieldType { + pub fn get_type(&self) -> FieldType { if self.values.is_empty() { FieldType::None } else {