From 5fb60350699200f36848526b62fe4a37d28f0b13 Mon Sep 17 00:00:00 2001 From: Jeff Baskin Date: Tue, 14 Oct 2025 14:29:11 -0400 Subject: [PATCH] Added a record iterator. --- src/message.rs | 400 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 271 insertions(+), 129 deletions(-) diff --git a/src/message.rs b/src/message.rs index 1864966..f994ecb 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,7 +1,7 @@ use chrono::prelude::*; use isolang::Language; use std::{ - collections::{HashMap, HashSet}, + collections::{HashMap, HashSet, VecDeque}, sync::{ mpsc::{channel, Receiver, Sender}, Arc, RwLock, @@ -28,6 +28,7 @@ enum MTTError { DocumentNotFound(String), FieldDuplicate(String, Field), FieldMissingData, + RecordMismatch, NameDuplicate(Name), NameInvalidID(Uuid), NameMissingTranslation(Language), @@ -267,7 +268,11 @@ impl Message { } fn get_path(&self) -> Path { - Path::new(Include::Some(self.msg_id.clone()), Include::Some(self.document_id.clone()), Include::Some(self.action.clone().into())) + Path::new( + Include::Some(self.msg_id.clone()), + Include::Some(self.document_id.clone()), + Include::Some(self.action.clone().into()), + ) } fn response(&self, action: A) -> Self @@ -288,9 +293,15 @@ mod messages { #[test] fn can_the_document_be_a_stringi_reference() { - let dts = [Name::english("one".to_string()), Name::english("two".to_string())]; + let dts = [ + Name::english("one".to_string()), + Name::english("two".to_string()), + ]; for document in dts.into_iter() { - let msg = Message::new(document.clone(), MsgAction::Create(DocDef::new(document.clone()))); + let msg = Message::new( + document.clone(), + MsgAction::Create(DocDef::new(document.clone())), + ); match msg.get_document_id() { NameType::Name(data) => assert_eq!(data, &document), _ => unreachable!("should have been a string id"), @@ -551,7 +562,7 @@ impl ToString for Name { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] struct Names { names: HashMap, ids: HashMap>, @@ -605,18 +616,23 @@ impl Names { } } - fn get_id(&self, name: NT) -> Result where NT: Into { + fn get_id(&self, name: NT) -> Result + where + NT: Into, + { 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 { + Err(MTTError::NameNotFound(Name::english(data.to_string()))) + } } - NameType::ID(data) => if self.ids.contains_key(&data) { - Ok(data) - } else { - Err(MTTError::NameNotFound(Name::english(data.to_string()))) - } - NameType::None => Err(MTTError::NameNotFound(Name::english("none".to_string()))) + NameType::None => Err(MTTError::NameNotFound(Name::english("none".to_string()))), } } @@ -643,6 +659,10 @@ impl Names { }; Ok(Route::new(path.msg_id.clone(), doc_id, path.action.clone())) } + + fn is_empty(&self) -> bool { + self.names.is_empty() + } } #[cfg(test)] @@ -1400,7 +1420,6 @@ mod queuedatas { } */ - struct DocRegistry { doc_names: Names, queue: Queue, @@ -1447,8 +1466,11 @@ impl DocRegistry { for send_id in send_to.iter() { self.queue.forward(send_id, msg.clone()); } - }, - Err(err) => self.queue.send(msg.response(MsgAction::Error(err))).unwrap(), + } + Err(err) => self + .queue + .send(msg.response(MsgAction::Error(err))) + .unwrap(), } } } @@ -1459,8 +1481,8 @@ impl DocRegistry { match reg.get_msg() { RegMsg::AddDocName(name) => match self.doc_names.add_name(name.clone()) { Ok(_) => reg.response(RegMsg::Ok), - Err(err) => reg.response(RegMsg::Error(err)) - } + Err(err) => reg.response(RegMsg::Error(err)), + }, RegMsg::AddRoute(path) => { let route = self.doc_names.path_to_route(path).unwrap(); let route_id: RouteID = route.into(); @@ -1722,7 +1744,10 @@ mod queues { fn can_register_routes() { let mut tester = TestQueue::new(); let mut queue = tester.get_queue(); - let names = [Name::english("task".to_string()), Name::english("recipe".to_string())]; + let names = [ + Name::english("task".to_string()), + Name::english("recipe".to_string()), + ]; for name in names.iter() { tester.add_document(name.clone()); } @@ -1749,7 +1774,10 @@ mod queues { fn can_multiple_register_for_the_same_route() { let mut tester = TestQueue::new(); let mut queue = tester.get_queue(); - let names = [Name::english("task".to_string()), Name::english("recipe".to_string())]; + let names = [ + Name::english("task".to_string()), + Name::english("recipe".to_string()), + ]; let route_req = Path::new(Include::All, Include::All, Include::All); let reg_msg = RegMsg::AddRoute(route_req); for name in names.iter() { @@ -1775,7 +1803,11 @@ mod queues { tester.add_document(name.clone()); let paths = [ Path::new(Include::All, Include::All, Include::All), - Path::new(Include::All, Include::Some(name.clone().into()), Include::All), + Path::new( + Include::All, + Include::Some(name.clone().into()), + Include::All, + ), ]; for path in paths.iter() { let reg_msg = RegMsg::AddRoute(path.clone()); @@ -1801,12 +1833,19 @@ mod queues { fn can_routing_be_based_on_message_id() { let mut tester = TestQueue::new(); let mut queue = tester.get_queue(); - let names = [Name::english("one".to_string()), Name::english("two".to_string())]; + let names = [ + Name::english("one".to_string()), + Name::english("two".to_string()), + ]; let mut inputs: HashMap = HashMap::new(); for name in names.iter() { tester.add_document(name.clone()); let input = Message::new(name.clone(), Query::new()); - let path = Path::new(Include::Some(input.get_message_id().clone()), Include::All, Include::All); + let path = Path::new( + Include::Some(input.get_message_id().clone()), + Include::All, + Include::All, + ); let reg_msg = RegMsg::AddRoute(path); let reg = Register::new(tester.get_doc_rx_id(&name).clone(), reg_msg); let msg = Message::new(NameType::None, reg); @@ -1835,12 +1874,19 @@ mod queues { fn can_routing_be_based_on_document_name() { let mut tester = TestQueue::new(); let mut queue = tester.get_queue(); - let names = [Name::english("one".to_string()), Name::english("two".to_string())]; + let names = [ + Name::english("one".to_string()), + Name::english("two".to_string()), + ]; let mut inputs: HashMap = HashMap::new(); for name in names.iter() { tester.add_document(name.clone()); let input = Message::new(name.clone(), Query::new()); - let path = Path::new(Include::All, Include::Some(name.clone().into()), Include::All); + let path = Path::new( + Include::All, + Include::Some(name.clone().into()), + Include::All, + ); let reg_msg = RegMsg::AddRoute(path); let reg = Register::new(tester.get_doc_rx_id(&name).clone(), reg_msg); let msg = Message::new(NameType::None, reg); @@ -1869,7 +1915,10 @@ mod queues { fn can_routing_be_based_on_action() { let mut tester = TestQueue::new(); let mut queue = tester.get_queue(); - let names = [Name::english("one".to_string()), Name::english("two".to_string())]; + let names = [ + Name::english("one".to_string()), + Name::english("two".to_string()), + ]; let paths = [ Path::new(Include::All, Include::All, Include::Some(Action::Reply)), Path::new(Include::All, Include::All, Include::Some(Action::Error)), @@ -2080,6 +2129,20 @@ impl From for Field { } } +impl From for Field { + fn from(value: isize) -> Self { + let data: i128 = value.try_into().unwrap(); + Self::from(data) + } +} + +impl From for Field { + fn from(value: i32) -> Self { + let data: i128 = value.into(); + Self::from(data) + } +} + #[cfg(test)] mod fields { use super::*; @@ -2291,7 +2354,10 @@ impl Addition { self.data.add_field(name, field); } - fn get_field(&self, name: NT) -> Option where NT: Into { + fn get_field(&self, name: NT) -> Option + where + NT: Into, + { self.data.get_field(name) } @@ -2392,14 +2458,20 @@ impl DocDef { self.fields.insert(id, FieldSetting::new(ftype)); } - fn get_field_id(&self, field_name: NT) -> Result where NT: Into { + 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), } } - fn get_field(&self, field_name: NT) -> Result<&FieldSetting, MTTError> where NT: Into { + 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), @@ -2407,7 +2479,10 @@ impl DocDef { Ok(self.fields.get(&id).unwrap()) } - fn get_field_mut(&mut self, field_name: NT) -> Result<&mut FieldSetting, MTTError> where NT: Into { + 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), @@ -2419,7 +2494,10 @@ impl DocDef { self.fields.keys().cloned().collect() } - fn validate(&self, field_name: NT, value: Option) -> Result where NT: Into { + fn validate(&self, field_name: NT, value: Option) -> Result + where + NT: Into, + { let id = match self.field_names.get_id(field_name) { Ok(data) => data, Err(err) => return Err(err), @@ -2437,7 +2515,6 @@ impl DocDef { Err(err) => Err(err), } - /* let setting = match self.get_field_mut(field_name) { Ok(data) => data, @@ -2458,9 +2535,6 @@ impl DocDef { self.indexes.insert(id.clone(), index_type); Ok(()) - - - /* let setting = match self.get_field(&field_name) { Ok(_) => {} @@ -3122,7 +3196,10 @@ impl Query { } } - fn add(&mut self, name: NT, operation: Calculation) -> Result<(), MTTError> where NT: Into { + fn add(&mut self, name: NT, operation: Calculation) -> Result<(), MTTError> + where + NT: Into, + { match operation.operation() { Operand::Equal => { self.data.insert(name.into(), operation); @@ -3132,7 +3209,10 @@ impl Query { } } - fn get(&self, name: NT) -> Option where NT: Into { + fn get(&self, name: NT) -> Option + where + NT: Into, + { match self.data.get(&name.into()) { Some(calc) => Some(calc.clone()), None => None, @@ -3298,7 +3378,7 @@ mod replies { #[derive(Clone, Debug)] struct Record { names: Names, - data: HashMap + data: HashMap, } impl Record { @@ -3309,7 +3389,11 @@ impl Record { } } - fn insert(&mut self, field_id: NT, data: F) -> Result<(), MTTError> where F: Into, NT: Into { + fn insert(&mut self, field_id: NT, data: F) -> Result<(), MTTError> + where + F: Into, + NT: Into, + { let id = match self.names.get_id(field_id) { Ok(data) => data, Err(err) => return Err(err), @@ -3318,7 +3402,10 @@ impl Record { Ok(()) } - fn get(&self, field_id: NT) -> Result where NT: Into { + fn get(&self, field_id: NT) -> Result + where + NT: Into, + { let id = match self.names.get_id(field_id) { Ok(data) => data, Err(err) => return Err(err), @@ -3331,66 +3418,59 @@ impl Record { } #[derive(Clone, Debug)] -struct Records { +struct RecordIter { names: Names, - records: Vec, + records: VecDeque>, } -impl Records { - fn new(names: Names) -> Self { +impl RecordIter { + fn new() -> Self { Self { - names: names, - records: Vec::new(), + names: Names::new(), + records: VecDeque::new(), } } - fn len(&self) -> usize { - 0 - } - - fn add(&mut self, name: NT, data: F) -> Result<(), MTTError> where F: Into, NT: Into { + fn push(&mut self, rec: Record) -> Result<(), MTTError> { + if self.names.is_empty() { + self.names = rec.names; + self.records.push_back(rec.data.clone()); + } else { + if rec.names != self.names { + return Err(MTTError::RecordMismatch); + } + let existing: HashSet = self.records.front().unwrap().keys().cloned().collect(); + let rec_keys: HashSet = rec.data.keys().cloned().collect(); + if existing == rec_keys { + self.records.push_back(rec.data.clone()); + } else { + return Err(MTTError::RecordMismatch); + } + } Ok(()) } - - fn get(&self, rec_num: usize, field_name: NT) -> Result<&Field, MTTError> where NT: Into { - Err(MTTError::QueryCannotChangeData) - } - - fn submit(&mut self) { - } - - fn iter(&self) -> impl Iterator { - RecordsIter::new(self) - } } -struct RecordsIter { - position: usize, - rec_count: usize, -} - -impl RecordsIter { - fn new(records: &Records) -> Self { - Self { - position: 0, - rec_count: records.len(), - } - } -} - -impl Iterator for RecordsIter { - type Item = usize; +impl Iterator for RecordIter { + type Item = Record; fn next(&mut self) -> Option { - self.position += 1; - if self.position < self.rec_count { - Some(self.position) - } else { - None + match self.records.pop_front() { + Some(data) => Some(Record { + names: self.names.clone(), + data: data, + }), + None => None, } } } +impl ExactSizeIterator for RecordIter { + fn len(&self) -> usize { + self.records.len() + } +} + #[cfg(test)] mod records { use super::*; @@ -3420,10 +3500,14 @@ mod records { assert_eq!(id1, id2, "id and name should produce the same result"); match id1 { Field::Uuid(data) => { - assert_eq!(data.to_string(), name.to_string(), "for this case, name and data should match"); + assert_eq!( + data.to_string(), + name.to_string(), + "for this case, name and data should match" + ); assert!(ids.contains(&data), "{:?} not in {:?}", id1, ids); ids.remove(&data); - }, + } _ => unreachable!("got {:?}, should have been uuid", id1), } } @@ -3440,7 +3524,7 @@ mod records { Err(err) => match err { MTTError::NameNotFound(data) => assert_eq!(data, name), _ => unreachable!("got {:?}, should have been not found", err), - } + }, } } @@ -3454,7 +3538,7 @@ mod records { Err(err) => match err { MTTError::NameNotFound(data) => assert_eq!(data, name), _ => unreachable!("got {:?}, should have been not found", err), - } + }, } } @@ -3467,37 +3551,77 @@ mod records { match rec.get(&name) { Ok(_) => unreachable!("should return not found error"), Err(err) => match err { - MTTError::FieldMissingData => {}, + MTTError::FieldMissingData => {} _ => unreachable!("got {:?}, should have been not found", err), - } + }, } } - - - - - #[test] - fn can_create_empty_record() { - let rec = Records::new(Names::new()); - assert_eq!(rec.len(), 0); - } - - #[test] - #[ignore] - fn can_have_multiple_rows() { + fn can_records_hold_multiple_entries() { + let field_name = Name::english("field".to_string()); let mut names = Names::new(); - let name = Name::english("field".to_string()); - let mut recs = Records::new(names); - let data: [Field; 3] = ["one".into(), "two".into(), "three".into()]; - for item in data.iter() { - recs.add(&name, item.clone()); - recs.submit(); + names.add_name(field_name.clone()); + let count: isize = 5; + let mut recs = RecordIter::new(); + assert_eq!(recs.len(), 0, "should contain no records"); + for data in 0..count { + let mut rec = Record::new(names.clone()); + rec.insert(field_name.clone(), data.clone()); + recs.push(rec); + let holder: usize = (data + 1).try_into().unwrap(); + assert_eq!(recs.len(), holder, "should increase the count"); } - assert_eq!(recs.len(), data.len(), "does not contain the correct number of records"); - for rec_num in recs.iter() { - assert!(data.contains(recs.get(rec_num, &name).unwrap())); + let mut data = 0; + for rec in recs { + let item = rec.get(&field_name).unwrap(); + assert_eq!(item, data.into()); + data += 1; + } + assert_eq!(data, count, "did not iterate") + } + + #[test] + fn errors_on_mismatched_names() { + let count = 2; + let mut recs: Vec = Vec::new(); + for i in 0..count { + let name = Name::english(i.to_string()); + let mut names = Names::new(); + names.add_name(name); + recs.push(Record::new(names)); + } + let mut rec_iter = RecordIter::new(); + rec_iter.push(recs[0].clone()).unwrap(); + match rec_iter.push(recs[1].clone()) { + Ok(_) => unreachable!("should have returned error"), + Err(err) => match err { + MTTError::RecordMismatch => {} + _ => unreachable!("got {:?}, should have gotten mismatched names", err), + }, + } + } + + #[test] + fn errors_on_mismatched_entries() { + let mut names = Names::new(); + let name1 = Name::english("first".to_string()); + let name2 = Name::english("second".to_string()); + names.add_name(name1.clone()).unwrap(); + names.add_name(name2.clone()).unwrap(); + let mut rec1 = Record::new(names.clone()); + rec1.insert(&name1, 1); + rec1.insert(&name2, 2); + let mut rec2 = Record::new(names.clone()); + rec2.insert(&name1, 1); + let mut rec_iter = RecordIter::new(); + rec_iter.push(rec1).unwrap(); + match rec_iter.push(rec2) { + Ok(_) => unreachable!("should have returned error"), + Err(err) => match err { + MTTError::RecordMismatch => {} + _ => unreachable!("got {:?}, should have gotten mismatched names", err), + }, } } } @@ -3522,7 +3646,10 @@ impl Document { self.data.insert(name.into(), field.into()); } - fn get_field(&self, name: NT) -> Option where NT: Into { + fn get_field(&self, name: NT) -> Option + where + NT: Into, + { match self.data.get(&name.into()) { Some(data) => Some(data.get()), None => None, @@ -4029,7 +4156,10 @@ impl DocumentFile { } */ - fn validate(&self, field_name: NT, value: Option) -> Result<(Uuid, Field), MTTError> where NT: Into { + fn validate(&self, field_name: NT, value: Option) -> Result<(Uuid, Field), MTTError> + where + NT: Into, + { let field_id = match self.docdef.get_field_id(field_name) { Ok(data) => data, Err(err) => return Err(err), @@ -4053,12 +4183,18 @@ impl DocumentFile { } } - fn add_to_index(&mut self, field_name: NT, field: Field, oid: Oid) where NT: Into { + fn add_to_index(&mut self, field_name: NT, field: Field, oid: Oid) + where + NT: Into, + { let field_id = self.docdef.get_field_id(field_name).unwrap(); self.indexes.add_to_index(&field_id, field, oid) } - fn remove_from_index(&mut self, field_name: NT, field: &Field, oid: &Oid) where NT: Into { + fn remove_from_index(&mut self, field_name: NT, field: &Field, oid: &Oid) + where + NT: Into, + { let field_id = self.docdef.get_field_id(field_name).unwrap(); self.indexes.remove_from_index(&field_id, field, oid); } @@ -4068,7 +4204,9 @@ impl DocumentFile { let doc = addition.get_document(); for (field, value) in doc.iter() { match self.validate(field, Some(value)) { - Ok((id, data)) => {holder.insert(id, data);}, + Ok((id, data)) => { + holder.insert(id, data); + } Err(err) => return MsgAction::Error(err), } } @@ -4076,7 +4214,9 @@ impl DocumentFile { let all_fields = self.docdef.field_ids(); for field in all_fields.difference(&requested).cloned() { match self.validate(field, None) { - Ok((id, data)) => {holder.insert(id, data);}, + Ok((id, data)) => { + holder.insert(id, data); + } Err(err) => return MsgAction::Error(err), } } @@ -4088,7 +4228,6 @@ impl DocumentFile { } self.docs.insert(oid, holder); - /* self.docs.insert(oid.clone(), holder.clone()); for (key, value) in holder.iter() { @@ -4096,13 +4235,9 @@ impl DocumentFile { } reply.add(holder); */ - - } reply.into() - - /* let mut holder = Document::new(); let doc = addition.get_document(); @@ -4183,10 +4318,6 @@ impl DocumentFile { } Ok(oids) - - - - /* let query_ids = query.field_ids(); let doc_ids = self.docdef.field_ids(); @@ -4382,7 +4513,8 @@ mod document_files { let msg = Message::new(self.docdef.get_document_name().clone(), self.docdef.clone()); DocumentFile::start(self.queue.clone(), msg); for route in self.routes.iter() { - let request = Register::new(self.sender_id.clone(), RegMsg::AddRoute(route.clone())); + let request = + Register::new(self.sender_id.clone(), RegMsg::AddRoute(route.clone())); let add_route = Message::new(NameType::None, request); self.queue.send(add_route).unwrap(); self.rx.recv().unwrap(); @@ -4495,9 +4627,16 @@ mod document_files { Ok(data) => data.clone(), Err(err) => unreachable!("for {:?} got {:?}", msg_action, err), }; - assert_eq!(result.get_message_id(), msg.get_message_id(), "for {:?} response and reply ids should equal", msg_action); + assert_eq!( + result.get_message_id(), + msg.get_message_id(), + "for {:?} response and reply ids should equal", + msg_action + ); match result.get_action() { - MsgAction::Reply(data) => assert_eq!(data.len(), 0, "for {:?} got {:?}", msg_action, result), + MsgAction::Reply(data) => { + assert_eq!(data.len(), 0, "for {:?} got {:?}", msg_action, result) + } _ => unreachable!( "for {:?} got {:?}: should have received a reply", msg_action, @@ -4571,7 +4710,10 @@ mod document_files { None => unreachable!("{:?} did not contain field '{:?}'", holder, name), } } - _ => unreachable!("\n\ngot {:?}\n\nfor {:?}\n\nshould have been a reply", result, docdef), + _ => unreachable!( + "\n\ngot {:?}\n\nfor {:?}\n\nshould have been a reply", + result, docdef + ), } /*