From 85e12e20d4e0595c0f0db12bb41cd1d868155646 Mon Sep 17 00:00:00 2001 From: Jeff Baskin Date: Thu, 18 Sep 2025 13:30:20 -0400 Subject: [PATCH] Started to add the functionality to allow default functions. --- Cargo.lock | 61 +++++++- Cargo.toml | 1 + src/message.rs | 370 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 431 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index a92590c..e01ad0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -577,6 +577,7 @@ dependencies = [ "clap", "http-body-util", "isolang", + "rand", "serde_json", "tokio", "tower", @@ -685,6 +686,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro2" version = "1.0.95" @@ -709,6 +719,35 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom", +] + [[package]] name = "redox_syscall" version = "0.5.13" @@ -1239,3 +1278,23 @@ checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags", ] + +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index ea3d47f..9c557b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ axum = ">=0.8.0" chrono = { version = ">=0.4.40", features = ["now"] } clap = { version = ">=4.5.1", features = ["derive"] } isolang = ">=2.4.0" +rand = ">=0.9.2" serde_json = ">=1.0.140" tokio = { version = ">=1.36.0", features = ["full"] } tower-cookies = ">=0.11.0" diff --git a/src/message.rs b/src/message.rs index 476e3c2..df2d087 100644 --- a/src/message.rs +++ b/src/message.rs @@ -6,6 +6,7 @@ use std::{ Arc, RwLock, }, thread::spawn, + time::Duration, }; use uuid::Uuid; @@ -951,7 +952,10 @@ impl CreateDoc { #[derive(Clone, Debug, PartialEq)] enum FieldType { + Boolean, DateTime, + Duration, + Integer, None, StaticString, Uuid, @@ -960,7 +964,10 @@ enum FieldType { impl FieldType { fn get_default(&self) -> Field { match self { + FieldType::Boolean => false.into(), FieldType::DateTime => Utc::now().into(), + FieldType::Duration => Duration::from_secs(0).into(), + FieldType::Integer => 0.into(), FieldType::None => Field::None, FieldType::StaticString => "".into(), FieldType::Uuid => Uuid::new_v4().into(), @@ -971,7 +978,10 @@ impl FieldType { impl From<&Field> for FieldType { fn from(value: &Field) -> Self { match value { + Field::Boolean(_) => Self::Boolean, Field::DateTime(_) => Self::DateTime, + Field::Duration(_) => Self::Duration, + Field::Integer(_) => Self::Integer, Field::None => Self::None, Field::StaticString(_) => Self::StaticString, Field::Uuid(_) => Self::Uuid, @@ -1017,7 +1027,10 @@ mod fieldtypes { #[derive(Clone, Debug, Eq, Hash, PartialEq)] enum Field { + Boolean(bool), DateTime(DateTime), + Duration(Duration), + Integer(u128), None, StaticString(String), Uuid(Uuid), @@ -1029,12 +1042,24 @@ impl Field { } } +impl From for Field { + fn from(value: bool) -> Self { + Self::Boolean(value) + } +} + impl From> for Field { fn from(value: DateTime) -> Self { Self::DateTime(value) } } +impl From for Field { + fn from(value: Duration) -> Self { + Self::Duration(value) + } +} + impl From for Field { fn from(value: String) -> Self { Self::StaticString(value) @@ -1053,6 +1078,12 @@ impl From for Field { } } +impl From for Field { + fn from(value: u128) -> Self { + Self::Integer(value) + } +} + #[cfg(test)] mod fields { use super::*; @@ -1091,6 +1122,17 @@ mod fields { } assert_eq!(result.get_type(), FieldType::Uuid); } + + #[test] + fn create_from_datatime() { + let data = Utc::now(); + let result: Field = data.into(); + match result.clone() { + Field::DateTime(output) => assert_eq!(output, data), + _ => unreachable!("got {:?}: should have been uuid", result), + } + assert_eq!(result.get_type(), FieldType::DateTime); + } } #[derive(Clone, Debug)] @@ -1212,6 +1254,31 @@ mod fieldsettings { Err(err) => unreachable!("got {:?}: should have gotten a value", err), } } + + //#[test] + fn can_default_be_calculated() { + let mut fset = FieldSetting::new(FieldType::DateTime); + fset.set_default(None); + let duration = Duration::from_secs(3600); + let start = Utc::now() + duration; + let result = match fset.validate(None).unwrap() { + Field::DateTime(data) => data, + _ => unreachable!("should return datetime"), + }; + let stop = Utc::now() + duration; + assert!( + result > start, + "{:?} should have been greater than {:?}", + result, + start + ); + assert!( + result > stop, + "{:?} should have been less than {:?}", + result, + stop + ); + } } #[derive(Clone, Debug)] @@ -1476,6 +1543,8 @@ mod docdefs { #[derive(Clone, Debug)] enum Operand { + Add, + Assign, Equal, } @@ -1483,6 +1552,7 @@ impl Operand { fn validate(&self, x: &Field, y: &Field) -> bool { match self { Self::Equal => x == y, + _ => false, } } } @@ -1508,6 +1578,306 @@ mod operands { } } +#[derive(Clone, Debug)] +enum CalcValue { + FType(FieldType), + Value(Field), +} + +impl CalcValue { + fn get(&self) -> Field { + match self { + Self::FType(ftype) => ftype.get_default(), + Self::Value(field) => field.clone(), + } + } +} + +impl From for CalcValue { + fn from(value: Field) -> Self { + Self::Value(value) + } +} + +impl From for CalcValue { + fn from(value: FieldType) -> Self { + Self::FType(value) + } +} + +impl From for CalcValue { + fn from(value: bool) -> Self { + let output: Field = value.into(); + Self::from(output).into() + } +} + +impl From> for CalcValue { + fn from(value: DateTime) -> Self { + let output: Field = value.into(); + Self::from(output).into() + } +} + +impl From for CalcValue { + fn from(value: Duration) -> Self { + let output: Field = value.into(); + Self::from(output).into() + } +} + +impl From for CalcValue { + fn from(value: u128) -> 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 for CalcValue { + fn from(value: String) -> Self { + let output: Field = value.into(); + Self::from(output).into() + } +} + +impl From 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 { + CalcValue::FType(_) => unreachable!("got {:?}, should have gotten a field", result), + CalcValue::Value(data) => assert_eq!(data, expected), + } + } + + #[test] + fn from_str() { + let value = "something"; + let expected: Field = value.into(); + let result: CalcValue = value.into(); + match result { + CalcValue::FType(_) => unreachable!("got {:?}, should have gotten a field", result), + CalcValue::Value(data) => assert_eq!(data, expected), + } + } + + #[test] + fn from_string() { + let value = "data".to_string(); + let expected: Field = value.clone().into(); + let result: CalcValue = value.into(); + match result { + CalcValue::FType(_) => unreachable!("got {:?}, should have gotten a field", result), + CalcValue::Value(data) => assert_eq!(data, expected), + } + } + + #[test] + fn from_boolean() { + let value = true; + let expected: Field = value.clone().into(); + let result: CalcValue = value.into(); + match result { + CalcValue::FType(_) => unreachable!("got {:?}, should have gotten a field", result), + CalcValue::Value(data) => assert_eq!(data, expected), + } + } + + #[test] + fn from_datetime() { + let value = Utc::now(); + let expected: Field = value.clone().into(); + let result: CalcValue = value.into(); + match result { + CalcValue::FType(_) => unreachable!("got {:?}, should have gotten a field", result), + CalcValue::Value(data) => assert_eq!(data, expected), + } + } + + #[test] + fn from_duration() { + let value = Duration::from_secs(5); + let expected: Field = value.clone().into(); + let result: CalcValue = value.into(); + match result { + CalcValue::FType(_) => unreachable!("got {:?}, should have gotten a field", result), + CalcValue::Value(data) => assert_eq!(data, expected), + } + } + + #[test] + fn from_integer() { + let value: u128 = 5; + let expected: Field = value.clone().into(); + let result: CalcValue = value.into(); + match result { + CalcValue::FType(_) => unreachable!("got {:?}, should have gotten a field", result), + CalcValue::Value(data) => assert_eq!(data, expected), + } + } +} + +#[derive(Clone, Debug)] +struct Calculation { + operation: Operand, + values: Vec, +} + +impl Calculation { + fn new(operand: Operand) -> Self { + Self { + operation: operand, + values: Vec::new(), + } + } + + fn get_fields(&self) -> Vec { + let mut output = Vec::new(); + for item in self.values.iter() { + output.push(item.get()); + } + output + } + + fn add_value(&mut self, data: CV) -> Result<(), MTTError> + where + CV: Into, + { + let holder: CalcValue = data.into(); + if self.values.len() == 0 { + self.values.push(holder); + } else { + match self.operation { + Operand::Add => { + if self.values[0].get().get_type() == holder.get().get_type() { + self.values.push(holder); + } else { + return Err(MTTError::DocumentFieldWrongDataType( + FieldType::None, + FieldType::None, + )); + } + } + _ => self.values.push(holder), + } + } + Ok(()) + } + + fn calculate(&self) -> Field { + match self.operation { + Operand::Add => { + let values = self.get_fields(); + match values[0].get_type() { + FieldType::Integer => { + let mut output: u128 = 0; + for item in values.iter() { + match item { + Field::Integer(data) => output += data, + _ => unreachable! {"got {:?} expected Integer", item}, + } + } + output.into() + } + _ => unreachable!("{:?} does not handle addition", values[0].get_type()), + } + } + Operand::Assign => self.values[0].get(), + Operand::Equal => { self.values[0].get() == self.values[1].get() }.into(), + } + } +} + +#[cfg(test)] +mod calculations { + use super::*; + use rand::random; + + #[test] + fn can_assign_value() { + let mut calc = Calculation::new(Operand::Assign); + let data: Field = Uuid::new_v4().into(); + calc.add_value(data.clone()); + let result = calc.calculate(); + assert_eq!(result, data); + } + + #[test] + fn can_assign_default_function() { + let mut calc = Calculation::new(Operand::Assign); + calc.add_value(FieldType::Uuid); + let result1 = calc.calculate(); + let result2 = calc.calculate(); + assert_ne!(result1, result2); + } + + #[test] + fn can_equal_true() { + let mut calc = Calculation::new(Operand::Equal); + let data: Field = Uuid::new_v4().into(); + calc.add_value(data.clone()); + calc.add_value(data.clone()); + let expected: Field = true.into(); + let result = calc.calculate(); + 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); + calc.add_value(value2); + let expected: Field = false.into(); + let result = calc.calculate(); + assert_eq!(result, expected); + } + + #[test] + fn can_add_numbers() { + let mut calc = Calculation::new(Operand::Add); + let value1: u128 = random::().into(); + let value2: u128 = random::().into(); + let expected: Field = { value1 + value2 }.into(); + let value1: Field = value1.into(); + let value2: Field = value2.into(); + calc.add_value(value1); + calc.add_value(value2); + let result = calc.calculate(); + assert_eq!(result, expected); + } + + #[test] + fn returns_error_on_mismatch() { + let mut calc = Calculation::new(Operand::Add); + calc.add_value(Uuid::nil()); + match calc.add_value("mismatch") { + Ok(_) => unreachable!("should have returned an error"), + Err(_) => {} + } + } +} + #[derive(Clone, Debug)] struct Operation { field_name: String,