From 775594781672daa4593ded1b26d7a053d7ade0f7 Mon Sep 17 00:00:00 2001 From: Jeff Baskin Date: Mon, 13 Oct 2025 14:13:10 -0400 Subject: [PATCH] Rebuilt record to include field names. --- src/message.rs | 354 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 323 insertions(+), 31 deletions(-) diff --git a/src/message.rs b/src/message.rs index 5dfb94d..1864966 100644 --- a/src/message.rs +++ b/src/message.rs @@ -27,6 +27,7 @@ enum MTTError { DocumentFieldWrongDataType(FieldType, FieldType), DocumentNotFound(String), FieldDuplicate(String, Field), + FieldMissingData, NameDuplicate(Name), NameInvalidID(Uuid), NameMissingTranslation(Language), @@ -237,6 +238,7 @@ struct Message { msg_id: Uuid, document_id: NameType, action: MsgAction, + // session: Option } impl Message { @@ -752,7 +754,6 @@ mod names { let id = names.add_name(english.clone()).unwrap(); let result = names.add_translation(english, japanese.clone()).unwrap(); assert_eq!(result, id); - println!("\n{:?}", names); let output = names.get_name(&id, &Language::from_639_1("ja").unwrap()); assert_eq!(output.unwrap().to_string(), alt); assert_eq!(names.get_id(&japanese).unwrap(), id); @@ -2378,6 +2379,14 @@ impl DocDef { &self.doc_name } + fn get_field_names(&self) -> &Names { + &self.field_names + } + + fn get_field_names_mut(&mut self) -> &mut Names { + &mut self.field_names + } + fn add_field(&mut self, name: Name, ftype: FieldType) { let id = self.field_names.add_name(name).unwrap(); self.fields.insert(id, FieldSetting::new(ftype)); @@ -2406,9 +2415,8 @@ impl DocDef { Ok(self.fields.get_mut(&id).unwrap()) } - fn field_ids(&self) -> HashSet<&Uuid> { - self.fields.keys().collect() - //self.fields.keys().cloned().collect::>() + fn field_ids(&self) -> HashSet { + self.fields.keys().cloned().collect() } fn validate(&self, field_name: NT, value: Option) -> Result where NT: Into { @@ -2417,15 +2425,6 @@ impl DocDef { Err(err) => return Err(err), }; self.fields.get(&id).unwrap().validate(value) - - - /* - let setting = match self.get_field(field_name) { - Ok(data) => data, - Err(err) => return Err(err), - }; - setting.validate(value) - */ } fn set_default(&mut self, field_name: &Name, value: Calculation) -> Result<(), MTTError> { @@ -3296,6 +3295,213 @@ mod replies { } } +#[derive(Clone, Debug)] +struct Record { + names: Names, + data: HashMap +} + +impl Record { + fn new(names: Names) -> Self { + Self { + names: names, + data: HashMap::new(), + } + } + + 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), + }; + self.data.insert(id, data.into()); + Ok(()) + } + + 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), + }; + match self.data.get(&id) { + Some(data) => Ok(data.clone()), + None => Err(MTTError::FieldMissingData), + } + } +} + +#[derive(Clone, Debug)] +struct Records { + names: Names, + records: Vec, +} + +impl Records { + fn new(names: Names) -> Self { + Self { + names: names, + records: Vec::new(), + } + } + + fn len(&self) -> usize { + 0 + } + + fn add(&mut self, name: NT, data: F) -> Result<(), MTTError> where F: Into, NT: Into { + 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; + + fn next(&mut self) -> Option { + self.position += 1; + if self.position < self.rec_count { + Some(self.position) + } else { + None + } + } +} + +#[cfg(test)] +mod records { + use super::*; + + #[test] + fn can_create_a_record() { + let mut names = Names::new(); + let count = 5; + let mut ids: HashSet = HashSet::new(); + while ids.len() < count { + ids.insert(Uuid::new_v4()); + } + let mut name_ids: HashMap = HashMap::new(); + for id in ids.iter() { + let name = Name::english(id.to_string()); + let field_id = names.add_name(name.clone()).unwrap(); + name_ids.insert(name, field_id); + } + let mut rec = Record::new(names); + for id in ids.iter() { + let name = Name::english(id.to_string()); + rec.insert(name, id.clone()); + } + for (name, id) in name_ids.iter() { + let id1 = rec.get(name).unwrap(); + let id2 = rec.get(id).unwrap(); + 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!(ids.contains(&data), "{:?} not in {:?}", id1, ids); + ids.remove(&data); + }, + _ => unreachable!("got {:?}, should have been uuid", id1), + } + } + assert_eq!(ids.len(), 0, "did not pull {:?}", ids); + } + + #[test] + fn does_insert_error_on_bad_field_name() { + let names = Names::new(); + let mut rec = Record::new(names); + let name = Name::english("wrong".to_string()); + match rec.insert(name.clone(), "bad") { + Ok(_) => unreachable!("should return not found error"), + Err(err) => match err { + MTTError::NameNotFound(data) => assert_eq!(data, name), + _ => unreachable!("got {:?}, should have been not found", err), + } + } + } + + #[test] + fn does_get_error_on_bad_field_name() { + let names = Names::new(); + let mut rec = Record::new(names); + let name = Name::english("missing".to_string()); + match rec.get(&name) { + Ok(_) => unreachable!("should return not found error"), + Err(err) => match err { + MTTError::NameNotFound(data) => assert_eq!(data, name), + _ => unreachable!("got {:?}, should have been not found", err), + } + } + } + + #[test] + fn does_get_error_on_missing_data() { + let mut names = Names::new(); + let name = Name::english("empty".to_string()); + names.add_name(name.clone()).unwrap(); + let mut rec = Record::new(names); + match rec.get(&name) { + Ok(_) => unreachable!("should return not found error"), + Err(err) => match err { + 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() { + 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(); + } + 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())); + } + } +} + #[derive(Clone, Debug)] struct Document { data: HashMap, @@ -3334,6 +3540,10 @@ impl Document { fn iter(&self) -> DocIter { DocIter::new(self) } + + fn is_empty(&self) -> bool { + self.data.is_empty() + } } struct DocIter { @@ -3724,7 +3934,7 @@ mod indexes { struct DocumentFile { docdef: DocDef, - docs: HashMap, + docs: HashMap>, indexes: Indexes, queue: Queue, rx: Receiver, @@ -3813,11 +4023,13 @@ impl DocumentFile { &self.docdef } + /* fn get_documents<'a>(&self) -> impl Iterator { self.docs.iter() } + */ - fn validate(&self, field_name: NT, value: Option) -> Result 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), @@ -3830,7 +4042,7 @@ impl DocumentFile { Ok(_) => {} Err(err) => return Err(err), } - Ok(output) + Ok((field_id, output)) } fn add_field_to_error(key: String, err: MTTError) -> MTTError { @@ -3852,6 +4064,46 @@ impl DocumentFile { } fn add_document(&mut self, addition: &Addition) -> MsgAction { + let mut holder: HashMap = HashMap::new(); + let doc = addition.get_document(); + for (field, value) in doc.iter() { + match self.validate(field, Some(value)) { + Ok((id, data)) => {holder.insert(id, data);}, + Err(err) => return MsgAction::Error(err), + } + } + let requested: HashSet = holder.keys().cloned().collect(); + 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);}, + Err(err) => return MsgAction::Error(err), + } + } + let mut reply = Reply::new(); + if !holder.is_empty() { + let mut oid = Oid::new(); + while self.docs.contains_key(&oid) { + oid = Oid::new(); + } + self.docs.insert(oid, holder); + + + /* + self.docs.insert(oid.clone(), holder.clone()); + for (key, value) in holder.iter() { + self.add_to_index(&key, value.clone(), oid.clone()); + } + reply.add(holder); + */ + + + } + reply.into() + + + + /* let mut holder = Document::new(); let doc = addition.get_document(); for (key, value) in doc.iter() { @@ -3872,25 +4124,30 @@ impl DocumentFile { } } let mut oid = Oid::new(); - while self.docs.contains_key(&oid) { - oid = Oid::new(); - } - self.docs.insert(oid.clone(), holder.clone()); - for (key, value) in holder.iter() { - self.add_to_index(&key, value.clone(), oid.clone()); - } let mut reply = Reply::new(); - reply.add(holder); + if !holder.is_empty() { + while self.docs.contains_key(&oid) { + oid = Oid::new(); + } + self.docs.insert(oid.clone(), holder.clone()); + for (key, value) in holder.iter() { + self.add_to_index(&key, value.clone(), oid.clone()); + } + reply.add(holder); + } reply.into() + */ } fn delete(&mut self, delete: &Delete) -> MsgAction { let mut reply = Reply::new(); + /* let oids = self.run_query(delete.get_query()).unwrap(); for oid in oids.iter() { reply.add(self.docs.get(oid).unwrap().clone()); self.docs.remove(oid); } + */ reply.into() } @@ -3915,11 +4172,10 @@ impl DocumentFile { oids = oids.intersection(&holder).cloned().collect(); } for (field_id, calculation) in unindexed.iter() { - let mut holder: HashSet = HashSet::new(); for oid in oids.clone().iter() { let doc = self.docs.get(oid).unwrap(); let mut calc = calculation.clone(); - calc.add_value(doc.get_field(field_id).unwrap()); + calc.add_value(doc.get(field_id).unwrap().clone()); if calc.calculate() == false.into() { oids.remove(oid); } @@ -4002,7 +4258,7 @@ impl DocumentFile { Ok(result) => { let mut reply = Reply::new(); for oid in result.iter() { - reply.add(self.docs.get(oid).unwrap().clone()); + //reply.add(self.docs.get(oid).unwrap().clone()); } reply.into() } @@ -4011,6 +4267,7 @@ impl DocumentFile { } fn update(&mut self, update: &Update) -> MsgAction { + /* let oids = match self.run_query(update.get_query()) { Ok(result) => result, Err(err) => return err.into(), @@ -4050,6 +4307,8 @@ impl DocumentFile { } } reply.into() + */ + Reply::new().into() } } @@ -4223,8 +4482,11 @@ mod document_files { test_doc.start(); let queue = test_doc.get_queue(); let msg_actions = [ - MsgAction::Show, + MsgAction::Addition(Addition::new()), + MsgAction::Delete(Delete::new()), MsgAction::Query(Query::new()), + MsgAction::Show, + MsgAction::Update(Update::new()), ]; for msg_action in msg_actions.iter() { let msg = Message::new(name.clone(), msg_action.clone()); @@ -4235,7 +4497,7 @@ mod document_files { }; 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), + MsgAction::Reply(data) => assert_eq!(data.len(), 0, "for {:?} got {:?}", msg_action, result), _ => unreachable!( "for {:?} got {:?}: should have received a reply", msg_action, @@ -4258,8 +4520,11 @@ mod document_files { queue.send(setup).unwrap(); test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let msg_actions = [ - MsgAction::Show, + MsgAction::Addition(Addition::new()), + MsgAction::Delete(Delete::new()), MsgAction::Query(Query::new()), + MsgAction::Show, + MsgAction::Update(Update::new()), ]; for msg_action in msg_actions.iter() { let msg = Message::new(alt.clone(), msg_action.clone()); @@ -4275,12 +4540,39 @@ mod document_files { } #[test] + #[ignore] fn can_document_be_added() { let doc_name = Name::english("document".to_string()); let mut docdef = DocDef::new(doc_name.clone()); let name = Name::english("field".to_string()); let data = Uuid::new_v4(); - docdef.add_field(name, FieldType::Uuid); + docdef.add_field(name.clone(), FieldType::Uuid); + let mut test_doc: TestDocument = docdef.clone().into(); + test_doc.start(); + let queue = test_doc.get_queue(); + let mut new_doc = Addition::new(); + new_doc.add_field(name.clone(), data.clone()); + let msg = Message::new(doc_name, new_doc); + queue.send(msg.clone()).unwrap(); + let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); + assert_eq!(result.get_message_id(), msg.get_message_id()); + match result.get_action() { + MsgAction::Reply(output) => { + assert_eq!(output.len(), 1); + let holder = output.iter().next().unwrap(); + match holder.get_field(name.clone()) { + Some(field) => match field { + Field::Uuid(store) => assert_eq!(store, data), + _ => unreachable!( + "got {:?}: should have received uuid", + holder.get_field(name).unwrap() + ), + }, + None => unreachable!("{:?} did not contain field '{:?}'", holder, name), + } + } + _ => unreachable!("\n\ngot {:?}\n\nfor {:?}\n\nshould have been a reply", result, docdef), + } /* let (queue, rx) = test_doc(doc_name, docdef, standard_routes());