arbiter_engine/
universe.rs

1//! The [`universe`] module contains the [`Universe`] struct which is the
2//! primary interface for creating and running many `World`s in parallel.
3
4use super::*;
5use crate::world::World;
6
7/// The [`Universe`] struct is the primary interface for creating and running
8/// many `World`s in parallel. At the moment, is a wrapper around a
9/// [`HashMap`] of [`World`]s, but can be extended to handle generics inside of
10/// [`World`]s and crosstalk between [`World`]s.
11#[derive(Debug, Default)]
12pub struct Universe {
13    worlds: Option<HashMap<String, World>>,
14    world_tasks: Option<Vec<Result<World, JoinError>>>,
15}
16
17impl Universe {
18    /// Creates a new [`Universe`].
19    pub fn new() -> Self {
20        Self {
21            worlds: Some(HashMap::new()),
22            world_tasks: None,
23        }
24    }
25
26    /// Adds a [`World`] to the [`Universe`].
27    pub fn add_world(&mut self, world: World) {
28        if let Some(worlds) = self.worlds.as_mut() {
29            worlds.insert(world.id.clone(), world);
30        }
31    }
32
33    /// Runs all of the [`World`]s in the [`Universe`] in parallel.
34    pub async fn run_worlds(&mut self) -> Result<(), ArbiterEngineError> {
35        if self.is_online() {
36            return Err(ArbiterEngineError::UniverseError(
37                "Universe is already running.".to_owned(),
38            ));
39        }
40        let mut tasks = Vec::new();
41        // NOTE: Unwrap is safe because we checked if the universe is online.
42        for (_, mut world) in self.worlds.take().unwrap().drain() {
43            tasks.push(spawn(async move {
44                world.run().await.unwrap();
45                world
46            }));
47        }
48        self.world_tasks = Some(join_all(tasks.into_iter()).await);
49        Ok(())
50    }
51
52    /// Returns `true` if the [`Universe`] is running.
53    pub fn is_online(&self) -> bool {
54        self.world_tasks.is_some()
55    }
56}
57
58#[cfg(test)]
59mod tests {
60
61    use super::*;
62
63    #[tokio::test]
64    async fn run_universe() {
65        let mut universe = Universe::new();
66        let world = World::new("test");
67        universe.add_world(world);
68        universe.run_worlds().await.unwrap();
69        universe.world_tasks.unwrap().remove(0).unwrap();
70    }
71
72    #[tokio::test]
73    #[should_panic(expected = "Universe is already running.")]
74    async fn cant_run_twice() {
75        let mut universe = Universe::new();
76        let world1 = World::new("test");
77        universe.add_world(world1);
78        universe.run_worlds().await.unwrap();
79        universe.run_worlds().await.unwrap();
80    }
81}