ED_LRR/rust/src/journal.rs

173 lines
4.5 KiB
Rust

//! Elite: Dangerous Journal Loadout even parser
use crate::common::get_fsd_info;
use crate::ship::Ship;
use eyre::Result;
use regex::Regex;
use serde::Deserialize;
use std::collections::HashMap;
#[derive(Clone, Debug, PartialEq, Deserialize)]
pub struct Event {
#[serde(flatten)]
pub event: EventData,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
#[serde(tag = "event")]
pub enum EventData {
Loadout(Loadout),
#[serde(other)]
Unknown,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Modifier {
label: String,
value: f32,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Engineering {
modifiers: Vec<Modifier>,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Module {
engineering: Option<Engineering>,
item: String,
slot: String,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct FuelCapacity {
main: f32,
reserve: f32,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Loadout {
#[serde(rename = "timestamp")]
timestamp: String,
ship: String,
ship_name: String,
ship_ident: String,
fuel_capacity: FuelCapacity,
unladen_mass: f32,
modules: Vec<Module>,
}
impl Engineering {
fn to_hashmap(&self) -> HashMap<String, f32> {
let mut h = HashMap::new();
for v in &self.modifiers {
h.insert(v.label.clone(), v.value);
}
return h;
}
}
impl Loadout {
fn get_booster(&self) -> Option<usize> {
self.modules.iter().cloned().find_map(|m| {
let Module { item, .. } = m;
if item.starts_with("int_guardianfsdbooster") {
return item
.chars()
.last()
.unwrap()
.to_digit(10)
.map(|v| v as usize);
}
return None;
})
}
fn get_fsd(&self) -> Option<(String, Option<Engineering>)> {
self.modules.iter().cloned().find_map(|m| {
let Module {
slot,
engineering,
item,
} = m;
if slot == "FrameShiftDrive" {
return Some((item, engineering));
}
return None;
})
}
pub fn try_into_ship(self) -> Result<(String, Ship), String> {
let fsd = self.get_fsd().ok_or("No FSD found!")?;
let booster = self.get_booster().unwrap_or(0);
let fsd_type = Regex::new(r"^int_hyperdrive_size(\d+)_class(\d+)$")
.unwrap()
.captures(&fsd.0);
let fsd_type: (usize, usize) = fsd_type
.and_then(|m| {
let s = m.get(1)?.as_str().to_owned().parse().ok()?;
let c = m.get(2)?.as_str().to_owned().parse().ok()?;
return Some((c, s));
})
.ok_or(format!("Invalid FSD found: {}", &fsd.0))?;
let eng = fsd
.1
.map(|eng| eng.to_hashmap())
.unwrap_or_else(HashMap::new);
let mut fsd_info = get_fsd_info(fsd_type.0, fsd_type.1)?;
let fsd_type = (
"_EDCBA"
.chars()
.nth(fsd_type.0)
.ok_or(format!("Invalid FSD found: {}", &fsd.0))?,
fsd_type.1 as u8,
);
fsd_info.extend(eng);
let max_fuel = fsd_info
.get("MaxFuel")
.ok_or(format!("Unknwon MaxFuelPerJump for FSD: {}", &fsd.0))?;
let opt_mass = fsd_info
.get("FSDOptimalMass")
.ok_or(format!("Unknwon FSDOptimalMass for FSD: {}", &fsd.0))?;
let key = format!(
"[{}] {} ({})",
if !self.ship_name.is_empty() {
self.ship_name
} else {
"<NO NAME>".to_owned()
},
self.ship_ident,
self.ship
);
return Ok((
key,
Ship::new(
self.unladen_mass,
self.fuel_capacity.main,
self.fuel_capacity.main,
fsd_type,
*max_fuel,
*opt_mass,
booster,
)?,
));
}
}
impl Event {
pub fn get_loadout(self) -> Option<Loadout> {
if let Event {
event: EventData::Loadout(loadout),
} = self
{
return Some(loadout);
}
None
}
}