use crate::error::SimulationError;
use positive::Positive;
use serde::{Serialize, Serializer};
use std::convert::TryInto;
use std::fmt::{Display, Formatter};
#[derive(Debug, Clone)]
pub struct Ystep<T>
where
T: TryInto<Positive> + Display + Clone,
{
index: i32,
value: T,
}
impl<T> Ystep<T>
where
T: TryInto<Positive> + Display + Clone,
{
pub fn new(index: i32, value: T) -> Self {
Self { index, value }
}
pub fn next(&self, value: T) -> Self {
let index = self.index + 1;
Self { index, value }
}
pub fn value(&self) -> &T {
&self.value
}
pub fn positive(&self) -> Result<Positive, SimulationError> {
self.value
.clone()
.try_into()
.map_err(|_| SimulationError::step_error("Failed to convert value to Positive"))
}
pub fn index(&self) -> &i32 {
&self.index
}
pub fn value_mut(&mut self) -> &mut T {
&mut self.value
}
}
impl<T> Display for Ystep<T>
where
T: TryInto<Positive> + Display + Clone,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let positive_value: Positive = self.positive().unwrap_or(Positive::ZERO);
write!(
f,
"Ystep {{ index: {}, value: {} }}",
self.index,
positive_value.round_to(3)
)
}
}
impl<T> Serialize for Ystep<T>
where
T: TryInto<Positive> + Display + Serialize + Clone,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let value: Positive = self.positive().unwrap_or(Positive::ZERO);
use serde::ser::SerializeStruct;
let mut state = serializer.serialize_struct("Ystep", 2)?;
state.serialize_field("index", &self.index)?;
state.serialize_field("value", &value)?;
state.end()
}
}
impl<T> TryFrom<Ystep<T>> for Positive
where
T: TryInto<Positive> + Display + Clone,
{
type Error = SimulationError;
fn try_from(step: Ystep<T>) -> Result<Self, Self::Error> {
step.positive()
}
}
#[cfg(test)]
mod tests_ystep {
use super::*;
#[test]
fn test_ystep_new() {
let value = 42.5;
let step = Ystep::new(0, value);
assert_eq!(step.index, 0);
assert_eq!(step.value, 42.5);
}
}
#[cfg(test)]
mod tests_serialize {
use super::*;
use positive::pos_or_panic;
use rust_decimal_macros::dec;
use serde_json::{Value, json};
#[test]
fn test_basic_serialization() {
let step = Ystep::new(5, 10.5f64);
let serialized = serde_json::to_string(&step).unwrap();
let parsed: Value = serde_json::from_str(&serialized).unwrap();
assert_eq!(parsed["index"], 5);
assert_eq!(parsed["value"], 10.5);
}
#[test]
fn test_serialized_structure() {
let step = Ystep::new(42, 15.75f64);
let serialized = serde_json::to_string(&step).unwrap();
let parsed: Value = serde_json::from_str(&serialized).unwrap();
assert!(parsed.is_object());
assert_eq!(parsed.as_object().unwrap().len(), 2);
assert!(parsed.get("index").unwrap().is_i64());
assert!(parsed.get("value").unwrap().is_number());
assert_eq!(parsed["index"], json!(42));
assert_eq!(parsed["value"], json!(15.75));
}
#[test]
fn test_type_conversion() {
let step_f64 = Ystep::new(1, 2.5f64);
let step_decimal = Ystep::new(1, dec!(2.5));
let step_positive = Ystep::new(1, pos_or_panic!(2.5));
let json_f64 = serde_json::to_string(&step_f64).unwrap();
let json_decimal = serde_json::to_string(&step_decimal).unwrap();
let json_positive = serde_json::to_string(&step_positive).unwrap();
let parsed_f64: Value = serde_json::from_str(&json_f64).unwrap();
let parsed_decimal: Value = serde_json::from_str(&json_decimal).unwrap();
let parsed_positive: Value = serde_json::from_str(&json_positive).unwrap();
assert_eq!(parsed_f64["value"], json!(2.5));
assert_eq!(parsed_decimal["value"], json!(2.5));
assert_eq!(parsed_positive["value"], json!(2.5));
}
#[test]
fn test_json_format_identity() {
let step_f64 = Ystep::new(3, 4.01f64);
let step_decimal = Ystep::new(3, dec!(4.01));
let step_positive = Ystep::new(3, pos_or_panic!(4.01));
let json_f64 = serde_json::to_string(&step_f64).unwrap();
let json_decimal = serde_json::to_string(&step_decimal).unwrap();
let json_positive = serde_json::to_string(&step_positive).unwrap();
assert_eq!(json_f64, json_decimal);
assert_eq!(json_decimal, json_positive);
}
#[test]
fn test_edge_cases() {
let step_zero = Ystep::new(0, 0.1f64);
let json_zero = serde_json::to_string(&step_zero).unwrap();
let parsed_zero: Value = serde_json::from_str(&json_zero).unwrap();
assert_eq!(parsed_zero["value"], json!(0.1));
let step_small = Ystep::new(1, 0.000001f64);
let json_small = serde_json::to_string(&step_small).unwrap();
let parsed_small: Value = serde_json::from_str(&json_small).unwrap();
assert!(parsed_small["value"].as_f64().unwrap() > 0.0);
assert!(parsed_small["value"].as_f64().unwrap() < 0.0001);
let step_large = Ystep::new(2, 1_000_000.01f64);
let json_large = serde_json::to_string(&step_large).unwrap();
let parsed_large: Value = serde_json::from_str(&json_large).unwrap();
assert_eq!(parsed_large["value"], json!(1_000_000.01));
}
#[test]
fn test_decimal_precision() {
let step = Ystep::new(1, 1.23456789f64);
let serialized = serde_json::to_string(&step).unwrap();
let parsed: Value = serde_json::from_str(&serialized).unwrap();
let value = parsed["value"].as_f64().unwrap();
assert!((value - 1.23456789).abs() < 0.0000001);
}
#[test]
fn test_next_serialization() {
let step = Ystep::new(1, 5.0f64);
let next_step = step.next(10.0f64);
let serialized = serde_json::to_string(&next_step).unwrap();
let parsed: Value = serde_json::from_str(&serialized).unwrap();
assert_eq!(parsed["index"], 2);
assert_eq!(parsed["value"], 10.0);
}
#[test]
fn test_pretty_serialization() {
let step = Ystep::new(7, 15.25f64);
let serialized = serde_json::to_string_pretty(&step).unwrap();
assert!(serialized.contains("\n"));
assert!(serialized.contains(" "));
let parsed: Value = serde_json::from_str(&serialized).unwrap();
assert_eq!(parsed["index"], 7);
assert_eq!(parsed["value"], 15.25);
}
}