2020-03-28 13:53:52 +00:00
|
|
|
// #![deny(warnings)]
|
2020-06-16 13:38:31 +00:00
|
|
|
#![allow(dead_code, clippy::needless_return, clippy::too_many_arguments)]
|
|
|
|
pub mod common;
|
|
|
|
pub mod edsm;
|
|
|
|
pub mod galaxy;
|
|
|
|
pub mod journal;
|
|
|
|
pub mod route;
|
|
|
|
pub mod ship;
|
2020-03-28 13:53:52 +00:00
|
|
|
|
|
|
|
extern crate derivative;
|
|
|
|
use crate::common::{find_matches, SysEntry};
|
|
|
|
use crate::route::{Router, SearchState};
|
2020-06-16 13:38:31 +00:00
|
|
|
use crate::ship::Ship;
|
2020-03-28 13:53:52 +00:00
|
|
|
use pyo3::exceptions::*;
|
|
|
|
use pyo3::prelude::*;
|
|
|
|
use pyo3::types::{PyDict, PyList, PyTuple};
|
|
|
|
use pyo3::PyObjectProtocol;
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
|
|
|
#[pyclass(dict)]
|
2020-06-16 13:38:31 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
#[text_signature = "(callback, /)"]
|
2020-03-28 13:53:52 +00:00
|
|
|
struct PyRouter {
|
|
|
|
router: Router,
|
2020-06-16 13:38:31 +00:00
|
|
|
primary_only: bool,
|
2020-03-28 13:53:52 +00:00
|
|
|
stars_path: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[pymethods]
|
|
|
|
impl PyRouter {
|
|
|
|
#[new]
|
|
|
|
#[args(callback = "None")]
|
|
|
|
fn new(callback: Option<PyObject>, py: Python<'static>) -> PyResult<Self> {
|
2020-06-16 13:38:31 +00:00
|
|
|
Ok(PyRouter {
|
|
|
|
router: Router::new(Box::new(
|
|
|
|
move |state: &SearchState| {
|
|
|
|
match callback.as_ref() {
|
|
|
|
Some(cb) => cb.call(py, (state.clone(),), None),
|
|
|
|
None => Ok(py.None()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)),
|
|
|
|
primary_only: false,
|
2020-03-28 13:53:52 +00:00
|
|
|
stars_path: String::from(""),
|
2020-06-16 13:38:31 +00:00
|
|
|
})
|
2020-03-28 13:53:52 +00:00
|
|
|
}
|
|
|
|
|
2020-06-16 13:38:31 +00:00
|
|
|
#[text_signature = "(ship, /)"]
|
|
|
|
fn set_ship(&mut self, py: Python, ship: &PyShip) -> PyResult<PyObject> {
|
|
|
|
self.router.set_ship(ship.ship.clone());
|
|
|
|
Ok(py.None())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[args(primary_only = "false")]
|
|
|
|
#[text_signature = "(path, primary_only, /)"]
|
|
|
|
fn load(
|
|
|
|
&mut self,
|
|
|
|
path: String,
|
|
|
|
primary_only: bool,
|
|
|
|
py: Python,
|
|
|
|
) -> PyResult<PyObject> {
|
2020-03-28 13:53:52 +00:00
|
|
|
self.stars_path = path;
|
2020-06-16 13:38:31 +00:00
|
|
|
self.primary_only = primary_only;
|
|
|
|
Ok(py.None())
|
2020-03-28 13:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[args(greedyness = "0.5", num_workers = "0", beam_width = "0")]
|
|
|
|
#[text_signature = "(hops, range, greedyness, beam_width, num_workers, /)"]
|
|
|
|
fn route(
|
|
|
|
&mut self,
|
|
|
|
hops: &PyList,
|
2020-06-16 13:38:31 +00:00
|
|
|
range: Option<f32>,
|
2020-03-28 13:53:52 +00:00
|
|
|
greedyness: f32,
|
|
|
|
beam_width: usize,
|
|
|
|
num_workers: usize,
|
|
|
|
py: Python,
|
|
|
|
) -> PyResult<PyObject> {
|
2020-06-16 13:38:31 +00:00
|
|
|
let route_res = self
|
|
|
|
.router
|
|
|
|
.load(&PathBuf::from(self.stars_path.clone()), self.primary_only);
|
2020-03-28 13:53:52 +00:00
|
|
|
if let Err(err_msg) = route_res {
|
|
|
|
return Err(PyErr::new::<ValueError, _>(err_msg));
|
|
|
|
};
|
|
|
|
let mut sys_entries: Vec<SysEntry> = Vec::new();
|
|
|
|
for hop in hops {
|
|
|
|
if let Ok(id) = hop.extract() {
|
|
|
|
sys_entries.push(SysEntry::ID(id));
|
|
|
|
} else {
|
|
|
|
sys_entries.push(SysEntry::parse(hop.extract()?));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
println!("Resolving systems...");
|
|
|
|
let ids: Vec<u32> = match resolve(&sys_entries, &self.router.path) {
|
|
|
|
Ok(ids) => ids,
|
|
|
|
Err(err_msg) => {
|
|
|
|
return Err(PyErr::new::<ValueError, _>(err_msg));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
match self
|
|
|
|
.router
|
2020-06-16 13:38:31 +00:00
|
|
|
.compute_route(&ids, range, greedyness, beam_width, num_workers)
|
2020-03-28 13:53:52 +00:00
|
|
|
{
|
2020-06-16 13:38:31 +00:00
|
|
|
Ok(route) => {
|
|
|
|
let py_route: Vec<_> = route.iter().map(|hop| hop.to_object(py)).collect();
|
|
|
|
Ok(py_route.to_object(py))
|
|
|
|
}
|
2020-03-28 13:53:52 +00:00
|
|
|
Err(err_msg) => Err(PyErr::new::<RuntimeError, _>(err_msg)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[args(hops = "*")]
|
|
|
|
#[text_signature = "(sys_1, sys_2, ..., /)"]
|
|
|
|
fn resolve_systems(&self, hops: &PyTuple, py: Python) -> PyResult<PyObject> {
|
|
|
|
let mut sys_entries: Vec<SysEntry> = Vec::new();
|
|
|
|
for hop in hops {
|
|
|
|
if let Ok(id) = hop.extract() {
|
|
|
|
sys_entries.push(SysEntry::ID(id));
|
|
|
|
} else {
|
|
|
|
sys_entries.push(SysEntry::parse(hop.extract()?));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
println!("Resolving systems...");
|
2020-06-16 13:38:31 +00:00
|
|
|
let stars_path = PathBuf::from(self.stars_path.clone());
|
|
|
|
let ids: Vec<u32> = match resolve(&sys_entries, &stars_path) {
|
2020-03-28 13:53:52 +00:00
|
|
|
Ok(ids) => ids,
|
|
|
|
Err(err_msg) => {
|
|
|
|
return Err(PyErr::new::<ValueError, _>(err_msg));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let ret: Vec<(_, u32)> = hops.into_iter().zip(ids.into_iter()).collect();
|
|
|
|
Ok(PyDict::from_sequence(py, ret.to_object(py))?.to_object(py))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[staticmethod]
|
|
|
|
fn preprocess_edsm() -> PyResult<()> {
|
2020-06-16 13:38:31 +00:00
|
|
|
todo!("Implement EDSM Preprocessor")
|
2020-03-28 13:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[staticmethod]
|
|
|
|
fn preprocess_galaxy() -> PyResult<()> {
|
2020-06-16 13:38:31 +00:00
|
|
|
todo!("Implement galaxy.json Preprocessor")
|
2020-03-28 13:53:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[pyproto]
|
|
|
|
impl PyObjectProtocol for PyRouter {
|
|
|
|
fn __str__(&self) -> PyResult<String> {
|
|
|
|
Ok(format!("{:?}", &self))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn __repr__(&self) -> PyResult<String> {
|
|
|
|
Ok(format!("{:?}", &self))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-16 13:38:31 +00:00
|
|
|
fn resolve(entries: &[SysEntry], path: &PathBuf) -> Result<Vec<u32>, String> {
|
2020-03-28 13:53:52 +00:00
|
|
|
let mut names: Vec<String> = Vec::new();
|
|
|
|
let mut ids: Vec<u32> = Vec::new();
|
|
|
|
let mut ret: Vec<u32> = Vec::new();
|
|
|
|
for ent in entries {
|
|
|
|
match ent {
|
|
|
|
SysEntry::Name(name) => names.push(name.to_owned()),
|
|
|
|
SysEntry::ID(id) => ids.push(*id),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !path.exists() {
|
|
|
|
return Err(format!(
|
|
|
|
"Source file \"{:?}\" does not exist!",
|
|
|
|
path.display()
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
let name_ids = find_matches(path, names, false)?;
|
|
|
|
for ent in entries {
|
|
|
|
match ent {
|
|
|
|
SysEntry::Name(name) => {
|
|
|
|
let ent_res = name_ids
|
|
|
|
.get(&name.to_owned())
|
|
|
|
.ok_or(format!("System {} not found", name))?;
|
|
|
|
let sys = ent_res
|
|
|
|
.1
|
|
|
|
.as_ref()
|
|
|
|
.ok_or(format!("System {} not found", name))?;
|
|
|
|
if ent_res.0 < 0.75 {
|
|
|
|
println!(
|
|
|
|
"WARNING: {} match to {} with low confidence ({:.2}%)",
|
|
|
|
name,
|
|
|
|
sys.system,
|
|
|
|
ent_res.0 * 100.0
|
|
|
|
);
|
|
|
|
}
|
|
|
|
ret.push(sys.id);
|
|
|
|
}
|
|
|
|
SysEntry::ID(id) => ret.push(*id),
|
|
|
|
}
|
|
|
|
}
|
2020-06-16 13:38:31 +00:00
|
|
|
Ok(ret)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[pyclass(dict)]
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct PyShip {
|
|
|
|
ship: Ship,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[pyproto]
|
|
|
|
impl PyObjectProtocol for PyShip {
|
|
|
|
fn __str__(&self) -> PyResult<String> {
|
|
|
|
Ok(format!("{:?}", &self.ship))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn __repr__(&self) -> PyResult<String> {
|
|
|
|
Ok(format!("{:?}", &self.ship))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[pymethods]
|
|
|
|
impl PyShip {
|
|
|
|
#[staticmethod]
|
|
|
|
fn from_loadout(py: Python, loadout: &str) -> PyResult<PyObject> {
|
|
|
|
match Ship::new_from_json(loadout) {
|
|
|
|
Ok(ship) => Ok((PyShip { ship }).into_py(py)),
|
|
|
|
Err(err_msg) => Err(PyErr::new::<ValueError, _>(err_msg)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#[staticmethod]
|
|
|
|
fn from_journal(py: Python) -> PyResult<PyObject> {
|
|
|
|
let mut ship = match Ship::new_from_journal() {
|
|
|
|
Ok(ship) => ship,
|
|
|
|
Err(err_msg) => {
|
|
|
|
return Err(PyErr::new::<ValueError, _>(err_msg));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let ships: Vec<(PyObject, PyObject)> = ship
|
|
|
|
.drain()
|
|
|
|
.map(|(k, v)| {
|
|
|
|
let k_py = k.to_object(py);
|
|
|
|
let v_py = (PyShip { ship: v }).into_py(py);
|
|
|
|
(k_py, v_py)
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
Ok(PyDict::from_sequence(py, ships.to_object(py))?.to_object(py))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn to_dict(&self, py: Python) -> PyResult<PyObject> {
|
|
|
|
self.ship.to_object(py)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[text_signature = "(dist, /)"]
|
|
|
|
fn fuel_cost(&self, _py: Python, dist: f32) -> PyResult<f32> {
|
|
|
|
Ok(self.ship.fuel_cost(dist))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[text_signature = "(/)"]
|
|
|
|
fn range(&self, _py: Python) -> PyResult<f32> {
|
|
|
|
Ok(self.ship.range())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[text_signature = "(/)"]
|
|
|
|
fn max_range(&self, _py: Python) -> PyResult<f32> {
|
|
|
|
Ok(self.ship.max_range())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[text_signature = "(dist, /)"]
|
|
|
|
fn make_jump(&mut self, dist: f32, _py: Python) -> PyResult<Option<f32>> {
|
|
|
|
Ok(self.ship.make_jump(dist))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[text_signature = "(dist, /)"]
|
|
|
|
fn can_jump(&self, dist: f32, _py: Python) -> PyResult<bool> {
|
|
|
|
Ok(self.ship.can_jump(dist))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[args(fuel_amount = "None")]
|
|
|
|
#[text_signature = "(fuel_amount, /)"]
|
|
|
|
fn refuel(&mut self, fuel_amount: Option<f32>, _py: Python) -> PyResult<()> {
|
|
|
|
if let Some(fuel) = fuel_amount {
|
|
|
|
self.ship.fuel_mass = (self.ship.fuel_mass + fuel).min(self.ship.fuel_capacity)
|
|
|
|
} else {
|
|
|
|
self.ship.fuel_mass = self.ship.fuel_capacity;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[text_signature = "(factor, /)"]
|
|
|
|
fn boost(&mut self, factor: f32, _py: Python) -> PyResult<()> {
|
|
|
|
self.ship.boost(factor);
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-03-28 13:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[pymodule]
|
|
|
|
pub fn _ed_lrr(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
|
|
better_panic::install();
|
|
|
|
|
|
|
|
m.add_class::<PyRouter>()?;
|
2020-06-16 13:38:31 +00:00
|
|
|
m.add_class::<PyShip>()?;
|
|
|
|
/*
|
|
|
|
#[pyfn(m, "get_ships_from_journal")]
|
|
|
|
fn get_ships_from_journal(py: Python) -> PyResult<PyObject> {
|
|
|
|
let ship = match Ship::new_from_journal() {
|
|
|
|
Ok(ship) => ship,
|
|
|
|
Err(err_msg) => {
|
|
|
|
return Err(PyErr::new::<ValueError, _>(err_msg));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let ships: Vec<(_,_)> = ship.iter().map(|(k,v)| (k.to_object(py),v.to_object(py))).collect();
|
|
|
|
Ok(PyDict::from_sequence(py, ships.to_object(py))?.to_object(py))
|
|
|
|
}
|
2020-03-28 13:53:52 +00:00
|
|
|
|
2020-06-16 13:38:31 +00:00
|
|
|
|
|
|
|
#[pyfn(m, "get_ships_from_loadout")]
|
|
|
|
fn get_ship_from_loadout(py: Python, loadout: &str) -> PyResult<PyObject> {
|
|
|
|
let ship = match Ship::new_from_json(loadout) {
|
|
|
|
Ok(ship) => ship,
|
|
|
|
Err(err_msg) => {
|
|
|
|
return Err(PyErr::new::<ValueError, _>(err_msg));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Ok(ship.to_object(py))
|
|
|
|
}
|
|
|
|
*/
|
2020-03-28 13:53:52 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
/// Preprocess bodies.json and systemsWithCoordinates.json into stars.csv
|
|
|
|
#[pyfn(m, "preprocess")]
|
|
|
|
#[text_signature = "(infile_systems, infile_bodies, outfile, callback, /)"]
|
|
|
|
fn ed_lrr_preprocess(
|
|
|
|
py: Python<'static>,
|
|
|
|
infile_systems: String,
|
|
|
|
infile_bodies: String,
|
|
|
|
outfile: String,
|
|
|
|
callback: PyObject,
|
|
|
|
) -> PyResult<PyObject> {
|
|
|
|
use preprocess::*;
|
|
|
|
let state = PyDict::new(py);
|
|
|
|
let state_dict = PyDict::new(py);
|
|
|
|
callback.call(py, (state_dict,), None).unwrap();
|
|
|
|
let callback_wrapped = move |state: &PreprocessState| {
|
|
|
|
// println!("SEND: {:?}",state);
|
|
|
|
state_dict.set_item("file", state.file.clone())?;
|
|
|
|
state_dict.set_item("total", state.total)?;
|
|
|
|
state_dict.set_item("count", state.count)?;
|
|
|
|
state_dict.set_item("done", state.done)?;
|
|
|
|
state_dict.set_item("message", state.message.clone())?;
|
|
|
|
callback.call(py, (state_dict,), None)
|
|
|
|
};
|
|
|
|
preprocess_files(
|
|
|
|
&PathBuf::from(infile_bodies),
|
|
|
|
&PathBuf::from(infile_systems),
|
|
|
|
&PathBuf::from(outfile),
|
|
|
|
&callback_wrapped,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
Ok(state.to_object(py))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Find system by name
|
|
|
|
#[pyfn(m, "find_sys")]
|
|
|
|
#[text_signature = "(sys_names, sys_list_path, /)"]
|
|
|
|
fn find_sys(py: Python, sys_names: Vec<String>, sys_list: String) -> PyResult<PyObject> {
|
|
|
|
let path = PathBuf::from(sys_list);
|
|
|
|
match find_matches(&path, sys_names, false) {
|
|
|
|
Ok(vals) => {
|
|
|
|
let ret = PyDict::new(py);
|
|
|
|
for (key, (diff, sys)) in vals {
|
|
|
|
let ret_dict = PyDict::new(py);
|
|
|
|
if let Some(val) = sys {
|
|
|
|
let pos = PyList::new(py, val.pos.iter());
|
|
|
|
ret_dict.set_item("star_type", val.star_type.clone())?;
|
|
|
|
ret_dict.set_item("system", val.system.clone())?;
|
|
|
|
ret_dict.set_item("body", val.body.clone())?;
|
|
|
|
ret_dict.set_item("distance", val.distance)?;
|
|
|
|
ret_dict.set_item("pos", pos)?;
|
|
|
|
ret_dict.set_item("id", val.id)?;
|
|
|
|
ret.set_item(key, (diff, ret_dict).to_object(py))?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(ret.to_object(py))
|
|
|
|
}
|
|
|
|
Err(e) => Err(PyErr::new::<ValueError, _>(e)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Compute a Route using the suplied parameters
|
|
|
|
#[pyfn(m, "route")]
|
|
|
|
#[text_signature = "(hops, range, mode, primary, permute, keep_first, keep_last, greedyness, precomp, path, num_workers, callback, /)"]
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
|
|
fn py_route(
|
|
|
|
py: Python<'static>,
|
|
|
|
hops: Vec<&str>,
|
|
|
|
range: f32,
|
|
|
|
mode: String,
|
|
|
|
primary: bool,
|
|
|
|
permute: bool,
|
|
|
|
keep_first: bool,
|
|
|
|
keep_last: bool,
|
|
|
|
greedyness: Option<f32>,
|
|
|
|
precomp: Option<String>,
|
|
|
|
path: String,
|
|
|
|
num_workers: Option<usize>,
|
|
|
|
callback: PyObject,
|
|
|
|
) -> PyResult<PyObject> {
|
|
|
|
use route::*;
|
|
|
|
let num_workers = num_workers.unwrap_or(1);
|
|
|
|
let mode = match Mode::parse(&mode) {
|
|
|
|
Ok(val) => val,
|
|
|
|
Err(e) => {
|
|
|
|
return Err(PyErr::new::<ValueError, _>(e));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let state_dict = PyDict::new(py);
|
|
|
|
{
|
|
|
|
let cb_res = callback.call(py, (state_dict,), None);
|
|
|
|
if cb_res.is_err() {
|
|
|
|
println!("Error: {:?}", cb_res);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let callback_wrapped = move |state: &SearchState| {
|
|
|
|
state_dict.set_item("mode", state.mode.clone())?;
|
|
|
|
state_dict.set_item("system", state.system.clone())?;
|
|
|
|
state_dict.set_item("body", state.body.clone())?;
|
|
|
|
state_dict.set_item("depth", state.depth)?;
|
|
|
|
state_dict.set_item("queue_size", state.queue_size)?;
|
|
|
|
state_dict.set_item("d_rem", state.d_rem)?;
|
|
|
|
state_dict.set_item("d_total", state.d_total)?;
|
|
|
|
state_dict.set_item("prc_done", state.prc_done)?;
|
|
|
|
state_dict.set_item("n_seen", state.n_seen)?;
|
|
|
|
state_dict.set_item("prc_seen", state.prc_seen)?;
|
|
|
|
state_dict.set_item("from", state.from.clone())?;
|
|
|
|
state_dict.set_item("to", state.to.clone())?;
|
|
|
|
let cb_res = callback.call(py, (state_dict,), None);
|
|
|
|
if cb_res.is_err() {
|
|
|
|
println!("Error: {:?}", cb_res);
|
|
|
|
}
|
|
|
|
cb_res
|
|
|
|
};
|
|
|
|
let hops: Vec<SysEntry> = (hops.iter().map(|v| SysEntry::from_str(&v)).collect::<Result<Vec<SysEntry>,_>>())?;
|
|
|
|
println!("Resolving systems...");
|
|
|
|
let hops: Vec<u32> = match resolve(&hops, &PathBuf::from(&path)) {
|
|
|
|
Ok(ids) => ids,
|
|
|
|
Err(err_msg) => {
|
|
|
|
return Err(PyErr::new::<ValueError, _>(err_msg));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let opts = RouteOpts {
|
|
|
|
systems: hops,
|
|
|
|
range: Some(range),
|
|
|
|
file_path: PathBuf::from(path),
|
|
|
|
precomp_file: precomp.map(PathBuf::from),
|
|
|
|
callback: Box::new(callback_wrapped),
|
|
|
|
mode,
|
|
|
|
factor: greedyness,
|
|
|
|
precompute: false,
|
|
|
|
permute,
|
|
|
|
keep_first,
|
|
|
|
keep_last,
|
|
|
|
primary,
|
|
|
|
workers: num_workers,
|
|
|
|
};
|
|
|
|
match route(opts) {
|
|
|
|
Ok(Some(route)) => {
|
|
|
|
let hops = route.iter().map(|hop| {
|
|
|
|
let pos = PyList::new(py, hop.pos.iter());
|
|
|
|
let elem = PyDict::new(py);
|
|
|
|
elem.set_item("star_type", hop.star_type.clone()).unwrap();
|
|
|
|
elem.set_item("system", hop.system.clone()).unwrap();
|
|
|
|
elem.set_item("body", hop.body.clone()).unwrap();
|
|
|
|
elem.set_item("distance", hop.distance).unwrap();
|
|
|
|
elem.set_item("pos", pos).unwrap();
|
|
|
|
elem
|
|
|
|
});
|
|
|
|
let lst = PyList::new(py, hops);
|
|
|
|
Ok(lst.to_object(py))
|
|
|
|
}
|
|
|
|
Ok(None) => Ok(py.None()),
|
|
|
|
Err(e) => Err(PyErr::new::<ValueError, _>(e)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*/
|