tokenomics_simulator/
engine_builder.rs

1//! # Engine builder module
2//!
3//! This module contains the builder for creating a new simulation.
4//!
5//! The builder allows for configuring the simulation before building it.
6//! The builder is used to ensure that all required fields are provided when creating a new simulation.
7
8use chrono::Utc;
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11use uuid::Uuid;
12
13use crate::{
14    Simulation, SimulationError, SimulationOptions, SimulationReport, SimulationStatus, Token,
15};
16
17/// Builder for creating a new simulation.
18#[derive(Debug, Default, PartialEq)]
19#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
20pub struct SimulationBuilder {
21    /// Name of the simulation.
22    /// Required field.
23    pub name: Option<String>,
24
25    /// Token used to run the simulation.
26    /// Required field.
27    pub token: Option<Token>,
28
29    /// Description of the simulation.
30    /// Optional field.
31    pub description: Option<String>,
32
33    /// Input parameters for the simulation.
34    /// Required field.
35    pub options: Option<SimulationOptions>,
36}
37
38impl SimulationBuilder {
39    /// Create a new simulation builder to configure the simulation.
40    ///
41    /// # Returns
42    ///
43    /// New simulation builder.
44    pub fn new() -> Self {
45        SimulationBuilder::default()
46    }
47
48    /// Set the name of the simulation.
49    ///
50    /// # Arguments
51    ///
52    /// * `name` - Name of the simulation.
53    ///
54    /// # Returns
55    ///
56    /// The simulation builder.
57    pub fn name(mut self, name: String) -> Self {
58        self.name = Some(name);
59        self
60    }
61
62    /// Set the token used to run the simulation.
63    ///
64    /// # Arguments
65    ///
66    /// * `token` - Token used to run the simulation.
67    ///
68    /// # Returns
69    ///
70    /// The simulation builder.
71    pub fn token(mut self, token: Token) -> Self {
72        self.token = Some(token);
73        self
74    }
75
76    /// Set the description of the simulation.
77    ///
78    /// # Arguments
79    ///
80    /// * `description` - Description of the simulation.
81    ///
82    /// # Returns
83    ///
84    /// The simulation builder.
85    pub fn description(mut self, description: String) -> Self {
86        self.description = Some(description);
87        self
88    }
89
90    /// Set the input parameters for the simulation.
91    ///
92    /// # Arguments
93    ///
94    /// * `options` - Input parameters for the simulation.
95    ///
96    /// # Returns
97    ///
98    /// The simulation builder.
99    pub fn options(mut self, options: SimulationOptions) -> Self {
100        self.options = Some(options);
101        self
102    }
103
104    /// Build the simulation.
105    ///
106    /// # Returns
107    ///
108    /// Built simulation or an error if required fields are missing.
109    pub fn build(self) -> Result<Simulation, SimulationError> {
110        Ok(Simulation {
111            id: Uuid::new_v4(),
112            description: self.description,
113            status: SimulationStatus::Pending,
114            name: self.name.ok_or(SimulationError::MissingName)?,
115            token: self.token.ok_or(SimulationError::MissingToken)?,
116            options: self.options.ok_or(SimulationError::MissingOptions)?,
117            interval_reports: vec![],
118            report: SimulationReport::default(),
119            created_at: Utc::now(),
120            updated_at: Utc::now(),
121        })
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use rust_decimal::Decimal;
128
129    use crate::{SimulationInterval, TokenBuilder, ValuationModel};
130
131    use super::*;
132
133    #[test]
134    fn test_new_simulation_builder() {
135        let builder = SimulationBuilder::new();
136
137        assert_eq!(builder.name, None);
138        assert_eq!(builder.token, None);
139        assert_eq!(builder.description, None);
140        assert_eq!(builder.options, None);
141    }
142
143    #[test]
144    fn test_build_simulation() {
145        let token = TokenBuilder::new()
146            .name("Test Token".to_string())
147            .total_supply(1_000_000)
148            .build()
149            .unwrap();
150        let options = SimulationOptions {
151            duration: 30,
152            total_users: 100,
153            decimal_precision: 4,
154            market_volatility: Decimal::new(5, 1),
155            transaction_fee_percentage: None,
156            interval_type: SimulationInterval::Daily,
157            adoption_rate: None,
158            valuation_model: Some(ValuationModel::Exponential(1.0)),
159        };
160
161        let simulation = SimulationBuilder::default()
162            .name("Test Simulation".to_string())
163            .description("Test Simulation".to_string())
164            .token(token.clone())
165            .options(options.clone())
166            .build()
167            .unwrap();
168
169        assert_eq!(simulation.name, "Test Simulation");
170        assert_eq!(simulation.token, token);
171        assert_eq!(simulation.options, options);
172    }
173
174    #[test]
175    fn test_build_simulation_missing_name() {
176        let token = TokenBuilder::new()
177            .name("Test Token".to_string())
178            .total_supply(1_000_000)
179            .build()
180            .unwrap();
181        let options = SimulationOptions {
182            duration: 30,
183            total_users: 100,
184            decimal_precision: 4,
185            market_volatility: Decimal::new(5, 1),
186            transaction_fee_percentage: None,
187            interval_type: SimulationInterval::Daily,
188            adoption_rate: None,
189            valuation_model: Some(ValuationModel::Exponential(1.0)),
190        };
191
192        let simulation = SimulationBuilder::default()
193            .token(token)
194            .options(options)
195            .build();
196
197        assert!(simulation.is_err());
198        assert_eq!(simulation.unwrap_err(), SimulationError::MissingName);
199    }
200
201    #[test]
202    fn test_build_simulation_missing_token() {
203        let options = SimulationOptions {
204            duration: 30,
205            total_users: 100,
206            decimal_precision: 4,
207            market_volatility: Decimal::new(5, 1),
208            transaction_fee_percentage: None,
209            interval_type: SimulationInterval::Daily,
210            adoption_rate: None,
211            valuation_model: Some(ValuationModel::Exponential(1.0)),
212        };
213
214        let simulation = SimulationBuilder::default()
215            .name("Test Simulation".to_string())
216            .options(options)
217            .build();
218
219        assert!(simulation.is_err());
220        assert_eq!(simulation.unwrap_err(), SimulationError::MissingToken);
221    }
222
223    #[test]
224    fn test_build_simulation_missing_options() {
225        let token = TokenBuilder::new()
226            .name("Test Token".to_string())
227            .total_supply(1_000_000)
228            .build()
229            .unwrap();
230
231        let simulation = SimulationBuilder::default()
232            .name("Test Simulation".to_string())
233            .token(token)
234            .build();
235
236        assert!(simulation.is_err());
237        assert_eq!(simulation.unwrap_err(), SimulationError::MissingOptions);
238    }
239}