use crate::route::Router; use dict_derive::IntoPyObject; use pyo3::conversion::ToPyObject; use pyo3::prelude::*; use pyo3::types::{PyDict, PyTuple}; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::collections::HashMap; use std::path::PathBuf; pub fn get_fsd_booster_info(class: usize) -> Result { // Data from https://elite-dangerous.fandom.com/wiki/Guardian_Frame_Shift_Drive_Booster let ret = match class { 0 => 0.0, 1 => 4.0, 2 => 6.0, 3 => 7.75, 4 => 9.25, 5 => 10.5, _ => return Err(format!("Invalid Guardian booster class: {}", class)), }; return Ok(ret); } pub fn get_fsd_info(rating: usize, class: usize) -> Result, String> { let mut ret = HashMap::new(); // Data from https://elite-dangerous.fandom.com/wiki/Frame_Shift_Drive#Specifications let (opt_mass, max_fuel) = match (class, rating) { (2, 1) => (48.0, 0.6), (2, 2) => (54.0, 0.6), (2, 3) => (60.0, 0.6), (2, 4) => (75.0, 0.8), (2, 5) => (90.0, 0.9), (3, 1) => (80.0, 1.2), (3, 2) => (90.0, 1.2), (3, 3) => (100.0, 1.2), (3, 4) => (125.0, 1.5), (3, 5) => (150.0, 1.8), (4, 1) => (280.0, 2.0), (4, 2) => (315.0, 2.0), (4, 3) => (350.0, 2.0), (4, 4) => (438.0, 2.5), (4, 5) => (525.0, 3.0), (5, 1) => (560.0, 3.3), (5, 2) => (630.0, 3.3), (5, 3) => (700.0, 3.3), (5, 4) => (875.0, 4.1), (5, 5) => (1050.0, 5.0), (6, 1) => (960.0, 5.3), (6, 2) => (1080.0, 5.3), (6, 3) => (1200.0, 5.3), (6, 4) => (1500.0, 6.6), (6, 5) => (1800.0, 8.0), (7, 1) => (1440.0, 8.5), (7, 2) => (1620.0, 8.5), (7, 3) => (1800.0, 8.5), (7, 4) => (2250.0, 10.6), (7, 5) => (2700.0, 12.8), (r, c) => return Err(format!("Invalid FSD Type: Rating: {}, Class: {}", r, c)), }; ret.insert("FSDOptimalMass".to_owned(), opt_mass); ret.insert("MaxFuel".to_owned(), max_fuel); return Ok(ret); } pub fn get_mult(star_type: &str) -> f32 { if star_type.contains("White Dwarf") { return 1.5; } if star_type.contains("Neutron") { return 4.0; } 1.0 } pub enum SysEntry { ID(u32), Name(String), } impl SysEntry { pub fn parse(s: &str) -> Self { if let Ok(n) = s.parse() { SysEntry::ID(n) } else { SysEntry::Name(s.to_owned()) } } } pub fn find_matches( path: &PathBuf, names: Vec, exact: bool, ) -> Result)>, String> { let mut best: HashMap)> = HashMap::new(); if names.is_empty() { return Ok(best); } for name in &names { best.insert(name.to_string(), (0.0, None)); } let mut reader = match csv::ReaderBuilder::new().from_path(path) { Ok(rdr) => rdr, Err(e) => { return Err(format!("Error opening {}: {}", path.to_str().unwrap(), e)); } }; let systems = reader.deserialize::(); for sys in systems { let sys = sys.unwrap(); for name in &names { best.entry(name.clone()).and_modify(|ent| { if (exact) && (&sys.system == name) { *ent = (1.0, Some(sys.clone().build())) } else { let d1 = strsim::normalized_levenshtein(&sys.system, &name); let d2 = strsim::normalized_levenshtein(&sys.body, &name); if d1 > ent.0 { *ent = (d1, Some(sys.clone().build())) } else if d2 > ent.0 { *ent = (d2, Some(sys.clone().build())) } } }); } } Ok(best) } #[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub struct TreeNode { pub id: u32, pub pos: [f32; 3], pub mult: f32, } impl TreeNode { pub fn get(&self, router: &Router) -> Option { let mut cache = router.cache.as_ref().unwrap().lock().unwrap(); cache.get(self.id) } } #[derive(Debug, Clone, Serialize, Deserialize, IntoPyObject)] pub struct SystemSerde { pub id: u32, pub star_type: String, pub system: String, pub body: String, pub mult: f32, pub distance: f32, pub x: f32, pub y: f32, pub z: f32, } impl SystemSerde { pub fn build(self) -> System { System { id: self.id, star_type: self.star_type, system: self.system, body: self.body, mult: self.mult, distance: self.distance, pos: [self.x, self.y, self.z], } } pub fn to_node(&self) -> TreeNode { TreeNode { id: self.id, pos: [self.x, self.y, self.z], mult: self.mult, } } } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct System { pub id: u32, pub star_type: String, pub system: String, pub body: String, pub mult: f32, pub distance: f32, pub pos: [f32; 3], } impl ToPyObject for System { fn to_object(&self, py: Python) -> PyObject { let pos = PyTuple::new(py, self.pos.iter()); let elem = PyDict::new(py); elem.set_item("star_type", self.star_type.clone()).unwrap(); elem.set_item("system", self.system.clone()).unwrap(); elem.set_item("body", self.body.clone()).unwrap(); elem.set_item("distance", self.distance).unwrap(); elem.set_item("mult", self.mult).unwrap(); elem.set_item("id", self.id).unwrap(); elem.set_item("pos", pos).unwrap(); elem.to_object(py) } } impl System { pub fn to_node(&self) -> TreeNode { TreeNode { id: self.id, pos: self.pos, mult: self.mult, } } } impl Ord for System { fn cmp(&self, other: &Self) -> Ordering { self.id.cmp(&other.id) } } impl PartialOrd for System { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } }