Moved calculation into action.
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 1s

This commit is contained in:
Jeff Baskin 2026-02-08 12:26:19 -05:00
parent aa7d5ae1f6
commit 3f16284682
8 changed files with 352 additions and 223 deletions

View File

@ -10,7 +10,7 @@ pub use crate::document::{
field::{Field, FieldType}, field::{Field, FieldType},
}; };
pub use action_type::Action; pub use action_type::Action;
pub use calculation::CalcValue; pub use calculation::{CalcValue, Calculation};
pub use message::MsgAction; pub use message::MsgAction;
pub use query::Query; pub use query::Query;
use request_data::RequestData; use request_data::RequestData;

View File

@ -1,5 +1,5 @@
use super::{Field, FieldType}; use super::{Field, FieldType};
use crate::message::wrapper::{Calculation, Operand}; use crate::{message::wrapper::Operand, mtterror::MTTError};
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use std::time::Duration; use std::time::Duration;
use uuid::Uuid; use uuid::Uuid;
@ -215,3 +215,343 @@ mod calcvalues {
); );
} }
} }
#[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),
}
}
}

View File

@ -1,5 +1,6 @@
use crate::{message::wrapper::Calculation, name::NameType}; use crate::name::NameType;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use super::Calculation;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Query { pub struct Query {

View File

@ -1,11 +1,11 @@
use crate::{ use crate::{
action::{Action, CalcValue, MsgAction, Query}, action::{Action, CalcValue, Calculation, MsgAction, Query},
document::{ document::{
definition::{DocDef, DocFuncType}, definition::{DocDef, DocFuncType},
field::Field, field::Field,
}, },
message::wrapper::{ message::wrapper::{
Calculation, InternalRecord, InternalRecords, Message, Oid, Records, Reply, Update, InternalRecord, InternalRecords, Message, Oid, Records, Reply, Update,
}, },
mtterror::MTTError, mtterror::MTTError,
name::NameType, name::NameType,

View File

@ -72,7 +72,7 @@ impl FieldSetting {
#[cfg(test)] #[cfg(test)]
mod fieldsettings { mod fieldsettings {
use super::*; use super::*;
use crate::message::wrapper::{Calculation, Operand}; use crate::{action::Calculation, message::wrapper::Operand};
use chrono::Utc; use chrono::Utc;
use std::time::Duration; use std::time::Duration;

View File

@ -1,11 +1,11 @@
use crate::{ use crate::{
action::{Action, CalcValue, FieldType, Query}, action::{Action, CalcValue, Calculation, FieldType, Query},
document::{ document::{
clock::Clock, clock::Clock,
create::IndexType, create::IndexType,
definition::{DocDef, DocFuncType}, definition::{DocDef, DocFuncType},
}, },
message::wrapper::{Calculation, Delete, Message, Operand, Update}, message::wrapper::{Delete, Message, Operand, Update},
name::{Name, NameType}, name::{Name, NameType},
queue::{ queue::{
data_director::{Include, Path, RegMsg, Register}, data_director::{Include, Path, RegMsg, Register},

View File

@ -5,9 +5,9 @@ mod mtterror;
mod name; mod name;
mod queue; mod queue;
use action::{Action, CalcValue, DocDef, Field, FieldType, MsgAction, Query, UserAction}; use action::{Action, CalcValue, Calculation, DocDef, Field, FieldType, MsgAction, Query, UserAction};
use document::{clock::Clock, create::CreateDoc, session::Session}; use document::{clock::Clock, create::CreateDoc, session::Session};
use message::wrapper::{Addition, Calculation, Message, Operand}; use message::wrapper::{Addition, Message, Operand};
pub use mtterror::MTTError; pub use mtterror::MTTError;
pub use name::{Name, NameType}; pub use name::{Name, NameType};
use queue::{ use queue::{

View File

@ -408,219 +408,6 @@ mod operands {
} }
/* /*
#[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)] #[derive(Clone, Debug)]
pub struct Calculation { pub struct Calculation {
operation: Operand, operation: Operand,
@ -960,6 +747,7 @@ mod calculations {
} }
} }
} }
*/
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]