use std::{
fs::{self, File},
io::Write,
path::{Path, PathBuf},
};
use anyhow::{Context, Result};
use regex::Regex;
const BASE_URL: &str = "https://ftp.imcce.fr/pub/ephem/planets/vsop87/";
const FILE_RE_STR: &str = r"VSOP87[A-Z]\.[A-Za-z]{3}";
pub fn ensure_dataset(data_dir: &Path) -> Result<()> {
if contains_vsop_files(data_dir)? {
return Ok(());
}
fs::create_dir_all(data_dir)
.with_context(|| format!("Could not create directory {data_dir:?}"))?;
let files = remote_file_list().context("Fetching VSOP87 directory listing")?;
for file in files {
let dst = data_dir.join(&file);
if dst.exists() {
continue; }
let url = format!("{BASE_URL}{file}");
download_file(&url, &dst)?;
}
Ok(())
}
fn contains_vsop_files(dir: &Path) -> Result<bool> {
if !dir.is_dir() {
return Ok(false);
}
let re = Regex::new(FILE_RE_STR).unwrap();
for entry in fs::read_dir(dir)? {
let entry = entry?;
if re.is_match(&entry.file_name().to_string_lossy()) {
return Ok(true);
}
}
Ok(false)
}
fn remote_file_list() -> Result<Vec<String>> {
let body = reqwest::blocking::get(BASE_URL)?.text()?;
let re = Regex::new(FILE_RE_STR).unwrap();
let mut files: Vec<String> = re.find_iter(&body).map(|m| m.as_str().to_owned()).collect();
files.sort();
files.dedup();
Ok(files)
}
fn download_file(url: &str, dst: &PathBuf) -> Result<()> {
println!("cargo:info=Downloading {} → {}", url, dst.display());
let bytes = reqwest::blocking::get(url)?.bytes()?;
let mut fh = File::create(dst).with_context(|| format!("Failed to create {dst:?}"))?;
fh.write_all(&bytes)
.with_context(|| format!("Failed to write {dst:?}"))?;
Ok(())
}