diff --git a/src/message.rs b/src/message.rs index 05acb1d..6f0756a 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,5 +1,5 @@ use std::{ - collections::HashMap, + collections::{HashMap, HashSet}, sync::{ mpsc::{channel, Receiver, Sender}, Arc, RwLock, @@ -18,10 +18,12 @@ mod support_test { #[derive(Clone, Debug)] enum MTTError { DocumentAlreadyExists(String), + DocumentFieldAlreadyExists(String, Field), DocumentFieldMissing(String), DocumentFieldNotFound(String), DocumentFieldWrongDataType(FieldType, FieldType), DocumentNotFound(String), + FieldDuplicate, } #[derive(Clone, Debug, Eq, Hash, PartialEq)] @@ -1009,7 +1011,7 @@ mod fieldtypes { } } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] enum Field { None, StaticString(String), @@ -1084,7 +1086,9 @@ mod fields { struct FieldSetting { fieldtype: FieldType, use_default: bool, + use_unique: bool, default_value: Field, + unique: HashSet, } impl FieldSetting { @@ -1092,7 +1096,9 @@ impl FieldSetting { Self { fieldtype: ftype, use_default: false, + use_unique: false, default_value: Field::None, + unique: HashSet::new(), } } @@ -1111,6 +1117,17 @@ impl FieldSetting { Ok(()) } + fn set_unique(&mut self) -> Result<(), MTTError> { + self.use_unique = true; + Ok(()) + } + + fn use_unique_value(&mut self, field: Field) { + if self.use_unique { + self.unique.insert(field); + } + } + fn check(&self, value: Option) -> Result { match value { Some(data) => { @@ -1121,6 +1138,11 @@ impl FieldSetting { vft, )); } + if self.use_unique { + if self.unique.get(&data).is_some() { + return Err(MTTError::FieldDuplicate); + } + } Ok(data.clone()) } None => { @@ -1199,6 +1221,21 @@ mod fieldsettings { Err(err) => unreachable!("got {:?}: should have gotten a value", err), } } + + #[test] + fn checks_for_unique_value() { + let mut fset = FieldSetting::new(FieldType::Uuid); + let field: Field = Uuid::new_v4().into(); + fset.set_unique(); + fset.use_unique_value(field.clone()); + match fset.check(Some(field)) { + Ok(data) => unreachable!("got {:?}: should have been error", data), + Err(err) => match err { + MTTError::FieldDuplicate => {} + _ => unreachable!("got {:?}: should be a duplicate field", err), + }, + } + } } #[derive(Clone, Debug)] @@ -1312,6 +1349,18 @@ impl DocDef { } } + fn set_unique(&mut self, field_name: &str) -> Result<(), MTTError> { + let setting = self.get_field_mut(field_name).unwrap(); + setting.set_unique(); + Ok(()) + } + + fn use_unique_value(&mut self, field_name: &str, field: Field) -> Result<(), MTTError> { + let setting = self.get_field_mut(field_name).unwrap(); + setting.use_unique_value(field); + Ok(()) + } + fn iter(&self) -> impl Iterator { self.fields.iter() } @@ -1633,7 +1682,7 @@ impl Update { } } -#[derive(Clone, Eq, Hash, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] struct Oid { oid: Uuid, } @@ -1738,9 +1787,12 @@ impl DocumentFile { let mut holder = Document::new(); let doc = addition.get_document(); for (key, value) in doc.iter() { - match self.docdef.get_field(&key) { + match self.docdef.get_field_mut(&key) { Ok(field_info) => match field_info.check(Some(value.clone())) { - Ok(data) => holder.add_field(key.clone(), value.clone()), + Ok(data) => { + holder.add_field(key.clone(), value.clone()); + field_info.use_unique_value(value.clone()); + } Err(err) => return err.into(), }, Err(err) => return err.into(), @@ -2595,9 +2647,8 @@ mod document_files { #[test] fn update_errors_on_bad_field_type() { - let (mut docdef, doc_name) = - create_docdef([FieldType::Uuid, FieldType::StaticString].to_vec()); - let (mut queue, rx) = test_doc(doc_name.as_str(), docdef, standard_routes()); + let (docdef, doc_name) = create_docdef([FieldType::Uuid, FieldType::StaticString].to_vec()); + let (queue, rx) = test_doc(doc_name.as_str(), docdef, standard_routes()); let id = Uuid::new_v4(); let old = "old"; let new = Uuid::nil(); @@ -2627,6 +2678,30 @@ mod document_files { _ => unreachable!("got {:?}: should have gotten an error", action), } } + + #[test] + fn can_field_be_marked_unique() { + let (mut docdef, doc_name) = create_docdef([FieldType::Uuid].to_vec()); + docdef.set_unique("field0"); + let (queue, rx) = test_doc(doc_name.as_str(), docdef, standard_routes()); + let field0 = Uuid::new_v4(); + let mut addition = Addition::new(); + addition.add_field("field0".to_string(), field0.clone()); + let msg = Message::new(doc_name.clone(), addition.clone()); + queue.send(msg).unwrap(); + rx.recv_timeout(TIMEOUT).unwrap(); + let msg2 = Message::new(doc_name.clone(), addition); + queue.send(msg2).unwrap(); + let result = rx.recv_timeout(TIMEOUT).unwrap(); + let action = result.get_action(); + match action { + MsgAction::Error(err) => match err { + MTTError::FieldDuplicate => {} + _ => unreachable!("got {:?}: should have gotten an missing field", err), + }, + _ => unreachable!("got {:?}: should have gotten an error", action), + } + } } #[cfg(test)]