use chrono::Utc;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::{
Simulation, SimulationError, SimulationOptions, SimulationReport, SimulationStatus, Token,
};
#[derive(Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct SimulationBuilder {
pub name: Option<String>,
pub token: Option<Token>,
pub description: Option<String>,
pub options: Option<SimulationOptions>,
}
impl SimulationBuilder {
pub fn new() -> Self {
SimulationBuilder::default()
}
pub fn name(mut self, name: String) -> Self {
self.name = Some(name);
self
}
pub fn token(mut self, token: Token) -> Self {
self.token = Some(token);
self
}
pub fn description(mut self, description: String) -> Self {
self.description = Some(description);
self
}
pub fn options(mut self, options: SimulationOptions) -> Self {
self.options = Some(options);
self
}
pub fn build(self) -> Result<Simulation, SimulationError> {
Ok(Simulation {
id: Uuid::new_v4(),
description: self.description,
status: SimulationStatus::Pending,
name: self.name.ok_or(SimulationError::MissingName)?,
token: self.token.ok_or(SimulationError::MissingToken)?,
options: self.options.ok_or(SimulationError::MissingOptions)?,
interval_reports: vec![],
report: SimulationReport::default(),
created_at: Utc::now(),
updated_at: Utc::now(),
})
}
}
#[cfg(test)]
mod tests {
use rust_decimal::Decimal;
use crate::{SimulationInterval, TokenBuilder, ValuationModel};
use super::*;
#[test]
fn test_new_simulation_builder() {
let builder = SimulationBuilder::new();
assert_eq!(builder.name, None);
assert_eq!(builder.token, None);
assert_eq!(builder.description, None);
assert_eq!(builder.options, None);
}
#[test]
fn test_build_simulation() {
let token = TokenBuilder::new()
.name("Test Token".to_string())
.total_supply(1_000_000)
.build()
.unwrap();
let options = SimulationOptions {
duration: 30,
total_users: 100,
decimal_precision: 4,
market_volatility: Decimal::new(5, 1),
transaction_fee_percentage: None,
interval_type: SimulationInterval::Daily,
adoption_rate: None,
valuation_model: Some(ValuationModel::Exponential(1.0)),
};
let simulation = SimulationBuilder::default()
.name("Test Simulation".to_string())
.description("Test Simulation".to_string())
.token(token.clone())
.options(options.clone())
.build()
.unwrap();
assert_eq!(simulation.name, "Test Simulation");
assert_eq!(simulation.token, token);
assert_eq!(simulation.options, options);
}
#[test]
fn test_build_simulation_missing_name() {
let token = TokenBuilder::new()
.name("Test Token".to_string())
.total_supply(1_000_000)
.build()
.unwrap();
let options = SimulationOptions {
duration: 30,
total_users: 100,
decimal_precision: 4,
market_volatility: Decimal::new(5, 1),
transaction_fee_percentage: None,
interval_type: SimulationInterval::Daily,
adoption_rate: None,
valuation_model: Some(ValuationModel::Exponential(1.0)),
};
let simulation = SimulationBuilder::default()
.token(token)
.options(options)
.build();
assert!(simulation.is_err());
assert_eq!(simulation.unwrap_err(), SimulationError::MissingName);
}
#[test]
fn test_build_simulation_missing_token() {
let options = SimulationOptions {
duration: 30,
total_users: 100,
decimal_precision: 4,
market_volatility: Decimal::new(5, 1),
transaction_fee_percentage: None,
interval_type: SimulationInterval::Daily,
adoption_rate: None,
valuation_model: Some(ValuationModel::Exponential(1.0)),
};
let simulation = SimulationBuilder::default()
.name("Test Simulation".to_string())
.options(options)
.build();
assert!(simulation.is_err());
assert_eq!(simulation.unwrap_err(), SimulationError::MissingToken);
}
#[test]
fn test_build_simulation_missing_options() {
let token = TokenBuilder::new()
.name("Test Token".to_string())
.total_supply(1_000_000)
.build()
.unwrap();
let simulation = SimulationBuilder::default()
.name("Test Simulation".to_string())
.token(token)
.build();
assert!(simulation.is_err());
assert_eq!(simulation.unwrap_err(), SimulationError::MissingOptions);
}
}