Files
morethantext/src/message/wrapper.rs

367 lines
10 KiB
Rust
Raw Normal View History

2026-02-18 10:09:55 -05:00
use super::MessageAction;
2026-01-31 13:03:01 -05:00
use crate::{
2026-02-14 13:04:55 -05:00
action::{CalcValue, Field, FieldType, MsgAction, Operand, Query, Reply},
mtterror::{ErrorID, MTTError},
2026-01-31 13:03:01 -05:00
name::{NameType, Names},
queue::data_director::{Include, Path, Route},
2026-01-31 13:03:01 -05:00
};
use chrono::prelude::*;
use std::{
collections::{HashMap, HashSet},
time::Duration,
};
use uuid::Uuid;
2026-03-10 10:41:24 -04:00
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct MessageID {
data: Uuid,
}
impl MessageID {
pub fn new() -> Self {
Self {
data: Uuid::new_v4(),
}
}
}
#[cfg(test)]
mod message_ids {
use super::*;
#[test]
fn are_message_ids_unique() {
let id1 = MessageID::new();
let id2 = MessageID::new();
assert!(id1 != id2);
}
}
2026-01-31 13:03:01 -05:00
#[derive(Clone, Debug)]
pub struct Message {
2026-03-10 10:41:24 -04:00
msg_id: MessageID,
2026-01-31 13:03:01 -05:00
action: MsgAction,
route: Route,
// session: Option<?>
}
impl Message {
2026-02-18 10:09:55 -05:00
pub fn new<A>(action: A) -> Self
where
A: Into<MsgAction>,
{
let msg_id = MessageID::new();
Self::with_id(msg_id, action)
}
pub fn with_id<A>(msg_id: MessageID, action: A) -> Self
2026-02-18 10:09:55 -05:00
where
A: Into<MsgAction>,
{
Self {
msg_id: msg_id,
2026-02-18 10:09:55 -05:00
action: action.into(),
route: Route::default(),
}
}
2026-03-10 10:41:24 -04:00
pub fn get_message_id(&self) -> &MessageID {
2026-01-31 13:03:01 -05:00
&self.msg_id
}
pub fn get_action(&self) -> &MsgAction {
&self.action
}
pub fn get_path(&self) -> Path {
Path::new(
Include::Just(self.msg_id.clone()),
2026-02-18 08:55:42 -05:00
Include::Just(self.action.doc_name().clone()),
2026-01-31 13:03:01 -05:00
Include::Just(self.action.clone().into()),
)
}
pub fn get_route(&self) -> Route {
self.route.clone()
}
pub fn set_route(&mut self, route: Route) {
self.route = route;
}
pub fn response<A>(&self, action: A) -> Self
where
A: Into<MsgAction>,
{
Self {
msg_id: self.msg_id.clone(),
action: action.into(),
route: Route::default(),
}
}
pub fn forward<D, A>(&self, doc_id: D, action: A) -> Self
where
D: Into<NameType>,
A: Into<MsgAction>,
{
Self {
msg_id: self.msg_id.clone(),
action: action.into(),
route: Route::default(),
}
}
}
impl MessageAction for Message {
fn doc_name(&self) -> &NameType {
self.get_action().doc_name()
}
}
2026-01-31 13:03:01 -05:00
#[cfg(test)]
mod messages {
use super::*;
2026-02-27 08:36:22 -05:00
use crate::{
action::DocDef,
name::{name_id_support::test_name_id, Name},
};
2026-01-31 13:03:01 -05:00
#[test]
fn can_the_document_be_a_named_reference() {
let dts = [Name::english("one"), Name::english("two")];
for document in dts.into_iter() {
2026-02-18 10:09:55 -05:00
let msg = Message::new(MsgAction::Create(DocDef::new(document.clone())));
2026-01-31 13:03:01 -05:00
match msg.get_action() {
MsgAction::Create(_) => {}
_ => unreachable!("should have been a create document"),
}
}
}
#[test]
fn can_the_document_be_an_id() {
2026-02-27 08:36:22 -05:00
let document = test_name_id();
2026-02-18 10:09:55 -05:00
let msg = Message::new(Query::new(document.clone()));
2026-01-31 13:03:01 -05:00
match msg.get_action() {
MsgAction::Query(_) => {}
_ => unreachable!("should have been an access query"),
}
}
#[test]
fn do_messages_contain_routes() {
2026-02-09 19:28:22 -05:00
let name = Name::english("whatever");
2026-02-18 10:09:55 -05:00
let mut msg = Message::new(Query::new(name.clone()));
2026-01-31 13:03:01 -05:00
let default_route = msg.get_route();
match default_route.msg_id {
Include::Just(_) => unreachable!("should defalt to all"),
Include::All => {}
}
match default_route.doc_id {
Include::Just(_) => unreachable!("should defalt to all"),
Include::All => {}
}
match default_route.action {
Include::Just(_) => unreachable!("should defalt to all"),
Include::All => {}
}
2026-02-27 08:36:22 -05:00
let doc_id = test_name_id();
2026-01-31 13:03:01 -05:00
let route = Route::new(
Include::Just(msg.get_message_id().clone()),
Include::Just(doc_id.clone()),
Include::Just(msg.get_action().into()),
);
msg.set_route(route);
let result = msg.get_route();
match result.msg_id {
Include::Just(data) => assert_eq!(&data, msg.get_message_id()),
Include::All => unreachable!("should have message id"),
}
match result.doc_id {
Include::Just(data) => assert_eq!(data, doc_id),
Include::All => unreachable!("should have document id"),
}
match result.action {
Include::Just(data) => assert_eq!(data, msg.get_action().into()),
Include::All => unreachable!("should have action"),
}
}
#[test]
fn is_the_message_id_random() {
2026-03-10 10:41:24 -04:00
let mut ids: Vec<MessageID> = Vec::new();
2026-01-31 13:03:01 -05:00
for _ in 0..5 {
2026-02-18 10:09:55 -05:00
let msg = Message::new(Query::new(Name::english("test")));
2026-01-31 13:03:01 -05:00
let id = msg.get_message_id().clone();
2026-03-10 10:41:24 -04:00
assert!(!ids.contains(&id), "{:?} containts {:?}", ids, id);
2026-01-31 13:03:01 -05:00
ids.push(id);
}
}
#[test]
fn can_make_reply_message() {
let name = Name::english("testing");
2026-02-18 10:09:55 -05:00
let msg = Message::new(Query::new(name.clone()));
let responce = Reply::new(Name::english("something"));
2026-01-31 13:03:01 -05:00
let reply = msg.response(responce);
assert_eq!(reply.get_message_id(), msg.get_message_id());
match reply.get_action() {
MsgAction::Reply(_) => {}
_ => unreachable!("should have been a reply"),
}
}
#[test]
fn can_make_error_message() {
let name = Name::english("testing");
2026-02-18 10:09:55 -05:00
let msg = Message::new(Query::new(name.clone()));
2026-01-31 13:03:01 -05:00
let err_msg = Uuid::new_v4().to_string();
2026-02-25 10:16:50 -05:00
let result = msg.response(MTTError::new(ErrorID::DocumentNotFound));
2026-01-31 13:03:01 -05:00
assert_eq!(result.get_message_id(), msg.get_message_id());
match result.get_action() {
2026-02-25 10:16:50 -05:00
MsgAction::Error(data) => match data.get_error_ids().back().unwrap() {
ErrorID::DocumentNotFound => {}
2026-01-31 13:03:01 -05:00
_ => unreachable!("got {:?}, should have received not found", data),
},
_ => unreachable!("should have been a reply"),
}
}
#[test]
fn can_make_a_response_message() {
2026-02-27 08:36:22 -05:00
let doc_id = test_name_id();
2026-02-18 10:09:55 -05:00
let msg = Message::new(Query::new(doc_id.clone()));
2026-01-31 13:03:01 -05:00
let data = Uuid::new_v4().to_string();
2026-02-25 10:16:50 -05:00
let result1 = msg.response(MTTError::new(ErrorID::DocumentNotFound));
let result2 = msg.response(Reply::new(NameType::None));
2026-01-31 13:03:01 -05:00
assert_eq!(result1.get_message_id(), msg.get_message_id());
assert_eq!(result2.get_message_id(), msg.get_message_id());
let action1 = result1.get_action();
match action1 {
2026-02-25 10:16:50 -05:00
MsgAction::Error(err) => match err.get_error_ids().back().unwrap() {
ErrorID::DocumentNotFound => {}
2026-01-31 13:03:01 -05:00
_ => unreachable!("got {:?}: should have received document not found", err),
},
_ => unreachable!("got {:?}: should have received error", action1),
}
let action2 = result2.get_action();
match action2 {
MsgAction::Reply(data) => assert_eq!(data.len(), 0),
_ => unreachable!("got {:?}: should have received a reply", action2),
}
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct Operation {
field_name: String,
operation: Operand,
value: Field,
}
#[allow(dead_code)]
impl Operation {
pub fn new<F>(name: String, op: Operand, value: F) -> Self
where
F: Into<Field>,
{
Self {
field_name: name,
operation: op,
value: value.into(),
}
}
fn which_field(&self) -> String {
self.field_name.clone()
}
pub fn validate(&self, field: &Field) -> bool {
self.operation.validate(field, &self.value)
}
}
#[derive(Clone, Debug)]
pub struct Document {
data: HashMap<NameType, CalcValue>,
}
impl Document {
fn new() -> Self {
Self {
data: HashMap::new(),
}
}
pub fn add_field<NT, CV>(&mut self, name: NT, field: CV)
where
CV: Into<CalcValue>,
NT: Into<NameType>,
{
self.data.insert(name.into(), field.into());
}
fn get_field<NT>(&self, name: NT) -> &CalcValue
where
NT: Into<NameType>,
{
match self.data.get(&name.into()) {
Some(data) => data,
None => &CalcValue::None,
}
}
#[allow(dead_code)]
fn get_all(&self) -> Vec<(NameType, Field)> {
let mut output = Vec::new();
for (key, value) in self.data.iter() {
output.push((key.clone(), value.get(&Field::None)));
}
output
}
pub fn iter(&self) -> impl Iterator<Item = (&NameType, &CalcValue)> {
self.data.iter()
}
}
#[cfg(test)]
mod documents {
use super::*;
use crate::name::Name;
#[test]
fn can_add_static_string() {
let mut add = Document::new();
let name = Name::english(Uuid::new_v4().to_string().as_str());
let data = Uuid::new_v4().to_string();
add.add_field(name.clone(), data.clone());
let result = add.get_field(&name);
match result {
CalcValue::Value(holder) => match holder {
Field::StaticString(result) => assert_eq!(result, &data),
_ => unreachable!("got {:?}: should have received static string", holder),
},
_ => unreachable!("got {:?}, should have been value", result),
}
}
#[test]
fn can_add_uuid() {
let mut add = Document::new();
let name = Name::english(Uuid::new_v4().to_string().as_str());
let data = Uuid::new_v4();
add.add_field(name.clone(), data.clone());
let result = add.get_field(&name);
match result {
CalcValue::Value(holder) => match holder {
Field::Uuid(result) => assert_eq!(result, &data),
_ => unreachable!("got {:?}: should have received static string", holder),
},
_ => unreachable!("got {:?}, should have been value", result),
}
}
}