Moved document definitions into separate module.
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 1s

This commit is contained in:
Jeff Baskin 2026-01-12 15:05:45 -05:00
parent f46c2929b7
commit 5e448a0071
4 changed files with 581 additions and 562 deletions

View File

@ -1,2 +1,3 @@
pub mod clock;
pub mod create;
pub mod definition;

View File

@ -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,

572
src/document/definition.rs Normal file
View File

@ -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<CV>(&mut self, holder: CV) -> Result<(), MTTError>
where
CV: Into<CalcValue>,
{
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<Field, MTTError> {
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<Name>,
field_names: Names,
fields: HashMap<Uuid, FieldSetting>,
indexes: HashMap<Uuid, IndexType>,
routes: Vec<PathAction>,
}
impl DocDef {
pub fn new(name: Name) -> Self {
let names = vec![name];
Self::with_names(names)
}
fn with_names(names: Vec<Name>) -> 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<Name> {
&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<NT>(&self, field_name: NT) -> Result<Uuid, MTTError>
where
NT: Into<NameType>,
{
match self.field_names.get_id(field_name) {
Ok(data) => Ok(data),
Err(err) => Err(err),
}
}
#[allow(dead_code)]
fn get_field<NT>(&self, field_name: NT) -> Result<&FieldSetting, MTTError>
where
NT: Into<NameType>,
{
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<NT>(&mut self, field_name: NT) -> Result<&mut FieldSetting, MTTError>
where
NT: Into<NameType>,
{
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<Uuid> {
self.fields.keys().cloned().collect()
}
pub fn validate<NT>(&self, field_name: NT, value: &Field) -> Result<Field, MTTError>
where
NT: Into<NameType>,
{
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<CV>(&mut self, field_name: &Name, value: CV) -> Result<(), MTTError>
where
CV: Into<CalcValue>,
{
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<Uuid, IndexType> {
&self.indexes
}
#[allow(dead_code)]
fn iter(&self) -> impl Iterator<Item = (&Uuid, &FieldSetting)> {
self.fields.iter()
}
pub fn iter_routes(&self) -> impl Iterator<Item = &PathAction> {
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<Action> = HashSet::new();
let mut doc_funcs: HashSet<String> = 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),
}
}
}

View File

@ -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<CV>(&mut self, holder: CV) -> Result<(), MTTError>
where
CV: Into<CalcValue>,
{
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<Field, MTTError> {
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<Name>,
field_names: Names,
fields: HashMap<Uuid, FieldSetting>,
indexes: HashMap<Uuid, IndexType>,
routes: Vec<PathAction>,
}
impl DocDef {
pub fn new(name: Name) -> Self {
let names = vec![name];
Self::with_names(names)
}
fn with_names(names: Vec<Name>) -> 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<Name> {
&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<NT>(&self, field_name: NT) -> Result<Uuid, MTTError>
where
NT: Into<NameType>,
{
match self.field_names.get_id(field_name) {
Ok(data) => Ok(data),
Err(err) => Err(err),
}
}
#[allow(dead_code)]
fn get_field<NT>(&self, field_name: NT) -> Result<&FieldSetting, MTTError>
where
NT: Into<NameType>,
{
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<NT>(&mut self, field_name: NT) -> Result<&mut FieldSetting, MTTError>
where
NT: Into<NameType>,
{
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<Uuid> {
self.fields.keys().cloned().collect()
}
pub fn validate<NT>(&self, field_name: NT, value: &Field) -> Result<Field, MTTError>
where
NT: Into<NameType>,
{
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<CV>(&mut self, field_name: &Name, value: CV) -> Result<(), MTTError>
where
CV: Into<CalcValue>,
{
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<Uuid, IndexType> {
&self.indexes
}
#[allow(dead_code)]
fn iter(&self) -> impl Iterator<Item = (&Uuid, &FieldSetting)> {
self.fields.iter()
}
pub fn iter_routes(&self) -> impl Iterator<Item = &PathAction> {
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<Action> = HashSet::new();
let mut doc_funcs: HashSet<String> = 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 {