1pub use mockforge_core::{Error, Result};
35
36use std::collections::HashMap;
37use std::sync::Arc;
38
39pub mod config;
41pub mod database;
42pub mod entities;
43pub mod schema;
44
45pub mod constraints;
47pub mod migration;
48
49pub mod api_generator;
51pub mod handlers;
52
53pub mod auth;
55pub mod session;
56
57pub mod aging;
59pub mod mutation_rules;
60pub mod scheduler;
61
62pub mod integration;
64
65pub mod openapi;
67
68pub mod seeding;
70
71pub mod id_generation;
73
74pub mod snapshots;
76
77pub use config::{StorageBackend, VbrConfig};
79pub use database::VirtualDatabase;
80pub use entities::{Entity, EntityRegistry};
81pub use mutation_rules::{
82 ComparisonOperator, MutationOperation, MutationRule, MutationRuleManager, MutationTrigger,
83};
84pub use schema::{ManyToManyDefinition, VbrSchemaDefinition};
85pub use snapshots::{SnapshotMetadata, TimeTravelSnapshotState};
86
87pub struct VbrEngine {
89 config: VbrConfig,
91 database: Arc<dyn VirtualDatabase + Send + Sync>,
93 registry: EntityRegistry,
95}
96
97impl VbrEngine {
98 pub async fn new(config: VbrConfig) -> Result<Self> {
100 let database = database::create_database(&config.storage).await?;
102
103 let registry = EntityRegistry::new();
105
106 Ok(Self {
107 config,
108 database,
109 registry,
110 })
111 }
112
113 pub fn config(&self) -> &VbrConfig {
115 &self.config
116 }
117
118 pub fn database_arc(&self) -> Arc<dyn VirtualDatabase + Send + Sync> {
120 Arc::clone(&self.database)
121 }
122
123 pub fn database(&self) -> &dyn VirtualDatabase {
125 self.database.as_ref()
126 }
127
128 pub fn registry(&self) -> &EntityRegistry {
130 &self.registry
131 }
132
133 pub fn registry_mut(&mut self) -> &mut EntityRegistry {
135 &mut self.registry
136 }
137
138 pub async fn from_openapi(
154 config: VbrConfig,
155 openapi_content: &str,
156 ) -> Result<(Self, openapi::OpenApiConversionResult)> {
157 let json_value: serde_json::Value = if openapi_content.trim_start().starts_with('{') {
159 serde_json::from_str(openapi_content)
160 .map_err(|e| Error::generic(format!("Failed to parse OpenAPI JSON: {}", e)))?
161 } else {
162 serde_yaml::from_str(openapi_content)
163 .map_err(|e| Error::generic(format!("Failed to parse OpenAPI YAML: {}", e)))?
164 };
165
166 let spec = mockforge_core::openapi::OpenApiSpec::from_json(json_value)
168 .map_err(|e| Error::generic(format!("Failed to load OpenAPI spec: {}", e)))?;
169
170 spec.validate()
172 .map_err(|e| Error::generic(format!("Invalid OpenAPI specification: {}", e)))?;
173
174 let conversion_result = openapi::convert_openapi_to_vbr(&spec)?;
176
177 let mut engine = Self::new(config).await?;
179
180 for (entity_name, vbr_schema) in &conversion_result.entities {
182 let entity = entities::Entity::new(entity_name.clone(), vbr_schema.clone());
183 engine.registry_mut().register(entity.clone())?;
184
185 migration::create_table_for_entity(engine.database.as_ref(), &entity).await?;
187 }
188
189 Ok((engine, conversion_result))
190 }
191
192 pub async fn from_openapi_file<P: AsRef<std::path::Path>>(
201 config: VbrConfig,
202 file_path: P,
203 ) -> Result<(Self, openapi::OpenApiConversionResult)> {
204 let content = tokio::fs::read_to_string(file_path.as_ref())
205 .await
206 .map_err(|e| Error::generic(format!("Failed to read OpenAPI file: {}", e)))?;
207
208 Self::from_openapi(config, &content).await
209 }
210
211 pub async fn seed_entity(
217 &self,
218 entity_name: &str,
219 records: &[HashMap<String, serde_json::Value>],
220 ) -> Result<usize> {
221 seeding::seed_entity(self.database.as_ref(), &self.registry, entity_name, records).await
222 }
223
224 pub async fn seed_all(&self, seed_data: &seeding::SeedData) -> Result<HashMap<String, usize>> {
229 seeding::seed_all(self.database.as_ref(), &self.registry, seed_data).await
230 }
231
232 pub async fn seed_from_file<P: AsRef<std::path::Path>>(
237 &self,
238 file_path: P,
239 ) -> Result<HashMap<String, usize>> {
240 let seed_data = seeding::load_seed_file(file_path).await?;
241 self.seed_all(&seed_data).await
242 }
243
244 pub async fn clear_entity(&self, entity_name: &str) -> Result<()> {
249 seeding::clear_entity(self.database.as_ref(), &self.registry, entity_name).await
250 }
251
252 pub async fn clear_all(&self) -> Result<()> {
254 seeding::clear_all(self.database.as_ref(), &self.registry).await
255 }
256
257 pub async fn create_snapshot<P: AsRef<std::path::Path>>(
264 &self,
265 name: &str,
266 description: Option<String>,
267 snapshots_dir: P,
268 ) -> Result<snapshots::SnapshotMetadata> {
269 let manager = snapshots::SnapshotManager::new(snapshots_dir);
270 manager
271 .create_snapshot(name, description, self.database.as_ref(), &self.registry)
272 .await
273 }
274
275 pub async fn create_snapshot_with_time_travel<P: AsRef<std::path::Path>>(
284 &self,
285 name: &str,
286 description: Option<String>,
287 snapshots_dir: P,
288 include_time_travel: bool,
289 time_travel_state: Option<snapshots::TimeTravelSnapshotState>,
290 ) -> Result<snapshots::SnapshotMetadata> {
291 let manager = snapshots::SnapshotManager::new(snapshots_dir);
292 manager
293 .create_snapshot_with_time_travel(
294 name,
295 description,
296 self.database.as_ref(),
297 &self.registry,
298 include_time_travel,
299 time_travel_state,
300 )
301 .await
302 }
303
304 pub async fn restore_snapshot<P: AsRef<std::path::Path>>(
310 &self,
311 name: &str,
312 snapshots_dir: P,
313 ) -> Result<()> {
314 let manager = snapshots::SnapshotManager::new(snapshots_dir);
315 manager.restore_snapshot(name, self.database.as_ref(), &self.registry).await
316 }
317
318 pub async fn restore_snapshot_with_time_travel<P, F>(
326 &self,
327 name: &str,
328 snapshots_dir: P,
329 restore_time_travel: bool,
330 time_travel_restore_callback: Option<F>,
331 ) -> Result<()>
332 where
333 P: AsRef<std::path::Path>,
334 F: FnOnce(snapshots::TimeTravelSnapshotState) -> Result<()>,
335 {
336 let manager = snapshots::SnapshotManager::new(snapshots_dir);
337 manager
338 .restore_snapshot_with_time_travel(
339 name,
340 self.database.as_ref(),
341 &self.registry,
342 restore_time_travel,
343 time_travel_restore_callback,
344 )
345 .await
346 }
347
348 pub async fn list_snapshots<P: AsRef<std::path::Path>>(
353 snapshots_dir: P,
354 ) -> Result<Vec<snapshots::SnapshotMetadata>> {
355 let manager = snapshots::SnapshotManager::new(snapshots_dir);
356 manager.list_snapshots().await
357 }
358
359 pub async fn delete_snapshot<P: AsRef<std::path::Path>>(
365 name: &str,
366 snapshots_dir: P,
367 ) -> Result<()> {
368 let manager = snapshots::SnapshotManager::new(snapshots_dir);
369 manager.delete_snapshot(name).await
370 }
371
372 pub async fn reset(&self) -> Result<()> {
374 snapshots::reset_database(self.database.as_ref(), &self.registry).await
375 }
376}
377
378#[cfg(test)]
379mod tests {
380 use super::*;
381
382 #[tokio::test]
383 async fn test_vbr_engine_creation() {
384 let config = VbrConfig::default().with_storage_backend(StorageBackend::Memory);
386 let engine = VbrEngine::new(config).await;
387 assert!(engine.is_ok());
388 }
389}