morethantext/src/message.rs
Jeff Baskin 5833d20ea6
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 1s
Moved queue into separate module.
2026-01-30 13:58:55 -05:00

1689 lines
46 KiB
Rust

use crate::{
document::{
definition::DocDef,
field::{Field, FieldType},
},
mtterror::MTTError,
name::{NameType, Names},
queue::data_director::{Include, Path, Register, Route},
};
use chrono::prelude::*;
use std::{
collections::{HashMap, HashSet},
time::Duration,
};
use uuid::Uuid;
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Action {
Addition,
Create,
Delete,
Error,
GetLog,
Log,
OnAddition,
OnDelete,
OnQuery,
OnUpdate,
Query,
Records,
Register,
Reply,
Show,
Update,
}
impl From<MsgAction> for Action {
fn from(value: MsgAction) -> Self {
match value {
MsgAction::Addition(_) => Action::Addition,
MsgAction::Create(_) => Action::Create,
MsgAction::Delete(_) => Action::Delete,
MsgAction::Error(_) => Action::Error,
MsgAction::GetLog(_) => Action::GetLog,
MsgAction::OnAddition(_) => Action::OnAddition,
MsgAction::OnDelete(_) => Action::OnDelete,
MsgAction::OnQuery(_) => Action::OnQuery,
MsgAction::OnUpdate(_) => Action::OnUpdate,
MsgAction::Query(_) => Action::Query,
MsgAction::Records(_) => Action::Records,
MsgAction::Register(_) => Action::Register,
MsgAction::Reply(_) => Action::Reply,
MsgAction::Show => Action::Show,
MsgAction::Update(_) => Action::Update,
}
}
}
impl From<&MsgAction> for Action {
fn from(value: &MsgAction) -> Self {
let action = value.clone();
Self::from(action)
}
}
#[derive(Clone, Debug)]
pub enum MsgAction {
Addition(Addition),
Create(DocDef),
// Alter
// Remove
Error(MTTError),
GetLog(Uuid),
OnAddition(Records),
OnDelete(Records),
OnQuery(Records),
OnUpdate(Records),
Query(Query),
Records(Records),
Register(Register),
Reply(Reply),
Show,
Delete(Delete),
Update(Update),
}
impl From<Addition> for MsgAction {
fn from(value: Addition) -> Self {
MsgAction::Addition(value)
}
}
impl From<Delete> for MsgAction {
fn from(value: Delete) -> Self {
MsgAction::Delete(value)
}
}
impl From<DocDef> for MsgAction {
fn from(value: DocDef) -> Self {
MsgAction::Create(value)
}
}
impl From<MTTError> for MsgAction {
fn from(value: MTTError) -> Self {
MsgAction::Error(value)
}
}
impl From<Query> for MsgAction {
fn from(value: Query) -> Self {
MsgAction::Query(value)
}
}
impl From<Records> for MsgAction {
fn from(value: Records) -> Self {
MsgAction::Records(value)
}
}
impl From<Register> for MsgAction {
fn from(value: Register) -> Self {
MsgAction::Register(value)
}
}
impl From<Reply> for MsgAction {
fn from(value: Reply) -> Self {
MsgAction::Reply(value)
}
}
impl From<Update> for MsgAction {
fn from(value: Update) -> Self {
MsgAction::Update(value)
}
}
impl From<Uuid> for MsgAction {
fn from(value: Uuid) -> Self {
MsgAction::GetLog(value)
}
}
impl From<&Uuid> for MsgAction {
fn from(value: &Uuid) -> Self {
Self::from(value.clone())
}
}
#[cfg(test)]
mod msgactions {
use super::*;
use crate::name::Name;
#[test]
fn turn_document_definition_into_action() {
let name = Name::english(Uuid::new_v4().to_string().as_str());
let value = DocDef::new(name.clone());
let result: MsgAction = value.into();
match result {
MsgAction::Create(def) => assert_eq!(def.get_document_names(), &[name].to_vec()),
_ => unreachable!("Got {:?}: dhould have been create", result),
}
}
#[test]
fn turn_error_into_action() {
let data = "data".to_string();
let value = MTTError::DocumentAlreadyExists(data.clone());
let result: MsgAction = value.into();
match result {
MsgAction::Error(result) => match result {
MTTError::DocumentAlreadyExists(output) => assert_eq!(output, data),
_ => unreachable!("Got {:?}: dhould have been create", result),
},
_ => unreachable!("Got {:?}: dhould have been create", result),
}
let value = MTTError::DocumentNotFound(data.clone());
let result: MsgAction = value.into();
match result {
MsgAction::Error(result) => match result {
MTTError::DocumentNotFound(output) => assert_eq!(output, data),
_ => unreachable!("Got {:?}: dhould have been create", result),
},
_ => unreachable!("Got {:?}: dhould have been create", result),
}
}
#[test]
fn turn_query_into_action() {
let value = Query::new();
let result: MsgAction = value.into();
match result {
MsgAction::Query(_) => {}
_ => unreachable!("Got {:?}: dhould have been query", result),
}
}
#[test]
fn turn_reply_into_action() {
let value = Reply::new();
let result: MsgAction = value.into();
match result {
MsgAction::Reply(_) => {}
_ => unreachable!("Got {:?}: dhould have been reply", result),
}
}
}
#[derive(Clone, Debug)]
pub struct Message {
msg_id: Uuid,
document_id: NameType,
action: MsgAction,
route: Route,
// session: Option<?>
}
impl Message {
pub fn new<D, A>(doc_id: D, action: A) -> Self
where
D: Into<NameType>,
A: Into<MsgAction>,
{
Self {
msg_id: Uuid::new_v4(),
document_id: doc_id.into(),
action: action.into(),
route: Route::default(),
}
}
pub fn get_message_id(&self) -> &Uuid {
&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()),
Include::Just(self.document_id.clone()),
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(),
document_id: self.document_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(),
document_id: doc_id.into(),
action: action.into(),
route: Route::default(),
}
}
}
#[cfg(test)]
mod messages {
use super::*;
use crate::name::Name;
#[test]
fn can_the_document_be_a_named_reference() {
let dts = [Name::english("one"), Name::english("two")];
for document in dts.into_iter() {
let msg = Message::new(
document.clone(),
MsgAction::Create(DocDef::new(document.clone())),
);
match &msg.document_id {
NameType::Name(data) => assert_eq!(data, &document),
_ => unreachable!("should have been a string id"),
}
match msg.get_action() {
MsgAction::Create(_) => {}
_ => unreachable!("should have been a create document"),
}
}
}
#[test]
fn can_the_document_be_an_id() {
let document = Uuid::new_v4();
let msg = Message::new(document.clone(), Query::new());
match msg.document_id {
NameType::ID(data) => assert_eq!(data, document),
_ => unreachable!("should have been an id"),
}
match msg.get_action() {
MsgAction::Query(_) => {}
_ => unreachable!("should have been an access query"),
}
}
#[test]
fn do_messages_contain_routes() {
let mut msg = Message::new(Name::english("whatever"), Query::new());
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 => {}
}
let doc_id = Uuid::new_v4();
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() {
let mut ids: Vec<Uuid> = Vec::new();
for _ in 0..5 {
let msg = Message::new(Name::english("tester"), Query::new());
let id = msg.get_message_id().clone();
assert!(!ids.contains(&id), "{:?} containts {}", ids, id);
ids.push(id);
}
}
#[test]
fn can_make_reply_message() {
let name = Name::english("testing");
let msg = Message::new(name.clone(), Query::new());
let responce = Reply::new();
let reply = msg.response(responce);
assert_eq!(reply.get_message_id(), msg.get_message_id());
match &reply.document_id {
NameType::Name(data) => assert_eq!(data, &name),
_ => unreachable!("should have been a name"),
}
match reply.get_action() {
MsgAction::Reply(_) => {}
_ => unreachable!("should have been a reply"),
}
}
#[test]
fn can_make_error_message() {
let name = Name::english("testing");
let msg = Message::new(name.clone(), Query::new());
let err_msg = Uuid::new_v4().to_string();
let result = msg.response(MTTError::DocumentNotFound(err_msg.clone()));
assert_eq!(result.get_message_id(), msg.get_message_id());
match &result.document_id {
NameType::Name(data) => assert_eq!(data, &name),
_ => unreachable!("should have been a name"),
}
match result.get_action() {
MsgAction::Error(data) => match data {
MTTError::DocumentNotFound(txt) => assert_eq!(txt, &err_msg),
_ => unreachable!("got {:?}, should have received not found", data),
},
_ => unreachable!("should have been a reply"),
}
}
#[test]
fn can_make_a_response_message() {
let doc_id = Uuid::new_v4();
let msg = Message::new(doc_id.clone(), Query::new());
let data = Uuid::new_v4().to_string();
let result1 = msg.response(MTTError::DocumentNotFound(data.clone()));
let result2 = msg.response(Reply::new());
assert_eq!(result1.get_message_id(), msg.get_message_id());
assert_eq!(result2.get_message_id(), msg.get_message_id());
assert_eq!(result1.document_id, msg.document_id);
assert_eq!(result2.document_id, msg.document_id);
let action1 = result1.get_action();
match action1 {
MsgAction::Error(err) => match err {
MTTError::DocumentNotFound(output) => assert_eq!(output, &data),
_ => 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),
}
}
}
#[derive(Clone, Debug)]
pub struct Addition {
data: Document,
}
impl Addition {
pub fn new() -> Self {
Self {
data: Document::new(),
}
}
#[allow(dead_code)]
pub fn add_field<NT, CV>(&mut self, name: NT, field: CV)
where
CV: Into<CalcValue>,
NT: Into<NameType>,
{
self.data.add_field(name, field);
}
#[allow(dead_code)]
fn get_field<NT>(&self, name: NT) -> &CalcValue
where
NT: Into<NameType>,
{
self.data.get_field(name)
}
#[allow(dead_code)]
fn get_document(&self) -> Document {
self.data.clone()
}
pub fn iter(&self) -> impl Iterator<Item = (&NameType, &CalcValue)> {
self.data.iter()
}
}
#[cfg(test)]
mod additions {
use super::*;
use crate::name::Name;
#[test]
fn can_add_static_string() {
let mut add = Addition::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(result) => match result {
Field::StaticString(output) => assert_eq!(output, &data),
_ => unreachable!("got {:?}, should have been a string", result),
},
_ => unreachable!("got {:?}: should have received value", result),
}
}
#[test]
fn can_add_uuid() {
let mut add = Addition::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 output = add.get_field(&name);
match output {
CalcValue::Value(result) => match result {
Field::Uuid(result) => assert_eq!(result, &data),
_ => unreachable!("got {:?}: should have received uuid", result),
},
_ => unreachable!("got {:?}: should have received value", output),
}
}
#[test]
fn can_get_document() {
let mut add = Addition::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 doc = add.get_document();
let output = doc.get_field(&name);
match output {
CalcValue::Value(holder) => match holder {
Field::Uuid(result) => assert_eq!(result, &data),
_ => unreachable!("should have received uuid"),
},
_ => unreachable!("got {:?}: should have received value", output),
}
}
}
#[derive(Clone, Debug)]
pub enum Operand {
Add,
Equal,
GreaterThan,
GreaterThanEqual,
LessThan,
LessThanEqual,
}
impl Operand {
#[allow(dead_code)]
fn validate(&self, x: &Field, y: &Field) -> bool {
match self {
Self::Equal => x == y,
Self::GreaterThan => x > y,
Self::GreaterThanEqual => x >= y,
Self::LessThan => x < y,
Self::LessThanEqual => x <= y,
_ => false,
}
}
}
#[cfg(test)]
mod operands {
use super::*;
#[test]
fn equals_true() {
let data: Field = Uuid::new_v4().into();
assert!(Operand::Equal.validate(&data, &data));
}
#[test]
fn equals_false() {
let x: Field = Uuid::new_v4().into();
let mut y: Field = Uuid::new_v4().into();
while x == y {
y = Uuid::new_v4().into();
}
assert!(!Operand::Equal.validate(&x, &y));
}
#[test]
fn does_greater() {
let data: Vec<Field> = vec![1.into(), 2.into(), 3.into()];
assert!(!Operand::GreaterThan.validate(&data[0], &data[1]));
assert!(!Operand::GreaterThan.validate(&data[1], &data[1]));
assert!(Operand::GreaterThan.validate(&data[2], &data[1]));
}
#[test]
fn does_greater_equal() {
let data: Vec<Field> = vec![1.into(), 2.into(), 3.into()];
assert!(!Operand::GreaterThanEqual.validate(&data[0], &data[1]));
assert!(Operand::GreaterThanEqual.validate(&data[1], &data[1]));
assert!(Operand::GreaterThanEqual.validate(&data[2], &data[1]));
}
#[test]
fn does_lesser() {
let data: Vec<Field> = vec![1.into(), 2.into(), 3.into()];
assert!(Operand::LessThan.validate(&data[0], &data[1]));
assert!(!Operand::LessThan.validate(&data[1], &data[1]));
assert!(!Operand::LessThan.validate(&data[2], &data[1]));
}
#[test]
fn does_lesser_equal() {
let data: Vec<Field> = vec![1.into(), 2.into(), 3.into()];
assert!(Operand::LessThanEqual.validate(&data[0], &data[1]));
assert!(Operand::LessThanEqual.validate(&data[1], &data[1]));
assert!(!Operand::LessThanEqual.validate(&data[2], &data[1]));
}
}
#[derive(Clone, Debug)]
pub enum CalcValue {
Calculate(Calculation),
Existing(FieldType),
FType(FieldType),
None,
Value(Field),
}
impl CalcValue {
pub fn get(&self, existing: &Field) -> Field {
match self {
Self::Calculate(calc) => calc.calculate(existing),
Self::Existing(_) => existing.clone(),
Self::FType(ftype) => ftype.get_default(),
Self::None => Field::None,
Self::Value(field) => field.clone(),
}
}
fn get_type(&self) -> FieldType {
match self {
Self::Calculate(calc) => calc.get_type(),
Self::Existing(ftype) => ftype.clone(),
Self::FType(ftype) => ftype.clone(),
Self::None => FieldType::None,
Self::Value(field) => field.into(),
}
}
}
impl From<Calculation> for CalcValue {
fn from(value: Calculation) -> Self {
Self::Calculate(value)
}
}
impl From<Field> for CalcValue {
fn from(value: Field) -> Self {
Self::Value(value)
}
}
impl From<&Field> for CalcValue {
fn from(value: &Field) -> Self {
Self::from(value.clone())
}
}
impl From<FieldType> for CalcValue {
fn from(value: FieldType) -> Self {
Self::FType(value)
}
}
impl From<bool> for CalcValue {
fn from(value: bool) -> Self {
let output: Field = value.into();
Self::from(output).into()
}
}
impl From<DateTime<Utc>> for CalcValue {
fn from(value: DateTime<Utc>) -> Self {
let output: Field = value.into();
Self::from(output).into()
}
}
impl From<Duration> for CalcValue {
fn from(value: Duration) -> Self {
let output: Field = value.into();
Self::from(output).into()
}
}
impl From<i128> for CalcValue {
fn from(value: i128) -> Self {
let output: Field = value.into();
Self::from(output).into()
}
}
impl From<&str> for CalcValue {
fn from(value: &str) -> Self {
let output: Field = value.into();
Self::from(output).into()
}
}
impl From<String> for CalcValue {
fn from(value: String) -> Self {
let output: Field = value.into();
Self::from(output).into()
}
}
impl From<Uuid> for CalcValue {
fn from(value: Uuid) -> Self {
let output: Field = value.into();
Self::from(output).into()
}
}
#[cfg(test)]
mod calcvalues {
use super::*;
#[test]
fn from_uuid() {
let value = Uuid::new_v4();
let expected: Field = value.into();
let result: CalcValue = value.into();
match result.clone() {
CalcValue::Value(data) => assert_eq!(data, expected),
_ => unreachable!("got {:?}, should have gotten a field", result),
}
assert_eq!(result.get(&Field::None), expected);
}
#[test]
fn from_str() {
let value = "something";
let expected: Field = value.into();
let result: CalcValue = value.into();
match result.clone() {
CalcValue::Value(data) => assert_eq!(data, expected),
_ => unreachable!("got {:?}, should have gotten a field", result),
}
assert_eq!(result.get(&Field::None), expected);
}
#[test]
fn from_string() {
let value = "data".to_string();
let expected: Field = value.clone().into();
let result: CalcValue = value.into();
match result.clone() {
CalcValue::Value(data) => assert_eq!(data, expected),
_ => unreachable!("got {:?}, should have gotten a field", result),
}
assert_eq!(result.get(&Field::None), expected);
}
#[test]
fn from_boolean() {
let value = true;
let expected: Field = value.clone().into();
let result: CalcValue = value.into();
match result.clone() {
CalcValue::Value(data) => assert_eq!(data, expected),
_ => unreachable!("got {:?}, should have gotten a field", result),
}
assert_eq!(result.get(&Field::None), expected);
}
#[test]
fn from_datetime() {
let value = Utc::now();
let expected: Field = value.clone().into();
let result: CalcValue = value.into();
match result.clone() {
CalcValue::Value(data) => assert_eq!(data, expected),
_ => unreachable!("got {:?}, should have gotten a field", result),
}
assert_eq!(result.get(&Field::None), expected);
}
#[test]
fn from_duration() {
let value = Duration::from_secs(5);
let expected: Field = value.clone().into();
let result: CalcValue = value.into();
match result.clone() {
CalcValue::Value(data) => assert_eq!(data, expected),
_ => unreachable!("got {:?}, should have gotten a field", result),
}
assert_eq!(result.get(&Field::None), expected);
}
#[test]
fn from_integer() {
let value: i128 = 5;
let expected: Field = value.clone().into();
let result: CalcValue = value.into();
match result.clone() {
CalcValue::Value(data) => assert_eq!(data, expected),
_ => unreachable!("got {:?}, should have gotten a field", result),
}
assert_eq!(result.get(&Field::None), expected);
}
#[test]
fn from_calculation() {
let duration = Duration::from_secs(300);
let start = Utc::now() + duration;
let mut calc = Calculation::new(Operand::Add);
calc.add_value(FieldType::DateTime).unwrap();
calc.add_value(duration.clone()).unwrap();
let result: CalcValue = calc.into();
let data = match result.get(&Field::None) {
Field::DateTime(data) => data,
_ => unreachable!(),
};
let stop = Utc::now() + duration;
assert!(
data > start && data < stop,
"should be about 5 minutes ahead"
);
}
}
#[derive(Clone, Debug)]
pub struct Calculation {
operation: Operand,
values: Vec<CalcValue>,
}
impl Calculation {
pub fn new(operand: Operand) -> Self {
Self {
operation: operand,
values: Vec::new(),
}
}
#[allow(dead_code)]
fn operation(&self) -> &Operand {
&self.operation
}
#[allow(dead_code)]
fn get_fields(&self, existing: Field) -> Vec<Field> {
let mut output = Vec::new();
for item in self.values.iter() {
output.push(item.get(&existing));
}
output
}
pub fn get_type(&self) -> FieldType {
if self.values.is_empty() {
FieldType::None
} else {
self.values[0].get_type()
}
}
pub fn add_value<CV>(&mut self, data: CV) -> Result<(), MTTError>
where
CV: Into<CalcValue>,
{
let holder: CalcValue = data.into();
if self.values.is_empty() {
self.values.push(holder);
return Ok(());
}
let mut base = self.get_type();
match self.operation {
Operand::Add => match base {
FieldType::DateTime => base = FieldType::Duration,
_ => {}
},
_ => {}
}
let ftype = holder.get_type();
if base == ftype {
self.values.push(holder);
} else {
return Err(MTTError::DocumentFieldWrongDataType(base, ftype));
}
Ok(())
}
#[allow(dead_code)]
fn validate_value<CV>(&self, value: CV) -> Result<(), MTTError>
where
CV: Into<CalcValue>,
{
if self.values.is_empty() {
return Ok(());
}
let holder = value.into();
let mut base = self.get_type();
match self.operation {
Operand::Add => {
if base == FieldType::DateTime {
base = FieldType::Duration;
}
}
_ => {}
}
let ftype = holder.get_type();
if base == ftype {
Ok(())
} else {
Err(MTTError::DocumentFieldWrongDataType(base, ftype))
}
}
pub fn calculate(&self, existing: &Field) -> Field {
let mut result = Field::None;
match self.operation {
Operand::Add => {
let mut first = true;
for value in self.values.iter() {
let data = value.get(existing);
if first {
result = data;
first = false;
} else {
result += data;
}
}
}
Operand::Equal => {
if self.values.len() >= 2 {
result = self.values[0]
.get(existing)
.equal(&self.values[1].get(existing));
}
}
Operand::GreaterThan => {
if self.values.len() >= 2 {
result = self.values[0]
.get(existing)
.greater(&self.values[1].get(existing));
}
}
Operand::GreaterThanEqual => {
if self.values.len() >= 2 {
result = self.values[0]
.get(existing)
.greater_equal(&self.values[1].get(existing));
}
}
Operand::LessThan => {
if self.values.len() >= 2 {
result = self.values[0]
.get(existing)
.lesser(&self.values[1].get(existing));
}
}
Operand::LessThanEqual => {
if self.values.len() >= 2 {
result = self.values[0]
.get(existing)
.lesser_equal(&self.values[1].get(existing));
}
}
}
result
}
}
#[cfg(test)]
mod calculations {
use super::*;
use rand::random;
#[test]
fn errors_on_different_field_types() {
let mut calc = Calculation::new(Operand::Equal);
calc.add_value(Uuid::nil()).unwrap();
match calc.add_value("other") {
Ok(_) => unreachable!("should have errored with wrong type"),
Err(err) => match err {
MTTError::DocumentFieldWrongDataType(expected, got) => {
assert_eq!(expected, FieldType::Uuid);
assert_eq!(got, FieldType::StaticString);
}
_ => unreachable!("got {:?}, expected wrong field type", err),
},
}
}
#[test]
fn returns_reference_to_operand() {
let calc = Calculation::new(Operand::Add);
match calc.operation() {
Operand::Add => {}
_ => unreachable!("got {:?}, shold have gotten assign", calc.operation()),
}
let calc = Calculation::new(Operand::Equal);
match calc.operation() {
Operand::Equal => {}
_ => unreachable!("got {:?}, shold have gotten assign", calc.operation()),
}
}
#[test]
fn can_equal_true() {
let mut calc = Calculation::new(Operand::Equal);
let data: Field = Uuid::new_v4().into();
calc.add_value(data.clone()).unwrap();
calc.add_value(data.clone()).unwrap();
let expected: Field = true.into();
let result = calc.calculate(&Field::None);
assert_eq!(result, expected);
}
#[test]
fn can_equal_false() {
let mut calc = Calculation::new(Operand::Equal);
let value1: Field = "fred".into();
let value2: Field = "barney".into();
calc.add_value(value1).unwrap();
calc.add_value(value2).unwrap();
let expected: Field = false.into();
let result = calc.calculate(&Field::None);
assert_eq!(result, expected);
}
#[test]
fn can_greater_than() {
let data: Vec<(Field, Field)> = vec![
(0.into(), false.into()),
(1.into(), false.into()),
(2.into(), true.into()),
];
for (item, expected) in data.iter() {
let mut calc = Calculation::new(Operand::GreaterThan);
calc.add_value(item.clone()).unwrap();
calc.add_value(data[1].0.clone()).unwrap();
let result = calc.calculate(&Field::None);
assert_eq!(&result, expected);
}
}
#[test]
fn can_greater_than_equal() {
let data: Vec<(Field, Field)> = vec![
(0.into(), false.into()),
(1.into(), true.into()),
(2.into(), true.into()),
];
for (item, expected) in data.iter() {
let mut calc = Calculation::new(Operand::GreaterThanEqual);
calc.add_value(item.clone()).unwrap();
calc.add_value(data[1].0.clone()).unwrap();
let result = calc.calculate(&Field::None);
assert_eq!(&result, expected);
}
}
#[test]
fn can_lesser_than() {
let data: Vec<(Field, Field)> = vec![
(0.into(), true.into()),
(1.into(), false.into()),
(2.into(), false.into()),
];
for (item, expected) in data.iter() {
let mut calc = Calculation::new(Operand::LessThan);
calc.add_value(item.clone()).unwrap();
calc.add_value(data[1].0.clone()).unwrap();
let result = calc.calculate(&Field::None);
assert_eq!(&result, expected);
}
}
#[test]
fn can_lesser_than_equal() {
let data: Vec<(Field, Field)> = vec![
(0.into(), true.into()),
(1.into(), true.into()),
(2.into(), false.into()),
];
for (item, expected) in data.iter() {
let mut calc = Calculation::new(Operand::LessThanEqual);
calc.add_value(item.clone()).unwrap();
calc.add_value(data[1].0.clone()).unwrap();
let result = calc.calculate(&Field::None);
assert_eq!(&result, expected);
}
}
#[test]
fn can_add_numbers() {
let mut calc = Calculation::new(Operand::Add);
let value1: i128 = random::<u8>().into();
let value2: i128 = random::<u8>().into();
let expected: Field = { value1 + value2 }.into();
let value1: Field = value1.into();
let value2: Field = value2.into();
calc.add_value(value1.clone()).unwrap();
calc.add_value(value2.clone()).unwrap();
let result = calc.calculate(&Field::None);
assert_eq!(
result, expected,
"{:?} plus {:?} should equal {:?}",
value1, value2, expected
);
}
#[test]
fn can_use_existing_values() {
let mut calc = Calculation::new(Operand::Add);
let value1: i128 = random::<u8>().into();
let value2: i128 = random::<u8>().into();
let expected: Field = { value1 + value2 }.into();
let value1: Field = value1.into();
let value2: Field = value2.into();
calc.add_value(value1.clone()).unwrap();
calc.add_value(CalcValue::Existing(FieldType::Integer))
.unwrap();
let result = calc.calculate(&value2);
assert_eq!(
result, expected,
"{:?} plus {:?} should equal {:?}",
value1, value2, expected
);
}
#[test]
fn returns_error_on_mismatch() {
let mut calc = Calculation::new(Operand::Add);
calc.add_value(Uuid::nil()).unwrap();
match calc.add_value("mismatch") {
Ok(_) => unreachable!("should have returned an error"),
Err(err) => match err {
MTTError::DocumentFieldWrongDataType(expected, got) => {
assert_eq!(got, FieldType::StaticString);
assert_eq!(expected, FieldType::Uuid);
}
_ => unreachable!("got {:?}, expected wrong field type", err),
},
}
}
#[test]
fn datetime_accepts_duration() {
let mut calc = Calculation::new(Operand::Add);
let duration = Duration::from_secs(3600);
let start = Utc::now() + duration;
calc.add_value(FieldType::DateTime).unwrap();
match calc.add_value(duration.clone()) {
Ok(_) => {}
Err(err) => unreachable!("got {:?}, should have returned normally", err),
}
let result = calc.calculate(&Field::None);
let stop = Utc::now() + duration;
match result {
Field::DateTime(data) => {
assert!(data > start);
assert!(data < stop);
}
_ => unreachable!("got {:?}, should have been datetime", result),
}
}
}
#[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)
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
enum QueryType {
Query(Query),
Oids(HashSet<Oid>),
}
impl From<Query> for QueryType {
fn from(value: Query) -> Self {
QueryType::Query(value)
}
}
impl From<HashSet<Oid>> for QueryType {
fn from(value: HashSet<Oid>) -> Self {
QueryType::Oids(value)
}
}
#[derive(Clone, Debug)]
pub struct Query {
data: HashMap<NameType, Calculation>,
}
impl Query {
pub fn new() -> Self {
Self {
data: HashMap::new(),
}
}
pub fn add<NT>(&mut self, name: NT, operation: Calculation)
where
NT: Into<NameType>,
{
self.data.insert(name.into(), operation);
}
#[allow(dead_code)]
fn get<NT>(&self, name: NT) -> Option<Calculation>
where
NT: Into<NameType>,
{
match self.data.get(&name.into()) {
Some(calc) => Some(calc.clone()),
None => None,
}
}
#[allow(dead_code)]
fn field_ids(&self) -> HashSet<&NameType> {
self.data.keys().collect()
}
pub fn iter(&self) -> impl Iterator<Item = (&NameType, &Calculation)> {
self.data.iter()
}
}
#[cfg(test)]
mod queries {
use super::*;
use crate::name::Name;
#[test]
fn holds_calculation_to_run_query() {
let name = Name::english(Uuid::new_v4().to_string().as_str());
let data = Uuid::new_v4();
let mut bad_data = data.clone();
while bad_data == data {
bad_data = Uuid::new_v4();
}
let mut query = Query::new();
let mut calc = Calculation::new(Operand::Equal);
calc.add_value(data.clone()).unwrap();
query.add(name.clone(), calc);
match query.get(&name) {
Some(op) => {
let expected: Field = true.into();
let mut holder = op.clone();
holder.add_value(data).unwrap();
assert_eq!(holder.calculate(&Field::None), expected);
}
None => unreachable!("should have returned a calculation"),
}
match query.get(&name) {
Some(op) => {
let expected: Field = false.into();
let mut holder = op.clone();
holder.add_value(bad_data).unwrap();
assert_eq!(holder.calculate(&Field::None), expected);
}
None => unreachable!("should have returned a calculation"),
}
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct Reply {
data: Vec<Document>,
}
#[allow(dead_code)]
impl Reply {
pub fn new() -> Self {
Self { data: Vec::new() }
}
fn add(&mut self, doc: Document) {
self.data.push(doc);
}
pub fn len(&self) -> usize {
self.data.len()
}
fn iter(&self) -> impl Iterator<Item = &Document> {
self.data.iter()
}
}
#[cfg(test)]
mod replies {
use super::*;
use crate::name::Name;
#[test]
fn is_new_empty() {
let reply = Reply::new();
assert_eq!(reply.len(), 0, "should have no records");
}
#[test]
fn can_add_documents() {
let mut reply = Reply::new();
let doc = Document::new();
reply.add(doc.clone());
assert_eq!(reply.len(), 1);
reply.add(doc.clone());
assert_eq!(reply.len(), 2);
}
#[test]
fn can_retrieve_documents() {
let fieldname = Name::english("field");
let mut doc1 = Document::new();
doc1.add_field(fieldname.clone(), "one");
let mut doc2 = Document::new();
doc2.add_field(fieldname.clone(), "two");
let mut reply = Reply::new();
reply.add(doc1);
reply.add(doc2);
let mut reply_iter = reply.iter();
let result1 = reply_iter.next().unwrap();
match result1.get_field(&fieldname) {
CalcValue::Value(data) => match data {
Field::StaticString(output) => assert_eq!(output, "one"),
_ => unreachable!("got {:?}: should have been static string", result1),
},
_ => unreachable!("got {:?}, should have been value", result1),
}
let result2 = reply_iter.next().unwrap();
match result2.get_field(&fieldname) {
CalcValue::Value(data) => match data {
Field::StaticString(output) => assert_eq!(output, "two"),
_ => unreachable!("got {:?}: should have been static string", result2),
},
_ => unreachable!("got {:?}, should have been value", result2),
}
match reply_iter.next() {
None => {}
Some(_) => unreachable!("should be out of data"),
}
}
}
#[derive(Clone, Debug)]
pub struct InternalRecord {
data: HashMap<Uuid, Field>,
}
impl InternalRecord {
pub fn new() -> Self {
Self {
data: HashMap::new(),
}
}
pub fn insert<F>(&mut self, id: Uuid, data: F) -> Field
where
F: Into<Field>,
{
match self.data.insert(id, data.into()) {
Some(data) => data.clone(),
None => Field::None,
}
}
pub fn get(&self, id: &Uuid) -> Option<&Field> {
self.data.get(id)
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
#[derive(Clone, Debug)]
pub struct InternalRecords {
data: HashMap<Oid, InternalRecord>,
}
impl InternalRecords {
pub fn new() -> Self {
Self {
data: HashMap::new(),
}
}
pub fn insert(&mut self, oid: Oid, record: InternalRecord) -> Option<InternalRecord> {
self.data.insert(oid, record)
}
pub fn get(&self, oid: &Oid) -> Option<&InternalRecord> {
self.data.get(oid)
}
pub fn remove(&mut self, oid: &Oid) -> Option<InternalRecord> {
self.data.remove(oid)
}
pub fn iter(&self) -> impl Iterator<Item = (&Oid, &InternalRecord)> {
self.data.iter()
}
pub fn keys(&self) -> impl Iterator<Item = &Oid> {
self.data.keys()
}
fn values(&self) -> impl Iterator<Item = &InternalRecord> {
self.data.values()
}
pub fn contains_key(&self, oid: &Oid) -> bool {
self.data.contains_key(oid)
}
fn len(&self) -> usize {
self.data.len()
}
}
#[derive(Clone, Debug)]
pub struct Record {
names: Names,
data: InternalRecord,
}
impl Record {
fn with_data(names: Names, rec: InternalRecord) -> Self {
Self {
names: names,
data: rec,
}
}
pub fn get<NT>(&self, field_id: NT) -> Result<Field, MTTError>
where
NT: Into<NameType>,
{
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)]
pub struct Records {
names: Names,
data: InternalRecords,
}
impl Records {
pub fn new(names: Names) -> Self {
Self {
names: names,
data: InternalRecords::new(),
}
}
pub fn with_data(names: Names, records: InternalRecords) -> Self {
Self {
names: names,
data: records,
}
}
pub fn insert(&mut self, oid: Oid, record: InternalRecord) -> Option<InternalRecord> {
self.data.insert(oid, record)
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn iter(&self) -> impl Iterator<Item = Record> {
RecordIter::new(self)
}
pub fn get_internal_records(&self) -> &InternalRecords {
&self.data
}
}
struct RecordIter {
names: Names,
recs: Vec<InternalRecord>,
}
impl RecordIter {
fn new(records: &Records) -> Self {
Self {
names: records.names.clone(),
recs: records.data.values().cloned().collect(),
}
}
}
impl Iterator for RecordIter {
type Item = Record;
fn next(&mut self) -> Option<Self::Item> {
match self.recs.pop() {
Some(rec) => Some(Record::with_data(self.names.clone(), rec.clone())),
None => None,
}
}
}
#[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),
}
}
}
#[derive(Clone, Debug)]
pub struct Delete {
query: Query,
}
impl Delete {
pub fn new(query: Query) -> Self {
Self {
query: query.into(),
}
}
pub fn get_query(&self) -> &Query {
&self.query
}
}
#[derive(Clone, Debug)]
pub struct Update {
query: Query,
values: Document,
}
impl Update {
pub fn new(query: Query) -> Self {
Self {
query: query.into(),
values: Document::new(),
}
}
pub fn get_query(&self) -> &Query {
&self.query
}
pub fn get_values(&self) -> &Document {
&self.values
}
pub fn get_values_mut(&mut self) -> &mut Document {
&mut self.values
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Oid {
oid: Uuid,
}
impl Oid {
pub fn new() -> Self {
Self {
oid: Uuid::new_v4(),
}
}
}
#[cfg(test)]
mod oids {
use super::*;
#[test]
fn are_oids_random() {
let count = 10;
let mut holder: Vec<Oid> = Vec::new();
while holder.len() < count {
let result = Oid::new();
assert!(!holder.contains(&result));
holder.push(result);
}
}
}