use super::{Term, VersionMap};
use anyhow::Context;
use regex::Regex;
use std::{
collections::BTreeMap,
fs::File,
io::{BufRead, BufReader},
path::Path,
};
use walkdir::WalkDir;
const REGEX_FILE: &str = r"^VSOP87([A-Z])\.[A-Za-z]{3}$";
const HEADER_REGEX: &str =
r"VSOP87 VERSION [A-Z]?\d+\s+(\S+)\s+VARIABLE\s+(\d+)\s+\(XYZ\)\s+\*T\*\*(\d+)\s+(\d+)\s+TERMS";
fn sk_to_term(s: f64, k: f64, a: f64, b: f64, c: f64) -> Option<Term> {
if a.abs() > 1e-15 {
Some(Term { a, b, c })
} else if s.abs() > 1e-15 || k.abs() > 1e-15 {
let r = (s * s + k * k).sqrt();
let alpha = s.atan2(k); Some(Term {
a: r,
b: b - alpha,
c,
})
} else {
None
}
}
fn parse_data_line(line: &str) -> Option<(f64, f64, f64, f64, f64)> {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() < 5 {
return None;
}
let len = parts.len();
let c = parts[len - 1].parse().ok()?;
let b = parts[len - 2].parse().ok()?;
let a = parts[len - 3].parse().ok()?;
let k = parts[len - 4].parse().ok()?;
let s = parts[len - 5].parse().ok()?;
Some((s, k, a, b, c))
}
pub fn collect_terms(data_dir: &Path) -> anyhow::Result<VersionMap> {
let file_re = Regex::new(REGEX_FILE)?;
let header_re = Regex::new(HEADER_REGEX)?;
let mut versions: VersionMap = BTreeMap::new();
for entry in WalkDir::new(data_dir)
.min_depth(1) .into_iter()
.filter_map(Result::ok)
{
let path = entry.path();
let fname = path.file_name().unwrap().to_string_lossy();
let caps = match file_re.captures(&fname) {
Some(c) => c,
None => continue, };
let v_char = caps[1].chars().next().unwrap();
let file = File::open(path).with_context(|| format!("Could not open {path:?}"))?;
let reader = BufReader::new(file);
let mut cur_planet = String::new(); let mut cur_coord: u8 = 0; let mut cur_t: u8 = 0; let mut remaining: u32 = 0;
for line in reader.lines() {
let line = line?;
if let Some(cap) = header_re.captures(&line) {
cur_planet = cap[1].to_uppercase();
cur_coord = cap[2].parse::<u8>().unwrap();
cur_t = cap[3].parse::<u8>().unwrap();
remaining = cap[4].parse::<u32>().unwrap();
continue; }
if remaining > 0 {
if let Some((s, k, a, b, c)) = parse_data_line(&line) {
if let Some(term) = sk_to_term(s, k, a, b, c) {
let planet_map = versions.entry(v_char).or_default();
let coord_map = planet_map.entry(cur_planet.clone()).or_default();
let t_map = coord_map.entry(cur_coord).or_default();
let vec_terms = t_map.entry(cur_t).or_default();
vec_terms.push(term);
}
remaining -= 1; }
}
}
}
Ok(versions)
}