From 32d3fbadea2be1cb631436be127f4ddf6bd6b9df Mon Sep 17 00:00:00 2001 From: Jeff Baskin Date: Sun, 14 Sep 2025 10:53:47 -0400 Subject: [PATCH] Got unique index working, and connected to DocDef. --- src/message.rs | 173 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 140 insertions(+), 33 deletions(-) diff --git a/src/message.rs b/src/message.rs index 5d07c65..dfb1bbb 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1105,7 +1105,7 @@ impl FieldSetting { fn set_default(&mut self, value: Option) -> Result<(), MTTError> { match value { Some(data) => { - match self.check(Some(data.clone())) { + match self.validate(Some(data.clone())) { Ok(_) => {} Err(err) => return Err(err), } @@ -1134,7 +1134,7 @@ impl FieldSetting { } } - fn check(&self, value: Option) -> Result { + fn validate(&self, value: Option) -> Result { match value { Some(data) => { let vft: FieldType = (&data).into(); @@ -1170,20 +1170,20 @@ mod fieldsettings { use super::*; #[test] - fn checks_field_type() { + fn validates_field_type() { let fset = FieldSetting::new(FieldType::Uuid); let value: Field = Uuid::new_v4().into(); - match fset.check(Some(value.clone())) { + match fset.validate(Some(value.clone())) { Ok(data) => assert_eq!(data, value), Err(err) => unreachable!("got {:?}: should have gotten a value", err), } } #[test] - fn checks_for_bad_field_type() { + fn validates_for_bad_field_type() { let fset = FieldSetting::new(FieldType::Uuid); let value: Field = "text".into(); - match fset.check(Some(value)) { + match fset.validate(Some(value)) { Ok(data) => unreachable!("got {:?}: should have gotten an error", data), Err(err) => match err { MTTError::DocumentFieldWrongDataType(expected, got) => { @@ -1198,7 +1198,7 @@ mod fieldsettings { #[test] fn no_default_returns_error() { let fset = FieldSetting::new(FieldType::Uuid); - match fset.check(None) { + match fset.validate(None) { Ok(data) => unreachable!("got {:?}: should have gotten an error", data), Err(err) => match err { MTTError::DocumentFieldMissing(data) => assert_eq!(data, ""), @@ -1211,7 +1211,7 @@ mod fieldsettings { fn returns_value_if_default_is_set() { let mut fset = FieldSetting::new(FieldType::StaticString); fset.set_default(None); - match fset.check(None) { + match fset.validate(None) { Ok(data) => assert_eq!(data, "".into()), Err(err) => unreachable!("got {:?}: should have gotten a value", err), } @@ -1222,19 +1222,19 @@ mod fieldsettings { let mut fset = FieldSetting::new(FieldType::StaticString); let input = "fred"; fset.set_default(Some(input.into())); - match fset.check(None) { + match fset.validate(None) { Ok(data) => assert_eq!(data, input.into()), Err(err) => unreachable!("got {:?}: should have gotten a value", err), } } #[test] - fn checks_for_unique_value() { + fn validates_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.clone())) { + match fset.validate(Some(field.clone())) { Ok(data) => unreachable!("got {:?}: should have been error", data), Err(err) => match err { MTTError::FieldDuplicate(key, result) => { @@ -1317,15 +1317,32 @@ mod additions { } } +#[derive(Clone, Debug)] +enum IndexType { + Index, + Unique, +} + +impl IndexType { + fn create_index(&self) -> Index { + match self { + Self::Index => Index::new(), + Self::Unique => Index::new_unique(), + } + } +} + #[derive(Clone, Debug)] struct DocDef { fields: HashMap, + indexes: HashMap, } impl DocDef { fn new() -> Self { Self { fields: HashMap::new(), + indexes: HashMap::new(), } } @@ -1347,12 +1364,12 @@ impl DocDef { } } - fn check(&self, field_name: &str, value: Option) -> Result { + fn validate(&self, field_name: &str, value: Option) -> Result { let setting = match self.get_field(field_name) { Ok(data) => data, Err(err) => return Err(err), }; - setting.check(value) + setting.validate(value) } fn set_default(&mut self, field_name: &str, value: Option) -> Result<(), MTTError> { @@ -1366,6 +1383,23 @@ impl DocDef { } } + fn add_index(&mut self, field_name: String, index_type: IndexType) -> Result<(), MTTError> { + let setting = match self.get_field(&field_name) { + Ok(_) => {} + Err(err) => return Err(err), + }; + self.indexes.insert(field_name, index_type); + Ok(()) + } + + fn create_indexes(&self) -> HashMap { + let mut output = HashMap::new(); + for (key, value) in self.indexes.iter() { + output.insert(key.clone(), value.create_index()); + } + output + } + fn set_unique(&mut self, field_name: &str) -> Result<(), MTTError> { let setting = match self.get_field_mut(field_name) { Ok(data) => data, @@ -1380,7 +1414,7 @@ impl DocDef { Ok(data) => data, Err(err) => return Err(err), }; - match setting.check(Some(field.clone())) { + match setting.validate(Some(field.clone())) { Ok(data) => setting.use_unique_value(field), Err(err) => return Err(err), } @@ -1412,7 +1446,7 @@ mod docdefs { let field_type = FieldType::Uuid; docdef.add_field(name.clone(), field_type.clone()); let result = docdef.get_field(name.as_str()).unwrap(); - match result.check(Some(Uuid::new_v4().into())) { + match result.validate(Some(Uuid::new_v4().into())) { Ok(_) => {} Err(err) => unreachable!("got {:?}: should have been a value", err), } @@ -1441,7 +1475,7 @@ mod docdefs { } for name in names.iter() { let result = docdef.get_field(name).unwrap(); - match result.check(Some("".into())) { + match result.validate(Some("".into())) { Ok(_) => {} Err(err) => unreachable!("got {:?}: should have been a value", err), } @@ -1454,7 +1488,7 @@ mod docdefs { let name = "defaultfunction"; docdef.add_field(name.to_string(), FieldType::StaticString); docdef.set_default(name, None); - match docdef.get_field(name).unwrap().check(None) { + match docdef.get_field(name).unwrap().validate(None) { Ok(data) => match data { Field::StaticString(result) => assert_eq!(result, ""), _ => unreachable!("got {:?}: should return a static string", data), @@ -1557,7 +1591,7 @@ enum Operand { } impl Operand { - fn check(&self, x: &Field, y: &Field) -> bool { + fn validate(&self, x: &Field, y: &Field) -> bool { match self { Self::Equal => x == y, } @@ -1571,7 +1605,7 @@ mod operands { #[test] fn equals_true() { let data: Field = Uuid::new_v4().into(); - assert!(Operand::Equal.check(&data, &data)); + assert!(Operand::Equal.validate(&data, &data)); } #[test] @@ -1581,7 +1615,7 @@ mod operands { while x == y { y = Uuid::new_v4().into(); } - assert!(!Operand::Equal.check(&x, &y)); + assert!(!Operand::Equal.validate(&x, &y)); } } @@ -1608,8 +1642,8 @@ impl Specifier { self.field_name.clone() } - fn check(&self, field: &Field) -> bool { - self.operation.check(field, &self.value) + fn validate(&self, field: &Field) -> bool { + self.operation.validate(field, &self.value) } } @@ -1813,31 +1847,44 @@ impl Oid { struct Index { data: HashMap>, + unique: bool, } impl Index { fn new() -> Self { Self { data: HashMap::new(), + unique: false, } } - fn add(&mut self, field: Field, oid: Oid) { + fn new_unique() -> Self { + Self { + data: HashMap::new(), + unique: true, + } + } + + fn add(&mut self, field: Field, oid: Oid) -> Result<(), MTTError> { let oids = match self.data.get_mut(&field) { Some(data) => data, None => { - let mut data = HashSet::new(); - self.data.insert(field.clone(), data); + self.data.insert(field.clone(), HashSet::new()); self.data.get_mut(&field).unwrap() } }; - oids.insert(oid); + if self.unique && oids.len() > 0 { + return Err(MTTError::FieldDuplicate("".to_string(), field)); + } else { + oids.insert(oid); + } + Ok(()) } fn get(&self, spec: &Specifier) -> Vec { let mut output = Vec::new(); for (field, oids) in self.data.iter() { - if spec.check(field) { + if spec.validate(field) { for oid in oids.iter() { output.push(oid.clone()); } @@ -1857,6 +1904,16 @@ impl Index { None => {} }; } + + fn validate(&self, field: &Field) -> Result<(), MTTError> { + if self.unique { + match self.data.get(field) { + Some(_) => return Err(MTTError::FieldDuplicate("".to_string(), field.clone())), + None => {} + } + } + Ok(()) + } } #[cfg(test)] @@ -1944,11 +2001,60 @@ mod indexes { index.remove(&field, &oid); assert_eq!(index.data.len(), 0); } + + #[test] + fn do_unique_indexes_error_on_duplicates() { + let mut index = Index::new_unique(); + let field: Field = "fred".into(); + let oids = get_oids(2); + index.add(field.clone(), oids[0].clone()).unwrap(); + match index.add(field.clone(), oids[0].clone()) { + Ok(_) => unreachable!("should have been an error"), + Err(err) => match err { + MTTError::FieldDuplicate(field_name, value) => { + assert_eq!(field_name, ""); + assert_eq!(value, field); + } + _ => unreachable!("got {:?}: should have been duplicate field", err), + }, + } + } + + #[test] + fn index_returns_validate() { + let mut index = Index::new(); + let field: Field = "stuff".into(); + let oid = Oid::new(); + index.add(field.clone(), oid).unwrap(); + match index.validate(&field) { + Ok(_) => {} + Err(err) => unreachable!("got {:?}: should have returned without issue", err), + } + } + + #[test] + fn unique_return_duplicate_error() { + let mut index = Index::new_unique(); + let field: Field = "fred".into(); + let oid = Oid::new(); + index.add(field.clone(), oid).unwrap(); + match index.validate(&field) { + Ok(_) => unreachable!("should have gotten a duplication error"), + Err(err) => match err { + MTTError::FieldDuplicate(field_name, value) => { + assert_eq!(field_name, ""); + assert_eq!(value, field); + } + _ => unreachable!("got {:?}: should have been duplicate field", err), + }, + } + } } struct DocumentFile { docdef: DocDef, docs: HashMap, + indexes: HashMap, queue: Queue, rx: Receiver, } @@ -1956,8 +2062,9 @@ struct DocumentFile { impl DocumentFile { fn new(queue: Queue, rx: Receiver, docdef: DocDef) -> Self { Self { - docdef: docdef, + docdef: docdef.clone(), docs: HashMap::new(), + indexes: docdef.create_indexes(), queue: queue, rx: rx, } @@ -2046,7 +2153,7 @@ impl DocumentFile { let mut holder = Document::new(); let doc = addition.get_document(); for (key, value) in doc.iter() { - match self.docdef.check(key, Some(value.clone())) { + match self.docdef.validate(key, Some(value.clone())) { Ok(data) => holder.add_field(key.clone(), value.clone()), Err(err) => return Self::add_field_to_error(key.to_string(), err).into(), } @@ -2054,7 +2161,7 @@ impl DocumentFile { for (key, value) in self.docdef.iter() { match holder.get_field(key) { Some(_) => {} - None => match value.check(None) { + None => match value.validate(None) { Ok(data) => holder.add_field(key.clone(), data.clone()), Err(err) => return Self::add_field_to_error(key.to_string(), err).into(), }, @@ -2078,7 +2185,7 @@ impl DocumentFile { for specifier in query.iter() { match self .docdef - .check(&specifier.field_name, Some(specifier.value.clone())) + .validate(&specifier.field_name, Some(specifier.value.clone())) { Ok(_) => {} Err(err) => match err { @@ -2125,7 +2232,7 @@ impl DocumentFile { for oid in oids.iter() { let doc = self.docs.get_mut(oid).unwrap(); for (key, value) in update.get_values().iter() { - match self.docdef.check(key, Some(value.clone())) { + match self.docdef.validate(key, Some(value.clone())) { Ok(field) => { self.docdef .remove_unique_value(key, doc.get_field(key).unwrap()); @@ -3044,7 +3151,7 @@ mod document_files { } } - // #[test] + //#[test] fn unique_available_after_bad_change() { let mut ids: Vec = Vec::new(); while ids.len() < 3 {