206 lines
5.7 KiB
Rust
206 lines
5.7 KiB
Rust
use crate::common::SystemSerde;
|
|
use fnv::FnvHashMap;
|
|
use pyo3::prelude::*;
|
|
use serde::Deserialize;
|
|
use serde_json::Result;
|
|
use std::fs::File;
|
|
use std::io::Seek;
|
|
use std::io::{BufRead, BufReader, BufWriter, SeekFrom};
|
|
use std::path::PathBuf;
|
|
use std::str;
|
|
use std::time::Instant;
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
#[allow(non_snake_case)]
|
|
struct Body {
|
|
name: String,
|
|
subType: String,
|
|
#[serde(rename = "type")]
|
|
body_type: String,
|
|
systemId: i32,
|
|
systemId64: i64,
|
|
#[serde(rename = "distanceToArrival")]
|
|
distance: u32,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct Coords {
|
|
x: f32,
|
|
y: f32,
|
|
z: f32,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct System {
|
|
id: i32,
|
|
id64: i64,
|
|
name: String,
|
|
coords: Coords,
|
|
date: String,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct PreprocessState {
|
|
pub file: String,
|
|
pub message: String,
|
|
pub total: u64,
|
|
pub done: u64,
|
|
pub count: usize,
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
fn process(
|
|
path: &PathBuf,
|
|
func: &mut dyn for<'r> FnMut(&'r str) -> (),
|
|
callback: &dyn Fn(&PreprocessState) -> PyResult<PyObject>,
|
|
) -> std::io::Result<()> {
|
|
let mut buffer = String::new();
|
|
let fh = File::open(path)?;
|
|
let total_size = fh.metadata()?.len();
|
|
let mut t_last = Instant::now();
|
|
let mut reader = BufReader::new(fh);
|
|
let mut state = PreprocessState {
|
|
file: path.to_str().unwrap().to_owned(),
|
|
total: total_size,
|
|
done: 0,
|
|
count: 0,
|
|
message: format!("Processing {} ...", path.to_str().unwrap()),
|
|
};
|
|
println!("Loading {} ...", path.to_str().unwrap());
|
|
while let Ok(n) = reader.read_line(&mut buffer) {
|
|
if n == 0 {
|
|
break;
|
|
}
|
|
buffer = buffer.trim_end().trim_end_matches(|c| c == ',').to_string();
|
|
if !buffer.is_empty() {
|
|
func(&buffer);
|
|
}
|
|
let pos = reader.seek(SeekFrom::Current(0)).unwrap();
|
|
state.done = pos;
|
|
state.count += 1;
|
|
if t_last.elapsed().as_millis() > 100 {
|
|
callback(&state)?;
|
|
t_last = Instant::now();
|
|
}
|
|
buffer.clear();
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn process_systems(
|
|
path: &PathBuf,
|
|
callback: &dyn Fn(&PreprocessState) -> PyResult<PyObject>,
|
|
) -> FnvHashMap<i32, System> {
|
|
let mut ret = FnvHashMap::default();
|
|
process(
|
|
path,
|
|
&mut |line| {
|
|
let sys_res: Result<System> = serde_json::from_str(&line);
|
|
if let Ok(sys) = sys_res {
|
|
ret.insert(sys.id, sys);
|
|
} else {
|
|
eprintln!("\nError parsing: {}\n\t{:?}\n", line, sys_res.unwrap_err());
|
|
}
|
|
},
|
|
callback,
|
|
)
|
|
.unwrap();
|
|
ret
|
|
}
|
|
|
|
fn build_index(path: &PathBuf) -> std::io::Result<()> {
|
|
let mut wtr = BufWriter::new(File::create(path.with_extension("idx"))?);
|
|
let mut idx: Vec<u64> = Vec::new();
|
|
let mut records = (csv::Reader::from_path(path)?).into_deserialize::<SystemSerde>();
|
|
loop {
|
|
idx.push(records.reader().position().byte());
|
|
if records.next().is_none() {
|
|
break;
|
|
}
|
|
}
|
|
bincode::serialize_into(&mut wtr, &idx).unwrap();
|
|
Ok(())
|
|
}
|
|
|
|
fn process_bodies(
|
|
path: &PathBuf,
|
|
out_path: &PathBuf,
|
|
systems: &mut FnvHashMap<i32, System>,
|
|
callback: &dyn Fn(&PreprocessState) -> PyResult<PyObject>,
|
|
) -> std::io::Result<()> {
|
|
println!(
|
|
"Processing {} into {} ...",
|
|
path.to_str().unwrap(),
|
|
out_path.to_str().unwrap(),
|
|
);
|
|
let mut n: u32 = 0;
|
|
let mut wtr = csv::Writer::from_path(out_path)?;
|
|
process(
|
|
path,
|
|
&mut |line| {
|
|
if !line.contains("Star") {
|
|
return;
|
|
}
|
|
let body_res: Result<Body> = serde_json::from_str(&line);
|
|
if let Ok(body) = body_res {
|
|
if !body.body_type.contains("Star") {
|
|
return;
|
|
}
|
|
if let Some(sys) = systems.get(&body.systemId) {
|
|
let sub_type = body.subType;
|
|
let mult = get_mult(&sub_type);
|
|
let sys_name = sys.name.clone();
|
|
let rec = SystemSerde {
|
|
id: n,
|
|
star_type: sub_type,
|
|
system: sys_name,
|
|
body: body.name,
|
|
mult,
|
|
distance: body.distance,
|
|
x: sys.coords.x,
|
|
y: sys.coords.y,
|
|
z: sys.coords.z,
|
|
};
|
|
wtr.serialize(rec).unwrap();
|
|
n += 1;
|
|
};
|
|
} else {
|
|
eprintln!("\nError parsing: {}\n\t{:?}\n", line, body_res.unwrap_err());
|
|
}
|
|
},
|
|
callback,
|
|
)
|
|
.unwrap();
|
|
println!("Total Systems: {}", n);
|
|
systems.clear();
|
|
Ok(())
|
|
}
|
|
|
|
pub fn preprocess_files(
|
|
bodies: &PathBuf,
|
|
systems: &PathBuf,
|
|
out_path: &PathBuf,
|
|
callback: &dyn Fn(&PreprocessState) -> PyResult<PyObject>,
|
|
) -> std::io::Result<()> {
|
|
if !out_path.exists() {
|
|
let mut systems = process_systems(systems, &callback);
|
|
process_bodies(bodies, out_path, &mut systems, &callback)?;
|
|
} else {
|
|
println!(
|
|
"File '{}' exists, not overwriting it",
|
|
out_path.to_str().unwrap()
|
|
);
|
|
}
|
|
println!("Building index...");
|
|
println!("Index result: {:?}", build_index(&out_path));
|
|
Ok(())
|
|
}
|