ed_journals/modules/state/models/resolvers/
game_state_resolver.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//! High level state resolver which encapsulates [LogState] and in turn
//! [LogStateResolver](super::log_state_resolver::LogStateResolver). Like the LogStateResolver, this
//! resolver handles all log events, but keep track of which commander the logs belong to.

pub mod game_commander_entry;

use std::collections::HashMap;

use serde::Serialize;

use crate::logs::{LogEvent, LogEventContent};
use crate::state::models::feed_result::FeedResult;
use crate::state::resolvers::game_state_resolver::game_commander_entry::GameCommanderEntry;
use crate::state::traits::state_resolver::StateResolver;
use crate::state::LogState;

/// High level state resolver which encapsulates [LogState] and in turn
/// [LogStateResolver](super::log_state_resolver::LogStateResolver). Like the LogStateResolver, this
/// resolver handles all log events, but keep track of which commander the logs belong to.
#[derive(Serialize, Default)]
pub struct GameStateResolver {
    /// A map of commanders that are tracked, where the key is the Frontier ID of the commander.
    pub commanders: HashMap<String, GameCommanderEntry>,
    current_commander_id: Option<String>,
}

impl StateResolver<LogEvent> for GameStateResolver {
    fn feed(&mut self, input: &LogEvent) -> FeedResult {
        match &input.content {
            LogEventContent::Commander(commander) => {
                self.current_commander_id = Some(commander.fid.to_string());

                if !self.commanders.contains_key(&commander.fid) {
                    self.commanders.insert(
                        commander.fid.to_string(),
                        GameCommanderEntry {
                            name: commander.name.to_string(),
                            log_state: LogState::default(),
                        },
                    );
                }
            }
            _ => {
                let Some(current) = self.current_commander_mut() else {
                    return FeedResult::Later;
                };

                current.log_state.feed(input);
            }
        }

        FeedResult::Accepted
    }

    fn flush_inner(&mut self) {
        for commander in self.commanders.values_mut() {
            commander.log_state.flush();
        }
    }
}

impl GameStateResolver {
    /// Returns the current commander which is active in the logs.
    pub fn current_commander(&self) -> Option<&GameCommanderEntry> {
        self.current_commander_id
            .as_ref()
            .and_then(|commander_id| self.commanders.get(commander_id))
    }

    /// Returns a mutable reference to the current active commander in the logs.
    pub fn current_commander_mut(&mut self) -> Option<&mut GameCommanderEntry> {
        self.current_commander_id
            .as_ref()
            .and_then(|commander_id| self.commanders.get_mut(commander_id))
    }
}

#[cfg(test)]
mod tests {
    use crate::logs::blocking::LogDirReader;
    use crate::state::GameState;
    use std::collections::HashSet;
    use std::env::current_dir;
    use std::time::Instant;

    #[test]
    fn state_is_correct() {
        let dir_path = current_dir().unwrap().join("test-files").join("journals");

        let log_dir = LogDirReader::open(dir_path);

        let mut state = GameState::default();
        let instant = Instant::now();

        for entry in log_dir {
            state.feed(&entry.unwrap());
        }

        state.flush();

        dbg!(instant.elapsed().as_nanos());

        // Confirms that there are only one species of each genus on each planet
        for commander in state.commanders.values() {
            for system in commander.log_state.systems.values() {
                for body in system.planet_state.values() {
                    let mut genuses = HashSet::new();

                    for species in &body.scanned_species {
                        let inserted = genuses.insert(species.genus());

                        if !inserted {
                            panic!("Not here!");
                        }
                    }
                }
            }
        }
    }
}