ed-journals 0.4.0

Provides models for representing and parsing elite dangerous journal files
Documentation
use std::collections::HashMap;

use chrono::{DateTime, Utc};
use serde::Serialize;

use crate::logs::{LogEvent, LogEventContent};
use crate::logs::fss_signal_discovered_event::FSSSignalDiscoveredEvent;
use crate::modules::civilization::LocationInfo;
use crate::state::ExobiologyState;
use crate::state::models::body_state::BodyState;
use crate::state::models::feed_result::FeedResult;

#[derive(Serialize)]
pub struct SystemState {
    pub location_info: LocationInfo,
    pub bodies: HashMap<u8, BodyState>,
    pub visits: Vec<DateTime<Utc>>,
    pub carrier_visits: Vec<DateTime<Utc>>,
    pub number_of_bodies: Option<u8>,
    pub progress: f32,
    pub all_found: bool,
    pub station_signals: Vec<FSSSignalDiscoveredEvent>,
    pub exobiology: ExobiologyState,
}

impl SystemState {
    pub fn feed_log_event(&mut self, log_event: &LogEvent) -> FeedResult {
        let Some(system_address) = log_event.content.system_address() else {
            return FeedResult::Skipped;
        };

        if system_address != self.location_info.system_address {
            return FeedResult::Skipped;
        }

        self.exobiology.feed_event(log_event);

        match &log_event.content {
            LogEventContent::FSSDiscoveryScan(event) => {
                self.number_of_bodies = Some(event.body_count);
                self.progress = event.progress;
            }
            LogEventContent::FSSAllBodiesFound(event) => {
                self.number_of_bodies = Some(event.count);
                self.all_found = true;
            }
            LogEventContent::FSSSignalDiscovered(event) => {
                if event.is_station {
                    self.station_signals.push(event.clone());
                }
            }
            LogEventContent::Scan(event) => {
                self.bodies
                    .entry(event.body_id)
                    .or_insert_with(|| BodyState::new(event.clone()));
            }

            _ => {
                if let Some(body_id) = log_event.content.body_id() {
                    let Some(body) = self.bodies.get_mut(&body_id) else {
                        return FeedResult::Later;
                    };

                    return body.feed_log_event(log_event);
                }
            }
        }

        FeedResult::Accepted
    }

    pub fn visit(&mut self, date_time: &DateTime<Utc>) {
        self.visits.push(*date_time);
    }

    pub fn carrier_visit(&mut self, date_time: &DateTime<Utc>) {
        self.carrier_visits.push(*date_time);
    }

    pub fn clear_organic_progress(&mut self) {
        for body in self.bodies.values_mut() {
            body.clear_organic_process();
        }
    }
}

impl From<&LocationInfo> for SystemState {
    fn from(value: &LocationInfo) -> Self {
        SystemState {
            location_info: value.clone(),
            bodies: HashMap::new(),
            visits: Vec::new(),
            carrier_visits: Vec::new(),
            number_of_bodies: None,
            progress: 0.0,
            all_found: false,
            station_signals: Vec::new(),
            exobiology: ExobiologyState::new(),
        }
    }
}