1use crate::entity::Entity;
7use crate::enums::RelationshipSchema;
8use crate::event::Event;
9use crate::simulation::Simulation;
10use crate::types::{EntityId, EventId, RelationshipId, Timestamp};
11use std::fmt;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum SimulationBuildError {
16 DuplicateEntityId(EntityId),
18 EventReferencesUnknownEntity(EventId, EntityId),
21 RelationshipReferencesUnknownEntity(RelationshipId, EntityId),
24 SelfRelationship(EntityId),
26}
27
28impl fmt::Display for SimulationBuildError {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 match self {
31 SimulationBuildError::DuplicateEntityId(id) => {
32 write!(f, "Duplicate entity ID: {}", id.as_str())
33 }
34 SimulationBuildError::EventReferencesUnknownEntity(event_id, entity_id) => {
35 write!(
36 f,
37 "Event '{}' references unknown entity: {}",
38 event_id.as_str(),
39 entity_id.as_str()
40 )
41 }
42 SimulationBuildError::RelationshipReferencesUnknownEntity(rel_id, entity_id) => {
43 write!(
44 f,
45 "Relationship '{}' references unknown entity: {}",
46 rel_id.as_str(),
47 entity_id.as_str()
48 )
49 }
50 SimulationBuildError::SelfRelationship(id) => {
51 write!(
52 f,
53 "Cannot create relationship between entity '{}' and itself",
54 id.as_str()
55 )
56 }
57 }
58 }
59}
60
61impl std::error::Error for SimulationBuildError {}
62
63struct PendingEntity {
65 entity: Entity,
66 anchor_timestamp: Timestamp,
67}
68
69struct PendingEvent {
71 event: Event,
72 timestamp: Timestamp,
73}
74
75struct PendingRelationship {
77 id: RelationshipId,
78 entity_a: EntityId,
79 entity_b: EntityId,
80 schema: RelationshipSchema,
81 formed_timestamp: Timestamp,
82}
83
84static PENDING_REL_COUNTER: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
86
87fn generate_pending_relationship_id() -> RelationshipId {
88 let count = PENDING_REL_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
89 RelationshipId::new(format!("rel_{}", count)).unwrap()
91}
92
93pub struct SimulationBuilder {
99 reference_date: Timestamp,
100 entities: Vec<PendingEntity>,
101 events: Vec<PendingEvent>,
102 relationships: Vec<PendingRelationship>,
103}
104
105impl SimulationBuilder {
106 #[must_use]
114 pub fn new(reference_date: Timestamp) -> Self {
115 SimulationBuilder {
116 reference_date,
117 entities: Vec::new(),
118 events: Vec::new(),
119 relationships: Vec::new(),
120 }
121 }
122
123 #[must_use]
127 pub fn add_entity(mut self, entity: Entity, anchor_timestamp: Timestamp) -> Self {
128 self.entities.push(PendingEntity {
129 entity,
130 anchor_timestamp,
131 });
132 self
133 }
134
135 #[must_use]
137 pub fn add_event(mut self, event: Event, timestamp: Timestamp) -> Self {
138 self.events.push(PendingEvent { event, timestamp });
139 self
140 }
141
142 #[must_use]
144 pub fn add_relationship(
145 mut self,
146 entity_a: EntityId,
147 entity_b: EntityId,
148 schema: RelationshipSchema,
149 formed_timestamp: Timestamp,
150 ) -> Self {
151 self.relationships.push(PendingRelationship {
152 id: generate_pending_relationship_id(),
153 entity_a,
154 entity_b,
155 schema,
156 formed_timestamp,
157 });
158 self
159 }
160
161 pub fn build(self) -> Result<Simulation, SimulationBuildError> {
171 let mut simulation = Simulation::new(self.reference_date);
172
173 let mut seen_ids = std::collections::HashSet::new();
175
176 for pending in &self.entities {
178 let id = pending.entity.id().clone();
179 if !seen_ids.insert(id.clone()) {
180 return Err(SimulationBuildError::DuplicateEntityId(id));
181 }
182 }
183
184 for pending in &self.events {
186 if let Some(source) = pending.event.source() {
188 if !seen_ids.contains(source) {
189 return Err(SimulationBuildError::EventReferencesUnknownEntity(
190 pending.event.id().clone(),
191 source.clone(),
192 ));
193 }
194 }
195 if let Some(target) = pending.event.target() {
197 if !seen_ids.contains(target) {
198 return Err(SimulationBuildError::EventReferencesUnknownEntity(
199 pending.event.id().clone(),
200 target.clone(),
201 ));
202 }
203 }
204 }
205
206 for pending in &self.relationships {
208 if pending.entity_a == pending.entity_b {
210 return Err(SimulationBuildError::SelfRelationship(
211 pending.entity_a.clone(),
212 ));
213 }
214
215 if !seen_ids.contains(&pending.entity_a) {
217 return Err(SimulationBuildError::RelationshipReferencesUnknownEntity(
218 pending.id.clone(),
219 pending.entity_a.clone(),
220 ));
221 }
222
223 if !seen_ids.contains(&pending.entity_b) {
225 return Err(SimulationBuildError::RelationshipReferencesUnknownEntity(
226 pending.id.clone(),
227 pending.entity_b.clone(),
228 ));
229 }
230 }
231
232 for pending in self.entities {
234 simulation.add_entity(pending.entity, pending.anchor_timestamp);
235 }
236
237 for pending in self.events {
238 simulation.add_event(pending.event, pending.timestamp);
239 }
240
241 for pending in self.relationships {
242 simulation.add_relationship(
243 pending.entity_a,
244 pending.entity_b,
245 pending.schema,
246 pending.formed_timestamp,
247 );
248 }
249
250 Ok(simulation)
251 }
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257 use crate::entity::EntityBuilder;
258 use crate::enums::{EventType, Species};
259 use crate::event::EventBuilder;
260
261 fn create_human(id: &str) -> Entity {
262 EntityBuilder::new()
263 .id(id)
264 .species(Species::Human)
265 .age(crate::types::Duration::years(30))
266 .build()
267 .unwrap()
268 }
269
270 fn reference_date() -> Timestamp {
271 Timestamp::from_ymd_hms(2024, 1, 1, 0, 0, 0)
272 }
273
274 #[test]
275 fn builder_creates_empty_simulation() {
276 let sim = SimulationBuilder::new(reference_date()).build().unwrap();
277
278 assert_eq!(sim.entity_count(), 0);
279 assert_eq!(sim.reference_date(), reference_date());
280 }
281
282 #[test]
283 fn builder_add_entity() {
284 let entity = create_human("person_001");
285
286 let sim = SimulationBuilder::new(reference_date())
287 .add_entity(entity, reference_date())
288 .build()
289 .unwrap();
290
291 assert_eq!(sim.entity_count(), 1);
292 }
293
294 #[test]
295 fn builder_add_multiple_entities() {
296 let sim = SimulationBuilder::new(reference_date())
297 .add_entity(create_human("alice"), reference_date())
298 .add_entity(create_human("bob"), reference_date())
299 .add_entity(create_human("carol"), reference_date())
300 .build()
301 .unwrap();
302
303 assert_eq!(sim.entity_count(), 3);
304 }
305
306 #[test]
307 fn builder_duplicate_entity_id_fails() {
308 let result = SimulationBuilder::new(reference_date())
309 .add_entity(create_human("alice"), reference_date())
310 .add_entity(create_human("alice"), reference_date())
311 .build();
312
313 assert!(result.is_err());
314 let err = result.unwrap_err();
315 assert!(
316 matches!(err, SimulationBuildError::DuplicateEntityId(ref id) if id.as_str() == "alice")
317 );
318 }
319
320 #[test]
321 fn builder_add_event() {
322 let target = EntityId::new("person_001").unwrap();
323 let event = EventBuilder::new(EventType::EndRelationshipRomantic)
324 .target(target.clone())
325 .severity(0.7)
326 .build()
327 .unwrap();
328
329 let sim = SimulationBuilder::new(reference_date())
330 .add_entity(create_human("person_001"), reference_date())
331 .add_event(event, Timestamp::from_ymd_hms(2024, 1, 15, 0, 0, 0))
332 .build()
333 .unwrap();
334
335 let events = sim.events_for(&target);
336 assert_eq!(events.len(), 1);
337 }
338
339 #[test]
340 fn builder_event_with_valid_source_succeeds() {
341 let source = EntityId::new("person_001").unwrap();
342 let target = EntityId::new("person_002").unwrap();
343 let event = EventBuilder::new(EventType::EndRelationshipRomantic)
344 .source(source.clone())
345 .target(target.clone())
346 .severity(0.7)
347 .build()
348 .unwrap();
349
350 let sim = SimulationBuilder::new(reference_date())
351 .add_entity(create_human("person_001"), reference_date())
352 .add_entity(create_human("person_002"), reference_date())
353 .add_event(event, Timestamp::from_ymd_hms(2024, 1, 15, 0, 0, 0))
354 .build()
355 .unwrap();
356
357 let events = sim.events_for(&target);
359 assert_eq!(events.len(), 1);
360 }
361
362 #[test]
363 fn builder_event_unknown_target_fails() {
364 let target = EntityId::new("unknown").unwrap();
365 let event = EventBuilder::new(EventType::EndRelationshipRomantic)
366 .target(target.clone())
367 .severity(0.7)
368 .build()
369 .unwrap();
370
371 let result = SimulationBuilder::new(reference_date())
372 .add_event(event, Timestamp::from_ymd_hms(2024, 1, 15, 0, 0, 0))
373 .build();
374
375 assert!(result.is_err());
376 let err = result.unwrap_err();
377 assert!(matches!(
378 err,
379 SimulationBuildError::EventReferencesUnknownEntity(_, ref entity_id)
380 if entity_id.as_str() == "unknown"
381 ));
382 }
383
384 #[test]
385 fn builder_event_unknown_source_fails() {
386 let source = EntityId::new("unknown_source").unwrap();
387 let target = EntityId::new("person_001").unwrap();
388 let event = EventBuilder::new(EventType::EndRelationshipRomantic)
389 .source(source.clone())
390 .target(target.clone())
391 .severity(0.7)
392 .build()
393 .unwrap();
394
395 let result = SimulationBuilder::new(reference_date())
396 .add_entity(create_human("person_001"), reference_date())
397 .add_event(event, Timestamp::from_ymd_hms(2024, 1, 15, 0, 0, 0))
398 .build();
399
400 assert!(result.is_err());
401 let err = result.unwrap_err();
402 assert!(matches!(
403 err,
404 SimulationBuildError::EventReferencesUnknownEntity(_, ref entity_id)
405 if entity_id.as_str() == "unknown_source"
406 ));
407 }
408
409 #[test]
410 fn builder_add_relationship() {
411 let alice_id = EntityId::new("alice").unwrap();
412 let bob_id = EntityId::new("bob").unwrap();
413
414 let sim = SimulationBuilder::new(reference_date())
415 .add_entity(create_human("alice"), reference_date())
416 .add_entity(create_human("bob"), reference_date())
417 .add_relationship(
418 alice_id.clone(),
419 bob_id,
420 RelationshipSchema::Peer,
421 reference_date(),
422 )
423 .build()
424 .unwrap();
425
426 assert_eq!(sim.relationship_count(), 1);
427 assert_eq!(sim.relationships_for(&alice_id).len(), 1);
428 }
429
430 #[test]
431 fn builder_relationship_unknown_entity_a_fails() {
432 let alice_id = EntityId::new("alice").unwrap();
433 let bob_id = EntityId::new("bob").unwrap();
434
435 let result = SimulationBuilder::new(reference_date())
436 .add_entity(create_human("bob"), reference_date())
437 .add_relationship(
438 alice_id.clone(),
439 bob_id,
440 RelationshipSchema::Peer,
441 reference_date(),
442 )
443 .build();
444
445 assert!(result.is_err());
446 let err = result.unwrap_err();
447 assert!(matches!(
448 err,
449 SimulationBuildError::RelationshipReferencesUnknownEntity(_, ref entity_id)
450 if entity_id.as_str() == "alice"
451 ));
452 }
453
454 #[test]
455 fn builder_relationship_unknown_entity_b_fails() {
456 let alice_id = EntityId::new("alice").unwrap();
457 let bob_id = EntityId::new("bob").unwrap();
458
459 let result = SimulationBuilder::new(reference_date())
460 .add_entity(create_human("alice"), reference_date())
461 .add_relationship(
462 alice_id,
463 bob_id.clone(),
464 RelationshipSchema::Peer,
465 reference_date(),
466 )
467 .build();
468
469 assert!(result.is_err());
470 let err = result.unwrap_err();
471 assert!(matches!(
472 err,
473 SimulationBuildError::RelationshipReferencesUnknownEntity(_, ref entity_id)
474 if entity_id.as_str() == "bob"
475 ));
476 }
477
478 #[test]
479 fn builder_self_relationship_fails() {
480 let alice_id = EntityId::new("alice").unwrap();
481
482 let result = SimulationBuilder::new(reference_date())
483 .add_entity(create_human("alice"), reference_date())
484 .add_relationship(
485 alice_id.clone(),
486 alice_id,
487 RelationshipSchema::Peer,
488 reference_date(),
489 )
490 .build();
491
492 assert!(result.is_err());
493 let err = result.unwrap_err();
494 assert!(matches!(
495 err,
496 SimulationBuildError::SelfRelationship(ref id)
497 if id.as_str() == "alice"
498 ));
499 }
500
501 #[test]
502 fn builder_fluent_chain() {
503 let alice = create_human("alice");
504 let bob = create_human("bob");
505 let alice_id = EntityId::new("alice").unwrap();
506 let bob_id = EntityId::new("bob").unwrap();
507
508 let event = EventBuilder::new(EventType::AchieveGoalMajor)
509 .target(alice_id.clone())
510 .build()
511 .unwrap();
512
513 let sim = SimulationBuilder::new(reference_date())
514 .add_entity(alice, reference_date())
515 .add_entity(bob, reference_date())
516 .add_event(event, Timestamp::from_ymd_hms(2024, 1, 10, 0, 0, 0))
517 .add_relationship(
518 alice_id.clone(),
519 bob_id,
520 RelationshipSchema::Peer,
521 Timestamp::from_ymd_hms(2024, 1, 5, 0, 0, 0),
522 )
523 .build()
524 .unwrap();
525
526 assert_eq!(sim.entity_count(), 2);
527 assert_eq!(sim.relationship_count(), 1);
528 assert_eq!(sim.events_for(&alice_id).len(), 1);
529 }
530
531 #[test]
532 fn simulation_build_error_display() {
533 let alice_id = EntityId::new("alice").unwrap();
534 let bob_id = EntityId::new("bob").unwrap();
535 let carol_id = EntityId::new("carol").unwrap();
536 let event_id = EventId::new("test_event").unwrap();
537 let rel_id = RelationshipId::new("test_rel").unwrap();
538
539 let err1 = SimulationBuildError::DuplicateEntityId(alice_id);
540 assert!(format!("{}", err1).contains("alice"));
541 assert!(format!("{}", err1).contains("Duplicate"));
542
543 let err2 = SimulationBuildError::EventReferencesUnknownEntity(event_id, bob_id.clone());
544 assert!(format!("{}", err2).contains("bob"));
545 assert!(format!("{}", err2).contains("unknown"));
546 assert!(format!("{}", err2).contains("test_event"));
547
548 let err3 = SimulationBuildError::RelationshipReferencesUnknownEntity(rel_id, bob_id);
549 assert!(format!("{}", err3).contains("bob"));
550 assert!(format!("{}", err3).contains("unknown"));
551 assert!(format!("{}", err3).contains("test_rel"));
552
553 let err4 = SimulationBuildError::SelfRelationship(carol_id);
554 assert!(format!("{}", err4).contains("carol"));
555 assert!(format!("{}", err4).contains("itself"));
556 }
557
558 #[test]
559 fn simulation_build_error_debug() {
560 let alice_id = EntityId::new("alice").unwrap();
561 let err = SimulationBuildError::DuplicateEntityId(alice_id);
562 let debug = format!("{:?}", err);
563 assert!(debug.contains("DuplicateEntityId"));
564 }
565
566 #[test]
567 fn simulation_build_error_clone() {
568 let alice_id = EntityId::new("alice").unwrap();
569 let err1 = SimulationBuildError::DuplicateEntityId(alice_id);
570 let err2 = err1.clone();
571 assert_eq!(err1, err2);
572 }
573
574 #[test]
575 fn event_without_target_allowed() {
576 let event = EventBuilder::new(EventType::AchieveGoalMajor)
578 .severity(0.5)
579 .build()
580 .unwrap();
581
582 let sim = SimulationBuilder::new(reference_date())
583 .add_event(event, Timestamp::from_ymd_hms(2024, 1, 15, 0, 0, 0))
584 .build()
585 .unwrap();
586
587 assert_eq!(sim.entity_count(), 0);
588 }
589
590 #[test]
591 fn simulation_build_error_is_std_error() {
592 let alice_id = EntityId::new("alice").unwrap();
593 let err: Box<dyn std::error::Error> =
594 Box::new(SimulationBuildError::DuplicateEntityId(alice_id));
595 assert!(err.source().is_none());
597 }
598
599 #[test]
600 fn simulation_build_error_eq() {
601 let alice1 = EntityId::new("alice").unwrap();
602 let alice2 = EntityId::new("alice").unwrap();
603 let bob = EntityId::new("bob").unwrap();
604
605 let err1 = SimulationBuildError::DuplicateEntityId(alice1);
606 let err2 = SimulationBuildError::DuplicateEntityId(alice2);
607 let err3 = SimulationBuildError::DuplicateEntityId(bob);
608
609 assert_eq!(err1, err2);
610 assert_ne!(err1, err3);
611 }
612}